From f0ed4703b8d36af9f301afbc4c2c44d03c5e11f2 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sun, 1 Nov 2020 23:48:22 +0100 Subject: [PATCH 01/26] Add CONTRIBUTING.rst in root folder Make link in docs/development.rst --- CONTRIBUTING.rst | 241 ++++++++++++++++++++++++++++++++++++++++++ docs/development.rst | 242 +------------------------------------------ 2 files changed, 242 insertions(+), 241 deletions(-) create mode 100644 CONTRIBUTING.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..049fe1a3 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,241 @@ +.. _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 + + +Reporting Bugs and Feedback +--------------------------- + +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. + +First, take the time to look into our GitHub `issues`_ tracker if +this already covered. If not, changes are good that we avoid double work. + + +Prerequisites +------------- + +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. + + + +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:`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 only specific Python versions. Use the ``-e`` + option and one or more abbreviations (``py27`` for Python 2.7, ``py34`` for + Python 3.4 etc.):: + + $ tox -e py34 + $ tox -e py27,py34 + + To get a complete list, run:: + + $ tox -l + +* 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_semver.py` for all + Python versions:: + + $ tox test_semver.py::test_immutable_major + + By default, pytest prints a dot for each test function only. 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 2.7 and 3.6 only:: + + $ tox -e py27,py36 test_semver.py::test_immutable_major + +Our code is checked against `flake8`_ for style guide issues. It is recommended +to run your tests in combination with :command:`flake8`, for example:: + + $ tox -e py27,py36,flake8 + + +.. _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:`dist/docs`. + + +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 compare(ver1, ver2): + """Compare two versions + + :param ver1: version string 1 + :param ver2: version string 2 + :return: The return value is negative if ver1 < ver2, + zero if ver1 == ver2 and strictly positive if ver1 > ver2 + :rtype: int + + >>> semver.compare("1.0.0", "2.0.0") + -1 + >>> semver.compare("2.0.0", "1.0.0") + 1 + >>> semver.compare("2.0.0", "2.0.0") + 0 + + """ + + * **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, if needed. + An appropriate directive could look like this:: + + def to_tuple(self): + """ + Convert the VersionInfo object to a tuple. + + .. versionadded:: 2.10.0 + Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to + make this function available in the public API. + [...] + """ + + * **The documentation** + + A docstring is good, but in most cases it's too dense. 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. + + +.. _changelog: + +Adding a Changelog Entry +------------------------ + +.. include:: ../changelog.d/README.rst + :start-after: -text-begin- + + +.. _flake8: https://flake8.readthedocs.io +.. _issues: https://github.com/python-semver/python-semver/issues +.. _pull request: https://github.com/python-semver/python-semver/pulls +.. _pytest: http://pytest.org/ +.. _Semantic Versioning: https://semver.org +.. _Sphinx style: https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html +.. _tox: https://tox.readthedocs.org/ + diff --git a/docs/development.rst b/docs/development.rst index 049fe1a3..e582053e 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -1,241 +1 @@ -.. _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 - - -Reporting Bugs and Feedback ---------------------------- - -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. - -First, take the time to look into our GitHub `issues`_ tracker if -this already covered. If not, changes are good that we avoid double work. - - -Prerequisites -------------- - -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. - - - -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:`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 only specific Python versions. Use the ``-e`` - option and one or more abbreviations (``py27`` for Python 2.7, ``py34`` for - Python 3.4 etc.):: - - $ tox -e py34 - $ tox -e py27,py34 - - To get a complete list, run:: - - $ tox -l - -* 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_semver.py` for all - Python versions:: - - $ tox test_semver.py::test_immutable_major - - By default, pytest prints a dot for each test function only. 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 2.7 and 3.6 only:: - - $ tox -e py27,py36 test_semver.py::test_immutable_major - -Our code is checked against `flake8`_ for style guide issues. It is recommended -to run your tests in combination with :command:`flake8`, for example:: - - $ tox -e py27,py36,flake8 - - -.. _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:`dist/docs`. - - -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 compare(ver1, ver2): - """Compare two versions - - :param ver1: version string 1 - :param ver2: version string 2 - :return: The return value is negative if ver1 < ver2, - zero if ver1 == ver2 and strictly positive if ver1 > ver2 - :rtype: int - - >>> semver.compare("1.0.0", "2.0.0") - -1 - >>> semver.compare("2.0.0", "1.0.0") - 1 - >>> semver.compare("2.0.0", "2.0.0") - 0 - - """ - - * **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, if needed. - An appropriate directive could look like this:: - - def to_tuple(self): - """ - Convert the VersionInfo object to a tuple. - - .. versionadded:: 2.10.0 - Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to - make this function available in the public API. - [...] - """ - - * **The documentation** - - A docstring is good, but in most cases it's too dense. 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. - - -.. _changelog: - -Adding a Changelog Entry ------------------------- - -.. include:: ../changelog.d/README.rst - :start-after: -text-begin- - - -.. _flake8: https://flake8.readthedocs.io -.. _issues: https://github.com/python-semver/python-semver/issues -.. _pull request: https://github.com/python-semver/python-semver/pulls -.. _pytest: http://pytest.org/ -.. _Semantic Versioning: https://semver.org -.. _Sphinx style: https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html -.. _tox: https://tox.readthedocs.org/ - +.. include:: ../CONTRIBUTING.rst From 42bf3e27e683d4235dc28ede615cb9b96c9088aa Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Mon, 2 Nov 2020 00:09:59 +0100 Subject: [PATCH 02/26] Correct CONTRIBUTING text * Remove old occurances of Python 2.7 * Correct wrong single test for tox * Use better example * Use references to black, flake8, mypy, and docformatter --- CONTRIBUTING.rst | 81 +++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 049fe1a3..ac40563b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -94,40 +94,42 @@ You can decide to run the complete test suite or only part of it: $ tox --skip-missing-interpreters - It is possible to use only specific Python versions. Use the ``-e`` - option and one or more abbreviations (``py27`` for Python 2.7, ``py34`` for - Python 3.4 etc.):: + It is possible to use one or more specific Python versions. Use the ``-e`` + option and one or more abbreviations (``py36`` for Python 3.6, ``py37`` for + Python 3.7 etc.):: - $ tox -e py34 - $ tox -e py27,py34 + $ tox -e py36 + $ tox -e py36,py37 - To get a complete list, run:: + To get a complete list and a short description, run:: - $ tox -l + $ 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_semver.py` for all + :func:`test_immutable_major` in the file :file:`test_bump.py` for all Python versions:: - $ tox test_semver.py::test_immutable_major + $ tox -e py36 -- tests/test_bump.py::test_should_bump_major - By default, pytest prints a dot for each test function only. To + 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 2.7 and 3.6 only:: + example, to limit the tests for Python 3.6 and 3.7 only:: - $ tox -e py27,py36 test_semver.py::test_immutable_major + $ tox -e py36,py37 -- tests/test_bump.py::test_should_bump_major -Our code is checked against `flake8`_ for style guide issues. It is recommended -to run your tests in combination with :command:`flake8`, for example:: +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 py27,py36,flake8 + $ tox -e checks,py36,py37 .. _doc: @@ -145,7 +147,7 @@ used efficiently. $ tox -e docs - The built documentation is available in :file:`dist/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 @@ -159,22 +161,18 @@ documentation includes: The docstring is extracted and reused in the :ref:`api` section. An appropriate docstring should look like this:: - def compare(ver1, ver2): - """Compare two versions + def to_tuple(self) -> VersionTuple: + """ + Convert the Version object to a tuple. - :param ver1: version string 1 - :param ver2: version string 2 - :return: The return value is negative if ver1 < ver2, - zero if ver1 == ver2 and strictly positive if ver1 > ver2 - :rtype: int + .. versionadded:: 2.10.0 + Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to + make this function available in the public API. - >>> semver.compare("1.0.0", "2.0.0") - -1 - >>> semver.compare("2.0.0", "1.0.0") - 1 - >>> semver.compare("2.0.0", "2.0.0") - 0 + :return: a tuple with all the parts + >>> semver.Version(5, 3, 1).to_tuple() + (5, 3, 1, None, None) """ * **An optional directive** @@ -201,22 +199,12 @@ documentation includes: be used instead, if appropriate. - Add such a directive *after* the summary line, if needed. - An appropriate directive could look like this:: - - def to_tuple(self): - """ - Convert the VersionInfo object to a tuple. - - .. versionadded:: 2.10.0 - Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to - make this function available in the public API. - [...] - """ + 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. Describe how + 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. @@ -231,11 +219,14 @@ Adding a Changelog Entry :start-after: -text-begin- -.. _flake8: https://flake8.readthedocs.io +.. _black: https://black.rtfd.io +.. _docformatter: https://pypi.org/project/docformatter/ +.. _flake8: https://flake8.rtfd.io +.. _mypy: http://mypy-lang.org/ .. _issues: https://github.com/python-semver/python-semver/issues .. _pull request: https://github.com/python-semver/python-semver/pulls .. _pytest: http://pytest.org/ .. _Semantic Versioning: https://semver.org -.. _Sphinx style: https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html -.. _tox: https://tox.readthedocs.org/ +.. _Sphinx style: https://sphinx-rtd-tutorial.rtfd.io/en/latest/docstrings.html +.. _tox: https://tox.rtfd.org/ From dcdcd2ab744776f830ead0073b28f885a2b5e0e0 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Tue, 3 Nov 2020 17:24:31 +0100 Subject: [PATCH 03/26] Fix #310: Correct API documentation * Remove all automatic generation and use a more "semi-manual" approach (gives more control) * Improve docstrings in semver module * Remove docstrings in some dunder methods; Sphinx and autodoc uses the docstring from the parent class * Remove sphinx-apidoc command from :file:`tox.ini` --- changelog.d/310.bugfix.rst | 3 ++ docs/_api/semver.__about__.rst | 5 --- docs/_static/css/custom.css | 4 +++ docs/api.rst | 65 ++++++++++++++++++++++++++++++---- docs/conf.py | 10 +++--- src/semver/__about__.py | 8 +++++ src/semver/_types.py | 2 ++ src/semver/cli.py | 12 ++++++- src/semver/version.py | 53 +++++++++++++-------------- tests/conftest.py | 2 +- tox.ini | 8 ----- 11 files changed, 119 insertions(+), 53 deletions(-) create mode 100644 changelog.d/310.bugfix.rst delete mode 100644 docs/_api/semver.__about__.rst diff --git a/changelog.d/310.bugfix.rst b/changelog.d/310.bugfix.rst new file mode 100644 index 00000000..6b042982 --- /dev/null +++ b/changelog.d/310.bugfix.rst @@ -0,0 +1,3 @@ +Rework API documentation. +Follow a more "semi-manual" attempt and add auto directives +into :file:`docs/api.rst`. \ No newline at end of file diff --git a/docs/_api/semver.__about__.rst b/docs/_api/semver.__about__.rst deleted file mode 100644 index 22395ebd..00000000 --- a/docs/_api/semver.__about__.rst +++ /dev/null @@ -1,5 +0,0 @@ -semver.\_\_about\_\_ module -=========================== - -.. automodule:: semver.__about__ - :members: diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css index 002e6b2f..81a5906f 100644 --- a/docs/_static/css/custom.css +++ b/docs/_static/css/custom.css @@ -35,6 +35,10 @@ div.related.top nav { font-weight: 700; } +.py.class { + margin-top: 1.5em; +} + .py.method { padding-top: 0.25em; padding-bottom: 1.25em; diff --git a/docs/api.rst b/docs/api.rst index 9d884601..d35c48fe 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,10 +1,63 @@ .. _api: -### -API -### +API Reference +============= -.. toctree:: - :maxdepth: 4 +.. currentmodule:: semver - _api/semver \ No newline at end of file + +Metadata :mod:`semver.__about__` +-------------------------------- + +.. automodule:: semver.__about__ + + +Deprecated Functions in :mod:`semver._deprecated` +------------------------------------------------- + +.. automodule:: semver._deprecated + +.. autofunction:: semver._deprecated.deprecated + + +CLI Parsing :mod:`semver.cli` +----------------------------- + +.. automodule:: semver.cli + +.. autofunction:: semver.cli.cmd_bump + +.. autofunction:: semver.cli.cmd_check + +.. autofunction:: semver.cli.cmd_compare + +.. autofunction:: semver.cli.createparser + +.. autofunction:: semver.cli.main + +.. autofunction:: semver.cli.process + + +Entry point :mod:`semver.__main__` +---------------------------------- + +.. automodule:: semver.__main__ + + + +Version Handling :mod:`semver.version` +-------------------------------------- + +.. automodule:: semver.version + +.. autofunction:: semver.version.cmp + +.. autofunction:: semver.version.ensure_str + +.. autofunction:: semver.version.comparator + +.. autoclass:: semver.version.VersionInfo + +.. autoclass:: semver.version.Version + :members: + :special-members: __iter__, __eq__, __ne__, __lt__, __le__, __gt__, __ge__, __getitem__, __hash__, __repr__, __str__ diff --git a/docs/conf.py b/docs/conf.py index f5e04b19..52a46704 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,8 @@ import re import sys -sys.path.insert(0, os.path.abspath("../src/")) +SRC_DIR = os.path.abspath("../src/") +sys.path.insert(0, SRC_DIR) # from semver import __version__ # noqa: E402 @@ -58,15 +59,16 @@ def find_version(*file_paths): # ones. extensions = [ "sphinx.ext.autodoc", - "sphinx.ext.autosummary", "sphinx_autodoc_typehints", "sphinx.ext.intersphinx", "sphinx.ext.extlinks", ] +# Autodoc configuration autoclass_content = "class" -autodoc_default_options = {} - +autodoc_typehints = "signature" +autodoc_member_order = "alphabetical" +add_function_parentheses = True # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/src/semver/__about__.py b/src/semver/__about__.py index aa293425..1f5fcae5 100644 --- a/src/semver/__about__.py +++ b/src/semver/__about__.py @@ -4,7 +4,15 @@ Contains information about semver's version, the implemented version of the semver specifictation, author, maintainers, and description. +.. autodata:: __author__ + +.. autodata:: __description__ + +.. autodata:: __maintainer__ + .. autodata:: __version__ + +.. autodata:: SEMVER_SPEC_VERSION """ #: Semver version diff --git a/src/semver/_types.py b/src/semver/_types.py index 823c7349..4f004a29 100644 --- a/src/semver/_types.py +++ b/src/semver/_types.py @@ -1,3 +1,5 @@ +"""Typing for semver.""" + from typing import Union, Optional, Tuple, Dict, Iterable, Callable, TypeVar VersionPart = Union[int, Optional[str]] diff --git a/src/semver/cli.py b/src/semver/cli.py index ca400373..65ca5187 100644 --- a/src/semver/cli.py +++ b/src/semver/cli.py @@ -1,4 +1,14 @@ -"""CLI parsing for :command:`pysemver` command.""" +""" +CLI parsing for :command:`pysemver` command. + +Each command in :command:`pysemver` is mapped to a ``cmd_`` function. +The :func:`main ` function calls +:func:`createparser ` and +:func:`process ` to parse and process +all the commandline options. + +The result of each command is printed on stdout. +""" import argparse import sys diff --git a/src/semver/version.py b/src/semver/version.py index 64353011..40132526 100644 --- a/src/semver/version.py +++ b/src/semver/version.py @@ -43,18 +43,14 @@ def ensure_str(s: String, encoding="utf-8", errors="strict") -> str: * `bytes` -> decoded to `str` :param s: the string to convert - :type s: str | bytes :param encoding: the encoding to apply, defaults to "utf-8" - :type encoding: str :param errors: set a different error handling scheme, defaults to "strict". Other possible values are `ignore`, `replace`, and `xmlcharrefreplace` as well as any other name registered with :func:`codecs.register_error`. - :type errors: str :raises TypeError: if ``s`` is not str or bytes type :return: the converted string - :rtype: str """ if isinstance(s, bytes): s = s.decode(encoding, errors) @@ -218,7 +214,7 @@ def build(self, value): def to_tuple(self) -> VersionTuple: """ - Convert the VersionInfo object to a tuple. + Convert the Version object to a tuple. .. versionadded:: 2.10.0 Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to @@ -233,7 +229,7 @@ def to_tuple(self) -> VersionTuple: def to_dict(self) -> VersionDict: """ - Convert the VersionInfo object to an OrderedDict. + Convert the Version object to an OrderedDict. .. versionadded:: 2.10.0 Renamed ``VersionInfo._asdict`` to ``VersionInfo.to_dict`` to @@ -257,7 +253,7 @@ def to_dict(self) -> VersionDict: ) def __iter__(self) -> VersionIterator: - """Implement iter(self).""" + """Return iter(self).""" yield from self.to_tuple() @staticmethod @@ -300,7 +296,6 @@ def bump_minor(self) -> "Version": :return: new object with the raised minor part - >>> ver = semver.parse("3.4.5") >>> ver.bump_minor() Version(major=3, minor=5, patch=0, prerelease=None, build=None) @@ -313,8 +308,7 @@ def bump_patch(self) -> "Version": Raise the patch part of the version, return a new object but leave self untouched. - :return: new object with the raised patch part - + :return: new object with the raised patch part >>> ver = semver.parse("3.4.5") >>> ver.bump_patch() @@ -328,7 +322,7 @@ def bump_prerelease(self, token: str = "rc") -> "Version": Raise the prerelease part of the version, return a new object but leave self untouched. - :param token: defaults to 'rc' + :param token: defaults to ``rc`` :return: new object with the raised prerelease part >>> ver = semver.parse("3.4.5") @@ -345,7 +339,7 @@ def bump_build(self, token: str = "build") -> "Version": Raise the build part of the version, return a new object but leave self untouched. - :param token: defaults to 'build' + :param token: defaults to ``build`` :return: new object with the raised build part >>> ver = semver.parse("3.4.5-rc.1+build.9") @@ -365,7 +359,6 @@ def compare(self, other: Comparable) -> int: :return: The return value is negative if ver1 < ver2, zero if ver1 == ver2 and strictly positive if ver1 > ver2 - >>> semver.compare("2.0.0") -1 >>> semver.compare("1.0.0") @@ -481,14 +474,17 @@ def __getitem__( self, index: Union[int, slice] ) -> Union[int, Optional[str], Tuple[Union[int, str], ...]]: """ - self.__getitem__(index) <==> self[index] Implement getitem. If the part - requested is undefined, or a part of the range requested is undefined, - it will throw an index error. Negative indices are not supported. + self.__getitem__(index) <==> self[index] Implement getitem. + + If the part requested is undefined, or a part of the range requested + is undefined, it will throw an index error. + Negative indices are not supported. :param Union[int, slice] 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 + >>> ver = semver.Version.parse("3.4.5") >>> ver[0], ver[1], ver[2] (3, 4, 5) @@ -519,7 +515,6 @@ def __repr__(self) -> str: return "%s(%s)" % (type(self).__name__, s) def __str__(self) -> str: - """str(self)""" version = "%d.%d.%d" % (self.major, self.minor, self.patch) if self.prerelease: version += "-%s" % self.prerelease @@ -533,7 +528,9 @@ def __hash__(self) -> int: def finalize_version(self) -> "Version": """ Remove any prerelease and build metadata from the version. + :return: a new instance with the finalized version string + >>> str(semver.Version.parse('1.2.3-rc.5').finalize_version()) '1.2.3' """ @@ -545,12 +542,12 @@ def match(self, match_expr: str) -> bool: Compare self to match a match expression. :param match_expr: operator and version; valid operators are - < smaller than - > greater than - >= greator or equal than - <= smaller or equal than - == equal - != not equal + ``<``` smaller than + ``>`` greater than + ``>=`` greator or equal than + ``<=`` smaller or equal than + ``==`` equal + ``!=`` not equal :return: True if the expression matches the version, otherwise False >>> semver.Version.parse("2.0.0").match(">=1.0.0") @@ -589,18 +586,18 @@ def match(self, match_expr: str) -> bool: @classmethod def parse(cls, version: String) -> "Version": """ - Parse version string to a VersionInfo instance. + Parse version string to a Version instance. .. versionchanged:: 2.11.0 Changed method from static to classmethod to allow subclasses. :param version: version string - :return: a :class:`VersionInfo` instance + :return: a new :class:`Version` instance :raises ValueError: if version is invalid >>> semver.Version.parse('3.4.5-pre.2+build.4') - VersionInfo(major=3, minor=4, patch=5, \ + Version(major=3, minor=4, patch=5, \ prerelease='pre.2', build='build.4') """ version_str = ensure_str(version) @@ -624,7 +621,7 @@ def replace(self, **parts: Union[int, Optional[str]]) -> "Version": ``major``, ``minor``, ``patch``, ``prerelease``, or ``build`` :return: the new :class:`Version` object with the changed parts - :raises TypeError: if ``parts`` contains invalid keys + :raises TypeError: if ``parts`` contain invalid keys """ version = self.to_dict() version.update(parts) @@ -632,7 +629,7 @@ def replace(self, **parts: Union[int, Optional[str]]) -> "Version": return Version(**version) # type: ignore except TypeError: unknownkeys = set(parts) - set(self.to_dict()) - error = "replace() got %d unexpected keyword " "argument(s): %s" % ( + error = "replace() got %d unexpected keyword argument(s): %s" % ( len(unknownkeys), ", ".join(unknownkeys), ) diff --git a/tests/conftest.py b/tests/conftest.py index f7f927cf..0450e0ee 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ @pytest.fixture(autouse=True) def add_semver(doctest_namespace): - doctest_namespace["Version"] = semver.Version + doctest_namespace["Version"] = semver.version.Version doctest_namespace["semver"] = semver doctest_namespace["coerce"] = coerce doctest_namespace["SemVerWithVPrefix"] = SemVerWithVPrefix diff --git a/tox.ini b/tox.ini index 73fbfc58..560084ef 100644 --- a/tox.ini +++ b/tox.ini @@ -67,15 +67,7 @@ deps = -r{toxinidir}/docs/requirements.txt skip_install = true allowlist_externals = make - rm echo - sed -commands_pre = - sphinx-apidoc --module-first -f --separate -H semver -o docs/_api src/semver src/semver/_types.py src/semver/_deprecated.py - # we don't need this, it just add another level and it's all in docs/api.rst - - rm docs/_api/modules.rst - # Include the semver.__about__ module before semver.cli: - sed -i '/semver\.cli/i\ \ \ semver.__about__' docs/_api/semver.rst commands = make -C docs html commands_post = From ff204d71e9665b454bcc2a13a911d39aac81ab01 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Tue, 3 Nov 2020 21:44:14 +0100 Subject: [PATCH 04/26] Remove already inserted changelog.d entry --- changelog.d/213.improvement.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog.d/213.improvement.rst diff --git a/changelog.d/213.improvement.rst b/changelog.d/213.improvement.rst deleted file mode 100644 index dcedc695..00000000 --- a/changelog.d/213.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Add typing information \ No newline at end of file From a6a8115cc7feef77ad6d4db03260eaf73a66b911 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Tue, 3 Nov 2020 23:53:44 +0100 Subject: [PATCH 05/26] Tox: skip installation for changelog target We only need to install towncrier, but it is not needed to install semver too. Therefor, skip the installation of semver. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 73fbfc58..5ba0ba13 100644 --- a/tox.ini +++ b/tox.ini @@ -103,6 +103,7 @@ commands = [testenv:changelog] description = Run towncrier to check, build, or create the CHANGELOG.rst basepython = python3 +skip_install = true deps = git+https://github.com/twisted/towncrier.git commands = From 40c26e5c00c51507b4ec91b57d00f0e641af5d67 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Wed, 4 Nov 2020 00:00:32 +0100 Subject: [PATCH 06/26] Add changelog.d entry file for #313 --- changelog.d/313.trivial.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/313.trivial.rst diff --git a/changelog.d/313.trivial.rst b/changelog.d/313.trivial.rst new file mode 100644 index 00000000..963b4f31 --- /dev/null +++ b/changelog.d/313.trivial.rst @@ -0,0 +1,3 @@ +Correct :file:`tox.ini` for ``changelog`` entry to skip +installation for semver. This should speed up the execution +of towncrier. From a8dd4eae7a57fb5f56e3390998d8b8b09930770c Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Tue, 3 Nov 2020 21:48:58 +0100 Subject: [PATCH 07/26] Fix #312: Rework Usage section * Correct :class: directive and use :class:`~semver.version.Version` * Mention the rename of VersionInfo -> Version class * Remove semver. prefix in doctests to make examples shorter * Correct some references to dunder methods like __getitem__, __gt__ etc. * Use :py:exec: reference to Python exceptions. * Remove inconsistencies and mention module level function as deprecated and discouraged from using * Make empty super() call in semverwithvprefix.py example * Add changelog.d file --- CHANGELOG.rst | 53 +++++++++ changelog.d/312.doc.rst | 11 ++ docs/semverwithvprefix.py | 11 +- docs/usage.rst | 245 +++++++++++++++++--------------------- 4 files changed, 178 insertions(+), 142 deletions(-) create mode 100644 changelog.d/312.doc.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 26f8ff79..2e3f97a5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,59 @@ in our repository. .. towncrier release notes start +Version 3.0.0-dev.2 +=================== + +:Released: 2020-11-04 +:Maintainer: + + +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 + + + +---- + + +Version 3.0.0-dev.2 +=================== + +:Released: 2020-11-04 +:Maintainer: + + +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:`__getitem__`, + :func:`__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 + + + +---- + + Version 3.0.0-dev.2 =================== diff --git a/changelog.d/312.doc.rst b/changelog.d/312.doc.rst new file mode 100644 index 00000000..6b18eb49 --- /dev/null +++ b/changelog.d/312.doc.rst @@ -0,0 +1,11 @@ +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 diff --git a/docs/semverwithvprefix.py b/docs/semverwithvprefix.py index 304ce772..5e375031 100644 --- a/docs/semverwithvprefix.py +++ b/docs/semverwithvprefix.py @@ -7,15 +7,13 @@ class SemVerWithVPrefix(Version): """ @classmethod - def parse(cls, version): + def parse(cls, version: str) -> "SemVerWithVPrefix": """ Parse version string to a Version instance. :param version: version string with "v" or "V" prefix - :type version: str :raises ValueError: when version does not start with "v" or "V" :return: a new instance - :rtype: :class:`SemVerWithVPrefix` """ if not version[0] in ("v", "V"): raise ValueError( @@ -23,9 +21,8 @@ def parse(cls, version): v=version ) ) - self = super(SemVerWithVPrefix, cls).parse(version[1:]) - return self + return super().parse(version[1:]) - def __str__(self): + def __str__(self) -> str: # Reconstruct the tag - return "v" + super(SemVerWithVPrefix, self).__str__() + return "v" + super().__str__() diff --git a/docs/usage.rst b/docs/usage.rst index 94a115a8..ad44ecaf 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -1,7 +1,7 @@ Using semver ============ -The ``semver`` module can store a version in the :class:`semver.Version` class. +The :mod:`semver` module can store a version in the :class:`~semver.version.Version` class. For historical reasons, a version can be also stored as a string or dictionary. Each type can be converted into the other, if the minimum requirements @@ -32,70 +32,54 @@ To know the version of semver itself, use the following construct:: Creating a Version ------------------ -Due to historical reasons, the semver project offers two ways of -creating a version: +.. versionchanged:: 3.0.0 -* through an object oriented approach with the :class:`semver.Version` - class. This is the preferred method when using semver. + The former :class:`~semver.version.VersionInfo` + has been renamed to :class:`~semver.version.Version`. -* through module level functions and builtin datatypes (usually string - and dict). - This method is still available for compatibility reasons, but are - marked as deprecated. Using it will emit a :class:`DeprecationWarning`. +The preferred way to create a new version is with the class +:class:`~semver.version.Version`. +.. note:: -.. warning:: **Deprecation Warning** + In the previous major release semver 2 it was possible to + 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. + For details, see the sections :ref:`sec_replace_deprecated_functions` + and :ref:`sec_display_deprecation_warnings`. - Module level functions are marked as *deprecated* in version 2.x.y now. - These functions will be removed in semver 3. - For details, see the sections :ref:`sec_replace_deprecated_functions` and - :ref:`sec_display_deprecation_warnings`. +A :class:`~semver.version.Version` instance can be created in different ways: +* From a Unicode string:: -A :class:`semver.Version` instance can be created in different ways: - -* From a string (a Unicode string in Python 2):: - - >>> semver.Version.parse("3.4.5-pre.2+build.4") + >>> from semver.version import Version + >>> Version.parse("3.4.5-pre.2+build.4") Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') - >>> semver.Version.parse(u"5.3.1") + >>> Version.parse(u"5.3.1") Version(major=5, minor=3, patch=1, prerelease=None, build=None) * From a byte string:: - >>> semver.Version.parse(b"2.3.4") + >>> Version.parse(b"2.3.4") Version(major=2, minor=3, patch=4, prerelease=None, build=None) * From individual parts by a dictionary:: >>> d = {'major': 3, 'minor': 4, 'patch': 5, 'prerelease': 'pre.2', 'build': 'build.4'} - >>> semver.Version(**d) + >>> Version(**d) Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') Keep in mind, the ``major``, ``minor``, ``patch`` parts has to - be positive. + be positive integers or strings: - >>> semver.Version(-1) + >>> d = {'major': -3, 'minor': 4, 'patch': 5, 'prerelease': 'pre.2', 'build': 'build.4'} + >>> Version(**d) Traceback (most recent call last): ... ValueError: 'major' is negative. A version can only be positive. - As a minimum requirement, your dictionary needs at least the - be positive. - - >>> semver.Version(-1) - Traceback (most recent call last): - ... - ValueError: 'major' is negative. A version can only be positive. - - As a minimum requirement, your dictionary needs at least the - be positive. - - >>> semver.Version(-1) - Traceback (most recent call last): - ... - ValueError: 'major' is negative. A version can only be positive. - As a minimum requirement, your dictionary needs at least the ``major`` key, others can be omitted. You get a ``TypeError`` if your dictionary contains invalid keys. @@ -105,20 +89,23 @@ A :class:`semver.Version` instance can be created in different ways: * From a tuple:: >>> t = (3, 5, 6) - >>> semver.Version(*t) + >>> Version(*t) Version(major=3, minor=5, patch=6, prerelease=None, build=None) You can pass either an integer or a string for ``major``, ``minor``, or ``patch``:: - >>> semver.Version("3", "5", 6) + >>> Version("3", "5", 6) Version(major=3, minor=5, patch=6, prerelease=None, build=None) -The old, deprecated module level functions are still available. If you -need them, they return different builtin objects (string and dictionary). +The old, deprecated module level functions are still available but +using them are discoraged. They are available to convert old code +to semver3. + +If you need them, they return different builtin objects (string and dictionary). Keep in mind, once you have converted a version into a string or dictionary, it's an ordinary builtin object. It's not a special version object like -the :class:`semver.Version` class anymore. +the :class:`~semver.version.Version` class anymore. Depending on your use case, the following methods are available: @@ -136,7 +123,7 @@ Depending on your use case, the following methods are available: >>> semver.parse("3.4.5-pre.2+build.4") OrderedDict([('major', 3), ('minor', 4), ('patch', 5), ('prerelease', 'pre.2'), ('build', 'build.4')]) - If you pass an invalid version string you will get a ``ValueError``:: + If you pass an invalid version string you will get a :py:exc:`ValueError`:: >>> semver.parse("1.2") Traceback (most recent call last): @@ -148,36 +135,23 @@ Parsing a Version String ------------------------ "Parsing" in this context means to identify the different parts in a string. +Use the function :func:`Version.parse `:: - -* With :func:`semver.parse_version_info`:: - - >>> semver.parse_version_info("3.4.5-pre.2+build.4") - Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') - -* With :func:`semver.Version.parse` (basically the same as - :func:`semver.parse_version_info`):: - - >>> semver.Version.parse("3.4.5-pre.2+build.4") + >>> Version.parse("3.4.5-pre.2+build.4") Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') -* With :func:`semver.parse`:: - - >>> semver.parse("3.4.5-pre.2+build.4") == {'major': 3, 'minor': 4, 'patch': 5, 'prerelease': 'pre.2', 'build': 'build.4'} - True - Checking for a Valid Semver Version ----------------------------------- If you need to check a string if it is a valid semver version, use the -classmethod :func:`semver.Version.isvalid`: +classmethod :func:`Version.isvalid `: .. code-block:: python - >>> semver.Version.isvalid("1.0.0") + >>> Version.isvalid("1.0.0") True - >>> semver.Version.isvalid("invalid") + >>> Version.isvalid("invalid") False @@ -186,12 +160,12 @@ classmethod :func:`semver.Version.isvalid`: Accessing Parts of a Version Through Names ------------------------------------------ -The :class:`semver.Version` contains attributes to access the different +The :class:`~semver.version.Version` class contains attributes to access the different parts of a version: .. code-block:: python - >>> v = semver.Version.parse("3.4.5-pre.2+build.4") + >>> v = Version.parse("3.4.5-pre.2+build.4") >>> v.major 3 >>> v.minor @@ -203,8 +177,8 @@ parts of a version: >>> v.build 'build.4' -However, the attributes are read-only. You cannot change an attribute. -If you do, you get an ``AttributeError``:: +However, the attributes are read-only. You cannot change any of the above attributes. +If you do, you get an :py:exc:`AttributeError`:: >>> v.minor = 5 Traceback (most recent call last): @@ -213,16 +187,16 @@ If you do, you get an ``AttributeError``:: If you need to replace different parts of a version, refer to section :ref:`sec.replace.parts`. -In case you need the different parts of a version stepwise, iterate over the :class:`semver.Version` instance:: +In case you need the different parts of a version stepwise, iterate over the :class:`~semver.version.Version` instance:: - >>> for item in semver.Version.parse("3.4.5-pre.2+build.4"): + >>> for item in Version.parse("3.4.5-pre.2+build.4"): ... print(item) 3 4 5 pre.2 build.4 - >>> list(semver.Version.parse("3.4.5-pre.2+build.4")) + >>> list(Version.parse("3.4.5-pre.2+build.4")) [3, 4, 5, 'pre.2', 'build.4'] @@ -234,15 +208,15 @@ Accessing Parts Through Index Numbers .. versionadded:: 2.10.0 Another way to access parts of a version is to use an index notation. The underlying -:class:`Version ` object allows to access its data through -the magic method :func:`__getitem__ `. +:class:`~semver.version.Version` object allows to access its data through +the magic method :func:`~semver.version.Version.__getitem__`. For example, the ``major`` part can be accessed by index number 0 (zero). Likewise the other parts: .. code-block:: python - >>> ver = semver.Version.parse("10.3.2-pre.5+build.10") + >>> ver = Version.parse("10.3.2-pre.5+build.10") >>> ver[0], ver[1], ver[2], ver[3], ver[4] (10, 3, 2, 'pre.5', 'build.10') @@ -261,11 +235,11 @@ Or, as an alternative, you can pass a :func:`slice` object: >>> ver[sl] (10, 3, 2) -Negative numbers or undefined parts raise an :class:`IndexError` exception: +Negative numbers or undefined parts raise an :py:exc:`IndexError` exception: .. code-block:: python - >>> ver = semver.Version.parse("10.3.2") + >>> ver = Version.parse("10.3.2") >>> ver[3] Traceback (most recent call last): ... @@ -281,9 +255,9 @@ Replacing Parts of a Version ---------------------------- If you want to replace different parts of a version, but leave other parts -unmodified, use the function :func:`semver.Version.replace` or :func:`semver.replace`: +unmodified, use the function :func:`replace `: -* From a :class:`semver.Version` instance:: +* From a :class:`Version ` instance:: >>> version = semver.Version.parse("1.4.5-pre.1+build.6") >>> version.replace(major=2, minor=2) @@ -312,25 +286,25 @@ If you pass invalid keys you get an exception:: Converting a Version instance into Different Types ------------------------------------------------------ -Sometimes it is needed to convert a :class:`semver.Version` instance into +Sometimes it is needed to convert a :class:`Version ` instance into a different type. For example, for displaying or to access all parts. -It is possible to convert a :class:`semver.Version` instance: +It is possible to convert a :class:`Version ` instance: * Into a string with the builtin function :func:`str`:: - >>> str(semver.Version.parse("3.4.5-pre.2+build.4")) + >>> str(Version.parse("3.4.5-pre.2+build.4")) '3.4.5-pre.2+build.4' -* Into a dictionary with :func:`semver.Version.to_dict`:: +* Into a dictionary with :func:`to_dict `:: - >>> v = semver.Version(major=3, minor=4, patch=5) + >>> 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:`semver.Version.to_tuple`:: +* Into a tuple with :func:`to_tuple `:: - >>> v = semver.Version(major=5, minor=4, patch=2) + >>> v = Version(major=5, minor=4, patch=2) >>> v.to_tuple() (5, 4, 2, None, None) @@ -341,27 +315,27 @@ Raising Parts of a Version The ``semver`` module contains the following functions to raise parts of a version: -* :func:`semver.Version.bump_major`: raises the major part and set all other parts to +* :func:`Version.bump_major `: raises the major part and set all other parts to zero. Set ``prerelease`` and ``build`` to ``None``. -* :func:`semver.Version.bump_minor`: raises the minor part and sets ``patch`` to zero. +* :func:`Version.bump_minor `: raises the minor part and sets ``patch`` to zero. Set ``prerelease`` and ``build`` to ``None``. -* :func:`semver.Version.bump_patch`: raises the patch part. Set ``prerelease`` and +* :func:`Version.bump_patch `: raises the patch part. Set ``prerelease`` and ``build`` to ``None``. -* :func:`semver.Version.bump_prerelease`: raises the prerelease part and set +* :func:`Version.bump_prerelease `: raises the prerelease part and set ``build`` to ``None``. -* :func:`semver.Version.bump_build`: raises the build part. +* :func:`Version.bump_build `: raises the build part. .. code-block:: python - >>> str(semver.Version.parse("3.4.5-pre.2+build.4").bump_major()) + >>> str(Version.parse("3.4.5-pre.2+build.4").bump_major()) '4.0.0' - >>> str(semver.Version.parse("3.4.5-pre.2+build.4").bump_minor()) + >>> str(Version.parse("3.4.5-pre.2+build.4").bump_minor()) '3.5.0' - >>> str(semver.Version.parse("3.4.5-pre.2+build.4").bump_patch()) + >>> str(Version.parse("3.4.5-pre.2+build.4").bump_patch()) '3.4.6' - >>> str(semver.Version.parse("3.4.5-pre.2+build.4").bump_prerelease()) + >>> str(Version.parse("3.4.5-pre.2+build.4").bump_prerelease()) '3.4.5-pre.3' - >>> str(semver.Version.parse("3.4.5-pre.2+build.4").bump_build()) + >>> str(Version.parse("3.4.5-pre.2+build.4").bump_build()) '3.4.5-pre.2+build.5' Likewise the module level functions :func:`semver.bump_major`. @@ -371,23 +345,23 @@ Increasing Parts of a Version Taking into Account Prereleases ------------------------------------------------------------- .. versionadded:: 2.10.0 - Added :func:`semver.Version.next_version`. + Added :func:`Version.next_version `. If you want to raise your version and take prereleases into account, -the function :func:`semver.Version.next_version` would perhaps a -better fit. +the function :func:`next_version ` +would perhaps a better fit. .. code-block:: python - >>> v = semver.Version.parse("3.4.5-pre.2+build.4") + >>> v = Version.parse("3.4.5-pre.2+build.4") >>> str(v.next_version(part="prerelease")) '3.4.5-pre.3' - >>> str(semver.Version.parse("3.4.5-pre.2+build.4").next_version(part="patch")) + >>> str(Version.parse("3.4.5-pre.2+build.4").next_version(part="patch")) '3.4.5' - >>> str(semver.Version.parse("3.4.5+build.4").next_version(part="patch")) + >>> str(Version.parse("3.4.5+build.4").next_version(part="patch")) '3.4.5' - >>> str(semver.Version.parse("0.1.4").next_version("prerelease")) + >>> str(Version.parse("0.1.4").next_version("prerelease")) '0.1.5-rc.1' @@ -410,23 +384,23 @@ 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:`semver.Version` **instances** +* **Two** :class:`Version ` **instances** Use the specific operator. Currently, the operators ``<``, ``<=``, ``>``, ``>=``, ``==``, and ``!=`` are supported:: - >>> v1 = semver.Version.parse("3.4.5") - >>> v2 = semver.Version.parse("3.5.1") + >>> v1 = Version.parse("3.4.5") + >>> v2 = Version.parse("3.5.1") >>> v1 < v2 True >>> v1 > v2 False -* **A** :class:`semver.Version` **type and a** :func:`tuple` **or** :func:`list` +* **A** :class:`Version ` **type and a** :func:`tuple` **or** :func:`list` - Use the operator as with two :class:`semver.Version` types:: + Use the operator as with two :class:`Version ` types:: - >>> v = semver.Version.parse("3.4.5") + >>> v = Version.parse("3.4.5") >>> v > (1, 0) True >>> v < [3, 5] @@ -439,7 +413,7 @@ To compare two versions depends on your type: >>> [3, 5] > v True -* **A** :class:`semver.Version` **type and a** :func:`str` +* **A** :class:`Version ` **type and a** :func:`str` You can use also raw strings to compare:: @@ -455,14 +429,14 @@ To compare two versions depends on your type: >>> "3.5.0" > v True - However, if you compare incomplete strings, you get a :class:`ValueError` exception:: + However, if you compare incomplete strings, you get a :py:exc:`ValueError` exception:: >>> v > "1.0" Traceback (most recent call last): ... ValueError: 1.0 is not valid SemVer string -* **A** :class:`semver.Version` **type and a** :func:`dict` +* **A** :class:`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):: @@ -475,7 +449,7 @@ To compare two versions depends on your type: >>> dict(major=1) < v True - If the dictionary contains unknown keys, you get a :class:`TypeError` exception:: + If the dictionary contains unknown keys, you get a :py:exc:`TypeError` exception:: >>> v > dict(major=1, unknown=42) Traceback (most recent call last): @@ -499,16 +473,16 @@ Version equality means for semver, that major, minor, patch, and prerelease parts are equal in both versions you compare. The build part is ignored. For example:: - >>> v = semver.Version.parse("1.2.3-rc4+1e4664d") + >>> v = Version.parse("1.2.3-rc4+1e4664d") >>> v == "1.2.3-rc4+dedbeef" True -This also applies when a :class:`semver.Version` is a member of a set, or a +This also applies when a :class:`Version ` is a member of a set, or a dictionary key:: >>> d = {} - >>> v1 = semver.Version.parse("1.2.3-rc4+1e4664d") - >>> v2 = semver.Version.parse("1.2.3-rc4+dedbeef") + >>> v1 = Version.parse("1.2.3-rc4+1e4664d") + >>> v2 = Version.parse("1.2.3-rc4+dedbeef") >>> d[v1] = 1 >>> d[v2] 1 @@ -554,34 +528,36 @@ 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:`semver.Version` implements :func:`__gt__()` and :func:`__lt__()`, it can be used with builtins requiring +Since :class:`Version ` implements +:func:`__gt__ ` and +:func:`__lt__ `, it can be used with builtins requiring: .. code-block:: python - >>> max([semver.Version(0, 1, 0), semver.Version(0, 2, 0), semver.Version(0, 1, 3)]) + >>> max([Version(0, 1, 0), Version(0, 2, 0), Version(0, 1, 3)]) Version(major=0, minor=2, patch=0, prerelease=None, build=None) - >>> min([semver.Version(0, 1, 0), semver.Version(0, 2, 0), semver.Version(0, 1, 3)]) + >>> min([Version(0, 1, 0), Version(0, 2, 0), Version(0, 1, 3)]) 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:`semver.Version`). +(convertible to :class:`Version `). For example, here are the maximum and minimum versions of a list of version strings: .. code-block:: python - >>> str(max(map(semver.Version.parse, ['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99']))) + >>> str(max(map(Version.parse, ['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99']))) '2.1.0' - >>> str(min(map(semver.Version.parse, ['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99']))) + >>> str(min(map(Version.parse, ['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99']))) '0.4.99' And the same can be done with tuples: .. code-block:: python - >>> max(map(lambda v: semver.Version(*v), [(1, 1, 0), (1, 2, 0), (2, 1, 0), (0, 5, 10), (0, 4, 99)])).to_tuple() + >>> max(map(lambda v: Version(*v), [(1, 1, 0), (1, 2, 0), (2, 1, 0), (0, 5, 10), (0, 4, 99)])).to_tuple() (2, 1, 0, None, None) - >>> min(map(lambda v: semver.Version(*v), [(1, 1, 0), (1, 2, 0), (2, 1, 0), (0, 5, 10), (0, 4, 99)])).to_tuple() + >>> min(map(lambda v: Version(*v), [(1, 1, 0), (1, 2, 0), (2, 1, 0), (0, 5, 10), (0, 4, 99)])).to_tuple() (0, 4, 99, None, None) For dictionaries, it is very similar to finding the max version tuple: see :ref:`sec.convert.versions`. @@ -616,7 +592,7 @@ information and returns a tuple with two items: :language: python -The function returns a *tuple*, containing a :class:`Version` +The function returns a *tuple*, containing a :class:`Version ` instance or None as the first element and the rest as the second element. The second element (the rest) can be used to make further adjustments. @@ -649,7 +625,7 @@ 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:`semver.Version` + Replace them with the respective methods of the :class:`Version ` class. For example, the function :func:`semver.bump_major` is replaced by :func:`semver.Version.bump_major` and calling the ``str(versionobject)``: @@ -657,7 +633,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> s1 = semver.bump_major("3.4.5") - >>> s2 = str(semver.Version.parse("3.4.5").bump_major()) + >>> s2 = str(Version.parse("3.4.5").bump_major()) >>> s1 == s2 True @@ -681,7 +657,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> s1 = semver.format_version(5, 4, 3, 'pre.2', 'build.1') - >>> s2 = str(semver.Version(5, 4, 3, 'pre.2', 'build.1')) + >>> s2 = str(Version(5, 4, 3, 'pre.2', 'build.1')) >>> s1 == s2 True @@ -692,7 +668,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> s1 = semver.max_ver("1.2.3", "1.2.4") - >>> s2 = str(max(map(semver.Version.parse, ("1.2.3", "1.2.4")))) + >>> s2 = str(max(map(Version.parse, ("1.2.3", "1.2.4")))) >>> s1 == s2 True @@ -703,7 +679,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> s1 = semver.min_ver("1.2.3", "1.2.4") - >>> s2 = str(min(map(semver.Version.parse, ("1.2.3", "1.2.4")))) + >>> s2 = str(min(map(Version.parse, ("1.2.3", "1.2.4")))) >>> s1 == s2 True @@ -715,7 +691,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> v1 = semver.parse("1.2.3") - >>> v2 = semver.Version.parse("1.2.3").to_dict() + >>> v2 = Version.parse("1.2.3").to_dict() >>> v1 == v2 True @@ -726,7 +702,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> v1 = semver.parse_version_info("3.4.5") - >>> v2 = semver.Version.parse("3.4.5") + >>> v2 = Version.parse("3.4.5") >>> v1 == v2 True @@ -737,7 +713,7 @@ them with code which is compatible for future versions: .. code-block:: python >>> s1 = semver.replace("1.2.3", major=2, patch=10) - >>> s2 = str(semver.Version.parse('1.2.3').replace(major=2, patch=10)) + >>> s2 = str(Version.parse('1.2.3').replace(major=2, patch=10)) >>> s1 == s2 True @@ -785,7 +761,7 @@ Creating Subclasses from Version If you do not like creating functions to modify the behavior of semver (as shown in section :ref:`sec_dealing_with_invalid_versions`), you can -also create a subclass of the :class:`Version` class. +also create a subclass of the :class:`Version ` class. For example, if you want to output a "v" prefix before a version, but the other behavior is the same, use the following code: @@ -811,4 +787,3 @@ the original class: Traceback (most recent call last): ... ValueError: '1.2.4': not a valid semantic version tag. Must start with 'v' or 'V' - From 67933209234509b6bb6ac3a68a24bcc9c65f921f Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sat, 7 Nov 2020 23:38:21 +0100 Subject: [PATCH 08/26] Fix #316: Return NotImplemented for comparisons The former code raised a TypeError exception for comparisons like __gt__, __lt__ etc. to indicate a wrong type. However, according to NotImplemented[1] documentation, we should return(!) NotImplemented (not raise) when a comparison with an invalid type is not implemented. [1] https://docs.python.org/3/library/constants.html#NotImplemented --- changelog.d/316.trivial.rst | 10 ++++++++++ src/semver/version.py | 4 +--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changelog.d/316.trivial.rst diff --git a/changelog.d/316.trivial.rst b/changelog.d/316.trivial.rst new file mode 100644 index 00000000..edb555ff --- /dev/null +++ b/changelog.d/316.trivial.rst @@ -0,0 +1,10 @@ +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 \ No newline at end of file diff --git a/src/semver/version.py b/src/semver/version.py index 40132526..4633f4bc 100644 --- a/src/semver/version.py +++ b/src/semver/version.py @@ -72,9 +72,7 @@ def wrapper(self: "Version", other: Comparable) -> bool: *String.__args__, # type: ignore ) if not isinstance(other, comparable_types): - raise TypeError( - "other type %r must be in %r" % (type(other), comparable_types) - ) + return NotImplemented return operator(self, other) return wrapper From 3031da4a56a2d1cb53b4588fc15ce5102357bff2 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sun, 8 Nov 2020 14:42:43 +0100 Subject: [PATCH 09/26] Introduce stages in .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. --- .travis.yml | 29 ++++++++++++++++------------- changelog.d/319.trivial.rst | 4 ++++ 2 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 changelog.d/319.trivial.rst diff --git a/.travis.yml b/.travis.yml index 665ebd19..3d31c894 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,40 +10,43 @@ install: - pip install virtualenv tox wheel - tox --version +stages: + - check + - test + script: tox -v matrix: include: - - - python: "3.6" + - stage: check + python: 3.6 env: TOXENV=checks - - python: "3.8" + - stage: test dist: xenial - env: TOXENV=mypy - - - python: "3.6" + python: "3.6" env: TOXENV=py36 - - python: "3.7" + - stage: test dist: xenial + python: "3.7" env: TOXENV=py37 - - python: "3.8" + - stage: test dist: xenial + python: "3.8" env: TOXENV=py38 - - python: "3.9-dev" + - stage: test dist: bionic + python: "3.9-dev" env: TOXENV=py39 - - python: "nightly" + - stage: test dist: bionic + python: "nightly" env: TOXENV=py310 - - python: "3.8" - dist: xenial - env: TOXENV=mypy jobs: allow_failures: diff --git a/changelog.d/319.trivial.rst b/changelog.d/319.trivial.rst new file mode 100644 index 00000000..c1c259a9 --- /dev/null +++ b/changelog.d/319.trivial.rst @@ -0,0 +1,4 @@ +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. \ No newline at end of file From 201d783474f32520a91046833ff426f5453fcf42 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Wed, 4 Nov 2020 16:33:06 +0100 Subject: [PATCH 10/26] Fix #309: Make some functions private Make private function in semver.version module: * Rename comparator -> _comparator, cmp -> _cmp * Remove ensure_str and integrate it into Version.parse * Rework tests in test_typeerror-274.py * Move _nat_cmp to Version and make it a class method * Remove private functions from API documentation --- changelog.d/309.trivial.rst | 17 ++++++ docs/api.rst | 6 -- src/semver/version.py | 110 ++++++++++++++---------------------- tests/test_typeerror-274.py | 89 ++--------------------------- 4 files changed, 64 insertions(+), 158 deletions(-) create mode 100644 changelog.d/309.trivial.rst diff --git a/changelog.d/309.trivial.rst b/changelog.d/309.trivial.rst new file mode 100644 index 00000000..97bbba1e --- /dev/null +++ b/changelog.d/309.trivial.rst @@ -0,0 +1,17 @@ +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`` diff --git a/docs/api.rst b/docs/api.rst index d35c48fe..196e30a9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -50,12 +50,6 @@ Version Handling :mod:`semver.version` .. automodule:: semver.version -.. autofunction:: semver.version.cmp - -.. autofunction:: semver.version.ensure_str - -.. autofunction:: semver.version.comparator - .. autoclass:: semver.version.VersionInfo .. autoclass:: semver.version.Version diff --git a/src/semver/version.py b/src/semver/version.py index 4633f4bc..9e02544f 100644 --- a/src/semver/version.py +++ b/src/semver/version.py @@ -29,37 +29,7 @@ Comparator = Callable[["Version", Comparable], bool] -def cmp(a, b): # TODO: type hints - """Return negative if ab.""" - return (a > b) - (a < b) - - -def ensure_str(s: String, encoding="utf-8", errors="strict") -> str: - # Taken from six project - """ - Coerce *s* to `str`. - - * `str` -> `str` - * `bytes` -> decoded to `str` - - :param s: the string to convert - :param encoding: the encoding to apply, defaults to "utf-8" - :param errors: set a different error handling scheme, - defaults to "strict". - Other possible values are `ignore`, `replace`, and - `xmlcharrefreplace` as well as any other name - registered with :func:`codecs.register_error`. - :raises TypeError: if ``s`` is not str or bytes type - :return: the converted string - """ - if isinstance(s, bytes): - s = s.decode(encoding, errors) - elif not isinstance(s, String.__args__): # type: ignore - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def comparator(operator: Comparator) -> Comparator: +def _comparator(operator: Comparator) -> Comparator: """Wrap a Version binary op method in a type-check.""" @wraps(operator) @@ -78,31 +48,9 @@ def wrapper(self: "Version", other: Comparable) -> bool: return wrapper -def _nat_cmp(a, b): # TODO: type hints - def convert(text): - return int(text) if re.match("^[0-9]+$", text) else text - - def split_key(key): - return [convert(c) for c in key.split(".")] - - def cmp_prerelease_tag(a, b): - if isinstance(a, int) and isinstance(b, int): - return cmp(a, b) - elif isinstance(a, int): - return -1 - elif isinstance(b, int): - return 1 - else: - return cmp(a, b) - - a, b = a or "", b or "" - a_parts, b_parts = split_key(a), split_key(b) - for sub_a, sub_b in zip(a_parts, b_parts): - cmp_result = cmp_prerelease_tag(sub_a, sub_b) - if cmp_result != 0: - return cmp_result - else: - return cmp(len(a), len(b)) +def _cmp(a, b): # TODO: type hints + """Return negative if ab.""" + return (a > b) - (a < b) class Version: @@ -165,6 +113,29 @@ def __init__( self._prerelease = None if prerelease is None else str(prerelease) self._build = None if build is None else str(build) + @classmethod + def _nat_cmp(cls, a, b): # TODO: type hints + def cmp_prerelease_tag(a, b): + if isinstance(a, int) and isinstance(b, int): + return _cmp(a, b) + elif isinstance(a, int): + return -1 + elif isinstance(b, int): + return 1 + else: + return _cmp(a, b) + + a, b = a or "", b or "" + a_parts, b_parts = a.split("."), b.split(".") + a_parts = [int(x) if re.match(r"^\d+$", x) else x for x in a_parts] + b_parts = [int(x) if re.match(r"^\d+$", x) else x for x in b_parts] + for sub_a, sub_b in zip(a_parts, b_parts): + cmp_result = cmp_prerelease_tag(sub_a, sub_b) + if cmp_result != 0: + return cmp_result + else: + return _cmp(len(a), len(b)) + @property def major(self) -> int: """The major part of a version (read-only).""" @@ -381,12 +352,12 @@ def compare(self, other: Comparable) -> int: v1 = self.to_tuple()[:3] v2 = other.to_tuple()[:3] - x = cmp(v1, v2) + x = _cmp(v1, v2) if x: return x rc1, rc2 = self.prerelease, other.prerelease - rccmp = _nat_cmp(rc1, rc2) + rccmp = self._nat_cmp(rc1, rc2) if not rccmp: return 0 @@ -444,27 +415,27 @@ def next_version(self, part: str, prerelease_token: str = "rc") -> "Version": version = version.bump_patch() return version.bump_prerelease(prerelease_token) - @comparator + @_comparator def __eq__(self, other: Comparable) -> bool: # type: ignore return self.compare(other) == 0 - @comparator + @_comparator def __ne__(self, other: Comparable) -> bool: # type: ignore return self.compare(other) != 0 - @comparator + @_comparator def __lt__(self, other: Comparable) -> bool: return self.compare(other) < 0 - @comparator + @_comparator def __le__(self, other: Comparable) -> bool: return self.compare(other) <= 0 - @comparator + @_comparator def __gt__(self, other: Comparable) -> bool: return self.compare(other) > 0 - @comparator + @_comparator def __ge__(self, other: Comparable) -> bool: return self.compare(other) >= 0 @@ -593,15 +564,20 @@ def parse(cls, version: String) -> "Version": :param version: version string :return: a new :class:`Version` instance :raises ValueError: if version is invalid + :raises TypeError: if version contains the wrong type >>> semver.Version.parse('3.4.5-pre.2+build.4') Version(major=3, minor=4, patch=5, \ prerelease='pre.2', build='build.4') """ - version_str = ensure_str(version) - match = cls._REGEX.match(version_str) + if isinstance(version, bytes): + version = version.decode("UTF-8") + elif not isinstance(version, String.__args__): # type: ignore + raise TypeError("not expecting type '%s'" % type(version)) + + match = cls._REGEX.match(version) if match is None: - raise ValueError(f"{version_str} is not valid SemVer string") + raise ValueError(f"{version} is not valid SemVer string") matched_version_parts: Dict[str, Any] = match.groupdict() diff --git a/tests/test_typeerror-274.py b/tests/test_typeerror-274.py index 61480bcf..326304b8 100644 --- a/tests/test_typeerror-274.py +++ b/tests/test_typeerror-274.py @@ -1,95 +1,14 @@ -import sys - import pytest - import semver -import semver.version - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - - -def ensure_binary(s, encoding="utf-8", errors="strict"): - """ - Coerce ``s`` to bytes. - - * `str` -> encoded to `bytes` - * `bytes` -> `bytes` - - :param s: the string to convert - :type s: str | bytes - :param encoding: the encoding to apply, defaults to "utf-8" - :type encoding: str - :param errors: set a different error handling scheme; - other possible values are `ignore`, `replace`, and - `xmlcharrefreplace` as well as any other name - registered with :func:`codecs.register_error`. - Defaults to "strict". - :type errors: str - :raises TypeError: if ``s`` is not str or bytes type - :return: the converted string - :rtype: str - """ - if isinstance(s, str): - return s.encode(encoding, errors) - elif isinstance(s, bytes): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) -def test_should_work_with_string_and_unicode(): +def test_should_work_with_string_and_bytes(): result = semver.compare("1.1.0", b"1.2.2") assert result == -1 result = semver.compare(b"1.1.0", "1.2.2") assert result == -1 -class TestEnsure: - # From six project - # grinning face emoji - UNICODE_EMOJI = "\U0001F600" - BINARY_EMOJI = b"\xf0\x9f\x98\x80" - - def test_ensure_binary_raise_type_error(self): - with pytest.raises(TypeError): - semver.version.ensure_str(8) - - def test_errors_and_encoding(self): - ensure_binary(self.UNICODE_EMOJI, encoding="latin-1", errors="ignore") - with pytest.raises(UnicodeEncodeError): - ensure_binary(self.UNICODE_EMOJI, encoding="latin-1", errors="strict") - - def test_ensure_binary_raise(self): - converted_unicode = ensure_binary( - self.UNICODE_EMOJI, encoding="utf-8", errors="strict" - ) - converted_binary = ensure_binary( - self.BINARY_EMOJI, encoding="utf-8", errors="strict" - ) - - # PY3: str -> bytes - assert converted_unicode == self.BINARY_EMOJI and isinstance( - converted_unicode, bytes - ) - # PY3: bytes -> bytes - assert converted_binary == self.BINARY_EMOJI and isinstance( - converted_binary, bytes - ) - - def test_ensure_str(self): - converted_unicode = semver.version.ensure_str( - self.UNICODE_EMOJI, encoding="utf-8", errors="strict" - ) - converted_binary = semver.version.ensure_str( - self.BINARY_EMOJI, encoding="utf-8", errors="strict" - ) - - # PY3: str -> str - assert converted_unicode == self.UNICODE_EMOJI and isinstance( - converted_unicode, str - ) - # PY3: bytes -> str - assert converted_binary == self.UNICODE_EMOJI and isinstance( - converted_unicode, str - ) +def test_should_not_work_with_invalid_args(): + with pytest.raises(TypeError): + semver.version.Version.parse(8) From 2818cdbacb4274386575197671e30a9cdcc61cee Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Tue, 10 Nov 2020 10:43:14 +0100 Subject: [PATCH 11/26] Fix #181: create issue templates Create GitHub issue templates for bugs and features. --- .github/ISSUE_TEMPLATE/bug_report.md | 33 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 24 +++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..461254e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a bug report to help us improve semver +title: '' +labels: bug +assignees: '' + +--- + + + +# Situation + + +# To Reproduce + + +# Expected Behavior + + +# Environment +- OS: [e.g. Linux, MacOS, Windows, ...] +- Python version [e.g. 3.6, 3.7, ...] +- Version of semver library [e.g. 3.0.0] + +# Additional context + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..5a24681d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + + + +# Situation + + +# Possible Solution/Idea + + + +# Additional context + From aa58f62b1b6f8ef378a9c270a643182a17b72228 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Wed, 11 Nov 2020 08:57:21 +0100 Subject: [PATCH 12/26] Fix #322: Implement GitHub Action * Add "gh-action" section in tox.ini * Add .github/workflows/python-testing.yml * Use dependent jobs; first start check, then tests jobs * Add changelog * Remove black-formatting.yml * Remove .travis.yml --- ...lack-formatting.yml => python-testing.yml} | 51 +++++++++++++----- .travis.yml | 53 ------------------- changelog.d/322.trivial.rst | 1 + tox.ini | 14 +++-- 4 files changed, 48 insertions(+), 71 deletions(-) rename .github/workflows/{black-formatting.yml => python-testing.yml} (50%) delete mode 100644 .travis.yml create mode 100644 changelog.d/322.trivial.rst diff --git a/.github/workflows/black-formatting.yml b/.github/workflows/python-testing.yml similarity index 50% rename from .github/workflows/black-formatting.yml rename to .github/workflows/python-testing.yml index 25b34f21..1147d288 100644 --- a/.github/workflows/black-formatting.yml +++ b/.github/workflows/python-testing.yml @@ -1,15 +1,20 @@ -name: Black Formatting +--- +name: Python -on: [pull_request] +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] jobs: - build: + check: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v1 - name: Output env variables run: | + echo "Default branch=${default-branch}" echo "GITHUB_WORKFLOW=${GITHUB_WORKFLOW}" echo "GITHUB_ACTION=$GITHUB_ACTION" echo "GITHUB_ACTIONS=$GITHUB_ACTIONS" @@ -26,18 +31,36 @@ jobs: cat $GITHUB_EVENT_PATH echo "\n" echo "::debug::---end" - - - name: Set up Python 3.7 - uses: actions/setup-python@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 with: - python-version: 3.7 - + python-version: 3.6 - name: Install dependencies run: | - python -m pip install --upgrade pip black + python3 -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Check + run: | + tox -e checks + + tests: + needs: check + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] - - name: Run black - id: black + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Test with tox run: | - black --check . - echo "::set-output name=rc::$?" + tox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3d31c894..00000000 --- a/.travis.yml +++ /dev/null @@ -1,53 +0,0 @@ -# config file for automatic testing at travis-ci.org -language: python -cache: pip - -before_install: - - sudo apt-get install -y python3-dev - -install: - - pip install --upgrade pip setuptools - - pip install virtualenv tox wheel - - tox --version - -stages: - - check - - test - -script: tox -v - -matrix: - include: - - stage: check - python: 3.6 - env: TOXENV=checks - - - stage: test - dist: xenial - python: "3.6" - env: TOXENV=py36 - - - stage: test - dist: xenial - python: "3.7" - env: TOXENV=py37 - - - stage: test - dist: xenial - python: "3.8" - env: TOXENV=py38 - - - stage: test - dist: bionic - python: "3.9-dev" - env: TOXENV=py39 - - - stage: test - dist: bionic - python: "nightly" - env: TOXENV=py310 - - -jobs: - allow_failures: - - python: "nightly" diff --git a/changelog.d/322.trivial.rst b/changelog.d/322.trivial.rst new file mode 100644 index 00000000..b9394c12 --- /dev/null +++ b/changelog.d/322.trivial.rst @@ -0,0 +1 @@ +Switch from Travis CI to GitHub Actions. diff --git a/tox.ini b/tox.ini index b9515b2b..ce566562 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,17 @@ [tox] envlist = - flake8 - py{36,37,38,39,310} - docs - mypy + checks + py{36,37,38,39} isolated_build = True +[gh-actions] +python = + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39 + # 3.10: py310 + [testenv] description = Run test suite for {basepython} From 3a58a6578d7d0f5edd3b9dad58a6e22b515a333f Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Wed, 11 Nov 2020 22:16:30 +0100 Subject: [PATCH 13/26] Add Gitter badge to README --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index d4f29819..496bc993 100644 --- a/README.rst +++ b/README.rst @@ -11,6 +11,7 @@ Quickstart A Python module for `semantic versioning`_. Simplifies comparing versions. |build-status| |python-support| |downloads| |license| |docs| |black| +|Gitter| .. teaser-end @@ -118,3 +119,6 @@ There are other functions to discover. Read on! .. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black :alt: Black Formatter +.. |Gitter| image:: https://badges.gitter.im/python-semver/community.svg + :target: https://gitter.im/python-semver/community + :alt: Gitter From d3d7b22f45462cf7e42fc03ab38d28fc8c8b64ca Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Thu, 12 Nov 2020 21:06:54 +0100 Subject: [PATCH 14/26] Improve batches in README * Add batch for isitmaintained.com * Add batch for GitHub Action --- README.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 496bc993..5180288f 100644 --- a/README.rst +++ b/README.rst @@ -10,8 +10,8 @@ Quickstart A Python module for `semantic versioning`_. Simplifies comparing versions. -|build-status| |python-support| |downloads| |license| |docs| |black| -|Gitter| +|GHAction| |python-support| |downloads| |license| |docs| |black| +|Gitter| |openissues| .. teaser-end @@ -100,9 +100,6 @@ There are other functions to discover. Read on! .. |latest-version| image:: https://img.shields.io/pypi/v/semver.svg :alt: Latest version on PyPI :target: https://pypi.org/project/semver -.. |build-status| image:: https://travis-ci.com/python-semver/python-semver.svg?branch=master - :alt: Build status - :target: https://travis-ci.com/python-semver/python-semver .. |python-support| image:: https://img.shields.io/pypi/pyversions/semver.svg :target: https://pypi.org/project/semver :alt: Python versions @@ -122,3 +119,8 @@ There are other functions to discover. Read on! .. |Gitter| image:: https://badges.gitter.im/python-semver/community.svg :target: https://gitter.im/python-semver/community :alt: Gitter +.. |openissues| image:: http://isitmaintained.com/badge/open/python-semver/python-semver.svg + :target: http://isitmaintained.com/project/python-semver/python-semver + :alt: Percentage of open issues +.. |GHAction| image:: https://github.com/python-semver/python-semver/workflows/Python/badge.svg + :alt: Python From 4eb6fa64c164d38617c3f83bfdb48e69d99291c5 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sat, 21 Nov 2020 12:50:51 +0100 Subject: [PATCH 15/26] Clean changelog remove double entries --- CHANGELOG.rst | 52 --------------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2e3f97a5..879a5bdd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,58 +16,6 @@ in our repository. .. towncrier release notes start -Version 3.0.0-dev.2 -=================== - -:Released: 2020-11-04 -:Maintainer: - - -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 - - - ----- - - -Version 3.0.0-dev.2 -=================== - -:Released: 2020-11-04 -:Maintainer: - - -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:`__getitem__`, - :func:`__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 - - - ----- - Version 3.0.0-dev.2 =================== From bc3f96a206440c3a52a4123c45329bc4ff074d96 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Fri, 11 Dec 2020 23:49:34 +0100 Subject: [PATCH 16/26] Amend Contributing with link to GH Discussion page --- CONTRIBUTING.rst | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ac40563b..5fd75ab2 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -8,14 +8,18 @@ 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 Feedback ---------------------------- +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. +feature? Great! We like to hear from you! -First, take the time to look into our GitHub `issues`_ tracker if -this already covered. If not, changes are good that we avoid double work. +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. Prerequisites @@ -229,4 +233,4 @@ Adding a Changelog Entry .. _Semantic Versioning: https://semver.org .. _Sphinx style: https://sphinx-rtd-tutorial.rtfd.io/en/latest/docstrings.html .. _tox: https://tox.rtfd.org/ - +.. _gh_discussions: https://github.com/python-semver/python-semver/discussions From 07cacb57a158c4627a424dd3bb6ba4c8bba9d73b Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Fri, 11 Dec 2020 23:50:30 +0100 Subject: [PATCH 17/26] Change README (Gitter -> GH Discussions) * Remove Gitter badge * Add GitHub Discussions badge --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5180288f..5c28cc69 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ Quickstart A Python module for `semantic versioning`_. Simplifies comparing versions. |GHAction| |python-support| |downloads| |license| |docs| |black| -|Gitter| |openissues| +|openissues| |GHDiscussion| .. teaser-end @@ -124,3 +124,6 @@ There are other functions to discover. Read on! :alt: Percentage of open issues .. |GHAction| image:: https://github.com/python-semver/python-semver/workflows/Python/badge.svg :alt: Python +.. |GHDiscussion| image:: https://shields.io/badge/GitHub-%20Discussions-green?logo=github + :target: https://github.com/python-semver/python-semver/discussions + :alt: GitHub Discussion From 198686009597ab27fb797b7865b2551a341144b2 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Sat, 12 Dec 2020 00:37:27 +0100 Subject: [PATCH 18/26] Create dependabot.yml --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0c5d1c56 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + day: "friday" + labels: + - "enhancement" + commit-message: + prefix: "pip" + # Allow up to 10 open pull requests for pip dependencies + open-pull-requests-limit: 5 From e7b0c088a15a003417b748fb6951b43771702a2d Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Thu, 17 Dec 2020 09:42:54 +0100 Subject: [PATCH 19/26] Add config.yml for GitHub issues Configure the template chooser and provide some additional information. --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..640cced4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Community Support + url: https://github.com/python-semver/python-semver/discussions + about: Ask and answer questions in our discussion forum. + - name: Documentation + url: https://python-semver.readthedocs.io/ + about: Find more information in our documentation. From 4aff99ea74cfd5fc8bad31e835d9d09b64af6319 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Thu, 17 Dec 2020 16:34:54 +0100 Subject: [PATCH 20/26] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..d4262f95 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,67 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master, maint/v2 ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '45 16 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 0db94e6cac797d9320b6817e3beda07ad2c48372 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Thu, 17 Dec 2020 16:45:22 +0100 Subject: [PATCH 21/26] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d4262f95..bff8173b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,7 +18,7 @@ on: # The branches below must be a subset of the branches above branches: [ master ] schedule: - - cron: '45 16 * * 5' + - cron: '50 16 * * 5' jobs: analyze: From f74e3a3cf658de08f5313c3aa868ed770a4a2c65 Mon Sep 17 00:00:00 2001 From: Zain Patel Date: Wed, 21 Apr 2021 17:06:00 +0100 Subject: [PATCH 22/26] Improve documentation for semver max/min; fix #337 --- docs/migratetosemver3.rst | 4 ++-- docs/usage.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/migratetosemver3.rst b/docs/migratetosemver3.rst index d6d90954..d977bc03 100644 --- a/docs/migratetosemver3.rst +++ b/docs/migratetosemver3.rst @@ -20,7 +20,7 @@ Use Version instead of VersionInfo The :class:`VersionInfo` has been renamed to :class:`Version` to have a more succinct name. An alias has been created to preserve compatibility but -using old name has been deprecated. +using the old name has been deprecated. If you still need the old version, use this line: @@ -39,4 +39,4 @@ import it from :mod:`semver.cli` in the future: .. code-block:: python - from semver.cli import cmd_bump \ No newline at end of file + from semver.cli import cmd_bump diff --git a/docs/usage.rst b/docs/usage.rst index ad44ecaf..eb4cc25b 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -546,9 +546,9 @@ For example, here are the maximum and minimum versions of a list of version stri .. code-block:: python - >>> str(max(map(Version.parse, ['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99']))) + >>> max(['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99'], key=Version.parse) '2.1.0' - >>> str(min(map(Version.parse, ['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99']))) + >>> min(['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99'], key=Version.parse) '0.4.99' And the same can be done with tuples: From 0398baaa2fdb080b6e59161e6c0217f2025d8a42 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Thu, 22 Apr 2021 13:34:42 +0200 Subject: [PATCH 23/26] Fix type definition problem Define new type "Decorator" for function "deprecated" to avoid this mypy error: src/semver/_deprecated.py:69: error: Incompatible return value type (got "Callable[[VarArg(Any), KwArg(Any)], Callable[..., F]]", expected "Union[Callable[..., F], partial[Any]]") Co-authored-by: Thomas Laferriere --- src/semver/_deprecated.py | 6 +++--- src/semver/_types.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/semver/_deprecated.py b/src/semver/_deprecated.py index 545a2438..61ceae12 100644 --- a/src/semver/_deprecated.py +++ b/src/semver/_deprecated.py @@ -7,11 +7,11 @@ import warnings from functools import partial, wraps from types import FrameType -from typing import Type, Union, Callable, cast +from typing import Type, Callable, cast from . import cli from .version import Version -from ._types import F, String +from ._types import Decorator, F, String def deprecated( @@ -19,7 +19,7 @@ def deprecated( replace: str = None, version: str = None, category: Type[Warning] = DeprecationWarning, -) -> Union[Callable[..., F], partial]: +) -> Decorator: """ Decorates a function to output a deprecation warning. diff --git a/src/semver/_types.py b/src/semver/_types.py index 4f004a29..7afb6ff0 100644 --- a/src/semver/_types.py +++ b/src/semver/_types.py @@ -1,5 +1,6 @@ """Typing for semver.""" +from functools import partial from typing import Union, Optional, Tuple, Dict, Iterable, Callable, TypeVar VersionPart = Union[int, Optional[str]] @@ -8,3 +9,4 @@ VersionIterator = Iterable[VersionPart] String = Union[str, bytes] F = TypeVar("F", bound=Callable) +Decorator = Union[Callable[..., F], partial] From 4d2df08c9a489db6ceaf0ebbef692c092393f124 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Mon, 10 Jan 2022 21:11:46 +0100 Subject: [PATCH 24/26] Start supporting Python 3.10 --- .github/workflows/python-testing.yml | 2 +- changelog.d/347.trivial.rst | 1 + pyproject.toml | 2 +- setup.cfg | 1 + tox.ini | 4 ++-- 5 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 changelog.d/347.trivial.rst diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml index 1147d288..8f36dbc9 100644 --- a/.github/workflows/python-testing.yml +++ b/.github/workflows/python-testing.yml @@ -49,7 +49,7 @@ jobs: strategy: max-parallel: 5 matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v1 diff --git a/changelog.d/347.trivial.rst b/changelog.d/347.trivial.rst new file mode 100644 index 00000000..2d44ceb1 --- /dev/null +++ b/changelog.d/347.trivial.rst @@ -0,0 +1 @@ +Support Python 3.10 in GitHub Action and other config files. diff --git a/pyproject.toml b/pyproject.toml index 1b406dac..e58eb25a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 88 -target-version = ['py36', 'py37', 'py38'] +target-version = ['py36', 'py37', 'py38', 'py39', 'py310'] # diff = true exclude = ''' ( diff --git a/setup.cfg b/setup.cfg index 681240ac..9467709c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Software Development :: Libraries :: Python Modules license = BSD diff --git a/tox.ini b/tox.ini index ce566562..8c7eb5e5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = checks - py{36,37,38,39} + py{36,37,38,39,310} isolated_build = True [gh-actions] @@ -10,7 +10,7 @@ python = 3.7: py37 3.8: py38 3.9: py39 - # 3.10: py310 + 3.10: py310 [testenv] From 221bfba902d13acc640c44602f8a090fb68215f3 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Wed, 19 Jan 2022 21:35:09 +0100 Subject: [PATCH 25/26] Fix #315: Create 3.0.0-dev.3 * Improve entries for PyPI in setup.cfg * Remove key "download_url" as it points to a broken URL * Add Changelog entry pointing to RTD * Raise version to 3.0.0-dev.3 * Update release procedure * Update Black formatter config * Replace "exclude" with "extend-exclude" * Ignore all *.py files in project's root directory * Include "setup.py" explicity --- changelog.d/315.doc.rst | 1 + docs/usage.rst | 2 +- pyproject.toml | 21 +++++---------- release-procedure.md | 58 +++++++++++++++++++++++++++++------------ setup.cfg | 2 +- src/semver/__about__.py | 2 +- 6 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 changelog.d/315.doc.rst diff --git a/changelog.d/315.doc.rst b/changelog.d/315.doc.rst new file mode 100644 index 00000000..77ca8ba5 --- /dev/null +++ b/changelog.d/315.doc.rst @@ -0,0 +1 @@ +Improve release procedure text diff --git a/docs/usage.rst b/docs/usage.rst index eb4cc25b..f6983d17 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -26,7 +26,7 @@ Getting the Version of semver To know the version of semver itself, use the following construct:: >>> semver.__version__ - '3.0.0-dev.2' + '3.0.0-dev.3' Creating a Version diff --git a/pyproject.toml b/pyproject.toml index e58eb25a..769b13d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,20 +11,13 @@ build-backend = "setuptools.build_meta" line-length = 88 target-version = ['py36', 'py37', 'py38', 'py39', 'py310'] # diff = true -exclude = ''' -( - /( - \.eggs # exclude a few common directories in the - | \.git # root of the project - | \.mypy_cache - | \.tox - | \.venv - | \.env - | _build - | build - | dist - )/ -) +extend-exclude = ''' +# A regex preceded with ^/ will apply only to files and directories +# in the root of the project. +^/*.py +''' +include = ''' +^/setup.py ''' [tool.towncrier] diff --git a/release-procedure.md b/release-procedure.md index db9ed1b5..251f23f0 100644 --- a/release-procedure.md +++ b/release-procedure.md @@ -5,22 +5,31 @@ create a new release. ## Prepare the Release -1. Verify that issues about new release are closed https://github.com/python-semver/python-semver/issues. +1. Verify: -1. Verify that no pull requests that should be included in this release haven't been left out https://github.com/python-semver/python-semver/pulls. + * all issues for a new release are closed: . -1. Verify that continuous integration for latest build was passing https://travis-ci.com/python-semver/python-semver. + * that all pull requests that should be included in this release are merged: . -1. Create a new branch `release/VERSION`. + * that continuous integration for latest build was passing: . + +1. Create a new branch `release/`. 1. If one or several supported Python versions have been removed or added, verify that the 3 following files have been updated: - * [setup.py](https://github.com/python-semver/python-semver/blob/master/setup.py) - * [tox.ini](https://github.com/python-semver/python-semver/blob/master/tox.ini) - * [.travis.yml](https://github.com/python-semver/python-semver/blob/master/.travis.yml) + * `setup.cfg` + * `tox.ini` + * `.git/workflows/pythonpackage.yml` + +1. Verify that the version has been updated and follow + : + + * `src/semver/__about__.py` + * `docs/usage.rst` 1. Add eventually new contributor(s) to [CONTRIBUTORS](https://github.com/python-semver/python-semver/blob/master/CONTRIBUTORS). -1. Verify that `__version__` in [semver.py](https://github.com/python-semver/python-semver/blob/master/semver.py) have been updated and follow https://semver.org. + +1. Check if all changelog entries are created. If some are missing, [create them](https://python-semver.readthedocs.io/en/latest/development.html#adding-a-changelog-entry). 1. Show the new draft [CHANGELOG](https://github.com/python-semver/python-semver/blob/master/CHANGELOG.rst) entry for the latest release with: @@ -36,32 +45,47 @@ create a new release. $ tox -e docs +1. Commit all changes, push, and create a pull request. + ## Create the New Release -1. Ensure that long description (ie [README.rst](https://github.com/python-semver/python-semver/blob/master/README.rst)) can be correctly rendered by Pypi using `restview --long-description` +1. Ensure that long description ([README.rst](https://github.com/python-semver/python-semver/blob/master/README.rst)) can be correctly rendered by Pypi using `restview --long-description` + +1. 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/* + +1. Create the distribution files (wheel and source): + + $ tox -e prepare-dist 1. Upload the wheel and source to TestPyPI first: - ```bash - $ git clean -xfd - $ rm dist/* - $ python3 setup.py sdist bdist_wheel + ```bash $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* ``` - If you have a `~/.pypirc` with a `testpyi` section, the upload can be + If you have a `~/.pypirc` with a `testpypi` section, the upload can be simplified: - $ twine upload --repository testpyi dist/* + $ twine upload --repository testpypi dist/* 1. Check if everything is okay with the wheel. + Check also the web site `https://test.pypi.org/project//` 1. Upload to PyPI: ```bash $ git clean -xfd - $ python setup.py register sdist bdist_wheel + $ tox -e prepare-dist $ twine upload dist/* ``` @@ -78,4 +102,6 @@ create a new release. document the new release. Usually it's enough to take it from a commit message or the tag description. +1. Announce it in . + You're done! Celebrate! diff --git a/setup.cfg b/setup.cfg index 9467709c..de2d226c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,8 +14,8 @@ author_email = k-bx@k-bx.com maintainer = Sebastien Celles, Tom Schraitle maintainer_email = s.celles@gmail.com url = https://github.com/python-semver/python-semver -download_url = https://github.com/python-semver/python-semver/downloads project_urls = + Changelog = https://python-semver.readthedocs.io/en/latest/changelog.html Documentation = https://python-semver.rtfd.io Releases = https://github.com/python-semver/python-semver/releases Bug Tracker = https://github.com/python-semver/python-semver/issues diff --git a/src/semver/__about__.py b/src/semver/__about__.py index 1f5fcae5..fa448ebe 100644 --- a/src/semver/__about__.py +++ b/src/semver/__about__.py @@ -16,7 +16,7 @@ """ #: Semver version -__version__ = "3.0.0-dev.2" +__version__ = "3.0.0-dev.3" #: Original semver author __author__ = "Kostiantyn Rybnikov" From 47f9f346ddb0a920b7a65abdbee50c97bdc9e8a5 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Wed, 19 Jan 2022 22:27:05 +0100 Subject: [PATCH 26/26] Update changelog --- CHANGELOG.rst | 85 +++++++++++++++++++++++++++++++++++++ changelog.d/309.trivial.rst | 17 -------- changelog.d/310.bugfix.rst | 3 -- changelog.d/312.doc.rst | 11 ----- changelog.d/313.trivial.rst | 3 -- changelog.d/315.doc.rst | 1 - changelog.d/316.trivial.rst | 10 ----- changelog.d/319.trivial.rst | 4 -- changelog.d/322.trivial.rst | 1 - changelog.d/347.trivial.rst | 1 - 10 files changed, 85 insertions(+), 51 deletions(-) delete mode 100644 changelog.d/309.trivial.rst delete mode 100644 changelog.d/310.bugfix.rst delete mode 100644 changelog.d/312.doc.rst delete mode 100644 changelog.d/313.trivial.rst delete mode 100644 changelog.d/315.doc.rst delete mode 100644 changelog.d/316.trivial.rst delete mode 100644 changelog.d/319.trivial.rst delete mode 100644 changelog.d/322.trivial.rst delete mode 100644 changelog.d/347.trivial.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 879a5bdd..3173507f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,91 @@ in our repository. .. towncrier release notes start +Version 3.0.0-dev.3 +=================== + +:Released: 2022-01-19 +:Maintainer: Tom Schraitle + + +Bug Fixes +--------- + +* :gh:`310`: Rework API documentation. + Follow a more "semi-manual" attempt and add auto directives + into :file:`docs/api.rst`. + + + +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 + + + +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 =================== diff --git a/changelog.d/309.trivial.rst b/changelog.d/309.trivial.rst deleted file mode 100644 index 97bbba1e..00000000 --- a/changelog.d/309.trivial.rst +++ /dev/null @@ -1,17 +0,0 @@ -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`` diff --git a/changelog.d/310.bugfix.rst b/changelog.d/310.bugfix.rst deleted file mode 100644 index 6b042982..00000000 --- a/changelog.d/310.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Rework API documentation. -Follow a more "semi-manual" attempt and add auto directives -into :file:`docs/api.rst`. \ No newline at end of file diff --git a/changelog.d/312.doc.rst b/changelog.d/312.doc.rst deleted file mode 100644 index 6b18eb49..00000000 --- a/changelog.d/312.doc.rst +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/changelog.d/313.trivial.rst b/changelog.d/313.trivial.rst deleted file mode 100644 index 963b4f31..00000000 --- a/changelog.d/313.trivial.rst +++ /dev/null @@ -1,3 +0,0 @@ -Correct :file:`tox.ini` for ``changelog`` entry to skip -installation for semver. This should speed up the execution -of towncrier. diff --git a/changelog.d/315.doc.rst b/changelog.d/315.doc.rst deleted file mode 100644 index 77ca8ba5..00000000 --- a/changelog.d/315.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Improve release procedure text diff --git a/changelog.d/316.trivial.rst b/changelog.d/316.trivial.rst deleted file mode 100644 index edb555ff..00000000 --- a/changelog.d/316.trivial.rst +++ /dev/null @@ -1,10 +0,0 @@ -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 \ No newline at end of file diff --git a/changelog.d/319.trivial.rst b/changelog.d/319.trivial.rst deleted file mode 100644 index c1c259a9..00000000 --- a/changelog.d/319.trivial.rst +++ /dev/null @@ -1,4 +0,0 @@ -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. \ No newline at end of file diff --git a/changelog.d/322.trivial.rst b/changelog.d/322.trivial.rst deleted file mode 100644 index b9394c12..00000000 --- a/changelog.d/322.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Switch from Travis CI to GitHub Actions. diff --git a/changelog.d/347.trivial.rst b/changelog.d/347.trivial.rst deleted file mode 100644 index 2d44ceb1..00000000 --- a/changelog.d/347.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Support Python 3.10 in GitHub Action and other config files. 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