Skip to content

Commit f5d1cab

Browse files
committed
Initial conversion
1 parent d7dec79 commit f5d1cab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3707
-116
lines changed

.composition.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ context:
2424
packaging_tool: pip/setuptools
2525
project_name: bump-my-version
2626
project_short_description: Version bump your Python project
27-
project_slug: bump_version
27+
project_slug: bumpversion
2828
version: 0.1.0
2929
directory: cookiecutter-boilerplate
3030
merge_strategies:
@@ -50,7 +50,7 @@ context:
5050
friendly_name: Bump My Version
5151
project_name: bump-my-version
5252
project_short_description: Version bump your Python project
53-
project_slug: bump_version
53+
project_slug: bumpversion
5454
version: 0.1.0
5555
directory: cookiecutter-cli
5656
merge_strategies:
@@ -86,7 +86,7 @@ context:
8686
friendly_name: Bump My Version
8787
github_user: callowayproject
8888
project_name: bump-my-version
89-
project_slug: bump_version
89+
project_slug: bumpversion
9090
directory: cookiecutter-docs
9191
merge_strategies:
9292
'*.json': comprehensive

bump_version/cli.py

-54
This file was deleted.

bump_version/commands/__init__.py

-1
This file was deleted.

bump_version/commands/say_hello.py

-7
This file was deleted.
File renamed without changes.

bumpversion/__main__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Main entrypoint for module."""
2+
from bumpversion.cli import cli
3+
4+
cli()

bumpversion/aliases.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Utilities for handling command aliases."""
2+
from typing import List
3+
4+
import rich_click as click
5+
from click import Context
6+
from rich_click.rich_group import RichGroup
7+
8+
9+
class AliasedGroup(RichGroup):
10+
"""
11+
This following example implements a subclass of Group that accepts a prefix for a command.
12+
13+
If there were a command called ``push``, it would accept ``pus`` as an alias (so long as it was unique)
14+
"""
15+
16+
def get_command(self, ctx: Context, cmd_name: str) -> None:
17+
"""Given a context and a command name, this returns a Command object if it exists or returns None."""
18+
rv = click.Group.get_command(self, ctx, cmd_name)
19+
if rv is not None:
20+
return rv
21+
matches = [x for x in self.list_commands(ctx) if x.startswith(cmd_name)]
22+
if not matches:
23+
return None
24+
elif len(matches) == 1:
25+
return click.Group.get_command(self, ctx, matches[0])
26+
ctx.fail(f"Too many matches: {', '.join(sorted(matches))}")
27+
28+
def resolve_command(self, ctx: Context, args: List[str]) -> tuple:
29+
"""Find the command and make sure the full command name is returned."""
30+
# always return the full command name
31+
_, cmd, args = super().resolve_command(ctx, args)
32+
return cmd.name, cmd, args

