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-6.3.2-py3.9.egg/EGG-INFO/LICENSE b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..89de354 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-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-6.3.2-py3.9.egg/EGG-INFO/PKG-INFO b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..e917b92 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,639 @@ +Metadata-Version: 2.1 +Name: setuptools-scm +Version: 6.3.2 +Summary: the blessed package to manage your versions by scm tags +Home-page: https://github.com/pypa/setuptools_scm/ +Author: Ronny Pfannschmidt +Author-email: opensource@ronnypfannschmidt.de +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Version Control +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: Utilities +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: packaging (>=20.0) +Requires-Dist: setuptools +Requires-Dist: tomli (>=1.0.0) +Provides-Extra: toml +Requires-Dist: setuptools (>=42) ; extra == 'toml' +Requires-Dist: tomli (>=1.0.0) ; extra == 'toml' + +setuptools_scm +============== + +``setuptools_scm`` handles managing your Python package versions +in SCM metadata instead of declaring them as the version argument +or in a SCM managed file. + +Additionally ``setuptools_scm`` provides setuptools with a list of files that are managed by the SCM +(i.e. it automatically adds all of the SCM-managed files to the sdist). +Unwanted files must be excluded by discarding them via ``MANIFEST.in``. + +``setuptools_scm`` support the following scm out of the box: + +* git +* mercurial + + + +.. image:: https://github.com/pypa/setuptools_scm/workflows/python%20tests+artifacts+release/badge.svg + :target: https://github.com/pypa/setuptools_scm/actions + +.. image:: https://tidelift.com/badges/package/pypi/setuptools-scm + :target: https://tidelift.com/subscription/pkg/pypi-setuptools-scm?utm_source=pypi-setuptools-scm&utm_medium=readme + + +``pyproject.toml`` usage +------------------------ + +The preferred way to configure ``setuptools_scm`` is to author +settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. + +This feature requires Setuptools 42 or later, released in Nov, 2019. +If your project needs to support build from sdist on older versions +of Setuptools, you will need to also implement the ``setup.py usage`` +for those legacy environments. + +First, ensure that ``setuptools_scm`` is present during the project's +built step by specifying it as one of the build requirements. + +.. code:: toml + + # pyproject.toml + [build-system] + requires = ["setuptools>=45", "wheel", "setuptools_scm>=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" + + +``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. + +Notable Plugins +--------------- + +`setuptools_scm_git_archive `_ + Provides partial support for obtaining versions from git archives that + belong to tagged versions. The only reason for not including it in + ``setuptools_scm`` itself is Git/GitHub not supporting sufficient metadata + for untagged/followup commits, which is preventing a consistent UX. + + +Default versioning scheme +------------------------- + +In the standard configuration ``setuptools_scm`` takes a look at three things: + +1. latest tag (with a version number) +2. the distance to this tag (e.g. number of revisions since latest tag) +3. workdir state (e.g. uncommitted changes since latest tag) + +and uses roughly the following logic to render the version: + +no distance and clean: + ``{tag}`` +distance and clean: + ``{next_version}.dev{distance}+{scm letter}{revision hash}`` +no distance and not clean: + ``{tag}+dYYYYMMDD`` +distance and not clean: + ``{next_version}.dev{distance}+{scm letter}{revision hash}.dYYYYMMDD`` + +The next version is calculated by adding ``1`` to the last numeric component of +the tag. + + +For Git projects, the version relies on `git describe `_, +so you will see an additional ``g`` prepended to the ``{revision hash}``. + +Semantic Versioning (SemVer) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to the default behavior it's necessary to always include a +patch version (the ``3`` in ``1.2.3``), or else the automatic guessing +will increment the wrong part of the SemVer (e.g. tag ``2.0`` results in +``2.1.devX`` instead of ``2.0.1.devX``). So please make sure to tag +accordingly. + +.. note:: + + Future versions of ``setuptools_scm`` will switch to `SemVer + `_ by default hiding the the old behavior as an + configurable option. + + +Builtin mechanisms for obtaining version numbers +------------------------------------------------ + +1. the SCM itself (git/hg) +2. ``.hg_archival`` files (mercurial archives) +3. ``PKG-INFO`` + +.. note:: + + Git archives are not supported due to Git shortcomings + + +File finders hook makes most of MANIFEST.in unnecessary +------------------------------------------------------- + +``setuptools_scm`` implements a `file_finders +`_ +entry point which returns all files tracked by your SCM. This eliminates +the need for a manually constructed ``MANIFEST.in`` in most cases where this +would be required when not using ``setuptools_scm``, namely: + +* To ensure all relevant files are packaged when running the ``sdist`` command. + +* When using `include_package_data `_ + to include package data as part of the ``build`` or ``bdist_wheel``. + +``MANIFEST.in`` may still be used: anything defined there overrides the hook. +This is mostly useful to exclude files tracked in your SCM from packages, +although in principle it can be used to explicitly include non-tracked files +too. + + +Configuration parameters +------------------------ + +In order to configure the way ``use_scm_version`` works you can provide +a mapping with options instead of a boolean value. + +The currently supported configuration keys are: + +:root: + Relative path to cwd, used for finding the SCM root; defaults to ``.`` + +:version_scheme: + Configures how the local version number is constructed; either an + entrypoint name or a callable. + +:local_scheme: + Configures how the local component of the version is constructed; either an + entrypoint name or a callable. + +:write_to: + A path to a file that gets replaced with a file containing the current + version. It is ideal for creating a ``_version.py`` file within the + package, typically used to avoid using `pkg_resources.get_distribution` + (which adds some overhead). + + .. warning:: + + Only files with :code:`.py` and :code:`.txt` extensions have builtin + templates, for other file types it is necessary to provide + :code:`write_to_template`. + +:write_to_template: + A newstyle format string that is given the current version as + the ``version`` keyword argument for formatting. + +:relative_to: + A file from which the root can be resolved. + Typically called by a script or module that is not in the root of the + repository to point ``setuptools_scm`` at the root of the repository by + supplying ``__file__``. + +:tag_regex: + A Python regex string to extract the version part from any SCM tag. + The regex needs to contain 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-6.3.2-py3.9.egg/EGG-INFO/RECORD b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..d148bc7 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/RECORD @@ -0,0 +1,23 @@ +setuptools_scm/__init__.py,sha256=zWUSg4yZvzIQ6F5s6kLhYoqLyeP70TaW2ZWTeu0Upqg,6274 +setuptools_scm/__main__.py,sha256=N1ovM8yVFiMh55m0JxeKpyTxky9RX_jsMNY8Bd6TUsw,295 +setuptools_scm/_version_cls.py,sha256=OqpnIzcegf4mPaiMy6QLHflwoxiApI8Qo0zkZe2fom8,1428 +setuptools_scm/config.py,sha256=jqMkPKLhagFfDkZc7WxcNBZoq9R3FTFmW9VDTivKYqc,6733 +setuptools_scm/discover.py,sha256=rncA7Go947oa3PRoMA1306GRXxd4Q23DRFvdbXBQT34,1557 +setuptools_scm/file_finder.py,sha256=JHCd6G6m3Df4iwUgszgylKfYNDEJWM2v8v6RuldfiY8,2554 +setuptools_scm/file_finder_git.py,sha256=fyTvB3qkYvrIv_K6j0V7Pxo3--MmW6qHRnoN4y50XjM,3244 +setuptools_scm/file_finder_hg.py,sha256=v03QeJOnHJsIYR-JCy-4ntTthg0zzDGhvmFMMKGz3l0,1492 +setuptools_scm/git.py,sha256=PrtlB917DAKSrahkgzx0TiYZq1QN1MkiHmTO0g4wskU,6204 +setuptools_scm/hacks.py,sha256=Y1tBCq3PsrovBSxgQBouEHW_iW7N_E5ADN7w0OiHrr8,1316 +setuptools_scm/hg.py,sha256=XZQQfIsQC8o6r_S-vaaT4U-TSkOQkYyxEbk5dGGQhkQ,5074 +setuptools_scm/hg_git.py,sha256=QnFvA2WcykTaa3p8sE33_IIPo-RJQ-sr9xYEvYAJHsk,3499 +setuptools_scm/integration.py,sha256=WxSJcsa4oE76wFZoJU3fJEpLFFKVWjRe8a1c-y7tlWM,2732 +setuptools_scm/scm_workdir.py,sha256=k0w7Cct1cHTnFM3FWY0gNg1i8RKbDUNJf6YjABHnKBk,322 +setuptools_scm/utils.py,sha256=32_SlZLAXc9KqUEOBExvXhU2fDyDk_A0DvmQ-4AMr2s,3963 +setuptools_scm/version.py,sha256=NqB0rGfmEgVkUc-m7nPyMuUb7V7fBVMqtmYGkVBcXWs,13818 +setuptools_scm-6.3.2.dist-info/LICENSE,sha256=iYB6zyMJvShfAzQE7nhYFgLzzZuBmhasLw5fYP9KRz4,1023 +setuptools_scm-6.3.2.dist-info/METADATA,sha256=tZATPwhkHQ_8bRhfJaCI2zbgSd3TiMdnwCD_JODppKs,22252 +setuptools_scm-6.3.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +setuptools_scm-6.3.2.dist-info/entry_points.txt,sha256=LzLFBv9B2emlz6AljQ8nhREGOeg8BUZAZq0am6uY9j4,1440 +setuptools_scm-6.3.2.dist-info/top_level.txt,sha256=kiu-91q3_rJLUoc2wl8_lC4cIlpgtgdD_4NaChF4hOA,15 +setuptools_scm-6.3.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +setuptools_scm-6.3.2.dist-info/RECORD,, diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/WHEEL b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/entry_points.txt b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/entry_points.txt new file mode 100644 index 0000000..88df813 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,37 @@ +[distutils.setup_keywords] +use_scm_version = setuptools_scm.integration:version_keyword + +[setuptools.file_finders] +setuptools_scm = setuptools_scm.integration:find_files + +[setuptools.finalize_distribution_options] +setuptools_scm = setuptools_scm.integration:infer_version + +[setuptools_scm.files_command] +.git = setuptools_scm.file_finder_git:git_find_files +.hg = setuptools_scm.file_finder_hg:hg_find_files + +[setuptools_scm.local_scheme] +dirty-tag = setuptools_scm.version:get_local_dirty_tag +no-local-version = setuptools_scm.version:get_no_local_node +node-and-date = setuptools_scm.version:get_local_node_and_date +node-and-timestamp = setuptools_scm.version:get_local_node_and_timestamp + +[setuptools_scm.parse_scm] +.git = setuptools_scm.git:parse +.hg = setuptools_scm.hg:parse + +[setuptools_scm.parse_scm_fallback] +.hg_archival.txt = setuptools_scm.hg:parse_archival +PKG-INFO = setuptools_scm.hacks:parse_pkginfo +pip-egg-info = setuptools_scm.hacks:parse_pip_egg_info +setup.py = setuptools_scm.hacks:fallback_version + +[setuptools_scm.version_scheme] +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-6.3.2-py3.9.egg/EGG-INFO/requires.txt b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/requires.txt new file mode 100644 index 0000000..bd01a6c --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/requires.txt @@ -0,0 +1,6 @@ +packaging>=20.0 +setuptools +tomli>=1.0.0 + +[toml] +setuptools>=42 diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/top_level.txt b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000..cba8d88 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +setuptools_scm diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/zip-safe b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/EGG-INFO/zip-safe @@ -0,0 +1 @@ + diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__init__.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__init__.py new file mode 100644 index 0000000..b4f86ea --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__init__.py @@ -0,0 +1,212 @@ +""" +:copyright: 2010-2015 by Ronny Pfannschmidt +:license: MIT +""" +import os +import warnings + +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 + +PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" +PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" + +TEMPLATES = { + ".py": """\ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = {version!r} +version_tuple = {version_tuple!r} +""", + ".txt": "{version}", +} + + +def version_from_scm(root): + warnings.warn( + "version_from_scm is deprecated please use get_version", + category=DeprecationWarning, + stacklevel=2, + ) + config = Configuration(root=root) + # TODO: Is it API? + return _version_from_entrypoints(config) + + +def _call_entrypoint_fn(root, config, fn): + if function_has_arg(fn, "config"): + return fn(root, config=config) + else: + warnings.warn( + 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=False): + if fallback: + entrypoint = "setuptools_scm.parse_scm_fallback" + root = config.fallback_root + else: + entrypoint = "setuptools_scm.parse_scm" + root = config.absolute_root + + for ep in iter_matching_entrypoints(root, entrypoint, config): + version = _call_entrypoint_fn(root, config, ep.load()) + trace(ep, version) + if version: + return version + + +def dump_version(root, version, write_to, template=None): + assert isinstance(version, str) + if not write_to: + return + target = os.path.normpath(os.path.join(root, write_to)) + ext = os.path.splitext(target)[1] + template = template or TEMPLATES.get(ext) + + if template is None: + raise ValueError( + "bad file format: '{}' (of {}) \nonly *.txt and *.py are supported".format( + os.path.splitext(target)[1], target + ) + ) + + parsed_version = Version(version) + version_fields = 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,) + + with open(target, "w") as fp: + fp.write(template.format(version=version, version_tuple=tuple(version_fields))) + + +def _do_parse(config): + + trace("dist name:", config.dist_name) + if config.dist_name is not None: + pretended = os.environ.get( + PRETEND_KEY_NAMED.format(name=config.dist_name.upper()) + ) + else: + pretended = None + + if pretended is None: + pretended = os.environ.get(PRETEND_KEY) + + if pretended: + # we use meta here since the pretended version + # must adhere to the pep to begin with + return meta(tag=pretended, preformatted=True, config=config) + + if config.parse: + parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) + if isinstance(parse_result, str): + raise TypeError( + "version parse result was a string\nplease return a parsed version" + ) + version = parse_result or _version_from_entrypoints(config, fallback=True) + else: + # include fallbacks after dropping them from the main entrypoint + version = _version_from_entrypoints(config) or _version_from_entrypoints( + config, fallback=True + ) + + if version: + return version + + raise LookupError( + "setuptools-scm was unable to detect version for %r.\n\n" + "Make sure you're either building from a fully intact git repository " + "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a " + "git checkout without the .git folder) don't contain the necessary " + "metadata and will not work.\n\n" + "For example, if you're using pip, instead of " + "https://github.com/user/proj/archive/master.zip " + "use git+https://github.com/user/proj.git#egg=proj" % config.absolute_root + ) + + +def get_version( + root=".", + version_scheme=DEFAULT_VERSION_SCHEME, + local_scheme=DEFAULT_LOCAL_SCHEME, + write_to=None, + write_to_template=None, + relative_to=None, + tag_regex=DEFAULT_TAG_REGEX, + parentdir_prefix_version=None, + fallback_version=None, + fallback_root=".", + parse=None, + git_describe_command=None, + dist_name=None, + version_cls=None, + normalize=True, + search_parent_directories=False, +): + """ + If supplied, relative_to should be a file from which root may + be resolved. Typically called by a script or module that is not + in the root of the repository to direct setuptools_scm to the + root of the repository by supplying ``__file__``. + """ + + config = Configuration(**locals()) + return _get_version(config) + + +def _get_version(config): + parsed_version = _do_parse(config) + + if parsed_version: + version_string = format_version( + parsed_version, + version_scheme=config.version_scheme, + local_scheme=config.local_scheme, + ) + dump_version( + root=config.root, + version=version_string, + write_to=config.write_to, + template=config.write_to_template, + ) + + return version_string + + +# Public API +__all__ = [ + "get_version", + "dump_version", + "version_from_scm", + "Configuration", + "DEFAULT_VERSION_SCHEME", + "DEFAULT_LOCAL_SCHEME", + "DEFAULT_TAG_REGEX", + "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-6.3.2-py3.9.egg/setuptools_scm/__main__.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__main__.py new file mode 100644 index 0000000..f3377b0 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__main__.py @@ -0,0 +1,15 @@ +import sys + +from setuptools_scm import get_version +from setuptools_scm.integration import find_files + + +def main() -> None: + print("Guessed Version", get_version()) + if "ls" in sys.argv: + for fname in find_files("."): + print(fname) + + +if __name__ == "__main__": + main() diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/__init__.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..62508f5 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/__init__.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/_version_cls.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/_version_cls.cpython-39.pyc new file mode 100644 index 0000000..c65129f Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/_version_cls.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/config.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/config.cpython-39.pyc new file mode 100644 index 0000000..dd158bb Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/config.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/discover.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/discover.cpython-39.pyc new file mode 100644 index 0000000..ab011c0 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/discover.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/file_finder.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/file_finder.cpython-39.pyc new file mode 100644 index 0000000..194a67f Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/file_finder.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/file_finder_git.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/file_finder_git.cpython-39.pyc new file mode 100644 index 0000000..1956c88 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/file_finder_git.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/git.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/git.cpython-39.pyc new file mode 100644 index 0000000..0c32f39 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/git.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/integration.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/integration.cpython-39.pyc new file mode 100644 index 0000000..5f6f677 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/integration.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/scm_workdir.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/scm_workdir.cpython-39.pyc new file mode 100644 index 0000000..f7d0686 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/scm_workdir.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/utils.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000..8a0c7c0 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/utils.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/version.cpython-39.pyc b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/version.cpython-39.pyc new file mode 100644 index 0000000..2fa2d29 Binary files /dev/null and b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/__pycache__/version.cpython-39.pyc differ diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/_version_cls.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/_version_cls.py new file mode 100644 index 0000000..0cefb26 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/_version_cls.py @@ -0,0 +1,49 @@ +try: + from packaging.version import Version + + assert hasattr(Version, "release") +except ImportError: + from pkg_resources._vendor.packaging.version import Version as SetuptoolsVersion + + try: + SetuptoolsVersion.release + Version = SetuptoolsVersion + except AttributeError: + + class Version(SetuptoolsVersion): # type: ignore + @property + def release(self): + return self._version.release + + @property + def dev(self): + return self._version.dev + + @property + def local(self): + return self._version.local + + +class NonNormalizedVersion(Version): + """A non-normalizing version handler. + + You can use this class to preserve version verification but skip normalization. + For example you can use this to avoid git release candidate version tags + ("1.0.0-rc1") to be normalized to "1.0.0rc1". Only use this if you fully + trust the version tags. + """ + + def __init__(self, version): + # parse and validate using parent + super().__init__(version) + + # store raw for str + self._raw_version = version + + def __str__(self): + # return the non-normalized version (parent returns the normalized) + return self._raw_version + + def __repr__(self): + # same pattern as parent + return f"" diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/config.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/config.py new file mode 100644 index 0000000..6bcf446 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/config.py @@ -0,0 +1,212 @@ +""" configuration """ +import os +import re +import warnings + +from ._version_cls import NonNormalizedVersion +from ._version_cls import Version +from .utils import trace + +DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" +DEFAULT_VERSION_SCHEME = "guess-next-dev" +DEFAULT_LOCAL_SCHEME = "node-and-date" + + +def _check_tag_regex(value): + if not value: + value = DEFAULT_TAG_REGEX + regex = re.compile(value) + + group_names = regex.groupindex.keys() + if regex.groups == 0 or (regex.groups > 1 and "version" not in group_names): + warnings.warn( + "Expected tag_regex to contain a single match group or a group named" + " 'version' to identify the version part of any tag." + ) + + return regex + + +def _check_absolute_root(root, relative_to): + trace("l", repr(locals())) + if relative_to: + if os.path.isabs(root) and not root.startswith(relative_to): + warnings.warn( + "absolute root path '%s' overrides relative_to '%s'" + % (root, relative_to) + ) + if os.path.isdir(relative_to): + warnings.warn( + "relative_to is expected to be a file," + " its the directory %r\n" + "assuming the parent directory was passed" % (relative_to,) + ) + trace("dir", relative_to) + root = os.path.join(relative_to, root) + else: + trace("file", relative_to) + root = os.path.join(os.path.dirname(relative_to), root) + return os.path.abspath(root) + + +def _lazy_tomli_load(data): + from tomli import loads + + return loads(data) + + +class Configuration: + """Global configuration model""" + + def __init__( + self, + relative_to=None, + root=".", + version_scheme=DEFAULT_VERSION_SCHEME, + local_scheme=DEFAULT_LOCAL_SCHEME, + write_to=None, + write_to_template=None, + tag_regex=DEFAULT_TAG_REGEX, + parentdir_prefix_version=None, + fallback_version=None, + fallback_root=".", + parse=None, + git_describe_command=None, + dist_name=None, + version_cls=None, + normalize=True, + search_parent_directories=False, + ): + # TODO: + self._relative_to = relative_to + self._root = "." + + self.root = root + self.version_scheme = version_scheme + self.local_scheme = local_scheme + self.write_to = write_to + self.write_to_template = write_to_template + self.parentdir_prefix_version = parentdir_prefix_version + self.fallback_version = fallback_version + self.fallback_root = fallback_root + self.parse = parse + self.tag_regex = tag_regex + self.git_describe_command = git_describe_command + self.dist_name = dist_name + self.search_parent_directories = search_parent_directories + self.parent = None + + if not normalize: + # `normalize = False` means `version_cls = NonNormalizedVersion` + if version_cls is not None: + raise ValueError( + "Providing a custom `version_cls` is not permitted when " + "`normalize=False`" + ) + self.version_cls = NonNormalizedVersion + else: + # Use `version_cls` if provided, default to packaging or pkg_resources + if version_cls is None: + version_cls = Version + elif isinstance(version_cls, str): + try: + # Not sure this will work in old python + import importlib + + pkg, cls_name = version_cls.rsplit(".", 1) + version_cls_host = importlib.import_module(pkg) + version_cls = getattr(version_cls_host, cls_name) + except: # noqa + raise ValueError(f"Unable to import version_cls='{version_cls}'") + self.version_cls = version_cls + + @property + def fallback_root(self): + return self._fallback_root + + @fallback_root.setter + def fallback_root(self, value): + self._fallback_root = os.path.abspath(value) + + @property + def absolute_root(self): + return self._absolute_root + + @property + def relative_to(self): + return self._relative_to + + @relative_to.setter + def relative_to(self, value): + self._absolute_root = _check_absolute_root(self._root, value) + self._relative_to = value + trace("root", repr(self._absolute_root)) + trace("relative_to", repr(value)) + + @property + def root(self): + return self._root + + @root.setter + def root(self, value): + self._absolute_root = _check_absolute_root(value, self._relative_to) + self._root = value + trace("root", repr(self._absolute_root)) + trace("relative_to", repr(self._relative_to)) + + @property + def tag_regex(self): + return self._tag_regex + + @tag_regex.setter + def tag_regex(self, value): + self._tag_regex = _check_tag_regex(value) + + @classmethod + def from_file( + cls, + name: str = "pyproject.toml", + dist_name=None, # type: str | None + _load_toml=_lazy_tomli_load, + ): + """ + Read Configuration from pyproject.toml (or similar). + Raises exceptions when file is not found or toml is + not installed or the file has invalid format or does + not contain the [tool.setuptools_scm] section. + """ + + with open(name, encoding="UTF-8") as strm: + data = strm.read() + defn = _load_toml(data) + try: + section = defn.get("tool", {})["setuptools_scm"] + except LookupError as e: + raise LookupError( + f"{name} does not contain a tool.setuptools_scm section" + ) from e + if "dist_name" in section: + if dist_name is None: + dist_name = section.pop("dist_name") + else: + assert dist_name == section["dist_name"] + del section["dist_name"] + if dist_name is None: + if "project" in defn: + # minimal pep 621 support for figuring the pretend keys + dist_name = defn["project"].get("name") + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + + return cls(dist_name=dist_name, **section) + + +def _read_dist_name_from_setup_cfg(): + + # minimal effort to read dist_name off setup.cfg metadata + import configparser + + parser = configparser.ConfigParser() + parser.read(["setup.cfg"]) + dist_name = parser.get("metadata", "name", fallback=None) + return dist_name diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/discover.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/discover.py new file mode 100644 index 0000000..f2aee17 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/discover.py @@ -0,0 +1,58 @@ +import os + +from .config import Configuration +from .utils import iter_entry_points +from .utils import trace + + +def walk_potential_roots(root, search_parents=True): + """ + Iterate though a path and each of its parents. + :param root: File path. + :param search_parents: If ``False`` the parents are not considered. + """ + + if not search_parents: + yield root + return + + tail = root + + while tail: + yield root + root, tail = os.path.split(root) + + +def match_entrypoint(root, name): + """ + Consider a ``root`` as entry-point. + :param root: File path. + :param name: Subdirectory name. + :return: ``True`` if a subdirectory ``name`` exits in ``root``. + """ + + if os.path.exists(os.path.join(root, name)): + if not os.path.isabs(name): + return True + trace("ignoring bad ep", name) + + return False + + +def iter_matching_entrypoints(root, entrypoint, config: Configuration): + """ + Consider different entry-points in ``root`` and optionally its parents. + :param root: File path. + :param entrypoint: Entry-point to consider. + :param config: Configuration, + read ``search_parent_directories``, write found parent to ``parent``. + """ + + trace("looking for ep", entrypoint, root) + + for wd in walk_potential_roots(root, config.search_parent_directories): + for ep in iter_entry_points(entrypoint): + if match_entrypoint(wd, ep.name): + trace("found ep", ep, "in", wd) + config.parent = wd + yield ep diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder.py new file mode 100644 index 0000000..4666024 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder.py @@ -0,0 +1,70 @@ +import os + +from .utils import trace + + +def scm_find_files(path, scm_files, scm_dirs): + """ setuptools compatible file finder that follows symlinks + + - path: the root directory from which to search + - scm_files: set of scm controlled files and symlinks + (including symlinks to directories) + - scm_dirs: set of scm controlled directories + (including directories containing no scm controlled files) + + scm_files and scm_dirs must be absolute with symlinks resolved (realpath), + with normalized case (normcase) + + Spec here: http://setuptools.readthedocs.io/en/latest/setuptools.html#\ + adding-support-for-revision-control-systems + """ + realpath = os.path.normcase(os.path.realpath(path)) + seen = set() + res = [] + for dirpath, dirnames, filenames in os.walk(realpath, followlinks=True): + # dirpath with symlinks resolved + realdirpath = os.path.normcase(os.path.realpath(dirpath)) + + def _link_not_in_scm(n): + fn = os.path.join(realdirpath, os.path.normcase(n)) + return os.path.islink(fn) and fn not in scm_files + + if realdirpath not in scm_dirs: + # directory not in scm, don't walk it's content + dirnames[:] = [] + continue + if os.path.islink(dirpath) and not os.path.relpath( + realdirpath, realpath + ).startswith(os.pardir): + # a symlink to a directory not outside path: + # we keep it in the result and don't walk its content + res.append(os.path.join(path, os.path.relpath(dirpath, path))) + dirnames[:] = [] + continue + if realdirpath in seen: + # symlink loop protection + dirnames[:] = [] + continue + dirnames[:] = [dn for dn in dirnames if not _link_not_in_scm(dn)] + for filename in filenames: + if _link_not_in_scm(filename): + continue + # dirpath + filename with symlinks preserved + fullfilename = os.path.join(dirpath, filename) + if os.path.normcase(os.path.realpath(fullfilename)) in scm_files: + res.append(os.path.join(path, os.path.relpath(fullfilename, realpath))) + seen.add(realdirpath) + return res + + +def is_toplevel_acceptable(toplevel): + """ """ + if toplevel is None: + return False + + ignored = os.environ.get("SETUPTOOLS_SCM_IGNORE_VCS_ROOTS", "").split(os.pathsep) + ignored = [os.path.normcase(p) for p in ignored] + + trace(toplevel, ignored) + + return toplevel not in ignored diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder_git.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder_git.py new file mode 100644 index 0000000..c6f96d8 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder_git.py @@ -0,0 +1,93 @@ +import logging +import os +import subprocess +import tarfile + +from .file_finder import is_toplevel_acceptable +from .file_finder import scm_find_files +from .utils import do_ex +from .utils import trace + +log = logging.getLogger(__name__) + + +def _git_toplevel(path): + 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, toplevel): + with tarfile.open(fileobj=fd, mode="r|*") as tf: + git_files = set() + git_dirs = {toplevel} + for member in tf.getmembers(): + name = os.path.normcase(member.name).replace("/", os.path.sep) + if member.type == tarfile.DIRTYPE: + git_dirs.add(name) + else: + git_files.add(name) + return git_files, git_dirs + + +def _git_ls_files_and_dirs(toplevel): + # use git archive instead of git ls-file to honor + # export-ignore git attribute + + cmd = ["git", "archive", "--prefix", toplevel + os.path.sep, "HEAD"] + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, cwd=toplevel, stderr=subprocess.DEVNULL + ) + 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 (), () + + +def git_find_files(path=""): + toplevel = _git_toplevel(path) + if not is_toplevel_acceptable(toplevel): + return [] + fullpath = os.path.abspath(os.path.normpath(path)) + if not fullpath.startswith(toplevel): + trace("toplevel mismatch", toplevel, fullpath) + git_files, git_dirs = _git_ls_files_and_dirs(toplevel) + return scm_find_files(path, git_files, git_dirs) diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder_hg.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder_hg.py new file mode 100644 index 0000000..53878c6 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/file_finder_hg.py @@ -0,0 +1,49 @@ +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): + try: + with open(os.devnull, "wb") as devnull: + out = subprocess.check_output( + ["hg", "root"], + cwd=(path or "."), + universal_newlines=True, + stderr=devnull, + ) + return os.path.normcase(os.path.realpath(out.strip())) + except subprocess.CalledProcessError: + # hg returned error, we are not in a mercurial repo + return None + except OSError: + # hg command not found, probably + return None + + +def _hg_ls_files_and_dirs(toplevel): + hg_files = set() + hg_dirs = {toplevel} + out, 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=""): + toplevel = _hg_toplevel(path) + if not is_toplevel_acceptable(toplevel): + return [] + hg_files, hg_dirs = _hg_ls_files_and_dirs(toplevel) + return scm_find_files(path, hg_files, hg_dirs) diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/git.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/git.py new file mode 100644 index 0000000..22e870c --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/git.py @@ -0,0 +1,220 @@ +import os +import warnings +from datetime import date +from datetime import datetime +from os.path import isfile +from os.path import join +from os.path import samefile + +from .config import Configuration +from .scm_workdir import Workdir +from .utils import do_ex +from .utils import require_command +from .utils import trace +from .version import meta + +DEFAULT_DESCRIBE = "git describe --dirty --tags --long --match *[0-9]*" + + +class GitWorkdir(Workdir): + """experimental, may change at any time""" + + COMMAND = "git" + + @classmethod + def from_potential_worktree(cls, wd): + require_command(cls.COMMAND) + wd = os.path.abspath(wd) + real_wd, _, ret = do_ex("git rev-parse --show-prefix", wd) + real_wd = real_wd[:-1] # remove the trailing pathsep + if ret: + return + 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 + + return cls(real_wd) + + def is_dirty(self): + out, _, _ = self.do_ex("git status --porcelain --untracked-files=no") + return bool(out) + + def get_branch(self): + branch, err, ret = self.do_ex("git rev-parse --abbrev-ref HEAD") + if ret: + trace("branch err", branch, err, ret) + branch, err, ret = self.do_ex("git symbolic-ref --short HEAD") + if ret: + trace("branch err (symbolic-ref)", branch, err, ret) + branch = None + return branch + + def get_head_date(self): + timestamp, err, ret = self.do_ex("git log -n 1 HEAD --format=%cI") + if ret: + trace("timestamp err", timestamp, err, ret) + return + # TODO, when dropping python3.6 use fromiso + date_part = timestamp.split("T")[0] + if "%c" in date_part: + trace("git too old -> timestamp is ", timestamp) + return None + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self): + return isfile(join(self.path, ".git/shallow")) + + def fetch_shallow(self): + self.do_ex("git fetch --unshallow") + + def node(self): + node, _, ret = self.do_ex("git rev-parse --verify --quiet HEAD") + if not ret: + return node[:7] + + def count_all_nodes(self): + revs, _, _ = self.do_ex("git rev-list HEAD") + return revs.count("\n") + 1 + + def default_describe(self): + return self.do_ex(DEFAULT_DESCRIBE) + + +def warn_on_shallow(wd): + """experimental, may change at any time""" + if wd.is_shallow(): + warnings.warn(f'"{wd.path}" is shallow and may cause errors') + + +def fetch_on_shallow(wd): + """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): + """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): + """ + Return the working directory (``GitWorkdir``). + """ + + if config.parent: + return GitWorkdir.from_potential_worktree(config.parent) + + if config.search_parent_directories: + return search_parent(config.absolute_root) + + return GitWorkdir.from_potential_worktree(config.absolute_root) + + +def parse(root, describe_command=None, pre_parse=warn_on_shallow, config=None): + """ + :param pre_parse: experimental pre_parse action, may change at any time + """ + if not config: + config = Configuration(root=root) + + wd = get_working_directory(config) + if wd: + return _git_parse_inner( + config, wd, describe_command=describe_command, pre_parse=pre_parse + ) + + +def _git_parse_inner(config, wd, pre_parse=None, describe_command=None): + 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() + + 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): + # 'describe_output' looks e.g. like 'v1.5.0-0-g4060507' or + # 'v1.15.1rc1-37-g9bd1298-dirty'. + + if describe_output.endswith("-dirty"): + dirty = True + describe_output = describe_output[:-6] + else: + dirty = False + + tag, number, node = describe_output.rsplit("-", 2) + number = int(number) + return tag, number, node, dirty + + +def search_parent(dirname): + """ + Walk up the path to find the `.git` directory. + :param dirname: Directory from which to start searching. + """ + + # Code based on: + # https://github.com/gitpython-developers/GitPython/blob/main/git/repo/base.py + + curpath = os.path.abspath(dirname) + + while curpath: + + try: + wd = GitWorkdir.from_potential_worktree(curpath) + except Exception: + wd = None + + if wd is not None: + return wd + + curpath, tail = os.path.split(curpath) + + if not tail: + return None diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hacks.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hacks.py new file mode 100644 index 0000000..849f21f --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hacks.py @@ -0,0 +1,40 @@ +import os + +from .utils import data_from_mime +from .utils import trace +from .version import meta +from .version import tag_to_version + + +def parse_pkginfo(root, config=None): + + pkginfo = os.path.join(root, "PKG-INFO") + trace("pkginfo", pkginfo) + data = data_from_mime(pkginfo) + version = data.get("Version") + if version != "UNKNOWN": + return meta(version, preformatted=True, config=config) + + +def parse_pip_egg_info(root, config=None): + pipdir = os.path.join(root, "pip-egg-info") + if not os.path.isdir(pipdir): + return + items = os.listdir(pipdir) + trace("pip-egg-info", pipdir, items) + if not items: + return + return parse_pkginfo(os.path.join(pipdir, items[0]), config=config) + + +def fallback_version(root, config=None): + if config.parentdir_prefix_version is not None: + _, parent_name = os.path.split(os.path.abspath(root)) + if parent_name.startswith(config.parentdir_prefix_version): + version = tag_to_version( + parent_name[len(config.parentdir_prefix_version) :], config + ) + if version is not None: + return meta(str(version), preformatted=True, config=config) + if config.fallback_version is not None: + return meta(config.fallback_version, preformatted=True, config=config) diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hg.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hg.py new file mode 100644 index 0000000..8166a90 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hg.py @@ -0,0 +1,169 @@ +import os +from pathlib import Path + +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 tag_to_version + + +class HgWorkdir(Workdir): + + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd): + require_command(cls.COMMAND) + root, err, ret = do_ex("hg root", wd) + if ret: + return + return cls(root) + + def get_meta(self, config): + + node, tags, bookmark, node_date = self.hg_log( + ".", "{node}\n{tag}\n{bookmark}\n{date|shortdate}" + ).split("\n") + + # TODO: support bookmarks and topics (but nowadays bookmarks are + # mainly used to emulate Git branches, which is already supported with + # the dedicated class GitWorkdirHgClient) + + branch, dirty, dirty_date = self.do( + ["hg", "id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}"] + ).split("\n") + dirty = bool(int(dirty)) + + if dirty: + date = dirty_date + else: + date = node_date + + if all(c == "0" for c in node): + trace("initial node", self.path) + return meta("0.0", config=config, dirty=dirty, branch=branch) + + node = "h" + node[:7] + + tags = tags.split() + if "tip" in tags: + # tip is not a real tag + tags = tags.remove("tip") + + if tags: + tag = tags[0] + tag = tag_to_version(tag) + if tag: + return meta(tag, dirty=dirty, branch=branch, config=config) + + try: + tag = self.get_latest_normalizable_tag() + dist = self.get_distance_revs(tag) + if tag == "null": + tag = "0.0" + dist = int(dist) + 1 + + if self.check_changes_since_tag(tag) or dirty: + return meta( + tag, + distance=dist, + node=node, + dirty=dirty, + branch=branch, + config=config, + node_date=date, + ) + else: + return meta(tag, config=config) + + except ValueError: + pass # unpacking failed, old hg + + def hg_log(self, revset, template): + cmd = ["hg", "log", "-r", revset, "-T", template] + return self.do(cmd) + + def get_latest_normalizable_tag(self): + # Gets all tags containing a '.' (see #229) from oldest to newest + outlines = self.hg_log( + revset="ancestors(.) and tag('re:\\.')", + template="{tags}{if(tags, '\n', '')}", + ).split() + if not outlines: + return "null" + tag = outlines[-1].split()[-1] + return tag + + def get_distance_revs(self, rev1, rev2="."): + revset = f"({rev1}::{rev2})" + out = self.hg_log(revset, ".") + return len(out) - 1 + + def check_changes_since_tag(self, tag): + + if tag == "0.0": + return True + + revset = ( + "(branch(.)" # look for revisions in this branch only + f" and tag({tag!r})::." # after the last tag + # ignore commits that only modify .hgtags and nothing else: + " and (merge() or file('re:^(?!\\.hgtags).*$'))" + f" and not tag({tag!r}))" # ignore the tagged commit itself + ) + + return bool(self.hg_log(revset, ".")) + + +def parse(root, config=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 = GitWorkdirHgClient.from_potential_worktree(root) + if wd: + return _git_parse_inner(config, wd) + + wd = HgWorkdir.from_potential_worktree(config.absolute_root) + + if wd is None: + return + + return wd.get_meta(config) + + +def archival_to_version(data, config: "Configuration | None" = None): + trace("data", data) + node = data.get("node", "")[:12] + if node: + node = "h" + node + if "tag" in data: + return meta(data["tag"], config=config) + elif "latesttag" in data: + return meta( + data["latesttag"], + distance=data["latesttagdistance"], + node=node, + config=config, + ) + else: + return meta("0.0", node=node, config=config) + + +def parse_archival(root, config=None): + archival = os.path.join(root, ".hg_archival.txt") + data = data_from_mime(archival) + return archival_to_version(data, config=config) diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hg_git.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hg_git.py new file mode 100644 index 0000000..b871a39 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/hg_git.py @@ -0,0 +1,133 @@ +import os +from datetime import datetime + +from .git import GitWorkdir +from .hg import HgWorkdir +from .utils import do_ex +from .utils import require_command +from .utils import trace + + +class GitWorkdirHgClient(GitWorkdir, HgWorkdir): + COMMAND = "hg" + + @classmethod + def from_potential_worktree(cls, wd): + require_command(cls.COMMAND) + root, err, ret = do_ex("hg root", wd) + if ret: + return + return cls(root) + + def is_dirty(self): + out, _, _ = self.do_ex("hg id -T '{dirty}'") + return bool(out) + + def get_branch(self): + branch, err, ret = self.do_ex("hg id -T {bookmarks}") + if ret: + trace("branch err", branch, err, ret) + return + return branch + + def get_head_date(self): + date_part, err, ret = self.do_ex("hg log -r . -T {shortdate(date)}") + if ret: + trace("head date err", date_part, err, ret) + return + return datetime.strptime(date_part, r"%Y-%m-%d").date() + + def is_shallow(self): + return False + + def fetch_shallow(self): + pass + + def get_hg_node(self): + node, _, ret = self.do_ex("hg log -r . -T {node}") + if not ret: + return node + + def _hg2git(self, hg_node): + git_node = None + with open(os.path.join(self.path, ".hg/git-mapfile")) as file: + for line in file: + if hg_node in line: + git_node, hg_node = line.split() + break + return git_node + + def node(self): + hg_node = self.get_hg_node() + if hg_node is None: + return + + git_node = self._hg2git(hg_node) + + if git_node is None: + # trying again after hg -> git + self.do_ex("hg gexport") + git_node = self._hg2git(hg_node) + + if git_node is None: + trace("Cannot get git node so we use hg node", hg_node) + + if hg_node == "0" * len(hg_node): + # mimick Git behavior + return None + + return hg_node + + return git_node[:7] + + def count_all_nodes(self): + revs, _, _ = self.do_ex("hg log -r 'ancestors(.)' -T '.'") + return len(revs) + + def default_describe(self): + """ + Tentative to reproduce the output of + + `git describe --dirty --tags --long --match *[0-9]*` + + """ + hg_tags, _, ret = self.do_ex( + [ + "hg", + "log", + "-r", + "(reverse(ancestors(.)) and tag(r're:[0-9]'))", + "-T", + "{tags}{if(tags, ' ', '')}", + ] + ) + if ret: + return None, None, None + hg_tags = hg_tags.split() + + if not hg_tags: + return None, None, None + + git_tags = {} + with open(os.path.join(self.path, ".hg/git-tags")) as file: + for line in file: + node, tag = line.split() + git_tags[tag] = node + + # find the first hg tag which is also a git tag + for tag in hg_tags: + if tag in git_tags: + break + + out, _, ret = self.do_ex(["hg", "log", "-r", f"'{tag}'::.", "-T", "."]) + if ret: + return None, None, None + distance = len(out) - 1 + + node = self.node() + desc = f"{tag}-{distance}-g{node}" + + if self.is_dirty(): + desc += "-dirty" + + return desc, None, 0 diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/integration.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/integration.py new file mode 100644 index 0000000..ad69a3f --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/integration.py @@ -0,0 +1,94 @@ +import os +import warnings + +import setuptools + +from . import _get_version +from .config import _read_dist_name_from_setup_cfg +from .config import Configuration +from .utils import do +from .utils import iter_entry_points +from .utils import trace + + +def _warn_on_old_setuptools(_version=setuptools.__version__): + if int(_version.split(".")[0]) < 45: + warnings.warn( + RuntimeWarning( + f""" +ERROR: setuptools=={_version} is used in combination with setuptools_scm>=6.x + +Your build configuration is incomplete and previously worked by accident! + + +This happens as setuptools is unable to replace itself when a activated build dependency +requires a more recent setuptools version +(it does not respect "setuptools>X" in setup_requires). + + +setuptools>=31 is required for setup.cfg metadata support +setuptools>=42 is required for pyproject.toml configuration support + +Suggested workarounds if applicable: + - preinstalling build dependencies like setuptools_scm before running setup.py + - installing setuptools_scm using the system package manager to ensure consistency + - migrating from the deprecated setup_requires mechanism to pep517/518 + and using a pyproject.toml to declare build dependencies + which are reliably pre-installed before running the build tools +""" + ) + ) + + +_warn_on_old_setuptools() + + +def version_keyword(dist: setuptools.Distribution, keyword, value): + if not value: + return + if value is True: + value = {} + if getattr(value, "__call__", None): + value = value() + assert ( + "dist_name" not in value + ), "dist_name may not be specified in the setup keyword " + + trace( + "version keyword", + vars(dist.metadata), + ) + dist_name = dist.metadata.name # type: str | None + if dist_name is None: + dist_name = _read_dist_name_from_setup_cfg() + config = Configuration(dist_name=dist_name, **value) + dist.metadata.version = _get_version(config) + + +def find_files(path=""): + for ep in iter_entry_points("setuptools_scm.files_command"): + command = ep.load() + if isinstance(command, str): + # this technique is deprecated + res = do(ep.load(), path or ".").splitlines() + else: + res = command(path) + if res: + return res + return [] + + +def infer_version(dist: setuptools.Distribution): + trace( + "finalize hook", + vars(dist.metadata), + ) + dist_name = dist.metadata.name + if not os.path.isfile("pyproject.toml"): + return + try: + config = Configuration.from_file(dist_name=dist_name) + except LookupError as e: + trace(e) + else: + dist.metadata.version = _get_version(config) diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/scm_workdir.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/scm_workdir.py new file mode 100644 index 0000000..142065f --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/scm_workdir.py @@ -0,0 +1,15 @@ +from .utils import do +from .utils import do_ex +from .utils import require_command + + +class Workdir: + def __init__(self, path): + require_command(self.COMMAND) + self.path = path + + def do_ex(self, cmd): + return do_ex(cmd, cwd=self.path) + + def do(self, cmd): + return do(cmd, cwd=self.path) diff --git a/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/utils.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/utils.py new file mode 100644 index 0000000..2e84f87 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/utils.py @@ -0,0 +1,154 @@ +""" +utils +""" +import inspect +import os +import platform +import shlex +import subprocess +import sys +import warnings +from typing import Optional + +DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) +IS_WINDOWS = platform.system() == "Windows" + + +def no_git_env(env): + # adapted from pre-commit + # Too many bugs dealing with environment variables and GIT: + # https://github.com/pre-commit/pre-commit/issues/300 + # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running + # pre-commit hooks + # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE + # while running pre-commit hooks in submodules. + # GIT_DIR: Causes git clone to clone wrong thing + # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit + for k, v in env.items(): + if k.startswith("GIT_"): + trace(k, v) + return { + k: v + for k, v in env.items() + if not k.startswith("GIT_") + or k in ("GIT_EXEC_PATH", "GIT_SSH", "GIT_SSH_COMMAND") + } + + +def trace(*k) -> None: + if DEBUG: + print(*k, file=sys.stderr, flush=True) + + +def ensure_stripped_str(str_or_bytes): + if isinstance(str_or_bytes, str): + return str_or_bytes.strip() + else: + return str_or_bytes.decode("utf-8", "surrogateescape").strip() + + +def _always_strings(env_dict): + """ + On Windows and Python 2, environment dictionaries must be strings + and not unicode. + """ + if IS_WINDOWS: + env_dict.update((key, str(value)) for (key, value) in env_dict.items()) + return env_dict + + +def _popen_pipes(cmd, cwd): + return subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=str(cwd), + env=_always_strings( + dict( + no_git_env(os.environ), + # os.environ, + # try to disable i18n + LC_ALL="C", + LANGUAGE="", + HGPLAIN="1", + ) + ), + ) + + +def do_ex(cmd, cwd="."): + trace("cmd", repr(cmd)) + trace(" in", cwd) + if os.name == "posix" and not isinstance(cmd, (list, tuple)): + cmd = shlex.split(cmd) + + p = _popen_pipes(cmd, cwd) + out, err = p.communicate() + if out: + trace("out", repr(out)) + if err: + trace("err", repr(err)) + if p.returncode: + trace("ret", p.returncode) + return ensure_stripped_str(out), ensure_stripped_str(err), p.returncode + + +def do(cmd, cwd="."): + out, err, ret = do_ex(cmd, cwd) + if ret: + print(err) + return out + + +def data_from_mime(path): + with open(path, encoding="utf-8") as fp: + content = fp.read() + trace("content", repr(content)) + # the complex conditions come from reading pseudo-mime-messages + data = dict(x.split(": ", 1) for x in content.splitlines() if ": " in x) + trace("data", data) + return data + + +def function_has_arg(fn, argname): + assert inspect.isfunction(fn) + + argspec = inspect.signature(fn).parameters + + return argname in argspec + + +def has_command(name, warn=True): + try: + p = _popen_pipes([name, "help"], ".") + except OSError: + trace(*sys.exc_info()) + res = False + else: + p.communicate() + res = not p.returncode + if not res and warn: + warnings.warn("%r was not found" % name, category=RuntimeWarning) + return res + + +def require_command(name): + if not has_command(name, warn=False): + raise OSError("%r was not found" % name) + + +try: + from importlib.metadata import entry_points # type: ignore +except ImportError: + from pkg_resources import iter_entry_points +else: + + def iter_entry_points(group: str, name: Optional[str] = None): + 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-6.3.2-py3.9.egg/setuptools_scm/version.py b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/version.py new file mode 100644 index 0000000..91e25f6 --- /dev/null +++ b/.eggs/setuptools_scm-6.3.2-py3.9.egg/setuptools_scm/version.py @@ -0,0 +1,460 @@ +import datetime +import os +import re +import time +import warnings + +from .config import Configuration +from .config import Version as PkgVersion +from .utils import iter_entry_points +from .utils import trace + + +SEMVER_MINOR = 2 +SEMVER_PATCH = 3 +SEMVER_LEN = 3 + + +def _parse_version_tag(tag, config): + tagstring = tag if isinstance(tag, str) else str(tag) + match = config.tag_regex.match(tagstring) + + result = None + if match: + if len(match.groups()) == 1: + key = 1 + else: + key = "version" + + result = { + "version": match.group(key), + "prefix": match.group(0)[: match.start(key)], + "suffix": match.group(0)[match.end(key) :], + } + + trace(f"tag '{tag}' parsed to {result}") + return result + + +def callable_or_entrypoint(group, callable_or_name): + trace("ep", (group, callable_or_name)) + + if callable(callable_or_name): + return callable_or_name + + for ep in iter_entry_points(group, callable_or_name): + trace("ep found:", ep.name) + return ep.load() + + +def tag_to_version(tag, config: "Configuration | None" = 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 = tagdict["version"] + trace("version pre parse", version) + + if tagdict.get("suffix", ""): + warnings.warn( + "tag {!r} will be stripped of its suffix '{}'".format( + tag, tagdict["suffix"] + ) + ) + + version = config.version_cls(version) + trace("version", repr(version)) + + return version + + +def tags_to_versions(tags, config=None): + """ + take tags that might be prefixed with a keyword and return only the version part + :param tags: an iterable of tags + :param config: optional configuration object + """ + result = [] + for tag in tags: + tag = tag_to_version(tag, config=config) + if tag: + result.append(tag) + return result + + +class ScmVersion: + def __init__( + self, + tag_version, + distance=None, + node=None, + dirty=False, + preformatted=False, + branch=None, + config=None, + node_date=None, + **kw, + ): + if kw: + trace("unknown args", kw) + self.tag = tag_version + if dirty and distance is None: + distance = 0 + self.distance = distance + self.node = node + self.node_date = node_date + self.time = datetime.datetime.utcfromtimestamp( + int(os.environ.get("SOURCE_DATE_EPOCH", time.time())) + ) + self._extra = kw + self.dirty = dirty + self.preformatted = preformatted + self.branch = branch + self.config = config + + @property + def extra(self): + warnings.warn( + "ScmVersion.extra is deprecated and will be removed in future", + category=DeprecationWarning, + stacklevel=2, + ) + return self._extra + + @property + def exact(self): + return self.distance is None + + def __repr__(self): + return self.format_with( + "" + ) + + def format_with(self, fmt, **kw): + return fmt.format( + time=self.time, + tag=self.tag, + distance=self.distance, + node=self.node, + dirty=self.dirty, + branch=self.branch, + node_date=self.node_date, + **kw, + ) + + def format_choice(self, clean_format, dirty_format, **kw): + return self.format_with(dirty_format if self.dirty else clean_format, **kw) + + def format_next_version(self, guess_next, fmt="{guessed}.dev{distance}", **kw): + guessed = guess_next(self.tag, **kw) + return self.format_with(fmt, guessed=guessed) + + +def _parse_tag(tag, preformatted, config: "Configuration|None"): + if preformatted: + return tag + if config is None or not isinstance(tag, config.version_cls): + tag = tag_to_version(tag, config) + return tag + + +def meta( + tag, + distance: "int|None" = None, + dirty: bool = False, + node: "str|None" = None, + preformatted: bool = False, + branch: "str|None" = None, + config: "Configuration|None" = None, + **kw, +): + if not config: + warnings.warn( + "meta invoked without explicit configuration," + " will use defaults where required." + ) + parsed_version = _parse_tag(tag, preformatted, config) + trace("version", tag, "->", parsed_version) + assert parsed_version is not None, "Can't parse version %s" % tag + return ScmVersion( + parsed_version, distance, node, dirty, preformatted, branch, config, **kw + ) + + +def guess_next_version(tag_version: ScmVersion): + version = _strip_local(str(tag_version)) + return _bump_dev(version) or _bump_regex(version) + + +def _strip_local(version_string): + public, sep, local = version_string.partition("+") + return public + + +def _bump_dev(version): + if ".dev" not in version: + return + + prefix, tail = version.rsplit(".dev", 1) + if tail != "0": + raise ValueError( + "choosing custom numbers for the `.devX` distance " + "is not supported.\n " + "The {version} can't be bumped\n" + "Please drop the tag or create a new supported one".format(version=version) + ) + return prefix + + +def _bump_regex(version): + 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): + if version.exact: + return version.format_with("{tag}") + else: + return version.format_next_version(guess_next_version) + + +def guess_next_simple_semver(version, retain, increment=True): + try: + parts = [int(i) for i in str(version).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): + if version.exact: + return guess_next_simple_semver(version.tag, retain=SEMVER_LEN, increment=False) + else: + if version.branch is not None and "feature" in version.branch: + return version.format_next_version( + guess_next_simple_semver, retain=SEMVER_MINOR + ) + else: + return version.format_next_version( + guess_next_simple_semver, retain=SEMVER_PATCH + ) + + +def release_branch_semver_version(version): + 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 = _parse_version_tag(version.branch.split("/")[-1], version.config) + if branch_ver is not None: + branch_ver = branch_ver["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): + 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): + if version.exact: + return version.format_with("{tag}") + else: + return version.format_with("{tag}.post1.dev{distance}") + + +def date_ver_match(ver): + match = re.match( + ( + r"^(?P(?P\d{2}|\d{4})(?:\.\d{1,2}){2})" + r"(?:\.(?P\d*)){0,1}?$" + ), + str(ver), + ) + return match + + +def guess_next_date_ver(version, node_date=None, date_fmt=None, version_cls=None): + """ + same-day -> patch +1 + other-day -> today + + distance is always added as .devX + """ + match = date_ver_match(version) + 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" + + # deduct date format if not provided + if date_fmt is None: + date_fmt = "%Y.%m.%d" if len(match.group("year")) == 4 else "%y.%m.%d" + head_date = node_date or datetime.date.today() + # compute patch + if match is None: + tag_date = datetime.date.today() + else: + tag_date = datetime.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): + 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, time_format): + + if version.exact or version.node is None: + return version.format_choice( + "", "+d{time:{time_format}}", time_format=time_format + ) + else: + return version.format_choice( + "+{node}", "+{node}.d{time:{time_format}}", time_format=time_format + ) + + +def get_local_node_and_date(version): + return _format_local_with_time(version, time_format="%Y%m%d") + + +def get_local_node_and_timestamp(version, fmt="%Y%m%d%H%M%S"): + return _format_local_with_time(version, time_format=fmt) + + +def get_local_dirty_tag(version): + return version.format_choice("", "+dirty") + + +def get_no_local_node(_): + return "" + + +def postrelease_version(version): + if version.exact: + return version.format_with("{tag}") + else: + return version.format_with("{tag}.post{distance}") + + +def _get_ep(group, name): + for ep in iter_entry_points(group, name): + trace("ep found:", ep.name) + return ep.load() + + +def _iter_version_schemes(entrypoint, scheme_value, _memo=None): + if _memo is None: + _memo = set() + if isinstance(scheme_value, str): + scheme_value = _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 + + +def _call_version_scheme(version, entypoint, given_value, default): + 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, **config): + trace("scm version", version) + trace("config", config) + if version.preformatted: + 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/tomli-1.2.2-py3.9.egg/EGG-INFO/LICENSE b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..e859590 --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Taneli Hukkinen + +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/tomli-1.2.2-py3.9.egg/EGG-INFO/PKG-INFO b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..ad224bf --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,208 @@ +Metadata-Version: 2.1 +Name: tomli +Version: 1.2.2 +Summary: A lil' TOML parser +Keywords: toml +Author-email: Taneli Hukkinen +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +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: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Project-URL: Changelog, https://github.com/hukkin/tomli/blob/master/CHANGELOG.md +Project-URL: Homepage, https://github.com/hukkin/tomli + +[![Build Status](https://github.com/hukkin/tomli/workflows/Tests/badge.svg?branch=master)](https://github.com/hukkin/tomli/actions?query=workflow%3ATests+branch%3Amaster+event%3Apush) +[![codecov.io](https://codecov.io/gh/hukkin/tomli/branch/master/graph/badge.svg)](https://codecov.io/gh/hukkin/tomli) +[![PyPI version](https://img.shields.io/pypi/v/tomli)](https://pypi.org/project/tomli) + +# Tomli + +> A lil' TOML parser + +**Table of Contents** *generated with [mdformat-toc](https://github.com/hukkin/mdformat-toc)* + + + +- [Intro](#intro) +- [Installation](#installation) +- [Usage](#usage) + - [Parse a TOML string](#parse-a-toml-string) + - [Parse a TOML file](#parse-a-toml-file) + - [Handle invalid TOML](#handle-invalid-toml) + - [Construct `decimal.Decimal`s from TOML floats](#construct-decimaldecimals-from-toml-floats) +- [FAQ](#faq) + - [Why this parser?](#why-this-parser) + - [Is comment preserving round-trip parsing supported?](#is-comment-preserving-round-trip-parsing-supported) + - [Is there a `dumps`, `write` or `encode` function?](#is-there-a-dumps-write-or-encode-function) + - [How do TOML types map into Python types?](#how-do-toml-types-map-into-python-types) +- [Performance](#performance) + + + +## Intro + +Tomli is a Python library for parsing [TOML](https://toml.io). +Tomli is fully compatible with [TOML v1.0.0](https://toml.io/en/v1.0.0). + +## Installation + +```bash +pip install tomli +``` + +## Usage + +### Parse a TOML string + +```python +import tomli + +toml_str = """ + gretzky = 99 + + [kurri] + jari = 17 + """ + +toml_dict = tomli.loads(toml_str) +assert toml_dict == {"gretzky": 99, "kurri": {"jari": 17}} +``` + +### Parse a TOML file + +```python +import tomli + +with open("path_to_file/conf.toml", "rb") as f: + toml_dict = tomli.load(f) +``` + +The file must be opened in binary mode (with the `"rb"` flag). +Binary mode will enforce decoding the file as UTF-8 with universal newlines disabled, +both of which are required to correctly parse TOML. +Support for text file objects is deprecated for removal in the next major release. + +### Handle invalid TOML + +```python +import tomli + +try: + toml_dict = tomli.loads("]] this is invalid TOML [[") +except tomli.TOMLDecodeError: + print("Yep, definitely not valid.") +``` + +Note that while the `TOMLDecodeError` type is public API, error messages of raised instances of it are not. +Error messages should not be assumed to stay constant across Tomli versions. + +### Construct `decimal.Decimal`s from TOML floats + +```python +from decimal import Decimal +import tomli + +toml_dict = tomli.loads("precision-matters = 0.982492", parse_float=Decimal) +assert toml_dict["precision-matters"] == Decimal("0.982492") +``` + +Note that `decimal.Decimal` can be replaced with another callable that converts a TOML float from string to a Python type. +The `decimal.Decimal` is, however, a practical choice for use cases where float inaccuracies can not be tolerated. + +Illegal types include `dict`, `list`, and anything that has the `append` attribute. +Parsing floats into an illegal type results in undefined behavior. + +## FAQ + +### Why this parser? + +- it's lil' +- pure Python with zero dependencies +- the fastest pure Python parser [\*](#performance): + 15x as fast as [tomlkit](https://pypi.org/project/tomlkit/), + 2.4x as fast as [toml](https://pypi.org/project/toml/) +- outputs [basic data types](#how-do-toml-types-map-into-python-types) only +- 100% spec compliant: passes all tests in + [a test set](https://github.com/toml-lang/compliance/pull/8) + soon to be merged to the official + [compliance tests for TOML](https://github.com/toml-lang/compliance) + repository +- thoroughly tested: 100% branch coverage + +### Is comment preserving round-trip parsing supported? + +No. + +The `tomli.loads` function returns a plain `dict` that is populated with builtin types and types from the standard library only. +Preserving comments requires a custom type to be returned so will not be supported, +at least not by the `tomli.loads` and `tomli.load` functions. + +Look into [TOML Kit](https://github.com/sdispater/tomlkit) if preservation of style is what you need. + +### Is there a `dumps`, `write` or `encode` function? + +[Tomli-W](https://github.com/hukkin/tomli-w) is the write-only counterpart of Tomli, providing `dump` and `dumps` functions. + +The core library does not include write capability, as most TOML use cases are read-only, and Tomli intends to be minimal. + +### How do TOML types map into Python types? + +| TOML type | Python type | Details | +| ---------------- | ------------------- | ------------------------------------------------------------ | +| Document Root | `dict` | | +| Key | `str` | | +| String | `str` | | +| Integer | `int` | | +| Float | `float` | | +| Boolean | `bool` | | +| Offset Date-Time | `datetime.datetime` | `tzinfo` attribute set to an instance of `datetime.timezone` | +| Local Date-Time | `datetime.datetime` | `tzinfo` attribute set to `None` | +| Local Date | `datetime.date` | | +| Local Time | `datetime.time` | | +| Array | `list` | | +| Table | `dict` | | +| Inline Table | `dict` | | + +## Performance + +The `benchmark/` folder in this repository contains a performance benchmark for comparing the various Python TOML parsers. +The benchmark can be run with `tox -e benchmark-pypi`. +Running the benchmark on my personal computer output the following: + +```console +foo@bar:~/dev/tomli$ tox -e benchmark-pypi +benchmark-pypi installed: attrs==19.3.0,click==7.1.2,pytomlpp==1.0.2,qtoml==0.3.0,rtoml==0.7.0,toml==0.10.2,tomli==1.1.0,tomlkit==0.7.2 +benchmark-pypi run-test-pre: PYTHONHASHSEED='2658546909' +benchmark-pypi run-test: commands[0] | python -c 'import datetime; print(datetime.date.today())' +2021-07-23 +benchmark-pypi run-test: commands[1] | python --version +Python 3.8.10 +benchmark-pypi run-test: commands[2] | python benchmark/run.py +Parsing data.toml 5000 times: +------------------------------------------------------ + parser | exec time | performance (more is better) +-----------+------------+----------------------------- + rtoml | 0.901 s | baseline (100%) + pytomlpp | 1.08 s | 83.15% + tomli | 3.89 s | 23.15% + toml | 9.36 s | 9.63% + qtoml | 11.5 s | 7.82% + tomlkit | 56.8 s | 1.59% +``` + +The parsers are ordered from fastest to slowest, using the fastest parser as baseline. +Tomli performed the best out of all pure Python TOML parsers, +losing only to pytomlpp (wraps C++) and rtoml (wraps Rust). + diff --git a/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/RECORD b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..880ac4e --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/RECORD @@ -0,0 +1,9 @@ +tomli/__init__.py,sha256=kbhPFVUJrQxajcxAWEbYzDYEjjtRJ6dGT74U4XTOkhI,299 +tomli/_parser.py,sha256=HYJuOBq1QBZm0O6PMeLJPULdYVwsdYcdZUSuABujXTM,21659 +tomli/_re.py,sha256=bw4_EVo4n1qZwcEza7akJQ_wM6hLDJFn1Zsuf9YSjs8,2817 +tomli/_types.py,sha256=b1mavYLUYLBz0EP2lDrMVM6EGVFeqvxiqkS03jXNBvs,126 +tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26 +tomli-1.2.2.dist-info/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072 +tomli-1.2.2.dist-info/WHEEL,sha256=pVNS5wRGlMB8qzi0M1coslDk7i694hS7VxZqRXRntY4,81 +tomli-1.2.2.dist-info/METADATA,sha256=bhJIzo0PW08BpJ2wMFAGN19RxM8pU1eO5FMtjhAojRc,9089 +tomli-1.2.2.dist-info/RECORD,, diff --git a/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/WHEEL b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..3c6a102 --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/EGG-INFO/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.4.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.eggs/tomli-1.2.2-py3.9.egg/tomli/__init__.py b/.eggs/tomli-1.2.2-py3.9.egg/tomli/__init__.py new file mode 100644 index 0000000..7bcdbab --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/tomli/__init__.py @@ -0,0 +1,9 @@ +"""A lil' TOML parser.""" + +__all__ = ("loads", "load", "TOMLDecodeError") +__version__ = "1.2.2" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT + +from tomli._parser import TOMLDecodeError, load, loads + +# Pretend this exception was created here. +TOMLDecodeError.__module__ = "tomli" diff --git a/.eggs/tomli-1.2.2-py3.9.egg/tomli/_parser.py b/.eggs/tomli-1.2.2-py3.9.egg/tomli/_parser.py new file mode 100644 index 0000000..89e81c3 --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/tomli/_parser.py @@ -0,0 +1,663 @@ +import string +from types import MappingProxyType +from typing import Any, BinaryIO, Dict, FrozenSet, Iterable, NamedTuple, Optional, Tuple +import warnings + +from tomli._re import ( + RE_DATETIME, + RE_LOCALTIME, + RE_NUMBER, + match_to_datetime, + match_to_localtime, + match_to_number, +) +from tomli._types import Key, ParseFloat, Pos + +ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) + +# Neither of these sets include quotation mark or backslash. They are +# currently handled as separate cases in the parser functions. +ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t") +ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n") + +ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS +ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ILLEGAL_MULTILINE_BASIC_STR_CHARS + +ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS + +TOML_WS = frozenset(" \t") +TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n") +BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_") +KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'") +HEXDIGIT_CHARS = frozenset(string.hexdigits) + +BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType( + { + "\\b": "\u0008", # backspace + "\\t": "\u0009", # tab + "\\n": "\u000A", # linefeed + "\\f": "\u000C", # form feed + "\\r": "\u000D", # carriage return + '\\"': "\u0022", # quote + "\\\\": "\u005C", # backslash + } +) + + +class TOMLDecodeError(ValueError): + """An error raised if a document is not valid TOML.""" + + +def load(fp: BinaryIO, *, parse_float: ParseFloat = float) -> Dict[str, Any]: + """Parse TOML from a binary file object.""" + s_bytes = fp.read() + try: + s = s_bytes.decode() + except AttributeError: + warnings.warn( + "Text file object support is deprecated in favor of binary file objects." + ' Use `open("foo.toml", "rb")` to open the file in binary mode.', + DeprecationWarning, + stacklevel=2, + ) + s = s_bytes # type: ignore[assignment] + return loads(s, parse_float=parse_float) + + +def loads(s: str, *, parse_float: ParseFloat = float) -> Dict[str, Any]: # noqa: C901 + """Parse TOML from a string.""" + + # The spec allows converting "\r\n" to "\n", even in string + # literals. Let's do so to simplify parsing. + src = s.replace("\r\n", "\n") + pos = 0 + out = Output(NestedDict(), Flags()) + header: Key = () + + # Parse one statement at a time + # (typically means one line in TOML source) + while True: + # 1. Skip line leading whitespace + pos = skip_chars(src, pos, TOML_WS) + + # 2. Parse rules. Expect one of the following: + # - end of file + # - end of line + # - comment + # - key/value pair + # - append dict to list (and move to its namespace) + # - create dict (and move to its namespace) + # Skip trailing whitespace when applicable. + try: + char = src[pos] + except IndexError: + break + if char == "\n": + pos += 1 + continue + if char in KEY_INITIAL_CHARS: + pos = key_value_rule(src, pos, out, header, parse_float) + pos = skip_chars(src, pos, TOML_WS) + elif char == "[": + try: + second_char: Optional[str] = src[pos + 1] + except IndexError: + second_char = None + if second_char == "[": + pos, header = create_list_rule(src, pos, out) + else: + pos, header = create_dict_rule(src, pos, out) + pos = skip_chars(src, pos, TOML_WS) + elif char != "#": + raise suffixed_err(src, pos, "Invalid statement") + + # 3. Skip comment + pos = skip_comment(src, pos) + + # 4. Expect end of line or end of file + try: + char = src[pos] + except IndexError: + break + if char != "\n": + raise suffixed_err( + src, pos, "Expected newline or end of document after a statement" + ) + pos += 1 + + return out.data.dict + + +class Flags: + """Flags that map to parsed keys/namespaces.""" + + # Marks an immutable namespace (inline array or inline table). + FROZEN = 0 + # Marks a nest that has been explicitly created and can no longer + # be opened using the "[table]" syntax. + EXPLICIT_NEST = 1 + + def __init__(self) -> None: + self._flags: Dict[str, dict] = {} + + def unset_all(self, key: Key) -> None: + cont = self._flags + for k in key[:-1]: + if k not in cont: + return + cont = cont[k]["nested"] + cont.pop(key[-1], None) + + def set_for_relative_key(self, head_key: Key, rel_key: Key, flag: int) -> None: + cont = self._flags + for k in head_key: + if k not in cont: + cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} + cont = cont[k]["nested"] + for k in rel_key: + if k in cont: + cont[k]["flags"].add(flag) + else: + cont[k] = {"flags": {flag}, "recursive_flags": set(), "nested": {}} + cont = cont[k]["nested"] + + def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003 + cont = self._flags + key_parent, key_stem = key[:-1], key[-1] + for k in key_parent: + if k not in cont: + cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} + cont = cont[k]["nested"] + if key_stem not in cont: + cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}} + cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag) + + def is_(self, key: Key, flag: int) -> bool: + if not key: + return False # document root has no flags + cont = self._flags + for k in key[:-1]: + if k not in cont: + return False + inner_cont = cont[k] + if flag in inner_cont["recursive_flags"]: + return True + cont = inner_cont["nested"] + key_stem = key[-1] + if key_stem in cont: + cont = cont[key_stem] + return flag in cont["flags"] or flag in cont["recursive_flags"] + return False + + +class NestedDict: + def __init__(self) -> None: + # The parsed content of the TOML document + self.dict: Dict[str, Any] = {} + + def get_or_create_nest( + self, + key: Key, + *, + access_lists: bool = True, + ) -> dict: + cont: Any = self.dict + for k in key: + if k not in cont: + cont[k] = {} + cont = cont[k] + if access_lists and isinstance(cont, list): + cont = cont[-1] + if not isinstance(cont, dict): + raise KeyError("There is no nest behind this key") + return cont + + def append_nest_to_list(self, key: Key) -> None: + cont = self.get_or_create_nest(key[:-1]) + last_key = key[-1] + if last_key in cont: + list_ = cont[last_key] + try: + list_.append({}) + except AttributeError: + raise KeyError("An object other than list found behind this key") + else: + cont[last_key] = [{}] + + +class Output(NamedTuple): + data: NestedDict + flags: Flags + + +def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos: + try: + while src[pos] in chars: + pos += 1 + except IndexError: + pass + return pos + + +def skip_until( + src: str, + pos: Pos, + expect: str, + *, + error_on: FrozenSet[str], + error_on_eof: bool, +) -> Pos: + try: + new_pos = src.index(expect, pos) + except ValueError: + new_pos = len(src) + if error_on_eof: + raise suffixed_err(src, new_pos, f"Expected {expect!r}") from None + + if not error_on.isdisjoint(src[pos:new_pos]): + while src[pos] not in error_on: + pos += 1 + raise suffixed_err(src, pos, f"Found invalid character {src[pos]!r}") + return new_pos + + +def skip_comment(src: str, pos: Pos) -> Pos: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char == "#": + return skip_until( + src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False + ) + return pos + + +def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos: + while True: + pos_before_skip = pos + pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) + pos = skip_comment(src, pos) + if pos == pos_before_skip: + return pos + + +def create_dict_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: + pos += 1 # Skip "[" + pos = skip_chars(src, pos, TOML_WS) + pos, key = parse_key(src, pos) + + if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not declare {key} twice") + out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) + try: + out.data.get_or_create_nest(key) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") from None + + if not src.startswith("]", pos): + raise suffixed_err(src, pos, 'Expected "]" at the end of a table declaration') + return pos + 1, key + + +def create_list_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: + pos += 2 # Skip "[[" + pos = skip_chars(src, pos, TOML_WS) + pos, key = parse_key(src, pos) + + if out.flags.is_(key, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not mutate immutable namespace {key}") + # Free the namespace now that it points to another empty list item... + out.flags.unset_all(key) + # ...but this key precisely is still prohibited from table declaration + out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) + try: + out.data.append_nest_to_list(key) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") from None + + if not src.startswith("]]", pos): + raise suffixed_err(src, pos, 'Expected "]]" at the end of an array declaration') + return pos + 2, key + + +def key_value_rule( + src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat +) -> Pos: + pos, key, value = parse_key_value_pair(src, pos, parse_float) + key_parent, key_stem = key[:-1], key[-1] + abs_key_parent = header + key_parent + + if out.flags.is_(abs_key_parent, Flags.FROZEN): + raise suffixed_err( + src, pos, f"Can not mutate immutable namespace {abs_key_parent}" + ) + # Containers in the relative path can't be opened with the table syntax after this + out.flags.set_for_relative_key(header, key, Flags.EXPLICIT_NEST) + try: + nest = out.data.get_or_create_nest(abs_key_parent) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") from None + if key_stem in nest: + raise suffixed_err(src, pos, "Can not overwrite a value") + # Mark inline table and array namespaces recursively immutable + if isinstance(value, (dict, list)): + out.flags.set(header + key, Flags.FROZEN, recursive=True) + nest[key_stem] = value + return pos + + +def parse_key_value_pair( + src: str, pos: Pos, parse_float: ParseFloat +) -> Tuple[Pos, Key, Any]: + pos, key = parse_key(src, pos) + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char != "=": + raise suffixed_err(src, pos, 'Expected "=" after a key in a key/value pair') + pos += 1 + pos = skip_chars(src, pos, TOML_WS) + pos, value = parse_value(src, pos, parse_float) + return pos, key, value + + +def parse_key(src: str, pos: Pos) -> Tuple[Pos, Key]: + pos, key_part = parse_key_part(src, pos) + key: Key = (key_part,) + pos = skip_chars(src, pos, TOML_WS) + while True: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char != ".": + return pos, key + pos += 1 + pos = skip_chars(src, pos, TOML_WS) + pos, key_part = parse_key_part(src, pos) + key += (key_part,) + pos = skip_chars(src, pos, TOML_WS) + + +def parse_key_part(src: str, pos: Pos) -> Tuple[Pos, str]: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char in BARE_KEY_CHARS: + start_pos = pos + pos = skip_chars(src, pos, BARE_KEY_CHARS) + return pos, src[start_pos:pos] + if char == "'": + return parse_literal_str(src, pos) + if char == '"': + return parse_one_line_basic_str(src, pos) + raise suffixed_err(src, pos, "Invalid initial character for a key part") + + +def parse_one_line_basic_str(src: str, pos: Pos) -> Tuple[Pos, str]: + pos += 1 + return parse_basic_str(src, pos, multiline=False) + + +def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, list]: + pos += 1 + array: list = [] + + pos = skip_comments_and_array_ws(src, pos) + if src.startswith("]", pos): + return pos + 1, array + while True: + pos, val = parse_value(src, pos, parse_float) + array.append(val) + pos = skip_comments_and_array_ws(src, pos) + + c = src[pos : pos + 1] + if c == "]": + return pos + 1, array + if c != ",": + raise suffixed_err(src, pos, "Unclosed array") + pos += 1 + + pos = skip_comments_and_array_ws(src, pos) + if src.startswith("]", pos): + return pos + 1, array + + +def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, dict]: + pos += 1 + nested_dict = NestedDict() + flags = Flags() + + pos = skip_chars(src, pos, TOML_WS) + if src.startswith("}", pos): + return pos + 1, nested_dict.dict + while True: + pos, key, value = parse_key_value_pair(src, pos, parse_float) + key_parent, key_stem = key[:-1], key[-1] + if flags.is_(key, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not mutate immutable namespace {key}") + try: + nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") from None + if key_stem in nest: + raise suffixed_err(src, pos, f"Duplicate inline table key {key_stem!r}") + nest[key_stem] = value + pos = skip_chars(src, pos, TOML_WS) + c = src[pos : pos + 1] + if c == "}": + return pos + 1, nested_dict.dict + if c != ",": + raise suffixed_err(src, pos, "Unclosed inline table") + if isinstance(value, (dict, list)): + flags.set(key, Flags.FROZEN, recursive=True) + pos += 1 + pos = skip_chars(src, pos, TOML_WS) + + +def parse_basic_str_escape( # noqa: C901 + src: str, pos: Pos, *, multiline: bool = False +) -> Tuple[Pos, str]: + escape_id = src[pos : pos + 2] + pos += 2 + if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}: + # Skip whitespace until next non-whitespace character or end of + # the doc. Error if non-whitespace is found before newline. + if escape_id != "\\\n": + pos = skip_chars(src, pos, TOML_WS) + try: + char = src[pos] + except IndexError: + return pos, "" + if char != "\n": + raise suffixed_err(src, pos, 'Unescaped "\\" in a string') + pos += 1 + pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) + return pos, "" + if escape_id == "\\u": + return parse_hex_char(src, pos, 4) + if escape_id == "\\U": + return parse_hex_char(src, pos, 8) + try: + return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] + except KeyError: + if len(escape_id) != 2: + raise suffixed_err(src, pos, "Unterminated string") from None + raise suffixed_err(src, pos, 'Unescaped "\\" in a string') from None + + +def parse_basic_str_escape_multiline(src: str, pos: Pos) -> Tuple[Pos, str]: + return parse_basic_str_escape(src, pos, multiline=True) + + +def parse_hex_char(src: str, pos: Pos, hex_len: int) -> Tuple[Pos, str]: + hex_str = src[pos : pos + hex_len] + if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): + raise suffixed_err(src, pos, "Invalid hex value") + pos += hex_len + hex_int = int(hex_str, 16) + if not is_unicode_scalar_value(hex_int): + raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value") + return pos, chr(hex_int) + + +def parse_literal_str(src: str, pos: Pos) -> Tuple[Pos, str]: + pos += 1 # Skip starting apostrophe + start_pos = pos + pos = skip_until( + src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True + ) + return pos + 1, src[start_pos:pos] # Skip ending apostrophe + + +def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> Tuple[Pos, str]: + pos += 3 + if src.startswith("\n", pos): + pos += 1 + + if literal: + delim = "'" + end_pos = skip_until( + src, + pos, + "'''", + error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS, + error_on_eof=True, + ) + result = src[pos:end_pos] + pos = end_pos + 3 + else: + delim = '"' + pos, result = parse_basic_str(src, pos, multiline=True) + + # Add at maximum two extra apostrophes/quotes if the end sequence + # is 4 or 5 chars long instead of just 3. + if not src.startswith(delim, pos): + return pos, result + pos += 1 + if not src.startswith(delim, pos): + return pos, result + delim + pos += 1 + return pos, result + (delim * 2) + + +def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> Tuple[Pos, str]: + if multiline: + error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS + parse_escapes = parse_basic_str_escape_multiline + else: + error_on = ILLEGAL_BASIC_STR_CHARS + parse_escapes = parse_basic_str_escape + result = "" + start_pos = pos + while True: + try: + char = src[pos] + except IndexError: + raise suffixed_err(src, pos, "Unterminated string") from None + if char == '"': + if not multiline: + return pos + 1, result + src[start_pos:pos] + if src.startswith('"""', pos): + return pos + 3, result + src[start_pos:pos] + pos += 1 + continue + if char == "\\": + result += src[start_pos:pos] + pos, parsed_escape = parse_escapes(src, pos) + result += parsed_escape + start_pos = pos + continue + if char in error_on: + raise suffixed_err(src, pos, f"Illegal character {char!r}") + pos += 1 + + +def parse_value( # noqa: C901 + src: str, pos: Pos, parse_float: ParseFloat +) -> Tuple[Pos, Any]: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + + # Basic strings + if char == '"': + if src.startswith('"""', pos): + return parse_multiline_str(src, pos, literal=False) + return parse_one_line_basic_str(src, pos) + + # Literal strings + if char == "'": + if src.startswith("'''", pos): + return parse_multiline_str(src, pos, literal=True) + return parse_literal_str(src, pos) + + # Booleans + if char == "t": + if src.startswith("true", pos): + return pos + 4, True + if char == "f": + if src.startswith("false", pos): + return pos + 5, False + + # Dates and times + datetime_match = RE_DATETIME.match(src, pos) + if datetime_match: + try: + datetime_obj = match_to_datetime(datetime_match) + except ValueError as e: + raise suffixed_err(src, pos, "Invalid date or datetime") from e + return datetime_match.end(), datetime_obj + localtime_match = RE_LOCALTIME.match(src, pos) + if localtime_match: + return localtime_match.end(), match_to_localtime(localtime_match) + + # Integers and "normal" floats. + # The regex will greedily match any type starting with a decimal + # char, so needs to be located after handling of dates and times. + number_match = RE_NUMBER.match(src, pos) + if number_match: + return number_match.end(), match_to_number(number_match, parse_float) + + # Arrays + if char == "[": + return parse_array(src, pos, parse_float) + + # Inline tables + if char == "{": + return parse_inline_table(src, pos, parse_float) + + # Special floats + first_three = src[pos : pos + 3] + if first_three in {"inf", "nan"}: + return pos + 3, parse_float(first_three) + first_four = src[pos : pos + 4] + if first_four in {"-inf", "+inf", "-nan", "+nan"}: + return pos + 4, parse_float(first_four) + + raise suffixed_err(src, pos, "Invalid value") + + +def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError: + """Return a `TOMLDecodeError` where error message is suffixed with + coordinates in source.""" + + def coord_repr(src: str, pos: Pos) -> str: + if pos >= len(src): + return "end of document" + line = src.count("\n", 0, pos) + 1 + if line == 1: + column = pos + 1 + else: + column = pos - src.rindex("\n", 0, pos) + return f"line {line}, column {column}" + + return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})") + + +def is_unicode_scalar_value(codepoint: int) -> bool: + return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111) diff --git a/.eggs/tomli-1.2.2-py3.9.egg/tomli/_re.py b/.eggs/tomli-1.2.2-py3.9.egg/tomli/_re.py new file mode 100644 index 0000000..9126829 --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/tomli/_re.py @@ -0,0 +1,101 @@ +from datetime import date, datetime, time, timedelta, timezone, tzinfo +from functools import lru_cache +import re +from typing import Any, Optional, Union + +from tomli._types import ParseFloat + +# E.g. +# - 00:32:00.999999 +# - 00:32:00 +_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" + +RE_NUMBER = re.compile( + r""" +0 +(?: + x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex + | + b[01](?:_?[01])* # bin + | + o[0-7](?:_?[0-7])* # oct +) +| +[+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part +(?P + (?:\.[0-9](?:_?[0-9])*)? # optional fractional part + (?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part +) +""", + flags=re.VERBOSE, +) +RE_LOCALTIME = re.compile(_TIME_RE_STR) +RE_DATETIME = re.compile( + fr""" +([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 +(?: + [T ] + {_TIME_RE_STR} + (?:(Z)|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset +)? +""", + flags=re.VERBOSE, +) + + +def match_to_datetime(match: "re.Match") -> Union[datetime, date]: + """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. + + Raises ValueError if the match does not correspond to a valid date + or datetime. + """ + ( + year_str, + month_str, + day_str, + hour_str, + minute_str, + sec_str, + micros_str, + zulu_time, + offset_sign_str, + offset_hour_str, + offset_minute_str, + ) = match.groups() + year, month, day = int(year_str), int(month_str), int(day_str) + if hour_str is None: + return date(year, month, day) + hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) + micros = int(micros_str.ljust(6, "0")) if micros_str else 0 + if offset_sign_str: + tz: Optional[tzinfo] = cached_tz( + offset_hour_str, offset_minute_str, offset_sign_str + ) + elif zulu_time: + tz = timezone.utc + else: # local date-time + tz = None + return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) + + +@lru_cache(maxsize=None) +def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: + sign = 1 if sign_str == "+" else -1 + return timezone( + timedelta( + hours=sign * int(hour_str), + minutes=sign * int(minute_str), + ) + ) + + +def match_to_localtime(match: "re.Match") -> time: + hour_str, minute_str, sec_str, micros_str = match.groups() + micros = int(micros_str.ljust(6, "0")) if micros_str else 0 + return time(int(hour_str), int(minute_str), int(sec_str), micros) + + +def match_to_number(match: "re.Match", parse_float: "ParseFloat") -> Any: + if match.group("floatpart"): + return parse_float(match.group()) + return int(match.group(), 0) diff --git a/.eggs/tomli-1.2.2-py3.9.egg/tomli/_types.py b/.eggs/tomli-1.2.2-py3.9.egg/tomli/_types.py new file mode 100644 index 0000000..e37cc80 --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/tomli/_types.py @@ -0,0 +1,6 @@ +from typing import Any, Callable, Tuple + +# Type annotations +ParseFloat = Callable[[str], Any] +Key = Tuple[str, ...] +Pos = int diff --git a/.eggs/tomli-1.2.2-py3.9.egg/tomli/py.typed b/.eggs/tomli-1.2.2-py3.9.egg/tomli/py.typed new file mode 100644 index 0000000..7632ecf --- /dev/null +++ b/.eggs/tomli-1.2.2-py3.9.egg/tomli/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/.github/workflows/pytest-unit-tests.yml b/.github/workflows/pytest-unit-tests.yml index c0e3721..d1f1fd4 100644 --- a/.github/workflows/pytest-unit-tests.yml +++ b/.github/workflows/pytest-unit-tests.yml @@ -19,10 +19,10 @@ jobs: - name: Checkout simple_functions uses: actions/checkout@v2 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: 3.9 - name: Install dependencies run: | diff --git a/README.md b/README.md index 1ec0bba..3fddfe7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # CI MPM -Toy repo for CI lecture. +Toysad repo for CI lecture. diff --git a/simple_functions.egg-info/PKG-INFO b/simple_functions.egg-info/PKG-INFO new file mode 100644 index 0000000..16360f4 --- /dev/null +++ b/simple_functions.egg-info/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 2.1 +Name: simple-functions +Version: 0.1.dev6+g46b1b85.d20211026 +Summary: Environment for playing with CI. +Home-page: https://github.com/acse-2020 +Author: Imperial College London +Author-email: rhodri.nelson@imperial.ac.uk +License: UNKNOWN +Platform: UNKNOWN + +UNKNOWN + 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/__init__.py b/simple_functions/__init__.py index 963db77..4399296 100644 --- a/simple_functions/__init__.py +++ b/simple_functions/__init__.py @@ -1,4 +1,5 @@ from .functions1 import * # noqa +from .constants import * # noqa from pkg_resources import get_distribution, DistributionNotFound try: diff --git a/simple_functions/__pycache__/__init__.cpython-39.pyc b/simple_functions/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..8d40732 Binary files /dev/null and b/simple_functions/__pycache__/__init__.cpython-39.pyc differ diff --git a/simple_functions/__pycache__/constants.cpython-39.pyc b/simple_functions/__pycache__/constants.cpython-39.pyc new file mode 100644 index 0000000..7744de1 Binary files /dev/null and b/simple_functions/__pycache__/constants.cpython-39.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..1cb9b9d Binary files /dev/null and b/simple_functions/__pycache__/functions1.cpython-39.pyc differ diff --git a/simple_functions/constants.py b/simple_functions/constants.py new file mode 100644 index 0000000..c3fa1c0 --- /dev/null +++ b/simple_functions/constants.py @@ -0,0 +1,16 @@ +from numpy import sqrt +from simple_functions.functions1 import factorial +from functools import cache + + +__all__ = ['pi'] + + +def pi(terms=1): + return 1./(2.*sqrt(2.)/9801.*rsum(terms)) + + +@cache +def rsum(n): + t = factorial(4*n)*(1103+26390*n)/(factorial(n)**4*396**(4*n)) + return t + rsum(n-1) if n else t diff --git a/simple_functions/functions1.py b/simple_functions/functions1.py index 8c63a4d..eae5287 100644 --- a/simple_functions/functions1.py +++ b/simple_functions/functions1.py @@ -1,5 +1,7 @@ -__all__ = ['my_sum'] +from functools import cache + +__all__ = ['my_sum', 'factorial'] def my_sum(iterable): @@ -7,3 +9,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_constants.cpython-38-pytest-6.2.3.pyc b/tests/__pycache__/test_constants.cpython-38-pytest-6.2.3.pyc new file mode 100644 index 0000000..f2e1518 Binary files /dev/null and b/tests/__pycache__/test_constants.cpython-38-pytest-6.2.3.pyc differ diff --git a/tests/__pycache__/test_constants.cpython-39-pytest-6.2.4.pyc b/tests/__pycache__/test_constants.cpython-39-pytest-6.2.4.pyc new file mode 100644 index 0000000..ab5d02f Binary files /dev/null and b/tests/__pycache__/test_constants.cpython-39-pytest-6.2.4.pyc differ diff --git a/tests/__pycache__/test_simple_functions.cpython-38-pytest-6.2.3.pyc b/tests/__pycache__/test_simple_functions.cpython-38-pytest-6.2.3.pyc new file mode 100644 index 0000000..c746c1e Binary files /dev/null and b/tests/__pycache__/test_simple_functions.cpython-38-pytest-6.2.3.pyc differ diff --git a/tests/__pycache__/test_simple_functions.cpython-39-pytest-6.2.4.pyc b/tests/__pycache__/test_simple_functions.cpython-39-pytest-6.2.4.pyc new file mode 100644 index 0000000..e90cae4 Binary files /dev/null and b/tests/__pycache__/test_simple_functions.cpython-39-pytest-6.2.4.pyc differ diff --git a/tests/test_constants.py b/tests/test_constants.py new file mode 100644 index 0000000..6cf69b5 --- /dev/null +++ b/tests/test_constants.py @@ -0,0 +1,10 @@ +import numpy as np +from simple_functions import pi + + +class TestPi(object): + '''Class to test our constants are computed correctly''' + def test_pi(self): + '''Test computation of pi''' + my_pi = pi(2) + assert np.isclose(my_pi, np.pi, atol=1e-12) diff --git a/tests/test_simple_functions.py b/tests/test_simple_functions.py index 5a03b52..f0a4fd3 100644 --- a/tests/test_simple_functions.py +++ b/tests/test_simple_functions.py @@ -1,6 +1,6 @@ import pytest -from simple_functions import my_sum +from simple_functions import my_sum, factorial class TestSimpleFunctions(object): @@ -14,3 +14,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 add function''' + isum = factorial(number) + assert isum == expected