pax_global_header00006660000000000000000000000064136121323750014515gustar00rootroot0000000000000052 comment=0df34c7c3cecb8409d22a2a45d958457c30fbfde setuptools_scm-3.4.3/000077500000000000000000000000001361213237500146075ustar00rootroot00000000000000setuptools_scm-3.4.3/.github/000077500000000000000000000000001361213237500161475ustar00rootroot00000000000000setuptools_scm-3.4.3/.github/FUNDING.yml000066400000000000000000000000361361213237500177630ustar00rootroot00000000000000tidelift: pypi/setuptools-scm setuptools_scm-3.4.3/.gitignore000066400000000000000000000010701361213237500165750ustar00rootroot00000000000000### 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__/ *.py[cod] *$py.class # Distribution / packaging .env/ env/ build/ dist/ .eggs/ lib/ lib64/ *.egg-info/ # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache .pytest_cache nosetests.xml coverage.xml *,cover # Sphinx documentation docs/_build/ setuptools_scm-3.4.3/.pre-commit-config.yaml000066400000000000000000000007351361213237500210750ustar00rootroot00000000000000exclude: setuptools_scm/win_py31_compat.py repos: - repo: https://github.com/ambv/black rev: 19.10b0 hooks: - id: black args: [--safe, --quiet] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: debug-statements - id: flake8 - repo: https://github.com/asottile/pyupgrade rev: v1.25.1 hooks: - id: pyupgrade setuptools_scm-3.4.3/.travis.yml000066400000000000000000000047761361213237500167360ustar00rootroot00000000000000language: python sudo: false stages: - linting - test - deploy credentials: - &pypi provider: pypi user: ronny # use when testing, may require recreation of the user and its credentials # server: https://test.pypi.org/legacy/ # Remove for deployment to official PyPi repo password: secure: QGJhDXmfFDKysMJJV/ONGaHHzG/aImhU3DdhEP63d657iQSn/Cb4EG/l9YmVnRzpJ94nSDXZB8YwptR7rid0bOtidb32lxN8n6UiWILCXWeAN2FE+tT9/0xIct4HUJZ8OttD1gft/Di722Gy+s9PzFwjwrV4efkxCzgjfYOjkMeq3aO6NoG3ur0iZXJh7ODwLp4sRFep2NpIEaXm2qMdnnXpck6bJ1q/NtvPx9CAZivd9HYa0evg5j1ENTz1mXXafhgF+0vRCBXA33xJuysO6CKtk+2mizL1QHfosOERiKl9+zPyZw+VvSchbCVwgxrMSiRcpGag+4SegyHrj1M/2YqfFzMF/yuFGcqXl2VkEqlnBQOVMNW3Kdcmnm+caNbddnv+M384WFz4nV8nWjcsD5l27+XlMWfuvskDIvZKtVCXmmbtqgwM4tqoYd6uxbnooRfwINTGx8sNzKP10xkaesB3ZBCEpecOKA1AXUAZ74RfYWWExv6eIuVGwyIJmOcD8M/17N8g58GxxO+88gx50EuhyNiRjYZDUipfVydfJwBwpD+p695NixUMITuksucQftjHsQp+laGWJlDIPvFwI85wDJUYAyrzn6L1W+smkm1bGomuliW2MJfxeSZAmSk4CE5VOpIWQTBmDLR3pxBhcaqzwdd4mAWvMi/fpM4yJJI= on: tags: yes python: - '2.7' - '3.4' - '3.5' - '3.6' - '3.7' - '3.8-dev' dist: xenial # needed for 3.7+ env: - TOXENV=py-test jobs: include: - stage: linting name: check readme python: '3.6' env: TOXENV=check_readme # - stage: test # python: '3.7' # dist: xenial - stage: test python: '2.7' env: SELFINSTALL=1 - stage: test python: '3.6' env: SELFINSTALL=1 - stage: linting python: '3.6' name: validate pre-commit env: install: - pip install pre-commit - pre-commit install-hooks script: - pre-commit run --all-files - &deploy stage: deploy name: "modern distributions" python: '3.6' install: - pip install -U pip setuptools wheel script: skip deploy: <<: *pypi distributions: "sdist bdist_wheel" - &bdist_egg <<: *deploy name: "python eggs 2.7" python: '2.7' deploy: <<: *pypi distributions: "bdist_egg" - <<: *bdist_egg name: "python eggs 3.4" python: '3.4' - <<: *bdist_egg name: "python eggs 3.5" python: '3.5' - <<: *bdist_egg name: "python eggs 3.6" python: '3.6' - <<: *bdist_egg name: "python eggs 3.7" python: '3.7' - <<: *bdist_egg name: "python eggs 3.8" python: '3.8-dev' cache: directories: - $HOME/.cache/pip - $HOME/.cache/pre-commit install: pip install tox script: - python testing/runtests_travis.py setuptools_scm-3.4.3/CHANGELOG.rst000066400000000000000000000250361361213237500166360ustar00rootroot00000000000000v3.4.3 ====== * fix #399: ensure the git file finder terminates subprocess after reading archive v3.4.2 ====== * fix #395: correctly transfer tag regex in the Configuration constructor * rollback --first-parent for git describe as it turns out to be a regression for some users v3.4.1 ====== * pull in #377 to fix #374: correctly set up the default version scheme for pyproject usage. this bugfix got missed when ruushing the release. v3.4.0 ====== * fix #181 - add support for projects built under setuptools declarative config by way of the setuptools.finalize_distribution_options hook in Setuptools 42. * fix #305 - ensure the git file finder closes filedescriptors even when errors happen * fix #381 - clean out env vars from the git hook system to ensure correct function from within * modernize docs wrt importlib.metadata *edited* * use --first-parent for git describe v3.3.3 ====== * add eggs for python3.7 and 3.8 to the deploy v3.3.2 ====== * fix #335 - fix python3.8 support and add builds for up to python3.8 v3.3.1 ====== * fix #333 (regression from #198) - use a specific fallback root when calling fallbacks. Remove old hack that resets the root when fallback entrypoints are present. v3.3.0 ====== * fix #198 by adding the ``fallback_version`` option, which sets the version to be used when everything else fails. v3.2.0 ====== * fix #303 and #283 by adding the option ``git_describe_command`` to allow the user to control the way that `git describe` is called. v3.1.0 ======= * fix #297 - correct the invocation in version_from_scm and deprecate it as its exposed by accident * fix #298 - handle git file listing on empty repositories * fix #268 - deprecate ScmVersion.extra v3.0.6 ====== * fix #295 - correctly handle selfinstall from tarballs v3.0.5 ====== * fix #292 - match leading ``V`` character as well https://www.python.org/dev/peps/pep-0440/#preceding-v-character v3.0.4 ======= * rerelease of 3.0.3 after fixing the release process v3.0.3 (pulled from pypi due to a packaging issue) ====== * fix #286 - duo an oversight a helper functio nwas returning a generator instead of a list v3.0.2 ====== * fix a regression from tag parsing - support for multi-dashed prefixes - #284 v3.0.1 ======= * fix a regression in setuptools_scm.git.parse - reorder arguments so the positional invocation from before works as expected #281 v3.0.0 ======= * introduce pre-commit and use black * print the origin module to help testing * switch to src layout (breaking change) * no longer alias tag and parsed_version in order to support understanding a version parse failure * 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) v2.1.0 ====== * enhance docs for sphinx usage * add symlink support to file finder for git #247 (thanks Stéphane Bidoul) * enhance tests handling win32 (thanks Stéphane Bidoul) v2.0.0 ======== * 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 * reconfigure flake8 for future compatibility with black * introduce support for branch name in version metadata and support a opt-in simplified semver version scheme v1.17.0 ======== * fix regression in git support - use a function to ensure it works in egg isntalled mode * actually fail if file finding fails in order to see broken setups instead of generating broken dists (thanks Mehdi ABAAKOUK for both) v1.16.2 ======== * fix regression in handling git export ignores (thanks Mehdi ABAAKOUK) v1.16.1 ======= * fix regression in support for old setuptools versions (thanks Marco Clemencic) v1.16.0 ======= * drop support for eol python versions * #214 - fix missuse in surogate-escape api * add the node-and-timestamp local version sheme * respect git export ignores * avoid shlex.split on windows * fix #218 - better handling of mercurial edge-cases with tag commits being considered as the tagged commit * fix #223 - remove the dependency on the interal SetupttoolsVersion as it was removed after long-standing deprecation v1.15.7 ====== * Fix #174 with #207: Re-use samefile backport as developed in jaraco.windows, and only use the backport where samefile is not available. v1.15.6 ======= * fix #171 by unpinning the py version to allow a fixed one to get installed v1.15.5 ======= * fix #167 by correctly respecting preformatted version metadata from PKG-INFO/EGG-INFO v1.15.4 ======= * fix issue #164: iterate all found entry points to avoid erros when pip remakes egg-info * enhance self-use to enable pip install from github again v1.15.3 ======= * bring back correctly getting our version in the own sdist, finalizes #114 * fix issue #150: strip local components of tags v1.15.2 ======= * fix issue #128: return None when a scm specific parse fails in a worktree to ease parse reuse v1.15.1 ======= * fix issue #126: the local part of any tags is discarded when guessing new versions * minor performance optimization by doing fewer git calls in the usual cases v1.15.0 ======= * more sophisticated ignoring of mercurial tag commits 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 * 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 * address issue #93: provide an experimental api to customize behaviour on shallow git repos a custom parse function may pick pre parse actions to do when using git v1.14.1 ======= * fix #109: when detecting a dirty git workdir 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) v1.14.0 ======= * publish bdist_egg for python 2.6, 2.7 and 3.3-3.5 * fix issue #107 - dont use node if it is None v1.13.1 ======= * fix issue #86 - detect dirty git workdir without tags v1.13.0 ======= * fix regression caused by the fix of #101 * assert types for version dumping * strictly pass all versions trough parsed version metadata v1.12.0 ======= * fix issue #97 - add support for mercurial plugins * fix issue #101 - write version cache even for pretend version (thanks anarcat for reporting and fixing) v1.11.1 ======== * fix issue #88 - better docs for sphinx usage (thanks Jason) * fix issue #89 - use normpath to deal with windows (thanks Te-jé Rodgers for reporting and fixing) v1.11.0 ======= * always run tag_to_version so in order to handle prefixes on old setuptools (thanks to Brian May) * drop support for python 3.2 * extend the error message on missing scm metadata (thanks Markus Unterwaditzer) * fix bug when using callable version_scheme (thanks Esben Haabendal) v1.10.1 ======= * fix issue #73 - in hg pre commit merge, consider parent1 instead of failing v1.10.0 ======= * 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 and prepare the possibility of passing more options to scm backends * fix issue #70 and #71 by introducing the parse keyword to specify custom scm parsing, its an expert feature, use with caution this change also introduces the setuptools_scm.parse_scm_fallback entrypoint which can be used to register custom archive fallbacks v1.9.0 ====== * Add :code:`relative_to` parameter to :code:`get_version` function; fixes #44 per #45. v1.8.0 ====== * fix issue with setuptools wrong version warnings being printed to standard out. User is informed now by distutils-warnings. * restructure root finding, we now reliably ignore outer scm and prefer PKG-INFO over scm, fixes #43 and #45 v1.7.0 ====== * correct the url to github thanks David Szotten * enhance scm not found errors with a note on git tarballs thanks Markus * add support for :code:`write_to_template` v1.6.0 ====== * bail out early if the scm is missing this brings issues with git tarballs and older devpi-client releases to light, 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) v1.5.5 ====== * Fix bug on Python 2 on Windows when environment has unicode fields. v1.5.4 ====== * Fix bug on Python 2 when version is loaded from existing metadata. v1.5.3 ====== * #28: Fix decoding error when PKG-INFO contains non-ASCII. v1.5.2 ====== * add zip_safe flag v1.5.1 ====== * fix file access bug i missed in 1.5 v1.5.0 ====== * moved setuptools integration related code to own file * support storing version strings into a module/text file using the :code:`write_to` coniguration parameter v1.4.0 ====== * propper handling for sdist * fix file-finder failure from windows * resuffle docs v1.3.0 ====== * support setuptools easy_install egg creation details by hardwireing the version in the sdist v1.2.0 ====== * enhance self-use v1.1.0 ====== * enable self-use v1.0.0 ====== * documentation enhancements v0.26 ===== * rename to setuptools_scm * split into package, add lots of entry points for extension * pluggable version schemes v0.25 ===== * fix pep440 support this reshuffles the complete code for version guessing v0.24 ===== * dont drop dirty flag on node finding * fix distance for dirty flagged versions * use dashes for time again, its normalisation with setuptools * remove the own version attribute, it was too fragile to test for * include file finding * handle edge cases around dirty tagged versions v0.23 ===== * windows compatibility fix (thanks stefan) drop samefile since its missing in some python2 versions on windows * add tests to the source tarballs v0.22 ===== * windows compatibility fix (thanks stefan) use samefile since it does path normalisation v0.21 ===== * fix the own version attribute (thanks stefan) v0.20 ===== * fix issue 11: always take git describe long format to avoid the source of the ambiguity * fix issue 12: add a __version__ attribute via pkginfo v0.19 ===== * configurable next version guessing * fix distance guessing (thanks stefan) setuptools_scm-3.4.3/LICENSE000066400000000000000000000017771361213237500156300ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. setuptools_scm-3.4.3/MANIFEST.in000066400000000000000000000003301361213237500163410ustar00rootroot00000000000000exclude *.nix exclude appveyor.yml exclude .travis.yaml exclude .pre-commit-config.yaml include *.py include testing/*.py include tox.ini include *.rst include LICENSE include *.toml recursive-include testing *.bash setuptools_scm-3.4.3/README.rst000066400000000000000000000406541361213237500163070ustar00rootroot00000000000000setuptools_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``. .. image:: https://travis-ci.org/pypa/setuptools_scm.svg?branch=master :target: https://travis-ci.org/pypa/setuptools_scm .. 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_scm[toml]>=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 `_ and `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__}, 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 `_ 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 `_) 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 `_. 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 or the `importlib_metadata`_ backport: .. code:: python from importlib.metadata import version, PackageNotFoundError try: __version__ = version(__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(__name__).version except DistributionNotFound: # package is not installed pass This does place a runtime dependency on ``setuptools``. .. _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 `_ 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 `_, 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 `_ 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 `_ 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 `_ 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 three named groups prefix, version and suffix, where ``version`` captures the actual version information. Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX`` (see `config.py `_). :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 `_). 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'^(?Pv)?(?P[^\+]+)(?P.*)?$', } ) 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_DEBUG: when defined and not empty, a lot of debug information will be printed as part of ``setuptools_scm`` operating 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) :post-release: generates post release versions (adds :code:`postN`) ``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. 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 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 Code of Conduct --------------- Everyone interacting in the ``setuptools_scm`` project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. .. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ Security Contact ================ To report a security vulnerability, please use the `Tidelift security contact `_. Tidelift will coordinate the fix and disclosure. setuptools_scm-3.4.3/appveyor.yml000066400000000000000000000022131361213237500171750ustar00rootroot00000000000000environment: matrix: - PYTHON: "C:\\Python27" TOXENV: "py-test" - PYTHON: "C:\\Python27-x64" TOXENV: "py-test" - PYTHON: "C:\\Python34" TOXENV: "py-test" - PYTHON: "C:\\Python34-x64" TOXENV: "py-test" init: - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - ECHO "Updating Environment" - python -m pip install -U pip - python -m pip install -U pep517 - python -m pip install -U --upgrade-strategy=eager tox install: # Check that we have the expected version and architecture for Python - python -c "import sys, os;sys.stdout.write(str(sys.version) + os.linesep)" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" - python -m pip list build: false # Not a C# project, build stuff at the test step instead. test_script: # Build the compiled extension and run the project tests - python -m tox after_test: # If tests are successful, create a whl package for the project. - python -m pep517.build --binary . - ps: "ls dist" artifacts: # Archive the generated wheel package in the ci.appveyor.com build report. - path: dist\* setuptools_scm-3.4.3/default.nix000066400000000000000000000003431361213237500167530ustar00rootroot00000000000000{pkgs ? import {}}: with pkgs.pythonPackages; buildPythonPackage { name = "setuptools_scm"; src = ./.; version = "git"; buildInputs = [ setuptools pip pytest pkgs.git pkgs.mercurial ]; } setuptools_scm-3.4.3/pyproject.toml000066400000000000000000000001401361213237500175160ustar00rootroot00000000000000[build-system] requires = ["setuptools>=34.4", "wheel"] build-backend = "setuptools.build_meta" setuptools_scm-3.4.3/setup.cfg000066400000000000000000000002461361213237500164320ustar00rootroot00000000000000[bdist_wheel] universal=1 [metadata] # ensure that the LICENSE file is included in the built wheels license_file = LICENSE [devpi:upload] formats=sdist,bdist_wheel setuptools_scm-3.4.3/setup.py000066400000000000000000000100611361213237500163170ustar00rootroot00000000000000"""\ important note: the setup of setuptools_scm is self-using, the first execution of `python setup.py egg_info` will generate partial data its critical to run `python setup.py egg_info` once before running sdist or easy_install on a fresh checkouts pip usage is recommended """ from __future__ import print_function import os import sys import setuptools def scm_config(): 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) from setuptools_scm.hacks import parse_pkginfo from setuptools_scm.git import parse as parse_git from setuptools_scm.version import guess_next_dev_version, get_local_node_and_date def parse(root): try: return parse_pkginfo(root) except IOError: return parse_git(root) config = dict( 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)) with open("README.rst") as fp: long_description = fp.read() arguments = dict( name="setuptools_scm", url="https://github.com/pypa/setuptools_scm/", zip_safe=True, author="Ronny Pfannschmidt", author_email="opensource@ronnypfannschmidt.de", description=("the blessed package to manage your versions by scm tags"), long_description=long_description, license="MIT", packages=["setuptools_scm"], package_dir={"": "src"}, entry_points=""" [distutils.setup_keywords] use_scm_version = setuptools_scm.integration:version_keyword [setuptools.file_finders] setuptools_scm = setuptools_scm.integration:find_files [setuptools.finalize_distribution_options] setuptools_scm = setuptools_scm.integration:infer_version [setuptools_scm.parse_scm] .hg = setuptools_scm.hg:parse .git = setuptools_scm.git:parse [setuptools_scm.parse_scm_fallback] .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 setup.py = setuptools_scm.hacks:fallback_version [setuptools_scm.files_command] .hg = setuptools_scm.file_finder_hg:hg_find_files .git = setuptools_scm.file_finder_git:git_find_files [setuptools_scm.version_scheme] guess-next-dev = setuptools_scm.version:guess_next_dev_version post-release = setuptools_scm.version:postrelease_version python-simplified-semver = setuptools_scm.version:simplified_semver_version [setuptools_scm.local_scheme] node-and-date = setuptools_scm.version:get_local_node_and_date node-and-timestamp = \ setuptools_scm.version:get_local_node_and_timestamp dirty-tag = setuptools_scm.version:get_local_dirty_tag """, classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Version Control", "Topic :: System :: Software Distribution", "Topic :: Utilities", ], python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", extras_require=dict(toml=["toml"]), ) if __name__ == "__main__": arguments.update(scm_config()) setuptools.setup(**arguments) setuptools_scm-3.4.3/src/000077500000000000000000000000001361213237500153765ustar00rootroot00000000000000setuptools_scm-3.4.3/src/setuptools_scm/000077500000000000000000000000001361213237500204615ustar00rootroot00000000000000setuptools_scm-3.4.3/src/setuptools_scm/__init__.py000066400000000000000000000113471361213237500226000ustar00rootroot00000000000000""" :copyright: 2010-2015 by Ronny Pfannschmidt :license: MIT """ import os import warnings from .config import ( Configuration, DEFAULT_VERSION_SCHEME, DEFAULT_LOCAL_SCHEME, DEFAULT_TAG_REGEX, ) from .utils import function_has_arg, string_types from .version import format_version, meta from .discover import iter_matching_entrypoints PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" TEMPLATES = { ".py": """\ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control version = {version!r} """, ".txt": "{version}", } def version_from_scm(root): warnings.warn( "version_from_scm is deprecated please use get_version", category=DeprecationWarning, ) config = Configuration() config.root = root # TODO: Is it API? 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): assert isinstance(version, string_types) 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) if template is None: raise ValueError( "bad file format: '{}' (of {}) \nonly *.txt and *.py are supported".format( os.path.splitext(target)[1], target ) ) with open(target, "w") as fp: fp.write(template.format(version=version)) def _do_parse(config): 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) if config.parse: parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) if isinstance(parse_result, string_types): raise TypeError( "version parse result was a string\nplease return a parsed version" ) version = parse_result or _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 raise LookupError( "setuptools-scm was unable to detect version for %r.\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 ) def get_version( root=".", version_scheme=DEFAULT_VERSION_SCHEME, local_scheme=DEFAULT_LOCAL_SCHEME, write_to=None, write_to_template=None, relative_to=None, tag_regex=DEFAULT_TAG_REGEX, fallback_version=None, fallback_root=".", parse=None, git_describe_command=None, ): """ If supplied, relative_to should be a file from which root may be resolved. Typically called by a script or module that is not in the root of the repository to direct setuptools_scm to the root of the repository by supplying ``__file__``. """ config = Configuration(**locals()) return _get_version(config) def _get_version(config): parsed_version = _do_parse(config) if parsed_version: version_string = format_version( parsed_version, version_scheme=config.version_scheme, local_scheme=config.local_scheme, ) dump_version( root=config.root, version=version_string, write_to=config.write_to, template=config.write_to_template, ) return version_string setuptools_scm-3.4.3/src/setuptools_scm/__main__.py000066400000000000000000000006471361213237500225620ustar00rootroot00000000000000from __future__ import print_function import sys from setuptools_scm import get_version from setuptools_scm.integration import find_files from setuptools_scm.version import _warn_if_setuptools_outdated def main(): _warn_if_setuptools_outdated() print("Guessed Version", get_version()) if "ls" in sys.argv: for fname in find_files("."): print(fname) if __name__ == "__main__": main() setuptools_scm-3.4.3/src/setuptools_scm/config.py000066400000000000000000000067431361213237500223120ustar00rootroot00000000000000""" configuration """ from __future__ import print_function, unicode_literals import os import re import warnings from .utils import trace DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]+)(?:\+.*)?$" DEFAULT_VERSION_SCHEME = "guess-next-dev" DEFAULT_LOCAL_SCHEME = "node-and-date" def _check_tag_regex(value): if not value: value = DEFAULT_TAG_REGEX regex = re.compile(value) group_names = regex.groupindex.keys() if regex.groups == 0 or (regex.groups > 1 and "version" not in group_names): warnings.warn( "Expected tag_regex to contain a single match group or a group named" " 'version' to identify the version part of any tag." ) return regex def _check_absolute_root(root, relative_to): if relative_to: if os.path.isabs(root) and not root.startswith(relative_to): warnings.warn( "absolute root path '%s' overrides relative_to '%s'" % (root, relative_to) ) root = os.path.join(os.path.dirname(relative_to), root) return os.path.abspath(root) class Configuration(object): """ Global configuration model """ def __init__( self, relative_to=None, root=".", version_scheme=DEFAULT_VERSION_SCHEME, local_scheme=DEFAULT_LOCAL_SCHEME, write_to=None, write_to_template=None, tag_regex=DEFAULT_TAG_REGEX, fallback_version=None, fallback_root=".", parse=None, git_describe_command=None, ): # TODO: self._relative_to = relative_to self._root = "." self.root = root self.version_scheme = version_scheme self.local_scheme = local_scheme self.write_to = write_to self.write_to_template = write_to_template self.fallback_version = fallback_version self.fallback_root = fallback_root self.parse = parse self.tag_regex = tag_regex self.git_describe_command = git_describe_command @property def fallback_root(self): return self._fallback_root @fallback_root.setter def fallback_root(self, value): self._fallback_root = os.path.abspath(value) @property def absolute_root(self): return self._absolute_root @property def relative_to(self): return self._relative_to @relative_to.setter def relative_to(self, value): self._absolute_root = _check_absolute_root(self._root, value) self._relative_to = value trace("root", repr(self._absolute_root)) @property def root(self): return self._root @root.setter def root(self, value): self._absolute_root = _check_absolute_root(value, self._relative_to) self._root = value trace("root", repr(self._absolute_root)) @property def tag_regex(self): return self._tag_regex @tag_regex.setter def tag_regex(self, value): self._tag_regex = _check_tag_regex(value) @classmethod def from_file(cls, name="pyproject.toml"): """ 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(**section) setuptools_scm-3.4.3/src/setuptools_scm/discover.py000066400000000000000000000006401361213237500226510ustar00rootroot00000000000000import os from pkg_resources 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 setuptools_scm-3.4.3/src/setuptools_scm/file_finder.py000066400000000000000000000042541361213237500233060ustar00rootroot00000000000000import os def scm_find_files(path, scm_files, scm_dirs): """ setuptools compatible file finder that follows symlinks - path: the root directory from which to search - scm_files: set of scm controlled files and symlinks (including symlinks to directories) - scm_dirs: set of scm controlled directories (including directories containing no scm controlled files) scm_files and scm_dirs must be absolute with symlinks resolved (realpath), with normalized case (normcase) Spec here: http://setuptools.readthedocs.io/en/latest/setuptools.html#\ adding-support-for-revision-control-systems """ realpath = os.path.normcase(os.path.realpath(path)) seen = set() res = [] for dirpath, dirnames, filenames in os.walk(realpath, followlinks=True): # dirpath with symlinks resolved realdirpath = os.path.normcase(os.path.realpath(dirpath)) def _link_not_in_scm(n): fn = os.path.join(realdirpath, os.path.normcase(n)) return os.path.islink(fn) and fn not in scm_files if realdirpath not in scm_dirs: # directory not in scm, don't walk it's content dirnames[:] = [] continue if os.path.islink(dirpath) and not os.path.relpath( realdirpath, realpath ).startswith(os.pardir): # a symlink to a directory not outside path: # we keep it in the result and don't walk its content res.append(os.path.join(path, os.path.relpath(dirpath, path))) dirnames[:] = [] continue if realdirpath in seen: # symlink loop protection dirnames[:] = [] continue dirnames[:] = [dn for dn in dirnames if not _link_not_in_scm(dn)] for filename in filenames: if _link_not_in_scm(filename): continue # dirpath + filename with symlinks preserved fullfilename = os.path.join(dirpath, filename) if os.path.normcase(os.path.realpath(fullfilename)) in scm_files: res.append(os.path.join(path, os.path.relpath(fullfilename, path))) seen.add(realdirpath) return res setuptools_scm-3.4.3/src/setuptools_scm/file_finder_git.py000066400000000000000000000040141361213237500241430ustar00rootroot00000000000000import os import subprocess import tarfile import logging from .file_finder import scm_find_files from .utils import trace log = logging.getLogger(__name__) def _git_toplevel(path): try: with open(os.devnull, "wb") as devnull: out = subprocess.check_output( ["git", "rev-parse", "--show-toplevel"], cwd=(path or "."), universal_newlines=True, stderr=devnull, ) trace("find files toplevel", out) return os.path.normcase(os.path.realpath(out.strip())) except subprocess.CalledProcessError: # git returned error, we are not in a git repo return None except OSError: # git command not found, probably return None def _git_interpret_archive(fd, toplevel): with tarfile.open(fileobj=fd, mode="r|*") as tf: git_files = set() git_dirs = {toplevel} for member in tf.getmembers(): name = os.path.normcase(member.name).replace("/", os.path.sep) if member.type == tarfile.DIRTYPE: git_dirs.add(name) else: git_files.add(name) return git_files, git_dirs 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) try: try: return _git_interpret_archive(proc.stdout, toplevel) finally: # ensure we avoid resource warnings by cleaning up the process proc.terminate() except Exception: if proc.wait() != 0: log.exception("listing git files failed - pretending there aren't any") return (), () def git_find_files(path=""): toplevel = _git_toplevel(path) if not toplevel: return [] git_files, git_dirs = _git_ls_files_and_dirs(toplevel) return scm_find_files(path, git_files, git_dirs) setuptools_scm-3.4.3/src/setuptools_scm/file_finder_hg.py000066400000000000000000000026071361213237500237640ustar00rootroot00000000000000import os import subprocess from .file_finder import scm_find_files def _hg_toplevel(path): try: with open(os.devnull, "wb") as devnull: out = subprocess.check_output( ["hg", "root"], cwd=(path or "."), universal_newlines=True, stderr=devnull, ) return os.path.normcase(os.path.realpath(out.strip())) except subprocess.CalledProcessError: # hg returned error, we are not in a mercurial repo return None except OSError: # hg command not found, probably return None def _hg_ls_files_and_dirs(toplevel): hg_files = set() hg_dirs = {toplevel} out = subprocess.check_output( ["hg", "files"], cwd=toplevel, universal_newlines=True ) for name in out.splitlines(): name = os.path.normcase(name).replace("/", os.path.sep) fullname = os.path.join(toplevel, name) hg_files.add(fullname) dirname = os.path.dirname(fullname) while len(dirname) > len(toplevel) and dirname not in hg_dirs: hg_dirs.add(dirname) dirname = os.path.dirname(dirname) return hg_files, hg_dirs def hg_find_files(path=""): toplevel = _hg_toplevel(path) if not toplevel: return [] hg_files, hg_dirs = _hg_ls_files_and_dirs(toplevel) return scm_find_files(path, hg_files, hg_dirs) setuptools_scm-3.4.3/src/setuptools_scm/git.py000066400000000000000000000077431361213237500216310ustar00rootroot00000000000000from .config import Configuration from .utils import do_ex, trace, has_command from .version import meta from os.path import isfile, join import warnings try: from os.path import samefile except ImportError: from .win_py31_compat import samefile DEFAULT_DESCRIBE = "git describe --dirty --tags --long --match *.*" class GitWorkdir(object): """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) @classmethod def from_potential_worktree(cls, wd): real_wd, _, ret = do_ex("git rev-parse --show-toplevel", wd) if ret: return trace("real root", real_wd) if not samefile(real_wd, wd): return return cls(real_wd) def is_dirty(self): out, _, _ = self.do_ex("git status --porcelain --untracked-files=no") return bool(out) def get_branch(self): branch, err, ret = self.do_ex("git rev-parse --abbrev-ref HEAD") if ret: trace("branch err", branch, err, ret) return return branch def is_shallow(self): return isfile(join(self.path, ".git/shallow")) def fetch_shallow(self): self.do_ex("git fetch --unshallow") def node(self): rev_node, _, ret = self.do_ex("git rev-parse --verify --quiet HEAD") if not ret: return rev_node[:7] def count_all_nodes(self): revs, _, _ = self.do_ex("git rev-list HEAD") return revs.count("\n") + 1 def warn_on_shallow(wd): """experimental, may change at any time""" if wd.is_shallow(): warnings.warn('"{}" is shallow and may cause errors'.format(wd.path)) 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') wd.fetch_shallow() def fail_on_shallow(wd): """experimental, may change at any time""" if wd.is_shallow(): raise ValueError( "%r is shallow, please correct with " '"git fetch --unshallow"' % wd.path ) def parse( root, describe_command=DEFAULT_DESCRIBE, 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) if not has_command("git"): return wd = GitWorkdir.from_potential_worktree(config.absolute_root) if wd is None: return if pre_parse: pre_parse(wd) if config.git_describe_command: describe_command = config.git_describe_command out, unused_err, ret = wd.do_ex(describe_command) if ret: # If 'git git_describe_command' failed, try to get the information otherwise. rev_node = wd.node() dirty = wd.is_dirty() if rev_node is None: return meta("0.0", distance=0, dirty=dirty, config=config) return meta( "0.0", distance=wd.count_all_nodes(), node="g" + rev_node, dirty=dirty, branch=wd.get_branch(), 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, ) else: return meta(tag, config=config, node=node, dirty=dirty, branch=branch) def _git_parse_describe(describe_output): # 'describe_output' looks e.g. like 'v1.5.0-0-g4060507' or # 'v1.15.1rc1-37-g9bd1298-dirty'. if describe_output.endswith("-dirty"): dirty = True describe_output = describe_output[:-6] else: dirty = False tag, number, node = describe_output.rsplit("-", 2) number = int(number) return tag, number, node, dirty setuptools_scm-3.4.3/src/setuptools_scm/hacks.py000066400000000000000000000015071361213237500221270ustar00rootroot00000000000000import os from .utils import data_from_mime, trace from .version import meta def parse_pkginfo(root, config=None): pkginfo = os.path.join(root, "PKG-INFO") trace("pkginfo", pkginfo) data = data_from_mime(pkginfo) version = data.get("Version") if version != "UNKNOWN": return meta(version, preformatted=True, config=config) def parse_pip_egg_info(root, config=None): pipdir = os.path.join(root, "pip-egg-info") if not os.path.isdir(pipdir): return items = os.listdir(pipdir) trace("pip-egg-info", pipdir, items) if not items: return return parse_pkginfo(os.path.join(pipdir, items[0]), config=config) def fallback_version(root, config=None): if config.fallback_version is not None: return meta(config.fallback_version, preformatted=True, config=config) setuptools_scm-3.4.3/src/setuptools_scm/hg.py000066400000000000000000000065121361213237500214350ustar00rootroot00000000000000import os from .config import Configuration from .utils import do, trace, data_from_mime, has_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 ) else: return meta(tag, config=config) def parse(root, config=None): if not config: config = Configuration(root=root) if not has_command("hg"): return identity_data = do("hg id -i -b -t", config.absolute_root).split() if not identity_data: 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(tags[0], 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", "{}::{}".format(rev1, rev2)] out = do(cmd, root) return len(out.strip().splitlines()) - 1 def archival_to_version(data, config=None): trace("data", data) node = data.get("node", "")[:12] if node: node = "h" + node if "tag" in data: return meta(data["tag"], config=config) elif "latesttag" in data: return meta( data["latesttag"], distance=data["latesttagdistance"], node=node, config=config, ) else: return meta("0.0", node=node, config=config) def parse_archival(root, config=None): archival = os.path.join(root, ".hg_archival.txt") data = data_from_mime(archival) return archival_to_version(data, config=config) setuptools_scm-3.4.3/src/setuptools_scm/integration.py000066400000000000000000000024501361213237500233570ustar00rootroot00000000000000from pkg_resources import iter_entry_points from .version import _warn_if_setuptools_outdated from .utils import do, trace_exception from . import _get_version, Configuration def version_keyword(dist, keyword, value): _warn_if_setuptools_outdated() if not value: return if value is True: value = {} if getattr(value, "__call__", None): value = value() config = Configuration(**value) dist.metadata.version = _get_version(config) def find_files(path=""): for ep in iter_entry_points("setuptools_scm.files_command"): command = ep.load() if isinstance(command, str): # this technique is deprecated res = do(ep.load(), path or ".").splitlines() else: res = command(path) if res: return res return [] def _args_from_toml(name="pyproject.toml"): # todo: more sensible config initialization # move this elper 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): try: config = Configuration.from_file() except Exception: return trace_exception() dist.metadata.version = _get_version(config) setuptools_scm-3.4.3/src/setuptools_scm/utils.py000066400000000000000000000070161361213237500221770ustar00rootroot00000000000000""" utils """ from __future__ import print_function, unicode_literals import inspect import warnings import sys import shlex import subprocess import os import io import platform import traceback DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) IS_WINDOWS = platform.system() == "Windows" PY2 = sys.version_info < (3,) PY3 = sys.version_info > (3,) string_types = (str,) if PY3 else (str, unicode) # noqa def no_git_env(env): # adapted from pre-commit # Too many bugs dealing with environment variables and GIT: # https://github.com/pre-commit/pre-commit/issues/300 # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running # pre-commit hooks # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE # while running pre-commit hooks in submodules. # GIT_DIR: Causes git clone to clone wrong thing # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit for k, v in env.items(): if k.startswith("GIT_"): trace(k, v) return { k: v for k, v in env.items() if not k.startswith("GIT_") or k in ("GIT_EXEC_PATH", "GIT_SSH", "GIT_SSH_COMMAND") } def trace(*k): if DEBUG: print(*k) sys.stdout.flush() def trace_exception(): DEBUG and traceback.print_exc() def ensure_stripped_str(str_or_bytes): if isinstance(str_or_bytes, str): return str_or_bytes.strip() else: return str_or_bytes.decode("utf-8", "surrogateescape").strip() def _always_strings(env_dict): """ On Windows and Python 2, environment dictionaries must be strings and not unicode. """ if IS_WINDOWS or PY2: env_dict.update((key, str(value)) for (key, value) in env_dict.items()) return env_dict def _popen_pipes(cmd, cwd): return subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=str(cwd), env=_always_strings( dict( no_git_env(os.environ), # os.environ, # try to disable i18n LC_ALL="C", LANGUAGE="", HGPLAIN="1", ) ), ) def do_ex(cmd, cwd="."): trace("cmd", repr(cmd)) if os.name == "posix" and not isinstance(cmd, (list, tuple)): cmd = shlex.split(cmd) p = _popen_pipes(cmd, cwd) out, err = p.communicate() if out: trace("out", repr(out)) if err: trace("err", repr(err)) if p.returncode: trace("ret", p.returncode) return ensure_stripped_str(out), ensure_stripped_str(err), p.returncode def do(cmd, cwd="."): out, err, ret = do_ex(cmd, cwd) if ret: print(err) return out def data_from_mime(path): with io.open(path, encoding="utf-8") as fp: content = fp.read() trace("content", repr(content)) # the complex conditions come from reading pseudo-mime-messages data = dict(x.split(": ", 1) for x in content.splitlines() if ": " in x) trace("data", data) return data def function_has_arg(fn, argname): assert inspect.isfunction(fn) if PY2: argspec = inspect.getargspec(fn).args else: argspec = inspect.signature(fn).parameters return argname in argspec def has_command(name): try: p = _popen_pipes([name, "help"], ".") except OSError: trace(*sys.exc_info()) res = False else: p.communicate() res = not p.returncode if not res: warnings.warn("%r was not found" % name) return res setuptools_scm-3.4.3/src/setuptools_scm/version.py000066400000000000000000000203761361213237500225300ustar00rootroot00000000000000from __future__ import print_function import datetime import warnings import re from itertools import chain, repeat, islice from .config import Configuration from .utils import trace, string_types from pkg_resources import iter_entry_points from pkg_resources import parse_version as pkg_parse_version SEMVER_MINOR = 2 SEMVER_PATCH = 3 SEMVER_LEN = 3 def _pad(iterable, size, padding=None): padded = chain(iterable, repeat(padding)) return list(islice(padded, size)) def _parse_version_tag(tag, config): tagstring = tag if not isinstance(tag, string_types) else str(tag) match = config.tag_regex.match(tagstring) result = None if match: if len(match.groups()) == 1: key = 1 else: key = "version" result = { "version": match.group(key), "prefix": match.group(0)[: match.start(key)], "suffix": match.group(0)[match.end(key) :], } trace("tag '{}' parsed to {}".format(tag, result)) return result def _get_version_class(): modern_version = pkg_parse_version("1.0") if isinstance(modern_version, tuple): return None else: return type(modern_version) VERSION_CLASS = _get_version_class() class SetuptoolsOutdatedWarning(Warning): pass # append so integrators can disable the warning warnings.simplefilter("error", SetuptoolsOutdatedWarning, append=True) def _warn_if_setuptools_outdated(): if VERSION_CLASS is None: warnings.warn("your setuptools is too old (<12)", SetuptoolsOutdatedWarning) def callable_or_entrypoint(group, callable_or_name): trace("ep", (group, callable_or_name)) if callable(callable_or_name): return callable_or_name for ep in iter_entry_points(group, callable_or_name): trace("ep found:", ep.name) return ep.load() def tag_to_version(tag, config=None): """ take a tag that might be prefixed with a keyword and return only the version part :param config: optional configuration object """ trace("tag", tag) if not config: config = Configuration() tagdict = _parse_version_tag(tag, config) if not isinstance(tagdict, dict) or not tagdict.get("version", None): warnings.warn("tag {!r} no version found".format(tag)) return None version = tagdict["version"] trace("version pre parse", version) if tagdict.get("suffix", ""): warnings.warn( "tag {!r} will be stripped of its suffix '{}'".format( tag, tagdict["suffix"] ) ) if VERSION_CLASS is not None: version = pkg_parse_version(version) trace("version", repr(version)) return version def tags_to_versions(tags, config=None): """ take tags that might be prefixed with a keyword and return only the version part :param tags: an iterable of tags :param config: optional configuration object """ result = [] for tag in tags: tag = tag_to_version(tag, config=config) if tag: result.append(tag) return result class ScmVersion(object): def __init__( self, tag_version, distance=None, node=None, dirty=False, preformatted=False, branch=None, **kw ): if kw: trace("unknown args", kw) self.tag = tag_version if dirty and distance is None: distance = 0 self.distance = distance self.node = node self.time = datetime.datetime.now() self._extra = kw self.dirty = dirty self.preformatted = preformatted self.branch = branch @property def extra(self): warnings.warn( "ScmVersion.extra is deprecated and will be removed in future", category=DeprecationWarning, stacklevel=2, ) return self._extra @property def exact(self): return self.distance is None def __repr__(self): return self.format_with( "" ) def format_with(self, fmt, **kw): return fmt.format( time=self.time, tag=self.tag, distance=self.distance, node=self.node, dirty=self.dirty, branch=self.branch, **kw ) def format_choice(self, clean_format, dirty_format, **kw): return self.format_with(dirty_format if self.dirty else clean_format, **kw) def format_next_version(self, guess_next, fmt="{guessed}.dev{distance}", **kw): guessed = guess_next(self.tag, **kw) return self.format_with(fmt, guessed=guessed) def _parse_tag(tag, preformatted, config): if preformatted: return tag if VERSION_CLASS is None or not isinstance(tag, VERSION_CLASS): tag = tag_to_version(tag, config) return tag def meta( tag, distance=None, dirty=False, node=None, preformatted=False, config=None, **kw ): if not config: warnings.warn( "meta invoked without explicit configuration," " will use defaults where required." ) parsed_version = _parse_tag(tag, preformatted, config) trace("version", tag, "->", parsed_version) assert parsed_version is not None, "cant parse version %s" % tag return ScmVersion(parsed_version, distance, node, dirty, preformatted, **kw) def guess_next_version(tag_version): version = _strip_local(str(tag_version)) return _bump_dev(version) or _bump_regex(version) def _strip_local(version_string): public, sep, local = version_string.partition("+") return public def _bump_dev(version): if ".dev" not in version: return prefix, tail = version.rsplit(".dev", 1) assert tail == "0", "own dev numbers are unsupported" return prefix def _bump_regex(version): prefix, tail = re.match(r"(.*?)(\d+)$", version).groups() return "%s%d" % (prefix, int(tail) + 1) def guess_next_dev_version(version): 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): parts = map(int, str(version).split(".")) parts = _pad(parts, retain, 0) if increment: parts[-1] += 1 parts = _pad(parts, SEMVER_LEN, 0) return ".".join(map(str, parts)) def simplified_semver_version(version): if version.exact: return guess_next_simple_semver(version.tag, retain=SEMVER_LEN, increment=False) else: if version.branch is not None and "feature" in version.branch: return version.format_next_version( guess_next_simple_semver, retain=SEMVER_MINOR ) else: return version.format_next_version( guess_next_simple_semver, retain=SEMVER_PATCH ) def _format_local_with_time(version, time_format): if version.exact or version.node is None: return version.format_choice( "", "+d{time:{time_format}}", time_format=time_format ) else: return version.format_choice( "+{node}", "+{node}.d{time:{time_format}}", time_format=time_format ) def get_local_node_and_date(version): return _format_local_with_time(version, time_format="%Y%m%d") def get_local_node_and_timestamp(version, fmt="%Y%m%d%H%M%S"): return _format_local_with_time(version, time_format=fmt) def get_local_dirty_tag(version): return version.format_choice("", "+dirty") def postrelease_version(version): if version.exact: return version.format_with("{tag}") else: return version.format_with("{tag}.post{distance}") def format_version(version, **config): trace("scm version", version) trace("config", config) if version.preformatted: return version.tag version_scheme = callable_or_entrypoint( "setuptools_scm.version_scheme", config["version_scheme"] ) local_scheme = callable_or_entrypoint( "setuptools_scm.local_scheme", config["local_scheme"] ) main_version = version_scheme(version) trace("version", main_version) local_version = local_scheme(version) trace("local_version", local_version) return version_scheme(version) + local_scheme(version) setuptools_scm-3.4.3/src/setuptools_scm/win_py31_compat.py000066400000000000000000000117521361213237500240550ustar00rootroot00000000000000""" Backport of os.path.samefile for Python prior to 3.2 on Windows from jaraco.windows 3.8. DON'T EDIT THIS FILE! Instead, file tickets and PR's with `jaraco.windows `_ and request a port to setuptools_scm. """ import os import nt import posixpath import ctypes.wintypes import sys import __builtin__ as builtins ## # From jaraco.windows.error def format_system_message(errno): """ Call FormatMessage with a system error number to retrieve the descriptive error message. """ # first some flags used by FormatMessageW ALLOCATE_BUFFER = 0x100 FROM_SYSTEM = 0x1000 # Let FormatMessageW allocate the buffer (we'll free it below) # Also, let it know we want a system error message. flags = ALLOCATE_BUFFER | FROM_SYSTEM source = None message_id = errno language_id = 0 result_buffer = ctypes.wintypes.LPWSTR() buffer_size = 0 arguments = None bytes = ctypes.windll.kernel32.FormatMessageW( flags, source, message_id, language_id, ctypes.byref(result_buffer), buffer_size, arguments, ) # note the following will cause an infinite loop if GetLastError # repeatedly returns an error that cannot be formatted, although # this should not happen. handle_nonzero_success(bytes) message = result_buffer.value ctypes.windll.kernel32.LocalFree(result_buffer) return message class WindowsError(builtins.WindowsError): """ More info about errors at http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx """ def __init__(self, value=None): if value is None: value = ctypes.windll.kernel32.GetLastError() strerror = format_system_message(value) if sys.version_info > (3, 3): args = 0, strerror, None, value else: args = value, strerror super(WindowsError, self).__init__(*args) @property def message(self): return self.strerror @property def code(self): return self.winerror def __str__(self): return self.message def __repr__(self): return '{self.__class__.__name__}({self.winerror})'.format(**vars()) def handle_nonzero_success(result): if result == 0: raise WindowsError() ## # From jaraco.windows.api.filesystem FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 FILE_FLAG_BACKUP_SEMANTICS = 0x2000000 OPEN_EXISTING = 3 FILE_ATTRIBUTE_NORMAL = 0x80 FILE_READ_ATTRIBUTES = 0x80 INVALID_HANDLE_VALUE = ctypes.wintypes.HANDLE(-1).value class BY_HANDLE_FILE_INFORMATION(ctypes.Structure): _fields_ = [ ('file_attributes', ctypes.wintypes.DWORD), ('creation_time', ctypes.wintypes.FILETIME), ('last_access_time', ctypes.wintypes.FILETIME), ('last_write_time', ctypes.wintypes.FILETIME), ('volume_serial_number', ctypes.wintypes.DWORD), ('file_size_high', ctypes.wintypes.DWORD), ('file_size_low', ctypes.wintypes.DWORD), ('number_of_links', ctypes.wintypes.DWORD), ('file_index_high', ctypes.wintypes.DWORD), ('file_index_low', ctypes.wintypes.DWORD), ] @property def file_size(self): return (self.file_size_high << 32) + self.file_size_low @property def file_index(self): return (self.file_index_high << 32) + self.file_index_low class SECURITY_ATTRIBUTES(ctypes.Structure): _fields_ = ( ('length', ctypes.wintypes.DWORD), ('p_security_descriptor', ctypes.wintypes.LPVOID), ('inherit_handle', ctypes.wintypes.BOOLEAN), ) LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES) CreateFile = ctypes.windll.kernel32.CreateFileW CreateFile.argtypes = ( ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, LPSECURITY_ATTRIBUTES, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.HANDLE, ) CreateFile.restype = ctypes.wintypes.HANDLE GetFileInformationByHandle = ctypes.windll.kernel32.GetFileInformationByHandle GetFileInformationByHandle.restype = ctypes.wintypes.BOOL GetFileInformationByHandle.argtypes = ( ctypes.wintypes.HANDLE, ctypes.POINTER(BY_HANDLE_FILE_INFORMATION), ) ## # From jaraco.windows.filesystem def compat_stat(path): """ Generate stat as found on Python 3.2 and later. """ stat = os.stat(path) info = get_file_info(path) # rewrite st_ino, st_dev, and st_nlink based on file info return nt.stat_result( (stat.st_mode,) + (info.file_index, info.volume_serial_number, info.number_of_links) + stat[4:] ) def samefile(f1, f2): """ Backport of samefile from Python 3.2 with support for Windows. """ return posixpath.samestat(compat_stat(f1), compat_stat(f2)) def get_file_info(path): # open the file the same way CPython does in posixmodule.c desired_access = FILE_READ_ATTRIBUTES share_mode = 0 security_attributes = None creation_disposition = OPEN_EXISTING flags_and_attributes = ( FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT ) template_file = None handle = CreateFile( path, desired_access, share_mode, security_attributes, creation_disposition, flags_and_attributes, template_file, ) if handle == INVALID_HANDLE_VALUE: raise WindowsError() info = BY_HANDLE_FILE_INFORMATION() res = GetFileInformationByHandle(handle, info) handle_nonzero_success(res) return info setuptools_scm-3.4.3/testing/000077500000000000000000000000001361213237500162645ustar00rootroot00000000000000setuptools_scm-3.4.3/testing/conftest.py000066400000000000000000000041271361213237500204670ustar00rootroot00000000000000import os import itertools import pytest os.environ["SETUPTOOLS_SCM_DEBUG"] = "1" VERSION_PKGS = ["setuptools", "setuptools_scm"] def pytest_report_header(): import pkg_resources res = [] for pkg in VERSION_PKGS: version = pkg_resources.get_distribution(pkg).version path = __import__(pkg).__file__ res.append("{} version {} from {!r}".format(pkg, version, path)) return res class Wd(object): commit_command = None add_command = None def __init__(self, cwd): self.cwd = cwd self.__counter = itertools.count() def __call__(self, cmd, **kw): if kw: cmd = cmd.format(**kw) from setuptools_scm.utils import do return do(cmd, self.cwd) def write(self, name, value, **kw): filename = self.cwd.join(name) if kw: value = value.format(**kw) filename.write(value) return filename def _reason(self, given_reason): if given_reason is None: return "number-{c}".format(c=next(self.__counter)) else: return given_reason def add_and_commit(self, reason=None): self(self.add_command) self.commit(reason) def commit(self, reason=None): reason = self._reason(reason) self(self.commit_command, reason=reason) def commit_testfile(self, reason=None): reason = self._reason(reason) self.write("test.txt", "test {reason}", reason=reason) self(self.add_command) self.commit(reason=reason) def get_version(self, **kw): __tracebackhide__ = True from setuptools_scm import get_version version = get_version(root=str(self.cwd), fallback_root=str(self.cwd), **kw) print(version) return version @property def version(self): __tracebackhide__ = True return self.get_version() @pytest.yield_fixture(autouse=True) def debug_mode(): from setuptools_scm import utils utils.DEBUG = True yield utils.DEBUG = False @pytest.fixture def wd(tmpdir): return Wd(tmpdir.ensure("wd", dir=True)) setuptools_scm-3.4.3/testing/play_out_381.bash000077500000000000000000000011011361213237500213460ustar00rootroot00000000000000#!/usr/bin/env bash set -euxo pipefail rm -rf y z home venv tmp [ ! -d black ] && git clone https://github.com/psf/black export SETUPTOOLS_SCM_DEBUG=1 export PRE_COMMIT_HOME="$PWD/home" export TMPDIR="$PWD/tmp" git init y git init z git -C z commit --allow-empty -m 'commit!' git -C y submodule add "$PWD/z" cat > "$PWD/y/.git/modules/z/hooks/pre-commit" < ../file1 @pytest.mark.skipif( sys.platform == "win32", reason="symlinks to files not supported on windows" ) def test_symlink_file_source_not_in_scm(inwd): (inwd.cwd / "adir" / "file1link").mksymlinkto("../file1") assert set(find_files("adir")) == _sep({"adir/filea"}) @pytest.mark.skipif(sys.platform == "win32", reason="symlinks to dir not supported") def test_symlink_loop(inwd): (inwd.cwd / "adir" / "loop").mksymlinkto("../adir") inwd.add_and_commit() assert set(find_files("adir")) == _sep({"adir/filea", "adir/loop"}) # -> ../adir @pytest.mark.skipif(sys.platform == "win32", reason="symlinks to dir not supported") def test_symlink_loop_outside_path(inwd): (inwd.cwd / "bdir" / "loop").mksymlinkto("../bdir") (inwd.cwd / "adir" / "bdirlink").mksymlinkto("../bdir") inwd.add_and_commit() assert set(find_files("adir")) == _sep({"adir/filea", "adir/bdirlink/fileb"}) @pytest.mark.skipif(sys.platform == "win32", reason="symlinks to dir not supported") def test_symlink_dir_out_of_git(inwd): (inwd.cwd / "adir" / "outsidedirlink").mksymlinkto(os.path.join(__file__, "..")) inwd.add_and_commit() assert set(find_files("adir")) == _sep({"adir/filea"}) @pytest.mark.skipif( sys.platform == "win32", reason="symlinks to files not supported on windows" ) def test_symlink_file_out_of_git(inwd): (inwd.cwd / "adir" / "outsidefilelink").mksymlinkto(__file__) inwd.add_and_commit() assert set(find_files("adir")) == _sep({"adir/filea"}) def test_empty_root(inwd): subdir = inwd.cwd / "cdir" / "subdir" subdir.ensure(dir=True) (subdir / "filec").ensure(file=True) inwd.add_and_commit() assert set(find_files("cdir")) == _sep({"cdir/subdir/filec"}) def test_empty_subdir(inwd): subdir = inwd.cwd / "adir" / "emptysubdir" / "subdir" subdir.ensure(dir=True) (subdir / "xfile").ensure(file=True) inwd.add_and_commit() assert set(find_files("adir")) == _sep( {"adir/filea", "adir/emptysubdir/subdir/xfile"} ) @pytest.mark.skipif(sys.platform == "win32", reason="symlinks not supported on windows") def test_double_include_through_symlink(inwd): (inwd.cwd / "data").ensure(dir=True) (inwd.cwd / "data" / "datafile").ensure(file=True) (inwd.cwd / "adir" / "datalink").mksymlinkto("../data") (inwd.cwd / "adir" / "filealink").mksymlinkto("filea") inwd.add_and_commit() assert set(find_files()) == _sep( { "file1", "adir/datalink", # -> ../data "adir/filealink", # -> filea "adir/filea", "bdir/fileb", "data/datafile", } ) @pytest.mark.skipif(sys.platform == "win32", reason="symlinks not supported on windows") def test_symlink_not_in_scm_while_target_is(inwd): (inwd.cwd / "data").ensure(dir=True) (inwd.cwd / "data" / "datafile").ensure(file=True) inwd.add_and_commit() (inwd.cwd / "adir" / "datalink").mksymlinkto("../data") (inwd.cwd / "adir" / "filealink").mksymlinkto("filea") assert set(find_files()) == _sep( { "file1", "adir/filea", # adir/datalink and adir/afilelink not included # because the symlink themselves are not in scm "bdir/fileb", "data/datafile", } ) setuptools_scm-3.4.3/testing/test_functions.py000066400000000000000000000062621361213237500217130ustar00rootroot00000000000000import pytest import sys 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, ) from setuptools_scm.config import Configuration from setuptools_scm.utils import has_command PY3 = sys.version_info > (2,) class MockTime(object): def __format__(self, *k): return "time" @pytest.mark.parametrize( "tag, expected", [ ("1.1", "1.2"), ("1.2.dev", "1.2"), ("1.1a2", "1.1a3"), ("23.24.post2+deadbeef", "23.24.post3"), ], ) def test_next_tag(tag, expected): version = pkg_resources.parse_version(tag) assert guess_next_version(version) == expected c = Configuration() VERSIONS = { "exact": meta("1.1", distance=None, dirty=False, config=c), "zerodistance": meta("1.1", distance=0, dirty=False, config=c), "dirty": meta("1.1", distance=None, dirty=True, config=c), "distance": meta("1.1", distance=3, dirty=False, config=c), "distancedirty": meta("1.1", distance=3, dirty=True, config=c), } @pytest.mark.parametrize( "version,scheme,expected", [ ("exact", "guess-next-dev node-and-date", "1.1"), ("zerodistance", "guess-next-dev node-and-date", "1.2.dev0"), ("dirty", "guess-next-dev node-and-date", "1.2.dev0+dtime"), ("distance", "guess-next-dev node-and-date", "1.2.dev3"), ("distancedirty", "guess-next-dev node-and-date", "1.2.dev3+dtime"), ("exact", "post-release node-and-date", "1.1"), ("zerodistance", "post-release node-and-date", "1.1.post0"), ("dirty", "post-release node-and-date", "1.1.post0+dtime"), ("distance", "post-release node-and-date", "1.1.post3"), ("distancedirty", "post-release node-and-date", "1.1.post3+dtime"), ], ) def test_format_version(version, monkeypatch, scheme, expected): version = VERSIONS[version] monkeypatch.setattr(version, "time", MockTime()) vs, ls = scheme.split() assert format_version(version, version_scheme=vs, local_scheme=ls) == expected def test_dump_version_doesnt_bail_on_value_error(tmpdir): 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:") @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): monkeypatch.setenv(PRETEND_KEY, version) get_version(write_to=str(tmpdir.join("VERSION.txt"))) assert tmpdir.join("VERSION.txt").read() == version def test_has_command(recwarn): assert not has_command("yadayada_setuptools_aint_ne") msg = recwarn.pop() assert "yadayada" in str(msg.message) @pytest.mark.parametrize( "tag, expected_version", [ ("1.1", "1.1"), ("release-1.1", "1.1"), pytest.param("3.3.1-rc26", "3.3.1rc26", marks=pytest.mark.issue(266)), ], ) def test_tag_to_version(tag, expected_version): version = str(tag_to_version(tag)) assert version == expected_version setuptools_scm-3.4.3/testing/test_git.py000066400000000000000000000157531361213237500204730ustar00rootroot00000000000000import sys from setuptools_scm import integration from setuptools_scm.utils import do from setuptools_scm import git import pytest from datetime import date from os.path import join as opj from setuptools_scm.file_finder_git import git_find_files @pytest.fixture def wd(wd, monkeypatch): monkeypatch.delenv("HOME", raising=False) 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}" return wd @pytest.mark.parametrize( "given, tag, number, node, dirty", [ ("3.3.1-rc26-0-g9df187b", "3.3.1-rc26", 0, "g9df187b", False), ("17.33.0-rc-17-g38c3047c0", "17.33.0-rc", 17, "g38c3047c0", False), ], ) def test_parse_describe_output(given, tag, number, node, dirty): parsed = git._git_parse_describe(given) assert parsed == (tag, number, node, dirty) def test_root_relative_to(tmpdir, wd, monkeypatch): monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") p = wd.cwd.ensure("sub/package", dir=1) p.join("setup.py").write( """from setuptools import setup setup(use_scm_version={"root": "../..", "relative_to": __file__}) """ ) res = do((sys.executable, "setup.py", "--version"), p) assert res == "0.1.dev0" @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/298") def test_file_finder_no_history(wd, caplog): file_list = git_find_files(str(wd.cwd)) assert file_list == [] assert "listing git files failed - pretending there aren't any" in caplog.text @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/281") def test_parse_call_order(wd): git.parse(str(wd.cwd), git.DEFAULT_DESCRIBE) def test_version_from_git(wd): assert wd.version == "0.1.dev0" wd.commit_testfile() assert wd.version.startswith("0.1.dev1+g") assert not wd.version.endswith("1-") wd("git tag v0.1") assert wd.version == "0.1" wd.write("test.txt", "test2") assert wd.version.startswith("0.2.dev0+g") wd.commit_testfile() assert wd.version.startswith("0.2.dev1+g") wd("git tag version-0.2") assert wd.version.startswith("0.2") wd.commit_testfile() wd("git tag version-0.2.post210+gbe48adfpost3+g0cc25f2") with pytest.warns( UserWarning, match="tag '.*' will be stripped of its suffix '.*'" ): assert wd.version.startswith("0.2") wd.commit_testfile() wd("git tag 17.33.0-rc") assert wd.version == "17.33.0rc0" @pytest.mark.issue(179) def test_unicode_version_scheme(wd): scheme = b"guess-next-dev".decode("ascii") assert wd.get_version(version_scheme=scheme) @pytest.mark.issue(108) @pytest.mark.issue(109) def test_git_worktree(wd): wd.write("test.txt", "test2") # untracked files dont change the state assert wd.version == "0.1.dev0" wd("git add test.txt") assert wd.version.startswith("0.1.dev0+d") @pytest.mark.issue(86) def test_git_dirty_notag(wd): wd.commit_testfile() wd.write("test.txt", "test2") wd("git add test.txt") assert wd.version.startswith("0.1.dev1") today = date.today() # we are dirty, check for the tag assert today.strftime(".d%Y%m%d") in wd.version @pytest.mark.issue(193) def test_git_worktree_support(wd, tmpdir): wd.commit_testfile() worktree = tmpdir.join("work_tree") wd("git worktree add -b work-tree %s" % worktree) res = do([sys.executable, "-m", "setuptools_scm", "ls"], cwd=worktree) assert str(worktree) in res @pytest.fixture def shallow_wd(wd, tmpdir): wd.commit_testfile() wd.commit_testfile() wd.commit_testfile() target = tmpdir.join("wd_shallow") do(["git", "clone", "file://%s" % wd.cwd, str(target), "--depth=1"]) return target def test_git_parse_shallow_warns(shallow_wd, recwarn): git.parse(str(shallow_wd)) msg = recwarn.pop() assert "is shallow and may cause errors" in str(msg.message) def test_git_parse_shallow_fail(shallow_wd): with pytest.raises(ValueError) as einfo: git.parse(str(shallow_wd), pre_parse=git.fail_on_shallow) assert "git fetch" in str(einfo.value) def test_git_shallow_autocorrect(shallow_wd, recwarn): git.parse(str(shallow_wd), pre_parse=git.fetch_on_shallow) msg = recwarn.pop() assert "git fetch was used to rectify" in str(msg.message) git.parse(str(shallow_wd), pre_parse=git.fail_on_shallow) def test_find_files_stop_at_root_git(wd): wd.commit_testfile() wd.cwd.ensure("project/setup.cfg") assert integration.find_files(str(wd.cwd / "project")) == [] @pytest.mark.issue(128) def test_parse_no_worktree(tmpdir): ret = git.parse(str(tmpdir)) assert ret is None def test_alphanumeric_tags_match(wd): wd.commit_testfile() wd("git tag newstyle-development-started") assert wd.version.startswith("0.1.dev1+g") def test_git_archive_export_ignore(wd): wd.write("test1.txt", "test") wd.write("test2.txt", "test") wd.write( ".git/info/attributes", # Explicitly include test1.txt so that the test is not affected by # a potentially global gitattributes file on the test machine. "/test1.txt -export-ignore\n/test2.txt export-ignore", ) wd("git add test1.txt test2.txt") wd.commit() with wd.cwd.as_cwd(): assert integration.find_files(".") == [opj(".", "test1.txt")] @pytest.mark.issue(228) def test_git_archive_subdirectory(wd): wd("mkdir foobar") wd.write("foobar/test1.txt", "test") wd("git add foobar") wd.commit() with wd.cwd.as_cwd(): assert integration.find_files(".") == [opj(".", "foobar", "test1.txt")] @pytest.mark.issue(251) def test_git_archive_run_from_subdirectory(wd): wd("mkdir foobar") wd.write("foobar/test1.txt", "test") wd("git add foobar") wd.commit() with (wd.cwd / "foobar").as_cwd(): assert integration.find_files(".") == [opj(".", "test1.txt")] def test_git_feature_branch_increments_major(wd): wd.commit_testfile() wd("git tag 1.0.0") wd.commit_testfile() assert wd.get_version(version_scheme="python-simplified-semver").startswith("1.0.1") wd("git checkout -b feature/fun") wd.commit_testfile() assert wd.get_version(version_scheme="python-simplified-semver").startswith("1.1.0") @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/303") def test_not_matching_tags(wd): wd.commit_testfile() wd("git tag apache-arrow-0.11.1") wd.commit_testfile() wd("git tag apache-arrow-js-0.9.9") wd.commit_testfile() assert wd.get_version( tag_regex=r"^apache-arrow-([\.0-9]+)$", git_describe_command="git describe --dirty --tags --long --exclude *js* ", ).startswith("0.11.2") @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 monkeypatch.setenv("GIT_DIR", __file__) assert wd.version == normal setuptools_scm-3.4.3/testing/test_integration.py000066400000000000000000000021131361213237500222150ustar00rootroot00000000000000import sys import pytest from setuptools_scm.utils import do @pytest.fixture def wd(wd): 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}" return wd def test_pyproject_support(tmpdir, monkeypatch): 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("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): monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") pkg = tmpdir.join("wd") pkg.join("pyproject.toml").write("""[tool.setuptools_scm]""") pkg.join("setup.py").write("__import__('setuptools').setup()") res = do((sys.executable, "setup.py", "--version"), pkg) assert res == "0.1.dev0" setuptools_scm-3.4.3/testing/test_main.py000066400000000000000000000003761361213237500206270ustar00rootroot00000000000000import os.path def test_main(): mainfile = os.path.join( os.path.dirname(__file__), "..", "src", "setuptools_scm", "__main__.py" ) with open(mainfile) as f: code = compile(f.read(), "__main__.py", "exec") exec(code) setuptools_scm-3.4.3/testing/test_mercurial.py000066400000000000000000000113131361213237500216570ustar00rootroot00000000000000from 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 import pytest @pytest.fixture def wd(wd): wd("hg init") wd.add_command = "hg add ." wd.commit_command = 'hg commit -m test-{reason} -u test -d "0 0"' return wd archival_mapping = { "1.0": {"tag": "1.0"}, "1.1.dev3+h000000000000": { "latesttag": "1.0", "latesttagdistance": "3", "node": "0" * 20, }, "0.0": {"node": "0" * 20}, "1.2.2": {"tag": "release-1.2.2"}, "1.2.2.dev0": {"tag": "release-1.2.2.dev"}, } @pytest.mark.parametrize("expected,data", sorted(archival_mapping.items())) def test_archival_to_version(expected, data): config = Configuration() version = archival_to_version(data, config=config) assert ( format_version( version, version_scheme="guess-next-dev", local_scheme="node-and-date" ) == expected ) def test_find_files_stop_at_root_hg(wd): wd.commit_testfile() wd.cwd.ensure("project/setup.cfg") # setup.cfg has not been committed assert integration.find_files(str(wd.cwd / "project")) == [] # issue 251 wd.add_and_commit() with (wd.cwd / "project").as_cwd(): assert integration.find_files() == ["setup.cfg"] # XXX: better tests for tag prefixes def test_version_from_hg_id(wd): assert wd.version == "0.0" wd.commit_testfile() assert wd.version.startswith("0.1.dev2+") # tagging commit is considered the tag wd('hg tag v0.1 -u test -d "0 0"') assert wd.version == "0.1" wd.commit_testfile() assert wd.version.startswith("0.2.dev2") wd("hg up v0.1") assert wd.version == "0.1" # commit originating from the taged revision # that is not a actual tag wd.commit_testfile() assert wd.version.startswith("0.2.dev1+") # several tags wd("hg up") wd('hg tag v0.2 -u test -d "0 0"') wd('hg tag v0.3 -u test -d "0 0" -r v0.2') assert wd.version == "0.3" def test_version_from_archival(wd): # entrypoints are unordered, # cleaning the wd ensure this test wont break randomly wd.cwd.join(".hg").remove() wd.write(".hg_archival.txt", "node: 000000000000\n" "tag: 0.1\n") assert wd.version == "0.1" wd.write( ".hg_archival.txt", "node: 000000000000\n" "latesttag: 0.1\n" "latesttagdistance: 3\n", ) assert wd.version == "0.2.dev3+h000000000000" @pytest.mark.issue("#72") def test_version_in_merge(wd): wd.commit_testfile() wd.commit_testfile() wd("hg up 0") wd.commit_testfile() wd("hg merge --tool :merge") assert wd.version is not None @pytest.mark.issue(128) def test_parse_no_worktree(tmpdir): ret = parse(str(tmpdir)) assert ret is None @pytest.fixture def version_1_0(wd): wd("hg branch default") wd.commit_testfile() wd('hg tag 1.0.0 -u test -d "0 0"') return wd @pytest.fixture def pre_merge_commit_after_tag(wd, version_1_0): wd("hg branch testbranch") wd.write("branchfile", "branchtext") wd(wd.add_command) wd.commit() wd("hg update default") wd("hg merge testbranch") return wd @pytest.mark.usefixtures("pre_merge_commit_after_tag") def test_version_bump_before_merge_commit(wd): assert wd.version.startswith("1.0.1.dev1+") @pytest.mark.issue(219) @pytest.mark.usefixtures("pre_merge_commit_after_tag") def test_version_bump_from_merge_commit(wd): wd.commit() assert wd.version.startswith("1.0.1.dev3+") # issue 219 @pytest.mark.usefixtures("version_1_0") def test_version_bump_from_commit_including_hgtag_mods(wd): """ Test the case where a commit includes changes to .hgtags and other files """ with wd.cwd.join(".hgtags").open("a") as tagfile: tagfile.write("0 0\n") wd.write("branchfile", "branchtext") wd(wd.add_command) assert wd.version.startswith("1.0.1.dev1+") # bump from dirty version wd.commit() # commits both the testfile _and_ .hgtags assert wd.version.startswith("1.0.1.dev2+") @pytest.mark.issue(229) @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 """ 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): wd.commit_testfile() assert wd.get_version(version_scheme="python-simplified-semver").startswith("1.0.1") wd("hg branch feature/fun") assert wd.get_version(version_scheme="python-simplified-semver").startswith("1.1.0") setuptools_scm-3.4.3/testing/test_regressions.py000066400000000000000000000052071361213237500222440ustar00rootroot00000000000000import sys import subprocess from setuptools_scm import get_version from setuptools_scm.git import parse from setuptools_scm.utils import do_ex, do import pytest def test_pkginfo_noscmroot(tmpdir, monkeypatch): """if we are indeed a sdist, the root does not apply""" monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") # we should get the version from pkg-info if git is broken p = tmpdir.ensure("sub/package", dir=1) tmpdir.mkdir(".git") p.join("setup.py").write( "from setuptools import setup;" 'setup(use_scm_version={"root": ".."})' ) _, stderr, ret = do_ex((sys.executable, "setup.py", "--version"), p) assert "setuptools-scm was unable to detect version for" in stderr assert ret == 1 p.join("PKG-INFO").write("Version: 1.0") res = do((sys.executable, "setup.py", "--version"), p) assert res == "1.0" do("git init", p.dirpath()) res = do((sys.executable, "setup.py", "--version"), p) assert res == "0.1.dev0" def test_pip_egg_info(tmpdir, monkeypatch): """if we are indeed a sdist, the root does not apply""" # we should get the version from pkg-info if git is broken p = tmpdir.ensure("sub/package", dir=1) tmpdir.mkdir(".git") p.join("setup.py").write( "from setuptools import setup;" 'setup(use_scm_version={"root": ".."})' ) with pytest.raises(LookupError): get_version(root=p.strpath, fallback_root=p.strpath) p.ensure("pip-egg-info/random.egg-info/PKG-INFO").write("Version: 1.0") assert get_version(root=p.strpath, fallback_root=p.strpath) == "1.0" @pytest.mark.issue(164) def test_pip_download(tmpdir, monkeypatch): monkeypatch.chdir(tmpdir) subprocess.check_call([sys.executable, "-m", "pip", "download", "lz4==0.9.0"]) def test_use_scm_version_callable(tmpdir, monkeypatch): """use of callable as use_scm_version argument""" monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") p = tmpdir.ensure("sub/package", dir=1) p.join("setup.py").write( """from setuptools import setup def vcfg(): from setuptools_scm.version import guess_next_dev_version def vs(v): return guess_next_dev_version(v) return {"version_scheme": vs} setup(use_scm_version=vcfg) """ ) p.join("PKG-INFO").write("Version: 1.0") res = do((sys.executable, "setup.py", "--version"), p) assert res == "1.0" @pytest.mark.skipif(sys.platform != "win32", reason="this bug is only valid on windows") def test_case_mismatch_on_windows_git(tmpdir): """Case insensitive path checks on Windows""" p = tmpdir.ensure("CapitalizedDir", dir=1) do("git init", p) res = parse(str(p).lower()) assert res is not None setuptools_scm-3.4.3/testing/test_setuptools_support.py000066400000000000000000000042171361213237500237160ustar00rootroot00000000000000""" integration tests that check setuptools version support """ import sys import os import subprocess import pytest pytestmark = [ pytest.mark.skipif( "sys.version_info >= (3,6,0)", reason="integration with old versions no longer needed on py3.6+", ), pytest.mark.xfail( sys.platform == "win32", reason="path behaves unexpected on windows ci" ), ] @pytest.fixture(scope="session") def get_setuptools_packagedir(request): targets = request.config.cache.makedir("setuptools_installs") def makeinstall(version): target = targets.ensure(version, dir=1) subprocess.check_call( [ sys.executable, "-m", "pip", "install", "--no-binary", "setuptools", "setuptools==" + version, "-t", str(target), ] ) return target return makeinstall SCRIPT = """ from __future__ import print_function import sys import setuptools print(setuptools.__version__, 'expected', sys.argv[1]) import setuptools_scm.version from setuptools_scm.__main__ import main main() """ def check(packagedir, expected_version, **env): old_pythonpath = os.environ.get("PYTHONPATH") if old_pythonpath: pythonpath = "{}:{}".format(old_pythonpath, packagedir) else: pythonpath = str(packagedir) subprocess.check_call( [sys.executable, "-c", SCRIPT, expected_version], env=dict(os.environ, PYTHONPATH=pythonpath, **env), ) def test_old_setuptools_fails(get_setuptools_packagedir): packagedir = get_setuptools_packagedir("0.9.8") with pytest.raises(subprocess.CalledProcessError): check(packagedir, "0.9.8") def test_old_setuptools_allows_with_warnings(get_setuptools_packagedir): packagedir = get_setuptools_packagedir("0.9.8") # filter using warning since in the early python startup check(packagedir, "0.9.8", PYTHONWARNINGS="once::Warning") def test_distlib_setuptools_works(get_setuptools_packagedir): packagedir = get_setuptools_packagedir("12.0.1") check(packagedir, "12.0.1") setuptools_scm-3.4.3/testing/test_version.py000066400000000000000000000042251361213237500213650ustar00rootroot00000000000000import pytest from setuptools_scm.config import Configuration from setuptools_scm.version import meta, simplified_semver_version, tags_to_versions c = Configuration() @pytest.mark.parametrize( "version, expected_next", [ pytest.param(meta("1.0.0", config=c), "1.0.0", id="exact"), pytest.param(meta("1.0", config=c), "1.0.0", id="short_tag"), pytest.param( meta("1.0.0", distance=2, branch="default", config=c), "1.0.1.dev2", id="normal_branch", ), pytest.param( meta("1.0", distance=2, branch="default", config=c), "1.0.1.dev2", id="normal_branch_short_tag", ), pytest.param( meta("1.0.0", distance=2, branch="feature", config=c), "1.1.0.dev2", id="feature_branch", ), pytest.param( meta("1.0", distance=2, branch="feature", config=c), "1.1.0.dev2", id="feature_branch_short_tag", ), pytest.param( meta("1.0.0", distance=2, branch="features/test", config=c), "1.1.0.dev2", id="feature_in_branch", ), ], ) def test_next_semver(version, expected_next): computed = simplified_semver_version(version) assert computed == expected_next @pytest.mark.parametrize( "tag, expected", [ pytest.param("v1.0.0", "1.0.0"), pytest.param("v1.0.0-rc.1", "1.0.0rc1"), pytest.param("v1.0.0-rc.1+-25259o4382757gjurh54", "1.0.0rc1"), ], ) def test_tag_regex1(tag, expected): config = Configuration() config.tag_regex = r"^(?Pv)?(?P[^\+]+)(?P.*)?$" if "+" in tag: # pytest bug wrt cardinality with pytest.warns(UserWarning): result = meta(tag, config=config) else: result = meta(tag, config=config) assert result.tag.public == expected @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/286") def test_tags_to_versions(): config = Configuration() versions = tags_to_versions(["1.0", "2.0", "3.0"], config=config) assert isinstance(versions, list) # enable subscription setuptools_scm-3.4.3/tox.ini000066400000000000000000000030611361213237500161220ustar00rootroot00000000000000[tox] envlist=py{27,34,35,36,37,38}-test,flake8,check_readme,py{27,37}-selfcheck [pytest] testpaths=testing filterwarnings=error markers= issue(id): reference to github issue [flake8] max-complexity = 10 max-line-length = 88 ignore=E203,W503 exclude= .git, .tox, .env, .venv, .pytest_cache, __pycache__, ./setuptools_scm/win_py31_compat.py [testenv] usedevelop=True skip_install= selfcheck: True test: False deps= pytest setuptools >= 42 commands= test: py.test [] selfcheck: python setup.py --version extras = toml [testenv:flake8] skip_install=True deps= flake8 mccabe commands = flake8 setuptools_scm/ testing/ setup.py --exclude=setuptools_scm/win_py31_compat.py [testenv:check_readme] skip_install=True setenv = SETUPTOOLS_SCM_PRETEND_VERSION=2.0 deps= readme check-manifest commands= python setup.py check -r rst2html.py README.rst {envlogdir}/README.html --strict [] check-manifest [testenv:upload] deps= wheel twine commands= python setup.py clean --all rotate -k - -m .whl,.tar.gz,.zip python setup.py -q egg_info python setup.py -q sdist --formats zip bdist_wheel register [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