diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/Makefile | 153 | ||||
-rw-r--r-- | docs/_static/.empty | 0 | ||||
-rw-r--r-- | docs/changelog.rst | 1 | ||||
-rw-r--r-- | docs/conf.py | 111 | ||||
-rw-r--r-- | docs/development/getting-started.rst | 77 | ||||
-rw-r--r-- | docs/development/index.rst | 19 | ||||
-rw-r--r-- | docs/development/release-process.rst | 25 | ||||
-rw-r--r-- | docs/development/reviewing-patches.rst | 37 | ||||
-rw-r--r-- | docs/development/submitting-patches.rst | 74 | ||||
-rw-r--r-- | docs/index.rst | 38 | ||||
-rw-r--r-- | docs/markers.rst | 93 | ||||
-rw-r--r-- | docs/requirements.rst | 89 | ||||
-rw-r--r-- | docs/requirements.txt | 1 | ||||
-rw-r--r-- | docs/security.rst | 18 | ||||
-rw-r--r-- | docs/specifiers.rst | 222 | ||||
-rw-r--r-- | docs/tags.rst | 225 | ||||
-rw-r--r-- | docs/utils.rst | 92 | ||||
-rw-r--r-- | docs/version.rst | 292 |
18 files changed, 1567 insertions, 0 deletions
diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..9d683b4 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/packaging.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/packaging.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/packaging" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/packaging" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/_static/.empty b/docs/_static/.empty new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/docs/_static/.empty diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..565b052 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1 @@ +.. include:: ../CHANGELOG.rst diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..edd8dd5 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,111 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import os +import sys + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath(".")) + +# -- General configuration ---------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix of source filenames. +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = "Packaging" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# + +base_dir = os.path.join(os.path.dirname(__file__), os.pardir) +about = {} +with open(os.path.join(base_dir, "packaging", "__about__.py")) as f: + exec(f.read(), about) + +version = release = about["__version__"] +copyright = about["__copyright__"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["_build"] + +extlinks = { + "issue": ("https://github.com/pypa/packaging/issues/%s", "#"), + "pull": ("https://github.com/pypa/packaging/pull/%s", "PR #"), +} +# -- Options for HTML output -------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +html_theme = "furo" +html_title = "packaging" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# Output file base name for HTML help builder. +htmlhelp_basename = "packagingdoc" + + +# -- Options for LaTeX output ------------------------------------------------- + +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]) +latex_documents = [ + ("index", "packaging.tex", "Packaging Documentation", "Donald Stufft", "manual") +] + +# -- Options for manual page output ------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [("index", "packaging", "Packaging Documentation", ["Donald Stufft"], 1)] + +# -- Options for Texinfo output ----------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + "index", + "packaging", + "Packaging Documentation", + "Donald Stufft", + "packaging", + "Core utilities for Python packages", + "Miscellaneous", + ) +] + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {"https://docs.python.org/": None} + +epub_theme = "epub" diff --git a/docs/development/getting-started.rst b/docs/development/getting-started.rst new file mode 100644 index 0000000..8bd42ac --- /dev/null +++ b/docs/development/getting-started.rst @@ -0,0 +1,77 @@ +Getting started +=============== + +Working on packaging requires the installation of a small number of +development dependencies. To see what dependencies are required to +run the tests manually, please look at the ``noxfile.py`` file. + +Running tests +~~~~~~~~~~~~~ + +The packaging unit tests are found in the ``tests/`` directory and are +designed to be run using `pytest`_. `pytest`_ will discover the tests +automatically, so all you have to do is: + +.. code-block:: console + + $ python -m pytest + ... + 29204 passed, 4 skipped, 1 xfailed in 83.98 seconds + +This runs the tests with the default Python interpreter. This also allows +you to run select tests instead of the entire test suite. + +You can also verify that the tests pass on other supported Python interpreters. +For this we use `nox`_, which will automatically create a `virtualenv`_ for +each supported Python version and run the tests. For example: + +.. code-block:: console + + $ nox -s tests + ... + nox > Ran multiple sessions: + nox > * tests-3.6: success + nox > * tests-3.7: success + nox > * tests-3.8: success + nox > * tests-3.9: success + nox > * tests-pypy3: skipped + +You may not have all the required Python versions installed, in which case you +will see one or more ``InterpreterNotFound`` errors. + +Running linters +~~~~~~~~~~~~~~~ + +If you wish to run the linting rules, you may use `pre-commit`_ or run +``nox -s lint``. + +.. code-block:: console + + $ nox -s lint + ... + nox > Session lint was successful. + +Building documentation +~~~~~~~~~~~~~~~~~~~~~~ + +packaging documentation is stored in the ``docs/`` directory. It is +written in `reStructured Text`_ and rendered using `Sphinx`_. + +Use `nox`_ to build the documentation. For example: + +.. code-block:: console + + $ nox -s docs + ... + nox > Session docs was successful. + +The HTML documentation index can now be found at +``docs/_build/html/index.html``. + +.. _`pytest`: https://pypi.org/project/pytest/ +.. _`nox`: https://pypi.org/project/nox/ +.. _`virtualenv`: https://pypi.org/project/virtualenv/ +.. _`pip`: https://pypi.org/project/pip/ +.. _`sphinx`: https://pypi.org/project/Sphinx/ +.. _`reStructured Text`: http://sphinx-doc.org/rest.html +.. _`pre-commit`: https://pre-commit.com diff --git a/docs/development/index.rst b/docs/development/index.rst new file mode 100644 index 0000000..c0aea8a --- /dev/null +++ b/docs/development/index.rst @@ -0,0 +1,19 @@ +Development +=========== + +As an open source project, packaging welcomes contributions of all +forms. The sections below will help you get started. + +File bugs and feature requests on our issue tracker on `GitHub`_. If it is a +bug check out `what to put in your bug report`_. + +.. toctree:: + :maxdepth: 2 + + getting-started + submitting-patches + reviewing-patches + release-process + +.. _`GitHub`: https://github.com/pypa/packaging +.. _`what to put in your bug report`: http://www.contribution-guide.org/#what-to-put-in-your-bug-report diff --git a/docs/development/release-process.rst b/docs/development/release-process.rst new file mode 100644 index 0000000..84e5bec --- /dev/null +++ b/docs/development/release-process.rst @@ -0,0 +1,25 @@ +Release Process +=============== + +#. Checkout the current ``main`` branch. +#. Install the latest ``nox``:: + + $ pip install nox + +#. Run the release automation with the required version number (YY.N):: + + $ nox -s release -- YY.N + + You will need the password for your GPG key as well as an API token for PyPI. + +#. Add a `release on GitHub <https://github.com/pypa/packaging/releases>`__. + +#. Notify the other project owners of the release. + +.. note:: + + Access needed for making the release are: + + - PyPI maintainer (or owner) access to ``packaging`` + - push directly to the ``main`` branch on the source repository + - push tags directly to the source repository diff --git a/docs/development/reviewing-patches.rst b/docs/development/reviewing-patches.rst new file mode 100644 index 0000000..c476c75 --- /dev/null +++ b/docs/development/reviewing-patches.rst @@ -0,0 +1,37 @@ +Reviewing and merging patches +============================= + +Everyone is encouraged to review open pull requests. We only ask that you try +and think carefully, ask questions and are `excellent to one another`_. Code +review is our opportunity to share knowledge, design ideas and make friends. + +When reviewing a patch try to keep each of these concepts in mind: + +Architecture +------------ + +* Is the proposed change being made in the correct place? + +Intent +------ + +* What is the change being proposed? +* Do we want this feature or is the bug they're fixing really a bug? + +Implementation +-------------- + +* Does the change do what the author claims? +* Are there sufficient tests? +* Has it been documented? +* Will this change introduce new bugs? + +Grammar and style +----------------- + +These are small things that are not caught by the automated style checkers. + +* Does a variable need a better name? +* Should this be a keyword argument? + +.. _`excellent to one another`: https://speakerdeck.com/ohrite/better-code-review diff --git a/docs/development/submitting-patches.rst b/docs/development/submitting-patches.rst new file mode 100644 index 0000000..fbdb5a4 --- /dev/null +++ b/docs/development/submitting-patches.rst @@ -0,0 +1,74 @@ +Submitting patches +================== + +* Always make a new branch for your work. +* Patches should be small to facilitate easier review. `Studies have shown`_ + that review quality falls off as patch size grows. Sometimes this will result + in many small PRs to land a single large feature. +* Larger changes should be discussed in a ticket before submission. +* New features and significant bug fixes should be documented in the + :doc:`/changelog`. +* You must have legal permission to distribute any code you contribute and it + must be available under both the BSD and Apache Software License Version 2.0 + licenses. + +If you believe you've identified a security issue in packaging, please +follow the directions on the :doc:`security page </security>`. + +Code +---- + +This project's source is auto-formatted with |black|. You can check if your +code meets our requirements by running our linters against it with ``nox -s +lint`` or ``pre-commit run --all-files``. + +`Write comments as complete sentences.`_ + +Every code file must start with the boilerplate licensing notice: + +.. code-block:: python + + # This file is dual licensed under the terms of the Apache License, Version + # 2.0, and the BSD License. See the LICENSE file in the root of this repository + # for complete details. + +Tests +----- + +All code changes must be accompanied by unit tests with 100% code coverage (as +measured by the combined metrics across our build matrix). + + +Documentation +------------- + +All features should be documented with prose in the ``docs`` section. + +When referring to a hypothetical individual (such as "a person receiving an +encrypted message") use gender neutral pronouns (they/them/their). + +Docstrings are typically only used when writing abstract classes, but should +be written like this if required: + +.. code-block:: python + + def some_function(some_arg): + """ + Does some things. + + :param some_arg: Some argument. + """ + +So, specifically: + +* Always use three double quotes. +* Put the three double quotes on their own line. +* No blank line at the end. +* Use Sphinx parameter/attribute documentation `syntax`_. + + +.. |black| replace:: ``black`` +.. _black: https://pypi.org/project/black/ +.. _`Write comments as complete sentences.`: https://nedbatchelder.com/blog/201401/comments_should_be_sentences.html +.. _`syntax`: http://sphinx-doc.org/domains.html#info-field-lists +.. _`Studies have shown`: http://www.ibm.com/developerworks/rational/library/11-proven-practices-for-peer-review/ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..aafdae8 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,38 @@ +Welcome to packaging +==================== + +.. include:: ../README.rst + :start-after: start-intro + :end-before: end-intro + + +Installation +------------ + +You can install packaging with ``pip``: + +.. code-block:: console + + $ pip install packaging + + +.. toctree:: + :maxdepth: 1 + :caption: API Documentation + :hidden: + + version + specifiers + markers + requirements + tags + utils + +.. toctree:: + :maxdepth: 2 + :caption: Project + :hidden: + + development/index + security + changelog diff --git a/docs/markers.rst b/docs/markers.rst new file mode 100644 index 0000000..ad25361 --- /dev/null +++ b/docs/markers.rst @@ -0,0 +1,93 @@ +Markers +======= + +.. currentmodule:: packaging.markers + +One extra requirement of dealing with dependencies is the ability to specify +if it is required depending on the operating system or Python version in use. +`PEP 508`_ defines the scheme which has been implemented by this module. + +Usage +----- + +.. doctest:: + + >>> from packaging.markers import Marker, UndefinedEnvironmentName + >>> marker = Marker("python_version>'2'") + >>> marker + <Marker('python_version > "2"')> + >>> # We can evaluate the marker to see if it is satisfied + >>> marker.evaluate() + True + >>> # We can also override the environment + >>> env = {'python_version': '1.5.4'} + >>> marker.evaluate(environment=env) + False + >>> # Multiple markers can be ANDed + >>> and_marker = Marker("os_name=='a' and os_name=='b'") + >>> and_marker + <Marker('os_name == "a" and os_name == "b"')> + >>> # Multiple markers can be ORed + >>> or_marker = Marker("os_name=='a' or os_name=='b'") + >>> or_marker + <Marker('os_name == "a" or os_name == "b"')> + >>> # Markers can be also used with extras, to pull in dependencies if + >>> # a certain extra is being installed + >>> extra = Marker('extra == "bar"') + >>> # Evaluating an extra marker with no environment is an error + >>> try: + ... extra.evaluate() + ... except UndefinedEnvironmentName: + ... pass + >>> extra_environment = {'extra': ''} + >>> extra.evaluate(environment=extra_environment) + False + >>> extra_environment['extra'] = 'bar' + >>> extra.evaluate(environment=extra_environment) + True + + +Reference +--------- + +.. class:: Marker(markers) + + This class abstracts handling markers for dependencies of a project. It can + be passed a single marker or multiple markers that are ANDed or ORed + together. Each marker will be parsed according to PEP 508. + + :param str markers: The string representation of a marker or markers. + :raises InvalidMarker: If the given ``markers`` are not parseable, then + this exception will be raised. + + .. method:: evaluate(environment=None) + + Evaluate the marker given the context of the current Python process. + + :param dict environment: A dictionary containing keys and values to + override the detected environment. + :raises: UndefinedComparison: If the marker uses a PEP 440 comparison on + strings which are not valid PEP 440 versions. + :raises: UndefinedEnvironmentName: If the marker accesses a value that + isn't present inside of the environment + dictionary. + +.. exception:: InvalidMarker + + Raised when attempting to create a :class:`Marker` with a string that + does not conform to PEP 508. + + +.. exception:: UndefinedComparison + + Raised when attempting to evaluate a :class:`Marker` with a PEP 440 + comparison operator against values that are not valid PEP 440 versions. + + +.. exception:: UndefinedEnvironmentName + + Raised when attempting to evaluate a :class:`Marker` with a value that is + missing from the evaluation environment. + + +.. _`PEP 508`: https://www.python.org/dev/peps/pep-0508/ diff --git a/docs/requirements.rst b/docs/requirements.rst new file mode 100644 index 0000000..e7c5a85 --- /dev/null +++ b/docs/requirements.rst @@ -0,0 +1,89 @@ +Requirements +============ + +.. currentmodule:: packaging.requirements + +Parse a given requirements line for specifying dependencies of a Python +project, using `PEP 508`_ which defines the scheme that has been implemented +by this module. + +Usage +----- + +.. doctest:: + + >>> from packaging.requirements import Requirement + >>> simple_req = Requirement("name") + >>> simple_req + <Requirement('name')> + >>> simple_req.name + 'name' + >>> simple_req.url is None + True + >>> simple_req.extras + set() + >>> simple_req.specifier + <SpecifierSet('')> + >>> simple_req.marker is None + True + >>> # Requirements can be specified with extras, specifiers and markers + >>> req = Requirement('name[foo]>=2,<3; python_version>"2.0"') + >>> req.name + 'name' + >>> req.extras + {'foo'} + >>> req.specifier + <SpecifierSet('<3,>=2')> + >>> req.marker + <Marker('python_version > "2.0"')> + >>> # Requirements can also be specified with a URL, but may not specify + >>> # a version. + >>> url_req = Requirement('name @ https://github.com/pypa ;os_name=="a"') + >>> url_req.name + 'name' + >>> url_req.url + 'https://github.com/pypa' + >>> url_req.extras + set() + >>> url_req.marker + <Marker('os_name == "a"')> + + +Reference +--------- + +.. class:: Requirement(requirement) + + This class abstracts handling the details of a requirement for a project. + Each requirement will be parsed according to PEP 508. + + :param str requirement: The string representation of a requirement. + :raises InvalidRequirement: If the given ``requirement`` is not parseable, + then this exception will be raised. + + .. attribute:: name + + The name of the requirement. + + .. attribute:: url + + The URL, if any where to download the requirement from. Can be None. + + .. attribute:: extras + + A set of extras that the requirement specifies. + + .. attribute:: specifier + + A :class:`~.SpecifierSet` of the version specified by the requirement. + + .. attribute:: marker + + A :class:`~.Marker` of the marker for the requirement. Can be None. + +.. exception:: InvalidRequirement + + Raised when attempting to create a :class:`Requirement` with a string that + does not conform to PEP 508. + +.. _`PEP 508`: https://www.python.org/dev/peps/pep-0508/ diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..a95ae18 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +furo diff --git a/docs/security.rst b/docs/security.rst new file mode 100644 index 0000000..f7fdb00 --- /dev/null +++ b/docs/security.rst @@ -0,0 +1,18 @@ +Security +======== + +We take the security of packaging seriously. If you believe you've identified a +security issue in it, DO NOT report the issue in any public forum, including +(but not limited to): + +- GitHub issue tracker +- Official or unofficial chat channels +- Official or unofficial mailing lists + +Please report your issue to ``security@python.org``. Messages may be optionally +encrypted with GPG using key fingerprints available at the `Python Security +page <https://www.python.org/news/security/>`_. + +Once you've submitted an issue via email, you should receive an acknowledgment +within 48 hours, and depending on the action to be taken, you may receive +further follow-up emails. diff --git a/docs/specifiers.rst b/docs/specifiers.rst new file mode 100644 index 0000000..83299a8 --- /dev/null +++ b/docs/specifiers.rst @@ -0,0 +1,222 @@ +Specifiers +========== + +.. currentmodule:: packaging.specifiers + +A core requirement of dealing with dependencies is the ability to specify what +versions of a dependency are acceptable for you. `PEP 440`_ defines the +standard specifier scheme which has been implemented by this module. + +Usage +----- + +.. doctest:: + + >>> from packaging.specifiers import SpecifierSet + >>> from packaging.version import Version + >>> spec1 = SpecifierSet("~=1.0") + >>> spec1 + <SpecifierSet('~=1.0')> + >>> spec2 = SpecifierSet(">=1.0") + >>> spec2 + <SpecifierSet('>=1.0')> + >>> # We can combine specifiers + >>> combined_spec = spec1 & spec2 + >>> combined_spec + <SpecifierSet('>=1.0,~=1.0')> + >>> # We can also implicitly combine a string specifier + >>> combined_spec &= "!=1.1" + >>> combined_spec + <SpecifierSet('!=1.1,>=1.0,~=1.0')> + >>> # Create a few versions to check for contains. + >>> v1 = Version("1.0a5") + >>> v2 = Version("1.0") + >>> # We can check a version object to see if it falls within a specifier + >>> v1 in combined_spec + False + >>> v2 in combined_spec + True + >>> # We can even do the same with a string based version + >>> "1.4" in combined_spec + True + >>> # Finally we can filter a list of versions to get only those which are + >>> # contained within our specifier. + >>> list(combined_spec.filter([v1, v2, "1.4"])) + [<Version('1.0')>, '1.4'] + + +Reference +--------- + +.. class:: SpecifierSet(specifiers="", prereleases=None) + + This class abstracts handling specifying the dependencies of a project. It + can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. Each individual + specifier will be attempted to be parsed as a PEP 440 specifier + (:class:`Specifier`) or as a legacy, setuptools style specifier + (deprecated :class:`LegacySpecifier`). You may combine + :class:`SpecifierSet` instances using the ``&`` operator + (``SpecifierSet(">2") & SpecifierSet("<4")``). + + Both the membership tests and the combination support using raw strings + in place of already instantiated objects. + + :param str specifiers: The string representation of a specifier or a + comma-separated list of specifiers which will + be parsed and normalized before use. + :param bool prereleases: This tells the SpecifierSet if it should accept + prerelease versions if applicable or not. The + default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: If the given ``specifiers`` are not parseable + than this exception will be raised. + + .. attribute:: prereleases + + A boolean value indicating whether this :class:`SpecifierSet` + represents a specifier that includes a pre-release versions. This can be + set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to enable + autodetection. + + .. method:: __contains__(version) + + This is the more Pythonic version of :meth:`contains()`, but does + not allow you to override the ``prereleases`` argument. If you + need that, use :meth:`contains()`. + + See :meth:`contains()`. + + .. method:: contains(version, prereleases=None) + + Determines if ``version``, which can be either a version string, a + :class:`Version`, or a deprecated :class:`LegacyVersion` object, is + contained within this set of specifiers. + + This will either match or not match prereleases based on the + ``prereleases`` parameter. When ``prereleases`` is set to ``None`` + (the default) it will use the ``Specifier().prereleases`` attribute to + determine if to allow them. Otherwise it will use the boolean value of + the passed in value to determine if to allow them or not. + + .. method:: __len__() + + Returns the number of specifiers in this specifier set. + + .. method:: __iter__() + + Returns an iterator over all the underlying :class:`Specifier` (or + deprecated :class:`LegacySpecifier`) instances in this specifier set. + + .. method:: filter(iterable, prereleases=None) + + Takes an iterable that can contain version strings, :class:`~.Version`, + and deprecated :class:`~.LegacyVersion` instances and will then filter + it, returning an iterable that contains only items which match the + rules of this specifier object. + + This method is smarter than just + ``filter(Specifier().contains, [...])`` because it implements the rule + from PEP 440 where a prerelease item SHOULD be accepted if no other + versions match the given specifier. + + The ``prereleases`` parameter functions similarly to that of the same + parameter in ``contains``. If the value is ``None`` (the default) then + it will intelligently decide if to allow prereleases based on the + specifier, the ``Specifier().prereleases`` value, and the PEP 440 + rules. Otherwise it will act as a boolean which will enable or disable + all prerelease versions from being included. + + +.. class:: Specifier(specifier, prereleases=None) + + This class abstracts the handling of a single `PEP 440`_ compatible + specifier. It is generally not required to instantiate this manually, + preferring instead to work with :class:`SpecifierSet`. + + :param str specifier: The string representation of a specifier which will + be parsed and normalized before use. + :param bool prereleases: This tells the specifier if it should accept + prerelease versions if applicable or not. The + default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: If the ``specifier`` does not conform to PEP 440 + in any way then this exception will be raised. + + .. attribute:: operator + + The string value of the operator part of this specifier. + + .. attribute:: version + + The string version of the version part of this specifier. + + .. attribute:: prereleases + + See :attr:`SpecifierSet.prereleases`. + + .. method:: __contains__(version) + + See :meth:`SpecifierSet.__contains__()`. + + .. method:: contains(version, prereleases=None) + + See :meth:`SpecifierSet.contains()`. + + .. method:: filter(iterable, prereleases=None) + + See :meth:`SpecifierSet.filter()`. + + +.. class:: LegacySpecifier(specifier, prereleases=None) + + .. deprecated:: 20.5 + + Use :class:`Specifier` instead. + + This class abstracts the handling of a single legacy, setuptools style + specifier. It is generally not required to instantiate this manually, + preferring instead to work with :class:`SpecifierSet`. + + :param str specifier: The string representation of a specifier which will + be parsed and normalized before use. + :param bool prereleases: This tells the specifier if it should accept + prerelease versions if applicable or not. The + default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: If the ``specifier`` is not parseable then this + will be raised. + + .. attribute:: operator + + The string value of the operator part of this specifier. + + .. attribute:: version + + The string version of the version part of this specifier. + + .. attribute:: prereleases + + See :attr:`SpecifierSet.prereleases`. + + .. method:: __contains__(version) + + See :meth:`SpecifierSet.__contains__()`. + + .. method:: contains(version, prereleases=None) + + See :meth:`SpecifierSet.contains()`. + + .. method:: filter(iterable, prereleases=None) + + See :meth:`SpecifierSet.filter()`. + + +.. exception:: InvalidSpecifier + + Raised when attempting to create a :class:`Specifier` with a specifier + string that does not conform to `PEP 440`_. + + +.. _`PEP 440`: https://www.python.org/dev/peps/pep-0440/ diff --git a/docs/tags.rst b/docs/tags.rst new file mode 100644 index 0000000..ecd613b --- /dev/null +++ b/docs/tags.rst @@ -0,0 +1,225 @@ +Tags +==== + +.. currentmodule:: packaging.tags + +Wheels encode the Python interpreter, ABI, and platform that they support in +their filenames using `platform compatibility tags`_. This module provides +support for both parsing these tags as well as discovering what tags the +running Python interpreter supports. + +Usage +----- + +.. doctest:: + + >>> from packaging.tags import Tag, sys_tags + >>> import sys + >>> looking_for = Tag("py{major}".format(major=sys.version_info.major), "none", "any") + >>> supported_tags = list(sys_tags()) + >>> looking_for in supported_tags + True + >>> really_old = Tag("py1", "none", "any") + >>> wheels = {really_old, looking_for} + >>> best_wheel = None + >>> for supported_tag in supported_tags: + ... for wheel_tag in wheels: + ... if supported_tag == wheel_tag: + ... best_wheel = wheel_tag + ... break + >>> best_wheel == looking_for + True + +Reference +--------- + +High Level Interface +'''''''''''''''''''' + +The following functions are the main interface to the library, and are typically the only +items that applications should need to reference, in order to parse and check tags. + +.. class:: Tag(interpreter, abi, platform) + + A representation of the tag triple for a wheel. Instances are considered + immutable and thus are hashable. Equality checking is also supported. + + :param str interpreter: The interpreter name, e.g. ``"py"`` + (see :attr:`INTERPRETER_SHORT_NAMES` for mapping + well-known interpreter names to their short names). + :param str abi: The ABI that a wheel supports, e.g. ``"cp37m"``. + :param str platform: The OS/platform the wheel supports, + e.g. ``"win_amd64"``. + + .. attribute:: interpreter + + The interpreter name. + + .. attribute:: abi + + The supported ABI. + + .. attribute:: platform + + The OS/platform. + + +.. function:: parse_tag(tag) + + Parses the provided ``tag`` into a set of :class:`Tag` instances. + + Returning a set is required due to the possibility that the tag is a + `compressed tag set`_, e.g. ``"py2.py3-none-any"`` which supports both + Python 2 and Python 3. + + :param str tag: The tag to parse, e.g. ``"py3-none-any"``. + + +.. function:: sys_tags(*, warn=False) + + Yields the tags that the running interpreter supports. + + The iterable is ordered so that the best-matching tag is first in the + sequence. The exact preferential order to tags is interpreter-specific, but + in general the tag importance is in the order of: + + 1. Interpreter + 2. Platform + 3. ABI + + This order is due to the fact that an ABI is inherently tied to the + platform, but platform-specific code is not necessarily tied to the ABI. The + interpreter is the most important tag as it dictates basic support for any + wheel. + + The function returns an iterable in order to allow for the possible + short-circuiting of tag generation if the entire sequence is not necessary + and tag calculation happens to be expensive. + + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + + +Low Level Interface +''''''''''''''''''' + +The following functions are low-level implementation details. They should typically not +be needed in application code, unless the application has specialised requirements (for +example, constructing sets of supported tags for environments other than the running +interpreter). + +These functions capture the precise details of which environments support which tags. That +information is not defined in the compatibility tag standards but is noted as being up +to the implementation to provide. + + +.. attribute:: INTERPRETER_SHORT_NAMES + + A dictionary mapping interpreter names to their `abbreviation codes`_ + (e.g. ``"cpython"`` is ``"cp"``). All interpreter names are lower-case. + + +.. function:: interpreter_name() + + Returns the running interpreter's name. + + This typically acts as the prefix to the :attr:`~Tag.interpreter` tag. + + +.. function:: interpreter_version(*, warn=False) + + Returns the running interpreter's version. + + This typically acts as the suffix to the :attr:`~Tag.interpreter` tag. + + +.. function:: mac_platforms(version=None, arch=None) + + Yields the :attr:`~Tag.platform` tags for macOS. + + :param tuple version: A two-item tuple presenting the version of macOS. + Defaults to the current system's version. + :param str arch: The CPU architecture. Defaults to the architecture of the + current system, e.g. ``"x86_64"``. + + .. note:: + Equivalent support for the other major platforms is purposefully not + provided: + + - On Windows, platform compatibility is statically specified + - On Linux, code must be run on the system itself to determine + compatibility + + +.. function:: platform_tags(version=None, arch=None) + + Yields the :attr:`~Tag.platform` tags for the running interpreter. + + +.. function:: compatible_tags(python_version=None, interpreter=None, platforms=None) + + Yields the tags for an interpreter compatible with the Python version + specified by ``python_version``. + + The specific tags generated are: + + - ``py*-none-<platform>`` + - ``<interpreter>-none-any`` if ``interpreter`` is provided + - ``py*-none-any`` + + :param Sequence python_version: A one- or two-item sequence representing the + compatible version of Python. Defaults to + ``sys.version_info[:2]``. + :param str interpreter: The name of the interpreter (if known), e.g. + ``"cp38"``. Defaults to the current interpreter. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + +.. function:: cpython_tags(python_version=None, abis=None, platforms=None, *, warn=False) + + Yields the tags for the CPython interpreter. + + The specific tags generated are: + + - ``cp<python_version>-<abi>-<platform>`` + - ``cp<python_version>-abi3-<platform>`` + - ``cp<python_version>-none-<platform>`` + - ``cp<older version>-abi3-<platform>`` where "older version" is all older + minor versions down to Python 3.2 (when ``abi3`` was introduced) + + If ``python_version`` only provides a major-only version then only + user-provided ABIs via ``abis`` and the ``none`` ABI will be used. + + :param Sequence python_version: A one- or two-item sequence representing the + targeted Python version. Defaults to + ``sys.version_info[:2]``. + :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs + compatible with the current system. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + +.. function:: generic_tags(interpreter=None, abis=None, platforms=None, *, warn=False) + + Yields the tags for an interpreter which requires no specialization. + + This function should be used if one of the other interpreter-specific + functions provided by this module is not appropriate (i.e. not calculating + tags for a CPython interpreter). + + The specific tags generated are: + + - ``<interpreter>-<abi>-<platform>`` + + The ``"none"`` ABI will be added if it was not explicitly provided. + + :param str interpreter: The name of the interpreter. Defaults to being + calculated. + :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs + compatible with the current system. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + +.. _`abbreviation codes`: https://www.python.org/dev/peps/pep-0425/#python-tag +.. _`compressed tag set`: https://www.python.org/dev/peps/pep-0425/#compressed-tag-sets +.. _`platform compatibility tags`: https://packaging.python.org/specifications/platform-compatibility-tags/ diff --git a/docs/utils.rst b/docs/utils.rst new file mode 100644 index 0000000..8fbb025 --- /dev/null +++ b/docs/utils.rst @@ -0,0 +1,92 @@ +Utilities +========= + +.. currentmodule:: packaging.utils + + +A set of small, helper utilities for dealing with Python packages. + + +Reference +--------- + +.. function:: canonicalize_name(name) + + This function takes a valid Python package name, and returns the normalized + form of it. + + :param str name: The name to normalize. + + .. doctest:: + + >>> from packaging.utils import canonicalize_name + >>> canonicalize_name("Django") + 'django' + >>> canonicalize_name("oslo.concurrency") + 'oslo-concurrency' + >>> canonicalize_name("requests") + 'requests' + +.. function:: canonicalize_version(version) + + This function takes a string representing a package version (or a + :class:`~packaging.version.Version` instance), and returns the + normalized form of it. + + :param str version: The version to normalize. + + .. doctest:: + + >>> from packaging.utils import canonicalize_version + >>> canonicalize_version('1.4.0.0.0') + '1.4' + +.. function:: parse_wheel_filename(filename) + + This function takes the filename of a wheel file, and parses it, + returning a tuple of name, version, build number, and tags. + + The name part of the tuple is normalized. The version portion is an + instance of :class:`~packaging.version.Version`. The build number + is ``()`` if there is no build number in the wheel filename, + otherwise a two-item tuple of an integer for the leading digits and + a string for the rest of the build number. The tags portion is an + instance of :class:`~packaging.tags.Tag`. + + :param str filename: The name of the wheel file. + + .. doctest:: + + >>> from packaging.utils import parse_wheel_filename + >>> from packaging.tags import Tag + >>> from packaging.version import Version + >>> name, ver, build, tags = parse_wheel_filename("foo-1.0-py3-none-any.whl") + >>> name + 'foo' + >>> ver == Version('1.0') + True + >>> tags == {Tag("py3", "none", "any")} + True + >>> not build + True + +.. function:: parse_sdist_filename(filename) + + This function takes the filename of a sdist file (as specified + in the `Source distribution format`_ documentation), and parses + it, returning a tuple of the normalized name and version as + represented by an instance of :class:`~packaging.version.Version`. + + :param str filename: The name of the sdist file. + + .. doctest:: + + >>> from packaging.utils import parse_sdist_filename + >>> from packaging.version import Version + >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz") + >>> name + 'foo' + >>> ver == Version('1.0') + True + +.. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name diff --git a/docs/version.rst b/docs/version.rst new file mode 100644 index 0000000..a43cf78 --- /dev/null +++ b/docs/version.rst @@ -0,0 +1,292 @@ +Version Handling +================ + +.. currentmodule:: packaging.version + +A core requirement of dealing with packages is the ability to work with +versions. `PEP 440`_ defines the standard version scheme for Python packages +which has been implemented by this module. + +Usage +----- + +.. doctest:: + + >>> from packaging.version import Version, parse + >>> v1 = parse("1.0a5") + >>> v2 = Version("1.0") + >>> v1 + <Version('1.0a5')> + >>> v2 + <Version('1.0')> + >>> v1 < v2 + True + >>> v1.epoch + 0 + >>> v1.release + (1, 0) + >>> v1.pre + ('a', 5) + >>> v1.is_prerelease + True + >>> v2.is_prerelease + False + >>> Version("french toast") + Traceback (most recent call last): + ... + InvalidVersion: Invalid version: 'french toast' + >>> Version("1.0").post + >>> Version("1.0").is_postrelease + False + >>> Version("1.0.post0").post + 0 + >>> Version("1.0.post0").is_postrelease + True + + +Reference +--------- + +.. function:: parse(version) + + This function takes a version string and will parse it as a + :class:`Version` if the version is a valid PEP 440 version, otherwise it + will parse it as a deprecated :class:`LegacyVersion`. + + +.. class:: Version(version) + + This class abstracts handling of a project's versions. It implements the + scheme defined in `PEP 440`_. A :class:`Version` instance is comparison + aware and can be compared and sorted using the standard Python interfaces. + + :param str version: The string representation of a version which will be + parsed and normalized before use. + :raises InvalidVersion: If the ``version`` does not conform to PEP 440 in + any way then this exception will be raised. + + .. attribute:: public + + A string representing the public version portion of this ``Version()``. + + .. attribute:: base_version + + A string representing the base version of this :class:`Version` + instance. The base version is the public version of the project without + any pre or post release markers. + + .. attribute:: epoch + + An integer giving the version epoch of this :class:`Version` instance + + .. attribute:: release + + A tuple of integers giving the components of the release segment of + this :class:`Version` instance; that is, the ``1.2.3`` part of the + version number, including trailing zeroes but not including the epoch + or any prerelease/development/postrelease suffixes + + .. attribute:: major + + An integer representing the first item of :attr:`release` or ``0`` if unavailable. + + .. attribute:: minor + + An integer representing the second item of :attr:`release` or ``0`` if unavailable. + + .. attribute:: micro + + An integer representing the third item of :attr:`release` or ``0`` if unavailable. + + .. attribute:: local + + A string representing the local version portion of this ``Version()`` + if it has one, or ``None`` otherwise. + + .. attribute:: pre + + If this :class:`Version` instance represents a prerelease, this + attribute will be a pair of the prerelease phase (the string ``"a"``, + ``"b"``, or ``"rc"``) and the prerelease number (an integer). If this + instance is not a prerelease, the attribute will be `None`. + + .. attribute:: is_prerelease + + A boolean value indicating whether this :class:`Version` instance + represents a prerelease and/or development release. + + .. attribute:: dev + + If this :class:`Version` instance represents a development release, + this attribute will be the development release number (an integer); + otherwise, it will be `None`. + + .. attribute:: is_devrelease + + A boolean value indicating whether this :class:`Version` instance + represents a development release. + + .. attribute:: post + + If this :class:`Version` instance represents a postrelease, this + attribute will be the postrelease number (an integer); otherwise, it + will be `None`. + + .. attribute:: is_postrelease + + A boolean value indicating whether this :class:`Version` instance + represents a post-release. + + +.. class:: LegacyVersion(version) + + .. deprecated:: 20.5 + + Use :class:`Version` instead. + + This class abstracts handling of a project's versions if they are not + compatible with the scheme defined in `PEP 440`_. It implements a similar + interface to that of :class:`Version`. + + This class implements the previous de facto sorting algorithm used by + setuptools, however it will always sort as less than a :class:`Version` + instance. + + :param str version: The string representation of a version which will be + used as is. + + .. note:: + + :class:`LegacyVersion` instances are always ordered lower than :class:`Version` instances. + + >>> from packaging.version import Version, LegacyVersion + >>> v1 = Version("1.0") + >>> v2 = LegacyVersion("1.0") + >>> v1 > v2 + True + >>> v3 = LegacyVersion("1.3") + >>> v1 > v3 + True + + Also note that some strings are still valid PEP 440 strings (:class:`Version`), even if they look very similar to + other versions that are not (:class:`LegacyVersion`). Examples include versions with `Pre-release spelling`_ and + `Post-release spelling`_. + + >>> from packaging.version import parse + >>> v1 = parse('0.9.8a') + >>> v2 = parse('0.9.8beta') + >>> v3 = parse('0.9.8r') + >>> v4 = parse('0.9.8rev') + >>> v5 = parse('0.9.8t') + >>> v1 + <Version('0.9.8a0')> + >>> v1.is_prerelease + True + >>> v2 + <Version('0.9.8b0')> + >>> v2.is_prerelease + True + >>> v3 + <Version('0.9.8.post0')> + >>> v3.is_postrelease + True + >>> v4 + <Version('0.9.8.post0')> + >>> v4.is_postrelease + True + >>> v5 + <LegacyVersion('0.9.8t')> + >>> v5.is_prerelease + False + >>> v5.is_postrelease + False + + .. attribute:: public + + A string representing the public version portion of this + :class:`LegacyVersion`. This will always be the entire version string. + + .. attribute:: base_version + + A string representing the base version portion of this + :class:`LegacyVersion` instance. This will always be the entire version + string. + + .. attribute:: epoch + + This will always be ``-1`` since without `PEP 440`_ we do not have the + concept of version epochs. The value reflects the fact that + :class:`LegacyVersion` instances always compare less than + :class:`Version` instances. + + .. attribute:: release + + This will always be ``None`` since without `PEP 440`_ we do not have + the concept of a release segment or its components. It exists + primarily to allow a :class:`LegacyVersion` to be used as a stand in + for a :class:`Version`. + + .. attribute:: local + + This will always be ``None`` since without `PEP 440`_ we do not have + the concept of a local version. It exists primarily to allow a + :class:`LegacyVersion` to be used as a stand in for a :class:`Version`. + + .. attribute:: pre + + This will always be ``None`` since without `PEP 440`_ we do not have + the concept of a prerelease. It exists primarily to allow a + :class:`LegacyVersion` to be used as a stand in for a :class:`Version`. + + .. attribute:: is_prerelease + + A boolean value indicating whether this :class:`LegacyVersion` + represents a prerelease and/or development release. Since without + `PEP 440`_ there is no concept of pre or dev releases this will + always be `False` and exists for compatibility with :class:`Version`. + + .. attribute:: dev + + This will always be ``None`` since without `PEP 440`_ we do not have + the concept of a development release. It exists primarily to allow a + :class:`LegacyVersion` to be used as a stand in for a :class:`Version`. + + .. attribute:: is_devrelease + + A boolean value indicating whether this :class:`LegacyVersion` + represents a development release. Since without `PEP 440`_ there is + no concept of dev releases this will always be `False` and exists for + compatibility with :class:`Version`. + + .. attribute:: post + + This will always be ``None`` since without `PEP 440`_ we do not have + the concept of a postrelease. It exists primarily to allow a + :class:`LegacyVersion` to be used as a stand in for a :class:`Version`. + + .. attribute:: is_postrelease + + A boolean value indicating whether this :class:`LegacyVersion` + represents a post-release. Since without `PEP 440`_ there is no concept + of post-releases this will always be ``False`` and exists for + compatibility with :class:`Version`. + + +.. exception:: InvalidVersion + + Raised when attempting to create a :class:`Version` with a version string + that does not conform to `PEP 440`_. + + +.. data:: VERSION_PATTERN + + A string containing the regular expression used to match a valid version. + The pattern is not anchored at either end, and is intended for embedding + in larger expressions (for example, matching a version number as part of + a file name). The regular expression should be compiled with the + ``re.VERBOSE`` and ``re.IGNORECASE`` flags set. + + +.. _PEP 440: https://www.python.org/dev/peps/pep-0440/ +.. _Pre-release spelling : https://www.python.org/dev/peps/pep-0440/#pre-release-spelling +.. _Post-release spelling : https://www.python.org/dev/peps/pep-0440/#post-release-spelling |