././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8672943 incremental-24.7.2/0000755000175100001660000000000014651772605013527 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/.coveragerc0000644000175100001660000000043714651772574015661 0ustar00runnerdocker[run] # List of package names. source_pkgs = incremental # List of directory names. source = tests branch = True parallel = True [paths] source = src/incremental */src/incremental */site-packages/incremental [report] exclude_lines = pragma: no cover if TYPE_CHECKING: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/LICENSE0000644000175100001660000000363114651772574014544 0ustar00runnerdockerIncremental ----------- This project includes code from the Twisted Project, which is licensed as below. Copyright (c) 2001-2015 Allen Short Amber Hawkie Brown Andrew Bennetts Andy Gayton Antoine Pitrou Apple Computer, Inc. Ashwini Oruganti Benjamin Bruheim Bob Ippolito Canonical Limited Christopher Armstrong David Reid Divmod Inc. Donovan Preston Eric Mangold Eyal Lotem Google Inc. Hybrid Logic Ltd. Hynek Schlawack Itamar Turner-Trauring James Knight Jason A. Mobarak Jean-Paul Calderone Jessica McKellar Jonathan D. Simms Jonathan Jacobs Jonathan Lange Julian Berman Jürgen Hermann Kevin Horn Kevin Turner Laurens Van Houtven Mary Gardiner Massachusetts Institute of Technology Matthew Lefkowitz Moshe Zadka Paul Swartz Pavel Pergamenshchik Rackspace, US Inc. Ralph Meijer Richard Wall Sean Riley Software Freedom Conservancy Tavendo GmbH Thijs Triemstra Thomas Herve Timothy Allen Tom Prince Travis B. Hartwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/MANIFEST.in0000644000175100001660000000070014651772574015267 0ustar00runnerdockerinclude LICENSE include NEWS.rst include SECURITY.md include tox.ini include _build_meta.py include requirements_*.in include requirements_*.txt recursive-include src/incremental *.py include src/incremental/py.typed prune src/incremental/newsfragments global-exclude .coverage* include .coveragerc graft tests prune tests/example_*/src/*.egg-info prune tests/example_*/build prune tests/example_*/dist global-exclude __pycache__ *.py[cod] *~ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/NEWS.rst0000644000175100001660000001073514651772574015050 0ustar00runnerdockerIncremental 24.7.2 (2024-07-29) =============================== Bugfixes -------- - Incremental could mis-identify that a project had opted in to version management. If a ``pyproject.toml`` in the current directory contained a ``[project]`` table with a ``name`` key, but did not contain the opt-in ``[tool.incremental]`` table, Incremental would still treat the file as if the opt-in were present and attempt to validate the configuration. This could happen in contexts outside of packaging, such as when creating a virtualenv. When operating as a setuptools plugin Incremental now always ignores invalid configuration, such as configuration that doesn't match the content of the working directory. (`#106 `__) Incremental 24.7.1 (2024-07-27) =============================== Bugfixes -------- - Incremental 24.7.0 would produce an error when parsing the ``pyproject.toml`` of a project that lacked the ``use_incremental=True`` or ``[tool.incremental]`` opt-in markers if that file lacked a ``[project]`` section containing the package name. This could cause a project that only uses ``pyproject.toml`` to configure tools to fail to build if Incremental is installed. Incremental now ignores such projects. (`#100 `__) Misc ---- - `#101 `__ Incremental 24.7.0 (2024-07-25) =============================== Features -------- - Incremental can now be configured using ``pyproject.toml``. (`#90 `__) - Incremental now provides a read-only `Hatchling version source plugin `_. (`#93 `__) Bugfixes -------- - Incremental no longer inserts a dot before the rc version component (i.e., ``1.2.3rc1`` instead of ``1.2.3.rc1``), resulting in version numbers in the `canonical format `__. (`#81 `__) - Incremental's tests are now included in the sdist release artifact. (`#80 `__) Deprecations and Removals ------------------------- - ``incremental[scripts]`` no longer depends on Twisted. (`#88 `__) - Support for Python 2.7 has been dropped for lack of test infrastructure. We no longer provide universal wheels. (`#86 `__) - Support for Python 3.5, 3.6, and 3.7 has been dropped for lack of test infrastructure. (`#92 `__) Incremental 22.10.0 (2022-10-15) ================================ No significant changes. Incremental 22.10.0.rc1 (2022-10-04) ==================================== Features -------- - Incremental now supports type-checking with Mypy (#69) Incremental 21.3.0 (2021-03-01) =============================== Bugfixes -------- - The output of incremental is now compatible with Black (#56, #60) - Incremental now properly supports PEP 440-compliant dev, rc, post suffixes (#62) - Incremental now properly supports PEP 440-compliant post releases (#37) Incremental 17.5.0 (2017-05-20) =============================== Deprecations and Removals ------------------------- - Incremental will no longer attempt to read git or svn repositories to see if the project is running from a checkout. (#30, #31, #32) Incremental 16.10.1 (2016-10-20) ================================ Bugfixes -------- - Comparisons of Versions now compare the lowercased forms of both version packages, rather than being case sensitive. (#23) Incremental 16.10.0 (2016-10-10) ================================ Bugfixes -------- - incremental.update now adds a docstring to the autogenerated file. (#18) Misc ---- - #20 Incremental 16.9.1 (2016-09-21) =============================== Bugfixes -------- - python -m incremental.update --dev now starts a dev-less package at 0, not 1. (#15) Incremental 16.9.0 (2016-09-18) =============================== Features -------- - Incremental now uses 'rcX' instead of 'pre' for prereleases/release candidates, to match PEP440. (#4) - If you reference " NEXT" and use `python -m incremental.update`, it will automatically be updated to the next release version number. (#7) Misc ---- - #1, #10 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8672943 incremental-24.7.2/PKG-INFO0000644000175100001660000001761314651772605014634 0ustar00runnerdockerMetadata-Version: 2.1 Name: incremental Version: 24.7.2 Summary: A small library that versions your Python projects. Maintainer-email: Amber Brown Project-URL: Homepage, https://github.com/twisted/incremental Project-URL: Documentation, https://twisted.org/incremental/docs/ Project-URL: Issues, https://github.com/twisted/incremental/issues Project-URL: Changelog, https://github.com/twisted/incremental/blob/trunk/NEWS.rst Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Framework :: Hatch Classifier: Framework :: Setuptools Plugin Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: setuptools>=61.0 Requires-Dist: tomli; python_version < "3.11" Provides-Extra: scripts Requires-Dist: click>=6.0; extra == "scripts" Incremental =========== |gha| |pypi| |coverage| Incremental is a small library that versions your Python projects. API documentation can be found `here `_. .. contents:: Quick Start ----------- Using setuptools ~~~~~~~~~~~~~~~~ Add Incremental to your ``pyproject.toml``: .. code-block:: toml [build-system] requires = [ "setuptools", "incremental>=24.7.2", # ← Add incremental as a build dependency ] build-backend = "setuptools.build_meta" [project] name = "" dynamic = ["version"] # ← Mark the version dynamic dependencies = [ "incremental>=24.7.2", # ← Depend on incremental at runtime ] # ... [tool.incremental] # ← Activate Incremental's setuptools plugin It's fine if the ``[tool.incremental]`` table is empty, but it must be present. Remove any ``[project] version =`` entry and any ``[tool.setuptools.dynamic] version =`` entry. Next, `initialize the project`_. Using Hatchling ~~~~~~~~~~~~~~~ If you're using `Hatchling `_ to package your project, activate Incremental's Hatchling plugin by altering your ``pyproject.toml``: .. code:: toml [build-system] requires = [ "hatchling", "incremental>=24.7.2", # ← Add incremental as a build dependency ] build-backend = "hatchling.build" [project] name = "" dynamic = ["version"] # ← Mark the version dynamic dependencies = [ "incremental>=24.7.2", # ← Depend on incremental at runtime ] # ... [tool.hatch.version] source = "incremental" # ← Activate Incremental's Hatchling plugin Incremental can be configured as usual in an optional ``[tool.incremental]`` table. The ``hatch version`` command will report the Incremental-managed version. Use the ``python -m incremental.update`` command to change the version (setting it with ``hatch version`` is not supported). Next, `initialize the project`_. Using ``setup.py`` ~~~~~~~~~~~~~~~~~~ Incremental may be used from ``setup.py`` instead of ``pyproject.toml``. Add this to your ``setup()`` call, removing any other versioning arguments: .. code:: python setup( use_incremental=True, setup_requires=['incremental'], install_requires=['incremental'], # along with any other install dependencies ... } Then `initialize the project`_. Initialize the project ~~~~~~~~~~~~~~~~~~~~~~ Install Incremental to your local environment with ``pip install incremental[scripts]``. Then run ``python -m incremental.update --create``. It will create a file in your package named ``_version.py`` like this: .. code:: python from incremental import Version __version__ = Version("", 24, 1, 0) __all__ = ["__version__"] Then, so users of your project can find your version, in your root package's ``__init__.py`` add: .. code:: python from ._version import __version__ Subsequent installations of your project will then use Incremental for versioning. Incremental Versions -------------------- ``incremental.Version`` is a class that represents a version of a given project. It is made up of the following elements (which are given during instantiation): - ``package`` (required), the name of the package this ``Version`` represents. - ``major``, ``minor``, ``micro`` (all required), the X.Y.Z of your project's ``Version``. - ``release_candidate`` (optional), set to 0 or higher to mark this ``Version`` being of a release candidate (also sometimes called a "prerelease"). - ``post`` (optional), set to 0 or higher to mark this ``Version`` as a postrelease. - ``dev`` (optional), set to 0 or higher to mark this ``Version`` as a development release. You can extract a PEP-440 compatible version string by using the ``.public()`` method, which returns a ``str`` containing the full version. This is the version you should provide to users, or publicly use. An example output would be ``"13.2.0"``, ``"17.1.2dev1"``, or ``"18.8.0rc2"``. Calling ``repr()`` with a ``Version`` will give a Python-source-code representation of it, and calling ``str()`` on a ``Version`` produces a string like ``'[Incremental, version 16.10.1]'``. Updating -------- Incremental includes a tool to automate updating your Incremental-using project's version called ``incremental.update``. It updates the ``_version.py`` file and automatically updates some uses of Incremental versions from an indeterminate version to the current one. It requires ``click`` from PyPI. ``python -m incremental.update `` will perform updates on that package. The commands that can be given after that will determine what the next version is. - ``--newversion=``, to set the project version to a fully-specified version (like 1.2.3, or 17.1.0dev1). - ``--rc``, to set the project version to ``..0rc1`` if the current version is not a release candidate, or bump the release candidate number by 1 if it is. - ``--dev``, to set the project development release number to 0 if it is not a development release, or bump the development release number by 1 if it is. - ``--patch``, to increment the patch number of the release. This will also reset the release candidate number, pass ``--rc`` at the same time to increment the patch number and make it a release candidate. - ``--post``, to set the project postrelease number to 0 if it is not a postrelease, or bump the postrelease number by 1 if it is. This will also reset the release candidate and development release numbers. If you give no arguments, it will strip the release candidate number, making it a "full release". Incremental supports "indeterminate" versions, as a stand-in for the next "full" version. This can be used when the version which will be displayed to the end-user is unknown (for example "introduced in" or "deprecated in"). Incremental supports the following indeterminate versions: - ``Version("", "NEXT", 0, 0)`` - `` NEXT`` When you run ``python -m incremental.update --rc``, these will be updated to real versions (assuming the target final version is 17.1.0): - ``Version("", 17, 1, 0, release_candidate=1)`` - `` 17.1.0rc1`` Once the final version is made, it will become: - ``Version("", 17, 1, 0)`` - `` 17.1.0`` .. |coverage| image:: https://codecov.io/gh/twisted/incremental/branch/master/graph/badge.svg?token=K2ieeL887X .. _coverage: https://codecov.io/gh/twisted/incremental .. |gha| image:: https://github.com/twisted/incremental/actions/workflows/tests.yaml/badge.svg .. _gha: https://github.com/twisted/incremental/actions/workflows/tests.yaml .. |pypi| image:: http://img.shields.io/pypi/v/incremental.svg .. _pypi: https://pypi.python.org/pypi/incremental ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/README.rst0000644000175100001660000001541114651772574015225 0ustar00runnerdockerIncremental =========== |gha| |pypi| |coverage| Incremental is a small library that versions your Python projects. API documentation can be found `here `_. .. contents:: Quick Start ----------- Using setuptools ~~~~~~~~~~~~~~~~ Add Incremental to your ``pyproject.toml``: .. code-block:: toml [build-system] requires = [ "setuptools", "incremental>=24.7.2", # ← Add incremental as a build dependency ] build-backend = "setuptools.build_meta" [project] name = "" dynamic = ["version"] # ← Mark the version dynamic dependencies = [ "incremental>=24.7.2", # ← Depend on incremental at runtime ] # ... [tool.incremental] # ← Activate Incremental's setuptools plugin It's fine if the ``[tool.incremental]`` table is empty, but it must be present. Remove any ``[project] version =`` entry and any ``[tool.setuptools.dynamic] version =`` entry. Next, `initialize the project`_. Using Hatchling ~~~~~~~~~~~~~~~ If you're using `Hatchling `_ to package your project, activate Incremental's Hatchling plugin by altering your ``pyproject.toml``: .. code:: toml [build-system] requires = [ "hatchling", "incremental>=24.7.2", # ← Add incremental as a build dependency ] build-backend = "hatchling.build" [project] name = "" dynamic = ["version"] # ← Mark the version dynamic dependencies = [ "incremental>=24.7.2", # ← Depend on incremental at runtime ] # ... [tool.hatch.version] source = "incremental" # ← Activate Incremental's Hatchling plugin Incremental can be configured as usual in an optional ``[tool.incremental]`` table. The ``hatch version`` command will report the Incremental-managed version. Use the ``python -m incremental.update`` command to change the version (setting it with ``hatch version`` is not supported). Next, `initialize the project`_. Using ``setup.py`` ~~~~~~~~~~~~~~~~~~ Incremental may be used from ``setup.py`` instead of ``pyproject.toml``. Add this to your ``setup()`` call, removing any other versioning arguments: .. code:: python setup( use_incremental=True, setup_requires=['incremental'], install_requires=['incremental'], # along with any other install dependencies ... } Then `initialize the project`_. Initialize the project ~~~~~~~~~~~~~~~~~~~~~~ Install Incremental to your local environment with ``pip install incremental[scripts]``. Then run ``python -m incremental.update --create``. It will create a file in your package named ``_version.py`` like this: .. code:: python from incremental import Version __version__ = Version("", 24, 1, 0) __all__ = ["__version__"] Then, so users of your project can find your version, in your root package's ``__init__.py`` add: .. code:: python from ._version import __version__ Subsequent installations of your project will then use Incremental for versioning. Incremental Versions -------------------- ``incremental.Version`` is a class that represents a version of a given project. It is made up of the following elements (which are given during instantiation): - ``package`` (required), the name of the package this ``Version`` represents. - ``major``, ``minor``, ``micro`` (all required), the X.Y.Z of your project's ``Version``. - ``release_candidate`` (optional), set to 0 or higher to mark this ``Version`` being of a release candidate (also sometimes called a "prerelease"). - ``post`` (optional), set to 0 or higher to mark this ``Version`` as a postrelease. - ``dev`` (optional), set to 0 or higher to mark this ``Version`` as a development release. You can extract a PEP-440 compatible version string by using the ``.public()`` method, which returns a ``str`` containing the full version. This is the version you should provide to users, or publicly use. An example output would be ``"13.2.0"``, ``"17.1.2dev1"``, or ``"18.8.0rc2"``. Calling ``repr()`` with a ``Version`` will give a Python-source-code representation of it, and calling ``str()`` on a ``Version`` produces a string like ``'[Incremental, version 16.10.1]'``. Updating -------- Incremental includes a tool to automate updating your Incremental-using project's version called ``incremental.update``. It updates the ``_version.py`` file and automatically updates some uses of Incremental versions from an indeterminate version to the current one. It requires ``click`` from PyPI. ``python -m incremental.update `` will perform updates on that package. The commands that can be given after that will determine what the next version is. - ``--newversion=``, to set the project version to a fully-specified version (like 1.2.3, or 17.1.0dev1). - ``--rc``, to set the project version to ``..0rc1`` if the current version is not a release candidate, or bump the release candidate number by 1 if it is. - ``--dev``, to set the project development release number to 0 if it is not a development release, or bump the development release number by 1 if it is. - ``--patch``, to increment the patch number of the release. This will also reset the release candidate number, pass ``--rc`` at the same time to increment the patch number and make it a release candidate. - ``--post``, to set the project postrelease number to 0 if it is not a postrelease, or bump the postrelease number by 1 if it is. This will also reset the release candidate and development release numbers. If you give no arguments, it will strip the release candidate number, making it a "full release". Incremental supports "indeterminate" versions, as a stand-in for the next "full" version. This can be used when the version which will be displayed to the end-user is unknown (for example "introduced in" or "deprecated in"). Incremental supports the following indeterminate versions: - ``Version("", "NEXT", 0, 0)`` - `` NEXT`` When you run ``python -m incremental.update --rc``, these will be updated to real versions (assuming the target final version is 17.1.0): - ``Version("", 17, 1, 0, release_candidate=1)`` - `` 17.1.0rc1`` Once the final version is made, it will become: - ``Version("", 17, 1, 0)`` - `` 17.1.0`` .. |coverage| image:: https://codecov.io/gh/twisted/incremental/branch/master/graph/badge.svg?token=K2ieeL887X .. _coverage: https://codecov.io/gh/twisted/incremental .. |gha| image:: https://github.com/twisted/incremental/actions/workflows/tests.yaml/badge.svg .. _gha: https://github.com/twisted/incremental/actions/workflows/tests.yaml .. |pypi| image:: http://img.shields.io/pypi/v/incremental.svg .. _pypi: https://pypi.python.org/pypi/incremental ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/SECURITY.md0000644000175100001660000000043114651772574015323 0ustar00runnerdocker# Security Policy The twisted/incremental project uses the same security policy as the [twisted/twisted](https://github.com/twisted/twisted) project. For more details please refer to the [Twisted security process](https://github.com/twisted/twisted?tab=security-ov-file#readme). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/_build_meta.py0000644000175100001660000000114114651772574016347 0ustar00runnerdocker""" Comply with PEP 517's restictions on in-tree backends. We use setuptools to package Incremental and want to activate the in-tree Incremental plugin to manage its own version. To do this we specify ``backend-path`` in our ``pyproject.toml``, but PEP 517 requires that when ``backend-path`` is specified: > The backend code MUST be loaded from one of the directories > specified in backend-path (i.e., it is not permitted to > specify backend-path and not have in-tree backend code). We comply by re-publishing setuptools' ``build_meta``. """ from setuptools import build_meta __all__ = ["build_meta"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/pyproject.toml0000644000175100001660000000363614651772574016460 0ustar00runnerdocker[build-system] requires = [ # Keep this aligned with the project dependencies. "setuptools >= 61.0", "tomli; python_version < '3.11'", ] backend-path = [".", "./src"] # See _build_meta.py build-backend = "_build_meta:build_meta" [project] name = "incremental" dynamic = ["version"] maintainers = [ {name = "Amber Brown", email = "hawkowl@twistedmatrix.com"}, ] classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Framework :: Hatch", "Framework :: Setuptools Plugin", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] requires-python = ">=3.8" description = "A small library that versions your Python projects." readme = "README.rst" dependencies = [ "setuptools >= 61.0", "tomli; python_version < '3.11'", ] [project.optional-dependencies] scripts = [ "click>=6.0", ] [project.urls] Homepage = "https://github.com/twisted/incremental" Documentation = "https://twisted.org/incremental/docs/" Issues = "https://github.com/twisted/incremental/issues" Changelog = "https://github.com/twisted/incremental/blob/trunk/NEWS.rst" [project.entry-points."distutils.setup_keywords"] use_incremental = "incremental:_get_distutils_version" [project.entry-points."setuptools.finalize_distribution_options"] incremental = "incremental:_get_setuptools_version" [project.entry-points.hatch] incremental = "incremental._hatch" [tool.setuptools.dynamic] version = {attr = "incremental._setuptools_version"} [tool.incremental] [tool.black] target-version = ['py36', 'py37', 'py38'] [tool.towncrier] filename = "NEWS.rst" package_dir = "src/" package = "incremental" issue_format = "`#{issue} `__" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/requirements_mypy.in0000644000175100001660000000005314651772574017663 0ustar00runnerdockermypy==0.812 twisted hatchling # for types ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/requirements_mypy.txt0000644000175100001660000000201714651772574020076 0ustar00runnerdocker# # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --output-file=requirements_mypy.txt requirements_mypy.in # attrs==23.2.0 # via # automat # twisted automat==22.10.0 # via twisted constantly==23.10.4 # via twisted hatchling==1.25.0 # via -r requirements_mypy.in hyperlink==21.0.0 # via twisted idna==3.7 # via hyperlink incremental==22.10.0 # via twisted mypy==0.812 # via -r requirements_mypy.in mypy-extensions==0.4.4 # via mypy packaging==24.1 # via hatchling pathspec==0.12.1 # via hatchling pluggy==1.5.0 # via hatchling six==1.16.0 # via automat tomli==2.0.1 # via hatchling trove-classifiers==2024.7.2 # via hatchling twisted==24.3.0 # via -r requirements_mypy.in typed-ast==1.4.3 # via mypy typing-extensions==4.12.2 # via # mypy # twisted zope-interface==6.4.post2 # via twisted # The following packages are considered to be unsafe in a requirements file: # setuptools ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/requirements_tests.in0000644000175100001660000000005014651772574020024 0ustar00runnerdockerbuild coverage coverage-p twisted hatch ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/requirements_tests.txt0000644000175100001660000000532314651772574020245 0ustar00runnerdocker# # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --output-file=requirements_tests.txt requirements_tests.in # anyio==4.4.0 # via httpx attrs==23.2.0 # via # automat # twisted automat==22.10.0 # via twisted backports-tarfile==1.2.0 # via jaraco-context build==1.2.1 # via -r requirements_tests.in certifi==2024.7.4 # via # httpcore # httpx cffi==1.16.0 # via cryptography click==8.1.7 # via # hatch # userpath constantly==23.10.4 # via twisted coverage==7.5.4 # via # -r requirements_tests.in # coverage-p coverage-p==24.7.0 # via -r requirements_tests.in cryptography==42.0.8 # via secretstorage distlib==0.3.8 # via virtualenv exceptiongroup==1.2.1 # via anyio filelock==3.15.4 # via virtualenv h11==0.14.0 # via httpcore hatch==1.12.0 # via -r requirements_tests.in hatchling==1.25.0 # via hatch httpcore==1.0.5 # via httpx httpx==0.27.0 # via hatch hyperlink==21.0.0 # via # hatch # twisted idna==3.7 # via # anyio # httpx # hyperlink importlib-metadata==8.0.0 # via keyring incremental==22.10.0 # via twisted jaraco-classes==3.4.0 # via keyring jaraco-context==5.3.0 # via keyring jaraco-functools==4.0.1 # via keyring jeepney==0.8.0 # via # keyring # secretstorage keyring==25.2.1 # via hatch markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py more-itertools==10.3.0 # via # jaraco-classes # jaraco-functools packaging==24.1 # via # build # hatch # hatchling pathspec==0.12.1 # via hatchling pexpect==4.9.0 # via hatch platformdirs==4.2.2 # via # hatch # virtualenv pluggy==1.5.0 # via hatchling ptyprocess==0.7.0 # via pexpect pycparser==2.22 # via cffi pygments==2.18.0 # via rich pyproject-hooks==1.1.0 # via build rich==13.7.1 # via hatch secretstorage==3.3.3 # via keyring shellingham==1.5.4 # via hatch six==1.16.0 # via automat sniffio==1.3.1 # via # anyio # httpx tomli==2.0.1 # via # build # hatchling tomli-w==1.0.0 # via hatch tomlkit==0.13.0 # via hatch trove-classifiers==2024.7.2 # via hatchling twisted==24.3.0 # via -r requirements_tests.in typing-extensions==4.12.2 # via # anyio # twisted userpath==1.9.2 # via hatch uv==0.2.24 # via hatch virtualenv==20.26.3 # via hatch zipp==3.19.2 # via importlib-metadata zope-interface==6.4.post2 # via twisted zstandard==0.22.0 # via hatch # The following packages are considered to be unsafe in a requirements file: # setuptools ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8672943 incremental-24.7.2/setup.cfg0000644000175100001660000000024314651772605015347 0ustar00runnerdocker[flake8] max-line-length = 88 extend-ignore = E203, # whitespace before : is not PEP8 compliant (& conflicts with black) [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1722283396.857294 incremental-24.7.2/src/0000755000175100001660000000000014651772605014316 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8622942 incremental-24.7.2/src/incremental/0000755000175100001660000000000014651772605016617 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/__init__.py0000644000175100001660000003757114651772574020752 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Versions for Python packages. See L{Version}. """ from __future__ import division, absolute_import import os import sys import warnings from typing import TYPE_CHECKING, Any, TypeVar, Union, Optional, Dict, BinaryIO from dataclasses import dataclass if TYPE_CHECKING: import io from typing_extensions import Literal from distutils.dist import Distribution as _Distribution # # Compat functions # def _cmp(a, b): # type: (Any, Any) -> int """ Compare two objects. Returns a negative number if C{a < b}, zero if they are equal, and a positive number if C{a > b}. """ if a < b: return -1 elif a == b: return 0 else: return 1 # # Versioning # class _Inf(object): """ An object that is bigger than all other objects. """ def __cmp__(self, other): # type: (object) -> int """ @param other: Another object. @type other: any @return: 0 if other is inf, 1 otherwise. @rtype: C{int} """ if other is _inf: return 0 return 1 def __lt__(self, other): # type: (object) -> bool return self.__cmp__(other) < 0 def __le__(self, other): # type: (object) -> bool return self.__cmp__(other) <= 0 def __gt__(self, other): # type: (object) -> bool return self.__cmp__(other) > 0 def __ge__(self, other): # type: (object) -> bool return self.__cmp__(other) >= 0 _inf = _Inf() class IncomparableVersions(TypeError): """ Two versions could not be compared. """ class Version(object): """ An encapsulation of a version for a project, with support for outputting PEP-440 compatible version strings. This class supports the standard major.minor.micro[rcN] scheme of versioning. """ def __init__( self, package, # type: str major, # type: Union[Literal["NEXT"], int] minor, # type: int micro, # type: int release_candidate=None, # type: Optional[int] prerelease=None, # type: Optional[int] post=None, # type: Optional[int] dev=None, # type: Optional[int] ): """ @param package: Name of the package that this is a version of. @type package: C{str} @param major: The major version number. @type major: C{int} or C{str} (for the "NEXT" symbol) @param minor: The minor version number. @type minor: C{int} @param micro: The micro version number. @type micro: C{int} @param release_candidate: The release candidate number. @type release_candidate: C{int} @param prerelease: The prerelease number. (Deprecated) @type prerelease: C{int} @param post: The postrelease number. @type post: C{int} @param dev: The development release number. @type dev: C{int} """ if release_candidate and prerelease: raise ValueError("Please only return one of these.") elif prerelease and not release_candidate: release_candidate = prerelease warnings.warn( "Passing prerelease to incremental.Version was " "deprecated in Incremental 16.9.0. Please pass " "release_candidate instead.", DeprecationWarning, stacklevel=2, ) if major == "NEXT": if minor or micro or release_candidate or post or dev: raise ValueError( "When using NEXT, all other values except Package must be 0." ) self.package = package self.major = major self.minor = minor self.micro = micro self.release_candidate = release_candidate self.post = post self.dev = dev @property def prerelease(self): # type: () -> Optional[int] warnings.warn( "Accessing incremental.Version.prerelease was " "deprecated in Incremental 16.9.0. Use " "Version.release_candidate instead.", DeprecationWarning, stacklevel=2, ) return self.release_candidate def public(self): # type: () -> str """ Return a PEP440-compatible "public" representation of this L{Version}. Examples: - 14.4.0 - 1.2.3rc1 - 14.2.1rc1dev9 - 16.04.0dev0 """ if self.major == "NEXT": return self.major if self.release_candidate is None: rc = "" else: rc = "rc%s" % (self.release_candidate,) if self.post is None: post = "" else: post = ".post%s" % (self.post,) if self.dev is None: dev = "" else: dev = ".dev%s" % (self.dev,) return "%r.%d.%d%s%s%s" % (self.major, self.minor, self.micro, rc, post, dev) base = public short = public local = public def __repr__(self): # type: () -> str if self.release_candidate is None: release_candidate = "" else: release_candidate = ", release_candidate=%r" % (self.release_candidate,) if self.post is None: post = "" else: post = ", post=%r" % (self.post,) if self.dev is None: dev = "" else: dev = ", dev=%r" % (self.dev,) return "%s(%r, %r, %d, %d%s%s%s)" % ( self.__class__.__name__, self.package, self.major, self.minor, self.micro, release_candidate, post, dev, ) def __str__(self): # type: () -> str return "[%s, version %s]" % (self.package, self.short()) def __cmp__(self, other): # type: (object) -> int """ Compare two versions, considering major versions, minor versions, micro versions, then release candidates, then postreleases, then dev releases. Package names are case insensitive. A version with a release candidate is always less than a version without a release candidate. If both versions have release candidates, they will be included in the comparison. Likewise, a version with a dev release is always less than a version without a dev release. If both versions have dev releases, they will be included in the comparison. @param other: Another version. @type other: L{Version} @return: NotImplemented when the other object is not a Version, or one of -1, 0, or 1. @raise IncomparableVersions: when the package names of the versions differ. """ if not isinstance(other, self.__class__): return NotImplemented if self.package.lower() != other.package.lower(): raise IncomparableVersions("%r != %r" % (self.package, other.package)) if self.major == "NEXT": major = _inf # type: Union[int, _Inf] else: major = self.major if self.release_candidate is None: release_candidate = _inf # type: Union[int, _Inf] else: release_candidate = self.release_candidate if self.post is None: post = -1 else: post = self.post if self.dev is None: dev = _inf # type: Union[int, _Inf] else: dev = self.dev if other.major == "NEXT": othermajor = _inf # type: Union[int, _Inf] else: othermajor = other.major if other.release_candidate is None: otherrc = _inf # type: Union[int, _Inf] else: otherrc = other.release_candidate if other.post is None: otherpost = -1 else: otherpost = other.post if other.dev is None: otherdev = _inf # type: Union[int, _Inf] else: otherdev = other.dev x = _cmp( (major, self.minor, self.micro, release_candidate, post, dev), (othermajor, other.minor, other.micro, otherrc, otherpost, otherdev), ) return x def __eq__(self, other): # type: (object) -> bool c = self.__cmp__(other) if c is NotImplemented: return c # type: ignore[return-value] return c == 0 def __ne__(self, other): # type: (object) -> bool c = self.__cmp__(other) if c is NotImplemented: return c # type: ignore[return-value] return c != 0 def __lt__(self, other): # type: (object) -> bool c = self.__cmp__(other) if c is NotImplemented: return c # type: ignore[return-value] return c < 0 def __le__(self, other): # type: (object) -> bool c = self.__cmp__(other) if c is NotImplemented: return c # type: ignore[return-value] return c <= 0 def __gt__(self, other): # type: (object) -> bool c = self.__cmp__(other) if c is NotImplemented: return c # type: ignore[return-value] return c > 0 def __ge__(self, other): # type: (object) -> bool c = self.__cmp__(other) if c is NotImplemented: return c # type: ignore[return-value] return c >= 0 def getVersionString(version): # type: (Version) -> str """ Get a friendly string for the given version object. @param version: A L{Version} object. @return: A string containing the package and short version number. """ result = "%s %s" % (version.package, version.short()) return result def _findPath(path, package): # type: (str, str) -> str """ Determine the package root directory. The result is one of: - src/{package} - {package} Where {package} is downcased. """ src_dir = os.path.join(path, "src", package.lower()) current_dir = os.path.join(path, package.lower()) if os.path.isdir(src_dir): return src_dir elif os.path.isdir(current_dir): return current_dir else: raise ValueError( "Can't find the directory of project {}: I looked in {} and {}".format( package, src_dir, current_dir ) ) def _existing_version(version_path): # type: (str) -> Version """ Load the current version from a ``_version.py`` file. """ version_info = {} # type: Dict[str, Version] with open(version_path, "r") as f: exec(f.read(), version_info) return version_info["__version__"] def _get_setuptools_version(dist): # type: (_Distribution) -> None """ Setuptools integration: load the version from the working directory This function is registered as a setuptools.finalize_distribution_options entry point [1]. Consequently, it is called in all sorts of weird contexts. In setuptools, silent failure is the law. [1]: https://setuptools.pypa.io/en/latest/userguide/extension.html#customizing-distribution-options @param dist: A (possibly) empty C{setuptools.Distribution} instance to mutate. There may be some metadata here if a `setup.py` called `setup()`, but this hook is always called before setuptools loads anything from ``pyproject.toml``. """ try: # When operating in a packaging context (i.e. building an sdist or # wheel) pyproject.toml will always be found in the current working # directory. config = _load_pyproject_toml("./pyproject.toml") except Exception: return if not config.opt_in: return try: version = _existing_version(config.version_path) except FileNotFoundError: return dist.metadata.version = version.public() def _get_distutils_version(dist, keyword, value): # type: (_Distribution, object, object) -> None """ Distutils integration: get the version from the package listed in the Distribution. This function is invoked when a C{setup.py} calls C{setup(use_incremental=True)}. @see: https://setuptools.pypa.io/en/latest/userguide/extension.html#adding-arguments """ if not value: # use_incremental=False return # pragma: no cover from setuptools.command import build_py # type: ignore sp_command = build_py.build_py(dist) sp_command.finalize_options() for item in sp_command.find_all_modules(): if item[1] == "_version": version_path = os.path.join(os.path.dirname(item[2]), "_version.py") dist.metadata.version = _existing_version(version_path).public() return raise Exception("No _version.py found.") # pragma: no cover def _load_toml(f): # type: (BinaryIO) -> Any """ Read the content of a TOML file. """ # This import is deferred to avoid a hard dependency on tomli # when no pyproject.toml is present. if sys.version_info > (3, 11): import tomllib else: import tomli as tomllib return tomllib.load(f) @dataclass(frozen=True) class _IncrementalConfig: """ Configuration loaded from a ``pyproject.toml`` file. """ opt_in: bool """ Does the pyproject.toml file contain a [tool.incremental] section? This indicates that the package has explicitly opted-in to Incremental versioning. """ package: str """The project name, capitalized as in the project metadata.""" path: str """Path to the package root""" @property def version_path(self): # type: () -> str """Path of the ``_version.py`` file. May not exist.""" return os.path.join(self.path, "_version.py") def _load_pyproject_toml(toml_path): # type: (str) -> _IncrementalConfig """ Load Incremental configuration from a ``pyproject.toml`` If the [tool.incremental] section is empty we take the project name from the [project] section. Otherwise we require only a C{name} key specifying the project name. Other keys are forbidden to allow future extension and catch typos. @param toml_path: Path to the ``pyproject.toml`` to load. """ with open(toml_path, "rb") as f: data = _load_toml(f) tool_incremental = _extract_tool_incremental(data) # Extract the project name package = None if tool_incremental is not None and "name" in tool_incremental: package = tool_incremental["name"] if package is None: # Try to fall back to [project] try: package = data["project"]["name"] except KeyError: pass if package is None: # We can't proceed without a project name. raise ValueError("""\ Incremental failed to extract the project name from pyproject.toml. Specify it like: [project] name = "Foo" Or: [tool.incremental] name = "Foo" """) if not isinstance(package, str): raise TypeError( "The project name must be a string, but found {}".format(type(package)) ) return _IncrementalConfig( opt_in=tool_incremental is not None, package=package, path=_findPath(os.path.dirname(toml_path), package), ) def _extract_tool_incremental(data): # type: (Dict[str, object]) -> Optional[Dict[str, object]] if "tool" not in data: return None if not isinstance(data["tool"], dict): raise ValueError("[tool] must be a table") if "incremental" not in data["tool"]: return None tool_incremental = data["tool"]["incremental"] if not isinstance(tool_incremental, dict): raise ValueError("[tool.incremental] must be a table") if not {"name"}.issuperset(tool_incremental.keys()): raise ValueError("Unexpected key(s) in [tool.incremental]") return tool_incremental from ._version import __version__ # noqa: E402 def _setuptools_version(): # type: () -> str return __version__.public() # pragma: no cover __all__ = ["__version__", "Version", "getVersionString"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/_hatch.py0000644000175100001660000000223114651772574020422 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import os import shlex from typing import Any, Dict, List, Type, TypedDict from hatchling.version.source.plugin.interface import VersionSourceInterface from hatchling.plugin import hookimpl from incremental import _load_pyproject_toml, _existing_version class _VersionData(TypedDict): version: str class IncrementalVersionSource(VersionSourceInterface): PLUGIN_NAME = "incremental" def get_version_data(self) -> _VersionData: # type: ignore[override] path = os.path.join(self.root, "./pyproject.toml") config = _load_pyproject_toml(path) return {"version": _existing_version(config.version_path).public()} def set_version(self, version: str, version_data: Dict[Any, Any]) -> None: raise NotImplementedError( f"Run `python -m incremental.version --newversion" f" {shlex.quote(version)}` to set the version.\n\n" f" See `python -m incremental.version --help` for more options." ) @hookimpl def hatch_register_version_source() -> List[Type[VersionSourceInterface]]: return [IncrementalVersionSource] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/_version.py0000644000175100001660000000042014651772574021016 0ustar00runnerdocker""" Provides Incremental version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update Incremental` to change this file. from incremental import Version __version__ = Version("Incremental", 24, 7, 2) __all__ = ["__version__"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/py.typed0000644000175100001660000000000014651772574020311 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8642943 incremental-24.7.2/src/incremental/tests/0000755000175100001660000000000014651772605017761 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/tests/__init__.py0000644000175100001660000000000014651772574022065 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/tests/test_pyproject.py0000644000175100001660000001242014651772574023415 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """Test handling of ``pyproject.toml`` configuration""" import os from typing import cast, Optional, Union from pathlib import Path from twisted.trial.unittest import TestCase from incremental import _load_pyproject_toml, _IncrementalConfig class VerifyPyprojectDotTomlTests(TestCase): """Test the `_load_pyproject_toml` helper function""" def _loadToml( self, toml: str, *, path: Union[Path, str, None] = None ) -> Optional[_IncrementalConfig]: """ Read a TOML snipped from a temporary file with `_load_pyproject_toml` @param toml: TOML content of the temporary file @param path: Path to which the TOML is written """ path_: str if path is None: path_ = self.mktemp() # type: ignore else: path_ = str(path) with open(path_, "w") as f: f.write(toml) try: return _load_pyproject_toml(path_) except Exception as e: if hasattr(e, "add_note"): e.add_note( # type: ignore[attr-defined] f"While loading:\n\n{toml}" ) # pragma: no coverage raise def test_fileNotFound(self): """ An absent ``pyproject.toml`` file produces no result unless there is opt-in. """ path = os.path.join(cast(str, self.mktemp()), "pyproject.toml") self.assertRaises(FileNotFoundError, _load_pyproject_toml, path) def test_brokenToml(self): """ Syntactially invalid TOML produces an exception. The specific exception varies by the underlying TOML library. """ toml = '[project]\nname = "abc' # Truncated string. self.assertRaises(Exception, self._loadToml, toml) def test_nameMissing(self): """ `ValueError` is raised when we can't extract the project name. """ for toml in [ "\n", "[tool.notincremental]\n", "[project]\n", "[tool.incremental]\n", "[project]\n[tool.incremental]\n", ]: self.assertRaises(ValueError, self._loadToml, toml) def test_nameInvalidOptIn(self): """ `TypeError` is raised when the project name isn't a string. """ for toml in [ "[project]\nname = false\n", "[tool.incremental]\nname = -1\n", "[tool.incremental]\n[project]\nname = 1.0\n", ]: with self.assertRaisesRegex(TypeError, "The project name must be a string"): self._loadToml(toml) def test_toolIncrementalInvalid(self): """ `ValueError` is raised when the ``[tool]`` or ``[tool.incremental]`` isn't a table. """ for toml in [ "tool = false\n", "[tool]\nincremental = false\n", "[tool]\nincremental = 123\n", "[tool]\nincremental = null\n", ]: self.assertRaises(ValueError, self._loadToml, toml) def test_toolIncrementalUnexpecteKeys(self): """ Raise `ValueError` when the ``[tool.incremental]`` section contains keys other than ``"name"`` """ for toml in [ "[tool.incremental]\nfoo = false\n", '[tool.incremental]\nname = "OK"\nother = false\n', ]: self.assertRaises(ValueError, self._loadToml, toml) def test_setuptoolsOptIn(self): """ The package has opted-in to Incremental version management when the ``[tool.incremental]`` section is a dict. The project name is taken from ``[tool.incremental] name`` or ``[project] name``. """ root = Path(self.mktemp()) pkg = root / "src" / "foo" pkg.mkdir(parents=True) for toml in [ '[project]\nname = "Foo"\n[tool.incremental]\n', '[tool.incremental]\nname = "Foo"\n', ]: config = self._loadToml(toml, path=root / "pyproject.toml") self.assertEqual( config, _IncrementalConfig( opt_in=True, package="Foo", path=str(pkg), ), ) def test_packagePathRequired(self): """ Raise `ValueError` when the package root can't be resolved. """ root = Path(self.mktemp()) root.mkdir() # Contains no package directory. with self.assertRaisesRegex(ValueError, "Can't find the directory of project "): self._loadToml( '[project]\nname = "foo"\n', path=root / "pyproject.toml", ) def test_noToolIncrementalSection(self): """ The ``[tool.incremental]`` table is not strictly required, but its ``opt_in=False`` indicates its absence. """ root = Path(self.mktemp()) pkg = root / "src" / "foo" pkg.mkdir(parents=True) config = self._loadToml( '[project]\nname = "Foo"\n', path=root / "pyproject.toml", ) self.assertEqual( config, _IncrementalConfig( opt_in=False, package="Foo", path=str(pkg), ), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/tests/test_update.py0000644000175100001660000010225514651772574022666 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Tests for L{incremental.update}. """ from __future__ import division, absolute_import import sys import os import datetime from twisted.python.filepath import FilePath from twisted.python.compat import NativeStringIO from twisted.trial.unittest import TestCase from incremental.update import _run, run class NonCreatedUpdateTests(TestCase): def setUp(self): self.srcdir = FilePath(self.mktemp()) self.srcdir.makedirs() packagedir = self.srcdir.child("inctestpkg") packagedir.makedirs() packagedir.child("__init__.py").setContent( b""" from incremental import Version introduced_in = Version("inctestpkg", "NEXT", 0, 0).short() next_released_version = "inctestpkg NEXT" """ ) self.getcwd = lambda: self.srcdir.path self.packagedir = packagedir class Date(object): year = 2016 month = 8 self.date = Date() def test_create(self): """ `incremental.update package --create` initialises the version. """ self.assertFalse(self.packagedir.child("_version.py").exists()) out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=False, create=True, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertTrue(self.packagedir.child("_version.py").exists()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 16, 8, 0) __all__ = ["__version__"] ''', ) class MissingTests(TestCase): def setUp(self): self.srcdir = FilePath(self.mktemp()) self.srcdir.makedirs() self.srcdir.child("srca").makedirs() packagedir = self.srcdir.child("srca").child("inctestpkg") packagedir.makedirs() packagedir.child("__init__.py").setContent( b""" from incremental import Version introduced_in = Version("inctestpkg", "NEXT", 0, 0).short() next_released_version = "inctestpkg NEXT" """ ) packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3) __all__ = ["__version__"] """ ) self.getcwd = lambda: self.srcdir.path self.packagedir = packagedir class Date(object): year = 2016 month = 8 self.date = Date() def test_path(self): """ `incremental.update package --dev` raises and quits if it can't find the package. """ out = [] with self.assertRaises(ValueError): _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) class CreatedUpdateInSrcTests(TestCase): def setUp(self): self.srcdir = FilePath(self.mktemp()) self.srcdir.makedirs() self.srcdir.child("src").makedirs() packagedir = self.srcdir.child("src").child("inctestpkg") packagedir.makedirs() packagedir.child("__init__.py").setContent( b""" from incremental import Version introduced_in = Version("inctestpkg", "NEXT", 0, 0).short() next_released_version = "inctestpkg NEXT" """ ) packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3) __all__ = ["__version__"] """ ) self.getcwd = lambda: self.srcdir.path self.packagedir = packagedir class Date(object): year = 2016 month = 8 self.date = Date() def test_path(self): """ `incremental.update package --path= --dev` increments the dev version of the package on the given path """ out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertTrue(self.packagedir.child("_version.py").exists()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, dev=0) __all__ = ["__version__"] ''', ) _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertTrue(self.packagedir.child("_version.py").exists()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, dev=1) __all__ = ["__version__"] ''', ) class CreatedUpdateTests(TestCase): maxDiff = None def setUp(self): self.srcdir = FilePath(self.mktemp()) self.srcdir.makedirs() packagedir = self.srcdir.child("inctestpkg") packagedir.makedirs() packagedir.child("__init__.py").setContent( b""" from incremental import Version introduced_in = Version("inctestpkg", "NEXT", 0, 0).short() next_released_version = "inctestpkg NEXT" """ ) packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3) __all__ = ["__version__"] """ ) self.getcwd = lambda: self.srcdir.path self.packagedir = packagedir class Date(object): year = 2016 month = 8 self.date = Date() def test_path(self): """ `incremental.update package --path= --dev` increments the dev version of the package on the given path """ out = [] _run( "inctestpkg", path=self.packagedir.path, newversion=None, patch=False, rc=False, post=False, dev=True, create=False, _date=self.date, _print=out.append, ) self.assertTrue(self.packagedir.child("_version.py").exists()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, dev=0) __all__ = ["__version__"] ''', ) def test_dev(self): """ `incremental.update package --dev` increments the dev version. """ out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertTrue(self.packagedir.child("_version.py").exists()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, dev=0) __all__ = ["__version__"] ''', ) def test_patch(self): """ `incremental.update package --patch` increments the patch version. """ out = [] _run( "inctestpkg", path=None, newversion=None, patch=True, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 4) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 2, 4).short() next_released_version = "inctestpkg 1.2.4" """, ) def test_patch_with_prerelease_and_dev(self): """ `incremental.update package --patch` increments the patch version, and disregards any old prerelease/dev versions. """ self.packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, release_candidate=1, dev=2) __all__ = ["__version__"] """ ) out = [] _run( "inctestpkg", path=None, newversion=None, patch=True, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 4) __all__ = ["__version__"] ''', ) def test_rc_patch(self): """ `incremental.update package --patch --rc` increments the patch version and makes it a release candidate. """ out = [] _run( "inctestpkg", path=None, newversion=None, patch=True, rc=True, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 4, release_candidate=1) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 2, 4, release_candidate=1).short() next_released_version = "inctestpkg 1.2.4rc1" """, ) def test_rc_with_existing_rc(self): """ `incremental.update package --rc` increments the rc version if the existing version is an rc, and discards any dev version. """ self.packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, release_candidate=1, dev=2) __all__ = ["__version__"] """ ) out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=True, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, release_candidate=2) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 2, 3, release_candidate=2).short() next_released_version = "inctestpkg 1.2.3rc2" """, ) def test_rc_with_no_rc(self): """ `incremental.update package --rc`, when the package is not a release candidate, will issue a new major/minor rc, and disregards the micro and dev. """ self.packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, dev=2) __all__ = ["__version__"] """ ) out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=True, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 16, 8, 0, release_candidate=1) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 16, 8, 0, release_candidate=1).short() next_released_version = "inctestpkg 16.8.0rc1" """, ) def test_full_with_rc(self): """ `incremental.update package`, when the package is a release candidate, will issue the major/minor, sans release candidate or dev. """ out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=True, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 16, 8, 0, release_candidate=1) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 16, 8, 0, release_candidate=1).short() next_released_version = "inctestpkg 16.8.0rc1" """, ) _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 16, 8, 0) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 16, 8, 0).short() next_released_version = "inctestpkg 16.8.0" """, ) def test_full_without_rc(self): """ `incremental.update package`, when the package is NOT a release candidate, will raise an error. """ out = [] with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( e.exception.args[0], "You need to issue a rc before updating the major/minor", ) def test_post(self): """ `incremental.update package --post` increments the post version. """ out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=True, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertTrue(self.packagedir.child("_version.py").exists()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, post=0) __all__ = ["__version__"] ''', ) def test_post_with_prerelease_and_dev(self): """ `incremental.update package --post` increments the post version, and disregards any old prerelease/dev versions. """ self.packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, release_candidate=1, dev=2) __all__ = ["__version__"] """ ) out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=True, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, post=0) __all__ = ["__version__"] ''', ) def test_post_with_existing_post(self): """ `incremental.update package --post` increments the post version if the existing version is an postrelease, and discards any dev version. """ self.packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, post=1, dev=2) __all__ = ["__version__"] """ ) out = [] _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=True, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, post=2) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 2, 3, post=2).short() next_released_version = "inctestpkg 1.2.3.post2" """, ) def test_no_mix_newversion(self): """ The `--newversion` flag can't be mixed with --patch, --rc, --post, or --dev. """ out = [] with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion="1", patch=True, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --newversion") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion="1", patch=False, rc=True, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --newversion") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion="1", patch=False, rc=False, post=True, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --newversion") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion="1", patch=False, rc=False, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --newversion") def test_no_mix_dev(self): """ The `--dev` flag can't be mixed with --patch, --rc, or --post. """ out = [] with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=True, rc=False, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --dev") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=False, rc=True, post=False, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --dev") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=True, dev=True, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --dev") def test_no_mix_create(self): """ The `--create` flag can't be mixed with --patch, --rc, --post, --dev, or --newversion. """ out = [] with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=True, rc=False, post=False, dev=False, create=True, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --create") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion="1", patch=False, rc=False, post=False, dev=False, create=True, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --create") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=False, rc=True, post=False, dev=False, create=True, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --create") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=True, dev=False, create=True, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --create") with self.assertRaises(ValueError) as e: _run( "inctestpkg", path=None, newversion=None, patch=False, rc=False, post=False, dev=True, create=True, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual(e.exception.args[0], "Only give --create") def test_newversion(self): """ `incremental.update package --newversion=1.2.3rc1.post2.dev3`, will set that version in the package. """ out = [] _run( "inctestpkg", path=None, newversion="1.2.3rc1.post2.dev3", patch=False, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3, ''' b"""release_candidate=1, post=2, dev=3) __all__ = ["__version__"] """, ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), ( b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 2, 3, """ b"""release_candidate=1, post=2, dev=3).short() next_released_version = "inctestpkg 1.2.3rc1.post2.dev3" """ ), ) def test_newversion_bare(self): """ `incremental.update package --newversion=1`, will set that version in the package. """ out = [] _run( "inctestpkg", path=None, newversion="1", patch=False, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 0, 0) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 0, 0).short() next_released_version = "inctestpkg 1.0.0" """, ) def test_newversion_bare_major_minor(self): """ `incremental.update package --newversion=1.1`, will set that version in the package. """ out = [] _run( "inctestpkg", path=None, newversion="1.1", patch=False, rc=False, post=False, dev=False, create=False, _date=self.date, _getcwd=self.getcwd, _print=out.append, ) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 1, 1, 0) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 1, 1, 0).short() next_released_version = "inctestpkg 1.1.0" """, ) class ScriptTests(TestCase): def setUp(self): self.srcdir = FilePath(self.mktemp()) self.srcdir.makedirs() self.srcdir.child("src").makedirs() packagedir = self.srcdir.child("src").child("inctestpkg") packagedir.makedirs() packagedir.child("__init__.py").setContent( b""" from incremental import Version introduced_in = Version("inctestpkg", "NEXT", 0, 0).short() next_released_version = "inctestpkg NEXT" """ ) packagedir.child("_version.py").setContent( b""" from incremental import Version __version__ = Version("inctestpkg", 1, 2, 3) __all__ = ["__version__"] """ ) self.getcwd = lambda: self.srcdir.path self.packagedir = packagedir class Date(object): year = 2016 month = 8 class DateModule(object): def today(self): return Date() self.date = DateModule() def test_run(self): """ Calling run() with no args will cause it to print help. """ stringio = NativeStringIO() self.patch(sys, "stdout", stringio) with self.assertRaises(SystemExit) as e: run(["--help"]) self.assertEqual(e.exception.args[0], 0) self.assertIn("Show this message and exit", stringio.getvalue()) def test_insufficient_args(self): """ Calling run() with no args will cause it to print help. """ stringio = NativeStringIO() self.patch(sys, "stdout", stringio) self.patch(os, "getcwd", self.getcwd) self.patch(datetime, "date", self.date) with self.assertRaises(SystemExit) as e: run(["inctestpkg", "--rc"]) self.assertEqual(e.exception.args[0], 0) self.assertIn("Updating codebase", stringio.getvalue()) self.assertEqual( self.packagedir.child("_version.py").getContent(), b'''""" Provides inctestpkg version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update inctestpkg` to change this file. from incremental import Version __version__ = Version("inctestpkg", 16, 8, 0, release_candidate=1) __all__ = ["__version__"] ''', ) self.assertEqual( self.packagedir.child("__init__.py").getContent(), b""" from incremental import Version introduced_in = Version("inctestpkg", 16, 8, 0, release_candidate=1).short() next_released_version = "inctestpkg 16.8.0rc1" """, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/tests/test_version.py0000644000175100001660000004464114651772574023075 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Tests for L{incremental}. """ from __future__ import division, absolute_import import sys import unittest import operator from incremental import getVersionString, IncomparableVersions from incremental import Version, _inf from twisted.trial.unittest import TestCase class VersionsTests(TestCase): def test_localIsShort(self): """ The local version is the same as the short version. """ va = Version("dummy", 1, 0, 0, release_candidate=1, post=2, dev=3) self.assertEqual(va.local(), va.short()) def test_versionComparison(self): """ Versions can be compared for equality and order. """ va = Version("dummy", 1, 0, 0) vb = Version("dummy", 0, 1, 0) self.assertTrue(va > vb) self.assertTrue(vb < va) self.assertTrue(va >= vb) self.assertTrue(vb <= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("dummy", 0, 1, 0)) self.assertTrue(vb == vb) @unittest.skipIf(sys.version_info < (3,), "Comparisons do not raise on py2") def test_versionComparisonNonVersion(self): """ Versions can be compared with non-versions. """ v = Version("dummy", 1, 0, 0) o = object() with self.assertRaises(TypeError): v > o with self.assertRaises(TypeError): v < o with self.assertRaises(TypeError): v >= o with self.assertRaises(TypeError): v <= o self.assertFalse(v == o) self.assertTrue(v != o) def test_versionComparisonCaseInsensitive(self): """ Version package names are case insensitive. """ va = Version("dummy", 1, 0, 0) vb = Version("DuMmY", 0, 1, 0) self.assertTrue(va > vb) self.assertTrue(vb < va) self.assertTrue(va >= vb) self.assertTrue(vb <= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("dummy", 0, 1, 0)) self.assertTrue(vb == vb) def test_comparingNEXTReleases(self): """ NEXT releases are always larger than numbered releases. """ va = Version("whatever", "NEXT", 0, 0) vb = Version("whatever", 1, 0, 0) self.assertTrue(va > vb) self.assertFalse(va < vb) self.assertNotEquals(vb, va) def test_NEXTMustBeAlone(self): """ NEXT releases must always have the rest of the numbers set to 0. """ with self.assertRaises(ValueError): Version("whatever", "NEXT", 1, 0, release_candidate=0, post=0, dev=0) with self.assertRaises(ValueError): Version("whatever", "NEXT", 0, 1, release_candidate=0, post=0, dev=0) with self.assertRaises(ValueError): Version("whatever", "NEXT", 0, 0, release_candidate=1, post=0, dev=0) with self.assertRaises(ValueError): Version("whatever", "NEXT", 0, 0, release_candidate=0, post=1, dev=0) with self.assertRaises(ValueError): Version("whatever", "NEXT", 0, 0, release_candidate=0, post=0, dev=1) def test_comparingNEXTReleasesEqual(self): """ NEXT releases are equal to each other. """ va = Version("whatever", "NEXT", 0, 0) vb = Version("whatever", "NEXT", 0, 0) self.assertEquals(vb, va) def test_comparingPrereleasesWithReleases(self): """ Prereleases are always less than versions without prereleases. """ va = Version("whatever", 1, 0, 0, prerelease=1) vb = Version("whatever", 1, 0, 0) self.assertTrue(va < vb) self.assertFalse(va > vb) self.assertNotEquals(vb, va) def test_prereleaseDeprecated(self): """ Passing 'prerelease' to Version is deprecated. """ Version("whatever", 1, 0, 0, prerelease=1) warnings = self.flushWarnings([self.test_prereleaseDeprecated]) self.assertEqual(len(warnings), 1) self.assertEqual( warnings[0]["message"], "Passing prerelease to incremental.Version was deprecated in " "Incremental 16.9.0. Please pass release_candidate instead.", ) def test_prereleaseAttributeDeprecated(self): """ Accessing 'prerelease' on a Version is deprecated. """ va = Version("whatever", 1, 0, 0, release_candidate=1) va.prerelease warnings = self.flushWarnings([self.test_prereleaseAttributeDeprecated]) self.assertEqual(len(warnings), 1) self.assertEqual( warnings[0]["message"], "Accessing incremental.Version.prerelease was deprecated in " "Incremental 16.9.0. Use Version.release_candidate instead.", ) def test_comparingReleaseCandidatesWithReleases(self): """ Release Candidates are always less than versions without release candidates. """ va = Version("whatever", 1, 0, 0, release_candidate=1) vb = Version("whatever", 1, 0, 0) self.assertTrue(va < vb) self.assertFalse(va > vb) self.assertNotEquals(vb, va) def test_comparingPostReleasesWithReleases(self): """ Post releases are always greater than versions without post releases. """ va = Version("whatever", 1, 0, 0, post=1) vb = Version("whatever", 1, 0, 0) self.assertTrue(va > vb) self.assertFalse(va < vb) self.assertNotEquals(vb, va) def test_comparingDevReleasesWithPreviousPostReleases(self): """ Dev releases are always greater than postreleases based on previous releases. """ va = Version("whatever", 1, 0, 1, dev=1) vb = Version("whatever", 1, 0, 0, post=1) self.assertTrue(va > vb) self.assertFalse(va < vb) self.assertNotEquals(vb, va) def test_comparingDevReleasesWithReleases(self): """ Dev releases are always less than versions without dev releases. """ va = Version("whatever", 1, 0, 0, dev=1) vb = Version("whatever", 1, 0, 0) self.assertTrue(va < vb) self.assertFalse(va > vb) self.assertNotEquals(vb, va) def test_rcEqualspre(self): """ Release Candidates are equal to prereleases. """ va = Version("whatever", 1, 0, 0, release_candidate=1) vb = Version("whatever", 1, 0, 0, prerelease=1) self.assertTrue(va == vb) self.assertFalse(va != vb) def test_rcOrpreButNotBoth(self): """ Release Candidate and prerelease can't both be given. """ with self.assertRaises(ValueError): Version("whatever", 1, 0, 0, prerelease=1, release_candidate=1) def test_comparingReleaseCandidates(self): """ The value specified as the release candidate is used in version comparisons. """ va = Version("whatever", 1, 0, 0, release_candidate=1) vb = Version("whatever", 1, 0, 0, release_candidate=2) self.assertTrue(va < vb) self.assertTrue(vb > va) self.assertTrue(va <= vb) self.assertTrue(vb >= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("whatever", 1, 0, 0, release_candidate=2)) self.assertTrue(va == va) def test_comparingPost(self): """ The value specified as the postrelease is used in version comparisons. """ va = Version("whatever", 1, 0, 0, post=1) vb = Version("whatever", 1, 0, 0, post=2) self.assertTrue(va < vb) self.assertTrue(vb > va) self.assertTrue(va <= vb) self.assertTrue(vb >= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("whatever", 1, 0, 0, post=2)) self.assertTrue(va == va) def test_comparingDev(self): """ The value specified as the dev release is used in version comparisons. """ va = Version("whatever", 1, 0, 0, dev=1) vb = Version("whatever", 1, 0, 0, dev=2) self.assertTrue(va < vb) self.assertTrue(vb > va) self.assertTrue(va <= vb) self.assertTrue(vb >= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("whatever", 1, 0, 0, dev=2)) self.assertTrue(va == va) def test_comparingDevAndRC(self): """ The value specified as the dev release and release candidate is used in version comparisons. """ va = Version("whatever", 1, 0, 0, release_candidate=1, dev=1) vb = Version("whatever", 1, 0, 0, release_candidate=1, dev=2) self.assertTrue(va < vb) self.assertTrue(vb > va) self.assertTrue(va <= vb) self.assertTrue(vb >= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("whatever", 1, 0, 0, release_candidate=1, dev=2)) self.assertTrue(va == va) def test_comparingDevAndRCDifferent(self): """ The value specified as the dev release and release candidate is used in version comparisons. """ va = Version("whatever", 1, 0, 0, release_candidate=1, dev=1) vb = Version("whatever", 1, 0, 0, release_candidate=2, dev=1) self.assertTrue(va < vb) self.assertTrue(vb > va) self.assertTrue(va <= vb) self.assertTrue(vb >= va) self.assertTrue(va != vb) self.assertTrue(vb == Version("whatever", 1, 0, 0, release_candidate=2, dev=1)) self.assertTrue(va == va) def test_infComparisonSelf(self): """ L{_inf} is equal to L{_inf}. This is a regression test. """ self.assertEqual(_inf, _inf) self.assertFalse(_inf < _inf) self.assertFalse(_inf > _inf) self.assertTrue(_inf >= _inf) self.assertTrue(_inf <= _inf) self.assertFalse(_inf != _inf) def test_infComparison(self): """ L{_inf} is greater than any other object. """ o = object() self.assertTrue(_inf > o) self.assertFalse(_inf < o) self.assertTrue(_inf >= o) self.assertFalse(_inf <= o) self.assertTrue(_inf != o) self.assertFalse(_inf == o) def test_disallowBuggyComparisons(self): """ The package names of the Version objects need to be the same. """ self.assertRaises( IncomparableVersions, operator.eq, Version("dummy", 1, 0, 0), Version("dumym", 1, 0, 0), ) def test_notImplementedComparisons(self): """ Comparing a L{Version} to some other object type results in C{NotImplemented}. """ va = Version("dummy", 1, 0, 0) vb = ("dummy", 1, 0, 0) # a tuple is not a Version object self.assertEqual(va.__cmp__(vb), NotImplemented) def test_repr(self): """ Calling C{repr} on a version returns a human-readable string representation of the version. """ self.assertEqual(repr(Version("dummy", 1, 2, 3)), "Version('dummy', 1, 2, 3)") def test_reprWithPrerelease(self): """ Calling C{repr} on a version with a prerelease returns a human-readable string representation of the version including the prerelease as a release candidate.. """ self.assertEqual( repr(Version("dummy", 1, 2, 3, prerelease=4)), "Version('dummy', 1, 2, 3, release_candidate=4)", ) def test_reprWithReleaseCandidate(self): """ Calling C{repr} on a version with a release candidate returns a human-readable string representation of the version including the rc. """ self.assertEqual( repr(Version("dummy", 1, 2, 3, release_candidate=4)), "Version('dummy', 1, 2, 3, release_candidate=4)", ) def test_reprWithPost(self): """ Calling C{repr} on a version with a postrelease returns a human-readable string representation of the version including the postrelease. """ self.assertEqual( repr(Version("dummy", 1, 2, 3, post=4)), "Version('dummy', 1, 2, 3, post=4)" ) def test_reprWithDev(self): """ Calling C{repr} on a version with a dev release returns a human-readable string representation of the version including the dev release. """ self.assertEqual( repr(Version("dummy", 1, 2, 3, dev=4)), "Version('dummy', 1, 2, 3, dev=4)" ) def test_str(self): """ Calling C{str} on a version returns a human-readable string representation of the version. """ self.assertEqual(str(Version("dummy", 1, 2, 3)), "[dummy, version 1.2.3]") def test_strWithPrerelease(self): """ Calling C{str} on a version with a prerelease includes the prerelease as a release candidate. """ self.assertEqual( str(Version("dummy", 1, 0, 0, prerelease=1)), "[dummy, version 1.0.0rc1]" ) def test_strWithReleaseCandidate(self): """ Calling C{str} on a version with a release candidate includes the release candidate. """ self.assertEqual( str(Version("dummy", 1, 0, 0, release_candidate=1)), "[dummy, version 1.0.0rc1]", ) def test_strWithPost(self): """ Calling C{str} on a version with a postrelease includes the postrelease. """ self.assertEqual( str(Version("dummy", 1, 0, 0, post=1)), "[dummy, version 1.0.0.post1]" ) def test_strWithDevAndReleaseCandidate(self): """ Calling C{str} on a version with a release candidate and dev release includes the release candidate and the dev release. """ self.assertEqual( str(Version("dummy", 1, 0, 0, release_candidate=1, dev=2)), "[dummy, version 1.0.0rc1.dev2]", ) def test_strWithDev(self): """ Calling C{str} on a version with a dev release includes the dev release. """ self.assertEqual( str(Version("dummy", 1, 0, 0, dev=1)), "[dummy, version 1.0.0.dev1]" ) def test_strWithDevAndPost(self): """ Calling C{str} on a version with a postrelease and dev release includes the postrelease and the dev release. """ self.assertEqual( str(Version("dummy", 1, 0, 0, post=1, dev=2)), "[dummy, version 1.0.0.post1.dev2]", ) def testShort(self): self.assertEqual(Version("dummy", 1, 2, 3).short(), "1.2.3") def test_getVersionString(self): """ L{getVersionString} returns a string with the package name and the short version number. """ self.assertEqual("Twisted 8.0.0", getVersionString(Version("Twisted", 8, 0, 0))) def test_getVersionStringWithPrerelease(self): """ L{getVersionString} includes the prerelease as a release candidate, if any. """ self.assertEqual( getVersionString(Version("whatever", 8, 0, 0, prerelease=1)), "whatever 8.0.0rc1", ) def test_getVersionStringWithReleaseCandidate(self): """ L{getVersionString} includes the release candidate, if any. """ self.assertEqual( getVersionString(Version("whatever", 8, 0, 0, release_candidate=1)), "whatever 8.0.0rc1", ) def test_getVersionStringWithPost(self): """ L{getVersionString} includes the postrelease, if any. """ self.assertEqual( getVersionString(Version("whatever", 8, 0, 0, post=1)), "whatever 8.0.0.post1", ) def test_getVersionStringWithDev(self): """ L{getVersionString} includes the dev release, if any. """ self.assertEqual( getVersionString(Version("whatever", 8, 0, 0, dev=1)), "whatever 8.0.0.dev1" ) def test_getVersionStringWithDevAndRC(self): """ L{getVersionString} includes the dev release and release candidate, if any. """ self.assertEqual( getVersionString(Version("whatever", 8, 0, 0, release_candidate=2, dev=1)), "whatever 8.0.0rc2.dev1", ) def test_getVersionStringWithDevAndPost(self): """ L{getVersionString} includes the dev release and postrelease, if any. """ self.assertEqual( getVersionString(Version("whatever", 8, 0, 0, post=2, dev=1)), "whatever 8.0.0.post2.dev1", ) def test_baseWithNEXT(self): """ The C{base} method returns just "NEXT" when NEXT is the major version. """ self.assertEqual(Version("foo", "NEXT", 0, 0).base(), "NEXT") def test_base(self): """ The C{base} method returns a very simple representation of the version. """ self.assertEqual(Version("foo", 1, 0, 0).base(), "1.0.0") def test_baseWithPrerelease(self): """ The base version includes 'rcX' for versions with prereleases. """ self.assertEqual(Version("foo", 1, 0, 0, prerelease=8).base(), "1.0.0rc8") def test_baseWithPost(self): """ The base version includes 'postX' for versions with postreleases. """ self.assertEqual(Version("foo", 1, 0, 0, post=8).base(), "1.0.0.post8") def test_baseWithDev(self): """ The base version includes 'devX' for versions with dev releases. """ self.assertEqual(Version("foo", 1, 0, 0, dev=8).base(), "1.0.0.dev8") def test_baseWithReleaseCandidate(self): """ The base version includes 'rcX' for versions with prereleases. """ self.assertEqual( Version("foo", 1, 0, 0, release_candidate=8).base(), "1.0.0rc8" ) def test_baseWithDevAndRC(self): """ The base version includes 'rcXdevX' for versions with dev releases and a release candidate. """ self.assertEqual( Version("foo", 1, 0, 0, release_candidate=2, dev=8).base(), "1.0.0rc2.dev8" ) def test_baseWithDevAndPost(self): """ The base version includes 'postXdevX' for versions with dev releases and a postrelease. """ self.assertEqual( Version("foo", 1, 0, 0, post=2, dev=8).base(), "1.0.0.post2.dev8" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/src/incremental/update.py0000644000175100001660000001602014651772574020457 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from __future__ import absolute_import, division, print_function import click import os import datetime from typing import Dict, Optional, Callable from incremental import Version, _findPath, _existing_version _VERSIONPY_TEMPLATE = '''""" Provides {package} version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update {package}` to change this file. from incremental import Version __version__ = {version_repr} __all__ = ["__version__"] ''' _YEAR_START = 2000 def _run( package, # type: str path, # type: Optional[str] newversion, # type: Optional[str] patch, # type: bool rc, # type: bool post, # type: bool dev, # type: bool create, # type: bool _date=None, # type: Optional[datetime.date] _getcwd=None, # type: Optional[Callable[[], str]] _print=print, # type: Callable[[object], object] ): # type: (...) -> None if not _getcwd: _getcwd = os.getcwd if not _date: _date = datetime.date.today() if not path: path = _findPath(_getcwd(), package) if ( newversion and patch or newversion and dev or newversion and rc or newversion and post ): raise ValueError("Only give --newversion") if dev and patch or dev and rc or dev and post: raise ValueError("Only give --dev") if ( create and dev or create and patch or create and rc or create and post or create and newversion ): raise ValueError("Only give --create") versionpath = os.path.join(path, "_version.py") if newversion: from pkg_resources import parse_version existing = _existing_version(versionpath) st_version = parse_version(newversion)._version # type: ignore[attr-defined] release = list(st_version.release) minor = 0 micro = 0 if len(release) == 1: (major,) = release elif len(release) == 2: major, minor = release else: major, minor, micro = release v = Version( package, major, minor, micro, release_candidate=st_version.pre[1] if st_version.pre else None, post=st_version.post[1] if st_version.post else None, dev=st_version.dev[1] if st_version.dev else None, ) elif create: v = Version(package, _date.year - _YEAR_START, _date.month, 0) existing = v elif rc and not patch: existing = _existing_version(versionpath) if existing.release_candidate: v = Version( package, existing.major, existing.minor, existing.micro, existing.release_candidate + 1, ) else: v = Version(package, _date.year - _YEAR_START, _date.month, 0, 1) elif patch: existing = _existing_version(versionpath) v = Version( package, existing.major, existing.minor, existing.micro + 1, 1 if rc else None, ) elif post: existing = _existing_version(versionpath) if existing.post is None: _post = 0 else: _post = existing.post + 1 v = Version(package, existing.major, existing.minor, existing.micro, post=_post) elif dev: existing = _existing_version(versionpath) if existing.dev is None: _dev = 0 else: _dev = existing.dev + 1 v = Version( package, existing.major, existing.minor, existing.micro, existing.release_candidate, dev=_dev, ) else: existing = _existing_version(versionpath) if existing.release_candidate: v = Version(package, existing.major, existing.minor, existing.micro) else: raise ValueError("You need to issue a rc before updating the major/minor") NEXT_repr = repr(Version(package, "NEXT", 0, 0)).split("#")[0].replace("'", '"') NEXT_repr_bytes = NEXT_repr.encode("utf8") version_repr = repr(v).split("#")[0].replace("'", '"') version_repr_bytes = version_repr.encode("utf8") existing_version_repr = repr(existing).split("#")[0].replace("'", '"') existing_version_repr_bytes = existing_version_repr.encode("utf8") _print("Updating codebase to %s" % (v.public())) for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: filepath = os.path.join(dirpath, filename) with open(filepath, "rb") as f: original_content = f.read() content = original_content # Replace previous release_candidate calls to the new one if existing.release_candidate: content = content.replace( existing_version_repr_bytes, version_repr_bytes ) content = content.replace( (package.encode("utf8") + b" " + existing.public().encode("utf8")), (package.encode("utf8") + b" " + v.public().encode("utf8")), ) # Replace NEXT Version calls with the new one content = content.replace(NEXT_repr_bytes, version_repr_bytes) content = content.replace( NEXT_repr_bytes.replace(b"'", b'"'), version_repr_bytes ) # Replace NEXT with content = content.replace( package.encode("utf8") + b" NEXT", (package.encode("utf8") + b" " + v.public().encode("utf8")), ) if content != original_content: _print("Updating %s" % (filepath,)) with open(filepath, "wb") as f: f.write(content) _print("Updating %s" % (versionpath,)) with open(versionpath, "wb") as f: f.write( ( _VERSIONPY_TEMPLATE.format(package=package, version_repr=version_repr) ).encode("utf8") ) @click.command() @click.argument("package") @click.option("--path", default=None) @click.option("--newversion", default=None) @click.option("--patch", is_flag=True) @click.option("--rc", is_flag=True) @click.option("--post", is_flag=True) @click.option("--dev", is_flag=True) @click.option("--create", is_flag=True) def run( package, # type: str path, # type: Optional[str] newversion, # type: Optional[str] patch, # type: bool rc, # type: bool post, # type: bool dev, # type: bool create, # type: bool ): # type: (...) -> None return _run( package=package, path=path, newversion=newversion, patch=patch, rc=rc, post=post, dev=dev, create=create, ) if __name__ == "__main__": # pragma: no cover run() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8662944 incremental-24.7.2/src/incremental.egg-info/0000755000175100001660000000000014651772605020311 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283396.0 incremental-24.7.2/src/incremental.egg-info/PKG-INFO0000644000175100001660000001761314651772604021415 0ustar00runnerdockerMetadata-Version: 2.1 Name: incremental Version: 24.7.2 Summary: A small library that versions your Python projects. Maintainer-email: Amber Brown Project-URL: Homepage, https://github.com/twisted/incremental Project-URL: Documentation, https://twisted.org/incremental/docs/ Project-URL: Issues, https://github.com/twisted/incremental/issues Project-URL: Changelog, https://github.com/twisted/incremental/blob/trunk/NEWS.rst Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Framework :: Hatch Classifier: Framework :: Setuptools Plugin Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: setuptools>=61.0 Requires-Dist: tomli; python_version < "3.11" Provides-Extra: scripts Requires-Dist: click>=6.0; extra == "scripts" Incremental =========== |gha| |pypi| |coverage| Incremental is a small library that versions your Python projects. API documentation can be found `here `_. .. contents:: Quick Start ----------- Using setuptools ~~~~~~~~~~~~~~~~ Add Incremental to your ``pyproject.toml``: .. code-block:: toml [build-system] requires = [ "setuptools", "incremental>=24.7.2", # ← Add incremental as a build dependency ] build-backend = "setuptools.build_meta" [project] name = "" dynamic = ["version"] # ← Mark the version dynamic dependencies = [ "incremental>=24.7.2", # ← Depend on incremental at runtime ] # ... [tool.incremental] # ← Activate Incremental's setuptools plugin It's fine if the ``[tool.incremental]`` table is empty, but it must be present. Remove any ``[project] version =`` entry and any ``[tool.setuptools.dynamic] version =`` entry. Next, `initialize the project`_. Using Hatchling ~~~~~~~~~~~~~~~ If you're using `Hatchling `_ to package your project, activate Incremental's Hatchling plugin by altering your ``pyproject.toml``: .. code:: toml [build-system] requires = [ "hatchling", "incremental>=24.7.2", # ← Add incremental as a build dependency ] build-backend = "hatchling.build" [project] name = "" dynamic = ["version"] # ← Mark the version dynamic dependencies = [ "incremental>=24.7.2", # ← Depend on incremental at runtime ] # ... [tool.hatch.version] source = "incremental" # ← Activate Incremental's Hatchling plugin Incremental can be configured as usual in an optional ``[tool.incremental]`` table. The ``hatch version`` command will report the Incremental-managed version. Use the ``python -m incremental.update`` command to change the version (setting it with ``hatch version`` is not supported). Next, `initialize the project`_. Using ``setup.py`` ~~~~~~~~~~~~~~~~~~ Incremental may be used from ``setup.py`` instead of ``pyproject.toml``. Add this to your ``setup()`` call, removing any other versioning arguments: .. code:: python setup( use_incremental=True, setup_requires=['incremental'], install_requires=['incremental'], # along with any other install dependencies ... } Then `initialize the project`_. Initialize the project ~~~~~~~~~~~~~~~~~~~~~~ Install Incremental to your local environment with ``pip install incremental[scripts]``. Then run ``python -m incremental.update --create``. It will create a file in your package named ``_version.py`` like this: .. code:: python from incremental import Version __version__ = Version("", 24, 1, 0) __all__ = ["__version__"] Then, so users of your project can find your version, in your root package's ``__init__.py`` add: .. code:: python from ._version import __version__ Subsequent installations of your project will then use Incremental for versioning. Incremental Versions -------------------- ``incremental.Version`` is a class that represents a version of a given project. It is made up of the following elements (which are given during instantiation): - ``package`` (required), the name of the package this ``Version`` represents. - ``major``, ``minor``, ``micro`` (all required), the X.Y.Z of your project's ``Version``. - ``release_candidate`` (optional), set to 0 or higher to mark this ``Version`` being of a release candidate (also sometimes called a "prerelease"). - ``post`` (optional), set to 0 or higher to mark this ``Version`` as a postrelease. - ``dev`` (optional), set to 0 or higher to mark this ``Version`` as a development release. You can extract a PEP-440 compatible version string by using the ``.public()`` method, which returns a ``str`` containing the full version. This is the version you should provide to users, or publicly use. An example output would be ``"13.2.0"``, ``"17.1.2dev1"``, or ``"18.8.0rc2"``. Calling ``repr()`` with a ``Version`` will give a Python-source-code representation of it, and calling ``str()`` on a ``Version`` produces a string like ``'[Incremental, version 16.10.1]'``. Updating -------- Incremental includes a tool to automate updating your Incremental-using project's version called ``incremental.update``. It updates the ``_version.py`` file and automatically updates some uses of Incremental versions from an indeterminate version to the current one. It requires ``click`` from PyPI. ``python -m incremental.update `` will perform updates on that package. The commands that can be given after that will determine what the next version is. - ``--newversion=``, to set the project version to a fully-specified version (like 1.2.3, or 17.1.0dev1). - ``--rc``, to set the project version to ``..0rc1`` if the current version is not a release candidate, or bump the release candidate number by 1 if it is. - ``--dev``, to set the project development release number to 0 if it is not a development release, or bump the development release number by 1 if it is. - ``--patch``, to increment the patch number of the release. This will also reset the release candidate number, pass ``--rc`` at the same time to increment the patch number and make it a release candidate. - ``--post``, to set the project postrelease number to 0 if it is not a postrelease, or bump the postrelease number by 1 if it is. This will also reset the release candidate and development release numbers. If you give no arguments, it will strip the release candidate number, making it a "full release". Incremental supports "indeterminate" versions, as a stand-in for the next "full" version. This can be used when the version which will be displayed to the end-user is unknown (for example "introduced in" or "deprecated in"). Incremental supports the following indeterminate versions: - ``Version("", "NEXT", 0, 0)`` - `` NEXT`` When you run ``python -m incremental.update --rc``, these will be updated to real versions (assuming the target final version is 17.1.0): - ``Version("", 17, 1, 0, release_candidate=1)`` - `` 17.1.0rc1`` Once the final version is made, it will become: - ``Version("", 17, 1, 0)`` - `` 17.1.0`` .. |coverage| image:: https://codecov.io/gh/twisted/incremental/branch/master/graph/badge.svg?token=K2ieeL887X .. _coverage: https://codecov.io/gh/twisted/incremental .. |gha| image:: https://github.com/twisted/incremental/actions/workflows/tests.yaml/badge.svg .. _gha: https://github.com/twisted/incremental/actions/workflows/tests.yaml .. |pypi| image:: http://img.shields.io/pypi/v/incremental.svg .. _pypi: https://pypi.python.org/pypi/incremental ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283396.0 incremental-24.7.2/src/incremental.egg-info/SOURCES.txt0000644000175100001660000000241414651772604022175 0ustar00runnerdocker.coveragerc LICENSE MANIFEST.in NEWS.rst README.rst SECURITY.md _build_meta.py pyproject.toml requirements_mypy.in requirements_mypy.txt requirements_tests.in requirements_tests.txt setup.cfg tox.ini src/incremental/__init__.py src/incremental/_hatch.py src/incremental/_version.py src/incremental/py.typed src/incremental/update.py src/incremental.egg-info/PKG-INFO src/incremental.egg-info/SOURCES.txt src/incremental.egg-info/dependency_links.txt src/incremental.egg-info/entry_points.txt src/incremental.egg-info/requires.txt src/incremental.egg-info/top_level.txt src/incremental/tests/__init__.py src/incremental/tests/test_pyproject.py src/incremental/tests/test_update.py src/incremental/tests/test_version.py tests/test_examples.py tests/example_hatchling/pyproject.toml tests/example_hatchling/example_hatchling/__init__.py tests/example_hatchling/example_hatchling/_version.py tests/example_noop/pyproject.toml tests/example_noop/setup.py tests/example_noop/example_noop/__init__.py tests/example_setuppy/setup.py tests/example_setuppy/src/example_setuppy/__init__.py tests/example_setuppy/src/example_setuppy/_version.py tests/example_setuptools/pyproject.toml tests/example_setuptools/src/example_setuptools/__init__.py tests/example_setuptools/src/example_setuptools/_version.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283396.0 incremental-24.7.2/src/incremental.egg-info/dependency_links.txt0000644000175100001660000000000114651772604024356 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283396.0 incremental-24.7.2/src/incremental.egg-info/entry_points.txt0000644000175100001660000000033014651772604023602 0ustar00runnerdocker[distutils.setup_keywords] use_incremental = incremental:_get_distutils_version [hatch] incremental = incremental._hatch [setuptools.finalize_distribution_options] incremental = incremental:_get_setuptools_version ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283396.0 incremental-24.7.2/src/incremental.egg-info/requires.txt0000644000175100001660000000011114651772604022701 0ustar00runnerdockersetuptools>=61.0 [:python_version < "3.11"] tomli [scripts] click>=6.0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283396.0 incremental-24.7.2/src/incremental.egg-info/top_level.txt0000644000175100001660000000001414651772604023035 0ustar00runnerdockerincremental ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8642943 incremental-24.7.2/tests/0000755000175100001660000000000014651772605014671 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8642943 incremental-24.7.2/tests/example_hatchling/0000755000175100001660000000000014651772605020345 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8652942 incremental-24.7.2/tests/example_hatchling/example_hatchling/0000755000175100001660000000000014651772605024021 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_hatchling/example_hatchling/__init__.py0000644000175100001660000000007514651772574026141 0ustar00runnerdockerfrom ._version import __version__ __all__ = ["__version__"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_hatchling/example_hatchling/_version.py0000644000175100001660000000044214651772574026224 0ustar00runnerdocker""" Provides example_hatchling version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update example_hatchling` to change this file. from incremental import Version __version__ = Version("example_hatchling", 24, 7, 0) __all__ = ["__version__"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_hatchling/pyproject.toml0000644000175100001660000000036314651772574023270 0ustar00runnerdocker[build-system] requires = [ "hatchling", "incremental", ] build-backend = "hatchling.build" [project] name = "example_hatchling" dependencies = [ "incremental", ] dynamic = ["version"] [tool.hatch.version] source = "incremental" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8652942 incremental-24.7.2/tests/example_noop/0000755000175100001660000000000014651772605017357 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8652942 incremental-24.7.2/tests/example_noop/example_noop/0000755000175100001660000000000014651772605022045 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_noop/example_noop/__init__.py0000644000175100001660000000041214651772574024160 0ustar00runnerdocker""" An example setuptools project that doesn't use Incremental at all. This is used to verify that Incremental correctly deactivates itself when there is no opt-in, even though setuptools always invokes its hook. """ __version__ = "100" __all__ = ["__version__"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_noop/pyproject.toml0000644000175100001660000000021014651772574022271 0ustar00runnerdocker# This file exists, but doesn't contain any [project] or # [tool.incremental] configuration so Incremental ignores it. [tool.something] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_noop/setup.py0000644000175100001660000000043314651772574021076 0ustar00runnerdockerfrom setuptools import setup setup( name="example_noop", version="100", packages=["example_noop"], zip_safe=False, setup_requires=[ # Ensure Incremental is installed for test purposes # (but it won't do anything). "incremental", ], ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8652942 incremental-24.7.2/tests/example_setuppy/0000755000175100001660000000000014651772605020115 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_setuppy/setup.py0000644000175100001660000000034314651772574021634 0ustar00runnerdockerfrom setuptools import setup setup( name="example_setuppy", package_dir={"": "src"}, packages=["example_setuppy"], use_incremental=True, zip_safe=False, setup_requires=[ "incremental", ], ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8582942 incremental-24.7.2/tests/example_setuppy/src/0000755000175100001660000000000014651772605020704 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8662944 incremental-24.7.2/tests/example_setuppy/src/example_setuppy/0000755000175100001660000000000014651772605024130 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_setuppy/src/example_setuppy/__init__.py0000644000175100001660000000037714651772574026255 0ustar00runnerdocker""" An example project that uses setup.py @added: example_setuppy NEXT """ from incremental import Version from ._version import __version__ __all__ = ["__version__"] if Version("example_setuppy", "NEXT", 0, 0) > __version__: print("Unreleased!") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_setuppy/src/example_setuppy/_version.py0000644000175100001660000000034514651772574026335 0ustar00runnerdocker# This file is auto-generated! Do not edit! # Use `python -m incremental.update example_setuppy` to change this file. from incremental import Version __version__ = Version("example_setuppy", 1, 2, 3) __all__ = ["__version__"] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8662944 incremental-24.7.2/tests/example_setuptools/0000755000175100001660000000000014651772605020625 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_setuptools/pyproject.toml0000644000175100001660000000034214651772574023545 0ustar00runnerdocker[build-system] requires = [ "setuptools", "incremental", ] build-backend = "setuptools.build_meta" [project] name = "example_setuptools" dependencies = [ "incremental", ] dynamic = ["version"] [tool.incremental] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8592942 incremental-24.7.2/tests/example_setuptools/src/0000755000175100001660000000000014651772605021414 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1722283396.8662944 incremental-24.7.2/tests/example_setuptools/src/example_setuptools/0000755000175100001660000000000014651772605025350 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_setuptools/src/example_setuptools/__init__.py0000644000175100001660000000043114651772574027464 0ustar00runnerdocker""" An example project that uses setuptools in pyproject.toml @added: example_setuptools NEXT """ from incremental import Version from ._version import __version__ __all__ = ["__version__"] if Version("example_setuptools", "NEXT", 0, 0) > __version__: print("Unreleased!") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/example_setuptools/src/example_setuptools/_version.py0000644000175100001660000000035314651772574027554 0ustar00runnerdocker# This file is auto-generated! Do not edit! # Use `python -m incremental.update example_setuptools` to change this file. from incremental import Version __version__ = Version("example_setuptools", 2, 3, 4) __all__ = ["__version__"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tests/test_examples.py0000644000175100001660000001646114651772574020135 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Tests for the packaging examples. """ import os from importlib import metadata from subprocess import run from tempfile import TemporaryDirectory from build import ProjectBuilder, BuildBackendException from build.env import DefaultIsolatedEnv from twisted.python.filepath import FilePath from twisted.trial.unittest import TestCase from incremental import Version TEST_DIR = FilePath(os.path.abspath(os.path.dirname(__file__))) def build_and_install(path): # type: (FilePath) -> None with TemporaryDirectory(prefix="dist") as dist_dir: with DefaultIsolatedEnv(installer="pip") as env: env.install( { # Install the *exact* version of Incremental under test. # Otherwise pip might select a different version from # its cache. # # These are formally PEP 508 markers, so we pass a # file URL. "incremental @ file://" + os.environ["TOX_PACKAGE"], # A .pth file so that subprocess generate coverage. "coverage-p", } ) builder = ProjectBuilder.from_isolated_env(env, path.path) env.install(builder.build_system_requires) env.install(builder.get_requires_for_build("wheel", {})) pkgfile = builder.build("wheel", output_directory=dist_dir) # Force reinstall in case tox reused the venv. run(["pip", "install", "--force-reinstall", pkgfile], check=True) class ExampleTests(TestCase): def test_setuppy_version(self): """ example_setuppy has a version of 1.2.3. """ build_and_install(TEST_DIR.child("example_setuppy")) import example_setuppy self.assertEqual(example_setuppy.__version__.base(), "1.2.3") self.assertEqual(metadata.version("example_setuppy"), "1.2.3") def test_setuptools_version(self): """ example_setuptools has a version of 2.3.4. """ build_and_install(TEST_DIR.child("example_setuptools")) import example_setuptools self.assertEqual(example_setuptools.__version__.base(), "2.3.4") self.assertEqual(metadata.version("example_setuptools"), "2.3.4") def test_setuptools_no_optin(self): """ The setuptools plugin is a no-op when there isn't a [tool.incremental] table in pyproject.toml. """ src = FilePath(self.mktemp()) src.makedirs() src.child("pyproject.toml").setContent( b"""\ [project] name = "example_no_optin" version = "0.0.0" """ ) package_dir = src.child("example_no_optin") package_dir.makedirs() package_dir.child("__init__.py").setContent(b"") package_dir.child("_version.py").setContent( b"from incremental import Version\n" b'__version__ = Version("example_no_optin", 24, 7, 0)\n' ) build_and_install(src) self.assertEqual(metadata.version("example_no_optin"), "0.0.0") def test_setuptools_no_package(self): """ The setuptools plugin is a no-op when there isn't a package directory that matches the project name. """ src = FilePath(self.mktemp()) src.makedirs() src.child("pyproject.toml").setContent( b"""\ [project] name = "example_no_package" version = "0.0.0" [tool.incremental] """ ) build_and_install(src) self.assertEqual(metadata.version("example_no_package"), "0.0.0") def test_setuptools_missing_versionpy(self): """ The setuptools plugin is a no-op when the ``_version.py`` file isn't present. """ src = FilePath(self.mktemp()) src.makedirs() src.child("setup.py").setContent( b"""\ from setuptools import setup setup( name="example_missing_versionpy", version="0.0.1", packages=["example_missing_versionpy"], zip_safe=False, ) """ ) src.child("pyproject.toml").setContent( b"""\ [tool.incremental] name = "example_missing_versionpy" """ ) package_dir = src.child("example_missing_versionpy") package_dir.makedirs() package_dir.child("__init__.py").setContent(b"") # No _version.py exists build_and_install(src) # The version from setup.py wins. self.assertEqual(metadata.version("example_missing_versionpy"), "0.0.1") def test_setuptools_bad_versionpy(self): """ The setuptools plugin surfaces syntax errors in ``_version.py``. """ src = FilePath(self.mktemp()) src.makedirs() src.child("setup.py").setContent( b"""\ from setuptools import setup setup( name="example_bad_versionpy", version="0.1.2", packages=["example_bad_versionpy"], zip_safe=False, ) """ ) src.child("pyproject.toml").setContent( b"""\ [tool.incremental] name = "example_bad_versionpy" """ ) package_dir = src.child("example_bad_versionpy") package_dir.makedirs() package_dir.child("_version.py").setContent(b"bad version.py") with self.assertRaises(BuildBackendException): # This also spews a SyntaxError traceback to stdout. build_and_install(src) def test_hatchling_get_version(self): """ example_hatchling has a version of 24.7.0. """ build_and_install(TEST_DIR.child("example_hatchling")) import example_hatchling self.assertEqual( example_hatchling.__version__, Version("example_hatchling", 24, 7, 0), ) self.assertEqual(metadata.version("example_hatchling"), "24.7.0") def test_hatch_version(self): """ The ``hatch version`` command reports the version of a package packaged with hatchling. """ proc = run( ["hatch", "version"], cwd=TEST_DIR.child("example_hatchling").path, check=True, capture_output=True, ) self.assertEqual(proc.stdout, b"24.7.0\n") def test_hatch_version_set(self): """ The ``hatch version`` command can't set the version so its output tells the user to use ``incremental.update`` instead. """ proc = run( ["hatch", "--no-color", "version", "24.8.0"], cwd=TEST_DIR.child("example_hatchling").path, check=False, capture_output=True, ) suggestion = b"Run `python -m incremental.version --newversion 24.8.0` to set the version." self.assertGreater(proc.returncode, 0) self.assertRegex( proc.stdout, # Hatch may wrap the output, so we are flexible about the specifics of whitespace. suggestion.replace(b".", rb"\.").replace(b" ", b"\\s+"), ) def test_noop(self): """ The Incremental setuptools hook is a silent no-op when there is no Incremental configuration to be found. """ build_and_install(TEST_DIR.child("example_noop")) import example_noop self.assertEqual(example_noop.__version__, "100") self.assertEqual(metadata.version("example_noop"), "100") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1722283388.0 incremental-24.7.2/tox.ini0000644000175100001660000000304614651772574015052 0ustar00runnerdocker[tox] minversion = 3.23.0 requires = virtualenv >= 20.4.3 tox < 4.0.0 tox-wheel >= 0.6.0 isolated_build = true envlist = flake8, tests, apidocs [testenv] wheel = true wheel_build_env = build skip_install = pindeps: true deps = tests: -rrequirements_tests.txt mypy: -rrequirements_mypy.txt apidocs: pydoctor lint: pre-commit pindeps: pip-tools extras = mypy: scripts tests: scripts setenv = ; Suppress pointless chatter in the output. PIP_DISABLE_PIP_VERSION_CHECK=yes tests: COVERAGE_PROCESS_START={toxinidir}/.coveragerc ; This must be an absolute path because the example tests ; run Python processes with alternative working directories. tests: COVERAGE_FILE={toxinidir}/.coverage commands = python -V lint: pre-commit run --all-files --show-diff-on-failure apidocs: pydoctor -q --project-name incremental src/incremental tests: coverage --version tests: {envbindir}/trial --version tests: coverage erase tests: coverage run {envbindir}/trial incremental tests: coverage run {envbindir}/trial tests/test_examples.py tests: coverage combine tests: coverage report tests: coverage html tests: coverage xml mypy: mypy src pindeps: pip-compile -o requirements_tests.txt requirements_tests.in {posargs} pindeps: pip-compile -o requirements_mypy.txt requirements_mypy.in {posargs} [testenv:build] # empty environment to build universal wheel once per tox invocation # https://github.com/ionelmc/tox-wheel#build-configuration