Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Commit

Permalink
Merge branch 'bump-command'
Browse files Browse the repository at this point in the history
  • Loading branch information
orsinium committed Apr 10, 2019
2 parents 200b248 + 4fb251c commit a007097
Show file tree
Hide file tree
Showing 23 changed files with 705 additions and 31 deletions.
2 changes: 1 addition & 1 deletion dephell/__version__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

__version__ = '0.3.1'
__version__ = '0.3.2'
__author__ = 'Gram (@orsinium)'
__license__ = 'MIT'
9 changes: 9 additions & 0 deletions dephell/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,28 @@
from ._entrypoints import get_entrypoints
from ._json import make_json
from ._python import get_python, get_python_env
from ._roman import arabic2roman, roman2arabic
from ._venv import get_venv
from ._version import bump_file, bump_project, bump_version, get_version_from_file, get_version_from_project


__all__ = [
'arabic2roman',
'attach_deps',
'bump_file',
'bump_project',
'bump_version',
'get_downloads_by_category',
'get_entrypoints',
'get_python_env',
'get_python',
'get_total_downloads',
'get_venv',
'get_version_from_file',
'get_version_from_project',
'make_bash_autocomplete',
'make_editorconfig',
'make_json',
'make_zsh_autocomplete',
'roman2arabic',
]
45 changes: 45 additions & 0 deletions dephell/actions/_roman.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

NUMBERS = [
(1000, 'M'),
(900, 'CM'),
(500, 'D'),
(400, 'CD'),
(100, 'C'),
(90, 'XC'),
(50, 'L'),
(40, 'XL'),
(10, 'X'),
(9, 'IX'),
(5, 'V'),
(4, 'IV'),
(1, 'I'),
]


# https://stackoverflow.com/a/28777781/8704691
def arabic2roman(number):
result = ''
for arabic, roman in NUMBERS:
div = number // arabic
result += roman * div
number -= arabic * div
if number <= 0:
break
return result


def roman2arabic(number):
if not number:
return 0
if number[0] == 'M':
return 1000 + roman2arabic(number[1:])
if 'D' in number:
values = range(400, 900)
elif 'C' in number:
values = range(90, 1000)
else:
values = range(90)
for arabic in values:
if arabic2roman(arabic) == number:
return arabic
raise ValueError
183 changes: 183 additions & 0 deletions dephell/actions/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import re
from datetime import date
from pathlib import Path
from typing import Iterator, Union, Optional

from dephell_discover import Root
from packaging.version import Version, VERSION_PATTERN

from .. import constants
from ._roman import arabic2roman, roman2arabic

FILE_NAMES = (
'__init__.py',
'__version__.py',
'__about__.py',
'_version.py',
'_about.py',
)
REX_VERSION = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)
PREFIXES = {'__version__', 'VERSION', 'version'}


def get_version_from_file(path: Path) -> Optional[str]:
with path.open('r') as stream:
for line in stream:
prefix, sep, version = line.partition('=')
if not sep:
continue
if prefix.rstrip() not in PREFIXES:
continue
return version.strip().strip('\'"')
return None


def get_version_from_project(project: Root) -> Optional[str]:
for package in project.packages:
for path in package:
if path.name not in FILE_NAMES:
continue
version = get_version_from_file(path=path)
if version:
return version
return None


def bump_file(path: Path, old: str, new: str) -> bool:
file_bumped = False
new_content = []
with path.open('r') as stream:
for line in stream:
prefix, sep, _version = line.partition('=')
if not sep:
new_content.append(line)
continue
if prefix.rstrip() not in PREFIXES:
new_content.append(line)
continue

# replace old version
if old:
new_line = line.replace(old, new, 1)
if new_line != line:
new_content.append(new_line)
file_bumped = True
continue

# replace any version
new_line, count = REX_VERSION.subn(new, line)
if count == 1:
new_content.append(new_line)
file_bumped = True
continue

new_content.append(line)
if file_bumped:
path.write_text(''.join(new_content))
return file_bumped


def bump_project(project: Root, old: str, new: str) -> Iterator[Path]:
for package in project.packages:
for path in package:
if path.name not in FILE_NAMES:
continue
file_bumped = bump_file(path=path, old=old, new=new)
if file_bumped:
yield path


def bump_version(version: Union[Version, str], rule: str, scheme: str = 'semver') -> str:
# check scheme
if scheme not in constants.VERSION_SCHEMES:
raise ValueError('invalid scheme: {}'.format(scheme))

if rule == 'init':
return constants.VERSION_INIT[scheme]

# explicitly specified local version
if rule[0] == '+':
if 'local' not in constants.VERSION_SCHEMES[scheme]:
raise ValueError('local numbers are unsupported by scheme ' + scheme)
version = str(version).split('+')[0]
return version + rule

# check rule
if rule not in constants.VERSION_SCHEMES[scheme]:
if REX_VERSION.fullmatch(rule):
return rule
raise ValueError('rule {} is unsupported by scheme {}'.format(rule, scheme))

if scheme == 'roman':
version = roman2arabic(version)
return arabic2roman(version + 1)

if isinstance(version, str):
version = Version(version)

