diff --git a/.gitignore b/.gitignore index c0859cd3..dead3352 100644 --- a/.gitignore +++ b/.gitignore @@ -268,3 +268,6 @@ fabric.properties docs/_api !docs/_api/semver.__about__.rst + +# For node +node_modules/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7f515aa1..28a401e2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,8 @@ Changes for the upcoming release can be found in the `"changelog.d" directory `_ in our repository. +This section covers the changes between major version 2 and version 3. + .. Do *NOT* add changelog entries here! @@ -16,65 +18,108 @@ in our repository. .. towncrier release notes start -Version 3.0.0-dev.4 -=================== +Version 3.0.0 +============= -:Released: 2022-12-18 -:Maintainer: +:Released: 2023-03-19 +:Maintainer: Tom Schraitle Bug Fixes --------- +* :gh:`291`: Disallow negative numbers in VersionInfo arguments + for ``major``, ``minor``, and ``patch``. + +* :gh:`310`: Rework API documentation. + Follow a more "semi-manual" attempt and add auto directives + into :file:`docs/api.rst`. + +* :gh:`344`: Allow empty string, a string with a prefix, or ``None`` + as token in + :meth:`~semver.version.Version.bump_build` and + :meth:`~semver.version.Version.bump_prerelease`. + * :gh:`374`: Correct Towncrier's config entries in the :file:`pyproject.toml` file. The old entries ``[[tool.towncrier.type]]`` are deprecated and need to be replaced by ``[tool.towncrier.fragment.]``. +* :pr:`384`: General cleanup, reformat files: + * Reformat source code with black again as some config options + did accidentely exclude the semver source code. + Mostly remove some includes/excludes in the black config. + * Integrate concurrency in GH Action + * Ignore Python files on project dirs in .gitignore + * Remove unused patterns in MANIFEST.in + * Use ``extend-exclude`` for flake in :file:`setup.cfg`` and adapt list. + * Use ``skip_install=True`` in :file:`tox.ini` for black -Deprecations ------------- +* :pr:`393`: Fix command :command:`python -m semver` to avoid the error "invalid choice" -* :gh:`372`: Deprecate support for Python 3.6. +* :pr:`396`: Calling :meth:`~semver.version.Version.parse` on a derived class will show correct type of derived class. - Python 3.6 reached its end of life and isn't supported anymore. - At the time of writing (Dec 2022), the lowest version is 3.7. - Although the `poll `_ - didn't cast many votes, the majority agree to remove support for - Python 3.6. +Deprecations +------------ +* :gh:`169`: Deprecate CLI functions not imported from ``semver.cli``. +* :gh:`234`: In :file:`setup.py` simplified file and remove + ``Tox`` and ``Clean`` classes -Improved Documentation ----------------------- +* :gh:`284`: Deprecate the use of :meth:`~Version.isvalid`. -* :gh:`335`: Add new section "Converting versions between PyPI and semver" the limitations - and possible use cases to convert from one into the other versioning scheme. + Rename :meth:`~semver.version.Version.isvalid` + to :meth:`~semver.version.Version.is_valid` + for consistency reasons with :meth:`~semver.version.Version.is_compatible`. -* :gh:`340`: Describe how to get version from a file -* :gh:`343`: Describe combining Pydantic with semver in the "Advanced topic" - section. +* :pr:`290`: For semver 3.0.0-alpha0 deprecated: -* :gh:`350`: Restructure usage section. Create subdirectory "usage/" and splitted - all section into different files. + * Remove anything related to Python2 + * In :file:`tox.ini` and :file:`.travis.yml` + Remove targets py27, py34, py35, and pypy. + Add py38, py39, and nightly (allow to fail) + * In :file:`setup.py` simplified file and remove + ``Tox`` and ``Clean`` classes + * Remove old Python versions (2.7, 3.4, 3.5, and pypy) + from Travis -* :gh:`351`: Introduce new topics for: +* :gh:`372`: Deprecate support for Python 3.6. - * "Migration to semver3" - * "Advanced topics" + Python 3.6 reached its end of life and isn't supported anymore. + At the time of writing (Dec 2022), the lowest version is 3.7. + Although the `poll `_ + didn't cast many votes, the majority agreed to remove support for + Python 3.6. Features -------- -* :pr:`359`: Add optional parameter ``optional_minor_and_patch`` in :meth:`.Version.parse` to allow optional +* :gh:`169`: Create semver package and split code among different modules in the packages: + + * Remove :file:`semver.py` + * Create :file:`src/semver/__init__.py` + * Create :file:`src/semver/cli.py` for all CLI methods + * Create :file:`src/semver/_deprecated.py` for the ``deprecated`` decorator and other deprecated functions + * Create :file:`src/semver/__main__.py` to allow calling the CLI using :command:`python -m semver` + * Create :file:`src/semver/_types.py` to hold type aliases + * Create :file:`src/semver/version.py` to hold the :class:`Version` class (old name :class:`VersionInfo`) and its utility functions + * Create :file:`src/semver/__about__.py` for all the metadata variables + +* :gh:`213`: Add typing information + +* :gh:`284`: Implement :meth:`~semver.version.Version.is_compatible` to make "is self compatible with X". + +* :gh:`305`: Rename :class:`~semver.version.VersionInfo` to :class:`~semver.version.Version` but keep an alias for compatibility + +* :pr:`359`: Add optional parameter ``optional_minor_and_patch`` in :meth:`~semver.version.Version.parse` to allow optional minor and patch parts. -* :pr:`362`: Make :meth:`.Version.match` accept a bare version string as match expression, defaulting to - equality testing. +* :pr:`362`: Make :meth:`~semver.version.Version.match` accept a bare version string as match expression, defaulting to equality testing. * :gh:`364`: Enhance :file:`pyproject.toml` to make it possible to use the :command:`pyproject-build` command from the build module. @@ -95,34 +140,28 @@ Features -Trivial/Internal Changes ------------------------- - -* :gh:`378`: Fix some typos in Towncrier configuration - - - ----- - - -Version 3.0.0-dev.3 -=================== +Improved Documentation +---------------------- -:Released: 2022-01-19 -:Maintainer: Tom Schraitle +* :gh:`276`: Document how to create a sublass from :class:`~semver.version.VersionInfo` class +* :gh:`284`: Document deprecation of :meth:`~semver.version.Version.isvalid`. -Bug Fixes ---------- +* :pr:`290`: Several improvements in the documentation: -* :gh:`310`: Rework API documentation. - Follow a more "semi-manual" attempt and add auto directives - into :file:`docs/api.rst`. + * New layout to distinguish from the semver2 development line. + * Create new logo. + * Remove any occurances of Python2. + * Describe changelog process with Towncrier. + * Update the release process. +* :gh:`304`: Several improvements in documentation: + * Reorganize API documentation. + * Add migration chapter from semver2 to semver3. + * Distinguish between changlog for version 2 and 3 -Improved Documentation ----------------------- +* :gh:`305`: Add note about :class:`~semver.version.Version` rename. * :gh:`312`: Rework "Usage" section. @@ -130,109 +169,31 @@ Improved Documentation :class:`~semver.version.Version` class * Remove semver. prefix in doctests to make examples shorter * Correct some references to dunder methods like - :func:`~.semver.version.Version.__getitem__`, - :func:`~.semver.version.Version.__gt__` etc. + :func:`~semver.version.Version.__getitem__`, + :func:`~semver.version.Version.__gt__` etc. * Remove inconsistencies and mention module level function as deprecated and discouraged from using * Make empty :py:func:`super` call in :file:`semverwithvprefix.py` example * :gh:`315`: Improve release procedure text +* :gh:`335`: Add new section "Converting versions between PyPI and semver" the limitations + and possible use cases to convert from one into the other versioning scheme. +* :gh:`340`: Describe how to get version from a file -Trivial/Internal Changes ------------------------- - -* :gh:`309`: Some (private) functions from the :mod:`semver.version` - module has been changed. - - The following functions got renamed: - - * function ``semver.version.comparator`` got renamed to - :func:`semver.version._comparator` as it is only useful - inside the :class:`~semver.version.Version` class. - * function ``semver.version.cmp`` got renamed to - :func:`semver.version._cmp` as it is only useful - inside the :class:`~semver.version.Version` class. - - The following functions got integrated into the - :class:`~semver.version.Version` class: - - * function ``semver.version._nat_cmd`` as a classmethod - * function ``semver.version.ensure_str`` - -* :gh:`313`: Correct :file:`tox.ini` for ``changelog`` entry to skip - installation for semver. This should speed up the execution - of towncrier. - -* :gh:`316`: Comparisons of :class:`~semver.version.Version` class and other - types return now a :py:const:`NotImplemented` constant instead - of a :py:exc:`TypeError` exception. - - The `NotImplemented`_ section of the Python documentation recommends - returning this constant when comparing with ``__gt__``, ``__lt__``, - and other comparison operators to "to indicate that the operation is - not implemented with respect to the other type". - - .. _NotImplemented: https://docs.python.org/3/library/constants.html#NotImplemented - -* :gh:`319`: Introduce stages in :file:`.travis.yml` - The config file contains now two stages: check and test. If - check fails, the test stage won't be executed. This could - speed up things when some checks fails. - -* :gh:`322`: Switch from Travis CI to GitHub Actions. - -* :gh:`347`: Support Python 3.10 in GitHub Action and other config files. - - - ----- - - -Version 3.0.0-dev.2 -=================== - -:Released: 2020-11-01 -:Maintainer: Tom Schraitle - - -Deprecations ------------- - -* :gh:`169`: Deprecate CLI functions not imported from ``semver.cli``. - - - -Features --------- - -* :gh:`169`: Create semver package and split code among different modules in the packages. - - * Remove :file:`semver.py` - * Create :file:`src/semver/__init__.py` - * Create :file:`src/semver/cli.py` for all CLI methods - * Create :file:`src/semver/_deprecated.py` for the ``deprecated`` decorator and other deprecated functions - * Create :file:`src/semver/__main__.py` to allow calling the CLI using :command:`python -m semver` - * Create :file:`src/semver/_types.py` to hold type aliases - * Create :file:`src/semver/version.py` to hold the :class:`Version` class (old name :class:`VersionInfo`) and its utility functions - * Create :file:`src/semver/__about__.py` for all the metadata variables - -* :gh:`305`: Rename :class:`VersionInfo` to :class:`Version` but keep an alias for compatibility - - - -Improved Documentation ----------------------- +* :gh:`343`: Describe combining Pydantic with semver in the "Advanced topic" + section. -* :gh:`304`: Several improvements in documentation: +* :gh:`350`: Restructure usage section. Create subdirectory "usage/" and splitted + all section into different files. - * Reorganize API documentation. - * Add migration chapter from semver2 to semver3. - * Distinguish between changlog for version 2 and 3 +* :gh:`351`: Introduce new topics for: -* :gh:`305`: Add note about :class:`Version` rename. + * "Migration to semver3" + * "Advanced topics" +* :pr:`392`: Fix the example in the documentation for combining semver and pydantic. Trivial/Internal Changes @@ -247,6 +208,8 @@ Trivial/Internal Changes Increase coverage to 100% for all non-deprecated APIs +* :pr:`290`: Add supported Python versions to :command:`black`. + * :gh:`304`: Support PEP-561 :file:`py.typed`. According to the mentioned PEP: @@ -258,96 +221,61 @@ Trivial/Internal Changes Add package_data to :file:`setup.cfg` to include this marker in dist and whl file. +* :gh:`309`: Some (private) functions from the :mod:`semver.version` + module has been changed. + The following functions got renamed: ----- - - -Version 3.0.0-dev.1 -=================== - -:Released: 2020-10-26 -:Maintainer: Tom Schraitle - - -Deprecations ------------- - -* :pr:`290`: For semver 3.0.0-alpha0: - - * Remove anything related to Python2 - * In :file:`tox.ini` and :file:`.travis.yml` - Remove targets py27, py34, py35, and pypy. - Add py38, py39, and nightly (allow to fail) - * In :file:`setup.py` simplified file and remove - ``Tox`` and ``Clean`` classes - * Remove old Python versions (2.7, 3.4, 3.5, and pypy) - from Travis - -* :gh:`234`: In :file:`setup.py` simplified file and remove - ``Tox`` and ``Clean`` classes - - + * function :func:`semver.version.comparator` got renamed to + :func:`semver.version._comparator` as it is only useful + inside the :class:`~semver.version.Version` class. + * function :func:`semver.version.cmp` got renamed to + :func:`semver.version._cmp` as it is only useful + inside the :class:`~semver.version.Version` class. -Features --------- + The following functions got integrated into the + :class:`~semver.version.Version` class: -* :pr:`290`: Create semver 3.0.0-alpha0 - - * Update :file:`README.rst`, mention maintenance - branch ``maint/v2``. - * Remove old code mainly used for Python2 compatibility, - adjusted code to support Python3 features. - * Split test suite into separate files under :file:`tests/` - directory - * Adjust and update :file:`setup.py`. Requires Python >=3.6.* - Extract metadata directly from source (affects all the ``__version__``, - ``__author__`` etc. variables) - -* :gh:`270`: Configure Towncrier (:pr:`273`:) - - * Add :file:`changelog.d/.gitignore` to keep this directory - * Create :file:`changelog.d/README.rst` with some descriptions - * Add :file:`changelog.d/_template.rst` as Towncrier template - * Add ``[tool.towncrier]`` section in :file:`pyproject.toml` - * Add "changelog" target into :file:`tox.ini`. Use it like - :command:`tox -e changelog -- CMD` whereas ``CMD`` is a - Towncrier command. The default :command:`tox -e changelog` - calls Towncrier to create a draft of the changelog file - and output it to stdout. - * Update documentation and add include a new section - "Changelog" included from :file:`changelog.d/README.rst`. - -* :gh:`276`: Document how to create a sublass from :class:`VersionInfo` class + * function :func:`semver.version._nat_cmd` as a classmethod + * function :func:`semver.version.ensure_str` -* :gh:`213`: Add typing information +* :gh:`313`: Correct :file:`tox.ini` for ``changelog`` entry to skip + installation for semver. This should speed up the execution + of towncrier. +* :gh:`316`: Comparisons of :class:`~semver.version.Version` class and other + types return now a :py:const:`NotImplemented` constant instead + of a :py:exc:`TypeError` exception. -Bug Fixes ---------- + The `NotImplemented`_ section of the Python documentation recommends + returning this constant when comparing with ``__gt__``, ``__lt__``, + and other comparison operators to "to indicate that the operation is + not implemented with respect to the other type". -* :gh:`291`: Disallow negative numbers in VersionInfo arguments - for ``major``, ``minor``, and ``patch``. + .. _NotImplemented: https://docs.python.org/3/library/constants.html#NotImplemented +* :gh:`319`: Introduce stages in :file:`.travis.yml` + The config file contains now two stages: check and test. If + check fails, the test stage won't be executed. This could + speed up things when some checks fails. +* :gh:`322`: Switch from Travis CI to GitHub Actions. -Improved Documentation ----------------------- +* :gh:`347`: Support Python 3.10 in GitHub Action and other config files. -* :pr:`290`: Several improvements in the documentation: +* :gh:`378`: Fix some typos in Towncrier configuration - * New layout to distinguish from the semver2 development line. - * Create new logo. - * Remove any occurances of Python2. - * Describe changelog process with Towncrier. - * Update the release process. +* :gh:`388`: For pytest, switch to the more modern :mod:`importlib` approach + as it doesn't require to modify :data:`sys.path`: + https://docs.pytest.org/en/7.2.x/explanation/pythonpath.html +* :pr:`389`: Add public class variable :data:`Version.NAMES `. + This class variable contains a tuple of strings that contains the names of + all attributes of a Version (like ``"major"``, ``"minor"`` etc). -Trivial/Internal Changes ------------------------- + In cases we need to have dynamical values, this makes it easier to iterate. -* :pr:`290`: Add supported Python versions to :command:`black`. .. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index fe531990..ce598e01 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -8,6 +8,7 @@ The semver source code is managed using Git and is hosted on GitHub:: git clone git://github.com/python-semver/python-semver + Reporting Bugs and Asking Questions ----------------------------------- @@ -34,193 +35,14 @@ consider the following general requirements: If not, ask on its GitHub project https://github.com/semver/semver. +More topics +----------- -Modifying the Code ------------------- - -We recommend the following workflow: - -#. Fork our project on GitHub using this link: - https://github.com/python-semver/python-semver/fork - -#. Clone your forked Git repository (replace ``GITHUB_USER`` with your - account name on GitHub):: - - $ git clone git@github.com:GITHUB_USER/python-semver.git - -#. Create a new branch. You can name your branch whatever you like, but we - recommend to use some meaningful name. If your fix is based on a - existing GitHub issue, add also the number. Good examples would be: - - * ``feature/123-improve-foo`` when implementing a new feature in issue 123 - * ``bugfix/234-fix-security-bar`` a bugfixes for issue 234 - - Use this :command:`git` command:: - - $ git checkout -b feature/NAME_OF_YOUR_FEATURE - -#. Work on your branch and create a pull request: - - a. Write test cases and run the complete test suite, see :ref:`testsuite` - for details. - - b. Write a changelog entry, see section :ref:`add-changelog`. - - c. If you have implemented a new feature, document it into our - documentation to help our reader. See section :ref:`doc` for - further details. - - d. Create a `pull request`_. Describe in the pull request what you did - and why. If you have open questions, ask. - -#. Wait for feedback. If you receive any comments, address these. - -#. After your pull request got accepted, delete your branch. - - -.. _testsuite: - -Running the Test Suite ----------------------- - -We use `pytest`_ and `tox`_ to run tests against all supported Python -versions. All test dependencies are resolved automatically. - -You can decide to run the complete test suite or only part of it: - -* To run all tests, use:: - - $ tox - - If you have not all Python interpreters installed on your system - it will probably give you some errors (``InterpreterNotFound``). - To avoid such errors, use:: - - $ tox --skip-missing-interpreters - - It is possible to use one or more specific Python versions. Use the ``-e`` - option and one or more abbreviations (``py37`` for Python 3.7, - ``py38`` for Python 3.8 etc.):: - - $ tox -e py37 - $ tox -e py37,py38 - - To get a complete list and a short description, run:: - - $ tox -av - -* To run only a specific test, pytest requires the syntax - ``TEST_FILE::TEST_FUNCTION``. - - For example, the following line tests only the function - :func:`test_immutable_major` in the file :file:`test_bump.py` for all - Python versions:: - - $ tox -e py37 -- tests/test_bump.py::test_should_bump_major - - By default, pytest prints only a dot for each test function. To - reveal the executed test function, use the following syntax:: - - $ tox -- -v - - You can combine the specific test function with the ``-e`` option, for - example, to limit the tests for Python 3.7 and 3.8 only:: - - $ tox -e py37,py38 -- tests/test_bump.py::test_should_bump_major - -Our code is checked against formatting, style, type, and docstring issues -(`black`_, `flake8`_, `mypy`_, and `docformatter`_). -It is recommended to run your tests in combination with :command:`checks`, -for example:: - - $ tox -e checks,py37,py38 - - -.. _doc: - -Documenting semver ------------------- - -Documenting the features of semver is very important. It gives our developers -an overview what is possible with semver, how it "feels", and how it is -used efficiently. - -.. note:: - - To build the documentation locally use the following command:: - - $ tox -e docs - - The built documentation is available in :file:`docs/_build/html`. - - -A new feature is *not* complete if it isn't proberly documented. A good -documentation includes: - - * **A docstring** - - Each docstring contains a summary line, a linebreak, an optional - directive (see next item), the description of its arguments in - `Sphinx style`_, and an optional doctest. - The docstring is extracted and reused in the :ref:`api` section. - An appropriate docstring should look like this:: - - def to_tuple(self) -> VersionTuple: - """ - Convert the Version object to a tuple. - - .. versionadded:: 2.10.0 - Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to - make this function available in the public API. - - :return: a tuple with all the parts - - >>> semver.Version(5, 3, 1).to_tuple() - (5, 3, 1, None, None) - """ - - * **An optional directive** - - If you introduce a new feature, change a function/method, or remove something, - it is a good practice to introduce Sphinx directives into the docstring. - This gives the reader an idea what version is affected by this change. - - The first required argument, ``VERSION``, defines the version when this change - was introduced. You can choose from: - - * ``.. versionadded:: VERSION`` - - Use this directive to describe a new feature. - - * ``.. versionchanged:: VERSION`` - - Use this directive to describe when something has changed, for example, - new parameters were added, changed side effects, different return values, etc. - - * ``.. deprecated:: VERSION`` - - Use this directive when a feature is deprecated. Describe what should - be used instead, if appropriate. - - - Add such a directive *after* the summary line, as shown above. - - * **The documentation** - - A docstring is good, but in most cases it's too dense. API documentation - cannot replace a good user documentation. Describe how - to use your new feature in our documentation. Here you can give your - readers more examples, describe it in a broader context or show - edge cases. - - -.. _add-changelog: - -Adding a Changelog Entry ------------------------- - -.. include:: ../changelog.d/README.rst - :start-after: -text-begin- +* `Running the Test Suite `_ +* `Documenting semver `_ +* `Adding a Changelog Entry `_ +* `Preparing the Release `_ +* `Finish the Release `_ .. _black: https://black.rtfd.io diff --git a/CONTRIBUTORS b/CONTRIBUTORS index e5fef99b..0d90ab3a 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -22,30 +22,37 @@ Old maintainer: * Kostiantyn Rybnikov -Significant contributors -======================== +List of Contributors +==================== -* Alexander Puzynia -* Alexander Shorin -* Anton Talevnin -* Ben Finney +(in alphabetical order) + +* Jelo Agnasin * Carles Barrobés -* Craig Blaszczyk -* Damien Nadé * Eli Bishop -* George Sakkis -* Jan Pieter Waagmeester -* Jelo Agnasin -* Karol Werner * Peter Bittner -* robi-wan -* sbrudenell +* Craig Blaszczyk +* Tyler Cross +* Dennis Felsing +* Ben Finney +* Zane Geiger * T. Jameson Little +* Raphael Krupinski * Thomas Laferriere -* Tuure Laurinolli -* Tyler Cross * Zack Lalanne - +* Tuure Laurinolli +* Damien Nadé +* Jan Pieter Waagmeester +* Alexander Puzynia +* Lexi Robinson +* robi-wan +* George Sakkis +* Mike Salvatore +* sbrudenell +* Alexander Shorin +* Anton Talevnin +* Karol Werner + .. Local variables: coding: utf-8 diff --git a/README.rst b/README.rst index cad99a04..ede10a18 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,3 @@ -.. warning:: - - This is a development version. Do **NOT** use it - in production before the final 3.0.0 is released. - Quickstart ========== diff --git a/changelog.d/pr384.bugfix.rst b/changelog.d/pr384.bugfix.rst deleted file mode 100644 index ca0b08d0..00000000 --- a/changelog.d/pr384.bugfix.rst +++ /dev/null @@ -1,11 +0,0 @@ -General cleanup, reformat files: - -* Reformat source code with black again as some config options - did accidentely exclude the semver source code. - Mostly remove some includes/excludes in the black config. -* Integrate concurrency in GH Action -* Ignore Python files on project dirs in .gitignore -* Remove unused patterns in MANIFEST.in -* Use ``extend-exclude`` for flake in :file:`setup.cfg`` and adapt list. -* Use ``skip_install=True`` in :file:`tox.ini` for black - diff --git a/docs/advanced/combine-pydantic-and-semver.rst b/docs/advanced/combine-pydantic-and-semver.rst index a9249daf..a00c2cff 100644 --- a/docs/advanced/combine-pydantic-and-semver.rst +++ b/docs/advanced/combine-pydantic-and-semver.rst @@ -17,10 +17,14 @@ To work with Pydantic, use the following steps: from semver import Version class PydanticVersion(Version): + @classmethod + def _parse(cls, version): + return cls.parse(version) + @classmethod def __get_validators__(cls): """Return a list of validator methods for pydantic models.""" - yield cls.parse + yield cls._parse @classmethod def __modify_schema__(cls, field_schema): diff --git a/docs/advanced/convert-pypi-to-semver.rst b/docs/advanced/convert-pypi-to-semver.rst index 76653ceb..04737d29 100644 --- a/docs/advanced/convert-pypi-to-semver.rst +++ b/docs/advanced/convert-pypi-to-semver.rst @@ -135,7 +135,7 @@ semver: def convert2semver(ver: packaging.version.Version) -> semver.Version: """Converts a PyPI version into a semver version - :param packaging.version.Version ver: the PyPI version + :param ver: the PyPI version :return: a semver version :raises ValueError: if epoch or post parts are used """ @@ -145,7 +145,7 @@ semver: raise ValueError("Can't convert a post part to semver") pre = None if not ver.pre else "".join([str(i) for i in ver.pre]) - semver.Version(*ver.release, prerelease=pre, build=ver.dev) + return semver.Version(*ver.release, prerelease=pre, build=ver.dev) .. _convert_semver_to_pypi: diff --git a/docs/advanced/create-subclasses-from-version.rst b/docs/advanced/create-subclasses-from-version.rst index 7c97ee6f..7e99e217 100644 --- a/docs/advanced/create-subclasses-from-version.rst +++ b/docs/advanced/create-subclasses-from-version.rst @@ -16,7 +16,8 @@ but the other behavior is the same, use the following code: The derived class :class:`SemVerWithVPrefix` can be used like -the original class: +the original class. Additionally, you can pass "incomplete" +version strings like ``v2.3``: .. code-block:: python @@ -24,7 +25,7 @@ the original class: >>> assert str(v1) == "v1.2.3" >>> print(v1) v1.2.3 - >>> v2 = SemVerWithVPrefix.parse("v2.3.4") + >>> v2 = SemVerWithVPrefix.parse("v2.3") >>> v2 > v1 True >>> bad = SemVerWithVPrefix.parse("1.2.4") diff --git a/docs/advanced/index.rst b/docs/advanced/index.rst index 8a45d361..47c23b9d 100644 --- a/docs/advanced/index.rst +++ b/docs/advanced/index.rst @@ -3,6 +3,7 @@ Advanced topics .. toctree:: + :maxdepth: 1 deal-with-invalid-versions create-subclasses-from-version diff --git a/docs/advanced/semverwithvprefix.py b/docs/advanced/semverwithvprefix.py index f2a7fecd..7e411d35 100644 --- a/docs/advanced/semverwithvprefix.py +++ b/docs/advanced/semverwithvprefix.py @@ -20,7 +20,7 @@ def parse(cls, version: str) -> "SemVerWithVPrefix": f"{version!r}: not a valid semantic version tag. " "Must start with 'v' or 'V'" ) - return super().parse(version[1:]) + return super().parse(version[1:], optional_minor_and_patch=True) def __str__(self) -> str: # Reconstruct the tag diff --git a/docs/advanced/version-from-file.rst b/docs/advanced/version-from-file.rst index 6dc9bb48..b49ff36b 100644 --- a/docs/advanced/version-from-file.rst +++ b/docs/advanced/version-from-file.rst @@ -8,16 +8,17 @@ is to use the following function: .. code-block:: python + import os + from typing import Union from semver.version import Version - def get_version(path: str = "version") -> Version: + def get_version(path: Union[str, os.PathLike]) -> semver.Version: """ - Construct a Version from a file + Construct a Version object from a file :param path: A text file only containing the semantic version :return: A :class:`Version` object containing the semantic version from the file. """ - with open(path,"r") as fh: - version = fh.read().strip() + version = open(path,"r").read().strip() return Version.parse(version) diff --git a/docs/api.rst b/docs/api.rst index 196e30a9..279df408 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -17,8 +17,37 @@ Deprecated Functions in :mod:`semver._deprecated` .. automodule:: semver._deprecated +.. autofunction:: semver._deprecated.bump_build + +.. autofunction:: semver._deprecated.bump_major + +.. autofunction:: semver._deprecated.bump_minor + +.. autofunction:: semver._deprecated.bump_patch + +.. autofunction:: semver._deprecated.bump_prerelease + +.. autofunction:: semver._deprecated.compare + .. autofunction:: semver._deprecated.deprecated +.. autofunction:: semver._deprecated.finalize_version + +.. autofunction:: semver._deprecated.format_version + +.. autofunction:: semver._deprecated.match + +.. autofunction:: semver._deprecated.max_ver + +.. autofunction:: semver._deprecated.min_ver + +.. autofunction:: semver._deprecated.parse + +.. autofunction:: semver._deprecated.parse_version_info + +.. autofunction:: semver._deprecated.replace + + CLI Parsing :mod:`semver.cli` ----------------------------- diff --git a/BUILDING.rst b/docs/build-semver.rst similarity index 99% rename from BUILDING.rst rename to docs/build-semver.rst index 61f3d4cb..c938a1ee 100644 --- a/BUILDING.rst +++ b/docs/build-semver.rst @@ -3,6 +3,7 @@ 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/ diff --git a/docs/build.rst b/docs/build.rst deleted file mode 100644 index ba0c84a4..00000000 --- a/docs/build.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../BUILDING.rst diff --git a/docs/changelog-semver3-devel.rst b/docs/changelog-semver3-devel.rst new file mode 100644 index 00000000..2d40635d --- /dev/null +++ b/docs/changelog-semver3-devel.rst @@ -0,0 +1,367 @@ + +############################# +Changelog semver3 development +############################# + +This site contains all the changes during the development phase. + +.. _semver-3.0.0-dev.4: + +Version 3.0.0-dev.4 +=================== + +:Released: 2022-12-18 +:Maintainer: + + +.. _semver-3.0.0-dev.4-bugfixes: + +Bug Fixes +--------- + +* :gh:`374`: Correct Towncrier's config entries in the :file:`pyproject.toml` file. + The old entries ``[[tool.towncrier.type]]`` are deprecated and need + to be replaced by ``[tool.towncrier.fragment.]``. + + + +.. _semver-3.0.0-dev.4-deprecations: + +Deprecations +------------ + +* :gh:`372`: Deprecate support for Python 3.6. + + Python 3.6 reached its end of life and isn't supported anymore. + At the time of writing (Dec 2022), the lowest version is 3.7. + + Although the `poll `_ + didn't cast many votes, the majority agree to remove support for + Python 3.6. + + +.. _semver-3.0.0-dev.4-doc: + +Improved Documentation +---------------------- + +* :gh:`335`: Add new section "Converting versions between PyPI and semver" the limitations + and possible use cases to convert from one into the other versioning scheme. + +* :gh:`340`: Describe how to get version from a file + +* :gh:`343`: Describe combining Pydantic with semver in the "Advanced topic" + section. + +* :gh:`350`: Restructure usage section. Create subdirectory "usage/" and splitted + all section into different files. + +* :gh:`351`: Introduce new topics for: + + * "Migration to semver3" + * "Advanced topics" + + +.. _semver-3.0.0-dev.4-features: + +Features +-------- + +* :pr:`359`: Add optional parameter ``optional_minor_and_patch`` in :meth:`.Version.parse` to allow optional + minor and patch parts. + +* :pr:`362`: Make :meth:`.Version.match` accept a bare version string as match expression, defaulting to + equality testing. + +* :gh:`364`: Enhance :file:`pyproject.toml` to make it possible to use the + :command:`pyproject-build` command from the build module. + For more information, see :ref:`build-semver`. + +* :gh:`365`: Improve :file:`pyproject.toml`. + + * Use setuptools, add metadata. Taken approach from + `A Practical Guide to Setuptools and Pyproject.toml + `_. + * Doc: Describe building of semver + * Remove :file:`.travis.yml` in :file:`MANIFEST.in` + (not needed anymore) + * Distinguish between Python 3.6 and others in :file:`tox.ini` + * Add skip_missing_interpreters option for :file:`tox.ini` + * GH Action: Upgrade setuptools and setuptools-scm and test + against 3.11.0-rc.2 + + +.. _semver-3.0.0-dev.4-internal: + +Trivial/Internal Changes +------------------------ + +* :gh:`378`: Fix some typos in Towncrier configuration + + + +---- + +.. _semver-3.0.0-dev.3: + +Version 3.0.0-dev.3 +=================== + +:Released: 2022-01-19 +:Maintainer: Tom Schraitle + + +.. _semver-3.0.0-dev.3-bugfixes: + +Bug Fixes +--------- + +* :gh:`310`: Rework API documentation. + Follow a more "semi-manual" attempt and add auto directives + into :file:`docs/api.rst`. + + +.. _semver-3.0.0-dev.3-docs: + +Improved Documentation +---------------------- + +* :gh:`312`: Rework "Usage" section. + + * Mention the rename of :class:`~semver.version.VersionInfo` to + :class:`~semver.version.Version` class + * Remove semver. prefix in doctests to make examples shorter + * Correct some references to dunder methods like + :func:`~.semver.version.Version.__getitem__`, + :func:`~.semver.version.Version.__gt__` etc. + * Remove inconsistencies and mention module level function as + deprecated and discouraged from using + * Make empty :py:func:`super` call in :file:`semverwithvprefix.py` example + +* :gh:`315`: Improve release procedure text + + +.. _semver-3.0.0-dev.3-trivial: + +Trivial/Internal Changes +------------------------ + +* :gh:`309`: Some (private) functions from the :mod:`semver.version` + module has been changed. + + The following functions got renamed: + + * function ``semver.version.comparator`` got renamed to + :func:`semver.version._comparator` as it is only useful + inside the :class:`~semver.version.Version` class. + * function ``semver.version.cmp`` got renamed to + :func:`semver.version._cmp` as it is only useful + inside the :class:`~semver.version.Version` class. + + The following functions got integrated into the + :class:`~semver.version.Version` class: + + * function ``semver.version._nat_cmd`` as a classmethod + * function ``semver.version.ensure_str`` + +* :gh:`313`: Correct :file:`tox.ini` for ``changelog`` entry to skip + installation for semver. This should speed up the execution + of towncrier. + +* :gh:`316`: Comparisons of :class:`~semver.version.Version` class and other + types return now a :py:const:`NotImplemented` constant instead + of a :py:exc:`TypeError` exception. + + The `NotImplemented`_ section of the Python documentation recommends + returning this constant when comparing with ``__gt__``, ``__lt__``, + and other comparison operators to "to indicate that the operation is + not implemented with respect to the other type". + + .. _NotImplemented: https://docs.python.org/3/library/constants.html#NotImplemented + +* :gh:`319`: Introduce stages in :file:`.travis.yml` + The config file contains now two stages: check and test. If + check fails, the test stage won't be executed. This could + speed up things when some checks fails. + +* :gh:`322`: Switch from Travis CI to GitHub Actions. + +* :gh:`347`: Support Python 3.10 in GitHub Action and other config files. + + + +---- + +.. _semver-3.0.0-dev.2: + +Version 3.0.0-dev.2 +=================== + +:Released: 2020-11-01 +:Maintainer: Tom Schraitle + + +.. _semver-3.0.0-dev.2-deprecations: + +Deprecations +------------ + +* :gh:`169`: Deprecate CLI functions not imported from ``semver.cli``. + + +.. _semver-3.0.0-dev.2-features: + +Features +-------- + +* :gh:`169`: Create semver package and split code among different modules in the packages. + + * Remove :file:`semver.py` + * Create :file:`src/semver/__init__.py` + * Create :file:`src/semver/cli.py` for all CLI methods + * Create :file:`src/semver/_deprecated.py` for the ``deprecated`` decorator and other deprecated functions + * Create :file:`src/semver/__main__.py` to allow calling the CLI using :command:`python -m semver` + * Create :file:`src/semver/_types.py` to hold type aliases + * Create :file:`src/semver/version.py` to hold the :class:`Version` class (old name :class:`VersionInfo`) and its utility functions + * Create :file:`src/semver/__about__.py` for all the metadata variables + +* :gh:`305`: Rename :class:`VersionInfo` to :class:`Version` but keep an alias for compatibility + + +.. _semver-3.0.0-dev.2-docs: + +Improved Documentation +---------------------- + +* :gh:`304`: Several improvements in documentation: + + * Reorganize API documentation. + * Add migration chapter from semver2 to semver3. + * Distinguish between changlog for version 2 and 3 + +* :gh:`305`: Add note about :class:`Version` rename. + + +.. _semver-3.0.0-dev.2-trivial: + +Trivial/Internal Changes +------------------------ + +* :gh:`169`: Adapted infrastructure code to the new project layout. + + * Replace :file:`setup.py` with :file:`setup.cfg` because the :file:`setup.cfg` is easier to use + * Adapt documentation code snippets where needed + * Adapt tests + * Changed the ``deprecated`` to hardcode the ``semver`` package name in the warning. + + Increase coverage to 100% for all non-deprecated APIs + +* :gh:`304`: Support PEP-561 :file:`py.typed`. + + According to the mentioned PEP: + + "Package maintainers who wish to support type checking + of their code MUST add a marker file named :file:`py.typed` + to their package supporting typing." + + Add package_data to :file:`setup.cfg` to include this marker in dist + and whl file. + + + +---- + +.. _semver-3.0.0-dev.1: + +Version 3.0.0-dev.1 +=================== + +:Released: 2020-10-26 +:Maintainer: Tom Schraitle + + +.. _semver-3.0.0-dev.1-deprecations: + +Deprecations +------------ + +* :pr:`290`: For semver 3.0.0-alpha0: + + * Remove anything related to Python2 + * In :file:`tox.ini` and :file:`.travis.yml` + Remove targets py27, py34, py35, and pypy. + Add py38, py39, and nightly (allow to fail) + * In :file:`setup.py` simplified file and remove + ``Tox`` and ``Clean`` classes + * Remove old Python versions (2.7, 3.4, 3.5, and pypy) + from Travis + +* :gh:`234`: In :file:`setup.py` simplified file and remove + ``Tox`` and ``Clean`` classes + + +.. _semver-3.0.0-dev.1-features: + +Features +-------- + +* :pr:`290`: Create semver 3.0.0-alpha0 + + * Update :file:`README.rst`, mention maintenance + branch ``maint/v2``. + * Remove old code mainly used for Python2 compatibility, + adjusted code to support Python3 features. + * Split test suite into separate files under :file:`tests/` + directory + * Adjust and update :file:`setup.py`. Requires Python >=3.6.* + Extract metadata directly from source (affects all the ``__version__``, + ``__author__`` etc. variables) + +* :gh:`270`: Configure Towncrier (:pr:`273`:) + + * Add :file:`changelog.d/.gitignore` to keep this directory + * Create :file:`changelog.d/README.rst` with some descriptions + * Add :file:`changelog.d/_template.rst` as Towncrier template + * Add ``[tool.towncrier]`` section in :file:`pyproject.toml` + * Add "changelog" target into :file:`tox.ini`. Use it like + :command:`tox -e changelog -- CMD` whereas ``CMD`` is a + Towncrier command. The default :command:`tox -e changelog` + calls Towncrier to create a draft of the changelog file + and output it to stdout. + * Update documentation and add include a new section + "Changelog" included from :file:`changelog.d/README.rst`. + +* :gh:`276`: Document how to create a sublass from :class:`VersionInfo` class + +* :gh:`213`: Add typing information + + +.. _semver-3.0.0-dev.1-bugfixes: + +Bug Fixes +--------- + +* :gh:`291`: Disallow negative numbers in VersionInfo arguments + for ``major``, ``minor``, and ``patch``. + + +.. _semver-3.0.0-dev.1-docs: + +Improved Documentation +---------------------- + +* :pr:`290`: Several improvements in the documentation: + + * New layout to distinguish from the semver2 development line. + * Create new logo. + * Remove any occurances of Python2. + * Describe changelog process with Towncrier. + * Update the release process. + + +.. _semver-3.0.0-dev.1-trivial: + +Trivial/Internal Changes +------------------------ + +* :pr:`290`: Add supported Python versions to :command:`black`. diff --git a/docs/conf.py b/docs/conf.py index ed888361..9edfda4d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -118,8 +118,8 @@ def find_version(*file_paths): # Markup to shorten external links # See https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html extlinks = { - "gh": ("https://github.com/python-semver/python-semver/issues/%s", "#"), - "pr": ("https://github.com/python-semver/python-semver/pull/%s", "PR #"), + "gh": ("https://github.com/python-semver/python-semver/issues/%s", "#%s"), + "pr": ("https://github.com/python-semver/python-semver/pull/%s", "PR #%s"), } # -- Options for HTML output ---------------------------------------------- diff --git a/docs/contribute/add-changelog-entry.rst b/docs/contribute/add-changelog-entry.rst new file mode 100644 index 00000000..c0d426a8 --- /dev/null +++ b/docs/contribute/add-changelog-entry.rst @@ -0,0 +1,7 @@ +.. _add-changelog: + +Adding a Changelog Entry +======================== + +.. include:: ../../changelog.d/README.rst + :start-after: -text-begin- \ No newline at end of file diff --git a/docs/contribute/doc-semver.rst b/docs/contribute/doc-semver.rst new file mode 100644 index 00000000..fcc6c1ac --- /dev/null +++ b/docs/contribute/doc-semver.rst @@ -0,0 +1,80 @@ +.. _doc: + +Documenting semver +================== + +Documenting the features of semver is very important. It gives our developers +an overview what is possible with semver, how it "feels", and how it is +used efficiently. + +.. note:: + + To build the documentation locally use the following command:: + + $ tox -e docs + + The built documentation is available in :file:`docs/_build/html`. + + +A new feature is *not* complete if it isn't proberly documented. A good +documentation includes: + + * **A docstring** + + Each docstring contains a summary line, a linebreak, an optional + directive (see next item), the description of its arguments in + `Sphinx style`_, and an optional doctest. + The docstring is extracted and reused in the :ref:`api` section. + An appropriate docstring should look like this:: + + def to_tuple(self) -> VersionTuple: + """ + Convert the Version object to a tuple. + + .. versionadded:: 2.10.0 + Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to + make this function available in the public API. + + :return: a tuple with all the parts + + >>> semver.Version(5, 3, 1).to_tuple() + (5, 3, 1, None, None) + + """ + + * **An optional directive** + + If you introduce a new feature, change a function/method, or remove something, + it is a good practice to introduce Sphinx directives into the docstring. + This gives the reader an idea what version is affected by this change. + + The first required argument, ``VERSION``, defines the version when this change + was introduced. You can choose from: + + * ``.. versionadded:: VERSION`` + + Use this directive to describe a new feature. + + * ``.. versionchanged:: VERSION`` + + Use this directive to describe when something has changed, for example, + new parameters were added, changed side effects, different return values, etc. + + * ``.. deprecated:: VERSION`` + + Use this directive when a feature is deprecated. Describe what should + be used instead, if appropriate. + + + Add such a directive *after* the summary line, as shown above. + + * **The documentation** + + A docstring is good, but in most cases it's too dense. API documentation + cannot replace a good user documentation. Describe how + to use your new feature in our documentation. Here you can give your + readers more examples, describe it in a broader context or show + edge cases. + + +.. _Sphinx style: https://sphinx-rtd-tutorial.rtfd.io/en/latest/docstrings.html diff --git a/docs/contribute/finish-release.rst b/docs/contribute/finish-release.rst new file mode 100644 index 00000000..947fcf96 --- /dev/null +++ b/docs/contribute/finish-release.rst @@ -0,0 +1,24 @@ +.. _finish-release: + +Finish the Release +================== + +1. Create a tag: + + $ git tag -a x.x.x + + It’s recommended to use the generated Tox output from the Changelog. + +2. Push the tag: + + $ git push –tags + +3. In `GitHub Release + page `_ + document the new release. Select the tag from the last step and copy + the content of the tag description into the release description. + +4. Announce it in + https://github.com/python-semver/python-semver/discussions/categories/announcements. + +You’re done! Celebrate! diff --git a/docs/contribute/index.rst b/docs/contribute/index.rst new file mode 100644 index 00000000..f09e6418 --- /dev/null +++ b/docs/contribute/index.rst @@ -0,0 +1,28 @@ +.. _contributing: + +Contributing to semver +====================== + +The semver source code is managed using Git and is hosted on GitHub:: + + git clone git://github.com/python-semver/python-semver + + +.. include:: prerequisites.rst + :start-after: -text-begin- + + +.. toctree:: + :maxdepth: 1 + :caption: More topics + :includehidden: + + report-bugs + run-test-suite + doc-semver + add-changelog-entry + release-procedure + finish-release + + +.. _pull request: https://github.com/python-semver/python-semver/pulls diff --git a/docs/contribute/prerequisites.rst b/docs/contribute/prerequisites.rst new file mode 100644 index 00000000..79bbb571 --- /dev/null +++ b/docs/contribute/prerequisites.rst @@ -0,0 +1,15 @@ +Prerequisites +------------- + +.. -text-begin- + +Before you make changes to the code, we would highly appreciate if you +consider the following general requirements: + +* Make sure your code adheres to the `Semantic Versioning`_ specification. + +* Check if your feature is covered by the Semantic Versioning specification. + If not, ask on its GitHub project https://github.com/semver/semver. + + +.. _Semantic Versioning: https://semver.org diff --git a/docs/contribute/release-procedure.rst b/docs/contribute/release-procedure.rst new file mode 100644 index 00000000..c02148fe --- /dev/null +++ b/docs/contribute/release-procedure.rst @@ -0,0 +1,123 @@ +Release Procedure +================= + +The following procedures gives a short overview of what steps are needed +to create a new release. + +These steps are interesting for the release manager only. + + +Prepare the Release +------------------- + +1. Verify that: + + - all issues for a new release are closed: + https://github.com/python-semver/python-semver/issues. + + - all pull requests that should be included in this release are + merged: https://github.com/python-semver/python-semver/pulls. + + - continuous integration for latest build was passing: + https://github.com/python-semver/python-semver/actions. + +2. Create a new branch ``release/``. + +3. If one or several supported Python versions have been removed or + added, verify that the following files have been updated: + + - :file:`setup.cfg` + - :file:`tox.ini` + - :file:`.git/workflows/pythonpackage.yml` + - :file:`.github/workflows/python-testing.yml` + +4. Verify that the version in file :file:`src/semver/__about__.py` + has been updated and follows the `Semver `_ + specification. + +5. Add eventually new contributor(s) to + `CONTRIBUTORS `_. + +6. Check if all changelog entries are created. If some are missing, + `create + them `__. + +7. Show the new draft + `CHANGELOG `_ entry for the latest release with: + + :: + + $ tox -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: + + :: + + $ tox -e changelog -- build + +8. Build the documentation and check the output: + + :: + + $ tox -e docs + +9. Commit all changes, push, and create a pull request. + +Create the New Release +---------------------- + +1. Ensure that long description + (`README.rst `_) + can be correctly rendered by Pypi using + ``restview --long-description`` + +2. Clean up your local Git repository. Be careful, as it **will remove + all files** which are not versioned by Git: + + :: + + $ git clean -xfd + + Before you create your distribution files, clean the directory too: + + :: + + $ rm dist/* + +3. Create the distribution files (wheel and source): + + :: + + $ tox -e prepare-dist + +4. Upload the wheel and source to TestPyPI first: + + .. code:: bash + + $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* + + If you have a ``~/.pypirc`` with a ``testpypi`` section, the upload + can be simplified: + + :: + + $ twine upload --repository testpypi dist/* + +5. Check if everything is okay with the wheel. Check also the web site + ``https://test.pypi.org/project//`` + +6. If everything looks fine, merge the pull request. + +7. Upload to PyPI: + + .. code:: bash + + $ git clean -xfd + $ tox -e prepare-dist + $ twine upload dist/* + +8. Go to https://pypi.org/project/semver/ to verify that new version is + online and the page is rendered correctly. + diff --git a/docs/contribute/report-bugs.rst b/docs/contribute/report-bugs.rst new file mode 100644 index 00000000..fa14eb18 --- /dev/null +++ b/docs/contribute/report-bugs.rst @@ -0,0 +1,18 @@ +.. _report-bugs: + +Reporting Bugs and Asking Questions +----------------------------------- + +If you think you have encountered a bug in semver or have an idea for a new +feature? Great! We like to hear from you! + +There are several options to participate: + +* Open a new topic on our `GitHub discussion `_ page. + Tell us our ideas or ask your questions. + +* Look into our GitHub `issues`_ tracker or open a new issue. + + +.. _issues: https://github.com/python-semver/python-semver/issues +.. _gh_discussions: https://github.com/python-semver/python-semver/discussions diff --git a/docs/contribute/run-test-suite.rst b/docs/contribute/run-test-suite.rst new file mode 100644 index 00000000..07c49fff --- /dev/null +++ b/docs/contribute/run-test-suite.rst @@ -0,0 +1,64 @@ +.. _testsuite: + +Running the Test Suite +====================== + +We use `pytest`_ and `tox`_ to run tests against all supported Python +versions. All test dependencies are resolved automatically. + +You can decide to run the complete test suite or only part of it: + +* To run all tests, use:: + + $ tox + + If you have not all Python interpreters installed on your system + it will probably give you some errors (``InterpreterNotFound``). + To avoid such errors, use:: + + $ tox --skip-missing-interpreters + + It is possible to use one or more specific Python versions. Use the ``-e`` + option and one or more abbreviations (``py37`` for Python 3.7, + ``py38`` for Python 3.8 etc.):: + + $ tox -e py37 + $ tox -e py37,py38 + + To get a complete list and a short description, run:: + + $ tox -av + +* To run only a specific test, pytest requires the syntax + ``TEST_FILE::TEST_FUNCTION``. + + For example, the following line tests only the function + :func:`test_immutable_major` in the file :file:`test_bump.py` for all + Python versions:: + + $ tox -e py37 -- tests/test_bump.py::test_should_bump_major + + By default, pytest prints only a dot for each test function. To + reveal the executed test function, use the following syntax:: + + $ tox -- -v + + You can combine the specific test function with the ``-e`` option, for + example, to limit the tests for Python 3.7 and 3.8 only:: + + $ tox -e py37,py38 -- tests/test_bump.py::test_should_bump_major + +Our code is checked against formatting, style, type, and docstring issues +(`black`_, `flake8`_, `mypy`_, and `docformatter`_). +It is recommended to run your tests in combination with :command:`checks`, +for example:: + + $ tox -e checks,py37,py38 + + +.. _black: https://black.rtfd.io +.. _docformatter: https://pypi.org/project/docformatter/ +.. _flake8: https://flake8.rtfd.io +.. _mypy: http://mypy-lang.org/ +.. _pytest: http://pytest.org/ +.. _tox: https://tox.rtfd.org/ diff --git a/docs/development.rst b/docs/development.rst deleted file mode 100644 index e582053e..00000000 --- a/docs/development.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CONTRIBUTING.rst diff --git a/docs/index.rst b/docs/index.rst index 2dce2a50..4c9ccff7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,13 +8,14 @@ Semver |version| -- Semantic Versioning :maxdepth: 2 :caption: Contents :hidden: + :numbered: - build + build-semver install usage/index migration/index advanced/index - development + contribute/index api .. toctree:: @@ -27,7 +28,7 @@ Semver |version| -- Semantic Versioning .. toctree:: :maxdepth: 1 - :caption: Changelogs + :caption: Development :hidden: changelog diff --git a/docs/migration/migratetosemver3.rst b/docs/migration/migratetosemver3.rst index f869cad3..e8b5c9ab 100644 --- a/docs/migration/migratetosemver3.rst +++ b/docs/migration/migratetosemver3.rst @@ -3,8 +3,9 @@ Migrating from semver2 to semver3 ================================= -This document describes the visible differences for +This section describes the visible differences for users and how your code stays compatible for semver3. +Some changes are backward incompatible. Although the development team tries to make the transition to semver3 as smooth as possible, at some point change @@ -17,10 +18,11 @@ to our :ref:`change-log`. Use Version instead of VersionInfo ---------------------------------- -The :class:`VersionInfo` has been renamed to :class:`Version` -to have a more succinct name. +The :class:`~semver.version.VersionInfo` has been renamed to +:class:`~semver.version.Version` to have a more succinct name. An alias has been created to preserve compatibility but -using the old name has been deprecated. +using the old name has been deprecated and will be removed +in future versions. If you still need the old version, use this line: @@ -34,9 +36,16 @@ Use semver.cli instead of semver -------------------------------- All functions related to CLI parsing are moved to :mod:`semver.cli`. -If you are such functions, like :func:`semver.cmd_bump `, +If you need such functions, like :meth:`~semver.cli.cmd_bump`, import it from :mod:`semver.cli` in the future: .. code-block:: python from semver.cli import cmd_bump + + +Use semver.Version.is_valid instead of semver.Version.isvalid +------------------------------------------------------------- + +The pull request :pr:`284` introduced the method :meth:`~semver.version.Version.is_compatible`. To keep consistency, the development team +decided to rename the :meth:`~semver.Version.isvalid` to :meth:`~semver.Version.is_valid`. diff --git a/docs/migration/replace-deprecated-functions.rst b/docs/migration/replace-deprecated-functions.rst index 9738001c..ebe8c354 100644 --- a/docs/migration/replace-deprecated-functions.rst +++ b/docs/migration/replace-deprecated-functions.rst @@ -6,7 +6,7 @@ Replacing Deprecated Functions .. versionchanged:: 2.10.0 The development team of semver has decided to deprecate certain functions on the module level. The preferred way of using semver is through the - :class:`semver.Version` class. + :class:`~semver.version.Version` class. The deprecated functions can still be used in version 2.10.0 and above. In version 3 of semver, the deprecated functions will be removed. @@ -17,10 +17,10 @@ them with code which is compatible for future versions: * :func:`semver.bump_major`, :func:`semver.bump_minor`, :func:`semver.bump_patch`, :func:`semver.bump_prerelease`, :func:`semver.bump_build` - Replace them with the respective methods of the :class:`Version ` + Replace them with the respective methods of the :class:`~semver.version.Version` class. For example, the function :func:`semver.bump_major` is replaced by - :func:`semver.Version.bump_major` and calling the ``str(versionobject)``: + :meth:`~semver.version.Version.bump_major` and calling the ``str(versionobject)``: .. code-block:: python @@ -31,9 +31,14 @@ them with code which is compatible for future versions: Likewise with the other module level functions. +* :func:`semver.Version.isvalid` + + Replace it with :meth:`semver.version.Version.is_valid`: + + * :func:`semver.finalize_version` - Replace it with :func:`semver.Version.finalize_version`: + Replace it with :func:`semver.version.Version.finalize_version`: .. code-block:: python @@ -55,7 +60,7 @@ them with code which is compatible for future versions: * :func:`semver.max_ver` - Replace it with ``max(version1, version2, ...)`` or ``max([version1, version2, ...])``: + Replace it with ``max(version1, version2, ...)`` or ``max([version1, version2, ...])`` and a ``key``: .. code-block:: python @@ -77,8 +82,8 @@ them with code which is compatible for future versions: * :func:`semver.parse` - Replace it with :func:`semver.Version.parse` and - :func:`semver.Version.to_dict`: + Replace it with :meth:`semver.version.Version.parse` and call + :meth:`semver.version.Version.to_dict`: .. code-block:: python @@ -89,7 +94,7 @@ them with code which is compatible for future versions: * :func:`semver.parse_version_info` - Replace it with :func:`semver.Version.parse`: + Replace it with :meth:`semver.version.Version.parse`: .. code-block:: python @@ -100,7 +105,7 @@ them with code which is compatible for future versions: * :func:`semver.replace` - Replace it with :func:`semver.Version.replace`: + Replace it with :meth:`semver.version.Version.replace`: .. code-block:: python diff --git a/docs/usage/access-parts-through-index.rst b/docs/usage/access-parts-through-index.rst index a261fda4..c3651a5e 100644 --- a/docs/usage/access-parts-through-index.rst +++ b/docs/usage/access-parts-through-index.rst @@ -7,7 +7,7 @@ Accessing Parts Through Index Numbers Another way to access parts of a version is to use an index notation. The underlying :class:`~semver.version.Version` object allows to access its data through -the magic method :func:`~semver.version.Version.__getitem__`. +the magic method :meth:`~semver.version.Version.__getitem__`. For example, the ``major`` part can be accessed by index number 0 (zero). Likewise the other parts: diff --git a/docs/usage/check-compatible-semver-version.rst b/docs/usage/check-compatible-semver-version.rst new file mode 100644 index 00000000..20330456 --- /dev/null +++ b/docs/usage/check-compatible-semver-version.rst @@ -0,0 +1,95 @@ +Checking for a Compatible Semver Version +======================================== + +To check if a *change* from a semver version ``a`` to a semver +version ``b`` is *compatible* according to semver rule, use the method +:meth:`~semver.version.Version.is_compatible`. + +The expression ``a.is_compatible(b) is True`` if one of the following +statements is true: + +* both versions are equal, or +* both majors are equal and higher than 0. The same applies for both + minor parts. Both pre-releases are equal, or +* both majors are equal and higher than 0. The minor of ``b``'s + minor version is higher then ``a``'s. Both pre-releases are equal. + +In all other cases, the result is false. + +Keep in mind, the method *does not* check patches! + + +* Two different majors: + + .. code-block:: python + + >>> a = Version(1, 1, 1) + >>> b = Version(2, 0, 0) + >>> a.is_compatible(b) + False + >>> b.is_compatible(a) + False + +* Two different minors: + + .. code-block:: python + + >>> a = Version(1, 1, 0) + >>> b = Version(1, 0, 0) + >>> a.is_compatible(b) + False + >>> b.is_compatible(a) + True + +* The same two majors and minors: + + .. code-block:: python + + >>> a = Version(1, 1, 1) + >>> b = Version(1, 1, 0) + >>> a.is_compatible(b) + True + >>> b.is_compatible(a) + True + +* Release and pre-release: + + .. code-block:: python + + >>> a = Version(1, 1, 1) + >>> b = Version(1, 0, 0,'rc1') + >>> a.is_compatible(b) + False + >>> b.is_compatible(a) + False + +* Different pre-releases: + + .. code-block:: python + + >>> a = Version(1, 0, 0, 'rc1') + >>> b = Version(1, 0, 0, 'rc2') + >>> a.is_compatible(b) + False + >>> b.is_compatible(a) + False + +* Identical pre-releases: + + .. code-block:: python + + >>> a = Version(1, 0, 0,'rc1') + >>> b = Version(1, 0, 0,'rc1') + >>> a.is_compatible(b) + True + +* All major zero versions are incompatible with anything but itself: + + .. code-block:: python + + >>> Version(0, 1, 0).is_compatible(Version(0, 1, 1)) + False + + # Only identical versions are compatible for major zero versions: + >>> Version(0, 1, 0).is_compatible(Version(0, 1, 0)) + True diff --git a/docs/usage/check-valid-semver-version.rst b/docs/usage/check-valid-semver-version.rst index 7aa9615b..bdd57e7a 100644 --- a/docs/usage/check-valid-semver-version.rst +++ b/docs/usage/check-valid-semver-version.rst @@ -2,11 +2,11 @@ Checking for a Valid Semver Version =================================== If you need to check a string if it is a valid semver version, use the -classmethod :func:`Version.isvalid `: +classmethod :meth:`~semver.version.Version.is_valid`: .. code-block:: python - >>> Version.isvalid("1.0.0") + >>> Version.is_valid("1.0.0") True - >>> Version.isvalid("invalid") + >>> Version.is_valid("invalid") False diff --git a/docs/usage/compare-versions-through-expression.rst b/docs/usage/compare-versions-through-expression.rst index 28fad671..e2dee4d6 100644 --- a/docs/usage/compare-versions-through-expression.rst +++ b/docs/usage/compare-versions-through-expression.rst @@ -2,7 +2,7 @@ Comparing Versions through an Expression ======================================== If you need a more fine-grained approach of comparing two versions, -use the :func:`semver.match` function. It expects two arguments: +use the :meth:`~semver.version.Version.match` function. It expects two arguments: 1. a version string 2. a match expression @@ -20,9 +20,9 @@ That gives you the following possibilities to express your condition: .. code-block:: python - >>> semver.match("2.0.0", ">=1.0.0") + >>> Version.parse("2.0.0").match(">=1.0.0") True - >>> semver.match("1.0.0", ">1.0.0") + >>> Version.parse("1.0.0").match(">1.0.0") False If no operator is specified, the match expression is interpreted as a @@ -33,7 +33,7 @@ handle both cases: .. code-block:: python - >>> semver.match("2.0.0", "2.0.0") + >>> Version.parse("2.0.0").match("2.0.0") True - >>> semver.match("1.0.0", "3.5.1") + >>> Version.parse("1.0.0").match("3.5.1") False diff --git a/docs/usage/compare-versions.rst b/docs/usage/compare-versions.rst index b42ba1a7..cf55eae3 100644 --- a/docs/usage/compare-versions.rst +++ b/docs/usage/compare-versions.rst @@ -17,7 +17,7 @@ To compare two versions depends on your type: The return value is negative if ``version1 < version2``, zero if ``version1 == version2`` and strictly positive if ``version1 > version2``. -* **Two** :class:`Version ` **instances** +* **Two** :class:`~semver.version.Version` **instances** Use the specific operator. Currently, the operators ``<``, ``<=``, ``>``, ``>=``, ``==``, and ``!=`` are supported:: @@ -29,9 +29,9 @@ To compare two versions depends on your type: >>> v1 > v2 False -* **A** :class:`Version ` **type and a** :func:`tuple` **or** :func:`list` +* **A** :class:`~semver.version.Version` **type and a** :func:`tuple` **or** :func:`list` - Use the operator as with two :class:`Version ` types:: + Use the operator as with two :class:`~semver.version.Version` types:: >>> v = Version.parse("3.4.5") >>> v > (1, 0) @@ -46,7 +46,7 @@ To compare two versions depends on your type: >>> [3, 5] > v True -* **A** :class:`Version ` **type and a** :func:`str` +* **A** :class:`~semver.version.Version` **type and a** :func:`str` You can use also raw strings to compare:: @@ -69,7 +69,7 @@ To compare two versions depends on your type: ... ValueError: 1.0 is not valid SemVer string -* **A** :class:`Version ` **type and a** :func:`dict` +* **A** :class:`~semver.version.Version` **type and a** :func:`dict` You can also use a dictionary. In contrast to strings, you can have an "incomplete" version (as the other parts are set to zero):: diff --git a/docs/usage/convert-version-into-different-types.rst b/docs/usage/convert-version-into-different-types.rst index 976283d8..6948438c 100644 --- a/docs/usage/convert-version-into-different-types.rst +++ b/docs/usage/convert-version-into-different-types.rst @@ -3,23 +3,23 @@ Converting a Version instance into Different Types ================================================== -Sometimes it is needed to convert a :class:`Version ` instance into +Sometimes it is needed to convert a :class:`~semver.version.Version` instance into a different type. For example, for displaying or to access all parts. -It is possible to convert a :class:`Version ` instance: +It is possible to convert a :class:`~semver.version.Version` instance: * Into a string with the builtin function :func:`str`:: >>> str(Version.parse("3.4.5-pre.2+build.4")) '3.4.5-pre.2+build.4' -* Into a dictionary with :func:`to_dict `:: +* Into a dictionary with :meth:`~semver.version.Version.to_dict`:: >>> v = Version(major=3, minor=4, patch=5) >>> v.to_dict() OrderedDict([('major', 3), ('minor', 4), ('patch', 5), ('prerelease', None), ('build', None)]) -* Into a tuple with :func:`to_tuple `:: +* Into a tuple with :meth:`~semver.version.Version.to_tuple`:: >>> v = Version(major=5, minor=4, patch=2) >>> v.to_tuple() diff --git a/docs/usage/create-a-version.rst b/docs/usage/create-a-version.rst index 3acb4c03..48bb58a1 100644 --- a/docs/usage/create-a-version.rst +++ b/docs/usage/create-a-version.rst @@ -3,7 +3,7 @@ Creating a Version .. versionchanged:: 3.0.0 - The former :class:`~semver.version.VersionInfo` + The former :class:`~semver.version.VersionInfo` class has been renamed to :class:`~semver.version.Version`. The preferred way to create a new version is with the class @@ -15,7 +15,7 @@ The preferred way to create a new version is with the class create a version with module level functions. However, module level functions are marked as *deprecated* since version 2.x.y now. - These functions will be removed in semver 3.1.0. + These functions will be removed. For details, see the sections :ref:`sec_replace_deprecated_functions` and :ref:`sec_display_deprecation_warnings`. diff --git a/docs/usage/get-min-and-max-of-multiple-versions.rst b/docs/usage/get-min-and-max-of-multiple-versions.rst index 266ee50b..e143162a 100644 --- a/docs/usage/get-min-and-max-of-multiple-versions.rst +++ b/docs/usage/get-min-and-max-of-multiple-versions.rst @@ -7,9 +7,9 @@ Getting Minimum and Maximum of Multiple Versions The functions :func:`semver.max_ver` and :func:`semver.min_ver` are deprecated in favor of their builtin counterparts :func:`max` and :func:`min`. -Since :class:`Version ` implements -:func:`__gt__ ` and -:func:`__lt__ `, it can be used with builtins requiring: +Since :class:`~semver.version.Version` implements +:meth:`~semver.version.Version.__gt__` and +:meth:`~semver.version.Version.__lt__`, it can be used with builtins requiring: .. code-block:: python @@ -19,7 +19,7 @@ Since :class:`Version ` implements Version(major=0, minor=1, patch=0, prerelease=None, build=None) Incidentally, using :func:`map`, you can get the min or max version of any number of versions of the same type -(convertible to :class:`Version `). +(convertible to :class:`~semver.version.Version`). For example, here are the maximum and minimum versions of a list of version strings: @@ -40,12 +40,3 @@ And the same can be done with tuples: (0, 4, 99, None, None) For dictionaries, it is very similar to finding the max version tuple: see :ref:`sec.convert.versions`. - -The "old way" with :func:`semver.max_ver` or :func:`semver.min_ver` is still available, but not recommended: - -.. code-block:: python - - >>> semver.max_ver("1.0.0", "2.0.0") - '2.0.0' - >>> semver.min_ver("1.0.0", "2.0.0") - '1.0.0' diff --git a/docs/usage/increase-parts-of-a-version_prereleases.rst b/docs/usage/increase-parts-of-a-version_prereleases.rst index 98283937..845f2290 100644 --- a/docs/usage/increase-parts-of-a-version_prereleases.rst +++ b/docs/usage/increase-parts-of-a-version_prereleases.rst @@ -1,11 +1,13 @@ +.. _increase-parts-of-a-version: + Increasing Parts of a Version Taking into Account Prereleases ============================================================= .. versionadded:: 2.10.0 - Added :func:`Version.next_version `. + Added :meth:`~semver.version.Version.next_version`. If you want to raise your version and take prereleases into account, -the function :func:`next_version ` +the function :meth:`~semver.version.Version.next_version` would perhaps a better fit. diff --git a/docs/usage/index.rst b/docs/usage/index.rst index ddfc2284..4b8e3fc9 100644 --- a/docs/usage/index.rst +++ b/docs/usage/index.rst @@ -8,6 +8,7 @@ Using semver create-a-version parse-version-string check-valid-semver-version + check-compatible-semver-version access-parts-of-a-version access-parts-through-index replace-parts-of-a-version diff --git a/docs/usage/parse-version-string.rst b/docs/usage/parse-version-string.rst index 0a39c8a3..0cf02650 100644 --- a/docs/usage/parse-version-string.rst +++ b/docs/usage/parse-version-string.rst @@ -2,7 +2,7 @@ Parsing a Version String ======================== "Parsing" in this context means to identify the different parts in a string. -Use the function :func:`Version.parse `:: +Use the function :meth:`~semver.version.Version.parse`:: >>> Version.parse("3.4.5-pre.2+build.4") Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') diff --git a/docs/usage/raise-parts-of-a-version.rst b/docs/usage/raise-parts-of-a-version.rst index cc62fffb..be89cf8d 100644 --- a/docs/usage/raise-parts-of-a-version.rst +++ b/docs/usage/raise-parts-of-a-version.rst @@ -1,18 +1,32 @@ Raising Parts of a Version ========================== +.. note:: + + Keep in mind, "raising" the pre-release 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 + method :meth:`~semver.version.Version.next_version` + in section :ref:`increase-parts-of-a-version`. + + The ``semver`` module contains the following functions to raise parts of a version: -* :func:`Version.bump_major `: raises the major part and set all other parts to +* :meth:`~semver.version.Version.bump_major`: raises the major part and set all other parts to zero. Set ``prerelease`` and ``build`` to ``None``. -* :func:`Version.bump_minor `: raises the minor part and sets ``patch`` to zero. +* :meth:`~semver.version.Version.bump_minor`: raises the minor part and sets ``patch`` to zero. Set ``prerelease`` and ``build`` to ``None``. -* :func:`Version.bump_patch `: raises the patch part. Set ``prerelease`` and +* :meth:`~semver.version.Version.bump_patch`: raises the patch part. Set ``prerelease`` and ``build`` to ``None``. -* :func:`Version.bump_prerelease `: raises the prerelease part and set +* :meth:`~semver.version.Version.bump_prerelease`: raises the prerelease part and set ``build`` to ``None``. -* :func:`Version.bump_build `: raises the build part. +* :meth:`~semver.version.Version.bump_build`: raises the build part. + .. code-block:: python @@ -28,3 +42,29 @@ a version: '3.4.5-pre.2+build.5' Likewise the module level functions :func:`semver.bump_major`. + +For the methods :meth:`~semver.version.Version.bump_prerelease` +and :meth:`~semver.version.Version.bump_build` it's possible to pass an empty string or ``None``. +However, it gives different results: + +.. code-block:: python + + >>> str(Version.parse("3.4.5").bump_prerelease('')) + '3.4.5-1' + >>> str(Version.parse("3.4.5").bump_prerelease(None)) + '3.4.5-rc.1' + +An empty string removes any prefix whereas ``None`` is the same as calling +the method without any argument. + +If you already have a prerelease, the argument for the method +is not taken into account: + +.. code-block:: python + + >>> str(Version.parse("3.4.5-rc.1").bump_prerelease(None)) + '3.4.5-rc.2' + >>> str(Version.parse("3.4.5-rc.1").bump_prerelease('')) + '3.4.5-rc.2' + + diff --git a/docs/usage/replace-parts-of-a-version.rst b/docs/usage/replace-parts-of-a-version.rst index b6c38865..57ab65e9 100644 --- a/docs/usage/replace-parts-of-a-version.rst +++ b/docs/usage/replace-parts-of-a-version.rst @@ -4,25 +4,14 @@ Replacing Parts of a Version ============================ If you want to replace different parts of a version, but leave other parts -unmodified, use the function :func:`replace `: - -* From a :class:`Version ` instance:: +unmodified, use the function :meth:`~semver.version.Version.replace`: >>> version = semver.Version.parse("1.4.5-pre.1+build.6") >>> version.replace(major=2, minor=2) Version(major=2, minor=2, patch=5, prerelease='pre.1', build='build.6') -* From a version string:: - - >>> semver.replace("1.4.5-pre.1+build.6", major=2) - '2.4.5-pre.1+build.6' - If you pass invalid keys you get an exception:: - >>> semver.replace("1.2.3", invalidkey=2) - Traceback (most recent call last): - ... - TypeError: replace() got 1 unexpected keyword argument(s): invalidkey >>> version = semver.Version.parse("1.4.5-pre.1+build.6") >>> version.replace(invalidkey=2) Traceback (most recent call last): diff --git a/docs/usage/semver-version.rst b/docs/usage/semver-version.rst index 8eeab62f..e8cc92b3 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.0-dev.4' + '3.0.0-rc.1' diff --git a/setup.cfg b/setup.cfg index 87ec3b8f..0ee8564c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,7 +39,7 @@ license = BSD package_dir = =src packages = find: -python_requires = >=3.7.* +python_requires = >=3.7 include_package_data = True [options.entry_points] @@ -55,12 +55,12 @@ semver = py.typed [tool:pytest] norecursedirs = .git build .env/ env/ .pyenv/ .tmp/ .eggs/ venv/ testpaths = tests docs -# pythonpath = src +pythonpath = src tests filterwarnings = ignore:Function 'semver.*:DeprecationWarning # ' <- This apostroph is just to fix syntax highlighting addopts = - # --import-mode=importlib + --import-mode=importlib --no-cov-on-fail --cov=semver --cov-report=term-missing diff --git a/src/semver/__about__.py b/src/semver/__about__.py index 0f7150bf..dd671d02 100644 --- a/src/semver/__about__.py +++ b/src/semver/__about__.py @@ -16,7 +16,7 @@ """ #: Semver version -__version__ = "3.0.0-dev.4" +__version__ = "3.0.0-rc.1" #: Original semver author __author__ = "Kostiantyn Rybnikov" diff --git a/src/semver/__main__.py b/src/semver/__main__.py index a6d448aa..6cb11f09 100644 --- a/src/semver/__main__.py +++ b/src/semver/__main__.py @@ -25,4 +25,4 @@ def main(cliargs: Optional[List[str]] = None) -> int: if __name__ == "__main__": - sys.exit(main(sys.argv)) + sys.exit(main(sys.argv[1:])) diff --git a/src/semver/_deprecated.py b/src/semver/_deprecated.py index 5f51c8f3..8dfb1933 100644 --- a/src/semver/_deprecated.py +++ b/src/semver/_deprecated.py @@ -25,7 +25,7 @@ def deprecated( :param func: the function to decorate :param replace: the function to replace (use the full qualified - name like ``semver.Version.bump_major``. + name like ``semver.version.Version.bump_major``. :param version: the first version when this function was deprecated. :param category: allow you to specify the deprecation warning class of your choice. By default, it's :class:`DeprecationWarning`, but @@ -75,7 +75,7 @@ def parse(version): Parse version to major, minor, patch, pre-release, build parts. .. deprecated:: 2.10.0 - Use :func:`semver.Version.parse` instead. + Use :meth:`~semver.version.Version.parse` instead. :param version: version string :return: dictionary with the keys 'build', 'major', 'minor', 'patch', @@ -98,13 +98,13 @@ def parse(version): return Version.parse(version).to_dict() -@deprecated(replace="semver.Version.parse", version="2.10.0") +@deprecated(replace="semver.version.Version.parse", version="2.10.0") def parse_version_info(version): """ - Parse version string to a VersionInfo instance. + Parse version string to a Version instance. .. deprecated:: 2.10.0 - Use :func:`semver.VersionInfo.parse` instead. + Use :meth:`~semver.version.Version.parse` instead. .. versionadded:: 2.7.2 Added :func:`semver.parse_version_info` @@ -153,6 +153,9 @@ def match(version, match_expr): """ Compare two versions strings through a comparison. + .. deprecated:: 2.10.0 + Use :meth:`~semver.version.Version.match` instead. + :param str version: a version string :param str match_expr: operator and version; valid operators are < smaller than @@ -178,6 +181,9 @@ def max_ver(ver1, ver2): """ Returns the greater version of two versions strings. + .. deprecated:: 2.10.2 + Use :func:`max` instead. + :param ver1: version string 1 :param ver2: version string 2 :return: the greater version of the two @@ -202,6 +208,9 @@ def min_ver(ver1, ver2): """ Returns the smaller version of two versions strings. + .. deprecated:: 2.10.2 + Use Use :func:`min` instead. + :param ver1: version string 1 :param ver2: version string 2 :return: the smaller version of the two @@ -246,7 +255,7 @@ def bump_major(version): Raise the major part of the version string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.bump_major` instead. + Use :meth:`~semver.version.Version.bump_major` instead. :param: version string :return: the raised version string @@ -264,7 +273,7 @@ def bump_minor(version): Raise the minor part of the version string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.bump_minor` instead. + Use :meth:`~semver.version.Version.bump_minor` instead. :param: version string :return: the raised version string @@ -282,7 +291,7 @@ def bump_patch(version): Raise the patch part of the version string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.bump_patch` instead. + Use :meth:`~semver.version.Version.bump_patch` instead. :param: version string :return: the raised version string @@ -300,7 +309,7 @@ def bump_prerelease(version, token="rc"): Raise the prerelease part of the version string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.bump_prerelease` instead. + Use :meth:`~semver.version.Version.bump_prerelease` instead. :param version: version string :param token: defaults to 'rc' @@ -319,7 +328,7 @@ def bump_build(version, token="build"): Raise the build part of the version string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.bump_build` instead. + Use :meth:`~semver.version.Version.bump_build` instead. :param version: version string :param token: defaults to 'build' @@ -338,7 +347,7 @@ def finalize_version(version): Remove any prerelease and build metadata from the version string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.finalize_version` instead. + Use :meth:`~semver.version.Version.finalize_version` instead. .. versionadded:: 2.7.9 Added :func:`finalize_version` @@ -360,7 +369,7 @@ def replace(version, **parts): Replace one or more parts of a version and return the new string. .. deprecated:: 2.10.0 - Use :func:`semver.Version.replace` instead. + Use :meth:`~semver.version.Version.replace` instead. .. versionadded:: 2.9.0 Added :func:`replace` diff --git a/src/semver/cli.py b/src/semver/cli.py index 3c573d63..b2751429 100644 --- a/src/semver/cli.py +++ b/src/semver/cli.py @@ -54,7 +54,7 @@ def cmd_check(args: argparse.Namespace) -> None: :param args: The parsed arguments """ - if Version.isvalid(args.version): + if Version.is_valid(args.version): return None raise ValueError("Invalid version %r" % args.version) diff --git a/src/semver/version.py b/src/semver/version.py index 96281192..cca744a1 100644 --- a/src/semver/version.py +++ b/src/semver/version.py @@ -1,4 +1,4 @@ -"""Version handling.""" +"""Version handling by a semver compatible version class.""" import collections import re @@ -14,6 +14,8 @@ cast, Callable, Collection, + Type, + TypeVar, ) from ._types import ( @@ -28,6 +30,8 @@ Comparable = Union["Version", Dict[str, VersionPart], Collection[VersionPart], str] Comparator = Callable[["Version", Comparable], bool] +T = TypeVar("T", bound="Version") + def _comparator(operator: Comparator) -> Comparator: """Wrap a Version binary op method in a type-check.""" @@ -57,6 +61,8 @@ class Version: """ A semver compatible version class. + See specification at https://semver.org. + :param major: version when you make incompatible API changes. :param minor: version when you add functionality in a backwards-compatible manner. @@ -66,6 +72,10 @@ class Version: """ __slots__ = ("_major", "_minor", "_patch", "_prerelease", "_build") + + #: The names of the different parts of a version + NAMES = tuple([item[1:] for item in __slots__]) + #: Regex for number in a prerelease _LAST_NUMBER = re.compile(r"(?:[^\d]*(\d+)[^\d]*)+") #: Regex template for a semver version @@ -197,7 +207,7 @@ def to_tuple(self) -> VersionTuple: Convert the Version object to a tuple. .. versionadded:: 2.10.0 - Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to + Renamed :meth:`Version._astuple` to :meth:`Version.to_tuple` to make this function available in the public API. :return: a tuple with all the parts @@ -212,7 +222,7 @@ def to_dict(self) -> VersionDict: Convert the Version object to an OrderedDict. .. versionadded:: 2.10.0 - Renamed ``VersionInfo._asdict`` to ``VersionInfo.to_dict`` to + Renamed :meth:`Version._asdict` to :meth:`Version.to_dict` to make this function available in the public API. :return: an OrderedDict with the keys in the order ``major``, ``minor``, @@ -261,7 +271,6 @@ def bump_major(self) -> "Version": :return: new object with the raised major part - >>> ver = semver.parse("3.4.5") >>> ver.bump_major() Version(major=4, minor=0, patch=0, prerelease=None, build=None) @@ -297,30 +306,44 @@ def bump_patch(self) -> "Version": cls = type(self) return cls(self._major, self._minor, self._patch + 1) - def bump_prerelease(self, token: str = "rc") -> "Version": + def bump_prerelease(self, token: Optional[str] = "rc") -> "Version": """ Raise the prerelease part of the version, return a new object but leave self untouched. - :param token: defaults to ``rc`` - :return: new object with the raised prerelease part + :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() - Version(major=3, minor=4, patch=5, prerelease='rc.2', \ -build=None) + >>> ver.bump_prerelease().prerelease + 'rc.2' + >>> ver.bump_prerelease('').prerelease + '1' + >>> ver.bump_prerelease(None).prerelease + 'rc.1' """ cls = type(self) - prerelease = cls._increment_string(self._prerelease or (token or "rc") + ".0") + if self._prerelease is not None: + prerelease = self._prerelease + elif token == "": + prerelease = "0" + elif token is None: + prerelease = "rc.0" + else: + prerelease = str(token) + ".0" + + prerelease = cls._increment_string(prerelease) return cls(self._major, self._minor, self._patch, prerelease) - def bump_build(self, token: str = "build") -> "Version": + def bump_build(self, token: Optional[str] = "build") -> "Version": """ Raise the build part of the version, return a new object but leave self untouched. - :param token: defaults to ``build`` - :return: new object with the raised build part + :param token: defaults to ``'build'`` + :return: new :class:`Version` object with the raised build part. + The original object is not modified. >>> ver = semver.parse("3.4.5-rc.1+build.9") >>> ver.bump_build() @@ -328,7 +351,28 @@ def bump_build(self, token: str = "build") -> "Version": build='build.10') """ cls = type(self) - build = cls._increment_string(self._build or (token or "build") + ".0") + 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) + 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) def compare(self, other: Comparable) -> int: @@ -398,18 +442,12 @@ def next_version(self, part: str, prerelease_token: str = "rc") -> "Version": :param prerelease_token: prefix string of prerelease, defaults to 'rc' :return: new object with the appropriate part raised """ - validparts = { - "major", - "minor", - "patch", - "prerelease", - # "build", # currently not used - } + cls = type(self) + # "build" is currently not used, that's why we use [:-1] + validparts = cls.NAMES[:-1] if part not in validparts: raise ValueError( - "Invalid part. Expected one of {validparts}, but got {part!r}".format( - validparts=validparts, part=part - ) + f"Invalid part. Expected one of {validparts}, but got {part!r}" ) version = self if (version.prerelease or version.build) and ( @@ -419,7 +457,8 @@ def next_version(self, part: str, prerelease_token: str = "rc") -> "Version": ): return version.replace(prerelease=None, build=None) - if part in ("major", "minor", "patch"): + # Only check the main parts: + if part in cls.NAMES[:3]: return getattr(version, "bump_" + part)() if not version.prerelease: @@ -460,7 +499,7 @@ def __getitem__( is undefined, it will throw an index error. Negative indices are not supported. - :param Union[int, slice] index: a positive integer indicating the + :param index: a positive integer indicating the offset or a :func:`slice` object :raises IndexError: if index is beyond the range or a part is None :return: the requested part of the version at position index @@ -522,7 +561,7 @@ def match(self, match_expr: str) -> bool: Compare self to match a match expression. :param match_expr: optional operator and version; valid operators are - ``<``` smaller than + ``<`` smaller than ``>`` greater than ``>=`` greator or equal than ``<=`` smaller or equal than @@ -570,8 +609,8 @@ def match(self, match_expr: str) -> bool: @classmethod def parse( - cls, version: String, optional_minor_and_patch: bool = False - ) -> "Version": + cls: Type[T], version: String, optional_minor_and_patch: bool = False + ) -> T: """ Parse version string to a Version instance. @@ -579,8 +618,8 @@ def parse( Changed method from static to classmethod to allow subclasses. .. versionchanged:: 3.0.0 - Added optional parameter optional_minor_and_patch to allow optional - minor and patch parts. + Added optional parameter ``optional_minor_and_patch`` to allow + optional minor and patch parts. :param version: version string :param optional_minor_and_patch: if set to true, the version string to parse \ @@ -625,8 +664,8 @@ def replace(self, **parts: Union[int, Optional[str]]) -> "Version": :param parts: the parts to be updated. Valid keys are: ``major``, ``minor``, ``patch``, ``prerelease``, or ``build`` - :return: the new :class:`Version` object with the changed - parts + :return: the new :class:`~semver.version.Version` object with + the changed parts :raises TypeError: if ``parts`` contain invalid keys """ version = self.to_dict() @@ -642,12 +681,15 @@ def replace(self, **parts: Union[int, Optional[str]]) -> "Version": raise TypeError(error) @classmethod - def isvalid(cls, version: str) -> bool: + def is_valid(cls, version: str) -> bool: """ Check if the string is a valid semver version. .. versionadded:: 2.9.1 + .. versionchanged:: 3.0.0 + Renamed from :meth:`~semver.version.Version.isvalid` + :param version: the version string to check :return: True if the version string is a valid semver version, False otherwise. @@ -658,6 +700,44 @@ def isvalid(cls, version: str) -> bool: except ValueError: return False + def is_compatible(self, other: "Version") -> bool: + """ + Check if current version is compatible with other version. + + The result is True, if either of the following is true: + + * both versions are equal, or + * both majors are equal and higher than 0. Same for both minors. + Both pre-releases are equal, or + * both majors are equal and higher than 0. The minor of b's + minor version is higher then a's. Both pre-releases are equal. + + The algorithm does *not* check patches. + + .. versionadded:: 3.0.0 + + :param other: the version to check for compatibility + :return: True, if ``other`` is compatible with the old version, + otherwise False + + >>> Version(1, 1, 0).is_compatible(Version(1, 0, 0)) + False + >>> Version(1, 0, 0).is_compatible(Version(1, 1, 0)) + True + """ + if not isinstance(other, Version): + raise TypeError(f"Expected a Version type but got {type(other)}") + + # All major-0 versions should be incompatible with anything but itself + if (0 == self.major == other.major) and (self[:4] != other[:4]): + return False + + return ( + (self.major == other.major) + and (other.minor >= self.minor) + and (self.prerelease == other.prerelease) + ) + #: Keep the VersionInfo name for compatibility VersionInfo = Version diff --git a/tests/conftest.py b/tests/conftest.py index beecffc9..9017bbbe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,8 +4,6 @@ import semver -# sys.path.insert(0, "docs/usage") - from coerce import coerce # noqa:E402 from semverwithvprefix import SemVerWithVPrefix # noqa:E402 import packaging.version diff --git a/tests/test_bump.py b/tests/test_bump.py index c28e1905..34e0b2ac 100644 --- a/tests/test_bump.py +++ b/tests/test_bump.py @@ -66,6 +66,30 @@ def test_should_versioninfo_bump_multiple(): assert v.bump_prerelease().bump_build().bump_build().bump_prerelease() == expected +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 + + +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 + + +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 + + +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 + + def test_should_ignore_extensions_for_bump(): assert bump_patch("3.4.5-rc1+build4") == "3.4.6" diff --git a/tests/test_semver.py b/tests/test_semver.py index b15bfeaf..782d5c79 100644 --- a/tests/test_semver.py +++ b/tests/test_semver.py @@ -73,10 +73,65 @@ def test_should_be_able_to_use_integers_as_prerelease_build(): def test_should_versioninfo_isvalid(): - assert Version.isvalid("1.0.0") is True - assert Version.isvalid("foo") is False + assert Version.is_valid("1.0.0") is True + assert Version.is_valid("foo") is False def test_versioninfo_compare_should_raise_when_passed_invalid_value(): with pytest.raises(TypeError): Version(1, 2, 3).compare(4) + + +@pytest.mark.parametrize( + "old, new", + [ + ((1, 2, 3), (1, 2, 3)), + ((1, 2, 3), (1, 2, 4)), + ((1, 2, 4), (1, 2, 3)), + ((1, 2, 3, "rc.0"), (1, 2, 4, "rc.0")), + ((0, 1, 0), (0, 1, 0)), + ], +) +def test_should_succeed_compatible_match(old, new): + old = Version(*old) + new = Version(*new) + assert old.is_compatible(new) + + +@pytest.mark.parametrize( + "old, new", + [ + ((1, 1, 0), (1, 0, 0)), + ((2, 0, 0), (1, 5, 0)), + ((1, 2, 3, "rc.1"), (1, 2, 3, "rc.0")), + ((1, 2, 3, "rc.1"), (1, 2, 4, "rc.0")), + ((0, 1, 0), (0, 1, 1)), + ((1, 0, 0), (1, 0, 0, "rc1")), + ((1, 0, 0, "rc1"), (1, 0, 0)), + ], +) +def test_should_fail_compatible_match(old, new): + old = Version(*old) + new = Version(*new) + assert not old.is_compatible(new) + + +@pytest.mark.parametrize( + "wrongtype", + [ + "wrongtype", + dict(a=2), + list(), + ], +) +def test_should_fail_with_incompatible_type_for_compatible_match(wrongtype): + with pytest.raises(TypeError, match="Expected a Version type .*"): + v = Version(1, 2, 3) + v.is_compatible(wrongtype) + + +def test_should_succeed_with_compatible_subclass_for_is_compatible(): + class CustomVersion(Version): + ... + + assert CustomVersion(1, 0, 0).is_compatible(Version(1, 0, 0)) diff --git a/tox.ini b/tox.ini index 8ca917b8..b71ae78e 100644 --- a/tox.ini +++ b/tox.ini @@ -18,6 +18,7 @@ python = [testenv] description = Run test suite for {basepython} +skip_install = true allowlist_externals = make commands = pytest {posargs:} deps = @@ -55,7 +56,7 @@ commands = mypy {posargs:--ignore-missing-imports --check-untyped-defs src} description = Check for PEP257 compatible docstrings basepython = python3 deps = docformatter -commands = docformatter --check {posargs:--pre-summary-newline -r src} +commands = docformatter --check --diff {posargs:--pre-summary-newline -r src} [testenv:checks] 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