bumpversion/autocast.py

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
Automatically detect the true Python type of a string and cast it to the correct type.
3+
4+
Based on https://github.com/cgreer/cgAutoCast/blob/master/cgAutoCast.py
5+
"""
6+
7+
import contextlib
8+
from typing import Any
9+
10+
11+
def boolify(s: str) -> bool:
12+
"""Convert a string to a boolean."""
13+
if s in {"True", "true"}:
14+
return True
15+
if s in {"False", "false"}:
16+
return False
17+
raise ValueError("Not Boolean Value!")
18+
19+
20+
def noneify(s: str) -> None:
21+
"""Convert a string to None."""
22+
if s == "None":
23+
return None
24+
raise ValueError("Not None Value!")
25+
26+
27+
def listify(s: str) -> list:
28+
"""
29+
Convert a string representation of a list into list of homogenous basic types.
30+
31+
Type of elements in list is determined via first element. Successive elements are
32+
cast to that type.
33+
34+
Args:
35+
s: String representation of a list.
36+
37+
Raises:
38+
ValueError: If string does not represent a list.
39+
TypeError: If string does not represent a list of homogenous basic types.
40+
41+
Returns:
42+
List of homogenous basic types.
43+
"""
44+
if "," not in s and "\n" not in s:
45+
raise ValueError("Not a List")
46+
47+
# derive the type of the variable
48+
str_list = s.strip().split(",") if "," in s else s.strip().split("\n")
49+
element_caster = str
50+
for caster in (boolify, int, float, noneify, element_caster):
51+
with contextlib.suppress(ValueError):
52+
caster(str_list[0]) # type: ignore[operator]
53+
element_caster = caster # type: ignore[assignment]
54+
break
55+
# cast all elements
56+
try:
57+
return [element_caster(x) for x in str_list]
58+
except ValueError as e:
59+
raise TypeError("Autocasted list must be all same type") from e
60+
61+
62+
def autocast_value(var: Any) -> Any:
63+
"""
64+
Guess the string representation of the variable's type.
65+
66+
Args:
67+
var: Value to autocast.
68+
69+
Returns:
70+
The autocasted value.
71+
"""
72+
if not isinstance(var, str): # don't need to guess non-string types
73+
return var
74+
75+
# guess string representation of var
76+
for caster in (boolify, int, float, noneify, listify):
77+
with contextlib.suppress(ValueError):
78+
return caster(var) # type: ignore[operator]
79+
80+
return var

bumpversion/bump.py

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""Version changing methods."""
2+
import logging
3+
import shlex
4+
from pathlib import Path
5+
from typing import TYPE_CHECKING, ChainMap, List, Optional
6+
7+
if TYPE_CHECKING: # pragma: no-coverage
8+
from bumpversion.files import ConfiguredFile
9+
from bumpversion.version_part import Version
10+
11+
from bumpversion.config import Config, update_config_file
12+
from bumpversion.exceptions import ConfigurationError
13+
from bumpversion.utils import get_context, key_val_string
14+
15+
logger = logging.getLogger("bumpversion")
16+
17+
18+
def get_next_version(
19+
current_version: "Version", config: Config, version_part: Optional[str], new_version: Optional[str]
20+
) -> "Version":
21+
"""
22+
Bump the version_part to the next value.
23+
24+
Args:
25+
current_version: The current version
26+
config: The current configuration
27+
version_part: Optional part of the version to bump
28+
new_version: Optional specific version to bump to
29+
30+
Returns:
31+
The new version
32+
33+
Raises:
34+
ConfigurationError: If it can't generate the next version.
35+
"""
36+
if new_version:
37+
next_version = config.version_config.parse(new_version)
38+
elif version_part:
39+
logger.info("Attempting to increment part '%s'", version_part)
40+
next_version = current_version.bump(version_part, config.version_config.order)
41+
else:
42+
raise ConfigurationError("Unable to get the next version.")
43+
44+
logger.info("Values are now: %s", key_val_string(next_version.values))
45+
return next_version
46+
47+
48+
def do_bump(
49+
version_part: Optional[str],
50+
new_version: Optional[str],
51+
config: Config,
52+
config_file: Optional[Path] = None,
53+
dry_run: bool = False,
54+
) -> None:
55+
"""
56+
Bump the version_part to the next value or set the version to new_version.
57+
58+
Args:
59+
version_part: The version part to bump
60+
new_version: The explicit version to set
61+
config: The configuration to use
62+
config_file: The configuration file to update
63+
dry_run: True if the operation should be a dry run
64+
"""
65+
from bumpversion.files import modify_files, resolve_file_config
66+
67+
ctx = get_context(config)
68+
version = config.version_config.parse(config.current_version)
69+
next_version = get_next_version(version, config, version_part, new_version)
70+
next_version_str = config.version_config.serialize(next_version, ctx)
71+
logger.info("New version will be '%s'", next_version_str)
72+
73+
ctx = get_context(config, version, next_version)
74+
75+
configured_files = resolve_file_config(config.files, config.version_config)
76+
77+
modify_files(configured_files, version, next_version, ctx, dry_run)
78+
79+
update_config_file(config_file, config.current_version, next_version_str, dry_run)
80+
81+
commit_and_tag(config, config_file, configured_files, ctx, dry_run)
82+
83+
84+
def commit_and_tag(
85+
config: Config,
86+
config_file: Optional[Path],
87+
configured_files: List["ConfiguredFile"],
88+
ctx: ChainMap,
89+
dry_run: bool = False,
90+
) -> None:
91+
"""
92+
Commit and tag the changes, if a tool is configured.
93+
94+
Args:
95+
config: The configuration
96+
config_file: The configuration file to include in the commit, if it exists
97+
configured_files: A list of files to commit
98+
ctx: The context used to render the tag and tag message
99+
dry_run: True if the operation should be a dry run
100+
"""
101+
if not config.scm_info.tool:
102+
return
103+
104+
extra_args = shlex.split(config.commit_args) if config.commit_args else []
105+
106+
commit_files = {f.path for f in configured_files}
107+
if config_file:
108+
commit_files |= {str(config_file)}
109+
110+
config.scm_info.tool.commit_to_scm(list(commit_files), config, ctx, extra_args, dry_run)
111+
config.scm_info.tool.tag_in_scm(config, ctx, dry_run)

0 commit comments

Comments
 (0)