diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index a263dcb9..6e75e2c8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -55,6 +55,8 @@ body: label: Situation description: A clear and concise description of what the bug is. placeholder: Describe the problem you see... + validations: + required: true - type: textarea id: reproduction_steps @@ -66,6 +68,8 @@ body: 2. Scroll down to '....' 3. See error placeholder: Describe the steps to reproduce the issue... + validations: + required: true - type: textarea id: expected_behavior @@ -73,6 +77,8 @@ body: label: Expected behavior description: A clear and concise description of what you expected to happen. placeholder: Describe the expected behavior... + validations: + required: true - type: textarea id: environment diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 33e3e3aa..626c2a5e 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,11 +10,17 @@ build: os: ubuntu-22.04 tools: python: "3.11" + jobs: + pre_install: + # - curl -LsSf https://astral.sh/uv/install.sh | sh + - pip install uv + - uv export --only-group docs --no-hashes --no-color > requirements-docs.txt + post_install: + - pip install -r requirements-docs.txt + pre_build: + - make -C docs html # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py -python: - install: - - requirements: docs/requirements.txt diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2281f167..9efa4477 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,11 +18,115 @@ This section covers the changes between major version 2 and version 3. .. towncrier release notes start +Version 3.0.4 +============= + +:Released: 2025-01-24 +:Maintainer: Tom Schraitle + + +Bug Fixes +--------- + +* :gh:`459`: Fix 3.0.3: + + * :pr:`457`: Re-enable Trove license identifier + * :pr:`456`: Fix source dist file + + +---- + + +Version 3.0.3 +============= + +:Released: 2025-01-18 +:Maintainer: Tom Schraitle + + +Bug Fixes +--------- + +* :pr:`453`: The check in ``_comparator`` does not match the check in :meth:`Version.compare`. + This breaks comparision with subclasses. + + + +Improved Documentation +---------------------- + +* :pr:`435`: Several small improvements for documentation: + + * Add meta description to improve SEO + * Use canonicals on ReadTheDocs (commit 87f639f) + * Pin versions for reproducable doc builds (commit 03fb990) + * Add missing :file:`.readthedocs.yaml` file (commit ec9348a) + * Correct some smaller issues when building (commit f65feab) + +* :pr:`436`: Move search box more at the top. This makes it easier for + users as if the TOC is long, the search box isn't visible + anymore. + + + +Features +-------- + +* :pr:`439`: Improve type hints to fix TODOs + + + +Internal Changes +---------------- + +* :pr:`440`: Update workflow file + +* :pr:`446`: Add Python 3.13 to GitHub Actions + +* :pr:`447`: Modernize project configs with :file:`pyproject.toml` and + use Astral's uv command. + + * In :file:`pyproject.toml`: + + * Move all project related data from :file:`setup.cfg` to :file:`pyproject.toml` + * Use new dependency group from :pep:`735` + * Consolidate flake8, isort, pycodestyle with ruff + * Split towncrier config type "trivial" into "trivial" and "internal" + + * Create config file for ruff (:file:`.ruff.toml`) + * Create config file for pytest (:file:`.pytest.ini`) + * Simplify :file:`tox.ini` and remove old stuff + * Document installation with new :command:`uv` command + * Simplify Sphinx config with :func:`find_version()` + * Update the authors + * Use :command:`uv` in GitHub Action :file:`python-testing.yml` workflow + +* Update :file:`release-procedure.md`. + +* :pr:`451`: Turn our Markdown issue templates into YAML + + +Trivial Changes +--------------- + +* :pr:`438`: Replace organization placeholder in license + +* :pr:`445`: Improve private :func:`_nat_cmp` method: + + * Remove obsolete else. + * Find a better way to identify digits without the :mod:`re` module. + * Fix docstring in :meth:`Version.compare` + + + +---- + + Version 3.0.2 ============= :Released: 2023-10-09 -:Maintainer: +:Maintainer: Tom Schraitle Bug Fixes diff --git a/MANIFEST.in b/MANIFEST.in index e37851c9..fa2dcb20 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,26 @@ +include *.md include *.rst include *.txt -include tests/test_*.py +include CONTRIBUTORS +include CITATION.cff +include Makefile +include changelog.d/* +graft docs/** +include tests/*.py +include tox.ini +include .pytest.ini +include .ruff.toml +include uv.lock + +# The dot files: +include .coveragerc +include .editorconfig +include .gitignore +include .readthedocs.yaml + prune docs/_build recursive-exclude .github * +prune docs/**/__pycache__ global-exclude __pycache__ diff --git a/changelog.d/460.bugfix.rst b/changelog.d/460.bugfix.rst new file mode 100644 index 00000000..dc21db80 --- /dev/null +++ b/changelog.d/460.bugfix.rst @@ -0,0 +1,9 @@ +:meth:`~semver.version.Version.bump_prerelease` will now add `.0` to an +existing prerelease when the last segment of the current prerelease, split by +dots (`.`), is not numeric. This is to ensure the new prerelease is considered +higher than the previous one. + +:meth:`~semver.version.Version.bump_prerelease` now also support an argument +`bump_when_empty` which will bump the patch version if there is no existing +prerelease, to ensure the resulting version is considered a higher version than +the previous one. \ No newline at end of file diff --git a/changelog.d/463.trivial.rst b/changelog.d/463.trivial.rst new file mode 100644 index 00000000..3dcad2ae --- /dev/null +++ b/changelog.d/463.trivial.rst @@ -0,0 +1 @@ +Remove double code in :meth:`Version.bump_build` \ No newline at end of file diff --git a/changelog.d/pr435.doc.rst b/changelog.d/pr435.doc.rst deleted file mode 100644 index 87925c3d..00000000 --- a/changelog.d/pr435.doc.rst +++ /dev/null @@ -1,7 +0,0 @@ -Several small improvements for documentation: - -* Add meta description to improve SEO -* Use canonicals on ReadTheDocs (commit 87f639f) -* Pin versions for reproducable doc builds (commit 03fb990) -* Add missing :file:`.readthedocs.yaml` file (commit ec9348a) -* Correct some smaller issues when building (commit f65feab) diff --git a/changelog.d/pr436.doc.rst b/changelog.d/pr436.doc.rst deleted file mode 100644 index 5f078361..00000000 --- a/changelog.d/pr436.doc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Move search box more at the top. This makes it easier for -users as if the TOC is long, the search box isn't visible -anymore. diff --git a/changelog.d/pr447.internal.rst b/changelog.d/pr447.internal.rst deleted file mode 100644 index 2f1e367d..00000000 --- a/changelog.d/pr447.internal.rst +++ /dev/null @@ -1,15 +0,0 @@ -Modernize project configs with :file:`pyproject.toml` - -* In :file:`pyproject.toml`: - * Move all project related data from :file:`setup.cfg` to :file:`pyproject.toml` - * Use new dependency group from :pep:`735` - * Consolidate flake8, isort, pycodestyle with ruff - * Split towncrier config type "trivial" into "trivial" and - "internal" -* Create config file for ruff (:file:`.ruff.toml`) -* Create config file for pytest (:file:`.pytest.ini`) -* Simplify :file:`tox.ini` and remove old stuff -* Document installation with new :command:`uv` command -* Simplify Sphinx config with :func:`find_version()` -* Update the authors -* Use :command:`uv` in GitHub Action :file:`python-testing.yml` workflow diff --git a/docs/advanced/combine-pydantic-and-semver.rst b/docs/advanced/combine-pydantic-and-semver.rst index c7566e12..975fb418 100644 --- a/docs/advanced/combine-pydantic-and-semver.rst +++ b/docs/advanced/combine-pydantic-and-semver.rst @@ -9,7 +9,22 @@ According to its homepage, `Pydantic `_ "enforces type hints at runtime, and provides user friendly errors when data is invalid." -To work with Pydantic>2.0, use the following steps: +If you are working with Pydantic>2.0 and pydantic-extra-types>=2.10.5 use the built in `SemanticVersion` type, which wraps the :class:`Version ` class. + + .. code-block:: python + + from pydantic import BaseModel + from pydantic_extra_types.semantic_version import SemanticVersion + + class appVersion(BaseModel): + version: SemanticVersion + + app_version = appVersion(version="1.2.3") + + print(app_version.version) + # > 1.2.3 + +To work with Pydantic>2.0 and without pydantic-extra-types use the following example to define your own type: 1. Derive a new class from :class:`~semver.version.Version` diff --git a/docs/build-semver.rst b/docs/build-semver.rst index b5626d22..9c0441d8 100644 --- a/docs/build-semver.rst +++ b/docs/build-semver.rst @@ -7,58 +7,27 @@ Building semver :description lang=en: Building semver -.. _PEP 517: https://www.python.org/dev/peps/pep-0517/ -.. _PEP 621: https://www.python.org/dev/peps/pep-0621/ -.. _A Practical Guide to Setuptools and Pyproject.toml: https://godatadriven.com/blog/a-practical-guide-to-setuptools-and-pyproject-toml/ -.. _Declarative config: https://setuptools.rtfd.io/en/latest/userguide/declarative_config.html +.. _Installing uv: https://docs.astral.sh/uv/getting-started/installation/ -This project changed slightly its way how it is built. The reason for this -was to still support the "traditional" way with :command:`setup.py`, -but at the same time try out the newer way with :file:`pyproject.toml`. -As Python 3.6 got deprecated, this project does support from now on only -:file:`pyproject.toml`. +This project changed its way how it is built over time. We used to have +a :file:`setup.py` file, but switched to a :file:`pyproject.toml` setup. +The build process is managed by :command:`uv` command. -Background information ----------------------- +You need: -Skip this section and head over to :ref:`build-pyproject-build` if you just -want to know how to build semver. -This section gives some background information how this project is set up. +* Python 3.7 or newer. -The traditional way with :command:`setup.py` in this project uses a -`Declarative config`_. With this approach, the :command:`setup.py` is -stripped down to its bare minimum and all the metadata is stored in -:file:`setup.cfg`. +* The :mod:`setuptools` module version 61 or newer which is used as + a build backend. -The new :file:`pyproject.toml` contains only information about the build backend, currently setuptools.build_meta. The idea is taken from -`A Practical Guide to Setuptools and Pyproject.toml`_. -Setuptools-specific configuration keys as defined in `PEP 621`_ are currently -not used. +* The command :command:`uv` from Astral. Refer to the section + `Installing uv`_ for more information. -.. _build-pyproject-build: - -Building with pyproject-build ------------------------------ - -To build semver you need: - -* The :mod:`build` module which implements the `PEP 517`_ build - frontend. - Install it with:: - - pip install build - - Some Linux distributions has already packaged it. If you prefer - to use the module with your package manager, search for - :file:`python-build` or :file:`python3-build` and install it. - -* The command :command:`pyproject-build` from the :mod:`build` module. - To build semver, run:: - pyproject-build + uv build After the command is finished, you can find two files in the :file:`dist` folder: a ``.tar.gz`` and a ``.whl`` file. \ No newline at end of file diff --git a/docs/usage/raise-parts-of-a-version.rst b/docs/usage/raise-parts-of-a-version.rst index be89cf8d..101a8c35 100644 --- a/docs/usage/raise-parts-of-a-version.rst +++ b/docs/usage/raise-parts-of-a-version.rst @@ -3,13 +3,14 @@ Raising Parts of a Version .. note:: - Keep in mind, "raising" the pre-release only will make your - complete version *lower* than before. + Keep in mind, by default, "raising" the pre-release for a version without an existing + prerelease part, only will make your complete version *lower* than before. For example, having version ``1.0.0`` and raising the pre-release will lead to ``1.0.0-rc.1``, but ``1.0.0-rc.1`` is smaller than ``1.0.0``. - If you search for a way to take into account this behavior, look for the + To avoid this, set `bump_when_empty=True` in the + :meth:`~semver.version.Version.bump_prerelease` method, or by using the method :meth:`~semver.version.Version.next_version` in section :ref:`increase-parts-of-a-version`. @@ -67,4 +68,14 @@ is not taken into account: >>> str(Version.parse("3.4.5-rc.1").bump_prerelease('')) '3.4.5-rc.2' +To ensure correct ordering, we append `.0` to the last prerelease identifier +if it's not numeric. This prevents cases where `rc9` would incorrectly sort +lower than `rc10` (non-numeric identifiers are compared alphabetically): + +.. code-block:: python + + >>> str(Version.parse("3.4.5-rc9").bump_prerelease()) + '3.4.5-rc9.0' + >>> str(Version.parse("3.4.5-rc.9").bump_prerelease()) + '3.4.5-rc.10' diff --git a/docs/usage/semver-version.rst b/docs/usage/semver-version.rst index 51165293..c771a006 100644 --- a/docs/usage/semver-version.rst +++ b/docs/usage/semver-version.rst @@ -4,4 +4,4 @@ Getting the Version of semver To know the version of semver itself, use the following construct:: >>> semver.__version__ - '3.0.3-alpha.1' + '3.0.4' diff --git a/pyproject.toml b/pyproject.toml index 6c9cb93a..60481679 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ [build-system] requires = [ - # sync with setup.py until we discard non-pep-517/518 "setuptools>=61", # "setuptools-scm>=8", ] @@ -19,6 +18,8 @@ requires-python = ">=3.7" name = "semver" description = "Python helper for Semantic Versioning (https://semver.org)" readme = "README.rst" +# PEP 639 +# licence = "BSD-2-Clause" # readme.content-type = "text/x-rst" license = { file = "LICENSE.txt" } authors = [ @@ -92,6 +93,7 @@ docs = [ "sphinx", # ==7.0.1 "sphinx-argparse", # ==0.4.0 "sphinx-autodoc-typehints", # ==1.24.0 + "restview", ] devel = [ {include-group = "typing"}, diff --git a/release-procedure.md b/release-procedure.md index 4225bfd9..f6c7c377 100644 --- a/release-procedure.md +++ b/release-procedure.md @@ -3,120 +3,142 @@ The following procedures gives a short overview of what steps are needed to create a new release. -## Prepare the Release +## Prepare your environment -1. Verify: +1. Create your API tokens: - * all issues for a new release are closed: . + 1. From the [PyPI test server](https://test.pypi.org/manage/account/token/). - * that all pull requests that should be included in this release are merged: . + 1. From the official [PyPI server](https://test.pypi.org/manage/account/token/). - * that continuous integration for latest build was passing: - . + 1. Save both tokens it in a safe place like your password manager. + +1. Create a file `~/.pypirc` with file mode 0600 and the following minimal content: + + # Protect the file with chmod 0600 ~/.pypirc + [distutils] + index-servers = + test-semver + semver + + [test-semver] + repository = https://test.pypi.org/legacy/ + username = __token__ + password = + + [semver] + repository = https://pypi.org/legacy/ + username = __token__ + password = + +1. Install uv as shown in Astral's [Installing uv](https://docs.astral.sh/uv/getting-started/installation/) documentation. + +1. Update the project's environment: + + uv sync --group devel + +1. Activate your environment: + + source .venv/bin/activate + +## Prepare the Release 1. Create a new branch `release/`. -1. If one or several supported Python versions have been removed or added, verify that the 3 following files have been updated: - * `setup.cfg` +1. If one or several supported **Python** versions have been removed or added, verify that the following files have been updated: + * `pyproject.toml` (look into the key `project.requires-python` and `project.classifiers`) * `tox.ini` * `.git/workflows/pythonpackage.yml` * `CITATION.cff` -1. Verify that the version has been updated and follow - : +1. Verify that: + + * the version has been updated and follow : + * `src/semver/__about__.py` + * `docs/usage/semver-version.rst` + + * all issues for a new release are closed: . - * `src/semver/__about__.py` - * `docs/usage/semver-version.rst` + * all pull requests that should be included in this release are merged: . + + * continuous integration for latest build was passing: + . 1. Add eventually new contributor(s) to [CONTRIBUTORS](https://github.com/python-semver/python-semver/blob/master/CONTRIBUTORS). +1. Create the changelog: -1. Check if all changelog entries are created. If some are missing, [create them](https://python-semver.readthedocs.io/en/latest/development.html#adding-a-changelog-entry). + 1. Check if all changelog entries are created. If some are missing, [create them](https://python-semver.readthedocs.io/en/latest/development.html#adding-a-changelog-entry). -1. Show the new draft [CHANGELOG](https://github.com/python-semver/python-semver/blob/master/CHANGELOG.rst) entry for the latest release with: + 1. Show the new draft [CHANGELOG](https://github.com/python-semver/python-semver/blob/master/CHANGELOG.rst) entry for the latest release with: - $ tox -e changelog + uvx tox r -e changelog - Check the output. If you are not happy, update the files in the - `changelog.d/` directory. - If everything is okay, build the new `CHANGELOG` with: + 1. Check the output. If you are not happy, update the files in the + `changelog.d/` directory. + If everything is okay, build the new `CHANGELOG` with: - $ tox -e changelog -- build + uvx tox r -e changelog -- build 1. Build the documentation and check the output: - $ tox -e docs + uvx tox r -e docs 1. Commit all changes, push, and create a pull request. ## Create the New Release -1. Ensure that long description ([README.rst](https://github.com/python-semver/python-semver/blob/master/README.rst)) can be correctly rendered by Pypi using `restview --long-description` +1. Ensure that long description ([README.rst](https://github.com/python-semver/python-semver/blob/master/README.rst)) can be correctly rendered by Pypi using `uvx restview -b README.rst` 1. Clean up your local Git repository. Be careful, as it **will remove all files** which are not versioned by Git: - $ git clean -xfd + git clean -xfd Before you create your distribution files, clean the directory too: - $ rm dist/* + rm dist/* 1. Create the distribution files (wheel and source): - $ tox -e prepare-dist + uvx tox r -e prepare-dist 1. Upload the wheel and source to TestPyPI first: - ```bash - $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* - ``` + twine upload --verbose --repository test-semver dist/* - If you have a `~/.pypirc` with a `testpypi` section, the upload can be - simplified: - - $ twine upload --repository testpypi dist/* + (Normally you would do it with `uv publish`, but for some unknown reason it didn't work for me.) 1. Check if everything is okay with the wheel. Check also the web site `https://test.pypi.org/project//` +# Finish the release + 1. If everything looks fine, merge the pull request. 1. Upload to PyPI: - ```bash - $ git clean -xfd - $ tox -e prepare-dist - $ twine upload dist/* - ``` + $ git clean -xfd + $ tox r -e prepare-dist + $ twine upload --verbose --repository semver dist/* 1. Go to https://pypi.org/project/semver/ to verify that new version is online and the page is rendered correctly. -# Finish the release - 1. Create a tag: - ```bash - $ git tag -a x.y.z - ``` + git tag -a x.y.z - It's recommended to use the generated Tox output - from the Changelog. + It's recommended to use the generated Tox output from the Changelog. 1. Push the tag: - ```bash - $ git push origin x.y.z - ``` + git push origin x.y.z -1. In [GitHub Release page](https://github.com/python-semver/python-semver/release) - document the new release. - Select the tag from the last step and copy the - content of the tag description into the release - description. +1. In [GitHub Release page](https://github.com/python-semver/python-semver/release) document the new release. + Select the tag from the last step and copy the content of the tag description into the release description. 1. Announce it in . diff --git a/src/semver/__about__.py b/src/semver/__about__.py index 697372f8..21494e68 100644 --- a/src/semver/__about__.py +++ b/src/semver/__about__.py @@ -16,7 +16,7 @@ """ #: Semver version -__version__ = "3.0.3-alpha.1" +__version__ = "3.0.4" #: Original semver author __author__ = "Kostiantyn Rybnikov" diff --git a/src/semver/version.py b/src/semver/version.py index 2f1f8cb7..f9450f95 100644 --- a/src/semver/version.py +++ b/src/semver/version.py @@ -41,7 +41,7 @@ def _comparator(operator: Comparator) -> Comparator: @wraps(operator) def wrapper(self: "Version", other: Comparable) -> bool: comparable_types = ( - Version, + type(self), dict, tuple, list, @@ -77,8 +77,10 @@ class Version: #: The names of the different parts of a version NAMES: ClassVar[Tuple[str, ...]] = tuple([item[1:] for item in __slots__]) - #: Regex for number in a prerelease + #: Regex for number in a build _LAST_NUMBER: ClassVar[Pattern[str]] = re.compile(r"(?:[^\d]*(\d+)[^\d]*)+") + #: Regex for number in a prerelease + _LAST_PRERELEASE: ClassVar[Pattern[str]] = re.compile(r"^(.*\.)?(\d+)$") #: Regex template for a semver version _REGEX_TEMPLATE: ClassVar[ str @@ -245,6 +247,23 @@ def __iter__(self) -> VersionIterator: """Return iter(self).""" yield from self.to_tuple() + @staticmethod + def _increment_prerelease(string: str) -> str: + """ + Check if the last part of a dot-separated string is numeric. If yes, + increase them. Else, add '.0' + + :param string: the prerelease version to increment + :return: the incremented string + """ + match = Version._LAST_PRERELEASE.search(string) + if match: + next_ = str(int(match.group(2)) + 1) + string = match.group(1) + next_ if match.group(1) else next_ + else: + string += ".0" + return string + @staticmethod def _increment_string(string: str) -> str: """ @@ -305,35 +324,52 @@ def bump_patch(self) -> "Version": cls = type(self) return cls(self._major, self._minor, self._patch + 1) - def bump_prerelease(self, token: Optional[str] = "rc") -> "Version": + def bump_prerelease( + self, + token: Optional[str] = "rc", + bump_when_empty: Optional[bool] = False + ) -> "Version": """ Raise the prerelease part of the version, return a new object but leave self untouched. + .. versionchanged:: 3.1.0 + Parameter `bump_when_empty` added. When set to true, bumps the patch version + when called with a version that has no prerelease segment, so the return + value will be considered a newer version. + + Adds `.0` to the prerelease if the last part of the dot-separated + prerelease is not a number. + :param token: defaults to ``'rc'`` :return: new :class:`Version` object with the raised prerelease part. The original object is not modified. >>> ver = semver.parse("3.4.5") >>> ver.bump_prerelease().prerelease - 'rc.2' + 'rc.1' >>> ver.bump_prerelease('').prerelease '1' >>> ver.bump_prerelease(None).prerelease 'rc.1' + >>> str(ver.bump_prerelease(bump_when_empty=True)) + '3.4.6-rc.1' """ cls = type(self) + patch = self._patch if self._prerelease is not None: - prerelease = self._prerelease - elif token == "": - prerelease = "0" - elif token is None: - prerelease = "rc.0" + prerelease = cls._increment_prerelease(self._prerelease) else: - prerelease = str(token) + ".0" + if bump_when_empty: + patch += 1 + if token == "": + prerelease = "1" + elif token is None: + prerelease = "rc.1" + else: + prerelease = str(token) + ".1" - prerelease = cls._increment_string(prerelease) - return cls(self._major, self._minor, self._patch, prerelease) + return cls(self._major, self._minor, patch, prerelease) def bump_build(self, token: Optional[str] = "build") -> "Version": """ @@ -359,18 +395,6 @@ def bump_build(self, token: Optional[str] = "build") -> "Version": else: build = str(token) + ".0" - # self._build or (token or "build") + ".0" - build = cls._increment_string(build) - if self._build is not None: - build = self._build - elif token == "": - build = "0" - elif token is None: - build = "build.0" - else: - build = str(token) + ".0" - - # self._build or (token or "build") + ".0" build = cls._increment_string(build) return cls(self._major, self._minor, self._patch, self._prerelease, build) @@ -457,10 +481,8 @@ def next_version(self, part: str, prerelease_token: str = "rc") -> "Version": # Only check the main parts: if part in cls.NAMES[:3]: return getattr(version, "bump_" + part)() - - if not version.prerelease: - version = version.bump_patch() - return version.bump_prerelease(prerelease_token) + else: + return version.bump_prerelease(prerelease_token, bump_when_empty=True) @_comparator def __eq__(self, other: Comparable) -> bool: # type: ignore diff --git a/tests/test_bump.py b/tests/test_bump.py index 34e0b2ac..fcbedf4c 100644 --- a/tests/test_bump.py +++ b/tests/test_bump.py @@ -6,6 +6,7 @@ bump_minor, bump_patch, bump_prerelease, + compare, parse_version_info, ) @@ -32,62 +33,101 @@ def test_should_versioninfo_bump_minor_and_patch(): v = parse_version_info("3.4.5") expected = parse_version_info("3.5.1") assert v.bump_minor().bump_patch() == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_patch_and_prerelease(): v = parse_version_info("3.4.5-rc.1") expected = parse_version_info("3.4.6-rc.1") assert v.bump_patch().bump_prerelease() == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_patch_and_prerelease_with_token(): v = parse_version_info("3.4.5-dev.1") expected = parse_version_info("3.4.6-dev.1") assert v.bump_patch().bump_prerelease("dev") == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_prerelease_and_build(): v = parse_version_info("3.4.5-rc.1+build.1") expected = parse_version_info("3.4.5-rc.2+build.2") assert v.bump_prerelease().bump_build() == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_prerelease_and_build_with_token(): v = parse_version_info("3.4.5-rc.1+b.1") expected = parse_version_info("3.4.5-rc.2+b.2") assert v.bump_prerelease().bump_build("b") == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_multiple(): v = parse_version_info("3.4.5-rc.1+build.1") expected = parse_version_info("3.4.5-rc.2+build.2") assert v.bump_prerelease().bump_build().bump_build() == expected + assert v.compare(expected) == -1 expected = parse_version_info("3.4.5-rc.3") assert v.bump_prerelease().bump_build().bump_build().bump_prerelease() == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_prerelease_with_empty_str(): v = parse_version_info("3.4.5") expected = parse_version_info("3.4.5-1") assert v.bump_prerelease("") == expected + assert v.compare(expected) == 1 def test_should_versioninfo_bump_prerelease_with_none(): v = parse_version_info("3.4.5") expected = parse_version_info("3.4.5-rc.1") assert v.bump_prerelease(None) == expected + assert v.compare(expected) == 1 + + +def test_should_versioninfo_bump_prerelease_nonnumeric(): + v = parse_version_info("3.4.5-rc1") + expected = parse_version_info("3.4.5-rc1.0") + assert v.bump_prerelease(None) == expected + assert v.compare(expected) == -1 + + +def test_should_versioninfo_bump_prerelease_nonnumeric_nine(): + v = parse_version_info("3.4.5-rc9") + expected = parse_version_info("3.4.5-rc9.0") + assert v.bump_prerelease(None) == expected + assert v.compare(expected) == -1 + + +def test_should_versioninfo_bump_prerelease_bump_patch(): + v = parse_version_info("3.4.5") + expected = parse_version_info("3.4.6-rc.1") + assert v.bump_prerelease(bump_when_empty=True) == expected + assert v.compare(expected) == -1 + + +def test_should_versioninfo_bump_patch_and_prerelease_bump_patch(): + v = parse_version_info("3.4.5") + expected = parse_version_info("3.4.7-rc.1") + assert v.bump_patch().bump_prerelease(bump_when_empty=True) == expected + assert v.compare(expected) == -1 def test_should_versioninfo_bump_build_with_empty_str(): v = parse_version_info("3.4.5") expected = parse_version_info("3.4.5+1") assert v.bump_build("") == expected + assert v.compare(expected) == 0 def test_should_versioninfo_bump_build_with_none(): v = parse_version_info("3.4.5") expected = parse_version_info("3.4.5+build.1") assert v.bump_build(None) == expected + assert v.compare(expected) == 0 def test_should_ignore_extensions_for_bump(): @@ -95,18 +135,18 @@ def test_should_ignore_extensions_for_bump(): @pytest.mark.parametrize( - "version,token,expected", + "version,token,expected,expected_compare", [ - ("3.4.5-rc.9", None, "3.4.5-rc.10"), - ("3.4.5", None, "3.4.5-rc.1"), - ("3.4.5", "dev", "3.4.5-dev.1"), - ("3.4.5", "", "3.4.5-rc.1"), + ("3.4.5-rc.9", None, "3.4.5-rc.10", -1), + ("3.4.5", None, "3.4.5-rc.1", 1), + ("3.4.5", "dev", "3.4.5-dev.1", 1), + ("3.4.5", "", "3.4.5-rc.1", 1), ], ) -def test_should_bump_prerelease(version, token, expected): +def test_should_bump_prerelease(version, token, expected, expected_compare): token = "rc" if not token else token assert bump_prerelease(version, token) == expected - + assert compare(version, expected) == expected_compare def test_should_ignore_build_on_prerelease_bump(): assert bump_prerelease("3.4.5-rc.1+build.4") == "3.4.5-rc.2" @@ -123,3 +163,4 @@ def test_should_ignore_build_on_prerelease_bump(): ) def test_should_bump_build(version, expected): assert bump_build(version) == expected + assert compare(version, expected) == 0 \ No newline at end of file diff --git a/tests/test_pysemver-cli.py b/tests/test_pysemver-cli.py index e783a0b4..5a0a2f82 100644 --- a/tests/test_pysemver-cli.py +++ b/tests/test_pysemver-cli.py @@ -55,7 +55,7 @@ def test_should_parse_cli_arguments(cli, expected): ( cmd_bump, Namespace(bump="prerelease", version="1.2.3-rc1"), - does_not_raise("1.2.3-rc2"), + does_not_raise("1.2.3-rc1.0"), ), ( cmd_bump, diff --git a/tests/test_subclass.py b/tests/test_subclass.py index b33f4969..0d390009 100644 --- a/tests/test_subclass.py +++ b/tests/test_subclass.py @@ -1,4 +1,5 @@ from semver import Version +import pytest def test_subclass_from_versioninfo(): @@ -51,3 +52,20 @@ def __str__(self) -> str: dev_version = version.replace(prerelease="dev.0") assert str(dev_version) == "v1.1.0-dev.0" + + +def test_compare_with_subclass(): + class SemVerSubclass(Version): + pass + + with pytest.raises(TypeError): + SemVerSubclass.parse("1.0.0").compare(Version.parse("1.0.0")) + assert Version.parse("1.0.0").compare(SemVerSubclass.parse("1.0.0")) == 0 + + assert ( + SemVerSubclass.parse("1.0.0").__eq__(Version.parse("1.0.0")) is NotImplemented + ) + assert Version.parse("1.0.0").__eq__(SemVerSubclass.parse("1.0.0")) is True + + assert SemVerSubclass.parse("1.0.0") == Version.parse("1.0.0") + assert Version.parse("1.0.0") == SemVerSubclass.parse("1.0.0") diff --git a/tox.ini b/tox.ini index 74918c44..d39ce81a 100644 --- a/tox.ini +++ b/tox.ini @@ -29,6 +29,7 @@ deps = setuptools-scm setenv = PIP_DISABLE_PIP_VERSION_CHECK = 1 +downloads = true [testenv:mypy] @@ -63,6 +64,7 @@ skip_install = true allowlist_externals = make echo + uvx commands = uvx make -C docs html commands_post = diff --git a/uv.lock b/uv.lock index 026a5848..c14d1363 100644 --- a/uv.lock +++ b/uv.lock @@ -278,7 +278,7 @@ name = "click" version = "8.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } @@ -876,16 +876,16 @@ wheels = [ [[package]] name = "readme-renderer" -version = "37.3" +version = "36.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bleach" }, { name = "docutils" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/c3/d20152fcd1986117b898f66928938f329d0c91ddc47f081c58e64e0f51dc/readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273", size = 29718 } +sdist = { url = "https://files.pythonhosted.org/packages/15/4e/0ffa80eb3e0d0fcc0c6b901b36d4faa11c47d10b9a066fdd42f24c7e646a/readme_renderer-36.0.tar.gz", hash = "sha256:f71aeef9a588fcbed1f4cc001ba611370e94a0cd27c75b1140537618ec78f0a2", size = 28680 } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/52/fd8a77d6f0a9ddeb26ed8fb334e01ac546106bf0c5b8e40dc826c5bd160f/readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343", size = 14055 }, + { url = "https://files.pythonhosted.org/packages/0a/3e/ce07c86adbc46cfedf637dd77333184bcd0a913f9c169b0b3afc25f67b6b/readme_renderer-36.0-py3-none-any.whl", hash = "sha256:2c37e472ca96755caba6cc58bcbf673a5574bc033385a2ac91d85dfef2799876", size = 14076 }, ] [[package]] @@ -915,6 +915,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, ] +[[package]] +name = "restview" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "pygments" }, + { name = "readme-renderer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/d4/36ed06051e9702d3dae5f7bb0296b79b18621ff5a1bf43247509cbfeff8d/restview-3.0.2.tar.gz", hash = "sha256:8b4d75a0bed76b67b456ef7011f4eb6c98a556c9f837642df2202c7312fccc1a", size = 50048 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/69/4046cbec75d4574e84fa3b2be53487ae9054129527211961555631805849/restview-3.0.2-py3-none-any.whl", hash = "sha256:9fbd26507f6ddc436a49d0ffced5bc8102a898f289fe53c1234a17be46eb660d", size = 38020 }, +] + [[package]] name = "rfc3986" version = "2.0.0" @@ -978,7 +992,6 @@ wheels = [ [[package]] name = "semver" -version = "3.0.3a1" source = { editable = "." } [package.dev-dependencies] @@ -993,6 +1006,7 @@ devel = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "restview" }, { name = "ruff" }, { name = "sphinx" }, { name = "sphinx-argparse" }, @@ -1002,6 +1016,7 @@ devel = [ { name = "twine" }, ] docs = [ + { name = "restview" }, { name = "sphinx" }, { name = "sphinx-argparse" }, { name = "sphinx-autodoc-typehints" }, @@ -1019,6 +1034,7 @@ gh-action = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "restview" }, { name = "ruff" }, { name = "sphinx" }, { name = "sphinx-argparse" }, @@ -1054,6 +1070,7 @@ devel = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "restview" }, { name = "ruff" }, { name = "sphinx" }, { name = "sphinx-argparse" }, @@ -1063,6 +1080,7 @@ devel = [ { name = "twine" }, ] docs = [ + { name = "restview" }, { name = "sphinx" }, { name = "sphinx-argparse" }, { name = "sphinx-autodoc-typehints" }, @@ -1080,6 +1098,7 @@ gh-action = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "restview" }, { name = "ruff" }, { name = "sphinx" }, { name = "sphinx-argparse" }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy