Commit 29c32645 authored by Wei Shoulin's avatar Wei Shoulin
Browse files

remove .eggs

parent 1773491b
This directory contains eggs that were downloaded by setuptools to build, test, and run plug-ins.
This directory caches those eggs to prevent repeated downloads.
However, it is safe to delete this directory.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Metadata-Version: 2.1
Name: setuptools-scm
Version: 5.0.1
Summary: the blessed package to manage your versions by scm tags
Home-page: https://github.com/pypa/setuptools_scm/
Author: Ronny Pfannschmidt
Author-email: opensource@ronnypfannschmidt.de
License: MIT
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Version Control
Classifier: Topic :: System :: Software Distribution
Classifier: Topic :: Utilities
Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7
Description-Content-Type: text/x-rst
Requires-Dist: setuptools
Provides-Extra: toml
Requires-Dist: toml ; extra == 'toml'
setuptools_scm
==============
``setuptools_scm`` handles managing your Python package versions
in SCM metadata instead of declaring them as the version argument
or in a SCM managed file.
Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM
(i.e. it automatically adds all of the SCM-managed files to the sdist).
Unwanted files must be excluded by discarding them via ``MANIFEST.in``.
``setuptools_scm`` support the following scm out of the box:
* git
* mercurial
.. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg
:target: https://github.com/pypa/setuptools_scm/actions
.. image:: https://tidelift.com/badges/package/pypi/setuptools-scm
:target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme
``pyproject.toml`` usage
------------------------
The preferred way to configure ``setuptools_scm`` is to author
settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``.
This feature requires Setuptools 42 or later, released in Nov, 2019.
If your project needs to support build from sdist on older versions
of Setuptools, you will need to also implement the ``setup.py usage``
for those legacy environments.
First, ensure that ``setuptools_scm`` is present during the project's
built step by specifying it as one of the build requirements.
.. code:: toml
# pyproject.toml
[build-system]
requires = ["setuptools>=42", "wheel", "setuptools_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 <https://pypi.org/project/pip>`_ and
`pep517 <https://pypi.org/project/pep517/>`_). Many tools,
especially those that invoke ``setup.py`` for any reason, may
continue to rely on ``setup_requires``. For maximum compatibility
with those uses, consider also including a ``setup_requires`` directive
(described below in ``setup.py usage`` and ``setup.cfg``).
To enable version inference, add this section to your pyproject.toml:
.. code:: toml
# pyproject.toml
[tool.setuptools_scm]
Including this section is comparable to supplying
``use_scm_version=True`` in ``setup.py``. Additionally,
include arbitrary keyword arguments in that section
to be supplied to ``get_version()``. For example:
.. code:: toml
# pyproject.toml
[tool.setuptools_scm]
write_to = "pkg/version.py"
``setup.py`` usage
------------------
The following settings are considered legacy behavior and
superseded by the ``pyproject.toml`` usage, but for maximal
compatibility, projects may also supply the configuration in
this older form.
To use ``setuptools_scm`` just modify your project's ``setup.py`` file
like this:
* Add ``setuptools_scm`` to the ``setup_requires`` parameter.
* Add the ``use_scm_version`` parameter and set it to ``True``.
For example:
.. code:: python
from setuptools import setup
setup(
...,
use_scm_version=True,
setup_requires=['setuptools_scm'],
...,
)
Arguments to ``get_version()`` (see below) may be passed as a dictionary to
``use_scm_version``. For example:
.. code:: python
from setuptools import setup
setup(
...,
use_scm_version = {
"root": "..",
"relative_to": __file__,
"local_scheme": "node-and-timestamp"
},
setup_requires=['setuptools_scm'],
...,
)
You can confirm the version number locally via ``setup.py``:
.. code-block:: shell
$ python setup.py --version
.. note::
If you see unusual version numbers for packages but ``python setup.py
--version`` reports the expected version number, ensure ``[egg_info]`` is
not defined in ``setup.cfg``.
``setup.cfg`` usage
-------------------
If using `setuptools 30.3.0
<https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_
or greater, you can store ``setup_requires`` configuration in ``setup.cfg``.
However, ``use_scm_version`` must still be placed in ``setup.py``. For example:
.. code:: python
# setup.py
from setuptools import setup
setup(
use_scm_version=True,
)
.. code:: ini
# setup.cfg
[metadata]
...
[options]
setup_requires =
setuptools_scm
...
.. important::
Ensure neither the ``[metadata]`` ``version`` option nor the ``[egg_info]``
section are defined, as these will interfere with ``setuptools_scm``.
You may also need to define a ``pyproject.toml`` file (`PEP-0518
<https://www.python.org/dev/peps/pep-0518>`_) to ensure you have the required
version of ``setuptools``:
.. code:: ini
# pyproject.toml
[build-system]
requires = ["setuptools>=30.3.0", "wheel", "setuptools_scm"]
For more information, refer to the `setuptools issue #1002
<https://github.com/pypa/setuptools/issues/1002>`_.
Programmatic usage
------------------
In order to use ``setuptools_scm`` from code that is one directory deeper
than the project's root, you can use:
.. code:: python
from setuptools_scm import get_version
version = get_version(root='..', relative_to=__file__)
See `setup.py Usage`_ above for how to use this within ``setup.py``.
Retrieving package version at runtime
-------------------------------------
If you have opted not to hardcode the version number inside the package,
you can retrieve it at runtime from PEP-0566_ metadata using
``importlib.metadata`` from the standard library (added in Python 3.8)
or the `importlib_metadata`_ backport:
.. code:: python
from importlib.metadata import version, PackageNotFoundError
try:
__version__ = version("package-name")
except PackageNotFoundError:
# package is not installed
pass
Alternatively, you can use ``pkg_resources`` which is included in
``setuptools``:
.. code:: python
from pkg_resources import get_distribution, DistributionNotFound
try:
__version__ = get_distribution("package-name").version
except DistributionNotFound:
# package is not installed
pass
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 <https://pypi.python.org/pypi/setuptools_scm_git_archive>`_
Provides partial support for obtaining versions from git archives that
belong to tagged versions. The only reason for not including it in
``setuptools_scm`` itself is Git/GitHub not supporting sufficient metadata
for untagged/followup commits, which is preventing a consistent UX.
Default versioning scheme
-------------------------
In the standard configuration ``setuptools_scm`` takes a look at three things:
1. latest tag (with a version number)
2. the distance to this tag (e.g. number of revisions since latest tag)
3. workdir state (e.g. uncommitted changes since latest tag)
and uses roughly the following logic to render the version:
no distance and clean:
``{tag}``
distance and clean:
``{next_version}.dev{distance}+{scm letter}{revision hash}``
no distance and not clean:
``{tag}+dYYYYMMDD``
distance and not clean:
``{next_version}.dev{distance}+{scm letter}{revision hash}.dYYYYMMDD``
The next version is calculated by adding ``1`` to the last numeric component of
the tag.
For Git projects, the version relies on `git describe <https://git-scm.com/docs/git-describe>`_,
so you will see an additional ``g`` prepended to the ``{revision hash}``.
Semantic Versioning (SemVer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Due to the default behavior it's necessary to always include a
patch version (the ``3`` in ``1.2.3``), or else the automatic guessing
will increment the wrong part of the SemVer (e.g. tag ``2.0`` results in
``2.1.devX`` instead of ``2.0.1.devX``). So please make sure to tag
accordingly.
.. note::
Future versions of ``setuptools_scm`` will switch to `SemVer
<http://semver.org/>`_ by default hiding the the old behavior as an
configurable option.
Builtin mechanisms for obtaining version numbers
------------------------------------------------
1. the SCM itself (git/hg)
2. ``.hg_archival`` files (mercurial archives)
3. ``PKG-INFO``
.. note::
Git archives are not supported due to Git shortcomings
File finders hook makes most of MANIFEST.in unnecessary
-------------------------------------------------------
``setuptools_scm`` implements a `file_finders
<https://setuptools.readthedocs.io/en/latest/setuptools.html#adding-support-for-revision-control-systems>`_
entry point which returns all files tracked by your SCM. This eliminates
the need for a manually constructed ``MANIFEST.in`` in most cases where this
would be required when not using ``setuptools_scm``, namely:
* To ensure all relevant files are packaged when running the ``sdist`` command.
* When using `include_package_data <https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files>`_
to include package data as part of the ``build`` or ``bdist_wheel``.
``MANIFEST.in`` may still be used: anything defined there overrides the hook.
This is mostly useful to exclude files tracked in your SCM from packages,
although in principle it can be used to explicitly include non-tracked files
too.
Configuration parameters
------------------------
In order to configure the way ``use_scm_version`` works you can provide
a mapping with options instead of a boolean value.
The currently supported configuration keys are:
:root:
Relative path to cwd, used for finding the SCM root; defaults to ``.``
:version_scheme:
Configures how the local version number is constructed; either an
entrypoint name or a callable.
:local_scheme:
Configures how the local component of the version is constructed; either an
entrypoint name or a callable.
:write_to:
A path to a file that gets replaced with a file containing the current
version. It is ideal for creating a ``version.py`` file within the
package, typically used to avoid using `pkg_resources.get_distribution`
(which adds some overhead).
.. warning::
Only files with :code:`.py` and :code:`.txt` extensions have builtin
templates, for other file types it is necessary to provide
:code:`write_to_template`.
:write_to_template:
A newstyle format string that is given the current version as
the ``version`` keyword argument for formatting.
:relative_to:
A file from which the root can be resolved.
Typically called by a script or module that is not in the root of the
repository to point ``setuptools_scm`` at the root of the repository by
supplying ``__file__``.
:tag_regex:
A Python regex string to extract the version part from any SCM tag.
The regex needs to contain either a single match group, or a group
named ``version``, that captures the actual version information.
Defaults to the value of ``setuptools_scm.config.DEFAULT_TAG_REGEX``
(see `config.py <src/setuptools_scm/config.py>`_).
:parentdir_prefix_version:
If the normal methods for detecting the version (SCM version,
sdist metadata) fail, and the parent directory name starts with
``parentdir_prefix_version``, then this prefix is stripped and the rest of
the parent directory name is matched with ``tag_regex`` to get a version
string. If this parameter is unset (the default), then this fallback is
not used.
This is intended to cover GitHub's "release tarballs", which extract into
directories named ``projectname-tag/`` (in which case
``parentdir_prefix_version`` can be set e.g. to ``projectname-``).
:fallback_version:
A version string that will be used if no other method for detecting the
version worked (e.g., when using a tarball with no metadata). If this is
unset (the default), setuptools_scm will error if it fails to detect the
version.
:parse:
A function that will be used instead of the discovered SCM for parsing the
version.
Use with caution, this is a function for advanced use, and you should be
familiar with the ``setuptools_scm`` internals to use it.
:git_describe_command:
This command will be used instead the default ``git describe`` command.
Use with caution, this is a function for advanced use, and you should be
familiar with the ``setuptools_scm`` internals to use it.
Defaults to the value set by ``setuptools_scm.git.DEFAULT_DESCRIBE``
(see `git.py <src/setuptools_scm/git.py>`_).
To use ``setuptools_scm`` in other Python code you can use the ``get_version``
function:
.. code:: python
from setuptools_scm import get_version
my_version = get_version()
It optionally accepts the keys of the ``use_scm_version`` parameter as
keyword arguments.
Example configuration in ``setup.py`` format:
.. code:: python
from setuptools import setup
setup(
use_scm_version={
'write_to': 'version.py',
'write_to_template': '__version__ = "{version}"',
'tag_regex': r'^(?P<prefix>v)?(?P<version>[^\+]+)(?P<suffix>.*)?$',
}
)
Environment variables
---------------------
:SETUPTOOLS_SCM_PRETEND_VERSION:
when defined and not empty,
its used as the primary source for the version number
in which case it will be a unparsed string
:SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}:
when defined and not empty,
its used as the primary source for the version number
in which case it will be a unparsed string
it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION``
:SETUPTOOLS_SCM_DEBUG:
when defined and not empty,
a lot of debug information will be printed as part of ``setuptools_scm``
operating
:SOURCE_DATE_EPOCH:
when defined, used as the timestamp from which the
``node-and-date`` and ``node-and-timestamp`` local parts are
derived, otherwise the current time is used
(https://reproducible-builds.org/docs/source-date-epoch/)
:SETUPTOOLS_SCM_IGNORE_VCS_ROOTS:
when defined, a ``os.pathsep`` separated list
of directory names to ignore for root finding
Extending setuptools_scm
------------------------
``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to
extend its default capabilities.
Adding a new SCM
~~~~~~~~~~~~~~~~
``setuptools_scm`` provides two entrypoints for adding new SCMs:
``setuptools_scm.parse_scm``
A function used to parse the metadata of the current workdir
using the name of the control directory/file of your SCM as the
entrypoint's name. E.g. for the built-in entrypoint for git the
entrypoint is named ``.git`` and references ``setuptools_scm.git:parse``
The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance
created by the function ``setuptools_scm.version:meta``.
``setuptools_scm.files_command``
Either a string containing a shell command that prints all SCM managed
files in its current working directory or a callable, that given a
pathname will return that list.
Also use then name of your SCM control directory as name of the entrypoint.
Version number construction
~~~~~~~~~~~~~~~~~~~~~~~~~~~
``setuptools_scm.version_scheme``
Configures how the version number is constructed given a
``setuptools_scm.version.ScmVersion`` instance and should return a string
representing the version.
Available implementations:
:guess-next-dev: Automatically guesses the next development version (default).
Guesses the upcoming release by incrementing the pre-release segment if present,
otherwise by incrementing the micro segment. Then appends :code:`.devN`.
In case the tag ends with ``.dev0`` the version is not bumped
and custom ``.devN`` versions will trigger a error.
:post-release: generates post release versions (adds :code:`.postN`)
:python-simplified-semver: Basic semantic versioning. Guesses the upcoming release
by incrementing the minor segment and setting the micro segment to zero if the
current branch contains the string ``'feature'``, otherwise by incrementing the
micro version. Then appends :code:`.devN`. Not compatible with pre-releases.
:release-branch-semver: Semantic versioning for projects with release branches. The
same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on
a release branch: a branch whose name (ignoring namespace) parses as a version
that matches the most recent tag up to the minor segment. Otherwise if on a
non-release branch, increments the minor segment and sets the micro segment to
zero, then appends :code:`.devN`.
:no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN`
``setuptools_scm.local_scheme``
Configures how the local part of a version is rendered given a
``setuptools_scm.version.ScmVersion`` instance and should return a string
representing the local version.
Dates and times are in Coordinated Universal Time (UTC), because as part
of the version, they should be location independent.
Available implementations:
:node-and-date: adds the node on dev versions and the date on dirty
workdir (default)
:node-and-timestamp: like ``node-and-date`` but with a timestamp of
the form ``{:%Y%m%d%H%M%S}`` instead
:dirty-tag: adds ``+dirty`` if the current workdir has changes
:no-local-version: omits local version, useful e.g. because pypi does
not support it
Importing in ``setup.py``
~~~~~~~~~~~~~~~~~~~~~~~~~
To support usage in ``setup.py`` passing a callable into ``use_scm_version``
is supported.
Within that callable, ``setuptools_scm`` is available for import.
The callable must return the configuration.
.. code:: python
# content of setup.py
import setuptools
def myversion():
from setuptools_scm.version import get_local_dirty_tag
def clean_scheme(version):
return get_local_dirty_tag(version) if version.dirty else '+clean'
return {'local_scheme': clean_scheme}
setup(
...,
use_scm_version=myversion,
...
)
Note on testing non-installed versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While the general advice is to test against a installed version,
some environments require a test prior to install,
.. code::
$ python setup.py egg_info
$ PYTHONPATH=$PWD:$PWD/src pytest
Interaction with Enterprise Distributions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some enterprise distributions like RHEL7 and others
ship rather old setuptools versions due to various release management details.
On such distributions one might observe errors like:
:code:``setuptools_scm.version.SetuptoolsOutdatedWarning: your setuptools is too old (<12)``
In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``.
As those old setuptools versions lack sensible types for versions,
modern setuptools_scm is unable to support them sensibly.
In case the project you need to build can not be patched to either use old setuptools_scm,
its still possible to install a more recent version of setuptools in order to handle the build
and/or install the package by using wheels or eggs.
Code of Conduct
---------------
Everyone interacting in the ``setuptools_scm`` project's codebases, issue
trackers, chat rooms, and mailing lists is expected to follow the
`PSF Code of Conduct`_.
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
Security Contact
================
To report a security vulnerability, please use the
`Tidelift security contact <https://tidelift.com/security>`_.
Tidelift will coordinate the fix and disclosure.
setuptools_scm/__init__.py,sha256=0NWHUiWlSYqA7whMYsVAVrmnujn_NPFt-AP-9EOAIIA,5578
setuptools_scm/__main__.py,sha256=r2weno4bqNJ3pFJCrArU2ryJOKNWZ_pg1Fz6wiEe6t0,423
setuptools_scm/config.py,sha256=nlMUfZhtzCz40iYXOq6adW9UB8PS8TS6jPDaGECOdHs,4282
setuptools_scm/discover.py,sha256=LqX0raq94k3llpxXmjNnDbkqECIGqEm1UcU3g2Rn2NI,416
setuptools_scm/file_finder.py,sha256=PTYcZXz6yZAhmsCaRcVd3EPe2p_f5T6Su4RYvqpWjhU,2548
setuptools_scm/file_finder_git.py,sha256=9KPibz5-qJG0170WpEgIhTD6NP1XkXjZyDTZaaTmL6M,2316
setuptools_scm/file_finder_hg.py,sha256=G5-sPQjHU3Kg7LGPtAtbvsFIVmPLgSc4-9ak5Mf3E1k,1487
setuptools_scm/git.py,sha256=p0El7RQOs3Tg68yzr-lOmm2DQNcdlW64DtaLfrdbWlk,4205
setuptools_scm/hacks.py,sha256=KEhSSGKLaTCfQVdqAZ8JUcdL5KIZBg0K_AHiyPpCJOY,1277
setuptools_scm/hg.py,sha256=F9gqx3S5u0m4tpOcZhGLbWBtt0qCoLdyAVQkXIIrnqE,3387
setuptools_scm/integration.py,sha256=tJK_iTyQj-haKjohnpa5RIcxUAdQvRXgiP5vwGeNCOI,1690
setuptools_scm/utils.py,sha256=JrpjpyzBaNP5-7uuANHJW0c7H2AJ26OLpCpkjLGcr88,3772
setuptools_scm/version.py,sha256=RKSwD0drbqE2Fc8VF9ltGhI76M28RrtaWRPZ3EjPE10,10814
setuptools_scm/win_py31_compat.py,sha256=w-TAp2Z21O_shi3gPvlcbxz1JZ8cGOLFd9HguCTYB6E,5098
setuptools_scm-5.0.1.dist-info/LICENSE,sha256=iYB6zyMJvShfAzQE7nhYFgLzzZuBmhasLw5fYP9KRz4,1023
setuptools_scm-5.0.1.dist-info/METADATA,sha256=QoFZ61ieCfZIxA_g5mJAtHKh2yHnIGaZoajm2pb9lvE,21762
setuptools_scm-5.0.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
setuptools_scm-5.0.1.dist-info/entry_points.txt,sha256=-ltXW9C7w2CaE7aerUXpZClGXQCCsm67Os_PA5oQjx4,1385
setuptools_scm-5.0.1.dist-info/top_level.txt,sha256=kiu-91q3_rJLUoc2wl8_lC4cIlpgtgdD_4NaChF4hOA,15
setuptools_scm-5.0.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
setuptools_scm-5.0.1.dist-info/RECORD,,
Wheel-Version: 1.0
Generator: bdist_wheel (0.36.2)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any
[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.files_command]
.git = setuptools_scm.file_finder_git:git_find_files
.hg = setuptools_scm.file_finder_hg:hg_find_files
[setuptools_scm.local_scheme]
dirty-tag = setuptools_scm.version:get_local_dirty_tag
no-local-version = setuptools_scm.version:get_no_local_node
node-and-date = setuptools_scm.version:get_local_node_and_date
node-and-timestamp = setuptools_scm.version:get_local_node_and_timestamp
[setuptools_scm.parse_scm]
.git = setuptools_scm.git:parse
.hg = setuptools_scm.hg: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.version_scheme]
guess-next-dev = setuptools_scm.version:guess_next_dev_version
no-guess-dev = setuptools_scm.version:no_guess_dev_version
post-release = setuptools_scm.version:postrelease_version
python-simplified-semver = setuptools_scm.version:simplified_semver_version
release-branch-semver = setuptools_scm.version:release_branch_semver_version
"""
: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, trace
from .version import format_version, meta
from .discover import iter_matching_entrypoints
PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION"
PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}"
TEMPLATES = {
".py": """\
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
version = {version!r}
version_tuple = {version_tuple!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
)
)
# version_tuple: each field is converted to an int if possible or kept as string
fields = tuple(version.split("."))
version_fields = []
for field in fields:
try:
v = int(field)
except ValueError:
v = field
version_fields.append(v)
with open(target, "w") as fp:
fp.write(template.format(version=version, version_tuple=tuple(version_fields)))
def _do_parse(config):
trace("dist name:", config.dist_name)
if config.dist_name is not None:
pretended = os.environ.get(
PRETEND_KEY_NAMED.format(name=config.dist_name.upper())
)
else:
pretended = None
if pretended is None:
pretended = os.environ.get(PRETEND_KEY)
if pretended:
# we use meta here since the pretended version
# must adhere to the pep to begin with
return meta(tag=pretended, preformatted=True, config=config)
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,
parentdir_prefix_version=None,
fallback_version=None,
fallback_root=".",
parse=None,
git_describe_command=None,
dist_name=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
from __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()
""" configuration """
from __future__ import print_function, unicode_literals
import os
import re
import warnings
from .utils import trace
DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P<version>[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):
trace("l", repr(locals()))
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)
)
if os.path.isdir(relative_to):
warnings.warn(
"relative_to is expected to be a file,"
" its the directory %r\n"
"assuming the parent directory was passed" % (relative_to,)
)
trace("dir", relative_to)
root = os.path.join(relative_to, root)
else:
trace("file", 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,
parentdir_prefix_version=None,
fallback_version=None,
fallback_root=".",
parse=None,
git_describe_command=None,
dist_name=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.parentdir_prefix_version = parentdir_prefix_version
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
self.dist_name = dist_name
@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))
trace("relative_to", repr(value))
@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))
trace("relative_to", repr(self._relative_to))
@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", dist_name=None):
"""
Read Configuration from pyproject.toml (or similar).
Raises exceptions when file is not found or toml is
not installed or the file has invalid format or does
not contain the [tool.setuptools_scm] section.
"""
with open(name) as strm:
defn = __import__("toml").load(strm)
section = defn.get("tool", {})["setuptools_scm"]
return cls(dist_name=dist_name, **section)
import 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
import os
from .utils import trace
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, realpath)))
seen.add(realdirpath)
return res
def is_toplevel_acceptable(toplevel):
""
if toplevel is None:
return False
ignored = os.environ.get("SETUPTOOLS_SCM_IGNORE_VCS_ROOTS", "").split(os.pathsep)
ignored = [os.path.normcase(p) for p in ignored]
trace(toplevel, ignored)
return toplevel not in ignored
import os
import subprocess
import tarfile
import logging
from .file_finder import scm_find_files
from .file_finder import is_toplevel_acceptable
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.stdout.close()
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 is_toplevel_acceptable(toplevel):
return []
fullpath = os.path.abspath(os.path.normpath(path))
if not fullpath.startswith(toplevel):
trace("toplevel mismatch", toplevel, fullpath)
git_files, git_dirs = _git_ls_files_and_dirs(toplevel)
return scm_find_files(path, git_files, git_dirs)
import os
import subprocess
from .file_finder import scm_find_files
from .file_finder import is_toplevel_acceptable
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 is_toplevel_acceptable(toplevel):
return []
hg_files, hg_dirs = _hg_ls_files_and_dirs(toplevel)
return scm_find_files(path, hg_files, hg_dirs)
from .config import Configuration
from .utils import do_ex, trace, require_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 *[0-9]*"
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)
require_command("git")
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.
branch, branch_err, branch_ret = wd.do_ex("git symbolic-ref --short HEAD")
if branch_ret:
branch = None
rev_node = wd.node()
dirty = wd.is_dirty()
if rev_node is None:
return meta("0.0", distance=0, dirty=dirty, branch=branch, config=config)
return meta(
"0.0",
distance=wd.count_all_nodes(),
node="g" + rev_node,
dirty=dirty,
branch=wd.get_branch(),
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
import os
from .utils import data_from_mime, trace
from .version import tag_to_version, 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.parentdir_prefix_version is not None:
_, parent_name = os.path.split(os.path.abspath(root))
if parent_name.startswith(config.parentdir_prefix_version):
version = tag_to_version(
parent_name[len(config.parentdir_prefix_version) :], config
)
if version is not None:
return meta(str(version), preformatted=True, config=config)
if config.fallback_version is not None:
return meta(config.fallback_version, preformatted=True, config=config)
import os
from .config import Configuration
from .utils import do, trace, data_from_mime, require_command
from .version import meta, tags_to_versions
def _hg_tagdist_normalize_tagcommit(config, tag, dist, node, branch):
dirty = node.endswith("+")
node = "h" + node.strip("+")
# Detect changes since the specified tag
revset = (
"(branch(.)" # look for revisions in this branch only
" and tag({tag!r})::." # after the last tag
# ignore commits that only modify .hgtags and nothing else:
" and (merge() or file('re:^(?!\\.hgtags).*$'))"
" and not tag({tag!r}))" # ignore the tagged commit itself
).format(tag=tag)
if tag != "0.0":
commits = do(
["hg", "log", "-r", revset, "--template", "{node|short}"],
config.absolute_root,
)
else:
commits = True
trace("normalize", locals())
if commits or dirty:
return meta(
tag, distance=dist, node=node, dirty=dirty, branch=branch, config=config
)
else:
return meta(tag, config=config)
def parse(root, config=None):
if not config:
config = Configuration(root=root)
require_command("hg")
identity_data = do("hg id -i -b -t", config.absolute_root).split()
if not identity_data:
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)
from pkg_resources import iter_entry_points
from .version import _warn_if_setuptools_outdated
from .utils import do, trace_exception, trace
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()
assert (
"dist_name" not in value
), "dist_name may not be specified in the setup keyword "
trace(
"version keyword",
vars(dist.metadata),
)
dist_name = dist.metadata.name
config = Configuration(dist_name=dist_name, **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 helper back to config and unify it with the code from get_config
with open(name) as strm:
defn = __import__("toml").load(strm)
return defn.get("tool", {})["setuptools_scm"]
def infer_version(dist):
trace(
"finalize hook",
vars(dist.metadata),
)
dist_name = dist.metadata.name
try:
config = Configuration.from_file(dist_name=dist_name)
except Exception:
return trace_exception()
dist.metadata.version = _get_version(config)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment