Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The hatch version command should respect exact version strings #697

Open
br3ndonland opened this issue Jan 3, 2023 · 2 comments
Open

The hatch version command should respect exact version strings #697

br3ndonland opened this issue Jan 3, 2023 · 2 comments

Comments

@br3ndonland
Copy link

br3ndonland commented Jan 3, 2023

Description

Thanks for Hatch! I'd like to ask about a small user experience issue with the hatch version command.

When incrementing the version of a project with hatch version <version>, Hatch changes the version syntax to the PEP 440 default, even if the version is a valid PEP 440 string.

This behavior of hatch version <version> particularly affects pre-releases. It can be an issue for users or teams who need to comply with other version syntaxes such as SemVer.

For a minimal reproduction, generate a project with hatch new, and bump the version with a valid PEP 440 string different from the default syntax, such as 0.0.2-alpha.0. Hatch converts 0.0.2-alpha.0 (compliant with both SemVer and PEP 440) to the PEP 440 default syntax 0.0.2a0 (not compliant with SemVer).

~
❯ python --version
Python 3.10.9

~
❯ hatch --version
Hatch, version 1.6.3

~cd ~/dev

~/dev
❯ hatch new hatch-example
hatch-example
├── hatch_example
│   ├── __about__.py
│   └── __init__.py
├── tests
│   └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml

~/dev
❯ cd hatch-example

~/dev/hatch-example
❯ hatch version
0.0.1

~/dev/hatch-example
❯ hatch version 0.0.2-alpha.0
Old: 0.0.1
New: 0.0.2a0

~/dev/hatch-example
❯ cat hatch_example/__about__.py | grep "__version__"
__version__ = '0.0.2a0'

By comparing the version set by Hatch with the desired version, we can see that they are both valid and considered equivalent:

~/dev/hatch-example
❯ hatch shell

~/dev/hatch-example
❯ python
>>> import packaging.version
>>> import hatch_example.__about__
>>> current_version = packaging.version.parse(hatch_example.__about__.__version__)
>>> str(current_version)
'0.0.2a0'
>>> desired_version = packaging.version.parse("0.0.2-alpha.0")
>>> current_version == desired_version
True
>>> current_version.is_prerelease
True
>>> desired_version.is_prerelease
True
>>>

Suggestions

I would be happy to provide a PR for this.

I'd like to suggest that, rather than converting the version string to the PEP 440 default, Hatch should instead:

To take it further, Hatch could offer a configuration setting to control the behavior of the hatch version command.

Related

External links

Project source code

The relevant code appears to be using a private method from packaging, packaging.version._parse_letter_version, along with some custom conditions. I know Hatch and packaging are both part of PyPA, but it might be prudent to avoid using private methods in other packages.

updated_version = app.project.metadata.hatch.version.scheme.update(
desired_version, original_version, version_data
)
source.set_version(updated_version, version_data)

self.project = cast(Project, None)

@property
def metadata(self):
if self._metadata is None:
from hatchling.metadata.core import ProjectMetadata
self._metadata = ProjectMetadata(self.location, self.plugin_manager, self.raw_config)
return self._metadata

self._version = HatchVersionConfig(self.root, deepcopy(options), self.plugin_manager)
return self._version
class HatchVersionConfig(Generic[PluginManagerBound]):
def __init__(self, root: str, config: dict[str, Any], plugin_manager: PluginManagerBound) -> None:
self.root = root
self.config = config
self.plugin_manager = plugin_manager
self._cached: str | None = None
self._source_name: str | None = None
self._scheme_name: str | None = None
self._source: VersionSourceInterface | None = None
self._scheme: VersionSchemeInterface | None = None

class StandardScheme(VersionSchemeInterface):
"""
See https://peps.python.org/pep-0440/
"""
PLUGIN_NAME = 'standard'
def update(self, desired_version: str, original_version: str, version_data: dict) -> str:
from packaging.version import Version, _parse_letter_version

elif version in ('a', 'b', 'c', 'rc', 'alpha', 'beta', 'pre', 'preview'):
phase, number = _parse_letter_version(version, 0)
if original.pre:
current_phase, current_number = _parse_letter_version(*original.pre)

@ofek
Copy link
Sponsor Collaborator

ofek commented Jan 3, 2023

@br3ndonland
Copy link
Author

Thanks for pointing out hatch-semver, looks helpful!

I think the comments in the original post would still apply directly to Hatch and Hatchling, though. I'm suggesting that, as long as the version is a valid PEP 440 string (whether the version scheme is SemVer or something else), Hatch should keep it as-is and not change it to the PEP 440 default.

I could also understand if Hatch decided to only supported PEP 440 default syntax in the hatch version command and related commands. If that is the decision, I think the behavior should at least be documented so that users know what to expect when they run hatch version. I'm happy to contribute some documentation for that, if that's the way you want to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants