Projects
openEuler:Mainline
python-setuptools_scm
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 3
View file
_service:tar_scm:python-setuptools_scm.spec
Changed
@@ -1,12 +1,13 @@ %global _empty_manifest_terminate_build 0 Name: python-setuptools_scm -Version: 6.0.0 +Version: 6.4.2 Release: 1 Summary: Manage your Python package versions in SCM metadata License: MIT URL: https://pypi.python.org/pypi/setuptools_scm/ -Source0: https://files.pythonhosted.org/packages/bf/b1/e647ab0ad77ad74fde7f088cfa796b6b8ff24d4e2d4fa5a0d924fdbd9c6b/setuptools_scm-6.0.0.tar.gz +Source0: https://files.pythonhosted.org/packages/4a/18/477d3d9eb2f88230ff2a41de9d8ffa3554b706352787d289f57f76bfba0b/setuptools_scm-6.4.2.tar.gz +BuildRequires: python3-tomli python3-packaging BuildArch: noarch Requires: python3-setuptool Requires: python3-toml @@ -70,11 +71,15 @@ %files -n python3-setuptools_scm -f filelist.lst %dir %{python3_sitelib}/* +%{python3_sitelib}/setuptools_scm %files help -f doclist.lst %{_pkgdocdir} %changelog +* Sun Apr 23 2023 caodongxia <caodongxia@h-partners.com> - 6.4.2-1 +- Update to 6.4.2 + * Thu Jun 16 2022 OpenStack_SIG <openstack@openeuler.org> - 6.0.0-1 - Upgrade version for openstack yoga
View file
_service
Changed
@@ -2,7 +2,7 @@ <service name="tar_scm"> <param name="scm">git</param> <param name="url">git@gitee.com:src-openeuler/python-setuptools_scm.git</param> - <param name="revision">a0a7f860f9a33c31a0ec8e4f7990e7afce043d9a</param> + <param name="revision">master</param> <param name="exclude">*</param> <param name="extract">*</param> </service>
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/.github
Deleted
-(directory)
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/.github/FUNDING.yml
Deleted
@@ -1,1 +0,0 @@ -tidelift: pypi/setuptools-scm
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/.github/workflows
Deleted
-(directory)
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/.github/workflows/pre-commit.yml
Deleted
@@ -1,20 +0,0 @@ -name: pre-commit - -on: - pull_request: - push: - branches: master - -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v2 - - name: set PY - run: echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - - uses: actions/cache@v1 - with: - path: ~/.cache/pre-commit - key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} - - uses: pre-commit/action@v1.0.0
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/.github/workflows/python-tests.yml
Deleted
@@ -1,122 +0,0 @@ -name: python tests+artifacts+release - -on: - pull_request: - push: - branches: - - main - tags: - - "v*" - release: - -jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - python_version: '3.6', '3.7', '3.8', '3.9', 'pypy3' - os: windows-latest, ubuntu-latest #, macos-latest - include: - - os: windows-latest - python_version: 'msys2' - - name: ${{ matrix.os }} - Python ${{ matrix.python_version }} - steps: - - uses: actions/checkout@v1 - - name: Setup python - uses: actions/setup-python@v2 - if: matrix.python_version != 'msys2' - with: - python-version: ${{ matrix.python_version }} - architecture: x64 - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - if: matrix.python_version == 'msys2' - with: - msystem: MINGW64 - install: git mingw-w64-x86_64-python mingw-w64-x86_64-python-setuptools - update: true - - run: pip install -U setuptools - if: matrix.python_version != 'msys2' - - run: pip install -e .toml pytest - - run: pytest - - check_selfinstall: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python_version: '3.6', '3.9', 'pypy3' - installer: "pip install", easy_install - name: check self install - Python ${{ matrix.python_version }} via ${{ matrix.installer }} - steps: - - uses: actions/checkout@v1 - - name: Setup python - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python_version }} - architecture: x64 - # self install testing needs some clarity - # so its being executed without any other tools running - # setuptools smaller 52 is needed too di easy_install - - run: pip install -U "setuptools<52" - - run: python setup.py egg_info - - run: python setup.py sdist - - run: ${{ matrix.installer }} dist/* - - run: python testing/check_self_install.py - - - dist: - runs-on: ubuntu-latest - - needs: test - name: Python bdist/wheel - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v2 - with: - python-version: "3.8" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install --upgrade wheel setuptools build - - - name: Build package - run: python -m build -s -w -o dist/ - - uses: actions/upload-artifact@v2 - with: - name: dist - path: dist - - - dist_check: - runs-on: ubuntu-latest - needs: dist - steps: - - uses: actions/setup-python@v2 - with: - python-version: "3.8" - - name: Install dependencies - run: pip install twine - - uses: actions/download-artifact@v2 - with: - name: dist - path: dist - - run: twine check dist/* - - dist_upload: - - runs-on: ubuntu-latest - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') - needs: dist_check - steps: - - uses: actions/download-artifact@v2 - with: - name: dist - path: dist - - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.pypi_token }}
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/.gitignore
Deleted
@@ -1,49 +0,0 @@ -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion - -*.iml - -## Directory-based project format: -.idea/ - -### Other editors -.*.swp - - -### Python template -# Byte-compiled / optimized -__pycache__/ -*.pycod -*$py.class - - -# Distribution / packaging -.env/ -env/ -.venv/ -venv/ -build/ -dist/ -.eggs/ -lib/ -lib64/ -*.egg-info/ - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt -pip-wheel-metadata - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -.pytest_cache -nosetests.xml -coverage.xml -*,cover - -# Sphinx documentation -docs/_build/
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/CHANGELOG.rst -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/CHANGELOG.rst
Changed
@@ -1,3 +1,99 @@ +v6.4.2 +====== + +* fix #671 : NoReturn is not avaliable in painfully dead python 3.6 + + +v6.4.1 +======= + + +* fix regression #669: restore get_version signature +* fix #668: harden the selftest for distribution extras + +6.4.0 +====== + +* compatibility adjustments for setuptools >58 +* only put minimal setuptools version into toml extra to warn people with old strict pins +* coorectly handle hg-git self-use +* better mercurial detection +* modernize packaging setup +* python 3.10 support +* better handling of setuptools install command deprecation +* consider ``pyproject.tomls`` when running as command +* use list in git describe command to avoid shell expansions while supporting both windows and posix +* add ``--strip-dev`` flag to ``python -m setuptools_scm`` to print the next guessed version cleanly +* ensure no-guess-dev will fail on bad tags instead of generating invalid versions +* ensure we use utc everywhere to avoid confusion + +6.3.2 +===== + +* fix #629: correctly convert Version data in tags_to_version parser to avoid errors + +6.3.1 +===== + +* fix #625: restore tomli in install_requires after the regression changes in took it out + and some users never added it even tho they have pyproject.toml files + +6.3.0 +======= + +.. warning:: + + This release explicitly warns on unsupported setuptools. + This unfortunately has to happen as the legacy ``setup_requires`` mechanism + incorrectly configures the setuptools working-set when a more recent setuptools + version than available is required. + + As all releases of setuptools are affected as the historic mechanism + for ensuring a working setuptools setup was shipping a ``ez_setup`` file + next to ``setup.py``, which would install the required version of setuptools. + + This mechanism has long since been deprecated and removed + as most people haven't been using it + + +* fix #612: depend on packaging to ensure version parsing parts +* fix #611: correct the typo that hid away the toml extra and add it in ``setup.py`` as well +* fix #615: restore support for the git_archive plugin which doesn't pass over the config +* restore the ability to run on old setuptools while to avoid breaking pipelines + +v6.2.0 +======= + +* fix #608: resolve tomli dependency issue by making it a hard dependency + as all intended/supported install options use pip/wheel this is only a feature release +* ensure python 3.10 works + +v6.1.1 +======= + +* fix #605: completely disallow bdist_egg - modern enough setuptools>=45 uses pip +* fix #606: re-integrate and harden toml parsing +* fix #597: harden and expand support for figuring the current distribution name from + `pyproject.toml` (`project.name` or `tool.setuptools_scm.dist_name`) section or `setup.cfg` (`metadata.name`) + +v6.1.0 +====== + +* fix #587: don't fail file finders when distribution is not given +* fix #524: new parameters ``normalize`` and ``version_cls`` to customize the version normalization class. +* fix #585: switch from toml to tomli for toml 1.0 support +* fix #591: allow to opt in for searching parent directories in the api +* fix #589: handle yaml encoding using the expected defaults +* fix #575: recommend storing the version_module inside of ``mypkg/_version.py`` +* fix #571: accept branches starting with ``v`` as release branches +* fix #557: Use ``packaging.version`` for ``version_tuple`` +* fix #544: enhance errors on unsupported python/setuptools versions + +v6.0.1 +====== + +* fix #537: drop node_date on old git to avoid errors on missing %cI + v6.0.0 ====== @@ -205,8 +301,8 @@ * require parse results to be ScmVersion or None (breaking change) * fix #266 by requiring the prefix word to be a word again (breaking change as the bug allowed arbitrary prefixes while the original feature only allowed words") -* introduce a internal config object to allow the configruation fo tag parsing and prefixes - (thanks to @punkadiddle for introducing it and passing it trough) +* introduce an internal config object to allow the configuration for tag parsing and prefixes + (thanks to @punkadiddle for introducing it and passing it through) v2.1.0 ====== @@ -223,7 +319,7 @@ * fix #237 - correct imports in code examples * improve mercurial commit detection (thanks Aaron) * breaking change: remove support for setuptools before parsed versions -* reintroduce manifest as the travis deploy cant use the file finder +* reintroduce manifest as the travis deploy can't use the file finder * reconfigure flake8 for future compatibility with black * introduce support for branch name in version metadata and support a opt-in simplified semver version scheme @@ -253,8 +349,8 @@ ======= * drop support for eol python versions -* #214 - fix missuse in surogate-escape api -* add the node-and-timestamp local version sheme +* #214 - fix misuse in surogate-escape api +* add the node-and-timestamp local version scheme * respect git export ignores * avoid shlex.split on windows * fix #218 - better handling of mercurial edge-cases with tag commits @@ -283,7 +379,7 @@ v1.15.4 ======= -* fix issue #164: iterate all found entry points to avoid erros when pip remakes egg-info +* fix issue #164: iterate all found entry points to avoid errors when pip remakes egg-info * enhance self-use to enable pip install from github again v1.15.3 @@ -314,7 +410,7 @@ when considering distance in commits (thanks Petre Mierlutiu) * fix issue #114: stop trying to be smart for the sdist - and ensure its always correctly usign itself + and ensure its always correctly using itself * update trove classifiers * fix issue #84: document using the installed package metadata for sphinx * fix issue #81: fail more gracious when git/hg are missing @@ -329,7 +425,7 @@ don't consider untracked file (this was a regression due to #86 in v1.13.1) * consider the distance 0 when the git node is unknown - (happens when you haven't commited anything) + (happens when you haven't committed anything) v1.14.0 ======= @@ -347,7 +443,7 @@ * fix regression caused by the fix of #101 * assert types for version dumping - * strictly pass all versions trough parsed version metadata + * strictly pass all versions through parsed version metadata v1.12.0 ======= @@ -385,7 +481,7 @@ * add support for overriding the version number via the environment variable SETUPTOOLS_SCM_PRETEND_VERSION -* fix isssue #63 by adding the --match parameter to the git describe call +* fix issue #63 by adding the --match parameter to the git describe call and prepare the possibility of passing more options to scm backends * fix issue #70 and #71 by introducing the parse keyword @@ -429,7 +525,7 @@ before we would let the setup stay at version 0.0, now there is a ValueError -* propperly raise errors on write_to missuse (thanks Te-jé Rodgers) +* properly raise errors on write_to misuse (thanks Te-jé Rodgers) v1.5.5 ====== @@ -466,7 +562,7 @@ v1.4.0 ====== -* propper handling for sdist +* proper handling for sdist * fix file-finder failure from windows * resuffle docs
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/MANIFEST.in -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/MANIFEST.in
Changed
@@ -6,4 +6,6 @@ include *.rst include LICENSE include *.toml +include mypy.ini +include testing/Dockerfile.busted-buster recursive-include testing *.bash
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/PKG-INFO -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/PKG-INFO
Changed
@@ -1,633 +1,11 @@ Metadata-Version: 2.1 Name: setuptools_scm -Version: 6.0.0 +Version: 6.4.2 Summary: the blessed package to manage your versions by scm tags Home-page: https://github.com/pypa/setuptools_scm/ Author: Ronny Pfannschmidt Author-email: opensource@ronnypfannschmidt.de License: MIT -Description: setuptools_scm - ============== - - ``setuptools_scm`` handles managing your Python package versions - in SCM metadata instead of declaring them as the version argument - or in a SCM managed file. - - Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM - (i.e. it automatically adds all of the SCM-managed files to the sdist). - Unwanted files must be excluded by discarding them via ``MANIFEST.in``. - - ``setuptools_scm`` support the following scm out of the box: - - * git - * mercurial - - - - .. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg - :target: https://github.com/pypa/setuptools_scm/actions - - .. image:: https://tidelift.com/badges/package/pypi/setuptools-scm - :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme - - - ``pyproject.toml`` usage - ------------------------ - - The preferred way to configure ``setuptools_scm`` is to author - settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. - - This feature requires Setuptools 42 or later, released in Nov, 2019. - If your project needs to support build from sdist on older versions - of Setuptools, you will need to also implement the ``setup.py usage`` - for those legacy environments. - - First, ensure that ``setuptools_scm`` is present during the project's - built step by specifying it as one of the build requirements. - - .. code:: toml - - # pyproject.toml - build-system - requires = "setuptools>=42", "wheel", "setuptools_scmtoml>=3.4" - - Note that the ``toml`` extra must be supplied. - - That will be sufficient to require ``setuptools_scm`` for projects - that support PEP 518 (`pip <https://pypi.org/project/pip>`_ and - `pep517 <https://pypi.org/project/pep517/>`_). Many tools, - especially those that invoke ``setup.py`` for any reason, may - continue to rely on ``setup_requires``. For maximum compatibility - with those uses, consider also including a ``setup_requires`` directive - (described below in ``setup.py usage`` and ``setup.cfg``). - - To enable version inference, add this section to your pyproject.toml: - - .. code:: toml - - # pyproject.toml - tool.setuptools_scm - - Including this section is comparable to supplying - ``use_scm_version=True`` in ``setup.py``. Additionally, - include arbitrary keyword arguments in that section - to be supplied to ``get_version()``. For example: - - .. code:: toml - - # pyproject.toml - - tool.setuptools_scm - write_to = "pkg/version.py" - - - ``setup.py`` usage - ------------------ - - The following settings are considered legacy behavior and - superseded by the ``pyproject.toml`` usage, but for maximal - compatibility, projects may also supply the configuration in - this older form. - - To use ``setuptools_scm`` just modify your project's ``setup.py`` file - like this: - - * Add ``setuptools_scm`` to the ``setup_requires`` parameter. - * Add the ``use_scm_version`` parameter and set it to ``True``. - - For example: - - .. code:: python - - from setuptools import setup - setup( - ..., - use_scm_version=True, - setup_requires='setuptools_scm', - ..., - ) - - Arguments to ``get_version()`` (see below) may be passed as a dictionary to - ``use_scm_version``. For example: - - .. code:: python - - from setuptools import setup - setup( - ..., - use_scm_version = { - "root": "..", - "relative_to": __file__, - "local_scheme": "node-and-timestamp" - }, - setup_requires='setuptools_scm', - ..., - ) - - You can confirm the version number locally via ``setup.py``: - - .. code-block:: shell - - $ python setup.py --version - - .. note:: - - If you see unusual version numbers for packages but ``python setup.py - --version`` reports the expected version number, ensure ``egg_info`` is - not defined in ``setup.cfg``. - - - ``setup.cfg`` usage - ------------------- - - If using `setuptools 30.3.0 - <https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_ - or greater, you can store ``setup_requires`` configuration in ``setup.cfg``. - However, ``use_scm_version`` must still be placed in ``setup.py``. For example: - - .. code:: python - - # setup.py - from setuptools import setup - setup( - use_scm_version=True, - ) - - .. code:: ini - - # setup.cfg - metadata - ... - - options - setup_requires = - setuptools_scm - ... - - .. important:: - - Ensure neither the ``metadata`` ``version`` option nor the ``egg_info`` - section are defined, as these will interfere with ``setuptools_scm``. - - You may also need to define a ``pyproject.toml`` file (`PEP-0518 - <https://www.python.org/dev/peps/pep-0518>`_) to ensure you have the required - version of ``setuptools``: - - .. code:: ini - - # pyproject.toml - build-system - requires = "setuptools>=30.3.0", "wheel", "setuptools_scm" - - For more information, refer to the `setuptools issue #1002 - <https://github.com/pypa/setuptools/issues/1002>`_. - - - Programmatic usage - ------------------ - - In order to use ``setuptools_scm`` from code that is one directory deeper - than the project's root, you can use: - - .. code:: python - - from setuptools_scm import get_version - version = get_version(root='..', relative_to=__file__) - - See `setup.py Usage`_ above for how to use this within ``setup.py``. - - - Retrieving package version at runtime - ------------------------------------- - - If you have opted not to hardcode the version number inside the package, - you can retrieve it at runtime from PEP-0566_ metadata using - ``importlib.metadata`` from the standard library (added in Python 3.8) - or the `importlib_metadata`_ backport: - - .. code:: python - - from importlib.metadata import version, PackageNotFoundError - - try: - __version__ = version("package-name") - except PackageNotFoundError: - # package is not installed - pass - - Alternatively, you can use ``pkg_resources`` which is included in - ``setuptools``: - - .. code:: python - - from pkg_resources import get_distribution, DistributionNotFound - - try: - __version__ = get_distribution("package-name").version - except DistributionNotFound: - # package is not installed - pass - - However, this does place a runtime dependency on ``setuptools`` and can add up to - a few 100ms overhead for the package import time. - - .. _PEP-0566: https://www.python.org/dev/peps/pep-0566/ - .. _importlib_metadata: https://pypi.org/project/importlib-metadata/ - - - Usage from Sphinx - ----------------- - - It is discouraged to use ``setuptools_scm`` from Sphinx itself, - instead use ``pkg_resources`` after editable/real installation: - - .. code:: python - - # contents of docs/conf.py - from pkg_resources import get_distribution - release = get_distribution('myproject').version - # for example take major/minor - version = '.'.join(release.split('.'):2) - - The underlying reason is, that services like *Read the Docs* sometimes change - the working directory for good reasons and using the installed metadata - prevents using needless volatile data there. - - Notable Plugins - --------------- - - `setuptools_scm_git_archive <https://pypi.python.org/pypi/setuptools_scm_git_archive>`_ - Provides partial support for obtaining versions from git archives that - belong to tagged versions. The only reason for not including it in - ``setuptools_scm`` itself is Git/GitHub not supporting sufficient metadata - for untagged/followup commits, which is preventing a consistent UX. - - - Default versioning scheme - ------------------------- - - In the standard configuration ``setuptools_scm`` takes a look at three things: - - 1. latest tag (with a version number) - 2. the distance to this tag (e.g. number of revisions since latest tag) - 3. workdir state (e.g. uncommitted changes since latest tag) - - and uses roughly the following logic to render the version: - - no distance and clean: - ``{tag}`` - distance and clean: - ``{next_version}.dev{distance}+{scm letter}{revision hash}`` - no distance and not clean: - ``{tag}+dYYYYMMDD`` - distance and not clean: - ``{next_version}.dev{distance}+{scm letter}{revision hash}.dYYYYMMDD`` - - The next version is calculated by adding ``1`` to the last numeric component of - the tag. - - - For Git projects, the version relies on `git describe <https://git-scm.com/docs/git-describe>`_, - so you will see an additional ``g`` prepended to the ``{revision hash}``. - - Semantic Versioning (SemVer) - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Due to the default behavior it's necessary to always include a - patch version (the ``3`` in ``1.2.3``), or else the automatic guessing - will increment the wrong part of the SemVer (e.g. tag ``2.0`` results in - ``2.1.devX`` instead of ``2.0.1.devX``). So please make sure to tag - accordingly. - - .. note:: - - Future versions of ``setuptools_scm`` will switch to `SemVer - <http://semver.org/>`_ by default hiding the the old behavior as an - configurable option. - - - Builtin mechanisms for obtaining version numbers - ------------------------------------------------ - - 1. the SCM itself (git/hg) - 2. ``.hg_archival`` files (mercurial archives) - 3. ``PKG-INFO`` - - .. note:: - - Git archives are not supported due to Git shortcomings - - - File finders hook makes most of MANIFEST.in unnecessary - ------------------------------------------------------- - - ``setuptools_scm`` implements a `file_finders - <https://setuptools.readthedocs.io/en/latest/setuptools.html#adding-support-for-revision-control-systems>`_ - entry point which returns all files tracked by your SCM. This eliminates - the need for a manually constructed ``MANIFEST.in`` in most cases where this - would be required when not using ``setuptools_scm``, namely: - - * To ensure all relevant files are packaged when running the ``sdist`` command. - - * When using `include_package_data <https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files>`_ - to include package data as part of the ``build`` or ``bdist_wheel``. - - ``MANIFEST.in`` may still be used: anything defined there overrides the hook. - This is mostly useful to exclude files tracked in your SCM from packages, - although in principle it can be used to explicitly include non-tracked files - too. - - - Configuration parameters - ------------------------ - - In order to configure the way ``use_scm_version`` works you can provide - a mapping with options instead of a boolean value. - - The currently supported configuration keys are: - - :root: - Relative path to cwd, used for finding the SCM root; defaults to ``.`` - - :version_scheme: - Configures how the local version number is constructed; either an - entrypoint name or a callable. - - :local_scheme: - Configures how the local component of the version is constructed; either an - entrypoint name or a callable. - - :write_to: - A path to a file that gets replaced with a file containing the current - version. It is ideal for creating a ``version.py`` file within the - package, typically used to avoid using `pkg_resources.get_distribution` - (which adds some overhead). - - .. warning:: - - Only files with :code:`.py` and :code:`.txt` extensions have builtin - templates, for other file types it is necessary to provide - :code:`write_to_template`. - - :write_to_template: - A newstyle format string that is given the current version as - the ``version`` keyword argument for formatting. - - :relative_to: - A file from which the root can be resolved. - Typically called by a script or module that is not in the root of the - repository to point ``setuptools_scm`` at the root of the repository by - supplying ``__file__``. - - :tag_regex: - A Python regex string to extract the version part from any SCM tag. - The regex needs to contain either a single match group, or a group - named ``version``, that captures the actual version information. - - Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX`` - (see `config.py <src/setuptools_scm/config.py>`_). - - :parentdir_prefix_version: - If the normal methods for detecting the version (SCM version, - sdist metadata) fail, and the parent directory name starts with - ``parentdir_prefix_version``, then this prefix is stripped and the rest of - the parent directory name is matched with ``tag_regex`` to get a version - string. If this parameter is unset (the default), then this fallback is - not used. - - This is intended to cover GitHub's "release tarballs", which extract into - directories named ``projectname-tag/`` (in which case - ``parentdir_prefix_version`` can be set e.g. to ``projectname-``). - - :fallback_version: - A version string that will be used if no other method for detecting the - version worked (e.g., when using a tarball with no metadata). If this is - unset (the default), setuptools_scm will error if it fails to detect the - version. - - :parse: - A function that will be used instead of the discovered SCM for parsing the - version. - Use with caution, this is a function for advanced use, and you should be - familiar with the ``setuptools_scm`` internals to use it. - - :git_describe_command: - This command will be used instead the default ``git describe`` command. - Use with caution, this is a function for advanced use, and you should be - familiar with the ``setuptools_scm`` internals to use it. - - Defaults to the value set by ``setuptools_scm.git.DEFAULT_DESCRIBE`` - (see `git.py <src/setuptools_scm/git.py>`_). - - To use ``setuptools_scm`` in other Python code you can use the ``get_version`` - function: - - .. code:: python - - from setuptools_scm import get_version - my_version = get_version() - - It optionally accepts the keys of the ``use_scm_version`` parameter as - keyword arguments. - - Example configuration in ``setup.py`` format: - - .. code:: python - - from setuptools import setup - - setup( - use_scm_version={ - 'write_to': 'version.py', - 'write_to_template': '__version__ = "{version}"', - 'tag_regex': r'^(?P<prefix>v)?(?P<version>^\++)(?P<suffix>.*)?$', - } - ) - - Environment variables - --------------------- - - :SETUPTOOLS_SCM_PRETEND_VERSION: - when defined and not empty, - its used as the primary source for the version number - in which case it will be a unparsed string - - - :SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}: - when defined and not empty, - its used as the primary source for the version number - in which case it will be a unparsed string - - it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION`` - - - :SETUPTOOLS_SCM_DEBUG: - when defined and not empty, - a lot of debug information will be printed as part of ``setuptools_scm`` - operating - - :SOURCE_DATE_EPOCH: - when defined, used as the timestamp from which the - ``node-and-date`` and ``node-and-timestamp`` local parts are - derived, otherwise the current time is used - (https://reproducible-builds.org/docs/source-date-epoch/) - - - :SETUPTOOLS_SCM_IGNORE_VCS_ROOTS: - when defined, a ``os.pathsep`` separated list - of directory names to ignore for root finding - - Extending setuptools_scm - ------------------------ - - ``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to - extend its default capabilities. - - Adding a new SCM - ~~~~~~~~~~~~~~~~ - - ``setuptools_scm`` provides two entrypoints for adding new SCMs: - - ``setuptools_scm.parse_scm`` - A function used to parse the metadata of the current workdir - using the name of the control directory/file of your SCM as the - entrypoint's name. E.g. for the built-in entrypoint for git the - entrypoint is named ``.git`` and references ``setuptools_scm.git:parse`` - - The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance - created by the function ``setuptools_scm.version:meta``. - - ``setuptools_scm.files_command`` - Either a string containing a shell command that prints all SCM managed - files in its current working directory or a callable, that given a - pathname will return that list. - - Also use then name of your SCM control directory as name of the entrypoint. - - Version number construction - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - ``setuptools_scm.version_scheme`` - Configures how the version number is constructed given a - ``setuptools_scm.version.ScmVersion`` instance and should return a string - representing the version. - - Available implementations: - - :guess-next-dev: Automatically guesses the next development version (default). - Guesses the upcoming release by incrementing the pre-release segment if present, - otherwise by incrementing the micro segment. Then appends :code:`.devN`. - In case the tag ends with ``.dev0`` the version is not bumped - and custom ``.devN`` versions will trigger a error. - :post-release: generates post release versions (adds :code:`.postN`) - :python-simplified-semver: Basic semantic versioning. Guesses the upcoming release - by incrementing the minor segment and setting the micro segment to zero if the - current branch contains the string ``'feature'``, otherwise by incrementing the - micro version. Then appends :code:`.devN`. Not compatible with pre-releases. - :release-branch-semver: Semantic versioning for projects with release branches. The - same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on - a release branch: a branch whose name (ignoring namespace) parses as a version - that matches the most recent tag up to the minor segment. Otherwise if on a - non-release branch, increments the minor segment and sets the micro segment to - zero, then appends :code:`.devN`. - :no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN` - - ``setuptools_scm.local_scheme`` - Configures how the local part of a version is rendered given a - ``setuptools_scm.version.ScmVersion`` instance and should return a string - representing the local version. - Dates and times are in Coordinated Universal Time (UTC), because as part - of the version, they should be location independent. - - Available implementations: - - :node-and-date: adds the node on dev versions and the date on dirty - workdir (default) - :node-and-timestamp: like ``node-and-date`` but with a timestamp of - the form ``{:%Y%m%d%H%M%S}`` instead - :dirty-tag: adds ``+dirty`` if the current workdir has changes - :no-local-version: omits local version, useful e.g. because pypi does - not support it - - - Importing in ``setup.py`` - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - To support usage in ``setup.py`` passing a callable into ``use_scm_version`` - is supported. - - Within that callable, ``setuptools_scm`` is available for import. - The callable must return the configuration. - - - .. code:: python - - # content of setup.py - import setuptools - - def myversion(): - from setuptools_scm.version import get_local_dirty_tag - def clean_scheme(version): - return get_local_dirty_tag(version) if version.dirty else '+clean' - - return {'local_scheme': clean_scheme} - - setup( - ..., - use_scm_version=myversion, - ... - ) - - - Note on testing non-installed versions - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - While the general advice is to test against a installed version, - some environments require a test prior to install, - - .. code:: - - $ python setup.py egg_info - $ PYTHONPATH=$PWD:$PWD/src pytest - - - Interaction with Enterprise Distributions - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Some enterprise distributions like RHEL7 and others - ship rather old setuptools versions due to various release management details. - - On such distributions one might observe errors like: - - :code:``setuptools_scm.version.SetuptoolsOutdatedWarning: your setuptools is too old (<12)`` - - In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. - As those old setuptools versions lack sensible types for versions, - modern setuptools_scm is unable to support them sensibly. - - In case the project you need to build can not be patched to either use old setuptools_scm, - its still possible to install a more recent version of setuptools in order to handle the build - and/or install the package by using wheels or eggs. - - - - - Code of Conduct - --------------- - - Everyone interacting in the ``setuptools_scm`` project's codebases, issue - trackers, chat rooms, and mailing lists is expected to follow the - `PSF Code of Conduct`_. - - .. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md - - Security Contact - ================ - - To report a security vulnerability, please use the - `Tidelift security contact <https://tidelift.com/security>`_. - Tidelift will coordinate the fix and disclosure. - Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers @@ -639,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Version Control Classifier: Topic :: System :: Software Distribution @@ -646,3 +25,645 @@ Requires-Python: >=3.6 Description-Content-Type: text/x-rst Provides-Extra: toml +Provides-Extra: test +License-File: LICENSE + +setuptools_scm +============== + +``setuptools_scm`` extract Python package versions from ``git`` or +``hg`` metadata instead of declaring them as the version argument +or in a SCM managed file. + +Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM +(i.e. it automatically adds all of the SCM-managed files to the sdist). +Unwanted files must be excluded by discarding them via ``MANIFEST.in``. + +``setuptools_scm`` support the following scm out of the box: + +* git +* mercurial + + + +.. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg + :target: https://github.com/pypa/setuptools_scm/actions + +.. image:: https://tidelift.com/badges/package/pypi/setuptools-scm + :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme + + +``pyproject.toml`` usage +------------------------ + +The preferred way to configure ``setuptools_scm`` is to author +settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. + +This feature requires Setuptools 42 or later, released in Nov, 2019. +If your project needs to support build from sdist on older versions +of Setuptools, you will need to also implement the ``setup.py usage`` +for those legacy environments. + +First, ensure that ``setuptools_scm`` is present during the project's +built step by specifying it as one of the build requirements. + +.. code:: toml + + # pyproject.toml + build-system + requires = "setuptools>=45", "wheel", "setuptools_scm>=6.2" + + +That will be sufficient to require ``setuptools_scm`` for projects +that support PEP 518 (`pip <https://pypi.org/project/pip>`_ and +`pep517 <https://pypi.org/project/pep517/>`_). Many tools, +especially those that invoke ``setup.py`` for any reason, may +continue to rely on ``setup_requires``. For maximum compatibility +with those uses, consider also including a ``setup_requires`` directive +(described below in ``setup.py usage`` and ``setup.cfg``). + +To enable version inference, add this section to your pyproject.toml: + +.. code:: toml + + # pyproject.toml + tool.setuptools_scm + +Including this section is comparable to supplying +``use_scm_version=True`` in ``setup.py``. Additionally, +include arbitrary keyword arguments in that section +to be supplied to ``get_version()``. For example: + +.. code:: toml + + # pyproject.toml + + tool.setuptools_scm + write_to = "pkg/_version.py" + + +``setup.py`` usage (deprecated) +------------------------------- + +.. warning:: + + ``setup_requires`` has been deprecated in favor of ``pyproject.toml`` + +The following settings are considered legacy behavior and +superseded by the ``pyproject.toml`` usage, but for maximal +compatibility, projects may also supply the configuration in +this older form. + +To use ``setuptools_scm`` just modify your project's ``setup.py`` file +like this: + +* Add ``setuptools_scm`` to the ``setup_requires`` parameter. +* Add the ``use_scm_version`` parameter and set it to ``True``. + +For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version=True, + setup_requires='setuptools_scm', + ..., + ) + +Arguments to ``get_version()`` (see below) may be passed as a dictionary to +``use_scm_version``. For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version = { + "root": "..", + "relative_to": __file__, + "local_scheme": "node-and-timestamp" + }, + setup_requires='setuptools_scm', + ..., + ) + +You can confirm the version number locally via ``setup.py``: + +.. code-block:: shell + + $ python setup.py --version + +.. note:: + + If you see unusual version numbers for packages but ``python setup.py + --version`` reports the expected version number, ensure ``egg_info`` is + not defined in ``setup.cfg``. + + +``setup.cfg`` usage (deprecated) +------------------------------------ + +as ``setup_requires`` is deprecated in favour of ``pyproject.toml`` +usage in ``setup.cfg`` is considered deprecated, +please use ``pyproject.toml`` whenever possible. + +Programmatic usage +------------------ + +In order to use ``setuptools_scm`` from code that is one directory deeper +than the project's root, you can use: + +.. code:: python + + from setuptools_scm import get_version + version = get_version(root='..', relative_to=__file__) + +See `setup.py Usage (deprecated)`_ above for how to use this within ``setup.py``. + + +Retrieving package version at runtime +------------------------------------- + +If you have opted not to hardcode the version number inside the package, +you can retrieve it at runtime from PEP-0566_ metadata using +``importlib.metadata`` from the standard library (added in Python 3.8) +or the `importlib_metadata`_ backport: + +.. code:: python + + from importlib.metadata import version, PackageNotFoundError + + try: + __version__ = version("package-name") + except PackageNotFoundError: + # package is not installed + pass + +Alternatively, you can use ``pkg_resources`` which is included in +``setuptools`` (but has a significant runtime cost): + +.. code:: python + + from pkg_resources import get_distribution, DistributionNotFound + + try: + __version__ = get_distribution("package-name").version + except DistributionNotFound: + # package is not installed + pass + +However, this does place a runtime dependency on ``setuptools`` and can add up to +a few 100ms overhead for the package import time. + +.. _PEP-0566: https://www.python.org/dev/peps/pep-0566/ +.. _importlib_metadata: https://pypi.org/project/importlib-metadata/ + + +Usage from Sphinx +----------------- + +It is discouraged to use ``setuptools_scm`` from Sphinx itself, +instead use ``importlib.metadata`` after editable/real installation: + +.. code:: python + + # contents of docs/conf.py + from importlib.metadata import version + release = version('myproject') + # for example take major/minor + version = '.'.join(release.split('.'):2) + +The underlying reason is, that services like *Read the Docs* sometimes change +the working directory for good reasons and using the installed metadata +prevents using needless volatile data there. + +Usage from Docker +----------------- + +By default, docker will not copy the ``.git`` folder into your container. +Therefore, builds with version inference might fail. +Consequently, you can use the following snipped to infer the version from +the host os without copying the entire ``.git`` folder to your Dockerfile. + +.. code:: dockerfile + + RUN --mount=source=.git,target=.git,type=bind \ + pip install --no-cache-dir -e . + +However, this build step introduces a dependency to the state of your local +.git folder the build cache and triggers the long-running pip install process on every build. +To optimize build caching, one can use an environment variable to pretend a pseudo +version that is used to cache the results of the pip install process: + +.. code:: dockerfile + + FROM python + COPY pyproject.toml + ARG PSEUDO_VERSION=1 + RUN SETUPTOOLS_SCM_PRETEND_VERSION=${PSEUDO_VERSION} pip install -e .test + RUN --mount=source=.git,target=.git,type=bind pip install -e . + +Note that running this Dockerfile requires docker with BuildKit enabled +`docs <https://github.com/moby/buildkit/blob/v0.8.3/frontend/dockerfile/docs/syntax.md>`_. + +To avoid BuildKit and mounting of the .git folder altogether, one can also pass the desired +version as a build argument. Note that ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}`` +is preferred over ``SETUPTOOLS_SCM_PRETEND_VERSION``. + +Notable Plugins +--------------- + +`setuptools_scm_git_archive <https://pypi.python.org/pypi/setuptools_scm_git_archive>`_ + Provides partial support for obtaining versions from git archives that + belong to tagged versions. The only reason for not including it in + ``setuptools_scm`` itself is Git/GitHub not supporting sufficient metadata + for untagged/followup commits, which is preventing a consistent UX. + + +Default versioning scheme +------------------------- + +In the standard configuration ``setuptools_scm`` takes a look at three things: + +1. latest tag (with a version number) +2. the distance to this tag (e.g. number of revisions since latest tag) +3. workdir state (e.g. uncommitted changes since latest tag) + +and uses roughly the following logic to render the version: + +no distance and clean: + ``{tag}`` +distance and clean: + ``{next_version}.dev{distance}+{scm letter}{revision hash}`` +no distance and not clean: + ``{tag}+dYYYYMMDD`` +distance and not clean: + ``{next_version}.dev{distance}+{scm letter}{revision hash}.dYYYYMMDD`` + +The next version is calculated by adding ``1`` to the last numeric component of +the tag. + + +For Git projects, the version relies on `git describe <https://git-scm.com/docs/git-describe>`_, +so you will see an additional ``g`` prepended to the ``{revision hash}``. + +Semantic Versioning (SemVer) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to the default behavior it's necessary to always include a +patch version (the ``3`` in ``1.2.3``), or else the automatic guessing +will increment the wrong part of the SemVer (e.g. tag ``2.0`` results in +``2.1.devX`` instead of ``2.0.1.devX``). So please make sure to tag +accordingly. + +.. note:: + + Future versions of ``setuptools_scm`` will switch to `SemVer + <http://semver.org/>`_ by default hiding the the old behavior as an + configurable option. + + +Builtin mechanisms for obtaining version numbers +------------------------------------------------ + +1. the SCM itself (git/hg) +2. ``.hg_archival`` files (mercurial archives) +3. ``PKG-INFO`` + +.. note:: + + Git archives are not supported due to Git shortcomings + + +File finders hook makes most of MANIFEST.in unnecessary +------------------------------------------------------- + +``setuptools_scm`` implements a `file_finders +<https://setuptools.readthedocs.io/en/latest/setuptools.html#adding-support-for-revision-control-systems>`_ +entry point which returns all files tracked by your SCM. This eliminates +the need for a manually constructed ``MANIFEST.in`` in most cases where this +would be required when not using ``setuptools_scm``, namely: + +* To ensure all relevant files are packaged when running the ``sdist`` command. + +* When using `include_package_data <https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files>`_ + to include package data as part of the ``build`` or ``bdist_wheel``. + +``MANIFEST.in`` may still be used: anything defined there overrides the hook. +This is mostly useful to exclude files tracked in your SCM from packages, +although in principle it can be used to explicitly include non-tracked files +too. + + +Configuration parameters +------------------------ + +In order to configure the way ``use_scm_version`` works you can provide +a mapping with options instead of a boolean value. + +The currently supported configuration keys are: + +:root: + Relative path to cwd, used for finding the SCM root; defaults to ``.`` + +:version_scheme: + Configures how the local version number is constructed; either an + entrypoint name or a callable. + +:local_scheme: + Configures how the local component of the version is constructed; either an + entrypoint name or a callable. + +:write_to: + A path to a file that gets replaced with a file containing the current + version. It is ideal for creating a ``_version.py`` file within the + package, typically used to avoid using `pkg_resources.get_distribution` + (which adds some overhead). + + .. warning:: + + Only files with :code:`.py` and :code:`.txt` extensions have builtin + templates, for other file types it is necessary to provide + :code:`write_to_template`. + +:write_to_template: + A newstyle format string that is given the current version as + the ``version`` keyword argument for formatting. + +:relative_to: + A file from which the root can be resolved. + Typically called by a script or module that is not in the root of the + repository to point ``setuptools_scm`` at the root of the repository by + supplying ``__file__``. + +:tag_regex: + A Python regex string to extract the version part from any SCM tag. + The regex needs to contain either a single match group, or a group + named ``version``, that captures the actual version information. + + Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX`` + (see `config.py <src/setuptools_scm/config.py>`_). + +:parentdir_prefix_version: + If the normal methods for detecting the version (SCM version, + sdist metadata) fail, and the parent directory name starts with + ``parentdir_prefix_version``, then this prefix is stripped and the rest of + the parent directory name is matched with ``tag_regex`` to get a version + string. If this parameter is unset (the default), then this fallback is + not used. + + This is intended to cover GitHub's "release tarballs", which extract into + directories named ``projectname-tag/`` (in which case + ``parentdir_prefix_version`` can be set e.g. to ``projectname-``). + +:fallback_version: + A version string that will be used if no other method for detecting the + version worked (e.g., when using a tarball with no metadata). If this is + unset (the default), setuptools_scm will error if it fails to detect the + version. + +:parse: + A function that will be used instead of the discovered SCM for parsing the + version. + Use with caution, this is a function for advanced use, and you should be + familiar with the ``setuptools_scm`` internals to use it. + +:git_describe_command: + This command will be used instead the default ``git describe`` command. + Use with caution, this is a function for advanced use, and you should be + familiar with the ``setuptools_scm`` internals to use it. + + Defaults to the value set by ``setuptools_scm.git.DEFAULT_DESCRIBE`` + (see `git.py <src/setuptools_scm/git.py>`_). + +:normalize: + A boolean flag indicating if the version string should be normalized. + Defaults to ``True``. Setting this to ``False`` is equivalent to setting + ``version_cls`` to ``setuptools_scm.version.NonNormalizedVersion`` + +:version_cls: + An optional class used to parse, verify and possibly normalize the version + string. Its constructor should receive a single string argument, and its + ``str`` should return the normalized version string to use. + This option can also receive a class qualified name as a string. + + This defaults to ``packaging.version.Version`` if available. If + ``packaging`` is not installed, ``pkg_resources.packaging.version.Version`` + is used. Note that it is known to modify git release candidate schemes. + + The ``setuptools_scm.NonNormalizedVersion`` convenience class is + provided to disable the normalization step done by + ``packaging.version.Version``. If this is used while ``setuptools_scm`` + is integrated in a setuptools packaging process, the non-normalized + version number will appear in all files (see ``write_to``) BUT note + that setuptools will still normalize it to create the final distribution, + so as to stay compliant with the python packaging standards. + + +To use ``setuptools_scm`` in other Python code you can use the ``get_version`` +function: + +.. code:: python + + from setuptools_scm import get_version + my_version = get_version() + +It optionally accepts the keys of the ``use_scm_version`` parameter as +keyword arguments. + +Example configuration in ``setup.py`` format: + +.. code:: python + + from setuptools import setup + + setup( + use_scm_version={ + 'write_to': '_version.py', + 'write_to_template': '__version__ = "{version}"', + 'tag_regex': r'^(?P<prefix>v)?(?P<version>^\++)(?P<suffix>.*)?$', + } + ) + +Environment variables +--------------------- + +:SETUPTOOLS_SCM_PRETEND_VERSION: + when defined and not empty, + its used as the primary source for the version number + in which case it will be a unparsed string + + +:SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}: + when defined and not empty, + its used as the primary source for the version number + in which case it will be a unparsed string + + it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION`` + + +:SETUPTOOLS_SCM_DEBUG: + when defined and not empty, + a lot of debug information will be printed as part of ``setuptools_scm`` + operating + +:SOURCE_DATE_EPOCH: + when defined, used as the timestamp from which the + ``node-and-date`` and ``node-and-timestamp`` local parts are + derived, otherwise the current time is used + (https://reproducible-builds.org/docs/source-date-epoch/) + + +:SETUPTOOLS_SCM_IGNORE_VCS_ROOTS: + when defined, a ``os.pathsep`` separated list + of directory names to ignore for root finding + +Extending setuptools_scm +------------------------ + +``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to +extend its default capabilities. + +Adding a new SCM +~~~~~~~~~~~~~~~~ + +``setuptools_scm`` provides two entrypoints for adding new SCMs: + +``setuptools_scm.parse_scm`` + A function used to parse the metadata of the current workdir + using the name of the control directory/file of your SCM as the + entrypoint's name. E.g. for the built-in entrypoint for git the + entrypoint is named ``.git`` and references ``setuptools_scm.git:parse`` + + The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance + created by the function ``setuptools_scm.version:meta``. + +``setuptools_scm.files_command`` + Either a string containing a shell command that prints all SCM managed + files in its current working directory or a callable, that given a + pathname will return that list. + + Also use then name of your SCM control directory as name of the entrypoint. + +Version number construction +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``setuptools_scm.version_scheme`` + Configures how the version number is constructed given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the version. + + Available implementations: + + :guess-next-dev: Automatically guesses the next development version (default). + Guesses the upcoming release by incrementing the pre-release segment if present, + otherwise by incrementing the micro segment. Then appends :code:`.devN`. + In case the tag ends with ``.dev0`` the version is not bumped + and custom ``.devN`` versions will trigger a error. + :post-release: generates post release versions (adds :code:`.postN`) + :python-simplified-semver: Basic semantic versioning. Guesses the upcoming release + by incrementing the minor segment and setting the micro segment to zero if the + current branch contains the string ``'feature'``, otherwise by incrementing the + micro version. Then appends :code:`.devN`. Not compatible with pre-releases. + :release-branch-semver: Semantic versioning for projects with release branches. The + same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on + a release branch: a branch whose name (ignoring namespace) parses as a version + that matches the most recent tag up to the minor segment. Otherwise if on a + non-release branch, increments the minor segment and sets the micro segment to + zero, then appends :code:`.devN`. + :no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN` + +``setuptools_scm.local_scheme`` + Configures how the local part of a version is rendered given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the local version. + Dates and times are in Coordinated Universal Time (UTC), because as part + of the version, they should be location independent. + + Available implementations: + + :node-and-date: adds the node on dev versions and the date on dirty + workdir (default) + :node-and-timestamp: like ``node-and-date`` but with a timestamp of + the form ``{:%Y%m%d%H%M%S}`` instead + :dirty-tag: adds ``+dirty`` if the current workdir has changes + :no-local-version: omits local version, useful e.g. because pypi does + not support it + + +Importing in ``setup.py`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To support usage in ``setup.py`` passing a callable into ``use_scm_version`` +is supported. + +Within that callable, ``setuptools_scm`` is available for import. +The callable must return the configuration. + + +.. code:: python + + # content of setup.py + import setuptools + + def myversion(): + from setuptools_scm.version import get_local_dirty_tag + def clean_scheme(version): + return get_local_dirty_tag(version) if version.dirty else '+clean' + + return {'local_scheme': clean_scheme} + + setup( + ..., + use_scm_version=myversion, + ... + ) + + +Note on testing non-installed versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While the general advice is to test against a installed version, +some environments require a test prior to install, + +.. code:: + + $ python setup.py egg_info + $ PYTHONPATH=$PWD:$PWD/src pytest + + +Interaction with Enterprise Distributions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some enterprise distributions like RHEL7 and others +ship rather old setuptools versions due to various release management details. + +In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. +As those old setuptools versions lack sensible types for versions, +modern setuptools_scm is unable to support them sensibly. + +In case the project you need to build can not be patched to either use old setuptools_scm, +its still possible to install a more recent version of setuptools in order to handle the build +and/or install the package by using wheels or eggs. + + + +Code of Conduct +--------------- + +Everyone interacting in the ``setuptools_scm`` project's codebases, issue +trackers, chat rooms, and mailing lists is expected to follow the +`PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact <https://tidelift.com/security>`_. +Tidelift will coordinate the fix and disclosure. + +
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/README.rst -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/README.rst
Changed
@@ -1,8 +1,8 @@ setuptools_scm ============== -``setuptools_scm`` handles managing your Python package versions -in SCM metadata instead of declaring them as the version argument +``setuptools_scm`` extract Python package versions from ``git`` or +``hg`` metadata instead of declaring them as the version argument or in a SCM managed file. Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM @@ -41,9 +41,8 @@ # pyproject.toml build-system - requires = "setuptools>=42", "wheel", "setuptools_scmtoml>=3.4" + requires = "setuptools>=45", "wheel", "setuptools_scm>=6.2" -Note that the ``toml`` extra must be supplied. That will be sufficient to require ``setuptools_scm`` for projects that support PEP 518 (`pip <https://pypi.org/project/pip>`_ and @@ -70,11 +69,15 @@ # pyproject.toml tool.setuptools_scm - write_to = "pkg/version.py" + write_to = "pkg/_version.py" -``setup.py`` usage ------------------- +``setup.py`` usage (deprecated) +------------------------------- + +.. warning:: + + ``setup_requires`` has been deprecated in favor of ``pyproject.toml`` The following settings are considered legacy behavior and superseded by the ``pyproject.toml`` usage, but for maximal @@ -129,51 +132,12 @@ not defined in ``setup.cfg``. -``setup.cfg`` usage -------------------- - -If using `setuptools 30.3.0 -<https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_ -or greater, you can store ``setup_requires`` configuration in ``setup.cfg``. -However, ``use_scm_version`` must still be placed in ``setup.py``. For example: - -.. code:: python - - # setup.py - from setuptools import setup - setup( - use_scm_version=True, - ) - -.. code:: ini - - # setup.cfg - metadata - ... - - options - setup_requires = - setuptools_scm - ... - -.. important:: - - Ensure neither the ``metadata`` ``version`` option nor the ``egg_info`` - section are defined, as these will interfere with ``setuptools_scm``. - -You may also need to define a ``pyproject.toml`` file (`PEP-0518 -<https://www.python.org/dev/peps/pep-0518>`_) to ensure you have the required -version of ``setuptools``: - -.. code:: ini - - # pyproject.toml - build-system - requires = "setuptools>=30.3.0", "wheel", "setuptools_scm" - -For more information, refer to the `setuptools issue #1002 -<https://github.com/pypa/setuptools/issues/1002>`_. +``setup.cfg`` usage (deprecated) +------------------------------------ +as ``setup_requires`` is deprecated in favour of ``pyproject.toml`` +usage in ``setup.cfg`` is considered deprecated, +please use ``pyproject.toml`` whenever possible. Programmatic usage ------------------ @@ -186,7 +150,7 @@ from setuptools_scm import get_version version = get_version(root='..', relative_to=__file__) -See `setup.py Usage`_ above for how to use this within ``setup.py``. +See `setup.py Usage (deprecated)`_ above for how to use this within ``setup.py``. Retrieving package version at runtime @@ -208,7 +172,7 @@ pass Alternatively, you can use ``pkg_resources`` which is included in -``setuptools``: +``setuptools`` (but has a significant runtime cost): .. code:: python @@ -231,13 +195,13 @@ ----------------- It is discouraged to use ``setuptools_scm`` from Sphinx itself, -instead use ``pkg_resources`` after editable/real installation: +instead use ``importlib.metadata`` after editable/real installation: .. code:: python # contents of docs/conf.py - from pkg_resources import get_distribution - release = get_distribution('myproject').version + from importlib.metadata import version + release = version('myproject') # for example take major/minor version = '.'.join(release.split('.'):2) @@ -245,6 +209,39 @@ the working directory for good reasons and using the installed metadata prevents using needless volatile data there. +Usage from Docker +----------------- + +By default, docker will not copy the ``.git`` folder into your container. +Therefore, builds with version inference might fail. +Consequently, you can use the following snipped to infer the version from +the host os without copying the entire ``.git`` folder to your Dockerfile. + +.. code:: dockerfile + + RUN --mount=source=.git,target=.git,type=bind \ + pip install --no-cache-dir -e . + +However, this build step introduces a dependency to the state of your local +.git folder the build cache and triggers the long-running pip install process on every build. +To optimize build caching, one can use an environment variable to pretend a pseudo +version that is used to cache the results of the pip install process: + +.. code:: dockerfile + + FROM python + COPY pyproject.toml + ARG PSEUDO_VERSION=1 + RUN SETUPTOOLS_SCM_PRETEND_VERSION=${PSEUDO_VERSION} pip install -e .test + RUN --mount=source=.git,target=.git,type=bind pip install -e . + +Note that running this Dockerfile requires docker with BuildKit enabled +`docs <https://github.com/moby/buildkit/blob/v0.8.3/frontend/dockerfile/docs/syntax.md>`_. + +To avoid BuildKit and mounting of the .git folder altogether, one can also pass the desired +version as a build argument. Note that ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}`` +is preferred over ``SETUPTOOLS_SCM_PRETEND_VERSION``. + Notable Plugins --------------- @@ -351,7 +348,7 @@ :write_to: A path to a file that gets replaced with a file containing the current - version. It is ideal for creating a ``version.py`` file within the + version. It is ideal for creating a ``_version.py`` file within the package, typically used to avoid using `pkg_resources.get_distribution` (which adds some overhead). @@ -411,6 +408,30 @@ Defaults to the value set by ``setuptools_scm.git.DEFAULT_DESCRIBE`` (see `git.py <src/setuptools_scm/git.py>`_). +:normalize: + A boolean flag indicating if the version string should be normalized. + Defaults to ``True``. Setting this to ``False`` is equivalent to setting + ``version_cls`` to ``setuptools_scm.version.NonNormalizedVersion`` + +:version_cls: + An optional class used to parse, verify and possibly normalize the version + string. Its constructor should receive a single string argument, and its + ``str`` should return the normalized version string to use. + This option can also receive a class qualified name as a string. + + This defaults to ``packaging.version.Version`` if available. If + ``packaging`` is not installed, ``pkg_resources.packaging.version.Version`` + is used. Note that it is known to modify git release candidate schemes. + + The ``setuptools_scm.NonNormalizedVersion`` convenience class is + provided to disable the normalization step done by + ``packaging.version.Version``. If this is used while ``setuptools_scm`` + is integrated in a setuptools packaging process, the non-normalized + version number will appear in all files (see ``write_to``) BUT note + that setuptools will still normalize it to create the final distribution, + so as to stay compliant with the python packaging standards. + + To use ``setuptools_scm`` in other Python code you can use the ``get_version`` function: @@ -430,7 +451,7 @@ setup( use_scm_version={ - 'write_to': 'version.py', + 'write_to': '_version.py', 'write_to_template': '__version__ = "{version}"', 'tag_regex': r'^(?P<prefix>v)?(?P<version>^\++)(?P<suffix>.*)?$', } @@ -589,10 +610,6 @@ Some enterprise distributions like RHEL7 and others ship rather old setuptools versions due to various release management details. -On such distributions one might observe errors like: - -:code:``setuptools_scm.version.SetuptoolsOutdatedWarning: your setuptools is too old (<12)`` - In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. As those old setuptools versions lack sensible types for versions, modern setuptools_scm is unable to support them sensibly. @@ -603,7 +620,6 @@ - Code of Conduct ---------------
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/mypy.ini
Added
@@ -0,0 +1,10 @@ +mypy +python_version = 3.6 +warn_return_any = True +warn_unused_configs = True +mypy_path = $MYPY_CONFIG_FILE_DIR/src + +mypy-setuptools_scm.* +# disabled as it will take a bit +# disallow_untyped_defs = True +# strict = true
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/pyproject.toml -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/pyproject.toml
Changed
@@ -1,3 +1,8 @@ build-system -requires = "setuptools>=45", "wheel" +requires = + "setuptools>=45", + "wheel", + "tomli>=1.0", + "packaging>=20.0" + build-backend = "setuptools.build_meta"
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/setup.cfg -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/setup.cfg
Changed
@@ -19,6 +19,7 @@ Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Software Development :: Libraries Topic :: Software Development :: Version Control Topic :: System :: Software Distribution @@ -27,12 +28,17 @@ options packages = find: install_requires = - setuptools>=45 + packaging>=20.0 + setuptools + tomli>=1.0.0 # keep in sync python_requires = >=3.6 package_dir = =src zip_safe = true +options.packages.find +where = src + options.entry_points distutils.setup_keywords = use_scm_version = setuptools_scm.integration:version_keyword @@ -56,6 +62,7 @@ PKG-INFO = setuptools_scm.hacks:parse_pkginfo pip-egg-info = setuptools_scm.hacks:parse_pip_egg_info setup.py = setuptools_scm.hacks:fallback_version + pyproject.toml = setuptools_scm.hacks:fallback_version setuptools_scm.version_scheme = guess-next-dev = setuptools_scm.version:guess_next_dev_version post-release = setuptools_scm.version:postrelease_version @@ -64,13 +71,6 @@ no-guess-dev = setuptools_scm.version:no_guess_dev_version calver-by-date = setuptools_scm.version:calver_by_date -options.extras_require -toml = - toml - -options.packages.find -where = src - egg_info tag_build = tag_date = 0
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/setup.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/setup.py
Changed
@@ -11,42 +11,58 @@ """ import os import sys + import setuptools +from setuptools.command.bdist_egg import bdist_egg as original_bdist_egg + + +class bdist_egg(original_bdist_egg): + def run(self): + raise SystemExit( + "%s is forbidden, " + "please update to setuptools>=45 which uses pip" % type(self).__name__ + ) + +def scm_version(): -def scm_config(): + if sys.version_info < (3, 6): + raise RuntimeError( + "support for python < 3.6 has been removed in setuptools_scm>=6.0.0" + ) here = os.path.dirname(os.path.abspath(__file__)) src = os.path.join(here, "src") - egg_info = os.path.join(src, "setuptools_scm.egg-info") - has_entrypoints = os.path.isdir(egg_info) - import pkg_resources sys.path.insert(0, src) - pkg_resources.working_set.add_entry(src) - # FIXME: remove debug - print(src) - print(pkg_resources.working_set) + + from setuptools_scm import get_version from setuptools_scm.hacks import parse_pkginfo - from setuptools_scm.git import parse as parse_git + from setuptools_scm import git + from setuptools_scm import hg from setuptools_scm.version import guess_next_dev_version, get_local_node_and_date - def parse(root): + def parse(root, config): try: - return parse_pkginfo(root) + return parse_pkginfo(root, config) except OSError: - return parse_git(root) + return git.parse(root, config=config) or hg.parse(root, config=config) - config = dict( - version_scheme=guess_next_dev_version, local_scheme=get_local_node_and_date + return get_version( + root=here, + parse=parse, + version_scheme=guess_next_dev_version, + local_scheme=get_local_node_and_date, ) - if has_entrypoints: - return dict(use_scm_version=config) - else: - from setuptools_scm import get_version - - return dict(version=get_version(root=here, parse=parse, **config)) - if __name__ == "__main__": - setuptools.setup(**scm_config()) + setuptools.setup( + version=scm_version(), + extras_require={ + "toml": + "setuptools>=42", + , + "test": "pytest>=6.2", "virtualenv>20", + }, + cmdclass={"bdist_egg": bdist_egg}, + )
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm.egg-info/PKG-INFO -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm.egg-info/PKG-INFO
Changed
@@ -1,633 +1,11 @@ Metadata-Version: 2.1 Name: setuptools-scm -Version: 6.0.0 +Version: 6.4.2 Summary: the blessed package to manage your versions by scm tags Home-page: https://github.com/pypa/setuptools_scm/ Author: Ronny Pfannschmidt Author-email: opensource@ronnypfannschmidt.de License: MIT -Description: setuptools_scm - ============== - - ``setuptools_scm`` handles managing your Python package versions - in SCM metadata instead of declaring them as the version argument - or in a SCM managed file. - - Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM - (i.e. it automatically adds all of the SCM-managed files to the sdist). - Unwanted files must be excluded by discarding them via ``MANIFEST.in``. - - ``setuptools_scm`` support the following scm out of the box: - - * git - * mercurial - - - - .. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg - :target: https://github.com/pypa/setuptools_scm/actions - - .. image:: https://tidelift.com/badges/package/pypi/setuptools-scm - :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme - - - ``pyproject.toml`` usage - ------------------------ - - The preferred way to configure ``setuptools_scm`` is to author - settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. - - This feature requires Setuptools 42 or later, released in Nov, 2019. - If your project needs to support build from sdist on older versions - of Setuptools, you will need to also implement the ``setup.py usage`` - for those legacy environments. - - First, ensure that ``setuptools_scm`` is present during the project's - built step by specifying it as one of the build requirements. - - .. code:: toml - - # pyproject.toml - build-system - requires = "setuptools>=42", "wheel", "setuptools_scmtoml>=3.4" - - Note that the ``toml`` extra must be supplied. - - That will be sufficient to require ``setuptools_scm`` for projects - that support PEP 518 (`pip <https://pypi.org/project/pip>`_ and - `pep517 <https://pypi.org/project/pep517/>`_). Many tools, - especially those that invoke ``setup.py`` for any reason, may - continue to rely on ``setup_requires``. For maximum compatibility - with those uses, consider also including a ``setup_requires`` directive - (described below in ``setup.py usage`` and ``setup.cfg``). - - To enable version inference, add this section to your pyproject.toml: - - .. code:: toml - - # pyproject.toml - tool.setuptools_scm - - Including this section is comparable to supplying - ``use_scm_version=True`` in ``setup.py``. Additionally, - include arbitrary keyword arguments in that section - to be supplied to ``get_version()``. For example: - - .. code:: toml - - # pyproject.toml - - tool.setuptools_scm - write_to = "pkg/version.py" - - - ``setup.py`` usage - ------------------ - - The following settings are considered legacy behavior and - superseded by the ``pyproject.toml`` usage, but for maximal - compatibility, projects may also supply the configuration in - this older form. - - To use ``setuptools_scm`` just modify your project's ``setup.py`` file - like this: - - * Add ``setuptools_scm`` to the ``setup_requires`` parameter. - * Add the ``use_scm_version`` parameter and set it to ``True``. - - For example: - - .. code:: python - - from setuptools import setup - setup( - ..., - use_scm_version=True, - setup_requires='setuptools_scm', - ..., - ) - - Arguments to ``get_version()`` (see below) may be passed as a dictionary to - ``use_scm_version``. For example: - - .. code:: python - - from setuptools import setup - setup( - ..., - use_scm_version = { - "root": "..", - "relative_to": __file__, - "local_scheme": "node-and-timestamp" - }, - setup_requires='setuptools_scm', - ..., - ) - - You can confirm the version number locally via ``setup.py``: - - .. code-block:: shell - - $ python setup.py --version - - .. note:: - - If you see unusual version numbers for packages but ``python setup.py - --version`` reports the expected version number, ensure ``egg_info`` is - not defined in ``setup.cfg``. - - - ``setup.cfg`` usage - ------------------- - - If using `setuptools 30.3.0 - <https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_ - or greater, you can store ``setup_requires`` configuration in ``setup.cfg``. - However, ``use_scm_version`` must still be placed in ``setup.py``. For example: - - .. code:: python - - # setup.py - from setuptools import setup - setup( - use_scm_version=True, - ) - - .. code:: ini - - # setup.cfg - metadata - ... - - options - setup_requires = - setuptools_scm - ... - - .. important:: - - Ensure neither the ``metadata`` ``version`` option nor the ``egg_info`` - section are defined, as these will interfere with ``setuptools_scm``. - - You may also need to define a ``pyproject.toml`` file (`PEP-0518 - <https://www.python.org/dev/peps/pep-0518>`_) to ensure you have the required - version of ``setuptools``: - - .. code:: ini - - # pyproject.toml - build-system - requires = "setuptools>=30.3.0", "wheel", "setuptools_scm" - - For more information, refer to the `setuptools issue #1002 - <https://github.com/pypa/setuptools/issues/1002>`_. - - - Programmatic usage - ------------------ - - In order to use ``setuptools_scm`` from code that is one directory deeper - than the project's root, you can use: - - .. code:: python - - from setuptools_scm import get_version - version = get_version(root='..', relative_to=__file__) - - See `setup.py Usage`_ above for how to use this within ``setup.py``. - - - Retrieving package version at runtime - ------------------------------------- - - If you have opted not to hardcode the version number inside the package, - you can retrieve it at runtime from PEP-0566_ metadata using - ``importlib.metadata`` from the standard library (added in Python 3.8) - or the `importlib_metadata`_ backport: - - .. code:: python - - from importlib.metadata import version, PackageNotFoundError - - try: - __version__ = version("package-name") - except PackageNotFoundError: - # package is not installed - pass - - Alternatively, you can use ``pkg_resources`` which is included in - ``setuptools``: - - .. code:: python - - from pkg_resources import get_distribution, DistributionNotFound - - try: - __version__ = get_distribution("package-name").version - except DistributionNotFound: - # package is not installed - pass - - However, this does place a runtime dependency on ``setuptools`` and can add up to - a few 100ms overhead for the package import time. - - .. _PEP-0566: https://www.python.org/dev/peps/pep-0566/ - .. _importlib_metadata: https://pypi.org/project/importlib-metadata/ - - - Usage from Sphinx - ----------------- - - It is discouraged to use ``setuptools_scm`` from Sphinx itself, - instead use ``pkg_resources`` after editable/real installation: - - .. code:: python - - # contents of docs/conf.py - from pkg_resources import get_distribution - release = get_distribution('myproject').version - # for example take major/minor - version = '.'.join(release.split('.'):2) - - The underlying reason is, that services like *Read the Docs* sometimes change - the working directory for good reasons and using the installed metadata - prevents using needless volatile data there. - - Notable Plugins - --------------- - - `setuptools_scm_git_archive <https://pypi.python.org/pypi/setuptools_scm_git_archive>`_ - Provides partial support for obtaining versions from git archives that - belong to tagged versions. The only reason for not including it in - ``setuptools_scm`` itself is Git/GitHub not supporting sufficient metadata - for untagged/followup commits, which is preventing a consistent UX. - - - Default versioning scheme - ------------------------- - - In the standard configuration ``setuptools_scm`` takes a look at three things: - - 1. latest tag (with a version number) - 2. the distance to this tag (e.g. number of revisions since latest tag) - 3. workdir state (e.g. uncommitted changes since latest tag) - - and uses roughly the following logic to render the version: - - no distance and clean: - ``{tag}`` - distance and clean: - ``{next_version}.dev{distance}+{scm letter}{revision hash}`` - no distance and not clean: - ``{tag}+dYYYYMMDD`` - distance and not clean: - ``{next_version}.dev{distance}+{scm letter}{revision hash}.dYYYYMMDD`` - - The next version is calculated by adding ``1`` to the last numeric component of - the tag. - - - For Git projects, the version relies on `git describe <https://git-scm.com/docs/git-describe>`_, - so you will see an additional ``g`` prepended to the ``{revision hash}``. - - Semantic Versioning (SemVer) - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Due to the default behavior it's necessary to always include a - patch version (the ``3`` in ``1.2.3``), or else the automatic guessing - will increment the wrong part of the SemVer (e.g. tag ``2.0`` results in - ``2.1.devX`` instead of ``2.0.1.devX``). So please make sure to tag - accordingly. - - .. note:: - - Future versions of ``setuptools_scm`` will switch to `SemVer - <http://semver.org/>`_ by default hiding the the old behavior as an - configurable option. - - - Builtin mechanisms for obtaining version numbers - ------------------------------------------------ - - 1. the SCM itself (git/hg) - 2. ``.hg_archival`` files (mercurial archives) - 3. ``PKG-INFO`` - - .. note:: - - Git archives are not supported due to Git shortcomings - - - File finders hook makes most of MANIFEST.in unnecessary - ------------------------------------------------------- - - ``setuptools_scm`` implements a `file_finders - <https://setuptools.readthedocs.io/en/latest/setuptools.html#adding-support-for-revision-control-systems>`_ - entry point which returns all files tracked by your SCM. This eliminates - the need for a manually constructed ``MANIFEST.in`` in most cases where this - would be required when not using ``setuptools_scm``, namely: - - * To ensure all relevant files are packaged when running the ``sdist`` command. - - * When using `include_package_data <https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files>`_ - to include package data as part of the ``build`` or ``bdist_wheel``. - - ``MANIFEST.in`` may still be used: anything defined there overrides the hook. - This is mostly useful to exclude files tracked in your SCM from packages, - although in principle it can be used to explicitly include non-tracked files - too. - - - Configuration parameters - ------------------------ - - In order to configure the way ``use_scm_version`` works you can provide - a mapping with options instead of a boolean value. - - The currently supported configuration keys are: - - :root: - Relative path to cwd, used for finding the SCM root; defaults to ``.`` - - :version_scheme: - Configures how the local version number is constructed; either an - entrypoint name or a callable. - - :local_scheme: - Configures how the local component of the version is constructed; either an - entrypoint name or a callable. - - :write_to: - A path to a file that gets replaced with a file containing the current - version. It is ideal for creating a ``version.py`` file within the - package, typically used to avoid using `pkg_resources.get_distribution` - (which adds some overhead). - - .. warning:: - - Only files with :code:`.py` and :code:`.txt` extensions have builtin - templates, for other file types it is necessary to provide - :code:`write_to_template`. - - :write_to_template: - A newstyle format string that is given the current version as - the ``version`` keyword argument for formatting. - - :relative_to: - A file from which the root can be resolved. - Typically called by a script or module that is not in the root of the - repository to point ``setuptools_scm`` at the root of the repository by - supplying ``__file__``. - - :tag_regex: - A Python regex string to extract the version part from any SCM tag. - The regex needs to contain either a single match group, or a group - named ``version``, that captures the actual version information. - - Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX`` - (see `config.py <src/setuptools_scm/config.py>`_). - - :parentdir_prefix_version: - If the normal methods for detecting the version (SCM version, - sdist metadata) fail, and the parent directory name starts with - ``parentdir_prefix_version``, then this prefix is stripped and the rest of - the parent directory name is matched with ``tag_regex`` to get a version - string. If this parameter is unset (the default), then this fallback is - not used. - - This is intended to cover GitHub's "release tarballs", which extract into - directories named ``projectname-tag/`` (in which case - ``parentdir_prefix_version`` can be set e.g. to ``projectname-``). - - :fallback_version: - A version string that will be used if no other method for detecting the - version worked (e.g., when using a tarball with no metadata). If this is - unset (the default), setuptools_scm will error if it fails to detect the - version. - - :parse: - A function that will be used instead of the discovered SCM for parsing the - version. - Use with caution, this is a function for advanced use, and you should be - familiar with the ``setuptools_scm`` internals to use it. - - :git_describe_command: - This command will be used instead the default ``git describe`` command. - Use with caution, this is a function for advanced use, and you should be - familiar with the ``setuptools_scm`` internals to use it. - - Defaults to the value set by ``setuptools_scm.git.DEFAULT_DESCRIBE`` - (see `git.py <src/setuptools_scm/git.py>`_). - - To use ``setuptools_scm`` in other Python code you can use the ``get_version`` - function: - - .. code:: python - - from setuptools_scm import get_version - my_version = get_version() - - It optionally accepts the keys of the ``use_scm_version`` parameter as - keyword arguments. - - Example configuration in ``setup.py`` format: - - .. code:: python - - from setuptools import setup - - setup( - use_scm_version={ - 'write_to': 'version.py', - 'write_to_template': '__version__ = "{version}"', - 'tag_regex': r'^(?P<prefix>v)?(?P<version>^\++)(?P<suffix>.*)?$', - } - ) - - Environment variables - --------------------- - - :SETUPTOOLS_SCM_PRETEND_VERSION: - when defined and not empty, - its used as the primary source for the version number - in which case it will be a unparsed string - - - :SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}: - when defined and not empty, - its used as the primary source for the version number - in which case it will be a unparsed string - - it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION`` - - - :SETUPTOOLS_SCM_DEBUG: - when defined and not empty, - a lot of debug information will be printed as part of ``setuptools_scm`` - operating - - :SOURCE_DATE_EPOCH: - when defined, used as the timestamp from which the - ``node-and-date`` and ``node-and-timestamp`` local parts are - derived, otherwise the current time is used - (https://reproducible-builds.org/docs/source-date-epoch/) - - - :SETUPTOOLS_SCM_IGNORE_VCS_ROOTS: - when defined, a ``os.pathsep`` separated list - of directory names to ignore for root finding - - Extending setuptools_scm - ------------------------ - - ``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to - extend its default capabilities. - - Adding a new SCM - ~~~~~~~~~~~~~~~~ - - ``setuptools_scm`` provides two entrypoints for adding new SCMs: - - ``setuptools_scm.parse_scm`` - A function used to parse the metadata of the current workdir - using the name of the control directory/file of your SCM as the - entrypoint's name. E.g. for the built-in entrypoint for git the - entrypoint is named ``.git`` and references ``setuptools_scm.git:parse`` - - The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance - created by the function ``setuptools_scm.version:meta``. - - ``setuptools_scm.files_command`` - Either a string containing a shell command that prints all SCM managed - files in its current working directory or a callable, that given a - pathname will return that list. - - Also use then name of your SCM control directory as name of the entrypoint. - - Version number construction - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - ``setuptools_scm.version_scheme`` - Configures how the version number is constructed given a - ``setuptools_scm.version.ScmVersion`` instance and should return a string - representing the version. - - Available implementations: - - :guess-next-dev: Automatically guesses the next development version (default). - Guesses the upcoming release by incrementing the pre-release segment if present, - otherwise by incrementing the micro segment. Then appends :code:`.devN`. - In case the tag ends with ``.dev0`` the version is not bumped - and custom ``.devN`` versions will trigger a error. - :post-release: generates post release versions (adds :code:`.postN`) - :python-simplified-semver: Basic semantic versioning. Guesses the upcoming release - by incrementing the minor segment and setting the micro segment to zero if the - current branch contains the string ``'feature'``, otherwise by incrementing the - micro version. Then appends :code:`.devN`. Not compatible with pre-releases. - :release-branch-semver: Semantic versioning for projects with release branches. The - same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on - a release branch: a branch whose name (ignoring namespace) parses as a version - that matches the most recent tag up to the minor segment. Otherwise if on a - non-release branch, increments the minor segment and sets the micro segment to - zero, then appends :code:`.devN`. - :no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN` - - ``setuptools_scm.local_scheme`` - Configures how the local part of a version is rendered given a - ``setuptools_scm.version.ScmVersion`` instance and should return a string - representing the local version. - Dates and times are in Coordinated Universal Time (UTC), because as part - of the version, they should be location independent. - - Available implementations: - - :node-and-date: adds the node on dev versions and the date on dirty - workdir (default) - :node-and-timestamp: like ``node-and-date`` but with a timestamp of - the form ``{:%Y%m%d%H%M%S}`` instead - :dirty-tag: adds ``+dirty`` if the current workdir has changes - :no-local-version: omits local version, useful e.g. because pypi does - not support it - - - Importing in ``setup.py`` - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - To support usage in ``setup.py`` passing a callable into ``use_scm_version`` - is supported. - - Within that callable, ``setuptools_scm`` is available for import. - The callable must return the configuration. - - - .. code:: python - - # content of setup.py - import setuptools - - def myversion(): - from setuptools_scm.version import get_local_dirty_tag - def clean_scheme(version): - return get_local_dirty_tag(version) if version.dirty else '+clean' - - return {'local_scheme': clean_scheme} - - setup( - ..., - use_scm_version=myversion, - ... - ) - - - Note on testing non-installed versions - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - While the general advice is to test against a installed version, - some environments require a test prior to install, - - .. code:: - - $ python setup.py egg_info - $ PYTHONPATH=$PWD:$PWD/src pytest - - - Interaction with Enterprise Distributions - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Some enterprise distributions like RHEL7 and others - ship rather old setuptools versions due to various release management details. - - On such distributions one might observe errors like: - - :code:``setuptools_scm.version.SetuptoolsOutdatedWarning: your setuptools is too old (<12)`` - - In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. - As those old setuptools versions lack sensible types for versions, - modern setuptools_scm is unable to support them sensibly. - - In case the project you need to build can not be patched to either use old setuptools_scm, - its still possible to install a more recent version of setuptools in order to handle the build - and/or install the package by using wheels or eggs. - - - - - Code of Conduct - --------------- - - Everyone interacting in the ``setuptools_scm`` project's codebases, issue - trackers, chat rooms, and mailing lists is expected to follow the - `PSF Code of Conduct`_. - - .. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md - - Security Contact - ================ - - To report a security vulnerability, please use the - `Tidelift security contact <https://tidelift.com/security>`_. - Tidelift will coordinate the fix and disclosure. - Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers @@ -639,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Version Control Classifier: Topic :: System :: Software Distribution @@ -646,3 +25,645 @@ Requires-Python: >=3.6 Description-Content-Type: text/x-rst Provides-Extra: toml +Provides-Extra: test +License-File: LICENSE + +setuptools_scm +============== + +``setuptools_scm`` extract Python package versions from ``git`` or +``hg`` metadata instead of declaring them as the version argument +or in a SCM managed file. + +Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM +(i.e. it automatically adds all of the SCM-managed files to the sdist). +Unwanted files must be excluded by discarding them via ``MANIFEST.in``. + +``setuptools_scm`` support the following scm out of the box: + +* git +* mercurial + + + +.. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg + :target: https://github.com/pypa/setuptools_scm/actions + +.. image:: https://tidelift.com/badges/package/pypi/setuptools-scm + :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme + + +``pyproject.toml`` usage +------------------------ + +The preferred way to configure ``setuptools_scm`` is to author +settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. + +This feature requires Setuptools 42 or later, released in Nov, 2019. +If your project needs to support build from sdist on older versions +of Setuptools, you will need to also implement the ``setup.py usage`` +for those legacy environments. + +First, ensure that ``setuptools_scm`` is present during the project's +built step by specifying it as one of the build requirements. + +.. code:: toml + + # pyproject.toml + build-system + requires = "setuptools>=45", "wheel", "setuptools_scm>=6.2" + + +That will be sufficient to require ``setuptools_scm`` for projects +that support PEP 518 (`pip <https://pypi.org/project/pip>`_ and +`pep517 <https://pypi.org/project/pep517/>`_). Many tools, +especially those that invoke ``setup.py`` for any reason, may +continue to rely on ``setup_requires``. For maximum compatibility +with those uses, consider also including a ``setup_requires`` directive +(described below in ``setup.py usage`` and ``setup.cfg``). + +To enable version inference, add this section to your pyproject.toml: + +.. code:: toml + + # pyproject.toml + tool.setuptools_scm + +Including this section is comparable to supplying +``use_scm_version=True`` in ``setup.py``. Additionally, +include arbitrary keyword arguments in that section +to be supplied to ``get_version()``. For example: + +.. code:: toml + + # pyproject.toml + + tool.setuptools_scm + write_to = "pkg/_version.py" + + +``setup.py`` usage (deprecated) +------------------------------- + +.. warning:: + + ``setup_requires`` has been deprecated in favor of ``pyproject.toml`` + +The following settings are considered legacy behavior and +superseded by the ``pyproject.toml`` usage, but for maximal +compatibility, projects may also supply the configuration in +this older form. + +To use ``setuptools_scm`` just modify your project's ``setup.py`` file +like this: + +* Add ``setuptools_scm`` to the ``setup_requires`` parameter. +* Add the ``use_scm_version`` parameter and set it to ``True``. + +For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version=True, + setup_requires='setuptools_scm', + ..., + ) + +Arguments to ``get_version()`` (see below) may be passed as a dictionary to +``use_scm_version``. For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version = { + "root": "..", + "relative_to": __file__, + "local_scheme": "node-and-timestamp" + }, + setup_requires='setuptools_scm', + ..., + ) + +You can confirm the version number locally via ``setup.py``: + +.. code-block:: shell + + $ python setup.py --version + +.. note:: + + If you see unusual version numbers for packages but ``python setup.py + --version`` reports the expected version number, ensure ``egg_info`` is + not defined in ``setup.cfg``. + + +``setup.cfg`` usage (deprecated) +------------------------------------ + +as ``setup_requires`` is deprecated in favour of ``pyproject.toml`` +usage in ``setup.cfg`` is considered deprecated, +please use ``pyproject.toml`` whenever possible. + +Programmatic usage +------------------ + +In order to use ``setuptools_scm`` from code that is one directory deeper +than the project's root, you can use: + +.. code:: python + + from setuptools_scm import get_version + version = get_version(root='..', relative_to=__file__) + +See `setup.py Usage (deprecated)`_ above for how to use this within ``setup.py``. + + +Retrieving package version at runtime +------------------------------------- + +If you have opted not to hardcode the version number inside the package, +you can retrieve it at runtime from PEP-0566_ metadata using +``importlib.metadata`` from the standard library (added in Python 3.8) +or the `importlib_metadata`_ backport: + +.. code:: python + + from importlib.metadata import version, PackageNotFoundError + + try: + __version__ = version("package-name") + except PackageNotFoundError: + # package is not installed + pass + +Alternatively, you can use ``pkg_resources`` which is included in +``setuptools`` (but has a significant runtime cost): + +.. code:: python + + from pkg_resources import get_distribution, DistributionNotFound + + try: + __version__ = get_distribution("package-name").version + except DistributionNotFound: + # package is not installed + pass + +However, this does place a runtime dependency on ``setuptools`` and can add up to +a few 100ms overhead for the package import time. + +.. _PEP-0566: https://www.python.org/dev/peps/pep-0566/ +.. _importlib_metadata: https://pypi.org/project/importlib-metadata/ + + +Usage from Sphinx +----------------- + +It is discouraged to use ``setuptools_scm`` from Sphinx itself, +instead use ``importlib.metadata`` after editable/real installation: + +.. code:: python + + # contents of docs/conf.py + from importlib.metadata import version + release = version('myproject') + # for example take major/minor + version = '.'.join(release.split('.'):2) + +The underlying reason is, that services like *Read the Docs* sometimes change +the working directory for good reasons and using the installed metadata +prevents using needless volatile data there. + +Usage from Docker +----------------- + +By default, docker will not copy the ``.git`` folder into your container. +Therefore, builds with version inference might fail. +Consequently, you can use the following snipped to infer the version from +the host os without copying the entire ``.git`` folder to your Dockerfile. + +.. code:: dockerfile + + RUN --mount=source=.git,target=.git,type=bind \ + pip install --no-cache-dir -e . + +However, this build step introduces a dependency to the state of your local +.git folder the build cache and triggers the long-running pip install process on every build. +To optimize build caching, one can use an environment variable to pretend a pseudo +version that is used to cache the results of the pip install process: + +.. code:: dockerfile + + FROM python + COPY pyproject.toml + ARG PSEUDO_VERSION=1 + RUN SETUPTOOLS_SCM_PRETEND_VERSION=${PSEUDO_VERSION} pip install -e .test + RUN --mount=source=.git,target=.git,type=bind pip install -e . + +Note that running this Dockerfile requires docker with BuildKit enabled +`docs <https://github.com/moby/buildkit/blob/v0.8.3/frontend/dockerfile/docs/syntax.md>`_. + +To avoid BuildKit and mounting of the .git folder altogether, one can also pass the desired +version as a build argument. Note that ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}`` +is preferred over ``SETUPTOOLS_SCM_PRETEND_VERSION``. + +Notable Plugins +--------------- + +`setuptools_scm_git_archive <https://pypi.python.org/pypi/setuptools_scm_git_archive>`_ + Provides partial support for obtaining versions from git archives that + belong to tagged versions. The only reason for not including it in + ``setuptools_scm`` itself is Git/GitHub not supporting sufficient metadata + for untagged/followup commits, which is preventing a consistent UX. + + +Default versioning scheme +------------------------- + +In the standard configuration ``setuptools_scm`` takes a look at three things: + +1. latest tag (with a version number) +2. the distance to this tag (e.g. number of revisions since latest tag) +3. workdir state (e.g. uncommitted changes since latest tag) + +and uses roughly the following logic to render the version: + +no distance and clean: + ``{tag}`` +distance and clean: + ``{next_version}.dev{distance}+{scm letter}{revision hash}`` +no distance and not clean: + ``{tag}+dYYYYMMDD`` +distance and not clean: + ``{next_version}.dev{distance}+{scm letter}{revision hash}.dYYYYMMDD`` + +The next version is calculated by adding ``1`` to the last numeric component of +the tag. + + +For Git projects, the version relies on `git describe <https://git-scm.com/docs/git-describe>`_, +so you will see an additional ``g`` prepended to the ``{revision hash}``. + +Semantic Versioning (SemVer) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to the default behavior it's necessary to always include a +patch version (the ``3`` in ``1.2.3``), or else the automatic guessing +will increment the wrong part of the SemVer (e.g. tag ``2.0`` results in +``2.1.devX`` instead of ``2.0.1.devX``). So please make sure to tag +accordingly. + +.. note:: + + Future versions of ``setuptools_scm`` will switch to `SemVer + <http://semver.org/>`_ by default hiding the the old behavior as an + configurable option. + + +Builtin mechanisms for obtaining version numbers +------------------------------------------------ + +1. the SCM itself (git/hg) +2. ``.hg_archival`` files (mercurial archives) +3. ``PKG-INFO`` + +.. note:: + + Git archives are not supported due to Git shortcomings + + +File finders hook makes most of MANIFEST.in unnecessary +------------------------------------------------------- + +``setuptools_scm`` implements a `file_finders +<https://setuptools.readthedocs.io/en/latest/setuptools.html#adding-support-for-revision-control-systems>`_ +entry point which returns all files tracked by your SCM. This eliminates +the need for a manually constructed ``MANIFEST.in`` in most cases where this +would be required when not using ``setuptools_scm``, namely: + +* To ensure all relevant files are packaged when running the ``sdist`` command. + +* When using `include_package_data <https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files>`_ + to include package data as part of the ``build`` or ``bdist_wheel``. + +``MANIFEST.in`` may still be used: anything defined there overrides the hook. +This is mostly useful to exclude files tracked in your SCM from packages, +although in principle it can be used to explicitly include non-tracked files +too. + + +Configuration parameters +------------------------ + +In order to configure the way ``use_scm_version`` works you can provide +a mapping with options instead of a boolean value. + +The currently supported configuration keys are: + +:root: + Relative path to cwd, used for finding the SCM root; defaults to ``.`` + +:version_scheme: + Configures how the local version number is constructed; either an + entrypoint name or a callable. + +:local_scheme: + Configures how the local component of the version is constructed; either an + entrypoint name or a callable. + +:write_to: + A path to a file that gets replaced with a file containing the current + version. It is ideal for creating a ``_version.py`` file within the + package, typically used to avoid using `pkg_resources.get_distribution` + (which adds some overhead). + + .. warning:: + + Only files with :code:`.py` and :code:`.txt` extensions have builtin + templates, for other file types it is necessary to provide + :code:`write_to_template`. + +:write_to_template: + A newstyle format string that is given the current version as + the ``version`` keyword argument for formatting. + +:relative_to: + A file from which the root can be resolved. + Typically called by a script or module that is not in the root of the + repository to point ``setuptools_scm`` at the root of the repository by + supplying ``__file__``. + +:tag_regex: + A Python regex string to extract the version part from any SCM tag. + The regex needs to contain either a single match group, or a group + named ``version``, that captures the actual version information. + + Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX`` + (see `config.py <src/setuptools_scm/config.py>`_). + +:parentdir_prefix_version: + If the normal methods for detecting the version (SCM version, + sdist metadata) fail, and the parent directory name starts with + ``parentdir_prefix_version``, then this prefix is stripped and the rest of + the parent directory name is matched with ``tag_regex`` to get a version + string. If this parameter is unset (the default), then this fallback is + not used. + + This is intended to cover GitHub's "release tarballs", which extract into + directories named ``projectname-tag/`` (in which case + ``parentdir_prefix_version`` can be set e.g. to ``projectname-``). + +:fallback_version: + A version string that will be used if no other method for detecting the + version worked (e.g., when using a tarball with no metadata). If this is + unset (the default), setuptools_scm will error if it fails to detect the + version. + +:parse: + A function that will be used instead of the discovered SCM for parsing the + version. + Use with caution, this is a function for advanced use, and you should be + familiar with the ``setuptools_scm`` internals to use it. + +:git_describe_command: + This command will be used instead the default ``git describe`` command. + Use with caution, this is a function for advanced use, and you should be + familiar with the ``setuptools_scm`` internals to use it. + + Defaults to the value set by ``setuptools_scm.git.DEFAULT_DESCRIBE`` + (see `git.py <src/setuptools_scm/git.py>`_). + +:normalize: + A boolean flag indicating if the version string should be normalized. + Defaults to ``True``. Setting this to ``False`` is equivalent to setting + ``version_cls`` to ``setuptools_scm.version.NonNormalizedVersion`` + +:version_cls: + An optional class used to parse, verify and possibly normalize the version + string. Its constructor should receive a single string argument, and its + ``str`` should return the normalized version string to use. + This option can also receive a class qualified name as a string. + + This defaults to ``packaging.version.Version`` if available. If + ``packaging`` is not installed, ``pkg_resources.packaging.version.Version`` + is used. Note that it is known to modify git release candidate schemes. + + The ``setuptools_scm.NonNormalizedVersion`` convenience class is + provided to disable the normalization step done by + ``packaging.version.Version``. If this is used while ``setuptools_scm`` + is integrated in a setuptools packaging process, the non-normalized + version number will appear in all files (see ``write_to``) BUT note + that setuptools will still normalize it to create the final distribution, + so as to stay compliant with the python packaging standards. + + +To use ``setuptools_scm`` in other Python code you can use the ``get_version`` +function: + +.. code:: python + + from setuptools_scm import get_version + my_version = get_version() + +It optionally accepts the keys of the ``use_scm_version`` parameter as +keyword arguments. + +Example configuration in ``setup.py`` format: + +.. code:: python + + from setuptools import setup + + setup( + use_scm_version={ + 'write_to': '_version.py', + 'write_to_template': '__version__ = "{version}"', + 'tag_regex': r'^(?P<prefix>v)?(?P<version>^\++)(?P<suffix>.*)?$', + } + ) + +Environment variables +--------------------- + +:SETUPTOOLS_SCM_PRETEND_VERSION: + when defined and not empty, + its used as the primary source for the version number + in which case it will be a unparsed string + + +:SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}: + when defined and not empty, + its used as the primary source for the version number + in which case it will be a unparsed string + + it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION`` + + +:SETUPTOOLS_SCM_DEBUG: + when defined and not empty, + a lot of debug information will be printed as part of ``setuptools_scm`` + operating + +:SOURCE_DATE_EPOCH: + when defined, used as the timestamp from which the + ``node-and-date`` and ``node-and-timestamp`` local parts are + derived, otherwise the current time is used + (https://reproducible-builds.org/docs/source-date-epoch/) + + +:SETUPTOOLS_SCM_IGNORE_VCS_ROOTS: + when defined, a ``os.pathsep`` separated list + of directory names to ignore for root finding + +Extending setuptools_scm +------------------------ + +``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to +extend its default capabilities. + +Adding a new SCM +~~~~~~~~~~~~~~~~ + +``setuptools_scm`` provides two entrypoints for adding new SCMs: + +``setuptools_scm.parse_scm`` + A function used to parse the metadata of the current workdir + using the name of the control directory/file of your SCM as the + entrypoint's name. E.g. for the built-in entrypoint for git the + entrypoint is named ``.git`` and references ``setuptools_scm.git:parse`` + + The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance + created by the function ``setuptools_scm.version:meta``. + +``setuptools_scm.files_command`` + Either a string containing a shell command that prints all SCM managed + files in its current working directory or a callable, that given a + pathname will return that list. + + Also use then name of your SCM control directory as name of the entrypoint. + +Version number construction +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``setuptools_scm.version_scheme`` + Configures how the version number is constructed given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the version. + + Available implementations: + + :guess-next-dev: Automatically guesses the next development version (default). + Guesses the upcoming release by incrementing the pre-release segment if present, + otherwise by incrementing the micro segment. Then appends :code:`.devN`. + In case the tag ends with ``.dev0`` the version is not bumped + and custom ``.devN`` versions will trigger a error. + :post-release: generates post release versions (adds :code:`.postN`) + :python-simplified-semver: Basic semantic versioning. Guesses the upcoming release + by incrementing the minor segment and setting the micro segment to zero if the + current branch contains the string ``'feature'``, otherwise by incrementing the + micro version. Then appends :code:`.devN`. Not compatible with pre-releases. + :release-branch-semver: Semantic versioning for projects with release branches. The + same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on + a release branch: a branch whose name (ignoring namespace) parses as a version + that matches the most recent tag up to the minor segment. Otherwise if on a + non-release branch, increments the minor segment and sets the micro segment to + zero, then appends :code:`.devN`. + :no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN` + +``setuptools_scm.local_scheme`` + Configures how the local part of a version is rendered given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the local version. + Dates and times are in Coordinated Universal Time (UTC), because as part + of the version, they should be location independent. + + Available implementations: + + :node-and-date: adds the node on dev versions and the date on dirty + workdir (default) + :node-and-timestamp: like ``node-and-date`` but with a timestamp of + the form ``{:%Y%m%d%H%M%S}`` instead + :dirty-tag: adds ``+dirty`` if the current workdir has changes + :no-local-version: omits local version, useful e.g. because pypi does + not support it + + +Importing in ``setup.py`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To support usage in ``setup.py`` passing a callable into ``use_scm_version`` +is supported. + +Within that callable, ``setuptools_scm`` is available for import. +The callable must return the configuration. + + +.. code:: python + + # content of setup.py + import setuptools + + def myversion(): + from setuptools_scm.version import get_local_dirty_tag + def clean_scheme(version): + return get_local_dirty_tag(version) if version.dirty else '+clean' + + return {'local_scheme': clean_scheme} + + setup( + ..., + use_scm_version=myversion, + ... + ) + + +Note on testing non-installed versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While the general advice is to test against a installed version, +some environments require a test prior to install, + +.. code:: + + $ python setup.py egg_info + $ PYTHONPATH=$PWD:$PWD/src pytest + + +Interaction with Enterprise Distributions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some enterprise distributions like RHEL7 and others +ship rather old setuptools versions due to various release management details. + +In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. +As those old setuptools versions lack sensible types for versions, +modern setuptools_scm is unable to support them sensibly. + +In case the project you need to build can not be patched to either use old setuptools_scm, +its still possible to install a more recent version of setuptools in order to handle the build +and/or install the package by using wheels or eggs. + + + +Code of Conduct +--------------- + +Everyone interacting in the ``setuptools_scm`` project's codebases, issue +trackers, chat rooms, and mailing lists is expected to follow the +`PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact <https://tidelift.com/security>`_. +Tidelift will coordinate the fix and disclosure. + +
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm.egg-info/SOURCES.txt -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm.egg-info/SOURCES.txt
Changed
@@ -1,17 +1,18 @@ -.gitignore CHANGELOG.rst LICENSE MANIFEST.in README.rst +mypy.ini pyproject.toml setup.cfg setup.py tox.ini -.github/FUNDING.yml -.github/workflows/pre-commit.yml -.github/workflows/python-tests.yml src/setuptools_scm/__init__.py src/setuptools_scm/__main__.py +src/setuptools_scm/_entrypoints.py +src/setuptools_scm/_overrides.py +src/setuptools_scm/_types.py +src/setuptools_scm/_version_cls.py src/setuptools_scm/config.py src/setuptools_scm/discover.py src/setuptools_scm/file_finder.py @@ -20,7 +21,9 @@ src/setuptools_scm/git.py src/setuptools_scm/hacks.py src/setuptools_scm/hg.py +src/setuptools_scm/hg_git.py src/setuptools_scm/integration.py +src/setuptools_scm/scm_workdir.py src/setuptools_scm/utils.py src/setuptools_scm/version.py src/setuptools_scm.egg-info/PKG-INFO @@ -30,6 +33,7 @@ src/setuptools_scm.egg-info/requires.txt src/setuptools_scm.egg-info/top_level.txt src/setuptools_scm.egg-info/zip-safe +testing/Dockerfile.busted-buster testing/check_self_install.py testing/conftest.py testing/play_out_381.bash @@ -38,6 +42,7 @@ testing/test_file_finder.py testing/test_functions.py testing/test_git.py +testing/test_hg_git.py testing/test_integration.py testing/test_main.py testing/test_mercurial.py
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm.egg-info/entry_points.txt -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm.egg-info/entry_points.txt
Changed
@@ -25,6 +25,7 @@ .hg_archival.txt = setuptools_scm.hg:parse_archival PKG-INFO = setuptools_scm.hacks:parse_pkginfo pip-egg-info = setuptools_scm.hacks:parse_pip_egg_info +pyproject.toml = setuptools_scm.hacks:fallback_version setup.py = setuptools_scm.hacks:fallback_version setuptools_scm.version_scheme
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm.egg-info/requires.txt -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm.egg-info/requires.txt
Changed
@@ -1,4 +1,10 @@ -setuptools>=45 +packaging>=20.0 +setuptools +tomli>=1.0.0 + +test +pytest>=6.2 +virtualenv>20 toml -toml +setuptools>=42
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/__init__.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/__init__.py
Changed
@@ -4,19 +4,32 @@ """ import os import warnings - -from .config import ( - Configuration, - DEFAULT_VERSION_SCHEME, - DEFAULT_LOCAL_SCHEME, - DEFAULT_TAG_REGEX, -) -from .utils import function_has_arg, trace -from .version import format_version, meta +from typing import Optional +from typing import TYPE_CHECKING + +from . import _types +from ._entrypoints import _call_entrypoint_fn +from ._entrypoints import _version_from_entrypoints +from ._overrides import _read_pretended_version_for +from ._overrides import PRETEND_KEY +from ._overrides import PRETEND_KEY_NAMED +from ._version_cls import _version_as_tuple +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version +from .config import Configuration +from .config import DEFAULT_LOCAL_SCHEME +from .config import DEFAULT_TAG_REGEX +from .config import DEFAULT_VERSION_SCHEME from .discover import iter_matching_entrypoints +from .utils import function_has_arg +from .utils import trace +from .version import format_version +from .version import meta +from .version import ScmVersion + +if TYPE_CHECKING: + from typing import NoReturn -PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" -PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" TEMPLATES = { ".py": """\ @@ -34,44 +47,19 @@ warnings.warn( "version_from_scm is deprecated please use get_version", category=DeprecationWarning, + stacklevel=2, ) - config = Configuration() - config.root = root - # TODO: Is it API? + config = Configuration(root=root) return _version_from_entrypoints(config) -def _call_entrypoint_fn(root, config, fn): - if function_has_arg(fn, "config"): - return fn(root, config=config) - else: - warnings.warn( - "parse functions are required to provide a named argument" - " 'config' in the future.", - category=PendingDeprecationWarning, - stacklevel=2, - ) - return fn(root) - - -def _version_from_entrypoints(config, fallback=False): - if fallback: - entrypoint = "setuptools_scm.parse_scm_fallback" - root = config.fallback_root - else: - entrypoint = "setuptools_scm.parse_scm" - root = config.absolute_root - for ep in iter_matching_entrypoints(root, entrypoint): - version = _call_entrypoint_fn(root, config, ep.load()) - - if version: - return version - - -def dump_version(root, version, write_to, template=None): +def dump_version( + root: _types.PathT, + version: str, + write_to: _types.PathT, + template: "str | None" = None, +): assert isinstance(version, str) - if not write_to: - return target = os.path.normpath(os.path.join(root, write_to)) ext = os.path.splitext(target)1 template = template or TEMPLATES.get(ext) @@ -82,38 +70,16 @@ os.path.splitext(target)1, target ) ) - - # version_tuple: each field is converted to an int if possible or kept as string - fields = tuple(version.split(".")) - version_fields = - for field in fields: - try: - v = int(field) - except ValueError: - v = field - version_fields.append(v) + version_tuple = _version_as_tuple(version) with open(target, "w") as fp: - fp.write(template.format(version=version, version_tuple=tuple(version_fields))) - - -def _do_parse(config): - - trace("dist name:", config.dist_name) - if config.dist_name is not None: - pretended = os.environ.get( - PRETEND_KEY_NAMED.format(name=config.dist_name.upper()) - ) - else: - pretended = None + fp.write(template.format(version=version, version_tuple=version_tuple)) - if pretended is None: - pretended = os.environ.get(PRETEND_KEY) - if pretended: - # we use meta here since the pretended version - # must adhere to the pep to begin with - return meta(tag=pretended, preformatted=True, config=config) +def _do_parse(config: Configuration) -> "ScmVersion|None": + pretended = _read_pretended_version_for(config) + if pretended is not None: + return pretended if config.parse: parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) @@ -121,25 +87,31 @@ raise TypeError( "version parse result was a string\nplease return a parsed version" ) - version = parse_result or _version_from_entrypoints(config, fallback=True) + version: OptionalScmVersion + if parse_result: + assert isinstance(parse_result, ScmVersion) + version = parse_result + else: + version = _version_from_entrypoints(config, fallback=True) else: # include fallbacks after dropping them from the main entrypoint version = _version_from_entrypoints(config) or _version_from_entrypoints( config, fallback=True ) - if version: - return version + return version + +def _version_missing(config) -> "NoReturn": raise LookupError( - "setuptools-scm was unable to detect version for %r.\n\n" + f"setuptools-scm was unable to detect version for {config.absolute_root}.\n\n" "Make sure you're either building from a fully intact git repository " "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a " "git checkout without the .git folder) don't contain the necessary " "metadata and will not work.\n\n" "For example, if you're using pip, instead of " "https://github.com/user/proj/archive/master.zip " - "use git+https://github.com/user/proj.git#egg=proj" % config.absolute_root + "use git+https://github.com/user/proj.git#egg=proj" ) @@ -157,6 +129,9 @@ parse=None, git_describe_command=None, dist_name=None, + version_cls=None, + normalize=True, + search_parent_directories=False, ): """ If supplied, relative_to should be a file from which root may @@ -166,18 +141,22 @@ """ config = Configuration(**locals()) - return _get_version(config) + maybe_version = _get_version(config) + if maybe_version is None: + _version_missing(config) + return maybe_version -def _get_version(config): +def _get_version(config: Configuration) -> "str|None": parsed_version = _do_parse(config) - - if parsed_version: - version_string = format_version( - parsed_version, - version_scheme=config.version_scheme, - local_scheme=config.local_scheme, - ) + if parsed_version is None: + return None + version_string = format_version( + parsed_version, + version_scheme=config.version_scheme, + local_scheme=config.local_scheme, + ) + if config.write_to is not None: dump_version( root=config.root, version=version_string, @@ -185,4 +164,26 @@ template=config.write_to_template, ) - return version_string + return version_string + + +# Public API +__all__ = + "get_version", + "dump_version", + "version_from_scm", + "Configuration", + "DEFAULT_VERSION_SCHEME", + "DEFAULT_LOCAL_SCHEME", + "DEFAULT_TAG_REGEX", + "PRETEND_KEY", + "PRETEND_KEY_NAMED", + "Version", + "NonNormalizedVersion", + # TODO: are the symbols below part of public API ? + "function_has_arg", + "trace", + "format_version", + "meta", + "iter_matching_entrypoints", +
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/__main__.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/__main__.py
Changed
@@ -1,14 +1,83 @@ +import argparse +import os import sys -from setuptools_scm import get_version + +from setuptools_scm import _get_version +from setuptools_scm.config import Configuration +from setuptools_scm.discover import walk_potential_roots from setuptools_scm.integration import find_files -def main(): - print("Guessed Version", get_version()) - if "ls" in sys.argv: - for fname in find_files("."): +def main() -> None: + opts = _get_cli_opts() + root = opts.root or "." + + try: + pyproject = opts.config or _find_pyproject(root) + root = opts.root or os.path.relpath(os.path.dirname(pyproject)) + config = Configuration.from_file(pyproject, root=root) + except (LookupError, FileNotFoundError) as ex: + # no pyproject.toml OR no tool.setuptools_scm + print( + f"Warning: could not use {os.path.relpath(pyproject)}," + " using default configuration.\n" + f" Reason: {ex}.", + file=sys.stderr, + ) + config = Configuration(root=root) + + version = _get_version(config) + assert version is not None + if opts.strip_dev: + version = version.partition(".dev")0 + print(version) + + if opts.command == "ls": + for fname in find_files(config.root): print(fname) +def _get_cli_opts() -> argparse.Namespace: + prog = "python -m setuptools_scm" + desc = "Print project version according to SCM metadata" + parser = argparse.ArgumentParser(prog, description=desc) + # By default, help for `--help` starts with lower case, so we keep the pattern: + parser.add_argument( + "-r", + "--root", + default=None, + help='directory managed by the SCM, default: inferred from config file, or "."', + ) + parser.add_argument( + "-c", + "--config", + default=None, + metavar="PATH", + help="path to 'pyproject.toml' with setuptools_scm config, " + "default: looked up in the current or parent directories", + ) + parser.add_argument( + "--strip-dev", + action="store_true", + help="remove the dev/local parts of the version before printing the version", + ) + sub = parser.add_subparsers(title="extra commands", dest="command", metavar="") + # We avoid `metavar` to prevent printing repetitive information + desc = "List files managed by the SCM" + sub.add_parser("ls", help=desc0.lower() + desc1:, description=desc) + return parser.parse_args() + + +def _find_pyproject(parent: str) -> str: + for directory in walk_potential_roots(os.path.abspath(parent)): + pyproject = os.path.join(directory, "pyproject.toml") + if os.path.isfile(pyproject): + return pyproject + + return os.path.abspath( + "pyproject.toml" + ) # use default name to trigger the default errors + + if __name__ == "__main__": main()
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/_entrypoints.py
Added
@@ -0,0 +1,58 @@ +import warnings +from typing import Optional + +from .config import Configuration +from .discover import iter_matching_entrypoints +from .utils import function_has_arg +from .utils import trace +from setuptools_scm.version import ScmVersion + + +def _call_entrypoint_fn(root, config, fn): + if function_has_arg(fn, "config"): + return fn(root, config=config) + else: + warnings.warn( + f"parse function {fn.__module__}.{fn.__name__}" + " are required to provide a named argument" + " 'config', setuptools_scm>=8.0 will remove support.", + category=DeprecationWarning, + stacklevel=2, + ) + return fn(root) + + +def _version_from_entrypoints( + config: Configuration, fallback: bool = False +) -> "ScmVersion|None": + if fallback: + entrypoint = "setuptools_scm.parse_scm_fallback" + root = config.fallback_root + else: + entrypoint = "setuptools_scm.parse_scm" + root = config.absolute_root + + trace("version_from_ep", entrypoint, root) + for ep in iter_matching_entrypoints(root, entrypoint, config): + version: OptionalScmVersion = _call_entrypoint_fn(root, config, ep.load()) + trace(ep, version) + if version: + return version + return None + + +try: + from importlib.metadata import entry_points # type: ignore +except ImportError: + from pkg_resources import iter_entry_points +else: + + def iter_entry_points(group: str, name: Optionalstr = None): + all_eps = entry_points() + if hasattr(all_eps, "select"): + eps = all_eps.select(group=group) + else: + eps = all_epsgroup + if name is None: + return iter(eps) + return (ep for ep in eps if ep.name == name)
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/_overrides.py
Added
@@ -0,0 +1,37 @@ +import os +from typing import Optional + +from .config import Configuration +from .utils import trace +from .version import meta +from .version import ScmVersion + + +PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" +PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" + + +def _read_pretended_version_for(config: Configuration) -> OptionalScmVersion: + """read a a overridden version from the environment + + tries ``SETUPTOOLS_SCM_PRETEND_VERSION`` + and ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_$UPPERCASE_DIST_NAME`` + """ + trace("dist name:", config.dist_name) + pretended: Optionalstr + if config.dist_name is not None: + pretended = os.environ.get( + PRETEND_KEY_NAMED.format(name=config.dist_name.upper()) + ) + else: + pretended = None + + if pretended is None: + pretended = os.environ.get(PRETEND_KEY) + + if pretended is not None: + # we use meta here since the pretended version + # must adhere to the pep to begin with + return meta(tag=pretended, preformatted=True, config=config) + else: + return None
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/_types.py
Added
@@ -0,0 +1,31 @@ +import os +from typing import Callable +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +if TYPE_CHECKING: + + from typing_extensions import ParamSpec +else: + + class ParamSpec(list): + def __init__(self, _) -> None: + pass + + +PathT = Union"os.PathLikestr", str + + +T = TypeVar("T") +T2 = TypeVar("T2") +PARAMS = ParamSpec("PARAMS") + + +def transfer_input_args( + template: "CallablePARAMS, T", +) -> CallableCallable..., T2, "CallablePARAMS, T2": + def decorate(func: Callable..., T2) -> "CallablePARAMS, T2": + return func + + return decorate
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/_version_cls.py
Added
@@ -0,0 +1,74 @@ +from logging import getLogger +from typing import Tuple + +try: + from packaging.version import Version, InvalidVersion + + assert hasattr( + Version, "release" + ), "broken installation ensure packaging>=20 is available" +except ImportError: + from pkg_resources._vendor.packaging.version import ( # type: ignore + Version as SetuptoolsVersion, + InvalidVersion, + ) + + try: + SetuptoolsVersion.release + Version = SetuptoolsVersion # type: ignore + except AttributeError: + + class Version(SetuptoolsVersion): # type: ignore + @property + def release(self): + return self._version.release + + @property + def dev(self): + return self._version.dev + + @property + def local(self): + return self._version.local + + +class NonNormalizedVersion(Version): + """A non-normalizing version handler. + + You can use this class to preserve version verification but skip normalization. + For example you can use this to avoid git release candidate version tags + ("1.0.0-rc1") to be normalized to "1.0.0rc1". Only use this if you fully + trust the version tags. + """ + + def __init__(self, version): + # parse and validate using parent + super().__init__(version) + + # store raw for str + self._raw_version = version + + def __str__(self): + # return the non-normalized version (parent returns the normalized) + return self._raw_version + + def __repr__(self): + # same pattern as parent + return f"<NonNormalizedVersion({self._raw_version!r})>" + + +def _version_as_tuple(version_str) -> Tuple"int | str", ...: + try: + parsed_version = Version(version_str) + except InvalidVersion: + + log = getLogger("setuptools_scm") + log.exception("failed to parse version %s", version_str) + return (version_str,) + else: + version_fields: Tuple"int | str", ... = parsed_version.release + if parsed_version.dev is not None: + version_fields += (f"dev{parsed_version.dev}",) + if parsed_version.local is not None: + version_fields += (parsed_version.local,) + return version_fields
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/config.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/config.py
Changed
@@ -2,7 +2,12 @@ import os import re import warnings +from typing import Type +from typing import TypeVar +from . import _types as _t +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version from .utils import trace DEFAULT_TAG_REGEX = r"^(?:\w-+-)?(?P<version>vV?\d+(?:\.\d+){0,2}^\+*)(?:\+.*)?$" @@ -25,10 +30,13 @@ return regex -def _check_absolute_root(root, relative_to): - trace("l", repr(locals())) +def _check_absolute_root(root: _t.PathT, relative_to: _t.PathT): + trace("abs root", repr(locals())) if relative_to: - if os.path.isabs(root) and not root.startswith(relative_to): + if ( + os.path.isabs(root) + and not os.path.commonpath(root, relative_to) == relative_to + ): warnings.warn( "absolute root path '%s' overrides relative_to '%s'" % (root, relative_to) @@ -47,24 +55,40 @@ return os.path.abspath(root) +def _lazy_tomli_load(data: str): + from tomli import loads + + return loads(data) + + +VersionT = TypeVar("VersionT", Version, NonNormalizedVersion) + + class Configuration: - """ Global configuration model """ + """Global configuration model""" + + _root: _t.PathT + _relative_to: "_t.PathT | None" + version_cls: "TypeVersion|TypeNonNormalizedVersion" def __init__( self, - relative_to=None, - root=".", - version_scheme=DEFAULT_VERSION_SCHEME, + relative_to: "_t.PathT | None" = None, + root: _t.PathT = ".", + version_scheme: str = DEFAULT_VERSION_SCHEME, local_scheme=DEFAULT_LOCAL_SCHEME, - write_to=None, - write_to_template=None, + write_to: "_t.PathT | None" = None, + write_to_template: "str|None" = None, tag_regex=DEFAULT_TAG_REGEX, parentdir_prefix_version=None, - fallback_version=None, - fallback_root=".", + fallback_version: "str|None" = None, + fallback_root: _t.PathT = ".", parse=None, git_describe_command=None, - dist_name=None, + dist_name: str = None, + version_cls: "TypeVersion|TypeNonNormalizedVersion|str|None" = None, + normalize: bool = True, + search_parent_directories: bool = False, ): # TODO: self._relative_to = relative_to @@ -82,6 +106,33 @@ self.tag_regex = tag_regex self.git_describe_command = git_describe_command self.dist_name = dist_name + self.search_parent_directories = search_parent_directories + self.parent = None + + if not normalize: + # `normalize = False` means `version_cls = NonNormalizedVersion` + if version_cls is not None: + raise ValueError( + "Providing a custom `version_cls` is not permitted when " + "`normalize=False`" + ) + self.version_cls = NonNormalizedVersion + else: + # Use `version_cls` if provided, default to packaging or pkg_resources + if version_cls is None: + self.version_cls = Version + elif isinstance(version_cls, str): + try: + # Not sure this will work in old python + import importlib + + pkg, cls_name = version_cls.rsplit(".", 1) + version_cls_host = importlib.import_module(pkg) + self.version_cls = getattr(version_cls_host, cls_name) + except: # noqa + raise ValueError(f"Unable to import version_cls='{version_cls}'") + else: + self.version_cls = version_cls @property def fallback_root(self): @@ -126,14 +177,51 @@ self._tag_regex = _check_tag_regex(value) @classmethod - def from_file(cls, name="pyproject.toml", dist_name=None): + def from_file( + cls, + name: str = "pyproject.toml", + dist_name=None, # type: str | None + _load_toml=_lazy_tomli_load, + **kwargs, + ): """ Read Configuration from pyproject.toml (or similar). Raises exceptions when file is not found or toml is not installed or the file has invalid format or does not contain the tool.setuptools_scm section. """ - with open(name) as strm: - defn = __import__("toml").load(strm) - section = defn.get("tool", {})"setuptools_scm" - return cls(dist_name=dist_name, **section) + + with open(name, encoding="UTF-8") as strm: + data = strm.read() + defn = _load_toml(data) + try: + section = defn.get("tool", {})"setuptools_scm" + except LookupError as e: + raise LookupError( + f"{name} does not contain a tool.setuptools_scm section" + ) from e + if "dist_name" in section: + if dist_name is None: + dist_name = section.pop("dist_name") + else: + assert dist_name == section"dist_name" + del section"dist_name" + if dist_name is None: + if "project" in defn: + # minimal pep 621 support for figuring the pretend keys + dist_name = defn"project".get("name") + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + + return cls(dist_name=dist_name, **section, **kwargs) + + +def _read_dist_name_from_setup_cfg(): + + # minimal effort to read dist_name off setup.cfg metadata + import configparser + + parser = configparser.ConfigParser() + parser.read("setup.cfg") + dist_name = parser.get("metadata", "name", fallback=None) + return dist_name
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/discover.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/discover.py
Changed
@@ -1,13 +1,58 @@ import os -from pkg_resources import iter_entry_points + +from .config import Configuration +from .utils import iter_entry_points from .utils import trace -def iter_matching_entrypoints(path, entrypoint): - trace("looking for ep", entrypoint, path) - for ep in iter_entry_points(entrypoint): - if os.path.exists(os.path.join(path, ep.name)): - if os.path.isabs(ep.name): - trace("ignoring bad ep", ep) - trace("found ep", ep) - yield ep +def walk_potential_roots(root, search_parents=True): + """ + Iterate though a path and each of its parents. + :param root: File path. + :param search_parents: If ``False`` the parents are not considered. + """ + + if not search_parents: + yield root + return + + tail = root + + while tail: + yield root + root, tail = os.path.split(root) + + +def match_entrypoint(root, name): + """ + Consider a ``root`` as entry-point. + :param root: File path. + :param name: Subdirectory name. + :return: ``True`` if a subdirectory ``name`` exits in ``root``. + """ + + if os.path.exists(os.path.join(root, name)): + if not os.path.isabs(name): + return True + trace("ignoring bad ep", name) + + return False + + +def iter_matching_entrypoints(root, entrypoint, config: Configuration): + """ + Consider different entry-points in ``root`` and optionally its parents. + :param root: File path. + :param entrypoint: Entry-point to consider. + :param config: Configuration, + read ``search_parent_directories``, write found parent to ``parent``. + """ + + trace("looking for ep", entrypoint, root) + + for wd in walk_potential_roots(root, config.search_parent_directories): + for ep in iter_entry_points(entrypoint): + if match_entrypoint(wd, ep.name): + trace("found ep", ep, "in", wd) + config.parent = wd + yield ep
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/file_finder.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/file_finder.py
Changed
@@ -1,4 +1,5 @@ import os + from .utils import trace @@ -57,7 +58,7 @@ def is_toplevel_acceptable(toplevel): - "" + """ """ if toplevel is None: return False
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/file_finder_git.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/file_finder_git.py
Changed
@@ -1,9 +1,11 @@ +import logging import os import subprocess import tarfile -import logging -from .file_finder import scm_find_files + from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex from .utils import trace log = logging.getLogger(__name__) @@ -12,13 +14,17 @@ def _git_toplevel(path): try: cwd = os.path.abspath(path or ".") - with open(os.devnull, "wb") as devnull: - out = subprocess.check_output( - "git", "rev-parse", "--show-prefix", - cwd=cwd, - universal_newlines=True, - stderr=devnull, - ) + out, err, ret = do_ex("git", "rev-parse", "HEAD", cwd=cwd) + if ret != 0: + # BAIL if there is no commit + log.error("listing git files failed - pretending there aren't any") + return None + out, err, ret = do_ex( + "git", "rev-parse", "--show-prefix", + cwd=cwd, + ) + if ret != 0: + return None out = out.strip():-1 # remove the trailing pathsep if not out: out = cwd @@ -27,7 +33,7 @@ # ``cwd`` is absolute path to current working directory. # the below method removes the length of ``out`` from # ``cwd``, which gives the git toplevel - assert cwd.replace("\\", "/").endswith(out) + assert cwd.replace("\\", "/").endswith(out), f"cwd={cwd!r}\nout={out!r}" # In windows cwd contains ``\`` which should be replaced by ``/`` # for this assertion to work. Length of string isn't changed by replace # ``\\`` is just and escape for `\` @@ -58,8 +64,11 @@ def _git_ls_files_and_dirs(toplevel): # use git archive instead of git ls-file to honor # export-ignore git attribute + cmd = "git", "archive", "--prefix", toplevel + os.path.sep, "HEAD" - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=toplevel) + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, cwd=toplevel, stderr=subprocess.DEVNULL + ) try: try: return _git_interpret_archive(proc.stdout, toplevel) @@ -69,7 +78,7 @@ proc.terminate() except Exception: if proc.wait() != 0: - log.exception("listing git files failed - pretending there aren't any") + log.error("listing git files failed - pretending there aren't any") return (), ()
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/file_finder_hg.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/file_finder_hg.py
Changed
@@ -1,8 +1,9 @@ import os import subprocess -from .file_finder import scm_find_files from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex def _hg_toplevel(path): @@ -26,9 +27,9 @@ def _hg_ls_files_and_dirs(toplevel): hg_files = set() hg_dirs = {toplevel} - out = subprocess.check_output( - "hg", "files", cwd=toplevel, universal_newlines=True - ) + out, err, ret = do_ex("hg", "files", cwd=toplevel) + if ret: + (), () for name in out.splitlines(): name = os.path.normcase(name).replace("/", os.path.sep) fullname = os.path.join(toplevel, name)
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/git.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/git.py
Changed
@@ -1,29 +1,40 @@ -from .config import Configuration -from .utils import do_ex, trace, require_command -from .version import meta -from datetime import datetime, date import os -from os.path import isfile, join import warnings - - +from datetime import date +from datetime import datetime +from os.path import isfile +from os.path import join from os.path import samefile +from .config import Configuration +from .scm_workdir import Workdir +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta -DEFAULT_DESCRIBE = "git describe --dirty --tags --long --match *0-9*" - - -class GitWorkdir: +# If testing command in shell make sure to quote the match argument like +# '*0-9*' as it will expand before being sent to git if there are any matching +# files in current directory. +DEFAULT_DESCRIBE = + "git", + "describe", + "--dirty", + "--tags", + "--long", + "--match", + "*0-9*", + + + +class GitWorkdir(Workdir): """experimental, may change at any time""" - def __init__(self, path): - self.path = path - - def do_ex(self, cmd): - return do_ex(cmd, cwd=self.path) + COMMAND = "git" @classmethod def from_potential_worktree(cls, wd): + require_command(cls.COMMAND) wd = os.path.abspath(wd) real_wd, _, ret = do_ex("git rev-parse --show-prefix", wd) real_wd = real_wd:-1 # remove the trailing pathsep @@ -51,16 +62,22 @@ branch, err, ret = self.do_ex("git rev-parse --abbrev-ref HEAD") if ret: trace("branch err", branch, err, ret) - return + branch, err, ret = self.do_ex("git symbolic-ref --short HEAD") + if ret: + trace("branch err (symbolic-ref)", branch, err, ret) + branch = None return branch def get_head_date(self): timestamp, err, ret = self.do_ex("git log -n 1 HEAD --format=%cI") if ret: - trace("branch err", timestamp, err, ret) + trace("timestamp err", timestamp, err, ret) return # TODO, when dropping python3.6 use fromiso date_part = timestamp.split("T")0 + if "%c" in date_part: + trace("git too old -> timestamp is ", timestamp) + return None return datetime.strptime(date_part, r"%Y-%m-%d").date() def is_shallow(self): @@ -70,14 +87,17 @@ self.do_ex("git fetch --unshallow") def node(self): - rev_node, _, ret = self.do_ex("git rev-parse --verify --quiet HEAD") + node, _, ret = self.do_ex("git rev-parse --verify --quiet HEAD") if not ret: - return rev_node:7 + return node:7 def count_all_nodes(self): revs, _, _ = self.do_ex("git rev-list HEAD") return revs.count("\n") + 1 + def default_describe(self): + return self.do_ex(DEFAULT_DESCRIBE) + def warn_on_shallow(wd): """experimental, may change at any time""" @@ -88,7 +108,7 @@ def fetch_on_shallow(wd): """experimental, may change at any time""" if wd.is_shallow(): - warnings.warn('"%s" was shallow, git fetch was used to rectify') + warnings.warn(f'"{wd.path}" was shallow, git fetch was used to rectify') wd.fetch_shallow() @@ -96,85 +116,77 @@ """experimental, may change at any time""" if wd.is_shallow(): raise ValueError( - "%r is shallow, please correct with " '"git fetch --unshallow"' % wd.path + f'{wd.path} is shallow, please correct with "git fetch --unshallow"' ) -def parse( - root, describe_command=DEFAULT_DESCRIBE, pre_parse=warn_on_shallow, config=None -): +def get_working_directory(config): + """ + Return the working directory (``GitWorkdir``). + """ + + if config.parent: + return GitWorkdir.from_potential_worktree(config.parent) + + if config.search_parent_directories: + return search_parent(config.absolute_root) + + return GitWorkdir.from_potential_worktree(config.absolute_root) + + +def parse(root, describe_command=None, pre_parse=warn_on_shallow, config=None): """ :param pre_parse: experimental pre_parse action, may change at any time """ if not config: config = Configuration(root=root) - require_command("git") + wd = get_working_directory(config) + if wd: + return _git_parse_inner( + config, wd, describe_command=describe_command, pre_parse=pre_parse + ) - wd = GitWorkdir.from_potential_worktree(config.absolute_root) - if wd is None: - return + +def _git_parse_inner(config, wd, pre_parse=None, describe_command=None): if pre_parse: pre_parse(wd) - if config.git_describe_command: + if config.git_describe_command is not None: describe_command = config.git_describe_command - out, unused_err, ret = wd.do_ex(describe_command) - node_date = wd.get_head_date() or date.today() + if describe_command is not None: + out, _, ret = wd.do_ex(describe_command) + else: + out, _, ret = wd.default_describe() - if ret: + if ret == 0: + tag, distance, node, dirty = _git_parse_describe(out) + if distance == 0 and not dirty: + distance = None + else: # If 'git git_describe_command' failed, try to get the information otherwise. - branch, branch_err, branch_ret = wd.do_ex("git symbolic-ref --short HEAD") - - if branch_ret: - branch = None - - rev_node = wd.node() + tag = "0.0" + node = wd.node() + if node is None: + distance = 0 + else: + distance = wd.count_all_nodes() + node = "g" + node dirty = wd.is_dirty() - if rev_node is None: - return meta( - "0.0", - distance=0, - node_date=node_date, - dirty=dirty, - branch=branch, - config=config, - ) - - return meta( - "0.0", - distance=wd.count_all_nodes(), - node="g" + rev_node, - dirty=dirty, - branch=wd.get_branch(), - node_date=node_date, - config=config, - ) - else: - tag, number, node, dirty = _git_parse_describe(out) - - branch = wd.get_branch() - if number: - return meta( - tag, - config=config, - distance=number, - node=node, - dirty=dirty, - branch=branch, - node_date=node_date, - ) - else: - return meta( - tag, - config=config, - node=node, - node_date=node_date, - dirty=dirty, - branch=branch, - ) + branch = wd.get_branch() + node_date = wd.get_head_date() or date.today() + + return meta( + tag, + branch=branch, + node=node, + node_date=node_date, + distance=distance, + dirty=dirty, + config=config, + ) def _git_parse_describe(describe_output): @@ -190,3 +202,30 @@ tag, number, node = describe_output.rsplit("-", 2) number = int(number) return tag, number, node, dirty + + +def search_parent(dirname): + """ + Walk up the path to find the `.git` directory. + :param dirname: Directory from which to start searching. + """ + + # Code based on: + # https://github.com/gitpython-developers/GitPython/blob/main/git/repo/base.py + + curpath = os.path.abspath(dirname) + + while curpath: + + try: + wd = GitWorkdir.from_potential_worktree(curpath) + except Exception: + wd = None + + if wd is not None: + return wd + + curpath, tail = os.path.split(curpath) + + if not tail: + return None
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/hacks.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/hacks.py
Changed
@@ -1,6 +1,9 @@ import os -from .utils import data_from_mime, trace -from .version import tag_to_version, meta + +from .utils import data_from_mime +from .utils import trace +from .version import meta +from .version import tag_to_version def parse_pkginfo(root, config=None): @@ -34,4 +37,5 @@ if version is not None: return meta(str(version), preformatted=True, config=config) if config.fallback_version is not None: + trace("FALLBACK") return meta(config.fallback_version, preformatted=True, config=config)
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/hg.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/hg.py
Changed
@@ -1,94 +1,151 @@ import os +from pathlib import Path + from .config import Configuration -from .utils import do, trace, data_from_mime, require_command -from .version import meta, tags_to_versions - - -def _hg_tagdist_normalize_tagcommit(config, tag, dist, node, branch): - dirty = node.endswith("+") - node = "h" + node.strip("+") - - # Detect changes since the specified tag - revset = ( - "(branch(.)" # look for revisions in this branch only - " and tag({tag!r})::." # after the last tag - # ignore commits that only modify .hgtags and nothing else: - " and (merge() or file('re:^(?!\\.hgtags).*$'))" - " and not tag({tag!r}))" # ignore the tagged commit itself - ).format(tag=tag) - if tag != "0.0": - commits = do( - "hg", "log", "-r", revset, "--template", "{node|short}", - config.absolute_root, - ) - else: - commits = True - trace("normalize", locals()) - if commits or dirty: - return meta( - tag, distance=dist, node=node, dirty=dirty, branch=branch, config=config +from .scm_workdir import Workdir +from .utils import data_from_mime +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta +from .version import tag_to_version + + +class HgWorkdir(Workdir): + + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd): + require_command(cls.COMMAND) + root, err, ret = do_ex("hg root", wd) + if ret: + return + return cls(root) + + def get_meta(self, config): + + node, tags, bookmark, node_date = self.hg_log( + ".", "{node}\n{tag}\n{bookmark}\n{date|shortdate}" + ).split("\n") + + # TODO: support bookmarks and topics (but nowadays bookmarks are + # mainly used to emulate Git branches, which is already supported with + # the dedicated class GitWorkdirHgClient) + + branch, dirty, dirty_date = self.do( + "hg", "id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}" + ).split("\n") + dirty = bool(int(dirty)) + + if dirty: + date = dirty_date + else: + date = node_date + + if all(c == "0" for c in node): + trace("initial node", self.path) + return meta("0.0", config=config, dirty=dirty, branch=branch) + + node = "h" + node:7 + + tags = tags.split() + if "tip" in tags: + # tip is not a real tag + tags = tags.remove("tip") + + if tags: + tag = tags0 + tag = tag_to_version(tag) + if tag: + return meta(tag, dirty=dirty, branch=branch, config=config) + + try: + tag = self.get_latest_normalizable_tag() + dist = self.get_distance_revs(tag) + if tag == "null": + tag = "0.0" + dist = int(dist) + 1 + + if self.check_changes_since_tag(tag) or dirty: + return meta( + tag, + distance=dist, + node=node, + dirty=dirty, + branch=branch, + config=config, + node_date=date, + ) + else: + return meta(tag, config=config) + + except ValueError: + pass # unpacking failed, old hg + + def hg_log(self, revset, template): + cmd = "hg", "log", "-r", revset, "-T", template + return self.do(cmd) + + def get_latest_normalizable_tag(self): + # Gets all tags containing a '.' (see #229) from oldest to newest + outlines = self.hg_log( + revset="ancestors(.) and tag('re:\\.')", + template="{tags}{if(tags, '\n', '')}", + ).split() + if not outlines: + return "null" + tag = outlines-1.split()-1 + return tag + + def get_distance_revs(self, rev1, rev2="."): + revset = f"({rev1}::{rev2})" + out = self.hg_log(revset, ".") + return len(out) - 1 + + def check_changes_since_tag(self, tag): + + if tag == "0.0": + return True + + revset = ( + "(branch(.)" # look for revisions in this branch only + f" and tag({tag!r})::." # after the last tag + # ignore commits that only modify .hgtags and nothing else: + " and (merge() or file('re:^(?!\\.hgtags).*$'))" + f" and not tag({tag!r}))" # ignore the tagged commit itself ) - else: - return meta(tag, config=config) + + return bool(self.hg_log(revset, ".")) def parse(root, config=None): if not config: config = Configuration(root=root) - require_command("hg") - identity_data = do("hg id -i -b -t", config.absolute_root).split() - if not identity_data: + if os.path.exists(os.path.join(root, ".hg/git")): + paths, _, ret = do_ex("hg path", root) + if not ret: + for line in paths.split("\n"): + if line.startswith("default ="): + path = Path(line.split()2) + if path.name.endswith(".git") or (path / ".git").exists(): + from .git import _git_parse_inner + from .hg_git import GitWorkdirHgClient + + wd = GitWorkdirHgClient.from_potential_worktree(root) + if wd: + return _git_parse_inner(config, wd) + + wd = HgWorkdir.from_potential_worktree(config.absolute_root) + + if wd is None: return - node = identity_data.pop(0) - branch = identity_data.pop(0) - if "tip" in identity_data: - # tip is not a real tag - identity_data.remove("tip") - tags = tags_to_versions(identity_data) - dirty = node-1 == "+" - if tags: - return meta(tags0, dirty=dirty, branch=branch, config=config) - - if node.strip("+") == "0" * 12: - trace("initial node", config.absolute_root) - return meta("0.0", config=config, dirty=dirty, branch=branch) - - try: - tag = get_latest_normalizable_tag(config.absolute_root) - dist = get_graph_distance(config.absolute_root, tag) - if tag == "null": - tag = "0.0" - dist = int(dist) + 1 - return _hg_tagdist_normalize_tagcommit(config, tag, dist, node, branch) - except ValueError: - pass # unpacking failed, old hg - - -def get_latest_normalizable_tag(root): - # Gets all tags containing a '.' (see #229) from oldest to newest - cmd = - "hg", - "log", - "-r", - "ancestors(.) and tag('re:\\.')", - "--template", - "{tags}\n", - - outlines = do(cmd, root).split() - if not outlines: - return "null" - tag = outlines-1.split()-1 - return tag - - -def get_graph_distance(root, rev1, rev2="."): - cmd = "hg", "log", "-q", "-r", f"{rev1}::{rev2}" - out = do(cmd, root) - return len(out.strip().splitlines()) - 1 - - -def archival_to_version(data, config=None): + + return wd.get_meta(config) + + +def archival_to_version(data, config: "Configuration | None" = None): trace("data", data) node = data.get("node", ""):12 if node:
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/hg_git.py
Added
@@ -0,0 +1,133 @@ +import os +from datetime import datetime + +from .git import GitWorkdir +from .hg import HgWorkdir +from .utils import do_ex +from .utils import require_command +from .utils import trace + + +class GitWorkdirHgClient(GitWorkdir, HgWorkdir): + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd): + require_command(cls.COMMAND) + root, err, ret = do_ex("hg root", wd) + if ret: + return + return cls(root) + + def is_dirty(self): + out, _, _ = self.do_ex("hg id -T '{dirty}'") + return bool(out) + + def get_branch(self): + branch, err, ret = self.do_ex("hg id -T {bookmarks}") + if ret: + trace("branch err", branch, err, ret) + return + return branch + + def get_head_date(self): + date_part, err, ret = self.do_ex("hg log -r . -T {shortdate(date)}") + if ret: + trace("head date err", date_part, err, ret) + return + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self): + return False + + def fetch_shallow(self): + pass + + def get_hg_node(self): + node, _, ret = self.do_ex("hg log -r . -T {node}") + if not ret: + return node + + def _hg2git(self, hg_node): + git_node = None + with open(os.path.join(self.path, ".hg/git-mapfile")) as file: + for line in file: + if hg_node in line: + git_node, hg_node = line.split() + break + return git_node + + def node(self): + hg_node = self.get_hg_node() + if hg_node is None: + return + + git_node = self._hg2git(hg_node) + + if git_node is None: + # trying again after hg -> git + self.do_ex("hg gexport") + git_node = self._hg2git(hg_node) + + if git_node is None: + trace("Cannot get git node so we use hg node", hg_node) + + if hg_node == "0" * len(hg_node): + # mimic Git behavior + return None + + return hg_node + + return git_node:7 + + def count_all_nodes(self): + revs, _, _ = self.do_ex("hg log -r 'ancestors(.)' -T '.'") + return len(revs) + + def default_describe(self): + """ + Tentative to reproduce the output of + + `git describe --dirty --tags --long --match *0-9*` + + """ + hg_tags, _, ret = self.do_ex( + + "hg", + "log", + "-r", + "(reverse(ancestors(.)) and tag(r're:0-9'))", + "-T", + "{tags}{if(tags, ' ', '')}", + + ) + if ret: + return None, None, None + hg_tags = hg_tags.split() + + if not hg_tags: + return None, None, None + + git_tags = {} + with open(os.path.join(self.path, ".hg/git-tags")) as file: + for line in file: + node, tag = line.split() + git_tagstag = node + + # find the first hg tag which is also a git tag + for tag in hg_tags: + if tag in git_tags: + break + + out, _, ret = self.do_ex("hg", "log", "-r", f"'{tag}'::.", "-T", ".") + if ret: + return None, None, None + distance = len(out) - 1 + + node = self.node() + desc = f"{tag}-{distance}-g{node}" + + if self.is_dirty(): + desc += "-dirty" + + return desc, None, 0
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/integration.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/integration.py
Changed
@@ -1,10 +1,59 @@ -from pkg_resources import iter_entry_points +import os +import warnings -from .utils import do, trace_exception, trace -from . import _get_version, Configuration +import setuptools +from . import _get_version +from . import _version_missing +from .config import _read_dist_name_from_setup_cfg +from .config import Configuration +from .utils import do +from .utils import iter_entry_points +from .utils import trace -def version_keyword(dist, keyword, value): + +def _warn_on_old_setuptools(_version=setuptools.__version__): + if int(_version.split(".")0) < 45: + warnings.warn( + RuntimeWarning( + f""" +ERROR: setuptools=={_version} is used in combination with setuptools_scm>=6.x + +Your build configuration is incomplete and previously worked by accident! + + +This happens as setuptools is unable to replace itself when a activated build dependency +requires a more recent setuptools version +(it does not respect "setuptools>X" in setup_requires). + + +setuptools>=31 is required for setup.cfg metadata support +setuptools>=42 is required for pyproject.toml configuration support + +Suggested workarounds if applicable: + - preinstalling build dependencies like setuptools_scm before running setup.py + - installing setuptools_scm using the system package manager to ensure consistency + - migrating from the deprecated setup_requires mechanism to pep517/518 + and using a pyproject.toml to declare build dependencies + which are reliably pre-installed before running the build tools +""" + ) + ) + + +_warn_on_old_setuptools() + + +def _assign_version(dist: setuptools.Distribution, config: Configuration): + maybe_version = _get_version(config) + + if maybe_version is None: + _version_missing(config) + else: + dist.metadata.version = maybe_version + + +def version_keyword(dist: setuptools.Distribution, keyword, value): if not value: return if value is True: @@ -19,9 +68,11 @@ "version keyword", vars(dist.metadata), ) - dist_name = dist.metadata.name + dist_name = dist.metadata.name # type: str | None + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() config = Configuration(dist_name=dist_name, **value) - dist.metadata.version = _get_version(config) + _assign_version(dist, config) def find_files(path=""): @@ -37,23 +88,17 @@ return -def _args_from_toml(name="pyproject.toml"): - # todo: more sensible config initialization - # move this helper back to config and unify it with the code from get_config - - with open(name) as strm: - defn = __import__("toml").load(strm) - return defn.get("tool", {})"setuptools_scm" - - -def infer_version(dist): +def infer_version(dist: setuptools.Distribution): trace( "finalize hook", vars(dist.metadata), ) dist_name = dist.metadata.name + if not os.path.isfile("pyproject.toml"): + return try: config = Configuration.from_file(dist_name=dist_name) - except Exception: - return trace_exception() - dist.metadata.version = _get_version(config) + except LookupError as e: + trace(e) + else: + _assign_version(dist, config)
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/scm_workdir.py
Added
@@ -0,0 +1,15 @@ +from .utils import do +from .utils import do_ex +from .utils import require_command + + +class Workdir: + def __init__(self, path): + require_command(self.COMMAND) + self.path = path + + def do_ex(self, cmd): + return do_ex(cmd, cwd=self.path) + + def do(self, cmd): + return do(cmd, cwd=self.path)
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/utils.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/utils.py
Changed
@@ -2,13 +2,12 @@ utils """ import inspect -import warnings -import sys -import shlex -import subprocess import os import platform -import traceback +import shlex +import subprocess +import sys +import warnings DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) @@ -36,14 +35,9 @@ } -def trace(*k): +def trace(*k) -> None: if DEBUG: - print(*k) - sys.stdout.flush() - - -def trace_exception(): - DEBUG and traceback.print_exc() + print(*k, file=sys.stderr, flush=True) def ensure_stripped_str(str_or_bytes): @@ -84,6 +78,7 @@ def do_ex(cmd, cwd="."): trace("cmd", repr(cmd)) + trace(" in", cwd) if os.name == "posix" and not isinstance(cmd, (list, tuple)): cmd = shlex.split(cmd) @@ -123,7 +118,7 @@ return argname in argspec -def has_command(name, warn=True): +def has_command(name: str, warn: bool = True) -> bool: try: p = _popen_pipes(name, "help", ".") except OSError: @@ -140,3 +135,10 @@ def require_command(name): if not has_command(name, warn=False): raise OSError("%r was not found" % name) + + +def iter_entry_points(*k, **kw): + + from ._entrypoints import iter_entry_points + + return iter_entry_points(*k, **kw)
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/src/setuptools_scm/version.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/src/setuptools_scm/version.py
Changed
@@ -1,21 +1,19 @@ -import datetime -import warnings -import re -import time import os +import re +import warnings +from datetime import datetime +from datetime import timezone +from typing import Callable +from typing import Iterator +from typing import List +from typing import overload +from typing import Tuple from .config import Configuration +from .config import Version as PkgVersion +from .utils import iter_entry_points from .utils import trace -try: - from packaging.version import Version -except ImportError: - import pkg_resources - - Version = pkg_resources.packaging.version.Version - - -from pkg_resources import iter_entry_points SEMVER_MINOR = 2 SEMVER_PATCH = 3 @@ -23,7 +21,7 @@ def _parse_version_tag(tag, config): - tagstring = tag if not isinstance(tag, str) else str(tag) + tagstring = tag if isinstance(tag, str) else str(tag) match = config.tag_regex.match(tagstring) result = None @@ -54,7 +52,7 @@ return ep.load() -def tag_to_version(tag, config=None): +def tag_to_version(tag, config: "Configuration | None" = None): """ take a tag that might be prefixed with a keyword and return only the version part :param config: optional configuration object @@ -79,7 +77,7 @@ ) ) - version = Version(version) + version = config.version_cls(version) trace("version", repr(version)) return version @@ -102,13 +100,13 @@ class ScmVersion: def __init__( self, - tag_version, - distance=None, - node=None, - dirty=False, - preformatted=False, - branch=None, - config=None, + tag_version: str, + distance: "int|None" = None, + node: "str|None" = None, + dirty: bool = False, + preformatted: bool = False, + branch: "str|None" = None, + config: "Configuration|None" = None, node_date=None, **kw, ): @@ -120,9 +118,11 @@ self.distance = distance self.node = node self.node_date = node_date - self.time = datetime.datetime.utcfromtimestamp( - int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) - ) + if "SOURCE_DATE_EPOCH" in os.environ: + date_epoch = int(os.environ"SOURCE_DATE_EPOCH") + self.time = datetime.fromtimestamp(date_epoch, timezone.utc) + else: + self.time = datetime.now(timezone.utc) self._extra = kw self.dirty = dirty self.preformatted = preformatted @@ -167,24 +167,24 @@ return self.format_with(fmt, guessed=guessed) -def _parse_tag(tag, preformatted, config): +def _parse_tag(tag, preformatted, config: "Configuration|None"): if preformatted: return tag - if not isinstance(tag, Version): + if config is None or not isinstance(tag, config.version_cls): tag = tag_to_version(tag, config) return tag def meta( tag, - distance=None, - dirty=False, - node=None, - preformatted=False, - branch=None, - config=None, + distance: "int|None" = None, + dirty: bool = False, + node: "str|None" = None, + preformatted: bool = False, + branch: "str|None" = None, + config: "Configuration|None" = None, **kw, -): +) -> ScmVersion: if not config: warnings.warn( "meta invoked without explicit configuration," @@ -198,32 +198,45 @@ ) -def guess_next_version(tag_version): +def guess_next_version(tag_version: ScmVersion): version = _strip_local(str(tag_version)) return _bump_dev(version) or _bump_regex(version) -def _strip_local(version_string): +def _dont_guess_next_version(tag_version: ScmVersion): + version = _strip_local(str(tag_version)) + return _bump_dev(version) or _add_post(version) + + +def _strip_local(version_string: str) -> str: public, sep, local = version_string.partition("+") return public -def _bump_dev(version): +def _add_post(version: str): + if "post" in version: + raise ValueError( + f"{version} already is a post release, refusing to guess the update" + ) + return f"{version}.post1" + + +def _bump_dev(version: str) -> "str | None": if ".dev" not in version: - return + return None prefix, tail = version.rsplit(".dev", 1) if tail != "0": raise ValueError( "choosing custom numbers for the `.devX` distance " "is not supported.\n " - "The {version} can't be bumped\n" - "Please drop the tag or create a new supported one".format(version=version) + f"The {version} can't be bumped\n" + "Please drop the tag or create a new supported one ending in .dev0" ) return prefix -def _bump_regex(version): +def _bump_regex(version: str) -> str: match = re.match(r"(.*?)(\d+)$", version) if match is None: raise ValueError( @@ -235,14 +248,14 @@ return "%s%d" % (prefix, int(tail) + 1) -def guess_next_dev_version(version): +def guess_next_dev_version(version: ScmVersion): if version.exact: return version.format_with("{tag}") else: return version.format_next_version(guess_next_version) -def guess_next_simple_semver(version, retain, increment=True): +def guess_next_simple_semver(version: str, retain: int, increment=True): try: parts = int(i) for i in str(version).split("."):retain except ValueError: @@ -277,11 +290,15 @@ # Does the branch name (stripped of namespace) parse as a version? branch_ver = _parse_version_tag(version.branch.split("/")-1, version.config) if branch_ver is not None: + branch_ver = branch_ver"version" + if branch_ver0 == "v": + # Allow branches that start with 'v', similar to Version. + branch_ver = branch_ver1: # Does the branch version up to the minor part match the tag? If not it # might be like, an issue number or something and not a version number, so # we only want to use it if it matches. tag_ver_up_to_minor = str(version.tag).split("."):SEMVER_MINOR - branch_ver_up_to_minor = branch_ver"version".split("."):SEMVER_MINOR + branch_ver_up_to_minor = branch_ver.split("."):SEMVER_MINOR if branch_ver_up_to_minor == tag_ver_up_to_minor: # We're in a release/maintenance branch, next is a patch/rc/beta bump: return version.format_next_version(guess_next_version) @@ -299,11 +316,11 @@ return release_branch_semver_version(version) -def no_guess_dev_version(version): +def no_guess_dev_version(version: ScmVersion): if version.exact: return version.format_with("{tag}") else: - return version.format_with("{tag}.post1.dev{distance}") + return version.format_next_version(_dont_guess_next_version) def date_ver_match(ver): @@ -317,7 +334,7 @@ return match -def guess_next_date_ver(version, node_date=None, date_fmt=None): +def guess_next_date_ver(version, node_date=None, date_fmt=None, version_cls=None): """ same-day -> patch +1 other-day -> today @@ -326,21 +343,28 @@ """ match = date_ver_match(version) if match is None: - raise ValueError( - "{version} does not correspond to a valid versioning date, " - "please correct or use a custom version scheme".format(version=version) + warnings.warn( + f"{version} does not correspond to a valid versioning date, " + "assuming legacy version" ) + if date_fmt is None: + date_fmt = "%y.%m.%d" + # deduct date format if not provided if date_fmt is None: date_fmt = "%Y.%m.%d" if len(match.group("year")) == 4 else "%y.%m.%d" - head_date = node_date or datetime.date.today() + today = datetime.now(timezone.utc).date() + head_date = node_date or today # compute patch - tag_date = datetime.datetime.strptime(match.group("date"), date_fmt).date() + if match is None: + tag_date = today + else: + tag_date = datetime.strptime(match.group("date"), date_fmt).date() if tag_date == head_date: - patch = match.group("patch") or "0" + patch = "0" if match is None else (match.group("patch") or "0") patch = int(patch) + 1 else: - if tag_date > head_date: + if tag_date > head_date and match is not None: # warn on future times warnings.warn( "your previous tag ({}) is ahead your node date ({})".format( @@ -352,8 +376,9 @@ node_date=head_date, date_fmt=date_fmt, patch=patch ) # rely on the Version object to ensure consistency (e.g. remove leading 0s) - # TODO: support for intentionally non-normalized date versions - next_version = str(Version(next_version)) + if version_cls is None: + version_cls = PkgVersion + next_version = str(version_cls(next_version)) return next_version @@ -368,7 +393,11 @@ match = date_ver_match(ver) if match: return ver - return version.format_next_version(guess_next_date_ver, node_date=version.node_date) + return version.format_next_version( + guess_next_date_ver, + node_date=version.node_date, + version_cls=version.config.version_cls, + ) def _format_local_with_time(version, time_format): @@ -412,7 +441,9 @@ return ep.load() -def _iter_version_schemes(entrypoint, scheme_value, _memo=None): +def _iter_version_schemes( + entrypoint: str, scheme_value: "str|Liststr|Tuplestr, ...", _memo=None +) -> IteratorCallable"ScmVersion", str: if _memo is None: _memo = set() if isinstance(scheme_value, str): @@ -427,7 +458,23 @@ yield scheme_value -def _call_version_scheme(version, entypoint, given_value, default): +@overload +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: str +) -> str: + ... + + +@overload +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: None +) -> "str|None": + ... + + +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: "str|None" +) -> "str|None": for scheme in _iter_version_schemes(entypoint, given_value): result = scheme(version) if result is not None: @@ -435,7 +482,7 @@ return default -def format_version(version, **config): +def format_version(version: ScmVersion, **config) -> str: trace("scm version", version) trace("config", config) if version.preformatted:
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/Dockerfile.busted-buster
Added
@@ -0,0 +1,3 @@ +FROM debian:buster +RUN apt-get update -q && apt-get install -yq python3-pip python3-setuptools +RUN printf "easy_install\nallow_hosts=localhost\nfind_links=/dist\n" > /root/.pydistutils.cfg
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/check_self_install.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/check_self_install.py
Changed
@@ -1,4 +1,5 @@ import pkg_resources + import setuptools_scm dist = pkg_resources.get_distribution("setuptools_scm")
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/conftest.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/conftest.py
Changed
@@ -1,5 +1,6 @@ -import os import itertools +import os + import pytest # 2009-02-13T23:31:30+00:00 @@ -19,6 +20,13 @@ return res +def pytest_addoption(parser): + group = parser.getgroup("setuptools_scm") + group.addoption( + "--test-legacy", dest="scm_test_virtualenv", default=False, action="store_true" + ) + + class Wd: commit_command = None add_command = None @@ -49,7 +57,7 @@ def _reason(self, given_reason): if given_reason is None: - return "number-{c}".format(c=next(self.__counter)) + return f"number-{next(self.__counter)}" else: return given_reason @@ -95,3 +103,32 @@ target_wd = tmp_path.resolve() / "wd" target_wd.mkdir() return Wd(target_wd) + + +@pytest.fixture +def repositories_hg_git(tmp_path): + from setuptools_scm.utils import do + + tmp_path = tmp_path.resolve() + path_git = tmp_path / "repo_git" + path_git.mkdir() + + wd = Wd(path_git) + wd("git init") + wd("git config user.email test@example.com") + wd('git config user.name "a test"') + wd.add_command = "git add ." + wd.commit_command = "git commit -m test-{reason}" + + path_hg = tmp_path / "repo_hg" + do(f"hg clone {path_git} {path_hg} --config extensions.hggit=") + assert path_hg.exists() + + with open(path_hg / ".hg/hgrc", "a") as file: + file.write("extensions\nhggit =\n") + + wd_hg = Wd(path_hg) + wd_hg.add_command = "hg add ." + wd_hg.commit_command = 'hg commit -m test-{reason} -u test -d "0 0"' + + return wd_hg, wd
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_basic_api.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_basic_api.py
Changed
@@ -1,18 +1,22 @@ import os +import shutil import sys -import py +from pathlib import Path + import pytest import setuptools_scm from setuptools_scm import dump_version -from setuptools_scm.utils import data_from_mime, do +from setuptools_scm.utils import data_from_mime +from setuptools_scm.utils import do +from setuptools_scm.version import ScmVersion @pytest.mark.parametrize("cmd", "ls", "dir") -def test_do(cmd, tmpdir): - if not py.path.local.sysfind(cmd): +def test_do(cmd, tmp_path: Path): + if not shutil.which(cmd): pytest.skip(cmd + " not found") - do(cmd, str(tmpdir)) + do(cmd, cwd=tmp_path) def test_data_from_mime(tmpdir): @@ -39,6 +43,7 @@ def assertion(config): assert config.absolute_root == expected_root + return ScmVersion("1.0") monkeypatch.setattr(setuptools_scm, "_do_parse", assertion) @@ -53,9 +58,12 @@ setuptools_scm.version_from_scm(str(wd)) -def test_root_parameter_pass_by(monkeypatch, tmpdir): - assert_root(monkeypatch, tmpdir) - setuptools_scm.get_version(root=tmpdir.strpath) +def test_root_parameter_pass_by(monkeypatch: pytest.MonkeyPatch, tmp_path: Path): + assert_root(monkeypatch, os.fspath(tmp_path)) + setuptools_scm.get_version(root=os.fspath(tmp_path)) + setuptools_scm.get_version( + os.fspath(tmp_path) + ) # issue 669 - posarg difference between Configuration and get_version def test_parentdir_prefix(tmpdir, monkeypatch): @@ -90,31 +98,51 @@ assert setuptools_scm.get_version() == version -def test_root_relative_to(monkeypatch, tmp_path): +def test_root_relative_to(monkeypatch: pytest.MonkeyPatch, tmp_path: Path): + tmp_path.joinpath("setup.cfg").touch() assert_root(monkeypatch, str(tmp_path / "alt")) module = tmp_path / "module/file.py" module.parent.mkdir() module.touch() - setuptools_scm.get_version(root="../alt", relative_to=str(module)) + + setuptools_scm.get_version( + root="../alt", + relative_to=str(module), + ) with pytest.warns(UserWarning, match="relative_to is expected to be a file.*"): - setuptools_scm.get_version(root="../alt", relative_to=str(module.parent)) + setuptools_scm.get_version( + root="../alt", + relative_to=str(module.parent), + ) + +def test_dump_version(tmp_path: Path): -def test_dump_version(tmpdir): - sp = tmpdir.strpath + dump_version(tmp_path, "1.0", "first.txt") - dump_version(sp, "1.0", "first.txt") - assert tmpdir.join("first.txt").read() == "1.0" + def read(name: str) -> str: + return tmp_path.joinpath(name).read_text() - dump_version(sp, "1.0.dev42", "first.py") - content = tmpdir.join("first.py").read() - lines = content.splitlines() + assert read("first.txt") == "1.0" + + dump_version(tmp_path, "1.0.dev42", "first.py") + lines = read("first.py").splitlines() assert "version = '1.0.dev42'" in lines assert "version_tuple = (1, 0, 'dev42')" in lines + dump_version(tmp_path, "1.0.1+g4ac9d2c", "second.py") + lines = read("second.py").splitlines() + assert "version = '1.0.1+g4ac9d2c'" in lines + assert "version_tuple = (1, 0, 1, 'g4ac9d2c')" in lines + + dump_version(tmp_path, "1.2.3.dev18+gb366d8b.d20210415", "third.py") + lines = read("third.py").splitlines() + assert "version = '1.2.3.dev18+gb366d8b.d20210415'" in lines + assert "version_tuple = (1, 2, 3, 'dev18', 'gb366d8b.d20210415')" in lines + import ast - ast.parse(content) + ast.parse(read("third.py")) def test_parse_plain_fails(recwarn): @@ -123,3 +151,24 @@ with pytest.raises(TypeError): setuptools_scm.get_version(parse=parse) + + +def test_custom_version_cls(): + """Test that `normalize` and `version_cls` work as expected""" + + class MyVersion: + def __init__(self, tag_str: str): + self.version = tag_str + + def __repr__(self): + return f"hello,{self.version}" + + # you can not use normalize=False and version_cls at the same time + with pytest.raises(ValueError): + setuptools_scm.get_version(normalize=False, version_cls=MyVersion) + + # TODO unfortunately with PRETEND_KEY the preformatted flag becomes True + # which bypasses our class. which other mechanism would be ok to use here + # to create a test? + # monkeypatch.setenv(setuptools_scm.PRETEND_KEY, "1.0.1") + # assert setuptools_scm.get_version(version_cls=MyVersion) == "1"
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_config.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_config.py
Changed
@@ -1,7 +1,10 @@ -from setuptools_scm.config import Configuration import re +import textwrap + import pytest +from setuptools_scm.config import Configuration + @pytest.mark.parametrize( "tag, expected_version", @@ -27,7 +30,17 @@ def test_config_from_pyproject(tmpdir): fn = tmpdir / "pyproject.toml" - fn.write_text("tool.setuptools_scm\n", encoding="utf-8") + fn.write_text( + textwrap.dedent( + """ + tool.setuptools_scm + project + description = "Factory ⸻ A code generator 🏭" + authors = {name = "Łukasz Langa"} + """ + ), + encoding="utf-8", + ) assert Configuration.from_file(str(fn))
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_file_finder.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_file_finder.py
Changed
@@ -31,7 +31,8 @@ bdir = wd.cwd / "bdir" bdir.mkdir() (bdir / "fileb").touch() - wd.add_and_commit() + if request.node.get_closest_marker("skip_commit") is None: + wd.add_and_commit() monkeypatch.chdir(wd.cwd) yield wd @@ -184,3 +185,9 @@ "data/datafile", } ) + + +@pytest.mark.issue(587) +@pytest.mark.skip_commit +def test_not_commited(inwd): + assert find_files() ==
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_functions.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_functions.py
Changed
@@ -1,15 +1,17 @@ -import pytest +from pathlib import Path + import pkg_resources -from setuptools_scm import dump_version, get_version, PRETEND_KEY -from setuptools_scm.version import ( - guess_next_version, - meta, - format_version, - tag_to_version, -) +import pytest +from setuptools_scm import dump_version +from setuptools_scm import get_version +from setuptools_scm import PRETEND_KEY from setuptools_scm.config import Configuration from setuptools_scm.utils import has_command +from setuptools_scm.version import format_version +from setuptools_scm.version import guess_next_version +from setuptools_scm.version import meta +from setuptools_scm.version import tag_to_version @pytest.mark.parametrize( @@ -61,27 +63,28 @@ assert format_version(version, version_scheme=vs, local_scheme=ls) == expected -def test_dump_version_doesnt_bail_on_value_error(tmpdir): +def test_dump_version_doesnt_bail_on_value_error(tmp_path): write_to = "VERSION" version = str(VERSIONS"exact".tag) - with pytest.raises(ValueError) as exc_info: - dump_version(tmpdir.strpath, version, write_to) - assert str(exc_info.value).startswith("bad file format:") + with pytest.raises(ValueError, match="^bad file format:"): + dump_version(tmp_path, version, write_to) @pytest.mark.parametrize( "version", "1.0", "1.2.3.dev1+ge871260", "1.2.3.dev15+ge871260.d20180625" ) -def test_dump_version_works_with_pretend(version, tmpdir, monkeypatch): +def test_dump_version_works_with_pretend( + version: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv(PRETEND_KEY, version) - get_version(write_to=str(tmpdir.join("VERSION.txt"))) - assert tmpdir.join("VERSION.txt").read() == version + target = tmp_path.joinpath("VERSION.txt") + get_version(write_to=target) + assert target.read_text() == version -def test_has_command(recwarn): - assert not has_command("yadayada_setuptools_aint_ne") - msg = recwarn.pop() - assert "yadayada" in str(msg.message) +def test_has_command() -> None: + with pytest.warns(RuntimeWarning, match="yadayada"): + assert not has_command("yadayada_setuptools_aint_ne") @pytest.mark.parametrize( @@ -92,6 +95,6 @@ pytest.param("3.3.1-rc26", "3.3.1rc26", marks=pytest.mark.issue(266)), , ) -def test_tag_to_version(tag, expected_version): +def test_tag_to_version(tag: str, expected_version: str) -> None: version = str(tag_to_version(tag)) assert version == expected_version
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_git.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_git.py
Changed
@@ -1,13 +1,20 @@ -import sys import os -from setuptools_scm import integration -from setuptools_scm.utils import do, has_command -from setuptools_scm import git -import pytest +import sys +from datetime import date from datetime import datetime +from datetime import timezone from os.path import join as opj +from unittest.mock import Mock +from unittest.mock import patch + +import pytest + +from setuptools_scm import git +from setuptools_scm import integration +from setuptools_scm import NonNormalizedVersion from setuptools_scm.file_finder_git import git_find_files -from datetime import date +from setuptools_scm.utils import do +from setuptools_scm.utils import has_command pytestmark = pytest.mark.skipif( @@ -52,6 +59,19 @@ assert res == "0.1.dev0" +def test_root_search_parent_directories(tmpdir, wd, monkeypatch): + monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") + p = wd.cwd.joinpath("sub/package") + p.mkdir(parents=True) + p.joinpath("setup.py").write_text( + """from setuptools import setup +setup(use_scm_version={"search_parent_directories": True}) +""" + ) + res = do((sys.executable, "setup.py", "--version"), p) + assert res == "0.1.dev0" + + def test_git_gone(wd, monkeypatch): monkeypatch.setenv("PATH", str(wd.cwd / "not-existing")) with pytest.raises(EnvironmentError, match="'git' was not found"): @@ -104,6 +124,70 @@ wd("git tag 17.33.0-rc") assert wd.version == "17.33.0rc0" + # custom normalization + assert wd.get_version(normalize=False) == "17.33.0-rc" + assert wd.get_version(version_cls=NonNormalizedVersion) == "17.33.0-rc" + assert ( + wd.get_version(version_cls="setuptools_scm.NonNormalizedVersion") + == "17.33.0-rc" + ) + + +@pytest.mark.parametrize("with_class", False, type, str) +def test_git_version_unnormalized_setuptools(with_class, tmpdir, wd, monkeypatch): + """ + Test that when integrating with setuptools without normalization, + the version is not normalized in write_to files, + but still normalized by setuptools for the final dist metadata. + """ + monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") + p = wd.cwd + + # create a setup.py + dest_file = str(tmpdir.join("VERSION.txt")).replace("\\", "/") + if with_class is False: + # try normalize = False + setup_py = """ +from setuptools import setup +setup(use_scm_version={'normalize': False, 'write_to': '%s'}) +""" + elif with_class is type: + # custom non-normalizing class + setup_py = """ +from setuptools import setup + +class MyVersion: + def __init__(self, tag_str: str): + self.version = tag_str + + def __repr__(self): + return self.version + +setup(use_scm_version={'version_cls': MyVersion, 'write_to': '%s'}) +""" + elif with_class is str: + # non-normalizing class referenced by name + setup_py = """from setuptools import setup +setup(use_scm_version={ + 'version_cls': 'setuptools_scm.NonNormalizedVersion', + 'write_to': '%s' +}) +""" + + # finally write the setup.py file + p.joinpath("setup.py").write_text(setup_py % dest_file) + + # do git operations and tag + wd.commit_testfile() + wd("git tag 17.33.0-rc1") + + # setuptools still normalizes using packaging.Version (removing the dash) + res = do((sys.executable, "setup.py", "--version"), p) + assert res == "17.33.0rc1" + + # but the version tag in the file is non-normalized (with the dash) + assert tmpdir.join("VERSION.txt").read() == "17.33.0-rc1" + @pytest.mark.issue(179) def test_unicode_version_scheme(wd): @@ -132,7 +216,7 @@ assert wd.version.startswith("0.1.dev1") if today: # the date on the tag is in UTC - tag = datetime.utcnow().date().strftime(".d%Y%m%d") + tag = datetime.now(timezone.utc).date().strftime(".d%Y%m%d") else: tag = ".d20090213" # we are dirty, check for the tag @@ -140,12 +224,14 @@ @pytest.mark.issue(193) -def test_git_worktree_support(wd, tmpdir): +@pytest.mark.xfail(reason="sometimes relative path results") +def test_git_worktree_support(wd, tmp_path): wd.commit_testfile() - worktree = tmpdir.join("work_tree") + worktree = tmp_path / "work_tree" wd("git worktree add -b work-tree %s" % worktree) res = do(sys.executable, "-m", "setuptools_scm", "ls", cwd=worktree) + assert "test.txt" in res assert str(worktree) in res @@ -216,7 +302,7 @@ @pytest.mark.issue(228) def test_git_archive_subdirectory(wd, monkeypatch): - wd("mkdir foobar") + os.mkdir(wd.cwd / "foobar") wd.write("foobar/test1.txt", "test") wd("git add foobar") wd.commit() @@ -226,7 +312,7 @@ @pytest.mark.issue(251) def test_git_archive_run_from_subdirectory(wd, monkeypatch): - wd("mkdir foobar") + os.mkdir(wd.cwd / "foobar") wd.write("foobar/test1.txt", "test") wd("git add foobar") wd.commit() @@ -284,7 +370,7 @@ @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/381") def test_gitdir(monkeypatch, wd): - """""" + """ """ wd.commit_testfile() normal = wd.version # git hooks set this and break subsequent setuptools_scm unless we clean @@ -307,3 +393,12 @@ assert git_wd.get_head_date() == today meta = git.parse(os.fspath(wd.cwd)) assert meta.node_date == today + + +def test_git_getdate_badgit( + wd, +): + wd.commit_testfile() + git_wd = git.GitWorkdir(os.fspath(wd.cwd)) + with patch.object(git_wd, "do_ex", Mock(return_value=("%cI", "", 0))): + assert git_wd.get_head_date() is None
View file
_service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_hg_git.py
Added
@@ -0,0 +1,79 @@ +import pytest + +from setuptools_scm.utils import do_ex +from setuptools_scm.utils import has_command + + +@pytest.fixture(scope="module", autouse=True) +def _check_hg_git(): + if not has_command("hg", warn=False): + pytest.skip("hg executable not found") + + python_hg, err, ret = do_ex("hg debuginstall --template {pythonexe}") + + if ret: + skip_no_hggit = True + else: + out, err, ret = do_ex(python_hg.strip(), "-c", "import hggit") + skip_no_hggit = bool(ret) + if skip_no_hggit: + pytest.skip("hg-git not installed") + + +def test_base(repositories_hg_git): + wd, wd_git = repositories_hg_git + + assert wd_git.version == "0.1.dev0" + assert wd.version == "0.1.dev0" + + wd_git.commit_testfile() + wd("hg pull -u") + + version_git = wd_git.version + version = wd.version + + assert version_git.startswith("0.1.dev1+g") + assert version.startswith("0.1.dev1+g") + + assert not version_git.endswith("1-") + assert not version.endswith("1-") + + wd_git("git tag v0.1") + wd("hg pull -u") + assert wd_git.version == "0.1" + assert wd.version == "0.1" + + wd_git.write("test.txt", "test2") + wd.write("test.txt", "test2") + assert wd_git.version.startswith("0.2.dev0+g") + assert wd.version.startswith("0.2.dev0+g") + + wd_git.commit_testfile() + wd("hg pull") + wd("hg up -C") + assert wd_git.version.startswith("0.2.dev1+g") + assert wd.version.startswith("0.2.dev1+g") + + wd_git("git tag version-0.2") + wd("hg pull -u") + assert wd_git.version.startswith("0.2") + assert wd.version.startswith("0.2") + + wd_git.commit_testfile() + wd_git("git tag version-0.2.post210+gbe48adfpost3+g0cc25f2") + wd("hg pull -u") + with pytest.warns( + UserWarning, match="tag '.*' will be stripped of its suffix '.*'" + ): + assert wd_git.version.startswith("0.2") + + with pytest.warns( + UserWarning, match="tag '.*' will be stripped of its suffix '.*'" + ): + assert wd.version.startswith("0.2") + + wd_git.commit_testfile() + wd_git("git tag 17.33.0-rc") + wd("hg pull -u") + assert wd_git.version == "17.33.0rc0" + assert wd.version == "17.33.0rc0"
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_integration.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_integration.py
Changed
@@ -1,9 +1,13 @@ +import os import sys +import textwrap import pytest +from setuptools_scm import PRETEND_KEY +from setuptools_scm import PRETEND_KEY_NAMED +from setuptools_scm.integration import _warn_on_old_setuptools from setuptools_scm.utils import do -from setuptools_scm import PRETEND_KEY, PRETEND_KEY_NAMED @pytest.fixture @@ -20,40 +24,81 @@ pytest.importorskip("toml") monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") pkg = tmpdir.ensure("package", dir=42) - pkg.join("pyproject.toml").write( - """tool.setuptools_scm -fallback_version = "12.34" -""" + pkg.join("pyproject.toml").write_text( + textwrap.dedent( + """ + tool.setuptools_scm + fallback_version = "12.34" + project + description = "Factory ⸻ A code generator 🏭" + authors = {name = "Łukasz Langa"} + """ + ), + encoding="utf-8", ) pkg.join("setup.py").write("__import__('setuptools').setup()") res = do((sys.executable, "setup.py", "--version"), pkg) assert res == "12.34" -def test_pyproject_support_with_git(tmpdir, monkeypatch, wd): - pytest.importorskip("toml") - pkg = tmpdir.join("wd") - pkg.join("pyproject.toml").write("""tool.setuptools_scm""") - pkg.join("setup.py").write( - "__import__('setuptools').setup(name='setuptools_scm_example')" - ) - res = do((sys.executable, "setup.py", "--version"), pkg) +PYPROJECT_FILES = { + "setup.py": "tool.setuptools_scm", + "setup.cfg": "tool.setuptools_scm", + "pyproject tool.setuptools_scm": ( + "tool.setuptools_scm\ndist_name='setuptools_scm_example'" + ), + "pyproject.project": ( + "project\nname='setuptools_scm_example'\ntool.setuptools_scm" + ), +} + +SETUP_PY_PLAIN = "__import__('setuptools').setup()" +SETUP_PY_WITH_NAME = "__import__('setuptools').setup(name='setuptools_scm_example')" + +SETUP_PY_FILES = { + "setup.py": SETUP_PY_WITH_NAME, + "setup.cfg": SETUP_PY_PLAIN, + "pyproject tool.setuptools_scm": SETUP_PY_PLAIN, + "pyproject.project": SETUP_PY_PLAIN, +} + +SETUP_CFG_FILES = { + "setup.py": "", + "setup.cfg": "metadata\nname=setuptools_scm_example", + "pyproject tool.setuptools_scm": "", + "pyproject.project": "", +} + +with_metadata_in = pytest.mark.parametrize( + "metadata_in", + "setup.py", "setup.cfg", "pyproject tool.setuptools_scm", "pyproject.project", +) + + +@with_metadata_in +def test_pyproject_support_with_git(wd, metadata_in): + pytest.importorskip("tomli") + wd.write("pyproject.toml", PYPROJECT_FILESmetadata_in) + wd.write("setup.py", SETUP_PY_FILESmetadata_in) + wd.write("setup.cfg", SETUP_CFG_FILESmetadata_in) + res = wd((sys.executable, "setup.py", "--version")) assert res.endswith("0.1.dev0") -def test_pretend_version(tmpdir, monkeypatch, wd): +def test_pretend_version(monkeypatch, wd): monkeypatch.setenv(PRETEND_KEY, "1.0.0") assert wd.get_version() == "1.0.0" assert wd.get_version(dist_name="ignored") == "1.0.0" -def test_pretend_version_named_pyproject_integration(tmpdir, monkeypatch, wd): - test_pyproject_support_with_git(tmpdir, monkeypatch, wd) +@with_metadata_in +def test_pretend_version_named_pyproject_integration(monkeypatch, wd, metadata_in): + test_pyproject_support_with_git(wd, metadata_in) monkeypatch.setenv( PRETEND_KEY_NAMED.format(name="setuptools_scm_example".upper()), "3.2.1" ) - res = do((sys.executable, "setup.py", "--version"), tmpdir / "wd") + res = wd((sys.executable, "setup.py", "--version")) assert res.endswith("3.2.1") @@ -68,3 +113,40 @@ monkeypatch.setenv(PRETEND_KEY_NAMED.format(name="test".upper()), "1.0.0") monkeypatch.setenv(PRETEND_KEY, "2.0.0") assert wd.get_version(dist_name="test") == "1.0.0" + + +def test_pretend_version_accepts_bad_string(monkeypatch, wd): + monkeypatch.setenv(PRETEND_KEY, "dummy") + wd.write("setup.py", SETUP_PY_PLAIN) + assert wd.get_version(write_to="test.py") == "dummy" + assert wd("python setup.py --version") == "0.0.0" + + +def test_own_setup_fails_on_old_python(monkeypatch): + monkeypatch.setattr("sys.version_info", (3, 5)) + monkeypatch.syspath_prepend(os.path.dirname(os.path.dirname(__file__))) + + import setup + + with pytest.raises( + RuntimeError, + match="support for python < 3.6 has been removed in setuptools_scm>=6.0.0", + ): + setup.scm_version() + + +def testwarn_on_broken_setuptools(): + _warn_on_old_setuptools("45") + with pytest.warns(RuntimeWarning, match="ERROR: setuptools==44"): + _warn_on_old_setuptools("44") + + +@pytest.mark.issue(611) +def test_distribution_procides_extras(): + try: + from importlib.metadata import distribution + except ImportError: + from importlib_metadata import distribution + + dist = distribution("setuptools_scm") + assert sorted(dist.metadata.get_all("Provides-Extra")) == "test", "toml"
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_main.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_main.py
Changed
@@ -1,4 +1,8 @@ import os.path +import sys +import textwrap + +import pytest def test_main(): @@ -8,3 +12,51 @@ with open(mainfile) as f: code = compile(f.read(), "__main__.py", "exec") exec(code) + + +@pytest.fixture +def repo(wd): + wd("git init") + wd("git config user.email user@host") + wd("git config user.name user") + wd.add_command = "git add ." + wd.commit_command = "git commit -m test-{reason}" + + wd.write("README.rst", "My example") + wd.add_and_commit() + wd("git tag v0.1.0") + + wd.write("file.txt", "file.txt") + wd.add_and_commit() + + return wd + + +def test_repo_with_config(repo): + pyproject = """\ + tool.setuptools_scm + version_scheme = "no-guess-dev" + + project + name = "example" + """ + repo.write("pyproject.toml", textwrap.dedent(pyproject)) + repo.add_and_commit() + res = repo((sys.executable, "-m", "setuptools_scm")) + assert res.startswith("0.1.0.post1.dev2") + + +def test_repo_without_config(repo): + res = repo((sys.executable, "-m", "setuptools_scm")) + assert res.startswith("0.1.1.dev1") + + +def test_repo_with_pyproject_missing_setuptools_scm(repo): + pyproject = """\ + project + name = "example" + """ + repo.write("pyproject.toml", textwrap.dedent(pyproject)) + repo.add_and_commit() + res = repo((sys.executable, "-m", "setuptools_scm")) + assert res.startswith("0.1.1.dev2")
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_mercurial.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_mercurial.py
Changed
@@ -1,9 +1,11 @@ +import pytest + from setuptools_scm import format_version -from setuptools_scm.hg import archival_to_version, parse from setuptools_scm import integration from setuptools_scm.config import Configuration +from setuptools_scm.hg import archival_to_version +from setuptools_scm.hg import parse from setuptools_scm.utils import has_command -import pytest pytestmark = pytest.mark.skipif( @@ -80,7 +82,7 @@ wd("hg up v0.1") assert wd.version == "0.1" - # commit originating from the taged revision + # commit originating from the tagged revision # that is not a actual tag wd.commit_testfile() assert wd.version.startswith("0.2.dev1+") @@ -94,7 +96,7 @@ def test_version_from_archival(wd): # entrypoints are unordered, - # cleaning the wd ensure this test wont break randomly + # cleaning the wd ensure this test won't break randomly wd.cwd.joinpath(".hg").rename(wd.cwd / ".nothg") wd.write(".hg_archival.txt", "node: 000000000000\n" "tag: 0.1\n") assert wd.version == "0.1" @@ -170,14 +172,14 @@ @pytest.mark.usefixtures("version_1_0") def test_latest_tag_detection(wd): """Tests that tags not containing a "." are ignored, the same as for git. - Note that will be superceded by the fix for pypa/setuptools_scm/issues/235 + Note that will be superseded by the fix for pypa/setuptools_scm/issues/235 """ wd('hg tag some-random-tag -u test -d "0 0"') assert wd.version == "1.0.0" @pytest.mark.usefixtures("version_1_0") -def test_feature_branch_increments_major(wd): +def test_feature_branch_increments_major(wd) -> None: wd.commit_testfile() assert wd.get_version(version_scheme="python-simplified-semver").startswith("1.0.1")
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_regressions.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_regressions.py
Changed
@@ -1,11 +1,12 @@ -import sys import subprocess +import sys + +import pytest from setuptools_scm import get_version from setuptools_scm.git import parse -from setuptools_scm.utils import do_ex, do - -import pytest +from setuptools_scm.utils import do +from setuptools_scm.utils import do_ex def test_pkginfo_noscmroot(tmpdir, monkeypatch):
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_setuptools_support.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_setuptools_support.py
Changed
@@ -1,34 +1,83 @@ """ integration tests that check setuptools version support """ -import sys import os +import pathlib import subprocess +import sys + import pytest -@pytest.fixture(scope="session") -def get_setuptools_packagedir(request): - targets = request.config.cache.makedir("setuptools_installs") +def cli_run(*k, **kw): + """this defers the virtualenv import + it helps to avoid warnings from the furthermore imported setuptools + """ + global cli_run + from virtualenv.run import cli_run + + return cli_run(*k, **kw) + + +pytestmark = pytest.mark.filterwarnings( + r"ignore:.*tool\.setuptools_scm.*", r"always:.*setup.py install is deprecated.*" +) + + +ROOT = pathlib.Path(__file__).parent.parent + + +class Venv: + def __init__(self, location: pathlib.Path): + self.location = location - def makeinstall(version): - target = targets.ensure(version, dir=1) + @property + def python(self): + return self.location / "bin/python" + + +class VenvMaker: + def __init__(self, base: pathlib.Path): + self.base = base + + def __repr__(self): + return f"<VenvMaker base={self.base}>" + + def get_venv(self, python, pip, setuptools, prefix="scm"): + name = f"{prefix}-py={python}-pip={pip}-setuptools={setuptools}" + path = self.base / name + if not path.is_dir(): + cli_run( + + str(path), + "--python", + python, + "--pip", + pip, + "--setuptools", + setuptools, + , + setup_logging=False, + ) + venv = Venv(path) + subprocess.run(venv.python, "-m", "pip", "install", "-e", str(ROOT)) + # fixup pip + subprocess.check_call(venv.python, "-m", "pip", "install", f"pip=={pip}") subprocess.check_call( - - sys.executable, - "-m", - "pip", - "install", - "--no-binary", - "setuptools", - "setuptools==" + version, - "-t", - str(target), - + venv.python, "-m", "pip", "install", f"setuptools~={setuptools}" ) - return target + return venv - return makeinstall + +@pytest.fixture +def venv_maker(pytestconfig): + if not pytestconfig.getoption("--test-legacy"): + pytest.skip( + "testing on legacy setuptools disabled, pass --test-legacy to run them" + ) + dir = pytestconfig.cache.makedir("setuptools_scm_venvs") + path = pathlib.Path(str(dir)) + return VenvMaker(path) SCRIPT = """ @@ -42,19 +91,100 @@ """ -def check(packagedir, expected_version, **env): +def check(venv, expected_version, **env): - old_pythonpath = os.environ.get("PYTHONPATH") - if old_pythonpath: - pythonpath = f"{old_pythonpath}:{packagedir}" - else: - pythonpath = str(packagedir) subprocess.check_call( - sys.executable, "-c", SCRIPT, expected_version, - env=dict(os.environ, PYTHONPATH=pythonpath, **env), + venv.python, "-c", SCRIPT, expected_version, + env=dict(os.environ, **env), ) -def test_distlib_setuptools_works(get_setuptools_packagedir): - packagedir = get_setuptools_packagedir("45.0.0") - check(packagedir, "45.0.0") +@pytest.mark.skipif( + sys.version_info:2 >= (3, 10), reason="old setuptools won't work on python 3.10" +) +def test_distlib_setuptools_works(venv_maker): + venv = venv_maker.get_venv(setuptools="45.0.0", pip="9.0", python="3.6") + subprocess.run(venv.python, "-m", "pip", "install", "-e", str(ROOT)) + + check(venv, "45.0.0") + + +SETUP_PY_NAME = """ +from setuptools import setup +setup(name='setuptools_scm_test_package') +""" + +SETUP_PY_KEYWORD = """ +from setuptools import setup +setup(use_scm_version={"write_to": "pkg_version.py"}) +""" + +PYPROJECT_TOML_WITH_KEY = """ +build-system +# Minimum requirements for the build system to execute. +requires = "setuptools>45", "wheel" # PEP 508 specifications. +tool.setuptools_scm +write_to = "pkg_version.py" +""" + +SETUP_CFG_NAME = """ +metadata +name = setuptools_scm_test_package +""" + + +def prepare_expecting_pyproject_support(pkg: pathlib.Path): + pkg.mkdir() + pkg.joinpath("setup.py").write_text(SETUP_PY_NAME) + pkg.joinpath("pyproject.toml").write_text(PYPROJECT_TOML_WITH_KEY) + pkg.joinpath("PKG-INFO").write_text("Version: 1.0.0") + + +def prepare_setup_py_config(pkg: pathlib.Path): + pkg.mkdir() + pkg.joinpath("setup.py").write_text(SETUP_PY_KEYWORD) + pkg.joinpath("setup.cfg").write_text(SETUP_CFG_NAME) + + pkg.joinpath("PKG-INFO").write_text("Version: 1.0.0") + + +@pytest.mark.skipif( + sys.version_info:2 >= (3, 10), reason="old setuptools won't work on python 3.10" +) +@pytest.mark.parametrize("setuptools", f"{v}.0" for v in range(31, 45)) +@pytest.mark.parametrize( + "project_create", + + pytest.param( + prepare_expecting_pyproject_support, + marks=pytest.mark.xfail(reason="pyproject requires setuptools > 42"), + ), + prepare_setup_py_config, + , +) +def test_on_old_setuptools( + venv_maker, tmp_path, setuptools, project_create, monkeypatch +): + pkg = tmp_path.joinpath("pkg") + project_create(pkg) + venv = venv_maker.get_venv(setuptools=setuptools, pip="9.0", python="3.6") + + # monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG", raising=False) + + def run_and_output(cmd): + res = subprocess.run(cmd, cwd=str(pkg), stdout=subprocess.PIPE) + if not res.returncode: + return res.stdout.strip() + else: + print(res.stdout) + pytest.fail(str(cmd), pytrace=False) + + version = run_and_output(venv.python, "setup.py", "--version") + name = run_and_output(venv.python, "setup.py", "--name") + assert (name, version) == (b"setuptools_scm_test_package", b"1.0.0") + + # monkeypatch.setenv( + # "SETUPTOOLS_SCM_PRETEND_VERSION_FOR_setuptools_scm_test_package", "2.0,0") + + # version_pretend = run_and_output(venv.python, "setup.py", "--version") + # assert version_pretend == b"2.0.0"
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/testing/test_version.py -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/testing/test_version.py
Changed
@@ -1,17 +1,17 @@ -from datetime import date, timedelta +from datetime import date +from datetime import timedelta import pytest + from setuptools_scm.config import Configuration -from setuptools_scm.version import ( - meta, - simplified_semver_version, - release_branch_semver_version, - tags_to_versions, - no_guess_dev_version, - guess_next_version, - format_version, - calver_by_date, -) +from setuptools_scm.version import calver_by_date +from setuptools_scm.version import format_version +from setuptools_scm.version import guess_next_version +from setuptools_scm.version import meta +from setuptools_scm.version import no_guess_dev_version +from setuptools_scm.version import release_branch_semver_version +from setuptools_scm.version import simplified_semver_version +from setuptools_scm.version import tags_to_versions c = Configuration() @@ -83,6 +83,11 @@ id="release_branch_legacy_version", ), pytest.param( + meta("1.0.0", distance=2, branch="v1.0.x", config=c), + "1.0.1.dev2", + id="release_branch_with_v_prefix", + ), + pytest.param( meta("1.0.0", distance=2, branch="release-1.0", config=c), "1.0.1.dev2", id="release_branch_with_prefix", @@ -99,21 +104,28 @@ assert computed == expected_next +def m(tag, **kw): + return meta(tag, **kw, config=c) + + @pytest.mark.parametrize( "version, expected_next", pytest.param( - meta("1.0.0", distance=2, branch="default", config=c), + m("1.0.0", distance=2), "1.0.0.post1.dev2", id="dev_distance", ), pytest.param( - meta("1.0", distance=2, branch="default", config=c), + m("1.0.dev0", distance=2), "1.0.dev2", id="dev_distance_after_dev_tag" + ), + pytest.param( + m("1.0", distance=2), "1.0.post1.dev2", id="dev_distance_short_tag", ), pytest.param( - meta("1.0.0", distance=None, branch="default", config=c), + m("1.0.0"), "1.0.0", id="no_dev_distance", ), @@ -124,8 +136,20 @@ assert computed == expected_next +@pytest.mark.parametrize( + "version, match", + + ("1.0.dev1", "choosing custom numbers for the `.devX` distance"), + ("1.0.post1", "already is a post release"), + , +) +def test_no_guess_version_bad(version, match): + with pytest.raises(ValueError, match=match): + no_guess_dev_version(m(version, distance=1)) + + def test_bump_dev_version_zero(): - guess_next_version("1.0.dev0") + assert guess_next_version("1.0.dev0") == "1.0" def test_bump_dev_version_nonzero_raises(): @@ -136,7 +160,7 @@ "choosing custom numbers for the `.devX` distance " "is not supported.\n " "The 1.0.dev1 can't be bumped\n" - "Please drop the tag or create a new supported one" + "Please drop the tag or create a new supported one ending in .dev0" ) @@ -203,7 +227,7 @@ meta(date_to_str() + ".1", config=c), date_to_str() + ".1", id="exact patch" ), pytest.param( - meta(date_to_str(fmt="20.01.02"), config=c), + meta("20.01.02", config=c), "20.1.2", id="leading 0s", ), @@ -251,6 +275,19 @@ date_to_str(date.today() - timedelta(days=2)) + ".3.dev2", id="node date distance", ), + pytest.param( + meta( + "1.2.0", + config=c, + distance=2, + node_date=date.today() - timedelta(days=2), + ), + date_to_str(days_offset=2) + ".0.dev2", + marks=pytest.mark.filterwarnings( + "ignore:.*not correspond to a valid versioning date.*:UserWarning" + ), + id="using on old version tag", + ), , ) def test_calver_by_date(version, expected_next): @@ -278,3 +315,19 @@ def test_calver_by_date_future_warning(): with pytest.warns(UserWarning, match="your previous tag*"): calver_by_date(meta(date_to_str(days_offset=-2), config=c, distance=2)) + + +def test_custom_version_cls(): + """Test that we can pass our own version class instead of pkg_resources""" + + class MyVersion: + def __init__(self, tag_str: str): + self.tag = tag_str + + def __repr__(self): + return "Custom %s" % self.tag + + scm_version = meta("1.0.0-foo", config=Configuration(version_cls=MyVersion)) + + assert isinstance(scm_version.tag, MyVersion) + assert repr(scm_version.tag) == "Custom 1.0.0-foo"
View file
_service:tar_scm:setuptools_scm-6.0.0.tar.gz/tox.ini -> _service:tar_scm:setuptools_scm-6.4.2.tar.gz/tox.ini
Changed
@@ -1,11 +1,14 @@ tox -envlist=py{27,34,35,36,37,38,39}-test,flake8,check_readme,check-dist,py{27,37}-selfcheck,docs +envlist=py{36,37,38,39,310}-{test,selfcheck},check_readme,check-dist pytest testpaths=testing -filterwarnings=error +filterwarnings= + error + ignore:.*tool\.setuptools_scm.* markers= issue(id): reference to github issue + skip_commit: allows to skip committing in the helpers # disable unraisable until investigated addopts = -p no:unraisableexception @@ -13,14 +16,7 @@ max-complexity = 10 max-line-length = 88 ignore=E203,W503 -exclude= - .git, - .tox, - .env, - .venv, - .pytest_cache, - __pycache__, - ./src/setuptools_scm/win_py31_compat.py + testenv usedevelop=True @@ -29,21 +25,14 @@ test: False deps= pytest - setuptools >= 42 - toml + setuptools >= 45 + tomli + virtualenv>20 commands= test: pytest selfcheck: python setup.py --version -extras = - toml -testenv:flake8 -skip_install=True -deps= - flake8 - mccabe -commands = - flake8 src/setuptools_scm/ testing/ setup.py --exclude=src/setuptools_scm/win_py31_compat.py + testenv:check_readme skip_install=True @@ -58,30 +47,12 @@ testenv:check_dist deps= - wheel + build twine commands= - python setup.py clean --all rotate -k 0 -m .whl,.tar.gz,.zip - python setup.py -q egg_info - python setup.py -q sdist --formats zip bdist_wheel + python -m build twine check dist/* -testenv:dist -deps= wheel -whitelist_externals = rm -commands= - python setup.py -q clean --all - python setup.py -q rotate -k 0 -m .egg,.zip,.whl,.tar.gz - python setup.py -q egg_info - python setup.py -q sdist --formats zip,bztar bdist_wheel upload - -testenv:devpi -deps= - devpi-client -commands = - python setup.py -q egg_info - devpi upload --from-dir dist - #XXX: envs for hg versions
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2