if scheme in ('semver', 'romver', 'pep', 'zerover'):
parts = version.release + (0, 0)
if scheme == 'zerover':
parts = (0, ) + parts[1:]
if rule in constants.VERSION_MAJOR:
return '{}.0.0'.format(parts[0] + 1)
if rule in constants.VERSION_MINOR:
return '{}.{}.0'.format(parts[0], parts[1] + 1)
if rule in constants.VERSION_PATCH:
return '{}.{}.{}'.format(parts[0], parts[1], parts[2] + 1)

if scheme in ('semver', 'romver', 'zerover'):
if rule in constants.VERSION_PRE:
pre = version.pre[1] if version.pre else 0
return '{}.{}.{}-rc.{}'.format(*parts[:3], pre + 1)
if rule in constants.VERSION_LOCAL:
pre = '-{}.{}'.format(*version.pre) if version.pre else ''
local = int(version.local) if version.local else 0
return '{}.{}.{}{}+{}'.format(*parts[:3], pre, local + 1)

if scheme == 'pep':
if rule in constants.VERSION_PRE:
pre = version.pre[1] if version.pre else 0
return '{}.{}.{}rc{}'.format(*parts[:3], pre + 1)
if rule in constants.VERSION_POST:
# PEP allows post-releases for pre-releases,
# but it "strongly discouraged", so let's ignore it.
return '{}.{}.{}.post{}'.format(*parts[:3], (version.post or 0) + 1)
if rule in constants.VERSION_DEV:
if version.pre:
suffix = 'rc{}'.format(version.pre[1])
elif version.post:
suffix = '.post{}'.format(version.post)
else:
suffix = ''
return '{}.{}.{}{}.dev{}'.format(*parts[:3], suffix, (version.dev or 0) + 1)
if rule in constants.VERSION_LOCAL:
old = str(version).split('+')[0]
local = int(version.local) if version.local else 0
return '{}+{}'.format(old, local + 1)

if scheme == 'comver':
parts = parts = version.release + (0,)
if rule in constants.VERSION_MAJOR:
return '{}.0'.format(parts[0] + 1)
if rule in constants.VERSION_MINOR:
return '{}.{}'.format(parts[0], parts[1] + 1)
if rule in constants.VERSION_PRE:
pre = version.pre[1] if version.pre else 0
return '{}.{}-rc.{}'.format(*parts[:2], pre + 1)
if rule in constants.VERSION_LOCAL:
pre = '-{}.{}'.format(*version.pre) if version.pre else ''
local = int(version.local) if version.local else 0
return '{}.{}{}+{}'.format(*parts[:2], pre, local + 1)

if scheme == 'calver':
today = date.today()
if rule in constants.VERSION_MAJOR:
return '{}.{}'.format(today.year, today.month)
if rule in constants.VERSION_PATCH:
if version.release[0] == today.year and version.release[1] == today.month:
micro = (version.release + (0, 0))[2]
micro = today.day if micro < today.day else micro + 1
else:
micro = today.day
return '{}.{}.{}'.format(today.year, today.month, micro)
9 changes: 6 additions & 3 deletions dephell/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# app
from .autocomplete import AutocompleteCommand
from .build import BuildCommand
from .deps_convert import DepsConvertCommand
from .deps_install import DepsInstallCommand
from .deps_licenses import DepsLicensesCommand
Expand All @@ -22,6 +21,8 @@
from .package_list import PackageListCommand
from .package_search import PackageSearchCommand
from .package_show import PackageShowCommand
from .project_build import ProjectBuildCommand
from .project_bump import ProjectBumpCommand
from .venv_create import VenvCreateCommand
from .venv_destroy import VenvDestroyCommand
from .venv_run import VenvRunCommand
Expand All @@ -30,7 +31,6 @@

__all__ = [
'AutocompleteCommand',
'BuildCommand',
'commands',
'DepsConvertCommand',
'DepsInstallCommand',
Expand All @@ -53,6 +53,8 @@
'PackageListCommand',
'PackageSearchCommand',
'PackageShowCommand',
'ProjectBuildCommand',
'ProjectBumpCommand',
'VenvCreateCommand',
'VenvDestroyCommand',
'VenvRunCommand',
Expand All @@ -62,7 +64,6 @@

commands = {
'autocomplete': AutocompleteCommand,
'build': BuildCommand,
'deps convert': DepsConvertCommand,
'deps install': DepsInstallCommand,
'deps licenses': DepsLicensesCommand,
Expand All @@ -87,6 +88,8 @@
'package list': PackageListCommand,
'package search': PackageSearchCommand,
'package show': PackageShowCommand,
'project build': ProjectBuildCommand,
'project bump': ProjectBumpCommand,
'venv create': VenvCreateCommand,
'venv destroy': VenvDestroyCommand,
'venv run': VenvRunCommand,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
)


class BuildCommand(BaseCommand):
class ProjectBuildCommand(BaseCommand):
"""Create dist archives for project.
https://dephell.readthedocs.io/en/latest/cmd-build.html
https://dephell.readthedocs.io/en/latest/cmd-project-build.html
"""
@classmethod
def get_parser(cls):
parser = ArgumentParser(
prog='dephell build',
prog='dephell project build',
description=cls.__doc__,
)
builders.build_config(parser)
Expand Down

0 comments on commit a007097

Please sign in to comment.