diff --git a/.eggs/README.txt b/.eggs/README.txt new file mode 100644 index 0000000..5d01668 --- /dev/null +++ b/.eggs/README.txt @@ -0,0 +1,6 @@ +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. + diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/LICENSE b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..89de354 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/LICENSE @@ -0,0 +1,17 @@ +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. diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/PKG-INFO b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..c269f3f --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,695 @@ +Metadata-Version: 2.1 +Name: setuptools-scm +Version: 7.0.5 +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 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Version Control +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: packaging (>=20.0) +Requires-Dist: setuptools +Requires-Dist: tomli (>=1.0.0) +Requires-Dist: typing-extensions +Requires-Dist: importlib-metadata ; python_version < "3.8" +Provides-Extra: test +Requires-Dist: pytest (>=6.2) ; extra == 'test' +Requires-Dist: virtualenv (>20) ; extra == 'test' +Provides-Extra: toml +Requires-Dist: setuptools (>=42) ; extra == 'toml' + +setuptools_scm +============== + +``setuptools_scm`` extract Python package versions from ``git`` or +``hg`` metadata instead of declaring them as the version argument +or in a SCM managed file. + +Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM +(i.e. it automatically adds all of the SCM-managed files to the sdist). +Unwanted files must be excluded by discarding them via ``MANIFEST.in``. + +``setuptools_scm`` support the following scm out of the box: + +* git +* mercurial + + + +.. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg + :target: https://github.com/pypa/setuptools_scm/actions + +.. image:: https://tidelift.com/badges/package/pypi/setuptools-scm + :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme + + +``pyproject.toml`` usage +------------------------ + +The preferred way to configure ``setuptools_scm`` is to author +settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. + +This feature requires Setuptools 42 or later, released in Nov, 2019. +If your project needs to support build from sdist on older versions +of Setuptools, you will need to also implement the ``setup.py usage`` +for those legacy environments. + +First, ensure that ``setuptools_scm`` is present during the project's +built step by specifying it as one of the build requirements. + +.. code:: toml + + # pyproject.toml + [build-system] + requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] + + +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" + + +If you need to confirm which version string is being generated +or debug the configuration, you can install +`setuptools-scm `_ +directly in your working environment and run: + +.. code-block:: shell + + $ python -m setuptools_scm + + # To explore other options, try: + $ python -m setuptools_scm --help + + +``setup.py`` usage (deprecated) +------------------------------- + +.. warning:: + + ``setup_requires`` has been deprecated in favor of ``pyproject.toml`` + +The following settings are considered legacy behavior and +superseded by the ``pyproject.toml`` usage, but for maximal +compatibility, projects may also supply the configuration in +this older form. + +To use ``setuptools_scm`` just modify your project's ``setup.py`` file +like this: + +* Add ``setuptools_scm`` to the ``setup_requires`` parameter. +* Add the ``use_scm_version`` parameter and set it to ``True``. + +For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version=True, + setup_requires=['setuptools_scm'], + ..., + ) + +Arguments to ``get_version()`` (see below) may be passed as a dictionary to +``use_scm_version``. For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version = { + "root": "..", + "relative_to": __file__, + "local_scheme": "node-and-timestamp" + }, + setup_requires=['setuptools_scm'], + ..., + ) + +You can confirm the version number locally via ``setup.py``: + +.. code-block:: shell + + $ python setup.py --version + +.. note:: + + If you see unusual version numbers for packages but ``python setup.py + --version`` reports the expected version number, ensure ``[egg_info]`` is + not defined in ``setup.cfg``. + + +``setup.cfg`` usage (deprecated) +------------------------------------ + +as ``setup_requires`` is deprecated in favour of ``pyproject.toml`` +usage in ``setup.cfg`` is considered deprecated, +please use ``pyproject.toml`` whenever possible. + +Programmatic usage +------------------ + +In order to use ``setuptools_scm`` from code that is one directory deeper +than the project's root, you can use: + +.. code:: python + + from setuptools_scm import get_version + version = get_version(root='..', relative_to=__file__) + +See `setup.py Usage (deprecated)`_ above for how to use this within ``setup.py``. + + +Retrieving package version at runtime +------------------------------------- + +If you have opted not to hardcode the version number inside the package, +you can retrieve it at runtime from PEP-0566_ metadata using +``importlib.metadata`` from the standard library (added in Python 3.8) +or the `importlib_metadata`_ backport: + +.. code:: python + + from importlib.metadata import version, PackageNotFoundError + + try: + __version__ = version("package-name") + except PackageNotFoundError: + # package is not installed + pass + +Alternatively, you can use ``pkg_resources`` which is included in +``setuptools`` (but has a significant runtime cost): + +.. code:: python + + from pkg_resources import get_distribution, DistributionNotFound + + try: + __version__ = get_distribution("package-name").version + except DistributionNotFound: + # package is not installed + pass + +However, this does place a runtime dependency on ``setuptools`` and can add up to +a few 100ms overhead for the package import time. + +.. _PEP-0566: https://www.python.org/dev/peps/pep-0566/ +.. _importlib_metadata: https://pypi.org/project/importlib-metadata/ + + +Usage from Sphinx +----------------- + +It is discouraged to use ``setuptools_scm`` from Sphinx itself, +instead use ``importlib.metadata`` after editable/real installation: + +.. code:: python + + # contents of docs/conf.py + from importlib.metadata import version + release = version('myproject') + # for example take major/minor + version = '.'.join(release.split('.')[:2]) + +The underlying reason is, that services like *Read the Docs* sometimes change +the working directory for good reasons and using the installed metadata +prevents using needless volatile data there. + +Usage from Docker +----------------- + +By default, docker will not copy the ``.git`` folder into your container. +Therefore, builds with version inference might fail. +Consequently, you can use the following snipped to infer the version from +the host os without copying the entire ``.git`` folder to your Dockerfile. + +.. code:: dockerfile + + RUN --mount=source=.git,target=.git,type=bind \ + pip install --no-cache-dir -e . + +However, this build step introduces a dependency to the state of your local +.git folder the build cache and triggers the long-running pip install process on every build. +To optimize build caching, one can use an environment variable to pretend a pseudo +version that is used to cache the results of the pip install process: + +.. code:: dockerfile + + FROM python + COPY pyproject.toml + ARG PSEUDO_VERSION=1 + RUN SETUPTOOLS_SCM_PRETEND_VERSION=${PSEUDO_VERSION} pip install -e .[test] + RUN --mount=source=.git,target=.git,type=bind pip install -e . + +Note that running this Dockerfile requires docker with BuildKit enabled +`[docs] `_. + +To avoid BuildKit and mounting of the .git folder altogether, one can also pass the desired +version as a build argument. Note that ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}`` +is preferred over ``SETUPTOOLS_SCM_PRETEND_VERSION``. + +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. ``.git_archival.txt`` files (git archives, see subsection below) +4. ``PKG-INFO`` + +Git archives +~~~~~~~~~~~~ + +Git archives are supported, but a few changes to your repository are required. + +Create a ``.git_archival.txt`` file in the root directory of your repository, +and copy-paste this into it:: + + node: $Format:%H$ + node-date: $Format:%cI$ + describe-name: $Format:%(describe:tags=true)$ + ref-names: $Format:%D$ + +Create the ``.gitattributes`` file in the root directory of your repository +if it doesn't already exist, and copy-paste this into it:: + + .git_archival.txt export-subst + +Finally, don't forget to commit those two files:: + + git add .git_archival.txt .gitattributes && git commit + + +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 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 `_). + +: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 `_). + +:normalize: + A boolean flag indicating if the version string should be normalized. + Defaults to ``True``. Setting this to ``False`` is equivalent to setting + ``version_cls`` to ``setuptools_scm.version.NonNormalizedVersion`` + +:version_cls: + An optional class used to parse, verify and possibly normalize the version + string. Its constructor should receive a single string argument, and its + ``str`` should return the normalized version string to use. + This option can also receive a class qualified name as a string. + + This defaults to ``packaging.version.Version`` if available. If + ``packaging`` is not installed, ``pkg_resources.packaging.version.Version`` + is used. Note that it is known to modify git release candidate schemes. + + The ``setuptools_scm.NonNormalizedVersion`` convenience class is + provided to disable the normalization step done by + ``packaging.version.Version``. If this is used while ``setuptools_scm`` + is integrated in a setuptools packaging process, the non-normalized + version number will appear in all files (see ``write_to``) BUT note + that setuptools will still normalize it to create the final distribution, + so as to stay compliant with the python packaging standards. + + +To use ``setuptools_scm`` in other Python code you can use the ``get_version`` +function: + +.. code:: python + + from setuptools_scm import get_version + my_version = get_version() + +It optionally accepts the keys of the ``use_scm_version`` parameter as +keyword arguments. + +Example configuration in ``setup.py`` format: + +.. code:: python + + from setuptools import setup + + setup( + use_scm_version={ + 'write_to': '_version.py', + 'write_to_template': '__version__ = "{version}"', + 'tag_regex': r'^(?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_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}: + when defined and not empty, + its used as the primary source for the version number + in which case it will be a unparsed string + + it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION`` + + +:SETUPTOOLS_SCM_DEBUG: + when defined and not empty, + a lot of debug information will be printed as part of ``setuptools_scm`` + operating + +:SOURCE_DATE_EPOCH: + when defined, used as the timestamp from which the + ``node-and-date`` and ``node-and-timestamp`` local parts are + derived, otherwise the current time is used + (https://reproducible-builds.org/docs/source-date-epoch/) + + +:SETUPTOOLS_SCM_IGNORE_VCS_ROOTS: + when defined, a ``os.pathsep`` separated list + of directory names to ignore for root finding + +Extending setuptools_scm +------------------------ + +``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to +extend its default capabilities. + +Adding a new SCM +~~~~~~~~~~~~~~~~ + +``setuptools_scm`` provides two entrypoints for adding new SCMs: + +``setuptools_scm.parse_scm`` + A function used to parse the metadata of the current workdir + using the name of the control directory/file of your SCM as the + entrypoint's name. E.g. for the built-in entrypoint for git the + entrypoint is named ``.git`` and references ``setuptools_scm.git:parse`` + + The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance + created by the function ``setuptools_scm.version:meta``. + +``setuptools_scm.files_command`` + Either a string containing a shell command that prints all SCM managed + files in its current working directory or a callable, that given a + pathname will return that list. + + Also use then name of your SCM control directory as name of the entrypoint. + +Version number construction +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``setuptools_scm.version_scheme`` + Configures how the version number is constructed given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the version. + + Available implementations: + + :guess-next-dev: Automatically guesses the next development version (default). + Guesses the upcoming release by incrementing the pre-release segment if present, + otherwise by incrementing the micro segment. Then appends :code:`.devN`. + In case the tag ends with ``.dev0`` the version is not bumped + and custom ``.devN`` versions will trigger a error. + :post-release: generates post release versions (adds :code:`.postN`) + :python-simplified-semver: Basic semantic versioning. Guesses the upcoming release + by incrementing the minor segment and setting the micro segment to zero if the + current branch contains the string ``'feature'``, otherwise by incrementing the + micro version. Then appends :code:`.devN`. Not compatible with pre-releases. + :release-branch-semver: Semantic versioning for projects with release branches. The + same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on + a release branch: a branch whose name (ignoring namespace) parses as a version + that matches the most recent tag up to the minor segment. Otherwise if on a + non-release branch, increments the minor segment and sets the micro segment to + zero, then appends :code:`.devN`. + :no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN` + +``setuptools_scm.local_scheme`` + Configures how the local part of a version is rendered given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the local version. + Dates and times are in Coordinated Universal Time (UTC), because as part + of the version, they should be location independent. + + Available implementations: + + :node-and-date: adds the node on dev versions and the date on dirty + workdir (default) + :node-and-timestamp: like ``node-and-date`` but with a timestamp of + the form ``{:%Y%m%d%H%M%S}`` instead + :dirty-tag: adds ``+dirty`` if the current workdir has changes + :no-local-version: omits local version, useful e.g. because pypi does + not support it + + +Importing in ``setup.py`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To support usage in ``setup.py`` passing a callable into ``use_scm_version`` +is supported. + +Within that callable, ``setuptools_scm`` is available for import. +The callable must return the configuration. + + +.. code:: python + + # content of setup.py + import setuptools + + def myversion(): + from setuptools_scm.version import get_local_dirty_tag + def clean_scheme(version): + return get_local_dirty_tag(version) if version.dirty else '+clean' + + return {'local_scheme': clean_scheme} + + setup( + ..., + use_scm_version=myversion, + ... + ) + + +Note on testing non-installed versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While the general advice is to test against a installed version, +some environments require a test prior to install, + +.. code:: + + $ python setup.py egg_info + $ PYTHONPATH=$PWD:$PWD/src pytest + + +Interaction with Enterprise Distributions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some enterprise distributions like RHEL7 and others +ship rather old setuptools versions due to various release management details. + +In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. +As those old setuptools versions lack sensible types for versions, +modern setuptools_scm is unable to support them sensibly. + +In case the project you need to build can not be patched to either use old setuptools_scm, +its still possible to install a more recent version of setuptools in order to handle the build +and/or install the package by using wheels or eggs. + + + +Code of Conduct +--------------- + +Everyone interacting in the ``setuptools_scm`` project's codebases, issue +trackers, chat rooms, and mailing lists is expected to follow the +`PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/RECORD b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..06ac4be --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/RECORD @@ -0,0 +1,30 @@ +setuptools_scm/__init__.py,sha256=KObyz_rl0Tz_6ApdV3VQ2jiQd9iFtQE5Az3N8nP7TJY,5907 +setuptools_scm/__main__.py,sha256=PUkvI9W-hylvxv5sd1sosqWXEliN3d30x7BIKVEAQaQ,98 +setuptools_scm/_cli.py,sha256=axI6F_QF66Vg0Pfp_7nvktH2gdeTiG_xG4D7XXXUk38,2845 +setuptools_scm/_entrypoints.py,sha256=lLf40uD0nWi4njK3Hs3JYWZbhw9wzGIJzlPpbc3wTiE,2696 +setuptools_scm/_overrides.py,sha256=y75kOjwBGdOqojz0we5NAf_-iMMJB-Tt6pPktwU1pd8,1067 +setuptools_scm/_types.py,sha256=sho6RgWu2eU1uURtJymR_9PZi_wvGaD95Y4SN_1lnOo,845 +setuptools_scm/_version_cls.py,sha256=bmNimKGN_rPTO_HoLbr2KOZh-Zpqa0YD_Cr9KyD7-QU,1575 +setuptools_scm/config.py,sha256=eRxBrtDbfhhiBA7bET5VZDf0qPDGVgrAAEURFtWDCEo,7237 +setuptools_scm/discover.py,sha256=Wl0m0oD54gc_0ObnEPKSuNsV85KF9ZlNKu-LMsrIT8M,1867 +setuptools_scm/file_finder.py,sha256=ktqgzpi-R0wUwf2arHu91zbQQjj5GJs7YJkx_BJtJZg,2830 +setuptools_scm/file_finder_git.py,sha256=PMQyb94Qxwhj0gWLfqwkw2AtmafmnRaD44mYEHb9m-s,3619 +setuptools_scm/file_finder_hg.py,sha256=wzjERKfT0n1cj7eDP_WBfJ9JqgTCh-Wo5HdRBxjqvsA,1573 +setuptools_scm/git.py,sha256=hknMPWPJsYNbyy5VKtbPKPzj5gAsVjrS7RvTGeN1KGU,9277 +setuptools_scm/hacks.py,sha256=wM5s2YBuTj1UmREt0yBa9Pxni66d91qugBMid4kTpDM,1776 +setuptools_scm/hg.py,sha256=2L6CAofHm-cNFFlb5V9VFhKmfVvRwAZnof-f-GLayWk,6128 +setuptools_scm/hg_git.py,sha256=s_mbg6v-jgA9KfkoZjqUiDayAPeQzk8TVoQT9jZ7D6Q,4166 +setuptools_scm/integration.py,sha256=kRFqHeoeUu348lMitQ4utVel9fqy2zji1Sd4wYy_FAs,3503 +setuptools_scm/scm_workdir.py,sha256=Njay5Ob7d1lvAV3MjJN4vfz7VQaT0kEUPj7-nlf4QRY,585 +setuptools_scm/utils.py,sha256=2fvBSG1IZThe12cJsJFOsDTIJGxoy3-8vYJt67GziIw,5117 +setuptools_scm/version.py,sha256=xuvazAc3YpOkjnWyENhecN8hs6y-sNVoI-35KARsWog,17034 +setuptools_scm/_integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools_scm/_integration/pyproject_reading.py,sha256=IYxhCmg4AZmMZ27WUGurOSeCzh8mz-C4G-Q4-kL3zx8,2745 +setuptools_scm/_integration/setuptools.py,sha256=z9e79MbpGtyq2lyo16Xw_i4K8mXYIgswn1hHoQJPXJo,512 +setuptools_scm-7.0.5.dist-info/LICENSE,sha256=iYB6zyMJvShfAzQE7nhYFgLzzZuBmhasLw5fYP9KRz4,1023 +setuptools_scm-7.0.5.dist-info/METADATA,sha256=FcXo2ApIgDwY5yzSdFMtYIuAJty8GSr8SQ_hwonsb_E,24354 +setuptools_scm-7.0.5.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +setuptools_scm-7.0.5.dist-info/entry_points.txt,sha256=vIbd4U1RxejAXqD5avwK9y3n9rLcwy3MFsQAVd08Nmc,1548 +setuptools_scm-7.0.5.dist-info/top_level.txt,sha256=kiu-91q3_rJLUoc2wl8_lC4cIlpgtgdD_4NaChF4hOA,15 +setuptools_scm-7.0.5.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +setuptools_scm-7.0.5.dist-info/RECORD,, diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/WHEEL b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/entry_points.txt b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/entry_points.txt new file mode 100644 index 0000000..b5dda72 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,38 @@ +[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] +.git_archival.txt = setuptools_scm.git:parse_archival +.hg_archival.txt = setuptools_scm.hg:parse_archival +PKG-INFO = setuptools_scm.hacks:parse_pkginfo +pip-egg-info = setuptools_scm.hacks:parse_pip_egg_info +pyproject.toml = setuptools_scm.hacks:fallback_version +setup.py = setuptools_scm.hacks:fallback_version + +[setuptools_scm.version_scheme] +calver-by-date = setuptools_scm.version:calver_by_date +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 diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/requires.txt b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/requires.txt new file mode 100644 index 0000000..cd5d948 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/requires.txt @@ -0,0 +1,11 @@ +packaging>=20.0 +setuptools +tomli>=1.0.0 +typing-extensions + +[test] +pytest>=6.2 +virtualenv>20 + +[toml] +setuptools>=42 diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/top_level.txt b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000..cba8d88 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +setuptools_scm diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/zip-safe b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/EGG-INFO/zip-safe @@ -0,0 +1 @@ + diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__init__.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__init__.py new file mode 100644 index 0000000..e614466 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__init__.py @@ -0,0 +1,192 @@ +""" +:copyright: 2010-2015 by Ronny Pfannschmidt +:license: MIT +""" +from __future__ import annotations + +import os +import warnings +from typing import Any +from typing import Callable +from typing import TYPE_CHECKING + +from ._entrypoints import _call_entrypoint_fn +from ._entrypoints import _version_from_entrypoints +from ._overrides import _read_pretended_version_for +from ._overrides import PRETEND_KEY +from ._overrides import PRETEND_KEY_NAMED +from ._version_cls import _version_as_tuple +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version +from .config import Configuration +from .config import DEFAULT_LOCAL_SCHEME +from .config import DEFAULT_TAG_REGEX +from .config import DEFAULT_VERSION_SCHEME +from .discover import iter_matching_entrypoints +from .utils import function_has_arg +from .utils import trace +from .version import format_version +from .version import meta +from .version import ScmVersion + +if TYPE_CHECKING: + from typing import NoReturn + + from . import _types as _t + +TEMPLATES = { + ".py": """\ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = {version!r} +__version_tuple__ = version_tuple = {version_tuple!r} +""", + ".txt": "{version}", +} + + +def version_from_scm(root: _t.PathT) -> ScmVersion | None: + warnings.warn( + "version_from_scm is deprecated please use get_version", + category=DeprecationWarning, + stacklevel=2, + ) + config = Configuration(root=root) + return _version_from_entrypoints(config) + + +def dump_version( + root: _t.PathT, + version: str, + write_to: _t.PathT, + template: str | None = None, +) -> None: + assert isinstance(version, str) + 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 = _version_as_tuple(version) + + with open(target, "w") as fp: + fp.write(template.format(version=version, version_tuple=version_tuple)) + + +def _do_parse(config: Configuration) -> ScmVersion | None: + pretended = _read_pretended_version_for(config) + if pretended is not None: + return pretended + + if config.parse: + parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) + if isinstance(parse_result, str): + raise TypeError( + f"version parse result was {str!r}\nplease return a parsed version" + ) + version: ScmVersion | None + if parse_result: + assert isinstance(parse_result, ScmVersion) + version = parse_result + else: + version = _version_from_entrypoints(config, fallback=True) + else: + # include fallbacks after dropping them from the main entrypoint + version = _version_from_entrypoints(config) or _version_from_entrypoints( + config, fallback=True + ) + + return version + + +def _version_missing(config: Configuration) -> NoReturn: + raise LookupError( + f"setuptools-scm was unable to detect version for {config.absolute_root}.\n\n" + "Make sure you're either building from a fully intact git repository " + "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a " + "git checkout without the .git folder) don't contain the necessary " + "metadata and will not work.\n\n" + "For example, if you're using pip, instead of " + "https://github.com/user/proj/archive/master.zip " + "use git+https://github.com/user/proj.git#egg=proj" + ) + + +def get_version( + root: str = ".", + version_scheme: Callable[[ScmVersion], str] | str = DEFAULT_VERSION_SCHEME, + local_scheme: Callable[[ScmVersion], str] | str = DEFAULT_LOCAL_SCHEME, + write_to: _t.PathT | None = None, + write_to_template: str | None = None, + relative_to: str | None = None, + tag_regex: str = DEFAULT_TAG_REGEX, + parentdir_prefix_version: str | None = None, + fallback_version: str | None = None, + fallback_root: _t.PathT = ".", + parse: Any | None = None, + git_describe_command: Any | None = None, + dist_name: str | None = None, + version_cls: Any | None = None, + normalize: bool = True, + search_parent_directories: bool = False, +) -> str: + """ + 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()) + maybe_version = _get_version(config) + if maybe_version is None: + _version_missing(config) + return maybe_version + + +def _get_version(config: Configuration) -> str | None: + parsed_version = _do_parse(config) + if parsed_version is None: + return None + version_string = format_version( + parsed_version, + version_scheme=config.version_scheme, + local_scheme=config.local_scheme, + ) + if config.write_to is not None: + dump_version( + root=config.root, + version=version_string, + write_to=config.write_to, + template=config.write_to_template, + ) + + return version_string + + +# Public API +__all__ = [ + "get_version", + "dump_version", + "version_from_scm", + "Configuration", + "DEFAULT_VERSION_SCHEME", + "DEFAULT_LOCAL_SCHEME", + "DEFAULT_TAG_REGEX", + "PRETEND_KEY", + "PRETEND_KEY_NAMED", + "Version", + "NonNormalizedVersion", + # TODO: are the symbols below part of public API ? + "function_has_arg", + "trace", + "format_version", + "meta", + "iter_matching_entrypoints", +] diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__main__.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__main__.py new file mode 100644 index 0000000..dab6068 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__main__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from ._cli import main + +if __name__ == "__main__": + main() diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/__init__.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..b1ba709 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/__init__.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_entrypoints.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_entrypoints.cpython-310.pyc new file mode 100644 index 0000000..b3dfb47 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_entrypoints.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_overrides.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_overrides.cpython-310.pyc new file mode 100644 index 0000000..3a94813 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_overrides.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_version_cls.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_version_cls.cpython-310.pyc new file mode 100644 index 0000000..7681c8c Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/_version_cls.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/config.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..09e25a5 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/config.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/discover.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/discover.cpython-310.pyc new file mode 100644 index 0000000..2b3c9f7 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/discover.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/file_finder.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/file_finder.cpython-310.pyc new file mode 100644 index 0000000..8f80a34 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/file_finder.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/file_finder_git.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/file_finder_git.cpython-310.pyc new file mode 100644 index 0000000..36cb985 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/file_finder_git.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/git.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/git.cpython-310.pyc new file mode 100644 index 0000000..9d35745 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/git.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/integration.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/integration.cpython-310.pyc new file mode 100644 index 0000000..7943503 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/integration.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/scm_workdir.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/scm_workdir.cpython-310.pyc new file mode 100644 index 0000000..2e664d2 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/scm_workdir.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/utils.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..723928f Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/utils.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/version.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..b4a113e Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/__pycache__/version.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_cli.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_cli.py new file mode 100644 index 0000000..8e01f24 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_cli.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import argparse +import os +import sys + +from setuptools_scm import _get_version +from setuptools_scm.config import Configuration +from setuptools_scm.discover import walk_potential_roots +from setuptools_scm.integration import find_files + + +def main(args: list[str] | None = None) -> None: + opts = _get_cli_opts(args) + inferred_root: str = opts.root or "." + + pyproject = opts.config or _find_pyproject(inferred_root) + + try: + + config = Configuration.from_file( + pyproject, + root=(os.path.abspath(opts.root) if opts.root is not None else None), + ) + except (LookupError, FileNotFoundError) as ex: + # no pyproject.toml OR no [tool.setuptools_scm] + print( + f"Warning: could not use {os.path.relpath(pyproject)}," + " using default configuration.\n" + f" Reason: {ex}.", + file=sys.stderr, + ) + config = Configuration(inferred_root) + + version = _get_version(config) + if version is None: + raise SystemExit("ERROR: no version found for", opts) + if opts.strip_dev: + version = version.partition(".dev")[0] + print(version) + + if opts.command == "ls": + for fname in find_files(config.root): + print(fname) + + +def _get_cli_opts(args: list[str] | None) -> argparse.Namespace: + prog = "python -m setuptools_scm" + desc = "Print project version according to SCM metadata" + parser = argparse.ArgumentParser(prog, description=desc) + # By default, help for `--help` starts with lower case, so we keep the pattern: + parser.add_argument( + "-r", + "--root", + default=None, + help='directory managed by the SCM, default: inferred from config file, or "."', + ) + parser.add_argument( + "-c", + "--config", + default=None, + metavar="PATH", + help="path to 'pyproject.toml' with setuptools_scm config, " + "default: looked up in the current or parent directories", + ) + parser.add_argument( + "--strip-dev", + action="store_true", + help="remove the dev/local parts of the version before printing the version", + ) + sub = parser.add_subparsers(title="extra commands", dest="command", metavar="") + # We avoid `metavar` to prevent printing repetitive information + desc = "List files managed by the SCM" + sub.add_parser("ls", help=desc[0].lower() + desc[1:], description=desc) + return parser.parse_args(args) + + +def _find_pyproject(parent: str) -> str: + for directory in walk_potential_roots(os.path.abspath(parent)): + pyproject = os.path.join(directory, "pyproject.toml") + if os.path.isfile(pyproject): + return pyproject + + return os.path.abspath( + "pyproject.toml" + ) # use default name to trigger the default errors diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_entrypoints.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_entrypoints.py new file mode 100644 index 0000000..2efb9f8 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_entrypoints.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +import warnings +from typing import Any +from typing import Iterator +from typing import overload +from typing import TYPE_CHECKING + +from .utils import function_has_arg +from .utils import trace +from .version import ScmVersion + +if TYPE_CHECKING: + from .config import Configuration + from typing_extensions import Protocol + from . import _types as _t +else: + Configuration = Any + + class Protocol: + pass + + +class MaybeConfigFunction(Protocol): + __name__: str + + @overload + def __call__(self, root: _t.PathT, config: Configuration) -> ScmVersion | None: + pass + + @overload + def __call__(self, root: _t.PathT) -> ScmVersion | None: + pass + + +def _call_entrypoint_fn( + root: _t.PathT, config: Configuration, fn: MaybeConfigFunction +) -> ScmVersion | None: + if function_has_arg(fn, "config"): + return fn(root, config=config) + else: + warnings.warn( + f"parse function {fn.__module__}.{fn.__name__}" + " are required to provide a named argument" + " 'config', setuptools_scm>=8.0 will remove support.", + category=DeprecationWarning, + stacklevel=2, + ) + return fn(root) + + +def _version_from_entrypoints( + config: Configuration, fallback: bool = False +) -> ScmVersion | None: + if fallback: + entrypoint = "setuptools_scm.parse_scm_fallback" + root = config.fallback_root + else: + entrypoint = "setuptools_scm.parse_scm" + root = config.absolute_root + + from .discover import iter_matching_entrypoints + + trace("version_from_ep", entrypoint, root) + for ep in iter_matching_entrypoints(root, entrypoint, config): + version: ScmVersion | None = _call_entrypoint_fn(root, config, ep.load()) + trace(ep, version) + if version: + return version + return None + + +try: + from importlib.metadata import entry_points # type: ignore +except ImportError: + try: + from importlib_metadata import entry_points + except ImportError: + from collections import defaultdict + + def entry_points() -> dict[str, list[_t.EntrypointProtocol]]: + warnings.warn( + "importlib metadata missing, " + "this may happen at build time for python3.7" + ) + return defaultdict(list) + + +def iter_entry_points( + group: str, name: str | None = None +) -> Iterator[_t.EntrypointProtocol]: + all_eps = entry_points() + if hasattr(all_eps, "select"): + eps = all_eps.select(group=group) + else: + eps = all_eps[group] + if name is None: + return iter(eps) + return (ep for ep in eps if ep.name == name) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__init__.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/__init__.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..26b2ee8 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/__init__.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/pyproject_reading.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/pyproject_reading.cpython-310.pyc new file mode 100644 index 0000000..c982238 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/pyproject_reading.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/setuptools.cpython-310.pyc b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/setuptools.cpython-310.pyc new file mode 100644 index 0000000..a9f4072 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/__pycache__/setuptools.cpython-310.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/pyproject_reading.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/pyproject_reading.py new file mode 100644 index 0000000..f43e6b1 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/pyproject_reading.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +import warnings +from typing import Any +from typing import Callable +from typing import Dict +from typing import NamedTuple +from typing import TYPE_CHECKING + +from .setuptools import read_dist_name_from_setup_cfg + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +_ROOT = "root" +TOML_RESULT: TypeAlias = Dict[str, Any] +TOML_LOADER: TypeAlias = Callable[[str], TOML_RESULT] + + +class PyProjectData(NamedTuple): + name: str + tool_name: str + project: TOML_RESULT + section: TOML_RESULT + + @property + def project_name(self) -> str | None: + return self.project.get("name") + + +def lazy_tomli_load(data: str) -> TOML_RESULT: + from tomli import loads + + return loads(data) + + +def read_pyproject( + name: str = "pyproject.toml", + tool_name: str = "setuptools_scm", + _load_toml: TOML_LOADER | None = None, +) -> PyProjectData: + if _load_toml is None: + _load_toml = lazy_tomli_load + with open(name, encoding="UTF-8") as strm: + data = strm.read() + defn = _load_toml(data) + try: + section = defn.get("tool", {})[tool_name] + except LookupError as e: + raise LookupError(f"{name} does not contain a tool.{tool_name} section") from e + project = defn.get("project", {}) + return PyProjectData(name, tool_name, project, section) + + +def get_args_for_pyproject( + pyproject: PyProjectData, + dist_name: str | None, + kwargs: TOML_RESULT, +) -> TOML_RESULT: + """drops problematic details and figures the distribution name""" + section = pyproject.section.copy() + kwargs = kwargs.copy() + if "relative_to" in section: + relative = section.pop("relative_to") + warnings.warn( + f"{pyproject.name}: at [tool.{pyproject.tool_name}]\n" + f"ignoring value relative_to={relative!r}" + " as its always relative to the config file" + ) + if "dist_name" in section: + if dist_name is None: + dist_name = section.pop("dist_name") + else: + assert dist_name == section["dist_name"] + del section["dist_name"] + if dist_name is None: + # minimal pep 621 support for figuring the pretend keys + dist_name = pyproject.project_name + if dist_name is None: + dist_name = read_dist_name_from_setup_cfg() + if _ROOT in kwargs: + if kwargs[_ROOT] is None: + kwargs.pop(_ROOT, None) + elif _ROOT in section: + if section[_ROOT] != kwargs[_ROOT]: + warnings.warn( + f"root {section[_ROOT]} is overridden" + f" by the cli arg {kwargs[_ROOT]}" + ) + section.pop("root", None) + return {"dist_name": dist_name, **section, **kwargs} diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/setuptools.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/setuptools.py new file mode 100644 index 0000000..5a0cc3a --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_integration/setuptools.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +import os +from typing import IO + + +def read_dist_name_from_setup_cfg( + input: str | os.PathLike[str] | IO[str] = "setup.cfg", +) -> str | None: + + # minimal effort to read dist_name off setup.cfg metadata + import configparser + + parser = configparser.ConfigParser() + + if isinstance(input, (os.PathLike, str)): + parser.read([input]) + else: + parser.read_file(input) + + dist_name = parser.get("metadata", "name", fallback=None) + return dist_name diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_overrides.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_overrides.py new file mode 100644 index 0000000..f18b82c --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_overrides.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import os + +from .config import Configuration +from .utils import trace +from .version import meta +from .version import ScmVersion + + +PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" +PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" + + +def _read_pretended_version_for(config: Configuration) -> ScmVersion | None: + """read a a overridden version from the environment + + tries ``SETUPTOOLS_SCM_PRETEND_VERSION`` + and ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_$UPPERCASE_DIST_NAME`` + """ + trace("dist name:", config.dist_name) + pretended: str | None + 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) + else: + return None diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_types.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_types.py new file mode 100644 index 0000000..6c6bdf8 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_types.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import List +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + + +if TYPE_CHECKING: + from setuptools_scm import version + import os + +from typing_extensions import ParamSpec, TypeAlias, Protocol + +PathT = Union["os.PathLike[str]", str] + +CMD_TYPE: TypeAlias = Union[List[str], str] + +VERSION_SCHEME = Union[str, Callable[["version.ScmVersion"], str]] + + +class EntrypointProtocol(Protocol): + name: str + + def load(self) -> Any: + pass + + +T = TypeVar("T") +T2 = TypeVar("T2") +P = ParamSpec("P") + + +def transfer_input_args( + template: Callable[P, T], +) -> Callable[[Callable[..., T]], Callable[P, T]]: + def decorate(func: Callable[..., T2]) -> Callable[P, T2]: + return func + + return decorate diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_version_cls.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_version_cls.py new file mode 100644 index 0000000..39e66b2 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/_version_cls.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from logging import getLogger + +from packaging.version import InvalidVersion +from packaging.version import Version as Version + + +class NonNormalizedVersion(Version): + """A non-normalizing version handler. + + You can use this class to preserve version verification but skip normalization. + For example you can use this to avoid git release candidate version tags + ("1.0.0-rc1") to be normalized to "1.0.0rc1". Only use this if you fully + trust the version tags. + """ + + def __init__(self, version: str) -> None: + # parse and validate using parent + super().__init__(version) + + # store raw for str + self._raw_version = version + + def __str__(self) -> str: + # return the non-normalized version (parent returns the normalized) + return self._raw_version + + def __repr__(self) -> str: + # same pattern as parent + return f"" + + +def _version_as_tuple(version_str: str) -> tuple[int | str, ...]: + try: + parsed_version = Version(version_str) + except InvalidVersion: + + log = getLogger("setuptools_scm") + log.exception("failed to parse version %s", version_str) + return (version_str,) + else: + version_fields: tuple[int | str, ...] = parsed_version.release + if parsed_version.dev is not None: + version_fields += (f"dev{parsed_version.dev}",) + if parsed_version.local is not None: + version_fields += (parsed_version.local,) + return version_fields diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/config.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/config.py new file mode 100644 index 0000000..3bf250a --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/config.py @@ -0,0 +1,216 @@ +""" configuration """ +from __future__ import annotations + +import os +import re +import warnings +from typing import Any +from typing import Callable +from typing import cast +from typing import Pattern +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +from ._integration.pyproject_reading import ( + get_args_for_pyproject as _get_args_for_pyproject, +) +from ._integration.pyproject_reading import read_pyproject as _read_pyproject +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version +from .utils import trace + + +if TYPE_CHECKING: + from . import _types as _t + from setuptools_scm.version import ScmVersion + +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: str | Pattern[str] | None) -> Pattern[str]: + 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: _t.PathT, relative_to: _t.PathT | None) -> str: + trace("abs root", repr(locals())) + if relative_to: + if ( + os.path.isabs(root) + and os.path.isabs(relative_to) + and not os.path.commonpath([root, relative_to]) == root + ): + 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) + + +_VersionT = Union[Version, NonNormalizedVersion] + + +def _validate_version_cls( + version_cls: type[_VersionT] | str | None, normalize: bool +) -> type[_VersionT]: + if not normalize: + # `normalize = False` means `version_cls = NonNormalizedVersion` + if version_cls is not None: + raise ValueError( + "Providing a custom `version_cls` is not permitted when " + "`normalize=False`" + ) + return NonNormalizedVersion + else: + # Use `version_cls` if provided, default to packaging or pkg_resources + if version_cls is None: + return Version + elif isinstance(version_cls, str): + try: + # Not sure this will work in old python + import importlib + + pkg, cls_name = version_cls.rsplit(".", 1) + version_cls_host = importlib.import_module(pkg) + return cast(Type[_VersionT], getattr(version_cls_host, cls_name)) + except: # noqa + raise ValueError(f"Unable to import version_cls='{version_cls}'") + else: + return version_cls + + +class Configuration: + """Global configuration model""" + + parent: _t.PathT | None + _root: str + _relative_to: str | None + version_cls: type[_VersionT] + + def __init__( + self, + relative_to: _t.PathT | None = None, + root: _t.PathT = ".", + version_scheme: ( + str | Callable[[ScmVersion], str | None] + ) = DEFAULT_VERSION_SCHEME, + local_scheme: (str | Callable[[ScmVersion], str | None]) = DEFAULT_LOCAL_SCHEME, + write_to: _t.PathT | None = None, + write_to_template: str | None = None, + tag_regex: str | Pattern[str] = DEFAULT_TAG_REGEX, + parentdir_prefix_version: str | None = None, + fallback_version: str | None = None, + fallback_root: _t.PathT = ".", + parse: Any | None = None, + git_describe_command: _t.CMD_TYPE | None = None, + dist_name: str | None = None, + version_cls: type[_VersionT] | type | str | None = None, + normalize: bool = True, + search_parent_directories: bool = False, + ): + # TODO: + self._relative_to = None if relative_to is None else os.fspath(relative_to) + self._root = "." + + self.root = os.fspath(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 # type: ignore + self.parse = parse + self.tag_regex = tag_regex # type: ignore + self.git_describe_command = git_describe_command + self.dist_name = dist_name + self.search_parent_directories = search_parent_directories + self.parent = None + + self.version_cls = _validate_version_cls(version_cls, normalize) + + @property + def fallback_root(self) -> str: + return self._fallback_root + + @fallback_root.setter + def fallback_root(self, value: _t.PathT) -> None: + self._fallback_root = os.path.abspath(value) + + @property + def absolute_root(self) -> str: + return self._absolute_root + + @property + def relative_to(self) -> str | None: + return self._relative_to + + @relative_to.setter + def relative_to(self, value: _t.PathT) -> None: + self._absolute_root = _check_absolute_root(self._root, value) + self._relative_to = os.fspath(value) + trace("root", repr(self._absolute_root)) + trace("relative_to", repr(value)) + + @property + def root(self) -> str: + return self._root + + @root.setter + def root(self, value: _t.PathT) -> None: + self._absolute_root = _check_absolute_root(value, self._relative_to) + self._root = os.fspath(value) + trace("root", repr(self._absolute_root)) + trace("relative_to", repr(self._relative_to)) + + @property + def tag_regex(self) -> Pattern[str]: + return self._tag_regex + + @tag_regex.setter + def tag_regex(self, value: str | Pattern[str]) -> None: + self._tag_regex = _check_tag_regex(value) + + @classmethod + def from_file( + cls, + name: str = "pyproject.toml", + dist_name: str | None = None, + _load_toml: Callable[[str], dict[str, Any]] | None = None, + **kwargs: Any, + ) -> Configuration: + """ + 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. + """ + + pyproject_data = _read_pyproject(name, _load_toml=_load_toml) + args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) + + return cls(relative_to=name, **args) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/discover.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/discover.py new file mode 100644 index 0000000..f7843ee --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/discover.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import os +from typing import Iterable +from typing import Iterator +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from . import _types as _t +from .config import Configuration +from .utils import trace + + +def walk_potential_roots( + root: _t.PathT, search_parents: bool = True +) -> Iterator[_t.PathT]: + """ + Iterate though a path and each of its parents. + :param root: File path. + :param search_parents: If ``False`` the parents are not considered. + """ + + if not search_parents: + yield root + return + + tail = root + + while tail: + yield root + root, tail = os.path.split(root) + + +def match_entrypoint(root: _t.PathT, name: str) -> bool: + """ + Consider a ``root`` as entry-point. + :param root: File path. + :param name: Subdirectory name. + :return: ``True`` if a subdirectory ``name`` exits in ``root``. + """ + + if os.path.exists(os.path.join(root, name)): + if not os.path.isabs(name): + return True + trace("ignoring bad ep", name) + + return False + + +def iter_matching_entrypoints( + root: _t.PathT, entrypoint: str, config: Configuration +) -> Iterable[_t.EntrypointProtocol]: + """ + Consider different entry-points in ``root`` and optionally its parents. + :param root: File path. + :param entrypoint: Entry-point to consider. + :param config: Configuration, + read ``search_parent_directories``, write found parent to ``parent``. + """ + + trace("looking for ep", entrypoint, root) + from ._entrypoints import iter_entry_points + + for wd in walk_potential_roots(root, config.search_parent_directories): + for ep in iter_entry_points(entrypoint): + if match_entrypoint(wd, ep.name): + trace("found ep", ep, "in", wd) + config.parent = wd + yield ep diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder.py new file mode 100644 index 0000000..99da792 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import os +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import TypeGuard + from . import _types as _t + +from .utils import trace + + +def scm_find_files( + path: _t.PathT, scm_files: set[str], scm_dirs: set[str] +) -> list[str]: + """ 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[str] = set() + res: list[str] = [] + 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: str) -> bool: + 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: str | None) -> TypeGuard[str]: + """ """ + 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 diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder_git.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder_git.py new file mode 100644 index 0000000..a83af21 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder_git.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import logging +import os +import subprocess +import tarfile +from typing import IO +from typing import TYPE_CHECKING + +from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex +from .utils import trace + +if TYPE_CHECKING: + from . import _types as _t + + +log = logging.getLogger(__name__) + + +def _git_toplevel(path: str) -> str | None: + try: + cwd = os.path.abspath(path or ".") + out, err, ret = do_ex(["git", "rev-parse", "HEAD"], cwd=cwd) + if ret != 0: + # BAIL if there is no commit + log.error("listing git files failed - pretending there aren't any") + return None + out, err, ret = do_ex( + ["git", "rev-parse", "--show-prefix"], + cwd=cwd, + ) + if ret != 0: + return None + out = out.strip()[:-1] # remove the trailing pathsep + if not out: + out = cwd + else: + # Here, ``out`` is a relative path to root of git. + # ``cwd`` is absolute path to current working directory. + # the below method removes the length of ``out`` from + # ``cwd``, which gives the git toplevel + assert cwd.replace("\\", "/").endswith(out), f"cwd={cwd!r}\nout={out!r}" + # In windows cwd contains ``\`` which should be replaced by ``/`` + # for this assertion to work. Length of string isn't changed by replace + # ``\\`` is just and escape for `\` + out = cwd[: -len(out)] + 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: IO[bytes], toplevel: str) -> tuple[set[str], set[str]]: + 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: str) -> tuple[set[str], set[str]]: + # 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, stderr=subprocess.DEVNULL + ) + assert proc.stdout is not None + 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.error("listing git files failed - pretending there aren't any") + return set(), set() + + +def git_find_files(path: _t.PathT = "") -> list[str]: + toplevel = _git_toplevel(os.fspath(path)) + if not is_toplevel_acceptable(toplevel): + return [] + assert toplevel is not None # mypy ignores typeguard + 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) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder_hg.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder_hg.py new file mode 100644 index 0000000..4f5e3ec --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/file_finder_hg.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import os +import subprocess + +from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex + + +def _hg_toplevel(path: str) -> str | None: + try: + out: str = subprocess.check_output( + ["hg", "root"], + cwd=(path or "."), + text=True, + stderr=subprocess.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: str) -> tuple[set[str], set[str]]: + hg_files: set[str] = set() + hg_dirs = {toplevel} + out, err, ret = do_ex(["hg", "files"], cwd=toplevel) + if ret: + (), () + for name in out.splitlines(): + name = os.path.normcase(name).replace("/", os.path.sep) + fullname = os.path.join(toplevel, name) + 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: str = "") -> list[str]: + toplevel = _hg_toplevel(path) + if not is_toplevel_acceptable(toplevel): + return [] + assert toplevel is not None + hg_files, hg_dirs = _hg_ls_files_and_dirs(toplevel) + return scm_find_files(path, hg_files, hg_dirs) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/git.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/git.py new file mode 100644 index 0000000..27cb159 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/git.py @@ -0,0 +1,312 @@ +from __future__ import annotations + +import os +import re +import warnings +from datetime import date +from datetime import datetime +from os.path import isfile +from os.path import join +from os.path import samefile +from typing import Callable +from typing import TYPE_CHECKING + +from .config import Configuration +from .scm_workdir import Workdir +from .utils import _CmdResult +from .utils import data_from_mime +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta +from .version import ScmVersion +from .version import tags_to_versions + +if TYPE_CHECKING: + from . import _types as _t + + from setuptools_scm.hg_git import GitWorkdirHgClient + +REF_TAG_RE = re.compile(r"(?<=\btag: )([^,]+)\b") +DESCRIBE_UNSUPPORTED = "%(describe" + +# If testing command in shell make sure to quote the match argument like +# '*[0-9]*' as it will expand before being sent to git if there are any matching +# files in current directory. +DEFAULT_DESCRIBE = [ + "git", + "describe", + "--dirty", + "--tags", + "--long", + "--match", + "*[0-9]*", +] + + +class GitWorkdir(Workdir): + """experimental, may change at any time""" + + COMMAND = "git" + + @classmethod + def from_potential_worktree(cls, wd: _t.PathT) -> GitWorkdir | None: + require_command(cls.COMMAND) + wd = os.path.abspath(wd) + git_dir = join(wd, ".git") + real_wd, _, ret = do_ex( + ["git", "--git-dir", git_dir, "rev-parse", "--show-prefix"], wd + ) + real_wd = real_wd[:-1] # remove the trailing pathsep + if ret: + return None + if not real_wd: + real_wd = wd + else: + assert wd.replace("\\", "/").endswith(real_wd) + # In windows wd contains ``\`` which should be replaced by ``/`` + # for this assertion to work. Length of string isn't changed by replace + # ``\\`` is just and escape for `\` + real_wd = wd[: -len(real_wd)] + trace("real root", real_wd) + if not samefile(real_wd, wd): + return None + + return cls(real_wd) + + def do_ex_git(self, cmd: list[str]) -> _CmdResult: + return self.do_ex(["git", "--git-dir", join(self.path, ".git")] + cmd) + + def is_dirty(self) -> bool: + out, _, _ = self.do_ex_git(["status", "--porcelain", "--untracked-files=no"]) + return bool(out) + + def get_branch(self) -> str | None: + branch, err, ret = self.do_ex_git(["rev-parse", "--abbrev-ref", "HEAD"]) + if ret: + trace("branch err", branch, err, ret) + branch, err, ret = self.do_ex_git(["symbolic-ref", "--short", "HEAD"]) + if ret: + trace("branch err (symbolic-ref)", branch, err, ret) + return None + return branch + + def get_head_date(self) -> date | None: + timestamp, err, ret = self.do_ex_git( + ["-c", "log.showSignature=false", "log", "-n", "1", "HEAD", "--format=%cI"] + ) + if ret: + trace("timestamp err", timestamp, err, ret) + return None + # TODO, when dropping python3.6 use fromiso + date_part = timestamp.split("T")[0] + if "%c" in date_part: + trace("git too old -> timestamp is ", timestamp) + return None + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self) -> bool: + return isfile(join(self.path, ".git/shallow")) + + def fetch_shallow(self) -> None: + self.do_ex_git(["fetch", "--unshallow"]) + + def node(self) -> str | None: + node, _, ret = self.do_ex_git(["rev-parse", "--verify", "--quiet", "HEAD"]) + if not ret: + return node[:7] + else: + return None + + def count_all_nodes(self) -> int: + revs, _, _ = self.do_ex_git(["rev-list", "HEAD"]) + return revs.count("\n") + 1 + + def default_describe(self) -> _CmdResult: + git_dir = join(self.path, ".git") + return self.do_ex( + DEFAULT_DESCRIBE[:1] + ["--git-dir", git_dir] + DEFAULT_DESCRIBE[1:] + ) + + +def warn_on_shallow(wd: GitWorkdir) -> None: + """experimental, may change at any time""" + if wd.is_shallow(): + warnings.warn(f'"{wd.path}" is shallow and may cause errors') + + +def fetch_on_shallow(wd: GitWorkdir) -> None: + """experimental, may change at any time""" + if wd.is_shallow(): + warnings.warn(f'"{wd.path}" was shallow, git fetch was used to rectify') + wd.fetch_shallow() + + +def fail_on_shallow(wd: GitWorkdir) -> None: + """experimental, may change at any time""" + if wd.is_shallow(): + raise ValueError( + f'{wd.path} is shallow, please correct with "git fetch --unshallow"' + ) + + +def get_working_directory(config: Configuration) -> GitWorkdir | None: + """ + Return the working directory (``GitWorkdir``). + """ + + if config.parent: + return GitWorkdir.from_potential_worktree(config.parent) + + if config.search_parent_directories: + return search_parent(config.absolute_root) + + return GitWorkdir.from_potential_worktree(config.absolute_root) + + +def parse( + root: str, + describe_command: str | list[str] | None = None, + pre_parse: Callable[[GitWorkdir], None] = warn_on_shallow, + config: Configuration | None = None, +) -> ScmVersion | None: + """ + :param pre_parse: experimental pre_parse action, may change at any time + """ + if not config: + config = Configuration(root=root) + + wd = get_working_directory(config) + if wd: + return _git_parse_inner( + config, wd, describe_command=describe_command, pre_parse=pre_parse + ) + else: + return None + + +def _git_parse_inner( + config: Configuration, + wd: GitWorkdir | GitWorkdirHgClient, + pre_parse: None | (Callable[[GitWorkdir | GitWorkdirHgClient], None]) = None, + describe_command: _t.CMD_TYPE | None = None, +) -> ScmVersion: + if pre_parse: + pre_parse(wd) + + if config.git_describe_command is not None: + describe_command = config.git_describe_command + + if describe_command is not None: + out, _, ret = wd.do_ex(describe_command) + else: + out, _, ret = wd.default_describe() + distance: int | None + node: str | None + if ret == 0: + tag, distance, node, dirty = _git_parse_describe(out) + if distance == 0 and not dirty: + distance = None + else: + # If 'git git_describe_command' failed, try to get the information otherwise. + tag = "0.0" + node = wd.node() + if node is None: + distance = 0 + else: + distance = wd.count_all_nodes() + node = "g" + node + dirty = wd.is_dirty() + + branch = wd.get_branch() + node_date = wd.get_head_date() or date.today() + + return meta( + tag, + branch=branch, + node=node, + node_date=node_date, + distance=distance, + dirty=dirty, + config=config, + ) + + +def _git_parse_describe(describe_output: str) -> tuple[str, int, str, bool]: + # '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) + return tag, int(number), node, dirty + + +def search_parent(dirname: _t.PathT) -> GitWorkdir | None: + """ + Walk up the path to find the `.git` directory. + :param dirname: Directory from which to start searching. + """ + + # Code based on: + # https://github.com/gitpython-developers/GitPython/blob/main/git/repo/base.py + + curpath = os.path.abspath(dirname) + + while curpath: + + try: + wd = GitWorkdir.from_potential_worktree(curpath) + except Exception: + wd = None + + if wd is not None: + return wd + + curpath, tail = os.path.split(curpath) + + if not tail: + return None + return None + + +def archival_to_version( + data: dict[str, str], config: Configuration | None = None +) -> ScmVersion | None: + node: str | None + trace("data", data) + archival_describe = data.get("describe-name", DESCRIBE_UNSUPPORTED) + if DESCRIBE_UNSUPPORTED in archival_describe: + warnings.warn("git archive did not support describe output") + else: + tag, number, node, _ = _git_parse_describe(archival_describe) + return meta( + tag, + config=config, + distance=None if number == 0 else number, + node=node, + ) + versions = tags_to_versions(REF_TAG_RE.findall(data.get("ref-names", ""))) + if versions: + return meta(versions[0], config=config) + else: + node = data.get("node") + if node is None: + return None + elif "$FORMAT" in node.upper(): + warnings.warn("unexported git archival found") + return None + else: + return meta("0.0", node=node, config=config) + + +def parse_archival( + root: _t.PathT, config: Configuration | None = None +) -> ScmVersion | None: + archival = os.path.join(root, ".git_archival.txt") + data = data_from_mime(archival) + return archival_to_version(data, config=config) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hacks.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hacks.py new file mode 100644 index 0000000..9ca0df9 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hacks.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import os +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from . import _types as _t +from .config import Configuration +from .utils import data_from_mime +from .utils import trace +from .version import meta +from .version import ScmVersion +from .version import tag_to_version + +_UNKNOWN = "UNKNOWN" + + +def parse_pkginfo( + root: _t.PathT, config: Configuration | None = None +) -> ScmVersion | None: + + pkginfo = os.path.join(root, "PKG-INFO") + trace("pkginfo", pkginfo) + data = data_from_mime(pkginfo) + version = data.get("Version", _UNKNOWN) + if version != _UNKNOWN: + return meta(version, preformatted=True, config=config) + else: + return None + + +def parse_pip_egg_info( + root: _t.PathT, config: Configuration | None = None +) -> ScmVersion | None: + pipdir = os.path.join(root, "pip-egg-info") + if not os.path.isdir(pipdir): + return None + items = os.listdir(pipdir) + trace("pip-egg-info", pipdir, items) + if not items: + return None + return parse_pkginfo(os.path.join(pipdir, items[0]), config=config) + + +def fallback_version(root: _t.PathT, config: Configuration) -> ScmVersion | 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: + trace("FALLBACK") + return meta(config.fallback_version, preformatted=True, config=config) + return None diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hg.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hg.py new file mode 100644 index 0000000..d6d8458 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hg.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import datetime +import os +from pathlib import Path +from typing import TYPE_CHECKING + +from ._version_cls import Version +from .config import Configuration +from .scm_workdir import Workdir +from .utils import data_from_mime +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta +from .version import ScmVersion +from .version import tag_to_version + +if TYPE_CHECKING: + from . import _types as _t + + +class HgWorkdir(Workdir): + + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd: _t.PathT) -> HgWorkdir | None: + require_command(cls.COMMAND) + root, err, ret = do_ex("hg root", wd) + if ret: + return None + return cls(root) + + def get_meta(self, config: Configuration) -> ScmVersion | None: + + node: str + tags_str: str + bookmark: str + node_date_str: str + node, tags_str, bookmark, node_date_str = self.hg_log( + ".", "{node}\n{tag}\n{bookmark}\n{date|shortdate}" + ).split("\n") + + # TODO: support bookmarks and topics (but nowadays bookmarks are + # mainly used to emulate Git branches, which is already supported with + # the dedicated class GitWorkdirHgClient) + + branch, dirty_str, dirty_date = self.do( + ["hg", "id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}"] + ).split("\n") + dirty = bool(int(dirty_str)) + # todo: fromiso + node_date = datetime.date( + *map(int, (dirty_date if dirty else node_date_str).split("-")) + ) + + if node.count("0") == len(node): + trace("initial node", self.path) + return meta( + "0.0", config=config, dirty=dirty, branch=branch, node_date=node_date + ) + + node = "h" + node[:7] + + tags = tags_str.split() + if "tip" in tags: + # tip is not a real tag + tags.remove("tip") + + if tags: + tag = tag_to_version(tags[0]) + if tag: + return meta(tag, dirty=dirty, branch=branch, config=config) + + try: + tag_str = self.get_latest_normalizable_tag() + if tag_str is None: + dist = self.get_distance_revs("") + else: + dist = self.get_distance_revs(tag_str) + + if tag_str == "null" or tag_str is None: + tag = Version("0.0") + dist = int(dist) + 1 + else: + tag = tag_to_version(tag_str, config=config) + assert tag is not None + + if self.check_changes_since_tag(tag_str) or dirty: + return meta( + tag, + distance=dist, + node=node, + dirty=dirty, + branch=branch, + config=config, + node_date=node_date, + ) + else: + return meta(tag, config=config, node_date=node_date) + + except ValueError as e: + trace("error", e) + pass # unpacking failed, old hg + + return None + + def hg_log(self, revset: str, template: str) -> str: + cmd = ["hg", "log", "-r", revset, "-T", template] + return self.do(cmd) + + def get_latest_normalizable_tag(self) -> str | None: + # Gets all tags containing a '.' (see #229) from oldest to newest + outlines = self.hg_log( + revset="ancestors(.) and tag('re:\\.')", + template="{tags}{if(tags, '\n', '')}", + ).split() + if not outlines: + return None + tag = outlines[-1].split()[-1] + return tag + + def get_distance_revs(self, rev1: str, rev2: str = ".") -> int: + + revset = f"({rev1}::{rev2})" + out = self.hg_log(revset, ".") + return len(out) - 1 + + def check_changes_since_tag(self, tag: str | None) -> bool: + + if tag == "0.0" or tag is None: + return True + + revset = ( + "(branch(.)" # look for revisions in this branch only + f" and tag({tag!r})::." # after the last tag + # ignore commits that only modify .hgtags and nothing else: + " and (merge() or file('re:^(?!\\.hgtags).*$'))" + f" and not tag({tag!r}))" # ignore the tagged commit itself + ) + + return bool(self.hg_log(revset, ".")) + + +def parse(root: _t.PathT, config: Configuration | None = None) -> ScmVersion | None: + if not config: + config = Configuration(root=root) + + if os.path.exists(os.path.join(root, ".hg/git")): + paths, _, ret = do_ex("hg path", root) + if not ret: + for line in paths.split("\n"): + if line.startswith("default ="): + path = Path(line.split()[2]) + if path.name.endswith(".git") or (path / ".git").exists(): + from .git import _git_parse_inner + from .hg_git import GitWorkdirHgClient + + wd_hggit = GitWorkdirHgClient.from_potential_worktree(root) + if wd_hggit: + return _git_parse_inner(config, wd_hggit) + + wd = HgWorkdir.from_potential_worktree(config.absolute_root) + + if wd is None: + return None + + return wd.get_meta(config) + + +def archival_to_version( + data: dict[str, str], config: Configuration | None = None +) -> ScmVersion: + 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=int(data["latesttagdistance"]), + node=node, + config=config, + ) + else: + return meta("0.0", node=node, config=config) + + +def parse_archival(root: _t.PathT, config: Configuration | None = None) -> ScmVersion: + archival = os.path.join(root, ".hg_archival.txt") + data = data_from_mime(archival) + return archival_to_version(data, config=config) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hg_git.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hg_git.py new file mode 100644 index 0000000..bde71ec --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/hg_git.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +import os +from contextlib import suppress +from datetime import date +from datetime import datetime + +from . import _types as _t +from .git import GitWorkdir +from .hg import HgWorkdir +from .utils import _CmdResult +from .utils import do_ex +from .utils import require_command +from .utils import trace + + +_FAKE_GIT_DESCRIBE_ERROR = _CmdResult("<>hg git failed", "", 1) + + +class GitWorkdirHgClient(GitWorkdir, HgWorkdir): + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd: _t.PathT) -> GitWorkdirHgClient | None: + require_command(cls.COMMAND) + root, _, ret = do_ex(["hg", "root"], wd) + if ret: + return None + return cls(root) + + def is_dirty(self) -> bool: + out, _, _ = self.do_ex('hg id -T "{dirty}"') + return bool(out) + + def get_branch(self) -> str | None: + res = self.do_ex('hg id -T "{bookmarks}"') + if res.returncode: + trace("branch err", res) + return None + return res.out + + def get_head_date(self) -> date | None: + date_part, err, ret = self.do_ex('hg log -r . -T "{shortdate(date)}"') + if ret: + trace("head date err", date_part, err, ret) + return None + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self) -> bool: + return False + + def fetch_shallow(self) -> None: + pass + + def get_hg_node(self) -> str | None: + node, _, ret = self.do_ex('hg log -r . -T "{node}"') + if not ret: + return node + else: + return None + + def _hg2git(self, hg_node: str) -> str | None: + with suppress(FileNotFoundError): + with open(os.path.join(self.path, ".hg/git-mapfile")) as map_items: + for item in map_items: + if hg_node in item: + git_node, hg_node = item.split() + return git_node + return None + + def node(self) -> str | None: + hg_node = self.get_hg_node() + if hg_node is None: + return None + + git_node = self._hg2git(hg_node) + + if git_node is None: + # trying again after hg -> git + self.do_ex("hg gexport") + git_node = self._hg2git(hg_node) + + if git_node is None: + trace("Cannot get git node so we use hg node", hg_node) + + if hg_node == "0" * len(hg_node): + # mimic Git behavior + return None + + return hg_node + + return git_node[:7] + + def count_all_nodes(self) -> int: + revs, _, _ = self.do_ex(["hg", "log", "-r", "ancestors(.)", "-T", "."]) + return len(revs) + + def default_describe(self) -> _CmdResult: + """ + Tentative to reproduce the output of + + `git describe --dirty --tags --long --match *[0-9]*` + + """ + hg_tags_str, _, ret = self.do_ex( + [ + "hg", + "log", + "-r", + "(reverse(ancestors(.)) and tag(r're:v?[0-9].*'))", + "-T", + "{tags}{if(tags, ' ', '')}", + ] + ) + if ret: + return _FAKE_GIT_DESCRIBE_ERROR + hg_tags: list[str] = hg_tags_str.split() + + if not hg_tags: + return _FAKE_GIT_DESCRIBE_ERROR + + with open(os.path.join(self.path, ".hg/git-tags")) as fp: + git_tags: dict[str, str] = dict(line.split()[::-1] for line in fp) + + tag: str + for hg_tag in hg_tags: + if hg_tag in git_tags: + tag = hg_tag + break + else: + trace("tag not found", hg_tags, git_tags) + return _FAKE_GIT_DESCRIBE_ERROR + + out, _, ret = self.do_ex(["hg", "log", "-r", f"'{tag}'::.", "-T", "."]) + if ret: + return _FAKE_GIT_DESCRIBE_ERROR + distance = len(out) - 1 + + node = self.node() + assert node is not None + desc = f"{tag}-{distance}-g{node}" + + if self.is_dirty(): + desc += "-dirty" + trace("desc", desc) + return _CmdResult(desc, "", 0) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/integration.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/integration.py new file mode 100644 index 0000000..45c841d --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/integration.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import os +import warnings +from typing import Any +from typing import Callable +from typing import TYPE_CHECKING + +import setuptools + +from . import _get_version +from . import _version_missing +from ._entrypoints import iter_entry_points +from ._integration.setuptools import ( + read_dist_name_from_setup_cfg as _read_dist_name_from_setup_cfg, +) +from .config import Configuration +from .utils import do +from .utils import trace + +if TYPE_CHECKING: + from . import _types as _t + + +def _warn_on_old_setuptools(_version: str = setuptools.__version__) -> None: + if int(_version.split(".")[0]) < 45: + warnings.warn( + RuntimeWarning( + f""" +ERROR: setuptools=={_version} is used in combination with setuptools_scm>=6.x + +Your build configuration is incomplete and previously worked by accident! +setuptools_scm requires setuptools>=45 + + +This happens as setuptools is unable to replace itself when a activated build dependency +requires a more recent setuptools version +(it does not respect "setuptools>X" in setup_requires). + + +setuptools>=31 is required for setup.cfg metadata support +setuptools>=42 is required for pyproject.toml configuration support + +Suggested workarounds if applicable: + - preinstalling build dependencies like setuptools_scm before running setup.py + - installing setuptools_scm using the system package manager to ensure consistency + - migrating from the deprecated setup_requires mechanism to pep517/518 + and using a pyproject.toml to declare build dependencies + which are reliably pre-installed before running the build tools +""" + ) + ) + + +_warn_on_old_setuptools() + + +def _assign_version(dist: setuptools.Distribution, config: Configuration) -> None: + maybe_version = _get_version(config) + + if maybe_version is None: + _version_missing(config) + else: + dist.metadata.version = maybe_version + + +def version_keyword( + dist: setuptools.Distribution, + keyword: str, + value: bool | dict[str, Any] | Callable[[], dict[str, Any]], +) -> None: + if not value: + return + elif value is True: + value = {} + elif callable(value): + 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 # type: str | None + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + config = Configuration(dist_name=dist_name, **value) + _assign_version(dist, config) + + +def find_files(path: _t.PathT = "") -> list[str]: + 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 infer_version(dist: setuptools.Distribution) -> None: + trace( + "finalize hook", + vars(dist.metadata), + ) + dist_name = dist.metadata.name + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + if not os.path.isfile("pyproject.toml"): + return + if dist_name == "setuptools_scm": + return + try: + config = Configuration.from_file(dist_name=dist_name) + except LookupError as e: + trace(e) + else: + _assign_version(dist, config) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/scm_workdir.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/scm_workdir.py new file mode 100644 index 0000000..113f68a --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/scm_workdir.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import ClassVar +from typing import TYPE_CHECKING + +from .utils import _CmdResult +from .utils import do +from .utils import do_ex +from .utils import require_command + +if TYPE_CHECKING: + from . import _types as _t + + +class Workdir: + COMMAND: ClassVar[str] + + def __init__(self, path: _t.PathT): + require_command(self.COMMAND) + self.path = path + + def do_ex(self, cmd: _t.CMD_TYPE) -> _CmdResult: + return do_ex(cmd, cwd=self.path) + + def do(self, cmd: _t.CMD_TYPE) -> str: + return do(cmd, cwd=self.path) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/utils.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/utils.py new file mode 100644 index 0000000..7c690b8 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/utils.py @@ -0,0 +1,180 @@ +""" +utils +""" +from __future__ import annotations + +import os +import platform +import shlex +import subprocess +import sys +import textwrap +import warnings +from types import CodeType +from types import FunctionType +from typing import Iterator +from typing import Mapping +from typing import NamedTuple +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + + from . import _types as _t + +DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) +IS_WINDOWS = platform.system() == "Windows" + + +class _CmdResult(NamedTuple): + out: str + err: str + returncode: int + + +def no_git_env(env: Mapping[str, str]) -> dict[str, str]: + # 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 avoid_pip_isolation(env: Mapping[str, str]) -> dict[str, str]: + """ + pip build isolation can break Mercurial + (see https://github.com/pypa/pip/issues/10635) + + pip uses PYTHONNOUSERSITE and a path in PYTHONPATH containing "pip-build-env-". + """ + new_env = {k: v for k, v in env.items() if k != "PYTHONNOUSERSITE"} + if "PYTHONPATH" not in new_env: + return new_env + + new_env["PYTHONPATH"] = os.pathsep.join( + [ + path + for path in new_env["PYTHONPATH"].split(os.pathsep) + if "pip-build-env-" not in path + ] + ) + return new_env + + +def trace(*k: object, indent: bool = False) -> None: + if DEBUG: + if indent and len(k) > 1: + k = (k[0],) + tuple(textwrap.indent(str(s), " ") for s in k[1:]) + print(*k, file=sys.stderr, flush=True) + + +def ensure_stripped_str(str_or_bytes: str | bytes) -> str: + if isinstance(str_or_bytes, str): + return str_or_bytes.strip() + else: + return str_or_bytes.decode("utf-8", "surrogateescape").strip() + + +def _run(cmd: _t.CMD_TYPE, cwd: _t.PathT) -> subprocess.CompletedProcess[str]: + return subprocess.run( + cmd, + capture_output=True, + cwd=str(cwd), + env=dict( + avoid_pip_isolation(no_git_env(os.environ)), + # os.environ, + # try to disable i18n + LC_ALL="C", + LANGUAGE="", + HGPLAIN="1", + ), + text=True, + ) + + +def do_ex(cmd: _t.CMD_TYPE, cwd: _t.PathT = ".") -> _CmdResult: + if not DEBUG or not isinstance(cmd, list): + cmd_4_trace = cmd + else: + # give better results than shlex.join in our cases + cmd_4_trace = " ".join( + [s if all(c not in s for c in " {[:") else f'"{s}"' for s in cmd] + ) + trace("----\ncmd:\n", cmd_4_trace, indent=True) + trace(" in:", cwd) + if os.name == "posix" and not isinstance(cmd, (list, tuple)): + cmd = shlex.split(cmd) + + res = _run(cmd, cwd) + if res.stdout: + trace("out:\n", res.stdout, indent=True) + if res.stderr: + trace("err:\n", res.stderr, indent=True) + if res.returncode: + trace("ret:", res.returncode) + return _CmdResult( + ensure_stripped_str(res.stdout), ensure_stripped_str(res.stderr), res.returncode + ) + + +def do(cmd: list[str] | str, cwd: str | _t.PathT = ".") -> str: + out, err, ret = do_ex(cmd, cwd) + if ret and not DEBUG: + print(err) + return out + + +def data_from_mime(path: _t.PathT) -> dict[str, str]: + with 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: object | FunctionType, argname: str) -> bool: + assert isinstance(fn, FunctionType) + code: CodeType = fn.__code__ + return argname in code.co_varnames + + +def has_command(name: str, args: list[str] | None = None, warn: bool = True) -> bool: + try: + cmd = [name, "help"] if args is None else [name, *args] + p = _run(cmd, ".") + except OSError: + trace(*sys.exc_info()) + res = False + else: + res = not p.returncode + if not res and warn: + warnings.warn("%r was not found" % name, category=RuntimeWarning) + return res + + +def require_command(name: str) -> None: + if not has_command(name, warn=False): + raise OSError("%r was not found" % name) + + +def iter_entry_points( + group: str, name: str | None = None +) -> Iterator[_t.EntrypointProtocol]: + + from ._entrypoints import iter_entry_points + + return iter_entry_points(group, name) diff --git a/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/version.py b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/version.py new file mode 100644 index 0000000..a53f50b --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.10.egg/setuptools_scm/version.py @@ -0,0 +1,563 @@ +from __future__ import annotations + +import os +import re +import warnings +from datetime import date +from datetime import datetime +from datetime import timezone +from typing import Any +from typing import Callable +from typing import cast +from typing import Iterator +from typing import List +from typing import Match +from typing import overload +from typing import Tuple +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import Concatenate + + from . import _types as _t + +from ._version_cls import Version as PkgVersion +from .config import Configuration +from .config import _VersionT +from .utils import trace + +SEMVER_MINOR = 2 +SEMVER_PATCH = 3 +SEMVER_LEN = 3 + + +def _parse_version_tag( + tag: str | object, config: Configuration +) -> dict[str, str] | None: + tagstring = tag if isinstance(tag, str) else str(tag) + match = config.tag_regex.match(tagstring) + + result = None + if match: + key: str | int + 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(f"tag '{tag}' parsed to {result}") + return result + + +def callable_or_entrypoint(group: str, callable_or_name: str | Any) -> Any: + trace("ep", (group, callable_or_name)) + + if callable(callable_or_name): + return callable_or_name + from ._entrypoints import iter_entry_points + + for ep in iter_entry_points(group, callable_or_name): + trace("ep found:", ep.name) + return ep.load() + + +def tag_to_version( + tag: _VersionT | str, config: Configuration | None = None +) -> _VersionT | 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(f"tag {tag!r} no version found") + return None + + version_str = tagdict["version"] + trace("version pre parse", version_str) + + if tagdict.get("suffix", ""): + warnings.warn( + "tag {!r} will be stripped of its suffix '{}'".format( + tag, tagdict["suffix"] + ) + ) + + version = config.version_cls(version_str) + trace("version", repr(version)) + + return version + + +def tags_to_versions( + tags: list[str], config: Configuration | None = None +) -> list[_VersionT]: + """ + 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: list[_VersionT] = [] + for tag in tags: + parsed = tag_to_version(tag, config=config) + if parsed: + result.append(parsed) + return result + + +class ScmVersion: + def __init__( + self, + tag_version: Any, + config: Configuration, + distance: int | None = None, + node: str | None = None, + dirty: bool = False, + preformatted: bool = False, + branch: str | None = None, + node_date: date | None = None, + **kw: object, + ): + 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.node_date = node_date + if "SOURCE_DATE_EPOCH" in os.environ: + date_epoch = int(os.environ["SOURCE_DATE_EPOCH"]) + self.time = datetime.fromtimestamp(date_epoch, timezone.utc) + else: + self.time = datetime.now(timezone.utc) + self._extra = kw + self.dirty = dirty + self.preformatted = preformatted + self.branch = branch + self.config = config + + @property + def extra(self) -> dict[str, Any]: + warnings.warn( + "ScmVersion.extra is deprecated and will be removed in future", + category=DeprecationWarning, + stacklevel=2, + ) + return self._extra + + @property + def exact(self) -> bool: + return self.distance is None + + def __repr__(self) -> str: + return self.format_with( + "" + ) + + def format_with(self, fmt: str, **kw: object) -> str: + return fmt.format( + time=self.time, + tag=self.tag, + distance=self.distance, + node=self.node, + dirty=self.dirty, + branch=self.branch, + node_date=self.node_date, + **kw, + ) + + def format_choice(self, clean_format: str, dirty_format: str, **kw: object) -> str: + return self.format_with(dirty_format if self.dirty else clean_format, **kw) + + def format_next_version( + self, + guess_next: Callable[Concatenate[ScmVersion, _t.P], str], + fmt: str = "{guessed}.dev{distance}", + *k: _t.P.args, # type: ignore + **kw: _t.P.kwargs, # type: ignore + ) -> str: + guessed = guess_next(self, *k, **kw) + return self.format_with(fmt, guessed=guessed) + + +def _parse_tag( + tag: _VersionT | str, preformatted: bool, config: Configuration | None +) -> _VersionT | str: + if preformatted: + return tag + elif config is None or not isinstance(tag, config.version_cls): + version = tag_to_version(tag, config) + assert version is not None + return version + else: + return tag + + +def meta( + tag: str | _VersionT, + distance: int | None = None, + dirty: bool = False, + node: str | None = None, + preformatted: bool = False, + branch: str | None = None, + config: Configuration | None = None, + node_date: date | None = None, + **kw: Any, +) -> ScmVersion: + if not config: + warnings.warn( + "meta invoked without explicit configuration," + " will use defaults where required." + ) + config = Configuration() + parsed_version = _parse_tag(tag, preformatted, config) + trace("version", tag, "->", parsed_version) + assert parsed_version is not None, "Can't parse version %s" % tag + return ScmVersion( + parsed_version, + distance=distance, + node=node, + dirty=dirty, + preformatted=preformatted, + branch=branch, + config=config, + node_date=node_date, + **kw, + ) + + +def guess_next_version(tag_version: ScmVersion) -> str: + version = _strip_local(str(tag_version.tag)) + return _bump_dev(version) or _bump_regex(version) + + +def _dont_guess_next_version(tag_version: ScmVersion) -> str: + version = _strip_local(str(tag_version.tag)) + return _bump_dev(version) or _add_post(version) + + +def _strip_local(version_string: str) -> str: + public, sep, local = version_string.partition("+") + return public + + +def _add_post(version: str) -> str: + if "post" in version: + raise ValueError( + f"{version} already is a post release, refusing to guess the update" + ) + return f"{version}.post1" + + +def _bump_dev(version: str) -> str | None: + if ".dev" not in version: + return None + + prefix, tail = version.rsplit(".dev", 1) + if tail != "0": + raise ValueError( + "choosing custom numbers for the `.devX` distance " + "is not supported.\n " + f"The {version} can't be bumped\n" + "Please drop the tag or create a new supported one ending in .dev0" + ) + return prefix + + +def _bump_regex(version: str) -> str: + match = re.match(r"(.*?)(\d+)$", version) + if match is None: + raise ValueError( + "{version} does not end with a number to bump, " + "please correct or use a custom version scheme".format(version=version) + ) + else: + prefix, tail = match.groups() + return "%s%d" % (prefix, int(tail) + 1) + + +def guess_next_dev_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + else: + return version.format_next_version(guess_next_version) + + +def guess_next_simple_semver( + version: ScmVersion, retain: int, increment: bool = True +) -> str: + try: + parts = [int(i) for i in str(version.tag).split(".")[:retain]] + except ValueError: + raise ValueError(f"{version} can't be parsed as numeric version") + while len(parts) < retain: + parts.append(0) + if increment: + parts[-1] += 1 + while len(parts) < SEMVER_LEN: + parts.append(0) + return ".".join(str(i) for i in parts) + + +def simplified_semver_version(version: ScmVersion) -> str: + if version.exact: + return guess_next_simple_semver(version, 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 release_branch_semver_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + if version.branch is not None: + # Does the branch name (stripped of namespace) parse as a version? + branch_ver_data = _parse_version_tag( + version.branch.split("/")[-1], version.config + ) + if branch_ver_data is not None: + branch_ver = branch_ver_data["version"] + if branch_ver[0] == "v": + # Allow branches that start with 'v', similar to Version. + branch_ver = branch_ver[1:] + # Does the branch version up to the minor part match the tag? If not it + # might be like, an issue number or something and not a version number, so + # we only want to use it if it matches. + tag_ver_up_to_minor = str(version.tag).split(".")[:SEMVER_MINOR] + branch_ver_up_to_minor = branch_ver.split(".")[:SEMVER_MINOR] + if branch_ver_up_to_minor == tag_ver_up_to_minor: + # We're in a release/maintenance branch, next is a patch/rc/beta bump: + return version.format_next_version(guess_next_version) + # We're in a development branch, next is a minor bump: + return version.format_next_version(guess_next_simple_semver, retain=SEMVER_MINOR) + + +def release_branch_semver(version: ScmVersion) -> str: + warnings.warn( + "release_branch_semver is deprecated and will be removed in future. " + + "Use release_branch_semver_version instead", + category=DeprecationWarning, + stacklevel=2, + ) + return release_branch_semver_version(version) + + +def no_guess_dev_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + else: + return version.format_next_version(_dont_guess_next_version) + + +def date_ver_match(ver: str) -> Match[str] | None: + match = re.match( + ( + r"^(?P(?P\d{2}|\d{4})(?:\.\d{1,2}){2})" + r"(?:\.(?P\d*)){0,1}?$" + ), + ver, + ) + return match + + +def guess_next_date_ver( + version: ScmVersion, + node_date: date | None = None, + date_fmt: str | None = None, + version_cls: type | None = None, +) -> str: + """ + same-day -> patch +1 + other-day -> today + + distance is always added as .devX + """ + match = date_ver_match(str(version.tag)) + if match is None: + warnings.warn( + f"{version} does not correspond to a valid versioning date, " + "assuming legacy version" + ) + if date_fmt is None: + date_fmt = "%y.%m.%d" + else: + # deduct date format if not provided + if date_fmt is None: + date_fmt = "%Y.%m.%d" if len(match.group("year")) == 4 else "%y.%m.%d" + today = datetime.now(timezone.utc).date() + head_date = node_date or today + # compute patch + if match is None: + tag_date = today + else: + tag_date = datetime.strptime(match.group("date"), date_fmt).date() + if tag_date == head_date: + patch = "0" if match is None else (match.group("patch") or "0") + patch = int(patch) + 1 + else: + if tag_date > head_date and match is not None: + # warn on future times + warnings.warn( + "your previous tag ({}) is ahead your node date ({})".format( + tag_date, head_date + ) + ) + patch = 0 + next_version = "{node_date:{date_fmt}}.{patch}".format( + node_date=head_date, date_fmt=date_fmt, patch=patch + ) + # rely on the Version object to ensure consistency (e.g. remove leading 0s) + if version_cls is None: + version_cls = PkgVersion + next_version = str(version_cls(next_version)) + return next_version + + +def calver_by_date(version: ScmVersion) -> str: + if version.exact and not version.dirty: + return version.format_with("{tag}") + # TODO: move the release-X check to a new scheme + if version.branch is not None and version.branch.startswith("release-"): + branch_ver = _parse_version_tag(version.branch.split("-")[-1], version.config) + if branch_ver is not None: + ver = branch_ver["version"] + match = date_ver_match(ver) + if match: + return ver + return version.format_next_version( + guess_next_date_ver, + node_date=version.node_date, + version_cls=version.config.version_cls, + ) + + +def _format_local_with_time(version: ScmVersion, time_format: str) -> str: + + 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: ScmVersion) -> str: + return _format_local_with_time(version, time_format="%Y%m%d") + + +def get_local_node_and_timestamp(version: ScmVersion, fmt: str = "%Y%m%d%H%M%S") -> str: + return _format_local_with_time(version, time_format=fmt) + + +def get_local_dirty_tag(version: ScmVersion) -> str: + return version.format_choice("", "+dirty") + + +def get_no_local_node(_: Any) -> str: + return "" + + +def postrelease_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + else: + return version.format_with("{tag}.post{distance}") + + +def _get_ep(group: str, name: str) -> Any | None: + from ._entrypoints import iter_entry_points + + for ep in iter_entry_points(group, name): + trace("ep found:", ep.name) + return ep.load() + else: + return None + + +def _iter_version_schemes( + entrypoint: str, + scheme_value: str + | list[str] + | tuple[str, ...] + | Callable[[ScmVersion], str] + | None, + _memo: set[object] | None = None, +) -> Iterator[Callable[[ScmVersion], str]]: + if _memo is None: + _memo = set() + if isinstance(scheme_value, str): + scheme_value = cast( + 'str|List[str]|Tuple[str, ...]|Callable[["ScmVersion"], str]|None', + _get_ep(entrypoint, scheme_value), + ) + + if isinstance(scheme_value, (list, tuple)): + for variant in scheme_value: + if variant not in _memo: + _memo.add(variant) + yield from _iter_version_schemes(entrypoint, variant, _memo=_memo) + elif callable(scheme_value): + yield scheme_value + + +@overload +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: str +) -> str: + ... + + +@overload +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: None +) -> str | None: + ... + + +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: str | None +) -> str | None: + for scheme in _iter_version_schemes(entypoint, given_value): + result = scheme(version) + if result is not None: + return result + return default + + +def format_version(version: ScmVersion, **config: Any) -> str: + trace("scm version", version) + trace("config", config) + if version.preformatted: + assert isinstance(version.tag, str) + return version.tag + main_version = _call_version_scheme( + version, "setuptools_scm.version_scheme", config["version_scheme"], None + ) + trace("version", main_version) + assert main_version is not None + local_version = _call_version_scheme( + version, "setuptools_scm.local_scheme", config["local_scheme"], "+unknown" + ) + trace("local_version", local_version) + return main_version + local_version diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/LICENSE b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..89de354 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/LICENSE @@ -0,0 +1,17 @@ +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. diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/PKG-INFO b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..c269f3f --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,695 @@ +Metadata-Version: 2.1 +Name: setuptools-scm +Version: 7.0.5 +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 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Version Control +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: packaging (>=20.0) +Requires-Dist: setuptools +Requires-Dist: tomli (>=1.0.0) +Requires-Dist: typing-extensions +Requires-Dist: importlib-metadata ; python_version < "3.8" +Provides-Extra: test +Requires-Dist: pytest (>=6.2) ; extra == 'test' +Requires-Dist: virtualenv (>20) ; extra == 'test' +Provides-Extra: toml +Requires-Dist: setuptools (>=42) ; extra == 'toml' + +setuptools_scm +============== + +``setuptools_scm`` extract Python package versions from ``git`` or +``hg`` metadata instead of declaring them as the version argument +or in a SCM managed file. + +Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM +(i.e. it automatically adds all of the SCM-managed files to the sdist). +Unwanted files must be excluded by discarding them via ``MANIFEST.in``. + +``setuptools_scm`` support the following scm out of the box: + +* git +* mercurial + + + +.. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg + :target: https://github.com/pypa/setuptools_scm/actions + +.. image:: https://tidelift.com/badges/package/pypi/setuptools-scm + :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme + + +``pyproject.toml`` usage +------------------------ + +The preferred way to configure ``setuptools_scm`` is to author +settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. + +This feature requires Setuptools 42 or later, released in Nov, 2019. +If your project needs to support build from sdist on older versions +of Setuptools, you will need to also implement the ``setup.py usage`` +for those legacy environments. + +First, ensure that ``setuptools_scm`` is present during the project's +built step by specifying it as one of the build requirements. + +.. code:: toml + + # pyproject.toml + [build-system] + requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] + + +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" + + +If you need to confirm which version string is being generated +or debug the configuration, you can install +`setuptools-scm `_ +directly in your working environment and run: + +.. code-block:: shell + + $ python -m setuptools_scm + + # To explore other options, try: + $ python -m setuptools_scm --help + + +``setup.py`` usage (deprecated) +------------------------------- + +.. warning:: + + ``setup_requires`` has been deprecated in favor of ``pyproject.toml`` + +The following settings are considered legacy behavior and +superseded by the ``pyproject.toml`` usage, but for maximal +compatibility, projects may also supply the configuration in +this older form. + +To use ``setuptools_scm`` just modify your project's ``setup.py`` file +like this: + +* Add ``setuptools_scm`` to the ``setup_requires`` parameter. +* Add the ``use_scm_version`` parameter and set it to ``True``. + +For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version=True, + setup_requires=['setuptools_scm'], + ..., + ) + +Arguments to ``get_version()`` (see below) may be passed as a dictionary to +``use_scm_version``. For example: + +.. code:: python + + from setuptools import setup + setup( + ..., + use_scm_version = { + "root": "..", + "relative_to": __file__, + "local_scheme": "node-and-timestamp" + }, + setup_requires=['setuptools_scm'], + ..., + ) + +You can confirm the version number locally via ``setup.py``: + +.. code-block:: shell + + $ python setup.py --version + +.. note:: + + If you see unusual version numbers for packages but ``python setup.py + --version`` reports the expected version number, ensure ``[egg_info]`` is + not defined in ``setup.cfg``. + + +``setup.cfg`` usage (deprecated) +------------------------------------ + +as ``setup_requires`` is deprecated in favour of ``pyproject.toml`` +usage in ``setup.cfg`` is considered deprecated, +please use ``pyproject.toml`` whenever possible. + +Programmatic usage +------------------ + +In order to use ``setuptools_scm`` from code that is one directory deeper +than the project's root, you can use: + +.. code:: python + + from setuptools_scm import get_version + version = get_version(root='..', relative_to=__file__) + +See `setup.py Usage (deprecated)`_ above for how to use this within ``setup.py``. + + +Retrieving package version at runtime +------------------------------------- + +If you have opted not to hardcode the version number inside the package, +you can retrieve it at runtime from PEP-0566_ metadata using +``importlib.metadata`` from the standard library (added in Python 3.8) +or the `importlib_metadata`_ backport: + +.. code:: python + + from importlib.metadata import version, PackageNotFoundError + + try: + __version__ = version("package-name") + except PackageNotFoundError: + # package is not installed + pass + +Alternatively, you can use ``pkg_resources`` which is included in +``setuptools`` (but has a significant runtime cost): + +.. code:: python + + from pkg_resources import get_distribution, DistributionNotFound + + try: + __version__ = get_distribution("package-name").version + except DistributionNotFound: + # package is not installed + pass + +However, this does place a runtime dependency on ``setuptools`` and can add up to +a few 100ms overhead for the package import time. + +.. _PEP-0566: https://www.python.org/dev/peps/pep-0566/ +.. _importlib_metadata: https://pypi.org/project/importlib-metadata/ + + +Usage from Sphinx +----------------- + +It is discouraged to use ``setuptools_scm`` from Sphinx itself, +instead use ``importlib.metadata`` after editable/real installation: + +.. code:: python + + # contents of docs/conf.py + from importlib.metadata import version + release = version('myproject') + # for example take major/minor + version = '.'.join(release.split('.')[:2]) + +The underlying reason is, that services like *Read the Docs* sometimes change +the working directory for good reasons and using the installed metadata +prevents using needless volatile data there. + +Usage from Docker +----------------- + +By default, docker will not copy the ``.git`` folder into your container. +Therefore, builds with version inference might fail. +Consequently, you can use the following snipped to infer the version from +the host os without copying the entire ``.git`` folder to your Dockerfile. + +.. code:: dockerfile + + RUN --mount=source=.git,target=.git,type=bind \ + pip install --no-cache-dir -e . + +However, this build step introduces a dependency to the state of your local +.git folder the build cache and triggers the long-running pip install process on every build. +To optimize build caching, one can use an environment variable to pretend a pseudo +version that is used to cache the results of the pip install process: + +.. code:: dockerfile + + FROM python + COPY pyproject.toml + ARG PSEUDO_VERSION=1 + RUN SETUPTOOLS_SCM_PRETEND_VERSION=${PSEUDO_VERSION} pip install -e .[test] + RUN --mount=source=.git,target=.git,type=bind pip install -e . + +Note that running this Dockerfile requires docker with BuildKit enabled +`[docs] `_. + +To avoid BuildKit and mounting of the .git folder altogether, one can also pass the desired +version as a build argument. Note that ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}`` +is preferred over ``SETUPTOOLS_SCM_PRETEND_VERSION``. + +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. ``.git_archival.txt`` files (git archives, see subsection below) +4. ``PKG-INFO`` + +Git archives +~~~~~~~~~~~~ + +Git archives are supported, but a few changes to your repository are required. + +Create a ``.git_archival.txt`` file in the root directory of your repository, +and copy-paste this into it:: + + node: $Format:%H$ + node-date: $Format:%cI$ + describe-name: $Format:%(describe:tags=true)$ + ref-names: $Format:%D$ + +Create the ``.gitattributes`` file in the root directory of your repository +if it doesn't already exist, and copy-paste this into it:: + + .git_archival.txt export-subst + +Finally, don't forget to commit those two files:: + + git add .git_archival.txt .gitattributes && git commit + + +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 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 `_). + +: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 `_). + +:normalize: + A boolean flag indicating if the version string should be normalized. + Defaults to ``True``. Setting this to ``False`` is equivalent to setting + ``version_cls`` to ``setuptools_scm.version.NonNormalizedVersion`` + +:version_cls: + An optional class used to parse, verify and possibly normalize the version + string. Its constructor should receive a single string argument, and its + ``str`` should return the normalized version string to use. + This option can also receive a class qualified name as a string. + + This defaults to ``packaging.version.Version`` if available. If + ``packaging`` is not installed, ``pkg_resources.packaging.version.Version`` + is used. Note that it is known to modify git release candidate schemes. + + The ``setuptools_scm.NonNormalizedVersion`` convenience class is + provided to disable the normalization step done by + ``packaging.version.Version``. If this is used while ``setuptools_scm`` + is integrated in a setuptools packaging process, the non-normalized + version number will appear in all files (see ``write_to``) BUT note + that setuptools will still normalize it to create the final distribution, + so as to stay compliant with the python packaging standards. + + +To use ``setuptools_scm`` in other Python code you can use the ``get_version`` +function: + +.. code:: python + + from setuptools_scm import get_version + my_version = get_version() + +It optionally accepts the keys of the ``use_scm_version`` parameter as +keyword arguments. + +Example configuration in ``setup.py`` format: + +.. code:: python + + from setuptools import setup + + setup( + use_scm_version={ + 'write_to': '_version.py', + 'write_to_template': '__version__ = "{version}"', + 'tag_regex': r'^(?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_PRETEND_VERSION_FOR_${UPPERCASED_DIST_NAME}: + when defined and not empty, + its used as the primary source for the version number + in which case it will be a unparsed string + + it takes precedence over ``SETUPTOOLS_SCM_PRETEND_VERSION`` + + +:SETUPTOOLS_SCM_DEBUG: + when defined and not empty, + a lot of debug information will be printed as part of ``setuptools_scm`` + operating + +:SOURCE_DATE_EPOCH: + when defined, used as the timestamp from which the + ``node-and-date`` and ``node-and-timestamp`` local parts are + derived, otherwise the current time is used + (https://reproducible-builds.org/docs/source-date-epoch/) + + +:SETUPTOOLS_SCM_IGNORE_VCS_ROOTS: + when defined, a ``os.pathsep`` separated list + of directory names to ignore for root finding + +Extending setuptools_scm +------------------------ + +``setuptools_scm`` ships with a few ``setuptools`` entrypoints based hooks to +extend its default capabilities. + +Adding a new SCM +~~~~~~~~~~~~~~~~ + +``setuptools_scm`` provides two entrypoints for adding new SCMs: + +``setuptools_scm.parse_scm`` + A function used to parse the metadata of the current workdir + using the name of the control directory/file of your SCM as the + entrypoint's name. E.g. for the built-in entrypoint for git the + entrypoint is named ``.git`` and references ``setuptools_scm.git:parse`` + + The return value MUST be a ``setuptools_scm.version.ScmVersion`` instance + created by the function ``setuptools_scm.version:meta``. + +``setuptools_scm.files_command`` + Either a string containing a shell command that prints all SCM managed + files in its current working directory or a callable, that given a + pathname will return that list. + + Also use then name of your SCM control directory as name of the entrypoint. + +Version number construction +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``setuptools_scm.version_scheme`` + Configures how the version number is constructed given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the version. + + Available implementations: + + :guess-next-dev: Automatically guesses the next development version (default). + Guesses the upcoming release by incrementing the pre-release segment if present, + otherwise by incrementing the micro segment. Then appends :code:`.devN`. + In case the tag ends with ``.dev0`` the version is not bumped + and custom ``.devN`` versions will trigger a error. + :post-release: generates post release versions (adds :code:`.postN`) + :python-simplified-semver: Basic semantic versioning. Guesses the upcoming release + by incrementing the minor segment and setting the micro segment to zero if the + current branch contains the string ``'feature'``, otherwise by incrementing the + micro version. Then appends :code:`.devN`. Not compatible with pre-releases. + :release-branch-semver: Semantic versioning for projects with release branches. The + same as ``guess-next-dev`` (incrementing the pre-release or micro segment) if on + a release branch: a branch whose name (ignoring namespace) parses as a version + that matches the most recent tag up to the minor segment. Otherwise if on a + non-release branch, increments the minor segment and sets the micro segment to + zero, then appends :code:`.devN`. + :no-guess-dev: Does no next version guessing, just adds :code:`.post1.devN` + +``setuptools_scm.local_scheme`` + Configures how the local part of a version is rendered given a + ``setuptools_scm.version.ScmVersion`` instance and should return a string + representing the local version. + Dates and times are in Coordinated Universal Time (UTC), because as part + of the version, they should be location independent. + + Available implementations: + + :node-and-date: adds the node on dev versions and the date on dirty + workdir (default) + :node-and-timestamp: like ``node-and-date`` but with a timestamp of + the form ``{:%Y%m%d%H%M%S}`` instead + :dirty-tag: adds ``+dirty`` if the current workdir has changes + :no-local-version: omits local version, useful e.g. because pypi does + not support it + + +Importing in ``setup.py`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To support usage in ``setup.py`` passing a callable into ``use_scm_version`` +is supported. + +Within that callable, ``setuptools_scm`` is available for import. +The callable must return the configuration. + + +.. code:: python + + # content of setup.py + import setuptools + + def myversion(): + from setuptools_scm.version import get_local_dirty_tag + def clean_scheme(version): + return get_local_dirty_tag(version) if version.dirty else '+clean' + + return {'local_scheme': clean_scheme} + + setup( + ..., + use_scm_version=myversion, + ... + ) + + +Note on testing non-installed versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While the general advice is to test against a installed version, +some environments require a test prior to install, + +.. code:: + + $ python setup.py egg_info + $ PYTHONPATH=$PWD:$PWD/src pytest + + +Interaction with Enterprise Distributions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some enterprise distributions like RHEL7 and others +ship rather old setuptools versions due to various release management details. + +In those case its typically possible to build by using a sdist against ``setuptools_scm<2.0``. +As those old setuptools versions lack sensible types for versions, +modern setuptools_scm is unable to support them sensibly. + +In case the project you need to build can not be patched to either use old setuptools_scm, +its still possible to install a more recent version of setuptools in order to handle the build +and/or install the package by using wheels or eggs. + + + +Code of Conduct +--------------- + +Everyone interacting in the ``setuptools_scm`` project's codebases, issue +trackers, chat rooms, and mailing lists is expected to follow the +`PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/RECORD b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..06ac4be --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/RECORD @@ -0,0 +1,30 @@ +setuptools_scm/__init__.py,sha256=KObyz_rl0Tz_6ApdV3VQ2jiQd9iFtQE5Az3N8nP7TJY,5907 +setuptools_scm/__main__.py,sha256=PUkvI9W-hylvxv5sd1sosqWXEliN3d30x7BIKVEAQaQ,98 +setuptools_scm/_cli.py,sha256=axI6F_QF66Vg0Pfp_7nvktH2gdeTiG_xG4D7XXXUk38,2845 +setuptools_scm/_entrypoints.py,sha256=lLf40uD0nWi4njK3Hs3JYWZbhw9wzGIJzlPpbc3wTiE,2696 +setuptools_scm/_overrides.py,sha256=y75kOjwBGdOqojz0we5NAf_-iMMJB-Tt6pPktwU1pd8,1067 +setuptools_scm/_types.py,sha256=sho6RgWu2eU1uURtJymR_9PZi_wvGaD95Y4SN_1lnOo,845 +setuptools_scm/_version_cls.py,sha256=bmNimKGN_rPTO_HoLbr2KOZh-Zpqa0YD_Cr9KyD7-QU,1575 +setuptools_scm/config.py,sha256=eRxBrtDbfhhiBA7bET5VZDf0qPDGVgrAAEURFtWDCEo,7237 +setuptools_scm/discover.py,sha256=Wl0m0oD54gc_0ObnEPKSuNsV85KF9ZlNKu-LMsrIT8M,1867 +setuptools_scm/file_finder.py,sha256=ktqgzpi-R0wUwf2arHu91zbQQjj5GJs7YJkx_BJtJZg,2830 +setuptools_scm/file_finder_git.py,sha256=PMQyb94Qxwhj0gWLfqwkw2AtmafmnRaD44mYEHb9m-s,3619 +setuptools_scm/file_finder_hg.py,sha256=wzjERKfT0n1cj7eDP_WBfJ9JqgTCh-Wo5HdRBxjqvsA,1573 +setuptools_scm/git.py,sha256=hknMPWPJsYNbyy5VKtbPKPzj5gAsVjrS7RvTGeN1KGU,9277 +setuptools_scm/hacks.py,sha256=wM5s2YBuTj1UmREt0yBa9Pxni66d91qugBMid4kTpDM,1776 +setuptools_scm/hg.py,sha256=2L6CAofHm-cNFFlb5V9VFhKmfVvRwAZnof-f-GLayWk,6128 +setuptools_scm/hg_git.py,sha256=s_mbg6v-jgA9KfkoZjqUiDayAPeQzk8TVoQT9jZ7D6Q,4166 +setuptools_scm/integration.py,sha256=kRFqHeoeUu348lMitQ4utVel9fqy2zji1Sd4wYy_FAs,3503 +setuptools_scm/scm_workdir.py,sha256=Njay5Ob7d1lvAV3MjJN4vfz7VQaT0kEUPj7-nlf4QRY,585 +setuptools_scm/utils.py,sha256=2fvBSG1IZThe12cJsJFOsDTIJGxoy3-8vYJt67GziIw,5117 +setuptools_scm/version.py,sha256=xuvazAc3YpOkjnWyENhecN8hs6y-sNVoI-35KARsWog,17034 +setuptools_scm/_integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools_scm/_integration/pyproject_reading.py,sha256=IYxhCmg4AZmMZ27WUGurOSeCzh8mz-C4G-Q4-kL3zx8,2745 +setuptools_scm/_integration/setuptools.py,sha256=z9e79MbpGtyq2lyo16Xw_i4K8mXYIgswn1hHoQJPXJo,512 +setuptools_scm-7.0.5.dist-info/LICENSE,sha256=iYB6zyMJvShfAzQE7nhYFgLzzZuBmhasLw5fYP9KRz4,1023 +setuptools_scm-7.0.5.dist-info/METADATA,sha256=FcXo2ApIgDwY5yzSdFMtYIuAJty8GSr8SQ_hwonsb_E,24354 +setuptools_scm-7.0.5.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +setuptools_scm-7.0.5.dist-info/entry_points.txt,sha256=vIbd4U1RxejAXqD5avwK9y3n9rLcwy3MFsQAVd08Nmc,1548 +setuptools_scm-7.0.5.dist-info/top_level.txt,sha256=kiu-91q3_rJLUoc2wl8_lC4cIlpgtgdD_4NaChF4hOA,15 +setuptools_scm-7.0.5.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +setuptools_scm-7.0.5.dist-info/RECORD,, diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/WHEEL b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/entry_points.txt b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/entry_points.txt new file mode 100644 index 0000000..b5dda72 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,38 @@ +[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] +.git_archival.txt = setuptools_scm.git:parse_archival +.hg_archival.txt = setuptools_scm.hg:parse_archival +PKG-INFO = setuptools_scm.hacks:parse_pkginfo +pip-egg-info = setuptools_scm.hacks:parse_pip_egg_info +pyproject.toml = setuptools_scm.hacks:fallback_version +setup.py = setuptools_scm.hacks:fallback_version + +[setuptools_scm.version_scheme] +calver-by-date = setuptools_scm.version:calver_by_date +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 diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/requires.txt b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/requires.txt new file mode 100644 index 0000000..cd5d948 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/requires.txt @@ -0,0 +1,11 @@ +packaging>=20.0 +setuptools +tomli>=1.0.0 +typing-extensions + +[test] +pytest>=6.2 +virtualenv>20 + +[toml] +setuptools>=42 diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/top_level.txt b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000..cba8d88 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +setuptools_scm diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/zip-safe b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/EGG-INFO/zip-safe @@ -0,0 +1 @@ + diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__init__.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__init__.py new file mode 100644 index 0000000..e614466 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__init__.py @@ -0,0 +1,192 @@ +""" +:copyright: 2010-2015 by Ronny Pfannschmidt +:license: MIT +""" +from __future__ import annotations + +import os +import warnings +from typing import Any +from typing import Callable +from typing import TYPE_CHECKING + +from ._entrypoints import _call_entrypoint_fn +from ._entrypoints import _version_from_entrypoints +from ._overrides import _read_pretended_version_for +from ._overrides import PRETEND_KEY +from ._overrides import PRETEND_KEY_NAMED +from ._version_cls import _version_as_tuple +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version +from .config import Configuration +from .config import DEFAULT_LOCAL_SCHEME +from .config import DEFAULT_TAG_REGEX +from .config import DEFAULT_VERSION_SCHEME +from .discover import iter_matching_entrypoints +from .utils import function_has_arg +from .utils import trace +from .version import format_version +from .version import meta +from .version import ScmVersion + +if TYPE_CHECKING: + from typing import NoReturn + + from . import _types as _t + +TEMPLATES = { + ".py": """\ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = {version!r} +__version_tuple__ = version_tuple = {version_tuple!r} +""", + ".txt": "{version}", +} + + +def version_from_scm(root: _t.PathT) -> ScmVersion | None: + warnings.warn( + "version_from_scm is deprecated please use get_version", + category=DeprecationWarning, + stacklevel=2, + ) + config = Configuration(root=root) + return _version_from_entrypoints(config) + + +def dump_version( + root: _t.PathT, + version: str, + write_to: _t.PathT, + template: str | None = None, +) -> None: + assert isinstance(version, str) + 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 = _version_as_tuple(version) + + with open(target, "w") as fp: + fp.write(template.format(version=version, version_tuple=version_tuple)) + + +def _do_parse(config: Configuration) -> ScmVersion | None: + pretended = _read_pretended_version_for(config) + if pretended is not None: + return pretended + + if config.parse: + parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) + if isinstance(parse_result, str): + raise TypeError( + f"version parse result was {str!r}\nplease return a parsed version" + ) + version: ScmVersion | None + if parse_result: + assert isinstance(parse_result, ScmVersion) + version = parse_result + else: + version = _version_from_entrypoints(config, fallback=True) + else: + # include fallbacks after dropping them from the main entrypoint + version = _version_from_entrypoints(config) or _version_from_entrypoints( + config, fallback=True + ) + + return version + + +def _version_missing(config: Configuration) -> NoReturn: + raise LookupError( + f"setuptools-scm was unable to detect version for {config.absolute_root}.\n\n" + "Make sure you're either building from a fully intact git repository " + "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a " + "git checkout without the .git folder) don't contain the necessary " + "metadata and will not work.\n\n" + "For example, if you're using pip, instead of " + "https://github.com/user/proj/archive/master.zip " + "use git+https://github.com/user/proj.git#egg=proj" + ) + + +def get_version( + root: str = ".", + version_scheme: Callable[[ScmVersion], str] | str = DEFAULT_VERSION_SCHEME, + local_scheme: Callable[[ScmVersion], str] | str = DEFAULT_LOCAL_SCHEME, + write_to: _t.PathT | None = None, + write_to_template: str | None = None, + relative_to: str | None = None, + tag_regex: str = DEFAULT_TAG_REGEX, + parentdir_prefix_version: str | None = None, + fallback_version: str | None = None, + fallback_root: _t.PathT = ".", + parse: Any | None = None, + git_describe_command: Any | None = None, + dist_name: str | None = None, + version_cls: Any | None = None, + normalize: bool = True, + search_parent_directories: bool = False, +) -> str: + """ + 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()) + maybe_version = _get_version(config) + if maybe_version is None: + _version_missing(config) + return maybe_version + + +def _get_version(config: Configuration) -> str | None: + parsed_version = _do_parse(config) + if parsed_version is None: + return None + version_string = format_version( + parsed_version, + version_scheme=config.version_scheme, + local_scheme=config.local_scheme, + ) + if config.write_to is not None: + dump_version( + root=config.root, + version=version_string, + write_to=config.write_to, + template=config.write_to_template, + ) + + return version_string + + +# Public API +__all__ = [ + "get_version", + "dump_version", + "version_from_scm", + "Configuration", + "DEFAULT_VERSION_SCHEME", + "DEFAULT_LOCAL_SCHEME", + "DEFAULT_TAG_REGEX", + "PRETEND_KEY", + "PRETEND_KEY_NAMED", + "Version", + "NonNormalizedVersion", + # TODO: are the symbols below part of public API ? + "function_has_arg", + "trace", + "format_version", + "meta", + "iter_matching_entrypoints", +] diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__main__.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__main__.py new file mode 100644 index 0000000..dab6068 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__main__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from ._cli import main + +if __name__ == "__main__": + main() diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/__init__.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..3aa8f1f Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/__init__.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_entrypoints.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_entrypoints.cpython-39.pyc new file mode 100644 index 0000000..2c42511 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_entrypoints.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_overrides.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_overrides.cpython-39.pyc new file mode 100644 index 0000000..bdc6b28 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_overrides.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_version_cls.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_version_cls.cpython-39.pyc new file mode 100644 index 0000000..63d2c5f Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/_version_cls.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/config.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/config.cpython-39.pyc new file mode 100644 index 0000000..42b8f99 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/config.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/discover.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/discover.cpython-39.pyc new file mode 100644 index 0000000..22b26d6 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/discover.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/file_finder.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/file_finder.cpython-39.pyc new file mode 100644 index 0000000..e2e62cc Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/file_finder.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/file_finder_git.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/file_finder_git.cpython-39.pyc new file mode 100644 index 0000000..0fa6315 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/file_finder_git.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/git.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/git.cpython-39.pyc new file mode 100644 index 0000000..c0d9a94 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/git.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/integration.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/integration.cpython-39.pyc new file mode 100644 index 0000000..84fce48 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/integration.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/scm_workdir.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/scm_workdir.cpython-39.pyc new file mode 100644 index 0000000..97851bd Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/scm_workdir.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/utils.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000..269a415 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/utils.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/version.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/version.cpython-39.pyc new file mode 100644 index 0000000..0c5a6e7 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/__pycache__/version.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_cli.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_cli.py new file mode 100644 index 0000000..8e01f24 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_cli.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import argparse +import os +import sys + +from setuptools_scm import _get_version +from setuptools_scm.config import Configuration +from setuptools_scm.discover import walk_potential_roots +from setuptools_scm.integration import find_files + + +def main(args: list[str] | None = None) -> None: + opts = _get_cli_opts(args) + inferred_root: str = opts.root or "." + + pyproject = opts.config or _find_pyproject(inferred_root) + + try: + + config = Configuration.from_file( + pyproject, + root=(os.path.abspath(opts.root) if opts.root is not None else None), + ) + except (LookupError, FileNotFoundError) as ex: + # no pyproject.toml OR no [tool.setuptools_scm] + print( + f"Warning: could not use {os.path.relpath(pyproject)}," + " using default configuration.\n" + f" Reason: {ex}.", + file=sys.stderr, + ) + config = Configuration(inferred_root) + + version = _get_version(config) + if version is None: + raise SystemExit("ERROR: no version found for", opts) + if opts.strip_dev: + version = version.partition(".dev")[0] + print(version) + + if opts.command == "ls": + for fname in find_files(config.root): + print(fname) + + +def _get_cli_opts(args: list[str] | None) -> argparse.Namespace: + prog = "python -m setuptools_scm" + desc = "Print project version according to SCM metadata" + parser = argparse.ArgumentParser(prog, description=desc) + # By default, help for `--help` starts with lower case, so we keep the pattern: + parser.add_argument( + "-r", + "--root", + default=None, + help='directory managed by the SCM, default: inferred from config file, or "."', + ) + parser.add_argument( + "-c", + "--config", + default=None, + metavar="PATH", + help="path to 'pyproject.toml' with setuptools_scm config, " + "default: looked up in the current or parent directories", + ) + parser.add_argument( + "--strip-dev", + action="store_true", + help="remove the dev/local parts of the version before printing the version", + ) + sub = parser.add_subparsers(title="extra commands", dest="command", metavar="") + # We avoid `metavar` to prevent printing repetitive information + desc = "List files managed by the SCM" + sub.add_parser("ls", help=desc[0].lower() + desc[1:], description=desc) + return parser.parse_args(args) + + +def _find_pyproject(parent: str) -> str: + for directory in walk_potential_roots(os.path.abspath(parent)): + pyproject = os.path.join(directory, "pyproject.toml") + if os.path.isfile(pyproject): + return pyproject + + return os.path.abspath( + "pyproject.toml" + ) # use default name to trigger the default errors diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_entrypoints.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_entrypoints.py new file mode 100644 index 0000000..2efb9f8 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_entrypoints.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +import warnings +from typing import Any +from typing import Iterator +from typing import overload +from typing import TYPE_CHECKING + +from .utils import function_has_arg +from .utils import trace +from .version import ScmVersion + +if TYPE_CHECKING: + from .config import Configuration + from typing_extensions import Protocol + from . import _types as _t +else: + Configuration = Any + + class Protocol: + pass + + +class MaybeConfigFunction(Protocol): + __name__: str + + @overload + def __call__(self, root: _t.PathT, config: Configuration) -> ScmVersion | None: + pass + + @overload + def __call__(self, root: _t.PathT) -> ScmVersion | None: + pass + + +def _call_entrypoint_fn( + root: _t.PathT, config: Configuration, fn: MaybeConfigFunction +) -> ScmVersion | None: + if function_has_arg(fn, "config"): + return fn(root, config=config) + else: + warnings.warn( + f"parse function {fn.__module__}.{fn.__name__}" + " are required to provide a named argument" + " 'config', setuptools_scm>=8.0 will remove support.", + category=DeprecationWarning, + stacklevel=2, + ) + return fn(root) + + +def _version_from_entrypoints( + config: Configuration, fallback: bool = False +) -> ScmVersion | None: + if fallback: + entrypoint = "setuptools_scm.parse_scm_fallback" + root = config.fallback_root + else: + entrypoint = "setuptools_scm.parse_scm" + root = config.absolute_root + + from .discover import iter_matching_entrypoints + + trace("version_from_ep", entrypoint, root) + for ep in iter_matching_entrypoints(root, entrypoint, config): + version: ScmVersion | None = _call_entrypoint_fn(root, config, ep.load()) + trace(ep, version) + if version: + return version + return None + + +try: + from importlib.metadata import entry_points # type: ignore +except ImportError: + try: + from importlib_metadata import entry_points + except ImportError: + from collections import defaultdict + + def entry_points() -> dict[str, list[_t.EntrypointProtocol]]: + warnings.warn( + "importlib metadata missing, " + "this may happen at build time for python3.7" + ) + return defaultdict(list) + + +def iter_entry_points( + group: str, name: str | None = None +) -> Iterator[_t.EntrypointProtocol]: + all_eps = entry_points() + if hasattr(all_eps, "select"): + eps = all_eps.select(group=group) + else: + eps = all_eps[group] + if name is None: + return iter(eps) + return (ep for ep in eps if ep.name == name) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__init__.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/__init__.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..3d515a6 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/__init__.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/pyproject_reading.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/pyproject_reading.cpython-39.pyc new file mode 100644 index 0000000..c620651 Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/pyproject_reading.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/setuptools.cpython-39.pyc b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/setuptools.cpython-39.pyc new file mode 100644 index 0000000..6d571fa Binary files /dev/null and b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/__pycache__/setuptools.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/pyproject_reading.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/pyproject_reading.py new file mode 100644 index 0000000..f43e6b1 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/pyproject_reading.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +import warnings +from typing import Any +from typing import Callable +from typing import Dict +from typing import NamedTuple +from typing import TYPE_CHECKING + +from .setuptools import read_dist_name_from_setup_cfg + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +_ROOT = "root" +TOML_RESULT: TypeAlias = Dict[str, Any] +TOML_LOADER: TypeAlias = Callable[[str], TOML_RESULT] + + +class PyProjectData(NamedTuple): + name: str + tool_name: str + project: TOML_RESULT + section: TOML_RESULT + + @property + def project_name(self) -> str | None: + return self.project.get("name") + + +def lazy_tomli_load(data: str) -> TOML_RESULT: + from tomli import loads + + return loads(data) + + +def read_pyproject( + name: str = "pyproject.toml", + tool_name: str = "setuptools_scm", + _load_toml: TOML_LOADER | None = None, +) -> PyProjectData: + if _load_toml is None: + _load_toml = lazy_tomli_load + with open(name, encoding="UTF-8") as strm: + data = strm.read() + defn = _load_toml(data) + try: + section = defn.get("tool", {})[tool_name] + except LookupError as e: + raise LookupError(f"{name} does not contain a tool.{tool_name} section") from e + project = defn.get("project", {}) + return PyProjectData(name, tool_name, project, section) + + +def get_args_for_pyproject( + pyproject: PyProjectData, + dist_name: str | None, + kwargs: TOML_RESULT, +) -> TOML_RESULT: + """drops problematic details and figures the distribution name""" + section = pyproject.section.copy() + kwargs = kwargs.copy() + if "relative_to" in section: + relative = section.pop("relative_to") + warnings.warn( + f"{pyproject.name}: at [tool.{pyproject.tool_name}]\n" + f"ignoring value relative_to={relative!r}" + " as its always relative to the config file" + ) + if "dist_name" in section: + if dist_name is None: + dist_name = section.pop("dist_name") + else: + assert dist_name == section["dist_name"] + del section["dist_name"] + if dist_name is None: + # minimal pep 621 support for figuring the pretend keys + dist_name = pyproject.project_name + if dist_name is None: + dist_name = read_dist_name_from_setup_cfg() + if _ROOT in kwargs: + if kwargs[_ROOT] is None: + kwargs.pop(_ROOT, None) + elif _ROOT in section: + if section[_ROOT] != kwargs[_ROOT]: + warnings.warn( + f"root {section[_ROOT]} is overridden" + f" by the cli arg {kwargs[_ROOT]}" + ) + section.pop("root", None) + return {"dist_name": dist_name, **section, **kwargs} diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/setuptools.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/setuptools.py new file mode 100644 index 0000000..5a0cc3a --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_integration/setuptools.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +import os +from typing import IO + + +def read_dist_name_from_setup_cfg( + input: str | os.PathLike[str] | IO[str] = "setup.cfg", +) -> str | None: + + # minimal effort to read dist_name off setup.cfg metadata + import configparser + + parser = configparser.ConfigParser() + + if isinstance(input, (os.PathLike, str)): + parser.read([input]) + else: + parser.read_file(input) + + dist_name = parser.get("metadata", "name", fallback=None) + return dist_name diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_overrides.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_overrides.py new file mode 100644 index 0000000..f18b82c --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_overrides.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import os + +from .config import Configuration +from .utils import trace +from .version import meta +from .version import ScmVersion + + +PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" +PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" + + +def _read_pretended_version_for(config: Configuration) -> ScmVersion | None: + """read a a overridden version from the environment + + tries ``SETUPTOOLS_SCM_PRETEND_VERSION`` + and ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_$UPPERCASE_DIST_NAME`` + """ + trace("dist name:", config.dist_name) + pretended: str | None + 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) + else: + return None diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_types.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_types.py new file mode 100644 index 0000000..6c6bdf8 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_types.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import List +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + + +if TYPE_CHECKING: + from setuptools_scm import version + import os + +from typing_extensions import ParamSpec, TypeAlias, Protocol + +PathT = Union["os.PathLike[str]", str] + +CMD_TYPE: TypeAlias = Union[List[str], str] + +VERSION_SCHEME = Union[str, Callable[["version.ScmVersion"], str]] + + +class EntrypointProtocol(Protocol): + name: str + + def load(self) -> Any: + pass + + +T = TypeVar("T") +T2 = TypeVar("T2") +P = ParamSpec("P") + + +def transfer_input_args( + template: Callable[P, T], +) -> Callable[[Callable[..., T]], Callable[P, T]]: + def decorate(func: Callable[..., T2]) -> Callable[P, T2]: + return func + + return decorate diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_version_cls.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_version_cls.py new file mode 100644 index 0000000..39e66b2 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/_version_cls.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from logging import getLogger + +from packaging.version import InvalidVersion +from packaging.version import Version as Version + + +class NonNormalizedVersion(Version): + """A non-normalizing version handler. + + You can use this class to preserve version verification but skip normalization. + For example you can use this to avoid git release candidate version tags + ("1.0.0-rc1") to be normalized to "1.0.0rc1". Only use this if you fully + trust the version tags. + """ + + def __init__(self, version: str) -> None: + # parse and validate using parent + super().__init__(version) + + # store raw for str + self._raw_version = version + + def __str__(self) -> str: + # return the non-normalized version (parent returns the normalized) + return self._raw_version + + def __repr__(self) -> str: + # same pattern as parent + return f"" + + +def _version_as_tuple(version_str: str) -> tuple[int | str, ...]: + try: + parsed_version = Version(version_str) + except InvalidVersion: + + log = getLogger("setuptools_scm") + log.exception("failed to parse version %s", version_str) + return (version_str,) + else: + version_fields: tuple[int | str, ...] = parsed_version.release + if parsed_version.dev is not None: + version_fields += (f"dev{parsed_version.dev}",) + if parsed_version.local is not None: + version_fields += (parsed_version.local,) + return version_fields diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/config.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/config.py new file mode 100644 index 0000000..3bf250a --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/config.py @@ -0,0 +1,216 @@ +""" configuration """ +from __future__ import annotations + +import os +import re +import warnings +from typing import Any +from typing import Callable +from typing import cast +from typing import Pattern +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +from ._integration.pyproject_reading import ( + get_args_for_pyproject as _get_args_for_pyproject, +) +from ._integration.pyproject_reading import read_pyproject as _read_pyproject +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version +from .utils import trace + + +if TYPE_CHECKING: + from . import _types as _t + from setuptools_scm.version import ScmVersion + +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: str | Pattern[str] | None) -> Pattern[str]: + 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: _t.PathT, relative_to: _t.PathT | None) -> str: + trace("abs root", repr(locals())) + if relative_to: + if ( + os.path.isabs(root) + and os.path.isabs(relative_to) + and not os.path.commonpath([root, relative_to]) == root + ): + 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) + + +_VersionT = Union[Version, NonNormalizedVersion] + + +def _validate_version_cls( + version_cls: type[_VersionT] | str | None, normalize: bool +) -> type[_VersionT]: + if not normalize: + # `normalize = False` means `version_cls = NonNormalizedVersion` + if version_cls is not None: + raise ValueError( + "Providing a custom `version_cls` is not permitted when " + "`normalize=False`" + ) + return NonNormalizedVersion + else: + # Use `version_cls` if provided, default to packaging or pkg_resources + if version_cls is None: + return Version + elif isinstance(version_cls, str): + try: + # Not sure this will work in old python + import importlib + + pkg, cls_name = version_cls.rsplit(".", 1) + version_cls_host = importlib.import_module(pkg) + return cast(Type[_VersionT], getattr(version_cls_host, cls_name)) + except: # noqa + raise ValueError(f"Unable to import version_cls='{version_cls}'") + else: + return version_cls + + +class Configuration: + """Global configuration model""" + + parent: _t.PathT | None + _root: str + _relative_to: str | None + version_cls: type[_VersionT] + + def __init__( + self, + relative_to: _t.PathT | None = None, + root: _t.PathT = ".", + version_scheme: ( + str | Callable[[ScmVersion], str | None] + ) = DEFAULT_VERSION_SCHEME, + local_scheme: (str | Callable[[ScmVersion], str | None]) = DEFAULT_LOCAL_SCHEME, + write_to: _t.PathT | None = None, + write_to_template: str | None = None, + tag_regex: str | Pattern[str] = DEFAULT_TAG_REGEX, + parentdir_prefix_version: str | None = None, + fallback_version: str | None = None, + fallback_root: _t.PathT = ".", + parse: Any | None = None, + git_describe_command: _t.CMD_TYPE | None = None, + dist_name: str | None = None, + version_cls: type[_VersionT] | type | str | None = None, + normalize: bool = True, + search_parent_directories: bool = False, + ): + # TODO: + self._relative_to = None if relative_to is None else os.fspath(relative_to) + self._root = "." + + self.root = os.fspath(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 # type: ignore + self.parse = parse + self.tag_regex = tag_regex # type: ignore + self.git_describe_command = git_describe_command + self.dist_name = dist_name + self.search_parent_directories = search_parent_directories + self.parent = None + + self.version_cls = _validate_version_cls(version_cls, normalize) + + @property + def fallback_root(self) -> str: + return self._fallback_root + + @fallback_root.setter + def fallback_root(self, value: _t.PathT) -> None: + self._fallback_root = os.path.abspath(value) + + @property + def absolute_root(self) -> str: + return self._absolute_root + + @property + def relative_to(self) -> str | None: + return self._relative_to + + @relative_to.setter + def relative_to(self, value: _t.PathT) -> None: + self._absolute_root = _check_absolute_root(self._root, value) + self._relative_to = os.fspath(value) + trace("root", repr(self._absolute_root)) + trace("relative_to", repr(value)) + + @property + def root(self) -> str: + return self._root + + @root.setter + def root(self, value: _t.PathT) -> None: + self._absolute_root = _check_absolute_root(value, self._relative_to) + self._root = os.fspath(value) + trace("root", repr(self._absolute_root)) + trace("relative_to", repr(self._relative_to)) + + @property + def tag_regex(self) -> Pattern[str]: + return self._tag_regex + + @tag_regex.setter + def tag_regex(self, value: str | Pattern[str]) -> None: + self._tag_regex = _check_tag_regex(value) + + @classmethod + def from_file( + cls, + name: str = "pyproject.toml", + dist_name: str | None = None, + _load_toml: Callable[[str], dict[str, Any]] | None = None, + **kwargs: Any, + ) -> Configuration: + """ + 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. + """ + + pyproject_data = _read_pyproject(name, _load_toml=_load_toml) + args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) + + return cls(relative_to=name, **args) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/discover.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/discover.py new file mode 100644 index 0000000..f7843ee --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/discover.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import os +from typing import Iterable +from typing import Iterator +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from . import _types as _t +from .config import Configuration +from .utils import trace + + +def walk_potential_roots( + root: _t.PathT, search_parents: bool = True +) -> Iterator[_t.PathT]: + """ + Iterate though a path and each of its parents. + :param root: File path. + :param search_parents: If ``False`` the parents are not considered. + """ + + if not search_parents: + yield root + return + + tail = root + + while tail: + yield root + root, tail = os.path.split(root) + + +def match_entrypoint(root: _t.PathT, name: str) -> bool: + """ + Consider a ``root`` as entry-point. + :param root: File path. + :param name: Subdirectory name. + :return: ``True`` if a subdirectory ``name`` exits in ``root``. + """ + + if os.path.exists(os.path.join(root, name)): + if not os.path.isabs(name): + return True + trace("ignoring bad ep", name) + + return False + + +def iter_matching_entrypoints( + root: _t.PathT, entrypoint: str, config: Configuration +) -> Iterable[_t.EntrypointProtocol]: + """ + Consider different entry-points in ``root`` and optionally its parents. + :param root: File path. + :param entrypoint: Entry-point to consider. + :param config: Configuration, + read ``search_parent_directories``, write found parent to ``parent``. + """ + + trace("looking for ep", entrypoint, root) + from ._entrypoints import iter_entry_points + + for wd in walk_potential_roots(root, config.search_parent_directories): + for ep in iter_entry_points(entrypoint): + if match_entrypoint(wd, ep.name): + trace("found ep", ep, "in", wd) + config.parent = wd + yield ep diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder.py new file mode 100644 index 0000000..99da792 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import os +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import TypeGuard + from . import _types as _t + +from .utils import trace + + +def scm_find_files( + path: _t.PathT, scm_files: set[str], scm_dirs: set[str] +) -> list[str]: + """ 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[str] = set() + res: list[str] = [] + 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: str) -> bool: + 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: str | None) -> TypeGuard[str]: + """ """ + 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 diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder_git.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder_git.py new file mode 100644 index 0000000..a83af21 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder_git.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import logging +import os +import subprocess +import tarfile +from typing import IO +from typing import TYPE_CHECKING + +from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex +from .utils import trace + +if TYPE_CHECKING: + from . import _types as _t + + +log = logging.getLogger(__name__) + + +def _git_toplevel(path: str) -> str | None: + try: + cwd = os.path.abspath(path or ".") + out, err, ret = do_ex(["git", "rev-parse", "HEAD"], cwd=cwd) + if ret != 0: + # BAIL if there is no commit + log.error("listing git files failed - pretending there aren't any") + return None + out, err, ret = do_ex( + ["git", "rev-parse", "--show-prefix"], + cwd=cwd, + ) + if ret != 0: + return None + out = out.strip()[:-1] # remove the trailing pathsep + if not out: + out = cwd + else: + # Here, ``out`` is a relative path to root of git. + # ``cwd`` is absolute path to current working directory. + # the below method removes the length of ``out`` from + # ``cwd``, which gives the git toplevel + assert cwd.replace("\\", "/").endswith(out), f"cwd={cwd!r}\nout={out!r}" + # In windows cwd contains ``\`` which should be replaced by ``/`` + # for this assertion to work. Length of string isn't changed by replace + # ``\\`` is just and escape for `\` + out = cwd[: -len(out)] + 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: IO[bytes], toplevel: str) -> tuple[set[str], set[str]]: + 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: str) -> tuple[set[str], set[str]]: + # 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, stderr=subprocess.DEVNULL + ) + assert proc.stdout is not None + 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.error("listing git files failed - pretending there aren't any") + return set(), set() + + +def git_find_files(path: _t.PathT = "") -> list[str]: + toplevel = _git_toplevel(os.fspath(path)) + if not is_toplevel_acceptable(toplevel): + return [] + assert toplevel is not None # mypy ignores typeguard + 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) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder_hg.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder_hg.py new file mode 100644 index 0000000..4f5e3ec --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/file_finder_hg.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import os +import subprocess + +from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex + + +def _hg_toplevel(path: str) -> str | None: + try: + out: str = subprocess.check_output( + ["hg", "root"], + cwd=(path or "."), + text=True, + stderr=subprocess.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: str) -> tuple[set[str], set[str]]: + hg_files: set[str] = set() + hg_dirs = {toplevel} + out, err, ret = do_ex(["hg", "files"], cwd=toplevel) + if ret: + (), () + for name in out.splitlines(): + name = os.path.normcase(name).replace("/", os.path.sep) + fullname = os.path.join(toplevel, name) + 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: str = "") -> list[str]: + toplevel = _hg_toplevel(path) + if not is_toplevel_acceptable(toplevel): + return [] + assert toplevel is not None + hg_files, hg_dirs = _hg_ls_files_and_dirs(toplevel) + return scm_find_files(path, hg_files, hg_dirs) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/git.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/git.py new file mode 100644 index 0000000..27cb159 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/git.py @@ -0,0 +1,312 @@ +from __future__ import annotations + +import os +import re +import warnings +from datetime import date +from datetime import datetime +from os.path import isfile +from os.path import join +from os.path import samefile +from typing import Callable +from typing import TYPE_CHECKING + +from .config import Configuration +from .scm_workdir import Workdir +from .utils import _CmdResult +from .utils import data_from_mime +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta +from .version import ScmVersion +from .version import tags_to_versions + +if TYPE_CHECKING: + from . import _types as _t + + from setuptools_scm.hg_git import GitWorkdirHgClient + +REF_TAG_RE = re.compile(r"(?<=\btag: )([^,]+)\b") +DESCRIBE_UNSUPPORTED = "%(describe" + +# If testing command in shell make sure to quote the match argument like +# '*[0-9]*' as it will expand before being sent to git if there are any matching +# files in current directory. +DEFAULT_DESCRIBE = [ + "git", + "describe", + "--dirty", + "--tags", + "--long", + "--match", + "*[0-9]*", +] + + +class GitWorkdir(Workdir): + """experimental, may change at any time""" + + COMMAND = "git" + + @classmethod + def from_potential_worktree(cls, wd: _t.PathT) -> GitWorkdir | None: + require_command(cls.COMMAND) + wd = os.path.abspath(wd) + git_dir = join(wd, ".git") + real_wd, _, ret = do_ex( + ["git", "--git-dir", git_dir, "rev-parse", "--show-prefix"], wd + ) + real_wd = real_wd[:-1] # remove the trailing pathsep + if ret: + return None + if not real_wd: + real_wd = wd + else: + assert wd.replace("\\", "/").endswith(real_wd) + # In windows wd contains ``\`` which should be replaced by ``/`` + # for this assertion to work. Length of string isn't changed by replace + # ``\\`` is just and escape for `\` + real_wd = wd[: -len(real_wd)] + trace("real root", real_wd) + if not samefile(real_wd, wd): + return None + + return cls(real_wd) + + def do_ex_git(self, cmd: list[str]) -> _CmdResult: + return self.do_ex(["git", "--git-dir", join(self.path, ".git")] + cmd) + + def is_dirty(self) -> bool: + out, _, _ = self.do_ex_git(["status", "--porcelain", "--untracked-files=no"]) + return bool(out) + + def get_branch(self) -> str | None: + branch, err, ret = self.do_ex_git(["rev-parse", "--abbrev-ref", "HEAD"]) + if ret: + trace("branch err", branch, err, ret) + branch, err, ret = self.do_ex_git(["symbolic-ref", "--short", "HEAD"]) + if ret: + trace("branch err (symbolic-ref)", branch, err, ret) + return None + return branch + + def get_head_date(self) -> date | None: + timestamp, err, ret = self.do_ex_git( + ["-c", "log.showSignature=false", "log", "-n", "1", "HEAD", "--format=%cI"] + ) + if ret: + trace("timestamp err", timestamp, err, ret) + return None + # TODO, when dropping python3.6 use fromiso + date_part = timestamp.split("T")[0] + if "%c" in date_part: + trace("git too old -> timestamp is ", timestamp) + return None + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self) -> bool: + return isfile(join(self.path, ".git/shallow")) + + def fetch_shallow(self) -> None: + self.do_ex_git(["fetch", "--unshallow"]) + + def node(self) -> str | None: + node, _, ret = self.do_ex_git(["rev-parse", "--verify", "--quiet", "HEAD"]) + if not ret: + return node[:7] + else: + return None + + def count_all_nodes(self) -> int: + revs, _, _ = self.do_ex_git(["rev-list", "HEAD"]) + return revs.count("\n") + 1 + + def default_describe(self) -> _CmdResult: + git_dir = join(self.path, ".git") + return self.do_ex( + DEFAULT_DESCRIBE[:1] + ["--git-dir", git_dir] + DEFAULT_DESCRIBE[1:] + ) + + +def warn_on_shallow(wd: GitWorkdir) -> None: + """experimental, may change at any time""" + if wd.is_shallow(): + warnings.warn(f'"{wd.path}" is shallow and may cause errors') + + +def fetch_on_shallow(wd: GitWorkdir) -> None: + """experimental, may change at any time""" + if wd.is_shallow(): + warnings.warn(f'"{wd.path}" was shallow, git fetch was used to rectify') + wd.fetch_shallow() + + +def fail_on_shallow(wd: GitWorkdir) -> None: + """experimental, may change at any time""" + if wd.is_shallow(): + raise ValueError( + f'{wd.path} is shallow, please correct with "git fetch --unshallow"' + ) + + +def get_working_directory(config: Configuration) -> GitWorkdir | None: + """ + Return the working directory (``GitWorkdir``). + """ + + if config.parent: + return GitWorkdir.from_potential_worktree(config.parent) + + if config.search_parent_directories: + return search_parent(config.absolute_root) + + return GitWorkdir.from_potential_worktree(config.absolute_root) + + +def parse( + root: str, + describe_command: str | list[str] | None = None, + pre_parse: Callable[[GitWorkdir], None] = warn_on_shallow, + config: Configuration | None = None, +) -> ScmVersion | None: + """ + :param pre_parse: experimental pre_parse action, may change at any time + """ + if not config: + config = Configuration(root=root) + + wd = get_working_directory(config) + if wd: + return _git_parse_inner( + config, wd, describe_command=describe_command, pre_parse=pre_parse + ) + else: + return None + + +def _git_parse_inner( + config: Configuration, + wd: GitWorkdir | GitWorkdirHgClient, + pre_parse: None | (Callable[[GitWorkdir | GitWorkdirHgClient], None]) = None, + describe_command: _t.CMD_TYPE | None = None, +) -> ScmVersion: + if pre_parse: + pre_parse(wd) + + if config.git_describe_command is not None: + describe_command = config.git_describe_command + + if describe_command is not None: + out, _, ret = wd.do_ex(describe_command) + else: + out, _, ret = wd.default_describe() + distance: int | None + node: str | None + if ret == 0: + tag, distance, node, dirty = _git_parse_describe(out) + if distance == 0 and not dirty: + distance = None + else: + # If 'git git_describe_command' failed, try to get the information otherwise. + tag = "0.0" + node = wd.node() + if node is None: + distance = 0 + else: + distance = wd.count_all_nodes() + node = "g" + node + dirty = wd.is_dirty() + + branch = wd.get_branch() + node_date = wd.get_head_date() or date.today() + + return meta( + tag, + branch=branch, + node=node, + node_date=node_date, + distance=distance, + dirty=dirty, + config=config, + ) + + +def _git_parse_describe(describe_output: str) -> tuple[str, int, str, bool]: + # '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) + return tag, int(number), node, dirty + + +def search_parent(dirname: _t.PathT) -> GitWorkdir | None: + """ + Walk up the path to find the `.git` directory. + :param dirname: Directory from which to start searching. + """ + + # Code based on: + # https://github.com/gitpython-developers/GitPython/blob/main/git/repo/base.py + + curpath = os.path.abspath(dirname) + + while curpath: + + try: + wd = GitWorkdir.from_potential_worktree(curpath) + except Exception: + wd = None + + if wd is not None: + return wd + + curpath, tail = os.path.split(curpath) + + if not tail: + return None + return None + + +def archival_to_version( + data: dict[str, str], config: Configuration | None = None +) -> ScmVersion | None: + node: str | None + trace("data", data) + archival_describe = data.get("describe-name", DESCRIBE_UNSUPPORTED) + if DESCRIBE_UNSUPPORTED in archival_describe: + warnings.warn("git archive did not support describe output") + else: + tag, number, node, _ = _git_parse_describe(archival_describe) + return meta( + tag, + config=config, + distance=None if number == 0 else number, + node=node, + ) + versions = tags_to_versions(REF_TAG_RE.findall(data.get("ref-names", ""))) + if versions: + return meta(versions[0], config=config) + else: + node = data.get("node") + if node is None: + return None + elif "$FORMAT" in node.upper(): + warnings.warn("unexported git archival found") + return None + else: + return meta("0.0", node=node, config=config) + + +def parse_archival( + root: _t.PathT, config: Configuration | None = None +) -> ScmVersion | None: + archival = os.path.join(root, ".git_archival.txt") + data = data_from_mime(archival) + return archival_to_version(data, config=config) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hacks.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hacks.py new file mode 100644 index 0000000..9ca0df9 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hacks.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import os +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from . import _types as _t +from .config import Configuration +from .utils import data_from_mime +from .utils import trace +from .version import meta +from .version import ScmVersion +from .version import tag_to_version + +_UNKNOWN = "UNKNOWN" + + +def parse_pkginfo( + root: _t.PathT, config: Configuration | None = None +) -> ScmVersion | None: + + pkginfo = os.path.join(root, "PKG-INFO") + trace("pkginfo", pkginfo) + data = data_from_mime(pkginfo) + version = data.get("Version", _UNKNOWN) + if version != _UNKNOWN: + return meta(version, preformatted=True, config=config) + else: + return None + + +def parse_pip_egg_info( + root: _t.PathT, config: Configuration | None = None +) -> ScmVersion | None: + pipdir = os.path.join(root, "pip-egg-info") + if not os.path.isdir(pipdir): + return None + items = os.listdir(pipdir) + trace("pip-egg-info", pipdir, items) + if not items: + return None + return parse_pkginfo(os.path.join(pipdir, items[0]), config=config) + + +def fallback_version(root: _t.PathT, config: Configuration) -> ScmVersion | 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: + trace("FALLBACK") + return meta(config.fallback_version, preformatted=True, config=config) + return None diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hg.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hg.py new file mode 100644 index 0000000..d6d8458 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hg.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import datetime +import os +from pathlib import Path +from typing import TYPE_CHECKING + +from ._version_cls import Version +from .config import Configuration +from .scm_workdir import Workdir +from .utils import data_from_mime +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta +from .version import ScmVersion +from .version import tag_to_version + +if TYPE_CHECKING: + from . import _types as _t + + +class HgWorkdir(Workdir): + + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd: _t.PathT) -> HgWorkdir | None: + require_command(cls.COMMAND) + root, err, ret = do_ex("hg root", wd) + if ret: + return None + return cls(root) + + def get_meta(self, config: Configuration) -> ScmVersion | None: + + node: str + tags_str: str + bookmark: str + node_date_str: str + node, tags_str, bookmark, node_date_str = self.hg_log( + ".", "{node}\n{tag}\n{bookmark}\n{date|shortdate}" + ).split("\n") + + # TODO: support bookmarks and topics (but nowadays bookmarks are + # mainly used to emulate Git branches, which is already supported with + # the dedicated class GitWorkdirHgClient) + + branch, dirty_str, dirty_date = self.do( + ["hg", "id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}"] + ).split("\n") + dirty = bool(int(dirty_str)) + # todo: fromiso + node_date = datetime.date( + *map(int, (dirty_date if dirty else node_date_str).split("-")) + ) + + if node.count("0") == len(node): + trace("initial node", self.path) + return meta( + "0.0", config=config, dirty=dirty, branch=branch, node_date=node_date + ) + + node = "h" + node[:7] + + tags = tags_str.split() + if "tip" in tags: + # tip is not a real tag + tags.remove("tip") + + if tags: + tag = tag_to_version(tags[0]) + if tag: + return meta(tag, dirty=dirty, branch=branch, config=config) + + try: + tag_str = self.get_latest_normalizable_tag() + if tag_str is None: + dist = self.get_distance_revs("") + else: + dist = self.get_distance_revs(tag_str) + + if tag_str == "null" or tag_str is None: + tag = Version("0.0") + dist = int(dist) + 1 + else: + tag = tag_to_version(tag_str, config=config) + assert tag is not None + + if self.check_changes_since_tag(tag_str) or dirty: + return meta( + tag, + distance=dist, + node=node, + dirty=dirty, + branch=branch, + config=config, + node_date=node_date, + ) + else: + return meta(tag, config=config, node_date=node_date) + + except ValueError as e: + trace("error", e) + pass # unpacking failed, old hg + + return None + + def hg_log(self, revset: str, template: str) -> str: + cmd = ["hg", "log", "-r", revset, "-T", template] + return self.do(cmd) + + def get_latest_normalizable_tag(self) -> str | None: + # Gets all tags containing a '.' (see #229) from oldest to newest + outlines = self.hg_log( + revset="ancestors(.) and tag('re:\\.')", + template="{tags}{if(tags, '\n', '')}", + ).split() + if not outlines: + return None + tag = outlines[-1].split()[-1] + return tag + + def get_distance_revs(self, rev1: str, rev2: str = ".") -> int: + + revset = f"({rev1}::{rev2})" + out = self.hg_log(revset, ".") + return len(out) - 1 + + def check_changes_since_tag(self, tag: str | None) -> bool: + + if tag == "0.0" or tag is None: + return True + + revset = ( + "(branch(.)" # look for revisions in this branch only + f" and tag({tag!r})::." # after the last tag + # ignore commits that only modify .hgtags and nothing else: + " and (merge() or file('re:^(?!\\.hgtags).*$'))" + f" and not tag({tag!r}))" # ignore the tagged commit itself + ) + + return bool(self.hg_log(revset, ".")) + + +def parse(root: _t.PathT, config: Configuration | None = None) -> ScmVersion | None: + if not config: + config = Configuration(root=root) + + if os.path.exists(os.path.join(root, ".hg/git")): + paths, _, ret = do_ex("hg path", root) + if not ret: + for line in paths.split("\n"): + if line.startswith("default ="): + path = Path(line.split()[2]) + if path.name.endswith(".git") or (path / ".git").exists(): + from .git import _git_parse_inner + from .hg_git import GitWorkdirHgClient + + wd_hggit = GitWorkdirHgClient.from_potential_worktree(root) + if wd_hggit: + return _git_parse_inner(config, wd_hggit) + + wd = HgWorkdir.from_potential_worktree(config.absolute_root) + + if wd is None: + return None + + return wd.get_meta(config) + + +def archival_to_version( + data: dict[str, str], config: Configuration | None = None +) -> ScmVersion: + 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=int(data["latesttagdistance"]), + node=node, + config=config, + ) + else: + return meta("0.0", node=node, config=config) + + +def parse_archival(root: _t.PathT, config: Configuration | None = None) -> ScmVersion: + archival = os.path.join(root, ".hg_archival.txt") + data = data_from_mime(archival) + return archival_to_version(data, config=config) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hg_git.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hg_git.py new file mode 100644 index 0000000..bde71ec --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/hg_git.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +import os +from contextlib import suppress +from datetime import date +from datetime import datetime + +from . import _types as _t +from .git import GitWorkdir +from .hg import HgWorkdir +from .utils import _CmdResult +from .utils import do_ex +from .utils import require_command +from .utils import trace + + +_FAKE_GIT_DESCRIBE_ERROR = _CmdResult("<>hg git failed", "", 1) + + +class GitWorkdirHgClient(GitWorkdir, HgWorkdir): + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd: _t.PathT) -> GitWorkdirHgClient | None: + require_command(cls.COMMAND) + root, _, ret = do_ex(["hg", "root"], wd) + if ret: + return None + return cls(root) + + def is_dirty(self) -> bool: + out, _, _ = self.do_ex('hg id -T "{dirty}"') + return bool(out) + + def get_branch(self) -> str | None: + res = self.do_ex('hg id -T "{bookmarks}"') + if res.returncode: + trace("branch err", res) + return None + return res.out + + def get_head_date(self) -> date | None: + date_part, err, ret = self.do_ex('hg log -r . -T "{shortdate(date)}"') + if ret: + trace("head date err", date_part, err, ret) + return None + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self) -> bool: + return False + + def fetch_shallow(self) -> None: + pass + + def get_hg_node(self) -> str | None: + node, _, ret = self.do_ex('hg log -r . -T "{node}"') + if not ret: + return node + else: + return None + + def _hg2git(self, hg_node: str) -> str | None: + with suppress(FileNotFoundError): + with open(os.path.join(self.path, ".hg/git-mapfile")) as map_items: + for item in map_items: + if hg_node in item: + git_node, hg_node = item.split() + return git_node + return None + + def node(self) -> str | None: + hg_node = self.get_hg_node() + if hg_node is None: + return None + + git_node = self._hg2git(hg_node) + + if git_node is None: + # trying again after hg -> git + self.do_ex("hg gexport") + git_node = self._hg2git(hg_node) + + if git_node is None: + trace("Cannot get git node so we use hg node", hg_node) + + if hg_node == "0" * len(hg_node): + # mimic Git behavior + return None + + return hg_node + + return git_node[:7] + + def count_all_nodes(self) -> int: + revs, _, _ = self.do_ex(["hg", "log", "-r", "ancestors(.)", "-T", "."]) + return len(revs) + + def default_describe(self) -> _CmdResult: + """ + Tentative to reproduce the output of + + `git describe --dirty --tags --long --match *[0-9]*` + + """ + hg_tags_str, _, ret = self.do_ex( + [ + "hg", + "log", + "-r", + "(reverse(ancestors(.)) and tag(r're:v?[0-9].*'))", + "-T", + "{tags}{if(tags, ' ', '')}", + ] + ) + if ret: + return _FAKE_GIT_DESCRIBE_ERROR + hg_tags: list[str] = hg_tags_str.split() + + if not hg_tags: + return _FAKE_GIT_DESCRIBE_ERROR + + with open(os.path.join(self.path, ".hg/git-tags")) as fp: + git_tags: dict[str, str] = dict(line.split()[::-1] for line in fp) + + tag: str + for hg_tag in hg_tags: + if hg_tag in git_tags: + tag = hg_tag + break + else: + trace("tag not found", hg_tags, git_tags) + return _FAKE_GIT_DESCRIBE_ERROR + + out, _, ret = self.do_ex(["hg", "log", "-r", f"'{tag}'::.", "-T", "."]) + if ret: + return _FAKE_GIT_DESCRIBE_ERROR + distance = len(out) - 1 + + node = self.node() + assert node is not None + desc = f"{tag}-{distance}-g{node}" + + if self.is_dirty(): + desc += "-dirty" + trace("desc", desc) + return _CmdResult(desc, "", 0) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/integration.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/integration.py new file mode 100644 index 0000000..45c841d --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/integration.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import os +import warnings +from typing import Any +from typing import Callable +from typing import TYPE_CHECKING + +import setuptools + +from . import _get_version +from . import _version_missing +from ._entrypoints import iter_entry_points +from ._integration.setuptools import ( + read_dist_name_from_setup_cfg as _read_dist_name_from_setup_cfg, +) +from .config import Configuration +from .utils import do +from .utils import trace + +if TYPE_CHECKING: + from . import _types as _t + + +def _warn_on_old_setuptools(_version: str = setuptools.__version__) -> None: + if int(_version.split(".")[0]) < 45: + warnings.warn( + RuntimeWarning( + f""" +ERROR: setuptools=={_version} is used in combination with setuptools_scm>=6.x + +Your build configuration is incomplete and previously worked by accident! +setuptools_scm requires setuptools>=45 + + +This happens as setuptools is unable to replace itself when a activated build dependency +requires a more recent setuptools version +(it does not respect "setuptools>X" in setup_requires). + + +setuptools>=31 is required for setup.cfg metadata support +setuptools>=42 is required for pyproject.toml configuration support + +Suggested workarounds if applicable: + - preinstalling build dependencies like setuptools_scm before running setup.py + - installing setuptools_scm using the system package manager to ensure consistency + - migrating from the deprecated setup_requires mechanism to pep517/518 + and using a pyproject.toml to declare build dependencies + which are reliably pre-installed before running the build tools +""" + ) + ) + + +_warn_on_old_setuptools() + + +def _assign_version(dist: setuptools.Distribution, config: Configuration) -> None: + maybe_version = _get_version(config) + + if maybe_version is None: + _version_missing(config) + else: + dist.metadata.version = maybe_version + + +def version_keyword( + dist: setuptools.Distribution, + keyword: str, + value: bool | dict[str, Any] | Callable[[], dict[str, Any]], +) -> None: + if not value: + return + elif value is True: + value = {} + elif callable(value): + 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 # type: str | None + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + config = Configuration(dist_name=dist_name, **value) + _assign_version(dist, config) + + +def find_files(path: _t.PathT = "") -> list[str]: + 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 infer_version(dist: setuptools.Distribution) -> None: + trace( + "finalize hook", + vars(dist.metadata), + ) + dist_name = dist.metadata.name + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + if not os.path.isfile("pyproject.toml"): + return + if dist_name == "setuptools_scm": + return + try: + config = Configuration.from_file(dist_name=dist_name) + except LookupError as e: + trace(e) + else: + _assign_version(dist, config) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/scm_workdir.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/scm_workdir.py new file mode 100644 index 0000000..113f68a --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/scm_workdir.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import ClassVar +from typing import TYPE_CHECKING + +from .utils import _CmdResult +from .utils import do +from .utils import do_ex +from .utils import require_command + +if TYPE_CHECKING: + from . import _types as _t + + +class Workdir: + COMMAND: ClassVar[str] + + def __init__(self, path: _t.PathT): + require_command(self.COMMAND) + self.path = path + + def do_ex(self, cmd: _t.CMD_TYPE) -> _CmdResult: + return do_ex(cmd, cwd=self.path) + + def do(self, cmd: _t.CMD_TYPE) -> str: + return do(cmd, cwd=self.path) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/utils.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/utils.py new file mode 100644 index 0000000..7c690b8 --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/utils.py @@ -0,0 +1,180 @@ +""" +utils +""" +from __future__ import annotations + +import os +import platform +import shlex +import subprocess +import sys +import textwrap +import warnings +from types import CodeType +from types import FunctionType +from typing import Iterator +from typing import Mapping +from typing import NamedTuple +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + + from . import _types as _t + +DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) +IS_WINDOWS = platform.system() == "Windows" + + +class _CmdResult(NamedTuple): + out: str + err: str + returncode: int + + +def no_git_env(env: Mapping[str, str]) -> dict[str, str]: + # 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 avoid_pip_isolation(env: Mapping[str, str]) -> dict[str, str]: + """ + pip build isolation can break Mercurial + (see https://github.com/pypa/pip/issues/10635) + + pip uses PYTHONNOUSERSITE and a path in PYTHONPATH containing "pip-build-env-". + """ + new_env = {k: v for k, v in env.items() if k != "PYTHONNOUSERSITE"} + if "PYTHONPATH" not in new_env: + return new_env + + new_env["PYTHONPATH"] = os.pathsep.join( + [ + path + for path in new_env["PYTHONPATH"].split(os.pathsep) + if "pip-build-env-" not in path + ] + ) + return new_env + + +def trace(*k: object, indent: bool = False) -> None: + if DEBUG: + if indent and len(k) > 1: + k = (k[0],) + tuple(textwrap.indent(str(s), " ") for s in k[1:]) + print(*k, file=sys.stderr, flush=True) + + +def ensure_stripped_str(str_or_bytes: str | bytes) -> str: + if isinstance(str_or_bytes, str): + return str_or_bytes.strip() + else: + return str_or_bytes.decode("utf-8", "surrogateescape").strip() + + +def _run(cmd: _t.CMD_TYPE, cwd: _t.PathT) -> subprocess.CompletedProcess[str]: + return subprocess.run( + cmd, + capture_output=True, + cwd=str(cwd), + env=dict( + avoid_pip_isolation(no_git_env(os.environ)), + # os.environ, + # try to disable i18n + LC_ALL="C", + LANGUAGE="", + HGPLAIN="1", + ), + text=True, + ) + + +def do_ex(cmd: _t.CMD_TYPE, cwd: _t.PathT = ".") -> _CmdResult: + if not DEBUG or not isinstance(cmd, list): + cmd_4_trace = cmd + else: + # give better results than shlex.join in our cases + cmd_4_trace = " ".join( + [s if all(c not in s for c in " {[:") else f'"{s}"' for s in cmd] + ) + trace("----\ncmd:\n", cmd_4_trace, indent=True) + trace(" in:", cwd) + if os.name == "posix" and not isinstance(cmd, (list, tuple)): + cmd = shlex.split(cmd) + + res = _run(cmd, cwd) + if res.stdout: + trace("out:\n", res.stdout, indent=True) + if res.stderr: + trace("err:\n", res.stderr, indent=True) + if res.returncode: + trace("ret:", res.returncode) + return _CmdResult( + ensure_stripped_str(res.stdout), ensure_stripped_str(res.stderr), res.returncode + ) + + +def do(cmd: list[str] | str, cwd: str | _t.PathT = ".") -> str: + out, err, ret = do_ex(cmd, cwd) + if ret and not DEBUG: + print(err) + return out + + +def data_from_mime(path: _t.PathT) -> dict[str, str]: + with 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: object | FunctionType, argname: str) -> bool: + assert isinstance(fn, FunctionType) + code: CodeType = fn.__code__ + return argname in code.co_varnames + + +def has_command(name: str, args: list[str] | None = None, warn: bool = True) -> bool: + try: + cmd = [name, "help"] if args is None else [name, *args] + p = _run(cmd, ".") + except OSError: + trace(*sys.exc_info()) + res = False + else: + res = not p.returncode + if not res and warn: + warnings.warn("%r was not found" % name, category=RuntimeWarning) + return res + + +def require_command(name: str) -> None: + if not has_command(name, warn=False): + raise OSError("%r was not found" % name) + + +def iter_entry_points( + group: str, name: str | None = None +) -> Iterator[_t.EntrypointProtocol]: + + from ._entrypoints import iter_entry_points + + return iter_entry_points(group, name) diff --git a/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/version.py b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/version.py new file mode 100644 index 0000000..a53f50b --- /dev/null +++ b/.eggs/setuptools_scm-7.0.5-py3.9.egg/setuptools_scm/version.py @@ -0,0 +1,563 @@ +from __future__ import annotations + +import os +import re +import warnings +from datetime import date +from datetime import datetime +from datetime import timezone +from typing import Any +from typing import Callable +from typing import cast +from typing import Iterator +from typing import List +from typing import Match +from typing import overload +from typing import Tuple +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import Concatenate + + from . import _types as _t + +from ._version_cls import Version as PkgVersion +from .config import Configuration +from .config import _VersionT +from .utils import trace + +SEMVER_MINOR = 2 +SEMVER_PATCH = 3 +SEMVER_LEN = 3 + + +def _parse_version_tag( + tag: str | object, config: Configuration +) -> dict[str, str] | None: + tagstring = tag if isinstance(tag, str) else str(tag) + match = config.tag_regex.match(tagstring) + + result = None + if match: + key: str | int + 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(f"tag '{tag}' parsed to {result}") + return result + + +def callable_or_entrypoint(group: str, callable_or_name: str | Any) -> Any: + trace("ep", (group, callable_or_name)) + + if callable(callable_or_name): + return callable_or_name + from ._entrypoints import iter_entry_points + + for ep in iter_entry_points(group, callable_or_name): + trace("ep found:", ep.name) + return ep.load() + + +def tag_to_version( + tag: _VersionT | str, config: Configuration | None = None +) -> _VersionT | 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(f"tag {tag!r} no version found") + return None + + version_str = tagdict["version"] + trace("version pre parse", version_str) + + if tagdict.get("suffix", ""): + warnings.warn( + "tag {!r} will be stripped of its suffix '{}'".format( + tag, tagdict["suffix"] + ) + ) + + version = config.version_cls(version_str) + trace("version", repr(version)) + + return version + + +def tags_to_versions( + tags: list[str], config: Configuration | None = None +) -> list[_VersionT]: + """ + 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: list[_VersionT] = [] + for tag in tags: + parsed = tag_to_version(tag, config=config) + if parsed: + result.append(parsed) + return result + + +class ScmVersion: + def __init__( + self, + tag_version: Any, + config: Configuration, + distance: int | None = None, + node: str | None = None, + dirty: bool = False, + preformatted: bool = False, + branch: str | None = None, + node_date: date | None = None, + **kw: object, + ): + 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.node_date = node_date + if "SOURCE_DATE_EPOCH" in os.environ: + date_epoch = int(os.environ["SOURCE_DATE_EPOCH"]) + self.time = datetime.fromtimestamp(date_epoch, timezone.utc) + else: + self.time = datetime.now(timezone.utc) + self._extra = kw + self.dirty = dirty + self.preformatted = preformatted + self.branch = branch + self.config = config + + @property + def extra(self) -> dict[str, Any]: + warnings.warn( + "ScmVersion.extra is deprecated and will be removed in future", + category=DeprecationWarning, + stacklevel=2, + ) + return self._extra + + @property + def exact(self) -> bool: + return self.distance is None + + def __repr__(self) -> str: + return self.format_with( + "" + ) + + def format_with(self, fmt: str, **kw: object) -> str: + return fmt.format( + time=self.time, + tag=self.tag, + distance=self.distance, + node=self.node, + dirty=self.dirty, + branch=self.branch, + node_date=self.node_date, + **kw, + ) + + def format_choice(self, clean_format: str, dirty_format: str, **kw: object) -> str: + return self.format_with(dirty_format if self.dirty else clean_format, **kw) + + def format_next_version( + self, + guess_next: Callable[Concatenate[ScmVersion, _t.P], str], + fmt: str = "{guessed}.dev{distance}", + *k: _t.P.args, # type: ignore + **kw: _t.P.kwargs, # type: ignore + ) -> str: + guessed = guess_next(self, *k, **kw) + return self.format_with(fmt, guessed=guessed) + + +def _parse_tag( + tag: _VersionT | str, preformatted: bool, config: Configuration | None +) -> _VersionT | str: + if preformatted: + return tag + elif config is None or not isinstance(tag, config.version_cls): + version = tag_to_version(tag, config) + assert version is not None + return version + else: + return tag + + +def meta( + tag: str | _VersionT, + distance: int | None = None, + dirty: bool = False, + node: str | None = None, + preformatted: bool = False, + branch: str | None = None, + config: Configuration | None = None, + node_date: date | None = None, + **kw: Any, +) -> ScmVersion: + if not config: + warnings.warn( + "meta invoked without explicit configuration," + " will use defaults where required." + ) + config = Configuration() + parsed_version = _parse_tag(tag, preformatted, config) + trace("version", tag, "->", parsed_version) + assert parsed_version is not None, "Can't parse version %s" % tag + return ScmVersion( + parsed_version, + distance=distance, + node=node, + dirty=dirty, + preformatted=preformatted, + branch=branch, + config=config, + node_date=node_date, + **kw, + ) + + +def guess_next_version(tag_version: ScmVersion) -> str: + version = _strip_local(str(tag_version.tag)) + return _bump_dev(version) or _bump_regex(version) + + +def _dont_guess_next_version(tag_version: ScmVersion) -> str: + version = _strip_local(str(tag_version.tag)) + return _bump_dev(version) or _add_post(version) + + +def _strip_local(version_string: str) -> str: + public, sep, local = version_string.partition("+") + return public + + +def _add_post(version: str) -> str: + if "post" in version: + raise ValueError( + f"{version} already is a post release, refusing to guess the update" + ) + return f"{version}.post1" + + +def _bump_dev(version: str) -> str | None: + if ".dev" not in version: + return None + + prefix, tail = version.rsplit(".dev", 1) + if tail != "0": + raise ValueError( + "choosing custom numbers for the `.devX` distance " + "is not supported.\n " + f"The {version} can't be bumped\n" + "Please drop the tag or create a new supported one ending in .dev0" + ) + return prefix + + +def _bump_regex(version: str) -> str: + match = re.match(r"(.*?)(\d+)$", version) + if match is None: + raise ValueError( + "{version} does not end with a number to bump, " + "please correct or use a custom version scheme".format(version=version) + ) + else: + prefix, tail = match.groups() + return "%s%d" % (prefix, int(tail) + 1) + + +def guess_next_dev_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + else: + return version.format_next_version(guess_next_version) + + +def guess_next_simple_semver( + version: ScmVersion, retain: int, increment: bool = True +) -> str: + try: + parts = [int(i) for i in str(version.tag).split(".")[:retain]] + except ValueError: + raise ValueError(f"{version} can't be parsed as numeric version") + while len(parts) < retain: + parts.append(0) + if increment: + parts[-1] += 1 + while len(parts) < SEMVER_LEN: + parts.append(0) + return ".".join(str(i) for i in parts) + + +def simplified_semver_version(version: ScmVersion) -> str: + if version.exact: + return guess_next_simple_semver(version, 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 release_branch_semver_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + if version.branch is not None: + # Does the branch name (stripped of namespace) parse as a version? + branch_ver_data = _parse_version_tag( + version.branch.split("/")[-1], version.config + ) + if branch_ver_data is not None: + branch_ver = branch_ver_data["version"] + if branch_ver[0] == "v": + # Allow branches that start with 'v', similar to Version. + branch_ver = branch_ver[1:] + # Does the branch version up to the minor part match the tag? If not it + # might be like, an issue number or something and not a version number, so + # we only want to use it if it matches. + tag_ver_up_to_minor = str(version.tag).split(".")[:SEMVER_MINOR] + branch_ver_up_to_minor = branch_ver.split(".")[:SEMVER_MINOR] + if branch_ver_up_to_minor == tag_ver_up_to_minor: + # We're in a release/maintenance branch, next is a patch/rc/beta bump: + return version.format_next_version(guess_next_version) + # We're in a development branch, next is a minor bump: + return version.format_next_version(guess_next_simple_semver, retain=SEMVER_MINOR) + + +def release_branch_semver(version: ScmVersion) -> str: + warnings.warn( + "release_branch_semver is deprecated and will be removed in future. " + + "Use release_branch_semver_version instead", + category=DeprecationWarning, + stacklevel=2, + ) + return release_branch_semver_version(version) + + +def no_guess_dev_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + else: + return version.format_next_version(_dont_guess_next_version) + + +def date_ver_match(ver: str) -> Match[str] | None: + match = re.match( + ( + r"^(?P(?P\d{2}|\d{4})(?:\.\d{1,2}){2})" + r"(?:\.(?P\d*)){0,1}?$" + ), + ver, + ) + return match + + +def guess_next_date_ver( + version: ScmVersion, + node_date: date | None = None, + date_fmt: str | None = None, + version_cls: type | None = None, +) -> str: + """ + same-day -> patch +1 + other-day -> today + + distance is always added as .devX + """ + match = date_ver_match(str(version.tag)) + if match is None: + warnings.warn( + f"{version} does not correspond to a valid versioning date, " + "assuming legacy version" + ) + if date_fmt is None: + date_fmt = "%y.%m.%d" + else: + # deduct date format if not provided + if date_fmt is None: + date_fmt = "%Y.%m.%d" if len(match.group("year")) == 4 else "%y.%m.%d" + today = datetime.now(timezone.utc).date() + head_date = node_date or today + # compute patch + if match is None: + tag_date = today + else: + tag_date = datetime.strptime(match.group("date"), date_fmt).date() + if tag_date == head_date: + patch = "0" if match is None else (match.group("patch") or "0") + patch = int(patch) + 1 + else: + if tag_date > head_date and match is not None: + # warn on future times + warnings.warn( + "your previous tag ({}) is ahead your node date ({})".format( + tag_date, head_date + ) + ) + patch = 0 + next_version = "{node_date:{date_fmt}}.{patch}".format( + node_date=head_date, date_fmt=date_fmt, patch=patch + ) + # rely on the Version object to ensure consistency (e.g. remove leading 0s) + if version_cls is None: + version_cls = PkgVersion + next_version = str(version_cls(next_version)) + return next_version + + +def calver_by_date(version: ScmVersion) -> str: + if version.exact and not version.dirty: + return version.format_with("{tag}") + # TODO: move the release-X check to a new scheme + if version.branch is not None and version.branch.startswith("release-"): + branch_ver = _parse_version_tag(version.branch.split("-")[-1], version.config) + if branch_ver is not None: + ver = branch_ver["version"] + match = date_ver_match(ver) + if match: + return ver + return version.format_next_version( + guess_next_date_ver, + node_date=version.node_date, + version_cls=version.config.version_cls, + ) + + +def _format_local_with_time(version: ScmVersion, time_format: str) -> str: + + 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: ScmVersion) -> str: + return _format_local_with_time(version, time_format="%Y%m%d") + + +def get_local_node_and_timestamp(version: ScmVersion, fmt: str = "%Y%m%d%H%M%S") -> str: + return _format_local_with_time(version, time_format=fmt) + + +def get_local_dirty_tag(version: ScmVersion) -> str: + return version.format_choice("", "+dirty") + + +def get_no_local_node(_: Any) -> str: + return "" + + +def postrelease_version(version: ScmVersion) -> str: + if version.exact: + return version.format_with("{tag}") + else: + return version.format_with("{tag}.post{distance}") + + +def _get_ep(group: str, name: str) -> Any | None: + from ._entrypoints import iter_entry_points + + for ep in iter_entry_points(group, name): + trace("ep found:", ep.name) + return ep.load() + else: + return None + + +def _iter_version_schemes( + entrypoint: str, + scheme_value: str + | list[str] + | tuple[str, ...] + | Callable[[ScmVersion], str] + | None, + _memo: set[object] | None = None, +) -> Iterator[Callable[[ScmVersion], str]]: + if _memo is None: + _memo = set() + if isinstance(scheme_value, str): + scheme_value = cast( + 'str|List[str]|Tuple[str, ...]|Callable[["ScmVersion"], str]|None', + _get_ep(entrypoint, scheme_value), + ) + + if isinstance(scheme_value, (list, tuple)): + for variant in scheme_value: + if variant not in _memo: + _memo.add(variant) + yield from _iter_version_schemes(entrypoint, variant, _memo=_memo) + elif callable(scheme_value): + yield scheme_value + + +@overload +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: str +) -> str: + ... + + +@overload +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: None +) -> str | None: + ... + + +def _call_version_scheme( + version: ScmVersion, entypoint: str, given_value: str, default: str | None +) -> str | None: + for scheme in _iter_version_schemes(entypoint, given_value): + result = scheme(version) + if result is not None: + return result + return default + + +def format_version(version: ScmVersion, **config: Any) -> str: + trace("scm version", version) + trace("config", config) + if version.preformatted: + assert isinstance(version.tag, str) + return version.tag + main_version = _call_version_scheme( + version, "setuptools_scm.version_scheme", config["version_scheme"], None + ) + trace("version", main_version) + assert main_version is not None + local_version = _call_version_scheme( + version, "setuptools_scm.local_scheme", config["local_scheme"], "+unknown" + ) + trace("local_version", local_version) + return main_version + local_version diff --git a/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/LICENSE b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..1df6b3b --- /dev/null +++ b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/LICENSE @@ -0,0 +1,254 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/PKG-INFO b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..1ed963a --- /dev/null +++ b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,189 @@ +Metadata-Version: 2.1 +Name: typing_extensions +Version: 4.4.0 +Summary: Backported and Experimental Type Hints for Python 3.7+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md +Project-URL: Documentation, https://typing.readthedocs.io/ +Project-URL: Home, https://github.com/python/typing_extensions +Project-URL: Q & A, https://github.com/python/typing/discussions +Project-URL: Repository, https://github.com/python/typing_extensions + +# Typing Extensions + +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing) + +## Overview + +The `typing_extensions` module serves two related purposes: + +- Enable use of new type system features on older Python versions. For example, + `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows + users on previous Python versions to use it too. +- Enable experimentation with new type system PEPs before they are accepted and + added to the `typing` module. + +New features may be added to `typing_extensions` as soon as they are specified +in a PEP that has been added to the [python/peps](https://github.com/python/peps) +repository. If the PEP is accepted, the feature will then be added to `typing` +for the next CPython release. No typing PEP has been rejected so far, so we +haven't yet figured out how to deal with that possibility. + +Starting with version 4.0.0, `typing_extensions` uses +[Semantic Versioning](https://semver.org/). The +major version is incremented for all backwards-incompatible changes. +Therefore, it's safe to depend +on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`, +where `x.y` is the first version that includes all features you need. + +`typing_extensions` supports Python versions 3.7 and higher. In the future, +support for older Python versions will be dropped some time after that version +reaches end of life. + +## Included items + +This module currently contains the following: + +- Experimental features + + - `override` (see PEP 698) + - The `default=` argument to `TypeVar`, `ParamSpec`, and `TypeVarTuple` (see PEP 696) + - The `infer_variance=` argument to `TypeVar` (see PEP 695) + +- In `typing` since Python 3.11 + + - `assert_never` + - `assert_type` + - `clear_overloads` + - `@dataclass_transform()` (see PEP 681) + - `get_overloads` + - `LiteralString` (see PEP 675) + - `Never` + - `NotRequired` (see PEP 655) + - `reveal_type` + - `Required` (see PEP 655) + - `Self` (see PEP 673) + - `TypeVarTuple` (see PEP 646; the `typing_extensions` version supports the `default=` argument from PEP 696) + - `Unpack` (see PEP 646) + +- In `typing` since Python 3.10 + + - `Concatenate` (see PEP 612) + - `ParamSpec` (see PEP 612; the `typing_extensions` version supports the `default=` argument from PEP 696) + - `ParamSpecArgs` (see PEP 612) + - `ParamSpecKwargs` (see PEP 612) + - `TypeAlias` (see PEP 613) + - `TypeGuard` (see PEP 647) + - `is_typeddict` + +- In `typing` since Python 3.9 + + - `Annotated` (see PEP 593) + +- In `typing` since Python 3.8 + + - `final` (see PEP 591) + - `Final` (see PEP 591) + - `Literal` (see PEP 586) + - `Protocol` (see PEP 544) + - `runtime_checkable` (see PEP 544) + - `TypedDict` (see PEP 589) + - `get_origin` (`typing_extensions` provides this function only in Python 3.7+) + - `get_args` (`typing_extensions` provides this function only in Python 3.7+) + +- In `typing` since Python 3.7 + + - `OrderedDict` + +- In `typing` since Python 3.5 or 3.6 (see [the typing documentation](https://docs.python.org/3.10/library/typing.html) for details) + + - `AsyncContextManager` + - `AsyncGenerator` + - `AsyncIterable` + - `AsyncIterator` + - `Awaitable` + - `ChainMap` + - `ClassVar` (see PEP 526) + - `ContextManager` + - `Coroutine` + - `Counter` + - `DefaultDict` + - `Deque` + - `NewType` + - `NoReturn` + - `overload` + - `Text` + - `Type` + - `TYPE_CHECKING` + - `get_type_hints` + +- The following have always been present in `typing`, but the `typing_extensions` versions provide + additional features: + + - `Any` (supports inheritance since Python 3.11) + - `NamedTuple` (supports multiple inheritance with `Generic` since Python 3.11) + - `TypeVar` (see PEPs 695 and 696) + +# Other Notes and Limitations + +Certain objects were changed after they were added to `typing`, and +`typing_extensions` provides a backport even on newer Python versions: + +- `TypedDict` does not store runtime information + about which (if any) keys are non-required in Python 3.8, and does not + honor the `total` keyword with old-style `TypedDict()` in Python + 3.9.0 and 3.9.1. `TypedDict` also does not support multiple inheritance + with `typing.Generic` on Python <3.11. +- `get_origin` and `get_args` lack support for `Annotated` in + Python 3.8 and lack support for `ParamSpecArgs` and `ParamSpecKwargs` + in 3.9. +- `@final` was changed in Python 3.11 to set the `.__final__` attribute. +- `@overload` was changed in Python 3.11 to make function overloads + introspectable at runtime. In order to access overloads with + `typing_extensions.get_overloads()`, you must use + `@typing_extensions.overload`. +- `NamedTuple` was changed in Python 3.11 to allow for multiple inheritance + with `typing.Generic`. +- Since Python 3.11, it has been possible to inherit from `Any` at + runtime. `typing_extensions.Any` also provides this capability. +- `TypeVar` gains two additional parameters, `default=` and `infer_variance=`, + in the draft PEPs 695 and 696, which are being considered for inclusion + in Python 3.12. + +There are a few types whose interface was modified between different +versions of typing. For example, `typing.Sequence` was modified to +subclass `typing.Reversible` as of Python 3.5.3. + +These changes are _not_ backported to prevent subtle compatibility +issues when mixing the differing implementations of modified classes. + +Certain types have incorrect runtime behavior due to limitations of older +versions of the typing module: + +- `ParamSpec` and `Concatenate` will not work with `get_args` and + `get_origin`. Certain PEP 612 special cases in user-defined + `Generic`s are also not available. + +These types are only guaranteed to work for static type checking. + +## Running tests + +To run tests, navigate into the appropriate source directory and run +`test_typing_extensions.py`. + diff --git a/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/RECORD b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..5ae4c71 --- /dev/null +++ b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/RECORD @@ -0,0 +1,5 @@ +typing_extensions.py,sha256=ipqWiq5AHzrwczt6c26AP05Llh6a5_GaXRpOBqbogHA,80078 +typing_extensions-4.4.0.dist-info/LICENSE,sha256=x6-2XnVXB7n7kEhziaF20-09ADHVExr95FwjcV_16JE,12787 +typing_extensions-4.4.0.dist-info/WHEEL,sha256=4TfKIB_xu-04bc2iKz6_zFt-gEFEEDU_31HGhqzOCE8,81 +typing_extensions-4.4.0.dist-info/METADATA,sha256=1zSh1eMLnLkLMMC6aZSGRKx3eRnivEGDFWGSVD1zqhA,7249 +typing_extensions-4.4.0.dist-info/RECORD,, diff --git a/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/WHEEL b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..668ba4d --- /dev/null +++ b/.eggs/typing_extensions-4.4.0-py3.10.egg/EGG-INFO/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.7.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.eggs/typing_extensions-4.4.0-py3.10.egg/typing_extensions.py b/.eggs/typing_extensions-4.4.0-py3.10.egg/typing_extensions.py new file mode 100644 index 0000000..ef42417 --- /dev/null +++ b/.eggs/typing_extensions-4.4.0-py3.10.egg/typing_extensions.py @@ -0,0 +1,2209 @@ +import abc +import collections +import collections.abc +import functools +import operator +import sys +import types as _types +import typing + + +__all__ = [ + # Super-special typing primitives. + 'Any', + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'ParamSpecArgs', + 'ParamSpecKwargs', + 'Self', + 'Type', + 'TypeVar', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'NamedTuple', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsIndex', + + # One-off things. + 'Annotated', + 'assert_never', + 'assert_type', + 'clear_overloads', + 'dataclass_transform', + 'get_overloads', + 'final', + 'get_args', + 'get_origin', + 'get_type_hints', + 'IntVar', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'override', + 'Protocol', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeGuard', + 'TYPE_CHECKING', + 'Never', + 'NoReturn', + 'Required', + 'NotRequired', +] + +# for backward compatibility +PEP_560 = True +GenericMeta = type + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + +_marker = object() + + +def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" + f" actual {alen}, expected {elen}") + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + for t in types: + if ( + isinstance(t, typevar_types) and + t not in tvars and + not _is_unpack(t) + ): + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + +NoReturn = typing.NoReturn + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +if sys.version_info >= (3, 11): + from typing import Any +else: + + class _AnyMeta(type): + def __instancecheck__(self, obj): + if self is Any: + raise TypeError("typing_extensions.Any cannot be used with isinstance()") + return super().__instancecheck__(obj) + + def __repr__(self): + if self is Any: + return "typing_extensions.Any" + return super().__repr__() + + class Any(metaclass=_AnyMeta): + """Special type indicating an unconstrained type. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + checks. + """ + def __new__(cls, *args, **kwargs): + if cls is Any: + raise TypeError("Any cannot be instantiated") + return super().__new__(cls, *args, **kwargs) + + +ClassVar = typing.ClassVar + +# On older versions of typing there is an internal class named "Final". +# 3.8+ +if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): + Final = typing.Final +# 3.7 +else: + class _FinalForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Final = _FinalForm('Final', + doc="""A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties.""") + +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +def IntVar(name): + return typing.TypeVar(name) + + +# 3.8+: +if hasattr(typing, 'Literal'): + Literal = typing.Literal +# 3.7: +else: + class _LiteralForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return typing._GenericAlias(self, parameters) + + Literal = _LiteralForm('Literal', + doc="""A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") + + +_overload_dummy = typing._overload_dummy # noqa + + +if hasattr(typing, "get_overloads"): # 3.11+ + overload = typing.overload + get_overloads = typing.get_overloads + clear_overloads = typing.clear_overloads +else: + # {module: {qualname: {firstlineno: func}}} + _overload_registry = collections.defaultdict( + functools.partial(collections.defaultdict, dict) + ) + + def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + + The overloads for a function can be retrieved at runtime using the + get_overloads() function. + """ + # classmethod and staticmethod + f = getattr(func, "__func__", func) + try: + _overload_registry[f.__module__][f.__qualname__][ + f.__code__.co_firstlineno + ] = func + except AttributeError: + # Not a normal function; ignore. + pass + return _overload_dummy + + def get_overloads(func): + """Return all defined overloads for *func* as a sequence.""" + # classmethod and staticmethod + f = getattr(func, "__func__", func) + if f.__module__ not in _overload_registry: + return [] + mod_dict = _overload_registry[f.__module__] + if f.__qualname__ not in mod_dict: + return [] + return list(mod_dict[f.__qualname__].values()) + + def clear_overloads(): + """Clear all overloads in the registry.""" + _overload_registry.clear() + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. + + +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +Deque = typing.Deque +ContextManager = typing.ContextManager +AsyncContextManager = typing.AsyncContextManager +DefaultDict = typing.DefaultDict + +# 3.7.2+ +if hasattr(typing, 'OrderedDict'): + OrderedDict = typing.OrderedDict +# 3.7.0-3.7.2 +else: + OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) + +Counter = typing.Counter +ChainMap = typing.ChainMap +AsyncGenerator = typing.AsyncGenerator +NewType = typing.NewType +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +_PROTO_WHITELIST = ['Callable', 'Awaitable', + 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', + 'ContextManager', 'AsyncContextManager'] + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in ('Protocol', 'Generic'): + continue + annotations = getattr(base, '__annotations__', {}) + for attr in list(base.__dict__.keys()) + list(annotations.keys()): + if (not attr.startswith('_abc_') and attr not in ( + '__abstractmethods__', '__annotations__', '__weakref__', + '_is_protocol', '_is_runtime_protocol', '__dict__', + '__args__', '__slots__', + '__next_in_mro__', '__parameters__', '__origin__', + '__orig_bases__', '__extra__', '__tree_hash__', + '__doc__', '__subclasshook__', '__init__', '__new__', + '__module__', '_MutableMapping__marker', '_gorg')): + attrs.add(attr) + return attrs + + +def _is_callable_members_only(cls): + return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) + + +def _maybe_adjust_parameters(cls): + """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__. + + The contents of this function are very similar + to logic found in typing.Generic.__init_subclass__ + on the CPython main branch. + """ + tvars = [] + if '__orig_bases__' in cls.__dict__: + tvars = typing._collect_type_vars(cls.__orig_bases__) + # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. + # If found, tvars must be a subset of it. + # If not found, tvars is it. + # Also check for and reject plain Generic, + # and reject multiple Generic[...] and/or Protocol[...]. + gvars = None + for base in cls.__orig_bases__: + if (isinstance(base, typing._GenericAlias) and + base.__origin__ in (typing.Generic, Protocol)): + # for error messages + the_base = base.__origin__.__name__ + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...]" + " and/or Protocol[...] multiple types.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) + s_args = ', '.join(str(g) for g in gvars) + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {the_base}[{s_args}]") + tvars = gvars + cls.__parameters__ = tuple(tvars) + + +# 3.8+ +if hasattr(typing, 'Protocol'): + Protocol = typing.Protocol +# 3.7 +else: + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + class _ProtocolMeta(abc.ABCMeta): # noqa: B024 + # This metaclass is a bit unfortunate and exists only because of the lack + # of __instancehook__. + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if ((not getattr(cls, '_is_protocol', False) or + _is_callable_members_only(cls)) and + issubclass(instance.__class__, cls)): + return True + if cls._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(cls, attr, None)) or + getattr(instance, attr) is not None) + for attr in _get_protocol_attrs(cls)): + return True + return super().__instancecheck__(instance) + + class Protocol(metaclass=_ProtocolMeta): + # There is quite a lot of overlapping code with typing.Generic. + # Unfortunately it is hard to avoid this while these live in two different + # modules. The duplicated code will be removed when Protocol is moved to typing. + """Base class for protocol classes. Protocol classes are defined as:: + + class Proto(Protocol): + def meth(self) -> int: + ... + + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: + + class C: + def meth(self) -> int: + return 0 + + def func(x: Proto) -> int: + return x.meth() + + func(C()) # Passes static type check + + See PEP 544 for details. Protocol classes decorated with + @typing_extensions.runtime act as simple-minded runtime protocol that checks + only the presence of given attributes, ignoring their type signatures. + + Protocol classes can be generic, they are defined as:: + + class GenProto(Protocol[T]): + def meth(self) -> T: + ... + """ + __slots__ = () + _is_protocol = True + + def __new__(cls, *args, **kwds): + if cls is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can only be used as a base class") + return super().__new__(cls) + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple): + params = (params,) + if not params and cls is not typing.Tuple: + raise TypeError( + f"Parameter list to {cls.__qualname__}[...] cannot be empty") + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) # noqa + if cls is Protocol: + # Generic can only be subscripted with unique type variables. + if not all(isinstance(p, typing.TypeVar) for p in params): + i = 0 + while isinstance(params[i], typing.TypeVar): + i += 1 + raise TypeError( + "Parameters to Protocol[...] must all be type variables." + f" Parameter {i + 1} is {params[i]}") + if len(set(params)) != len(params): + raise TypeError( + "Parameters to Protocol[...] must all be unique") + else: + # Subscripting a regular Generic subclass. + _check_generic(cls, params, len(cls.__parameters__)) + return typing._GenericAlias(cls, params) + + def __init_subclass__(cls, *args, **kwargs): + if '__orig_bases__' in cls.__dict__: + error = typing.Generic in cls.__orig_bases__ + else: + error = typing.Generic in cls.__bases__ + if error: + raise TypeError("Cannot inherit from plain Generic") + _maybe_adjust_parameters(cls) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + def _proto_hook(other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not getattr(cls, '_is_runtime_protocol', False): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Instance and class checks can only be used with" + " @runtime protocols") + if not _is_callable_members_only(cls): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + if not isinstance(other, type): + # Same error as for issubclass(1, int) + raise TypeError('issubclass() arg 1 must be a class') + for attr in _get_protocol_attrs(cls): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + annotations = getattr(base, '__annotations__', {}) + if (isinstance(annotations, typing.Mapping) and + attr in annotations and + isinstance(other, _ProtocolMeta) and + other._is_protocol): + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # We have nothing more to do for non-protocols. + if not cls._is_protocol: + return + + # Check consistency of bases. + for base in cls.__bases__: + if not (base in (object, typing.Generic) or + base.__module__ == 'collections.abc' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, _ProtocolMeta) and base._is_protocol): + raise TypeError('Protocols can only inherit from other' + f' protocols, got {repr(base)}') + cls.__init__ = _no_init + + +# 3.8+ +if hasattr(typing, 'runtime_checkable'): + runtime_checkable = typing.runtime_checkable +# 3.7 +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol, so that it + can be used with isinstance() and issubclass(). Raise TypeError + if applied to a non-protocol class. + + This allows a simple-minded structural check very similar to the + one-offs in collections.abc such as Hashable. + """ + if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + f' got {cls!r}') + cls._is_runtime_protocol = True + return cls + + +# Exists for backwards compatibility. +runtime = runtime_checkable + + +# 3.8+ +if hasattr(typing, 'SupportsIndex'): + SupportsIndex = typing.SupportsIndex +# 3.7 +else: + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + +if hasattr(typing, "Required"): + # The standard library TypedDict in Python 3.8 does not store runtime information + # about which (if any) keys are optional. See https://bugs.python.org/issue38834 + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + def _check_fails(cls, other): + try: + if sys._getframe(1).f_globals['__name__'] not in ['abc', + 'functools', + 'typing']: + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + except (AttributeError, ValueError): + pass + return False + + def _dict_new(*args, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + return dict(*args, **kwargs) + + _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)' + + def _typeddict_new(*args, total=True, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + if args: + typename, args = args[0], args[1:] # allow the "_typename" keyword be passed + elif '_typename' in kwargs: + typename = kwargs.pop('_typename') + import warnings + warnings.warn("Passing '_typename' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("TypedDict.__new__() missing 1 required positional " + "argument: '_typename'") + if args: + try: + fields, = args # allow the "_fields" keyword be passed + except ValueError: + raise TypeError('TypedDict.__new__() takes from 2 to 3 ' + f'positional arguments but {len(args) + 2} ' + 'were given') + elif '_fields' in kwargs and len(kwargs) == 1: + fields = kwargs.pop('_fields') + import warnings + warnings.warn("Passing '_fields' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + fields = None + + if fields is None: + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + + ns = {'__annotations__': dict(fields)} + try: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return _TypedDictMeta(typename, (), ns, total=total) + + _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,' + ' /, *, total=True, **kwargs)') + + class _TypedDictMeta(type): + def __init__(cls, name, bases, ns, total=True): + super().__init__(name, bases, ns) + + def __new__(cls, name, bases, ns, total=True): + # Create new typed dict class object. + # This method is called directly when TypedDict is subclassed, + # or via _typeddict_new when TypedDict is instantiated. This way + # TypedDict supports all three syntaxes described in its docstring. + # Subclasses and instances of TypedDict return actual dictionaries + # via _dict_new. + ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new + # Don't insert typing.Generic into __bases__ here, + # or Generic.__init_subclass__ will raise TypeError + # in the super().__new__() call. + # Instead, monkey-patch __bases__ onto the class after it's been created. + tp_dict = super().__new__(cls, name, (dict,), ns) + + if any(issubclass(base, typing.Generic) for base in bases): + tp_dict.__bases__ = (typing.Generic, dict) + _maybe_adjust_parameters(tp_dict) + + annotations = {} + own_annotations = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + own_annotations = { + n: typing._type_check(tp, msg) for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + + for base in bases: + annotations.update(base.__dict__.get('__annotations__', {})) + required_keys.update(base.__dict__.get('__required_keys__', ())) + optional_keys.update(base.__dict__.get('__optional_keys__', ())) + + annotations.update(own_annotations) + for annotation_key, annotation_type in own_annotations.items(): + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + annotation_origin = get_origin(annotation_type) + + if annotation_origin is Required: + required_keys.add(annotation_key) + elif annotation_origin is NotRequired: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + return tp_dict + + __instancecheck__ = __subclasscheck__ = _check_fails + + TypedDict = _TypedDictMeta('TypedDict', (dict,), {}) + TypedDict.__module__ = __name__ + TypedDict.__doc__ = \ + """A simple typed name space. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type that expects all of its + instances to have a certain set of keys, with each key + associated with a value of a consistent type. This expectation + is not checked at runtime but is only enforced by type checkers. + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports two additional equivalent forms:: + + Point2D = TypedDict('Point2D', x=int, y=int, label=str) + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + The class syntax is only supported in Python 3.6+, while two other + syntax forms work for Python 2.7 and 3.2+ + """ + + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + return isinstance(tp, tuple(_TYPEDDICT_TYPES)) + + +if hasattr(typing, "assert_type"): + assert_type = typing.assert_type + +else: + def assert_type(__val, __typ): + """Assert (to the type checker) that the value is of the given type. + + When the type checker encounters a call to assert_type(), it + emits an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # ok + assert_type(name, int) # type checker error + + At runtime this returns the first argument unchanged and otherwise + does nothing. + """ + return __val + + +if hasattr(typing, "Required"): + get_type_hints = typing.get_type_hints +else: + import functools + import types + + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, _AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return types.GenericAlias(t.__origin__, stripped_args) + if hasattr(types, "UnionType") and isinstance(t, types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if hasattr(typing, "Annotated"): + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + else: + hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + +# Python 3.9+ has PEP 593 (Annotated) +if hasattr(typing, 'Annotated'): + Annotated = typing.Annotated + # Not exported and not a public API, but needed for get_origin() and get_args() + # to work. + _AnnotatedAlias = typing._AnnotatedAlias +# 3.7-3.8 +else: + class _AnnotatedAlias(typing._GenericAlias, _root=True): + """Runtime representation of an annotated type. + + At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' + with extra annotations. The alias behaves like a normal typing alias, + instantiating is the same as instantiating the underlying type, binding + it to types is also the same. + """ + def __init__(self, origin, metadata): + if isinstance(origin, _AnnotatedAlias): + metadata = origin.__metadata__ + metadata + origin = origin.__origin__ + super().__init__(origin, origin) + self.__metadata__ = metadata + + def copy_with(self, params): + assert len(params) == 1 + new_type = params[0] + return _AnnotatedAlias(new_type, self.__metadata__) + + def __repr__(self): + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") + + def __reduce__(self): + return operator.getitem, ( + Annotated, (self.__origin__,) + self.__metadata__ + ) + + def __eq__(self, other): + if not isinstance(other, _AnnotatedAlias): + return NotImplemented + if self.__origin__ != other.__origin__: + return False + return self.__metadata__ == other.__metadata__ + + def __hash__(self): + return hash((self.__origin__, self.__metadata__)) + + class Annotated: + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type (and will be in + the __origin__ field), the remaining arguments are kept as a tuple in + the __extra__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + + __slots__ = () + + def __new__(cls, *args, **kwargs): + raise TypeError("Type Annotated cannot be instantiated.") + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + allowed_special_forms = (ClassVar, Final) + if get_origin(params[0]) in allowed_special_forms: + origin = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + origin = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + f"Cannot subclass {cls.__module__}.Annotated" + ) + +# Python 3.8 has get_origin() and get_args() but those implementations aren't +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +if sys.version_info[:2] >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.7-3.9 +else: + try: + # 3.9+ + from typing import _BaseGenericAlias + except ImportError: + _BaseGenericAlias = typing._GenericAlias + try: + # 3.9+ + from typing import GenericAlias as _typing_GenericAlias + except ImportError: + _typing_GenericAlias = typing._GenericAlias + + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, _AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, _AnnotatedAlias): + return (tp.__origin__,) + tp.__metadata__ + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)): + if getattr(tp, "_special", False): + return () + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeAliasForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") +# 3.7-3.8 +else: + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + TypeAlias = _TypeAliasForm('TypeAlias', + doc="""Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example + above.""") + + +class _DefaultMixin: + """Mixin for TypeVarLike defaults.""" + + __slots__ = () + + def __init__(self, default): + if isinstance(default, (tuple, list)): + self.__default__ = tuple((typing._type_check(d, "Default must be a type") + for d in default)) + elif default: + self.__default__ = typing._type_check(default, "Default must be a type") + else: + self.__default__ = None + + +# Add default and infer_variance parameters from PEP 696 and 695 +class TypeVar(typing.TypeVar, _DefaultMixin, _root=True): + """Type variable.""" + + __module__ = 'typing' + + def __init__(self, name, *constraints, bound=None, + covariant=False, contravariant=False, + default=None, infer_variance=False): + super().__init__(name, *constraints, bound=bound, covariant=covariant, + contravariant=contravariant) + _DefaultMixin.__init__(self, default) + self.__infer_variance__ = infer_variance + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.7-3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + +# 3.10+ +if hasattr(typing, 'ParamSpec'): + + # Add default Parameter - PEP 696 + class ParamSpec(typing.ParamSpec, _DefaultMixin, _root=True): + """Parameter specification variable.""" + + __module__ = 'typing' + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + default=None): + super().__init__(name, bound=bound, covariant=covariant, + contravariant=contravariant) + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + +# 3.7-3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list, _DefaultMixin): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + default=None): + super().__init__([self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + +# 3.7-3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + __class__ = typing._GenericAlias + + # Flag in 3.8. + _special = False + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + +# 3.7-3.9 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not isinstance(parameters[-1], ParamSpec): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = tuple(typing._type_check(p, msg) for p in parameters) + return _ConcatenateGenericAlias(self, parameters) + + +# 3.10+ +if hasattr(typing, 'Concatenate'): + Concatenate = typing.Concatenate + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_TypeAliasForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) +# 3.7-8 +else: + class _ConcatenateForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateForm( + 'Concatenate', + doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """) + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeGuardForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeGuardForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.7-3.8 +else: + class _TypeGuardForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeGuard = _TypeGuardForm( + 'TypeGuard', + doc="""Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """) + + +# Vendored from cpython typing._SpecialFrom +class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +if hasattr(typing, "LiteralString"): + LiteralString = typing.LiteralString +else: + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Self"): + Self = typing.Self +else: + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Never"): + Never = typing.Never +else: + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, 'Required'): + Required = typing.Required + NotRequired = typing.NotRequired +elif sys.version_info[:2] >= (3, 9): + class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: + class _RequiredForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Required = _RequiredForm( + 'Required', + doc="""A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """) + NotRequired = _RequiredForm( + 'NotRequired', + doc="""A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """) + + +if hasattr(typing, "Unpack"): # 3.11+ + Unpack = typing.Unpack +elif sys.version_info[:2] >= (3, 9): + class _UnpackSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @_UnpackSpecialForm + def Unpack(self, parameters): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +else: + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm( + 'Unpack', + doc="""A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + + +if hasattr(typing, "TypeVarTuple"): # 3.11+ + + # Add default Parameter - PEP 696 + class TypeVarTuple(typing.TypeVarTuple, _DefaultMixin, _root=True): + """Type variable tuple.""" + + def __init__(self, name, *, default=None): + super().__init__(name) + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + +else: + class TypeVarTuple(_DefaultMixin): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* + type such as ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, default=None): + self.__name__ = name + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + +if hasattr(typing, "reveal_type"): + reveal_type = typing.reveal_type +else: + def reveal_type(__obj: T) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr) + return __obj + + +if hasattr(typing, "assert_never"): + assert_never = typing.assert_never +else: + def assert_never(__arg: Never) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + raise AssertionError("Expected code to be unreachable") + + +if hasattr(typing, 'dataclass_transform'): + dataclass_transform = typing.dataclass_transform +else: + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_specifiers: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + **kwargs: typing.Any, + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_specifiers`` specifies a static list of supported classes + or functions that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "field_specifiers": field_specifiers, + "kwargs": kwargs, + } + return cls_or_fn + return decorator + + +if hasattr(typing, "override"): + override = typing.override +else: + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(__arg: _F) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: ... + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + See PEP 698 for details. + + """ + return __arg + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + typing._collect_type_vars = _collect_type_vars + typing._check_generic = _check_generic + + +# Backport typing.NamedTuple as it exists in Python 3.11. +# In 3.11, the ability to define generic `NamedTuple`s was supported. +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. +if sys.version_info >= (3, 11): + NamedTuple = typing.NamedTuple +else: + def _caller(): + try: + return sys._getframe(2).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): # For platforms without _getframe() + return None + + def _make_nmtuple(name, types, module, defaults=()): + fields = [n for n, t in types] + annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") + for n, t in types} + nm_tpl = collections.namedtuple(name, fields, + defaults=defaults, module=module) + nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations + # The `_field_types` attribute was removed in 3.9; + # in earlier versions, it is the same as the `__annotations__` attribute + if sys.version_info < (3, 9): + nm_tpl._field_types = annotations + return nm_tpl + + _prohibited_namedtuple_fields = typing._prohibited + _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) + + class _NamedTupleMeta(type): + def __new__(cls, typename, bases, ns): + assert _NamedTuple in bases + for base in bases: + if base is not _NamedTuple and base is not typing.Generic: + raise TypeError( + 'can only inherit from a NamedTuple type and Generic') + bases = tuple(tuple if base is _NamedTuple else base for base in bases) + types = ns.get('__annotations__', {}) + default_names = [] + for field_name in types: + if field_name in ns: + default_names.append(field_name) + elif default_names: + raise TypeError(f"Non-default namedtuple field {field_name} " + f"cannot follow default field" + f"{'s' if len(default_names) > 1 else ''} " + f"{', '.join(default_names)}") + nm_tpl = _make_nmtuple( + typename, types.items(), + defaults=[ns[n] for n in default_names], + module=ns['__module__'] + ) + nm_tpl.__bases__ = bases + if typing.Generic in bases: + class_getitem = typing.Generic.__class_getitem__.__func__ + nm_tpl.__class_getitem__ = classmethod(class_getitem) + # update from user namespace without overriding special namedtuple attributes + for key in ns: + if key in _prohibited_namedtuple_fields: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special_namedtuple_fields and key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + if typing.Generic in bases: + nm_tpl.__init_subclass__() + return nm_tpl + + def NamedTuple(__typename, __fields=None, **kwargs): + if __fields is None: + __fields = kwargs.items() + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + return _make_nmtuple(__typename, __fields, module=_caller()) + + NamedTuple.__doc__ = typing.NamedTuple.__doc__ + _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) + + # On 3.8+, alter the signature so that it matches typing.NamedTuple. + # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7, + # so just leave the signature as it is on 3.7. + if sys.version_info >= (3, 8): + NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)' + + def _namedtuple_mro_entries(bases): + assert NamedTuple in bases + return (_NamedTuple,) + + NamedTuple.__mro_entries__ = _namedtuple_mro_entries diff --git a/simple_functions.egg-info/PKG-INFO b/simple_functions.egg-info/PKG-INFO new file mode 100644 index 0000000..b0fdb16 --- /dev/null +++ b/simple_functions.egg-info/PKG-INFO @@ -0,0 +1,7 @@ +Metadata-Version: 2.1 +Name: simple-functions +Version: 0.1.dev9+ga9600c5.d20221018 +Summary: Environment for playing with CI. +Home-page: https://github.com/acse-2020 +Author: Imperial College London +Author-email: rhodri.nelson@imperial.ac.uk diff --git a/simple_functions.egg-info/SOURCES.txt b/simple_functions.egg-info/SOURCES.txt new file mode 100644 index 0000000..70eeb0c --- /dev/null +++ b/simple_functions.egg-info/SOURCES.txt @@ -0,0 +1,13 @@ +README.md +environment.yml +requirements.txt +setup.py +.github/workflows/flake8.yml +.github/workflows/pytest-unit-tests.yml +simple_functions/__init__.py +simple_functions/functions1.py +simple_functions.egg-info/PKG-INFO +simple_functions.egg-info/SOURCES.txt +simple_functions.egg-info/dependency_links.txt +simple_functions.egg-info/top_level.txt +tests/test_simple_functions.py \ No newline at end of file diff --git a/simple_functions.egg-info/dependency_links.txt b/simple_functions.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/simple_functions.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/simple_functions.egg-info/top_level.txt b/simple_functions.egg-info/top_level.txt new file mode 100644 index 0000000..a4ba85f --- /dev/null +++ b/simple_functions.egg-info/top_level.txt @@ -0,0 +1 @@ +simple_functions diff --git a/simple_functions/__pycache__/__init__.cpython-310.pyc b/simple_functions/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..75f02a2 Binary files /dev/null and b/simple_functions/__pycache__/__init__.cpython-310.pyc differ diff --git a/simple_functions/__pycache__/__init__.cpython-39.pyc b/simple_functions/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..09f0825 Binary files /dev/null and b/simple_functions/__pycache__/__init__.cpython-39.pyc differ diff --git a/simple_functions/__pycache__/functions1.cpython-310.pyc b/simple_functions/__pycache__/functions1.cpython-310.pyc new file mode 100644 index 0000000..c3d7eee Binary files /dev/null and b/simple_functions/__pycache__/functions1.cpython-310.pyc differ diff --git a/simple_functions/__pycache__/functions1.cpython-39.pyc b/simple_functions/__pycache__/functions1.cpython-39.pyc new file mode 100644 index 0000000..3dd5f15 Binary files /dev/null and b/simple_functions/__pycache__/functions1.cpython-39.pyc differ diff --git a/simple_functions/functions1.py b/simple_functions/functions1.py index 8c63a4d..c189eac 100644 --- a/simple_functions/functions1.py +++ b/simple_functions/functions1.py @@ -1,5 +1,5 @@ - -__all__ = ['my_sum'] +from functools import cache +__all__ = ['my_sum', 'factorial'] def my_sum(iterable): @@ -7,3 +7,8 @@ def my_sum(iterable): for i in iterable: tot += i return tot + + +@cache +def factorial(n): + return n * factorial(n-1) if n else 1 diff --git a/tests/__pycache__/test_simple_functions.cpython-310-pytest-7.1.2.pyc b/tests/__pycache__/test_simple_functions.cpython-310-pytest-7.1.2.pyc new file mode 100644 index 0000000..24c573e Binary files /dev/null and b/tests/__pycache__/test_simple_functions.cpython-310-pytest-7.1.2.pyc differ diff --git a/tests/__pycache__/test_simple_functions.cpython-39-pytest-7.1.1.pyc b/tests/__pycache__/test_simple_functions.cpython-39-pytest-7.1.1.pyc new file mode 100644 index 0000000..6369fec Binary files /dev/null and b/tests/__pycache__/test_simple_functions.cpython-39-pytest-7.1.1.pyc differ diff --git a/tests/test_simple_functions.py b/tests/test_simple_functions.py index 5a03b52..48507a6 100644 --- a/tests/test_simple_functions.py +++ b/tests/test_simple_functions.py @@ -1,6 +1,8 @@ + import pytest from simple_functions import my_sum +from simple_functions import factorial class TestSimpleFunctions(object): @@ -14,3 +16,13 @@ def test_my_add(self, iterable, expected): '''Test our add function''' isum = my_sum(iterable) assert isum == expected + + @pytest.mark.parametrize('number, expected', [ + (5, 120), + (3, 6), + (1, 1) + ]) + def test_factorial(self, number, expected): + '''Test our factorial function''' + answer = factorial(number) + assert answer == expected