From: JinWang An Date: Mon, 14 Dec 2020 06:38:43 +0000 (+0900) Subject: Imported Upstream version 19.0 X-Git-Tag: upstream/19.0^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=30a363ed20e732acd1a6b06d675b7016275c5895;p=platform%2Fupstream%2Fpython-packaging.git Imported Upstream version 19.0 --- diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..b5a35be --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +ignore = E203,W503,W504 diff --git a/.gitignore b/.gitignore index 608bf48..126d348 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,10 @@ .cache/ .coverage .idea +.venv __pycache__/ _build/ build/ dist/ -htmlcov/ \ No newline at end of file +htmlcov/ diff --git a/.travis.yml b/.travis.yml index f4b2ced..f6c835e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,26 @@ language: python cache: pip +python: 3.6 +env: TOXENV=py matrix: include: - python: 2.7 - env: TOXENV=py27 + - python: pypy + - python: pypy3 - python: 3.4 - env: TOXENV=py34 - python: 3.5 - env: TOXENV=py35 - - python: 3.6 - env: TOXENV=py36 - - python: nightly - env: TOXENV=py37 - - python: pypy - env: TOXENV=pypy - - env: TOXENV=pep8 - - env: TOXENV=py2pep8 - python: 3.6 - env: TOXENV=docs + - python: 3.7 + dist: xenial + - python: 3.8-dev + dist: xenial + - env: TOXENV=lint + - env: TOXENV=docs - env: TOXENV=packaging allow_failures: - - python: nightly + - python: 3.8-dev install: - pip install tox diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0fe202a..dcdf54c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,20 @@ Changelog --------- +19.0 - 2019-01-20 +~~~~~~~~~~~~~~~~~ + +* Fix string representation of PEP 508 direct URL requirements with markers. + +* Better handling of file URLs + + This allows for using ``file:///absolute/path``, which was previously + prevented due to the missing ``netloc``. + + This allows for all file URLs that ``urlunparse`` turns back into the + original URL to be valid. + + 18.0 - 2018-09-26 ~~~~~~~~~~~~~~~~~ diff --git a/MANIFEST.in b/MANIFEST.in index c713aff..36fcbd8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ include CHANGELOG.rst CONTRIBUTING.rst README.rst include LICENSE LICENSE.APACHE LICENSE.BSD include .coveragerc +include .flake8 include tox.ini recursive-include docs * diff --git a/dev-requirements.txt b/dev-requirements.txt index dd93cc2..e4cc498 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,6 @@ # Install our development requirements +pretend +pytest tox # Install packaging itself diff --git a/docs/conf.py b/docs/conf.py index cf1d01e..48f2354 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,7 +40,6 @@ master_doc = "index" # General information about the project. project = "Packaging" -copyright = "2014 Donald Stufft" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -53,6 +52,7 @@ 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. @@ -62,8 +62,8 @@ exclude_patterns = ["_build"] pygments_style = "sphinx" extlinks = { - 'issue': ('https://github.com/pypa/packaging/issues/%s', '#'), - 'pull': ('https://github.com/pypa/packaging/pull/%s', 'PR #'), + "issue": ("https://github.com/pypa/packaging/issues/%s", "#"), + "pull": ("https://github.com/pypa/packaging/pull/%s", "PR #"), } # -- Options for HTML output -------------------------------------------------- @@ -87,34 +87,19 @@ htmlhelp_basename = "packagingdoc" # -- Options for LaTeX output ------------------------------------------------- -latex_elements = { -} +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", - ), + ("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, - ) -] +man_pages = [("index", "packaging", "Packaging Documentation", ["Donald Stufft"], 1)] # -- Options for Texinfo output ----------------------------------------------- @@ -130,12 +115,10 @@ texinfo_documents = [ "packaging", "Core utilities for Python packages", "Miscellaneous", - ), + ) ] # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = { - "https://docs.python.org/": None, -} +intersphinx_mapping = {"https://docs.python.org/": None} epub_theme = "epub" diff --git a/docs/development/getting-started.rst b/docs/development/getting-started.rst index 7e6b45d..aa47fe6 100644 --- a/docs/development/getting-started.rst +++ b/docs/development/getting-started.rst @@ -9,8 +9,8 @@ dependencies, install packaging in ``editable`` mode. For example: .. code-block:: console $ # Create a virtualenv and activate it - $ pip install --requirement dev-requirements.txt - $ pip install --editable . + $ python -m pip install --requirement dev-requirements.txt + $ python -m pip install --editable . You are now ready to run the tests and build the documentation. @@ -23,7 +23,7 @@ automatically, so all you have to do is: .. code-block:: console - $ py.test + $ python -m pytest ... 62746 passed in 220.43 seconds @@ -73,4 +73,4 @@ The HTML documentation index can now be found at .. _`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 \ No newline at end of file +.. _`reStructured Text`: http://sphinx-doc.org/rest.html diff --git a/docs/development/index.rst b/docs/development/index.rst index f81799a..c0aea8a 100644 --- a/docs/development/index.rst +++ b/docs/development/index.rst @@ -13,6 +13,7 @@ bug check out `what to put in your bug report`_. 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..95375b2 --- /dev/null +++ b/docs/development/release-process.rst @@ -0,0 +1,26 @@ +Release Process +=============== + +#. Checkout the current ``master`` branch, with a clean working directory. +#. Modify the ``CHANGELOG.rst`` to include changes made since the last release. +#. Bump the version in ``packaging/__about__.py`` + +#. Install the latest ``setuptools``, ``wheel`` and ``twine`` packages + from PyPI:: + + $ pip install --upgrade setuptools wheel twine + +#. Ensure no ``dist/`` folder exists and then create the distribution files:: + + $ python setup.py sdist bdist_wheel + +#. Check the built distribution files with ``twine``:: + + $ twine check dist/* + +#. If all goes well, upload the build distribution files:: + + $ twine upload dist/* + +#. Bump the version for development in ``packaging/__about__.py`` and + ``CHANGELOG.rst``. diff --git a/docs/development/submitting-patches.rst b/docs/development/submitting-patches.rst index a2edde7..875b790 100644 --- a/docs/development/submitting-patches.rst +++ b/docs/development/submitting-patches.rst @@ -18,10 +18,9 @@ follow the directions on the :doc:`security page `. Code ---- -When in doubt, refer to :pep:`8` for Python code. You can check if your code -meets our automated requirements by running ``flake8`` against it. If you've -installed the development requirements this will automatically use our -configuration. You can also run the ``tox`` job with ``tox -e pep8``. +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 ``tox -e +lint``. `Write comments as complete sentences.`_ @@ -75,6 +74,8 @@ So, specifically: * 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/markers.rst b/docs/markers.rst index 7613dae..ad25361 100644 --- a/docs/markers.rst +++ b/docs/markers.rst @@ -16,7 +16,7 @@ Usage >>> marker = Marker("python_version>'2'") >>> marker "2"')> - >>> # We can evaluate a marker to see if the dependency is required + >>> # We can evaluate the marker to see if it is satisfied >>> marker.evaluate() True >>> # We can also override the environment diff --git a/packaging/__about__.py b/packaging/__about__.py index 21fc6ce..7481c9e 100644 --- a/packaging/__about__.py +++ b/packaging/__about__.py @@ -4,18 +4,24 @@ from __future__ import absolute_import, division, print_function __all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", ] __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "18.0" +__version__ = "19.0" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2014-2018 %s" % __author__ +__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/packaging/__init__.py b/packaging/__init__.py index 5ee6220..a0cf67d 100644 --- a/packaging/__init__.py +++ b/packaging/__init__.py @@ -4,11 +4,23 @@ from __future__ import absolute_import, division, print_function from .__about__ import ( - __author__, __copyright__, __email__, __license__, __summary__, __title__, - __uri__, __version__ + __author__, + __copyright__, + __email__, + __license__, + __summary__, + __title__, + __uri__, + __version__, ) __all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", ] diff --git a/packaging/_compat.py b/packaging/_compat.py index 210bb80..25da473 100644 --- a/packaging/_compat.py +++ b/packaging/_compat.py @@ -12,9 +12,9 @@ PY3 = sys.version_info[0] == 3 # flake8: noqa if PY3: - string_types = str, + string_types = (str,) else: - string_types = basestring, + string_types = (basestring,) def with_metaclass(meta, *bases): @@ -27,4 +27,5 @@ def with_metaclass(meta, *bases): class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) + + return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/packaging/_structures.py b/packaging/_structures.py index e9fc4a0..68dcca6 100644 --- a/packaging/_structures.py +++ b/packaging/_structures.py @@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function class Infinity(object): - def __repr__(self): return "Infinity" @@ -38,7 +37,6 @@ Infinity = Infinity() class NegativeInfinity(object): - def __repr__(self): return "-Infinity" diff --git a/packaging/markers.py b/packaging/markers.py index 5fdf510..eff5abb 100644 --- a/packaging/markers.py +++ b/packaging/markers.py @@ -17,8 +17,11 @@ from .specifiers import Specifier, InvalidSpecifier __all__ = [ - "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", - "Marker", "default_environment", + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", ] @@ -42,7 +45,6 @@ class UndefinedEnvironmentName(ValueError): class Node(object): - def __init__(self, value): self.value = value @@ -57,62 +59,52 @@ class Node(object): class Variable(Node): - def serialize(self): return str(self) class Value(Node): - def serialize(self): return '"{0}"'.format(self) class Op(Node): - def serialize(self): return str(self) VARIABLE = ( - L("implementation_version") | - L("platform_python_implementation") | - L("implementation_name") | - L("python_full_version") | - L("platform_release") | - L("platform_version") | - L("platform_machine") | - L("platform_system") | - L("python_version") | - L("sys_platform") | - L("os_name") | - L("os.name") | # PEP-345 - L("sys.platform") | # PEP-345 - L("platform.version") | # PEP-345 - L("platform.machine") | # PEP-345 - L("platform.python_implementation") | # PEP-345 - L("python_implementation") | # undocumented setuptools legacy - L("extra") + L("implementation_version") + | L("platform_python_implementation") + | L("implementation_name") + | L("python_full_version") + | L("platform_release") + | L("platform_version") + | L("platform_machine") + | L("platform_system") + | L("python_version") + | L("sys_platform") + | L("os_name") + | L("os.name") + | L("sys.platform") # PEP-345 + | L("platform.version") # PEP-345 + | L("platform.machine") # PEP-345 + | L("platform.python_implementation") # PEP-345 + | L("python_implementation") # PEP-345 + | L("extra") # undocumented setuptools legacy ) ALIASES = { - 'os.name': 'os_name', - 'sys.platform': 'sys_platform', - 'platform.version': 'platform_version', - 'platform.machine': 'platform_machine', - 'platform.python_implementation': 'platform_python_implementation', - 'python_implementation': 'platform_python_implementation' + "os.name": "os_name", + "sys.platform": "sys_platform", + "platform.version": "platform_version", + "platform.machine": "platform_machine", + "platform.python_implementation": "platform_python_implementation", + "python_implementation": "platform_python_implementation", } VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) VERSION_CMP = ( - L("===") | - L("==") | - L(">=") | - L("<=") | - L("!=") | - L("~=") | - L(">") | - L("<") + L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") ) MARKER_OP = VERSION_CMP | L("not in") | L("in") @@ -152,8 +144,11 @@ def _format_marker(marker, first=True): # where the single item is itself it's own list. In that case we want skip # the rest of this function so that we don't get extraneous () on the # outside. - if (isinstance(marker, list) and len(marker) == 1 and - isinstance(marker[0], (list, tuple))): + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): return _format_marker(marker[0]) if isinstance(marker, list): @@ -239,20 +234,20 @@ def _evaluate_markers(markers, environment): def format_full_version(info): - version = '{0.major}.{0.minor}.{0.micro}'.format(info) + version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel - if kind != 'final': + if kind != "final": version += kind[0] + str(info.serial) return version def default_environment(): - if hasattr(sys, 'implementation'): + if hasattr(sys, "implementation"): iver = format_full_version(sys.implementation.version) implementation_name = sys.implementation.name else: - iver = '0' - implementation_name = '' + iver = "0" + implementation_name = "" return { "implementation_name": implementation_name, @@ -270,13 +265,13 @@ def default_environment(): class Marker(object): - def __init__(self, marker): try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc:e.loc + 8]) + marker, marker[e.loc : e.loc + 8] + ) raise InvalidMarker(err_str) def __str__(self): diff --git a/packaging/requirements.py b/packaging/requirements.py index e8008a6..4d9688b 100644 --- a/packaging/requirements.py +++ b/packaging/requirements.py @@ -38,8 +38,8 @@ IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) NAME = IDENTIFIER("name") EXTRA = IDENTIFIER -URI = Regex(r'[^ ]+')("url") -URL = (AT + URI) +URI = Regex(r"[^ ]+")("url") +URL = AT + URI EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") @@ -48,17 +48,18 @@ VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), - joinString=",", adjacent=False)("_raw_spec") +VERSION_MANY = Combine( + VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False +)("_raw_spec") _VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start:t._original_end]) + lambda s, l, t: Marker(s[t._original_start : t._original_end]) ) MARKER_SEPARATOR = SEMICOLON MARKER = MARKER_SEPARATOR + MARKER_EXPR @@ -66,8 +67,7 @@ MARKER = MARKER_SEPARATOR + MARKER_EXPR VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) URL_AND_MARKER = URL + Optional(MARKER) -NAMED_REQUIREMENT = \ - NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) +NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd # pyparsing isn't thread safe during initialization, so we do it eagerly, see @@ -92,15 +92,21 @@ class Requirement(object): try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: - raise InvalidRequirement("Parse error at \"{0!r}\": {1}".format( - requirement_string[e.loc:e.loc + 8], e.msg - )) + raise InvalidRequirement( + 'Parse error at "{0!r}": {1}'.format( + requirement_string[e.loc : e.loc + 8], e.msg + ) + ) self.name = req.name if req.url: parsed_url = urlparse.urlparse(req.url) - if not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc): + if parsed_url.scheme == "file": + if urlparse.urlunparse(parsed_url) != req.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): raise InvalidRequirement("Invalid URL: {0}".format(req.url)) self.url = req.url else: @@ -120,6 +126,8 @@ class Requirement(object): if self.url: parts.append("@ {0}".format(self.url)) + if self.marker: + parts.append(" ") if self.marker: parts.append("; {0}".format(self.marker)) diff --git a/packaging/specifiers.py b/packaging/specifiers.py index 4c79899..743576a 100644 --- a/packaging/specifiers.py +++ b/packaging/specifiers.py @@ -19,7 +19,6 @@ class InvalidSpecifier(ValueError): class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): - @abc.abstractmethod def __str__(self): """ @@ -84,10 +83,7 @@ class _IndividualSpecifier(BaseSpecifier): if not match: raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) + self._spec = (match.group("operator").strip(), match.group("version").strip()) # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases @@ -99,11 +95,7 @@ class _IndividualSpecifier(BaseSpecifier): else "" ) - return "<{0}({1!r}{2})>".format( - self.__class__.__name__, - str(self), - pre, - ) + return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) def __str__(self): return "{0}{1}".format(*self._spec) @@ -194,8 +186,9 @@ class _IndividualSpecifier(BaseSpecifier): # If our version is a prerelease, and we were not set to allow # prereleases, then we'll store it for later incase nothing # else matches this specifier. - if (parsed_version.is_prerelease and not - (prereleases or self.prereleases)): + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): found_prereleases.append(version) # Either this is not a prerelease, or we should have been # accepting prereleases from the beginning. @@ -213,8 +206,7 @@ class _IndividualSpecifier(BaseSpecifier): class LegacySpecifier(_IndividualSpecifier): - _regex_str = ( - r""" + _regex_str = r""" (?P(==|!=|<=|>=|<|>)) \s* (?P @@ -225,10 +217,8 @@ class LegacySpecifier(_IndividualSpecifier): # them, and a comma since it's a version separator. ) """ - ) - _regex = re.compile( - r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) _operators = { "==": "equal", @@ -269,13 +259,13 @@ def _require_version_compare(fn): if not isinstance(prospective, Version): return False return fn(self, prospective, spec) + return wrapped class Specifier(_IndividualSpecifier): - _regex_str = ( - r""" + _regex_str = r""" (?P(~=|==|!=|<=|>=|<|>|===)) (?P (?: @@ -367,10 +357,8 @@ class Specifier(_IndividualSpecifier): ) ) """ - ) - _regex = re.compile( - r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) _operators = { "~=": "compatible", @@ -397,8 +385,7 @@ class Specifier(_IndividualSpecifier): prefix = ".".join( list( itertools.takewhile( - lambda x: (not x.startswith("post") and not - x.startswith("dev")), + lambda x: (not x.startswith("post") and not x.startswith("dev")), _version_split(spec), ) )[:-1] @@ -407,8 +394,9 @@ class Specifier(_IndividualSpecifier): # Add the prefix notation to the end of our string prefix += ".*" - return (self._get_operator(">=")(prospective, spec) and - self._get_operator("==")(prospective, prefix)) + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) @_require_version_compare def _compare_equal(self, prospective, spec): @@ -428,7 +416,7 @@ class Specifier(_IndividualSpecifier): # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - prospective = prospective[:len(spec)] + prospective = prospective[: len(spec)] # Pad out our two sides with zeros so that they both equal the same # length. @@ -567,27 +555,17 @@ def _pad_version(left, right): right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) # Get the rest of our versions - left_split.append(left[len(left_split[0]):]) - right_split.append(right[len(right_split[0]):]) + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) # Insert our padding - left_split.insert( - 1, - ["0"] * max(0, len(right_split[0]) - len(left_split[0])), - ) - right_split.insert( - 1, - ["0"] * max(0, len(left_split[0]) - len(right_split[0])), - ) + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - return ( - list(itertools.chain(*left_split)), - list(itertools.chain(*right_split)), - ) + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): # Split on , to break each indidivual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. @@ -721,10 +699,7 @@ class SpecifierSet(BaseSpecifier): # given version is contained within all of them. # Note: This use of all() here means that an empty set of specifiers # will always return True, this is an explicit design decision. - return all( - s.contains(item, prereleases=prereleases) - for s in self._specs - ) + return all(s.contains(item, prereleases=prereleases) for s in self._specs) def filter(self, iterable, prereleases=None): # Determine if we're forcing a prerelease or not, if we're not forcing diff --git a/packaging/utils.py b/packaging/utils.py index 4b94a82..8841878 100644 --- a/packaging/utils.py +++ b/packaging/utils.py @@ -36,13 +36,7 @@ def canonicalize_version(version): # Release segment # NB: This strips trailing '.0's to normalize - parts.append( - re.sub( - r'(\.0)+$', - '', - ".".join(str(x) for x in version.release) - ) - ) + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) # Pre-release if version.pre is not None: diff --git a/packaging/version.py b/packaging/version.py index 6ed5cbb..95157a1 100644 --- a/packaging/version.py +++ b/packaging/version.py @@ -10,14 +10,11 @@ import re from ._structures import Infinity -__all__ = [ - "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" -] +__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] _Version = collections.namedtuple( - "_Version", - ["epoch", "release", "dev", "pre", "post", "local"], + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) @@ -40,7 +37,6 @@ class InvalidVersion(ValueError): class _BaseVersion(object): - def __hash__(self): return hash(self._key) @@ -70,7 +66,6 @@ class _BaseVersion(object): class LegacyVersion(_BaseVersion): - def __init__(self, version): self._version = str(version) self._key = _legacy_cmpkey(self._version) @@ -126,12 +121,14 @@ class LegacyVersion(_BaseVersion): return False -_legacy_version_component_re = re.compile( - r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, -) +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) _legacy_version_replacement_map = { - "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", } @@ -215,10 +212,7 @@ VERSION_PATTERN = r""" class Version(_BaseVersion): - _regex = re.compile( - r"^\s*" + VERSION_PATTERN + r"\s*$", - re.VERBOSE | re.IGNORECASE, - ) + _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) def __init__(self, version): # Validate the version and parse it into pieces @@ -230,18 +224,11 @@ class Version(_BaseVersion): self._version = _Version( epoch=int(match.group("epoch")) if match.group("epoch") else 0, release=tuple(int(i) for i in match.group("release").split(".")), - pre=_parse_letter_version( - match.group("pre_l"), - match.group("pre_n"), - ), + pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")), post=_parse_letter_version( - match.group("post_l"), - match.group("post_n1") or match.group("post_n2"), - ), - dev=_parse_letter_version( - match.group("dev_l"), - match.group("dev_n"), + match.group("post_l"), match.group("post_n1") or match.group("post_n2") ), + dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")), local=_parse_local_version(match.group("local")), ) @@ -395,12 +382,7 @@ def _cmpkey(epoch, release, pre, post, dev, local): # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. release = tuple( - reversed(list( - itertools.dropwhile( - lambda x: x == 0, - reversed(release), - ) - )) + reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) ) # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. @@ -433,9 +415,6 @@ def _cmpkey(epoch, release, pre, post, dev, local): # - Numeric segments sort numerically # - Shorter versions sort before longer versions when the prefixes # match exactly - local = tuple( - (i, "") if isinstance(i, int) else (-Infinity, i) - for i in local - ) + local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local) return epoch, release, pre, post, dev, local diff --git a/setup.py b/setup.py index 7a1462b..23007c7 100644 --- a/setup.py +++ b/setup.py @@ -41,29 +41,19 @@ with open(os.path.join(base_dir, "CHANGELOG.rst")) as f: setup( name=about["__title__"], version=about["__version__"], - description=about["__summary__"], long_description=long_description, license=about["__license__"], url=about["__uri__"], - author=about["__author__"], author_email=about["__email__"], - - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', - - install_requires=[ - "pyparsing>=2.0.2", # Needed to avoid issue #91 - "six", - ], - + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + install_requires=["pyparsing>=2.0.2", "six"], # Needed to avoid issue #91 classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", "License :: OSI Approved :: BSD License", - "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", @@ -71,9 +61,7 @@ setup( "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", ], - - packages=[ - "packaging", - ], + packages=["packaging"], ) diff --git a/tasks/check.py b/tasks/check.py index 6138f41..930ab49 100644 --- a/tasks/check.py +++ b/tasks/check.py @@ -47,10 +47,12 @@ def pep440(cached=False): bar = progress.bar.ShadyBar("Fetching Versions") client = xmlrpc_client.Server("https://pypi.python.org/pypi") - data = dict([ - (project, client.package_releases(project, True)) - for project in bar.iter(client.list_packages()) - ]) + data = dict( + [ + (project, client.package_releases(project, True)) + for project in bar.iter(client.list_packages()) + ] + ) os.makedirs(os.path.dirname(cache_path), exist_ok=True) with open(cache_path, "w") as fp: @@ -62,17 +64,18 @@ def pep440(cached=False): # Determine the total number of versions which are compatible with the # current routine parsed_versions = [ - _parse_version(v) - for v in all_versions - if _parse_version(v) is not None + _parse_version(v) for v in all_versions if _parse_version(v) is not None ] # Determine a list of projects that sort exactly the same between # pkg_resources and PEP 440 compatible_sorting = [ - project for project, versions in data.items() - if (sorted(versions, key=pkg_resources.parse_version) == - sorted((x for x in versions if _parse_version(x)), key=Version)) + project + for project, versions in data.items() + if ( + sorted(versions, key=pkg_resources.parse_version) + == sorted((x for x in versions if _parse_version(x)), key=Version) + ) ] # Determine a list of projects that sort exactly the same between @@ -83,25 +86,29 @@ def pep440(cached=False): (p, [v for v in vs if _parse_version(v) is not None]) for p, vs in data.items() ) - if (sorted(versions, key=pkg_resources.parse_version) == - sorted(versions, key=Version)) + if ( + sorted(versions, key=pkg_resources.parse_version) + == sorted(versions, key=Version) + ) ] # Determine a list of projects which do not have any versions that are # valid with PEP 440 and which have any versions registered only_invalid_versions = [ - project for project, versions in data.items() - if (versions and not - [v for v in versions if _parse_version(v) is not None]) + project + for project, versions in data.items() + if (versions and not [v for v in versions if _parse_version(v) is not None]) ] # Determine a list of projects which have matching latest versions between # pkg_resources and PEP 440 differing_latest_versions = [ - project for project, versions in data.items() - if (sorted(versions, key=pkg_resources.parse_version)[-1:] != - sorted( - (x for x in versions if _parse_version(x)), key=Version)[-1:]) + project + for project, versions in data.items() + if ( + sorted(versions, key=pkg_resources.parse_version)[-1:] + != sorted((x for x in versions if _parse_version(x)), key=Version)[-1:] + ) ] # Print out our findings @@ -114,9 +121,7 @@ def pep440(cached=False): ) print( "Total Sorting Compatibility (Unfiltered): {}/{} ({:.2%})".format( - len(compatible_sorting), - len(data), - len(compatible_sorting) / len(data), + len(compatible_sorting), len(data), len(compatible_sorting) / len(data) ) ) print( diff --git a/tests/test_markers.py b/tests/test_markers.py index 0e2d96a..e271ad6 100644 --- a/tests/test_markers.py +++ b/tests/test_markers.py @@ -13,39 +13,54 @@ import pretend import pytest from packaging.markers import ( - Node, InvalidMarker, UndefinedComparison, UndefinedEnvironmentName, Marker, - default_environment, format_full_version, + Node, + InvalidMarker, + UndefinedComparison, + UndefinedEnvironmentName, + Marker, + default_environment, + format_full_version, ) VARIABLES = [ - "extra", "implementation_name", "implementation_version", "os_name", - "platform_machine", "platform_release", "platform_system", - "platform_version", "python_full_version", "python_version", - "platform_python_implementation", "sys_platform", + "extra", + "implementation_name", + "implementation_version", + "os_name", + "platform_machine", + "platform_release", + "platform_system", + "platform_version", + "python_full_version", + "python_version", + "platform_python_implementation", + "sys_platform", ] PEP_345_VARIABLES = [ - "os.name", "sys.platform", "platform.version", "platform.machine", + "os.name", + "sys.platform", + "platform.version", + "platform.machine", "platform.python_implementation", ] -SETUPTOOLS_VARIABLES = [ - "python_implementation", -] +SETUPTOOLS_VARIABLES = ["python_implementation"] -OPERATORS = [ - "===", "==", ">=", "<=", "!=", "~=", ">", "<", "in", "not in", -] +OPERATORS = ["===", "==", ">=", "<=", "!=", "~=", ">", "<", "in", "not in"] VALUES = [ - "1.0", "5.6a0", "dog", "freebsd", "literally any string can go here", + "1.0", + "5.6a0", + "dog", + "freebsd", + "literally any string can go here", "things @#4 dsfd (((", ] class TestNode: - @pytest.mark.parametrize("value", ["one", "two", None, 3, 5, []]) def test_accepts_value(self, value): assert Node(value).value == value @@ -64,12 +79,11 @@ class TestNode: class TestOperatorEvaluation: - def test_prefers_pep440(self): - assert Marker('"2.7.9" < "foo"').evaluate(dict(foo='2.7.10')) + assert Marker('"2.7.9" < "foo"').evaluate(dict(foo="2.7.10")) def test_falls_back_to_python(self): - assert Marker('"b" > "a"').evaluate(dict(a='a')) + assert Marker('"b" > "a"').evaluate(dict(a="a")) def test_fails_when_undefined(self): with pytest.raises(UndefinedComparison): @@ -77,15 +91,14 @@ class TestOperatorEvaluation: FakeVersionInfo = collections.namedtuple( - "FakeVersionInfo", - ["major", "minor", "micro", "releaselevel", "serial"], + "FakeVersionInfo", ["major", "minor", "micro", "releaselevel", "serial"] ) class TestDefaultEnvironment: - - @pytest.mark.skipif(hasattr(sys, 'implementation'), - reason='sys.implementation does exist') + @pytest.mark.skipif( + hasattr(sys, "implementation"), reason="sys.implementation does exist" + ) def test_matches_expected_no_sys_implementation(self): environment = default_environment() @@ -103,8 +116,9 @@ class TestDefaultEnvironment: "sys_platform": sys.platform, } - @pytest.mark.skipif(not hasattr(sys, 'implementation'), - reason='sys.implementation does not exist') + @pytest.mark.skipif( + not hasattr(sys, "implementation"), reason="sys.implementation does not exist" + ) def test_matches_expected_deleted_sys_implementation(self, monkeypatch): monkeypatch.delattr(sys, "implementation") @@ -124,14 +138,13 @@ class TestDefaultEnvironment: "sys_platform": sys.platform, } - @pytest.mark.skipif(not hasattr(sys, 'implementation'), - reason='sys.implementation does not exist') + @pytest.mark.skipif( + not hasattr(sys, "implementation"), reason="sys.implementation does not exist" + ) def test_matches_expected(self): environment = default_environment() - iver = "{0.major}.{0.minor}.{0.micro}".format( - sys.implementation.version - ) + iver = "{0.major}.{0.minor}.{0.micro}".format(sys.implementation.version) if sys.implementation.version.releaselevel != "final": iver = "{0}{1[0]}{2}".format( iver, @@ -153,14 +166,16 @@ class TestDefaultEnvironment: "sys_platform": sys.platform, } - @pytest.mark.skipif(hasattr(sys, 'implementation'), - reason='sys.implementation does exist') + @pytest.mark.skipif( + hasattr(sys, "implementation"), reason="sys.implementation does exist" + ) def test_monkeypatch_sys_implementation(self, monkeypatch): monkeypatch.setattr( - sys, "implementation", - pretend.stub(version=FakeVersionInfo(3, 4, 2, "final", 0), - name="linux"), - raising=False) + sys, + "implementation", + pretend.stub(version=FakeVersionInfo(3, 4, 2, "final", 0), name="linux"), + raising=False, + ) environment = default_environment() assert environment == { @@ -179,21 +194,21 @@ class TestDefaultEnvironment: def tests_when_releaselevel_final(self): v = FakeVersionInfo(3, 4, 2, "final", 0) - assert format_full_version(v) == '3.4.2' + assert format_full_version(v) == "3.4.2" def tests_when_releaselevel_not_final(self): v = FakeVersionInfo(3, 4, 2, "beta", 4) - assert format_full_version(v) == '3.4.2b4' + assert format_full_version(v) == "3.4.2b4" class TestMarker: - @pytest.mark.parametrize( "marker_string", [ "{0} {1} {2!r}".format(*i) for i in itertools.product(VARIABLES, OPERATORS, VALUES) - ] + [ + ] + + [ "{2!r} {1} {0}".format(*i) for i in itertools.product(VARIABLES, OPERATORS, VALUES) ], @@ -220,7 +235,6 @@ class TestMarker: # Test the different quoting rules ("python_version == '2.7'", 'python_version == "2.7"'), ('python_version == "2.7"', 'python_version == "2.7"'), - # Test and/or expressions ( 'python_version == "2.7" and os_name == "linux"', @@ -236,7 +250,6 @@ class TestMarker: 'python_version == "2.7" and os_name == "linux" or ' 'sys_platform == "win32"', ), - # Test nested expressions and grouping with () ('(python_version == "2.7")', 'python_version == "2.7"'), ( @@ -293,16 +306,8 @@ class TestMarker: {"os_name": "other", "python_version": "2.7.4"}, False, ), - ( - "extra == 'security'", - {"extra": "quux"}, - False, - ), - ( - "extra == 'security'", - {"extra": "security"}, - True, - ), + ("extra == 'security'", {"extra": "quux"}, False), + ("extra == 'security'", {"extra": "security"}, True), ], ) def test_evaluates(self, marker_string, environment, expected): @@ -314,7 +319,8 @@ class TestMarker: [ "{0} {1} {2!r}".format(*i) for i in itertools.product(PEP_345_VARIABLES, OPERATORS, VALUES) - ] + [ + ] + + [ "{2!r} {1} {0}".format(*i) for i in itertools.product(PEP_345_VARIABLES, OPERATORS, VALUES) ], @@ -327,16 +333,8 @@ class TestMarker: [ ("os.name == '{0}'".format(os.name), None, True), ("sys.platform == 'win32'", {"sys_platform": "linux2"}, False), - ( - "platform.version in 'Ubuntu'", - {"platform_version": "#39"}, - False, - ), - ( - "platform.machine=='x86_64'", - {"platform_machine": "x86_64"}, - True, - ), + ("platform.version in 'Ubuntu'", {"platform_version": "#39"}, False), + ("platform.machine=='x86_64'", {"platform_machine": "x86_64"}, True), ( "platform.python_implementation=='Jython'", {"platform_python_implementation": "CPython"}, @@ -350,8 +348,7 @@ class TestMarker: ), ], ) - def test_evaluate_pep345_markers(self, marker_string, environment, - expected): + def test_evaluate_pep345_markers(self, marker_string, environment, expected): args = [] if environment is None else [environment] assert Marker(marker_string).evaluate(*args) == expected @@ -360,7 +357,8 @@ class TestMarker: [ "{0} {1} {2!r}".format(*i) for i in itertools.product(SETUPTOOLS_VARIABLES, OPERATORS, VALUES) - ] + [ + ] + + [ "{2!r} {1} {0}".format(*i) for i in itertools.product(SETUPTOOLS_VARIABLES, OPERATORS, VALUES) ], diff --git a/tests/test_requirements.py b/tests/test_requirements.py index cd42aa9..0e56cd9 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -12,23 +12,28 @@ from packaging.specifiers import SpecifierSet class TestRequirements: - def test_string_specifier_marker(self): requirement = 'name[bar]>=3; python_version == "2.7"' req = Requirement(requirement) assert str(req) == requirement def test_string_url(self): - requirement = 'name@ http://foo.com' + requirement = "name@ http://foo.com" + req = Requirement(requirement) + assert str(req) == requirement + + def test_string_url_with_marker(self): + requirement = 'name@ http://foo.com ; extra == "feature"' req = Requirement(requirement) assert str(req) == requirement def test_repr(self): - req = Requirement('name') + req = Requirement("name") assert repr(req) == "" - def _assert_requirement(self, req, name, url=None, extras=[], - specifier='', marker=None): + def _assert_requirement( + self, req, name, url=None, extras=[], specifier="", marker=None + ): assert req.name == name assert req.url == url assert sorted(req.extras) == sorted(extras) @@ -62,8 +67,9 @@ class TestRequirements: def test_with_legacy_version_and_marker(self): req = Requirement("name>=1.x.y;python_version=='2.6'") - self._assert_requirement(req, "name", specifier=">=1.x.y", - marker='python_version == "2.6"') + self._assert_requirement( + req, "name", specifier=">=1.x.y", marker='python_version == "2.6"' + ) def test_version_with_parens_and_whitespace(self): req = Requirement("name (==4)") @@ -88,12 +94,12 @@ class TestRequirements: def test_url(self): url_section = "@ http://example.com" parsed = URL.parseString(url_section) - assert parsed.url == 'http://example.com' + assert parsed.url == "http://example.com" def test_url_and_marker(self): instring = "@ http://example.com ; os_name=='a'" parsed = URL_AND_MARKER.parseString(instring) - assert parsed.url == 'http://example.com' + assert parsed.url == "http://example.com" assert str(parsed.marker) == 'os_name == "a"' def test_invalid_url(self): @@ -102,30 +108,42 @@ class TestRequirements: assert "Invalid URL: " in str(e) assert "gopher:/foo/com" in str(e) + def test_file_url(self): + req = Requirement("name @ file:///absolute/path") + self._assert_requirement(req, "name", "file:///absolute/path") + req = Requirement("name @ file://.") + self._assert_requirement(req, "name", "file://.") + + def test_invalid_file_urls(self): + with pytest.raises(InvalidRequirement): + Requirement("name @ file:.") + with pytest.raises(InvalidRequirement): + Requirement("name @ file:/.") + def test_extras_and_url_and_marker(self): - req = Requirement( - "name [fred,bar] @ http://foo.com ; python_version=='2.7'") - self._assert_requirement(req, "name", extras=["bar", "fred"], - url="http://foo.com", - marker='python_version == "2.7"') + req = Requirement("name [fred,bar] @ http://foo.com ; python_version=='2.7'") + self._assert_requirement( + req, + "name", + extras=["bar", "fred"], + url="http://foo.com", + marker='python_version == "2.7"', + ) def test_complex_url_and_marker(self): url = "https://example.com/name;v=1.1/?query=foo&bar=baz#blah" req = Requirement("foo @ %s ; python_version=='3.4'" % url) - self._assert_requirement(req, "foo", url=url, - marker='python_version == "3.4"') + self._assert_requirement(req, "foo", url=url, marker='python_version == "3.4"') def test_multiple_markers(self): req = Requirement( - "name[quux, strange];python_version<'2.7' and " - "platform_version=='2'") + "name[quux, strange];python_version<'2.7' and " "platform_version=='2'" + ) marker = 'python_version < "2.7" and platform_version == "2"' - self._assert_requirement(req, "name", extras=["strange", "quux"], - marker=marker) + self._assert_requirement(req, "name", extras=["strange", "quux"], marker=marker) def test_multiple_comparsion_markers(self): - req = Requirement( - "name; os_name=='a' and os_name=='b' or os_name=='c'") + req = Requirement("name; os_name=='a' and os_name=='b' or os_name=='c'") marker = 'os_name == "a" and os_name == "b" or os_name == "c"' self._assert_requirement(req, "name", marker=marker) @@ -160,7 +178,7 @@ class TestRequirements: def test_sys_platform_linux_equal(self): req = Requirement('something>=1.2.3; sys_platform == "foo"') - assert req.name == 'something' + assert req.name == "something" assert req.marker is not None assert req.marker.evaluate(dict(sys_platform="foo")) is True assert req.marker.evaluate(dict(sys_platform="bar")) is False @@ -168,7 +186,7 @@ class TestRequirements: def test_sys_platform_linux_in(self): req = Requirement("aviato>=1.2.3; 'f' in sys_platform") - assert req.name == 'aviato' + assert req.name == "aviato" assert req.marker is not None assert req.marker.evaluate(dict(sys_platform="foo")) is True assert req.marker.evaluate(dict(sys_platform="bar")) is False diff --git a/tests/test_specifiers.py b/tests/test_specifiers.py index adc10e5..a3feecc 100644 --- a/tests/test_specifiers.py +++ b/tests/test_specifiers.py @@ -9,7 +9,10 @@ import operator import pytest from packaging.specifiers import ( - InvalidSpecifier, LegacySpecifier, Specifier, SpecifierSet, + InvalidSpecifier, + LegacySpecifier, + Specifier, + SpecifierSet, ) from packaging.version import LegacyVersion, Version, parse @@ -17,17 +20,29 @@ from .test_version import VERSIONS, LEGACY_VERSIONS LEGACY_SPECIFIERS = [ - "==2.1.0.3", "!=2.2.0.5", "<=5", ">=7.9a1", "<1.0.dev1", ">2.0.post1", + "==2.1.0.3", + "!=2.2.0.5", + "<=5", + ">=7.9a1", + "<1.0.dev1", + ">2.0.post1", ] SPECIFIERS = [ - "~=2.0", "==2.1.*", "==2.1.0.3", "!=2.2.*", "!=2.2.0.5", "<=5", ">=7.9a1", - "<1.0.dev1", ">2.0.post1", "===lolwat", + "~=2.0", + "==2.1.*", + "==2.1.0.3", + "!=2.2.*", + "!=2.2.0.5", + "<=5", + ">=7.9a1", + "<1.0.dev1", + ">2.0.post1", + "===lolwat", ] class TestSpecifier: - @pytest.mark.parametrize("specifier", SPECIFIERS) def test_specifiers_valid(self, specifier): Specifier(specifier) @@ -37,42 +52,33 @@ class TestSpecifier: [ # Operator-less specifier "2.0", - # Invalid operator "=>2.0", - # Version-less specifier "==", - # Local segment on operators which don't support them "~=1.0+5", ">=1.0+deadbeef", "<=1.0+abc123", ">1.0+watwat", "<1.0+1.0", - # Prefix matching on operators which don't support them "~=1.0.*", ">=1.0.*", "<=1.0.*", ">1.0.*", "<1.0.*", - # Combination of local and prefix matching on operators which do # support one or the other "==1.0.*+5", "!=1.0.*+deadbeef", - # Prefix matching cannot be used inside of a local version "==1.0+5.*", "!=1.0+deadbeef.*", - # Prefix matching must appear at the end "==1.0.*.5", - # Compatible operator requires 2 digits in the release operator "~=1", - # Cannot use a prefix matching after a .devN version "==1.0.dev1.*", "!=1.0.dev1.*", @@ -99,7 +105,6 @@ class TestSpecifier: "1.0.DEV1", "1.0-DEV", "1.0-DEV1", - # Various alpha incarnations "1.0a", "1.0.a", @@ -121,7 +126,6 @@ class TestSpecifier: "1.0.ALPHA1", "1.0-ALPHA", "1.0-ALPHA1", - # Various beta incarnations "1.0b", "1.0.b", @@ -143,7 +147,6 @@ class TestSpecifier: "1.0.BETA1", "1.0-BETA", "1.0-BETA1", - # Various release candidate incarnations "1.0c", "1.0.c", @@ -165,7 +168,6 @@ class TestSpecifier: "1.0.RC1", "1.0-RC", "1.0-RC1", - # Various post release incarnations "1.0post", "1.0.post", @@ -181,10 +183,8 @@ class TestSpecifier: "1.0-POST", "1.0-POST1", "1.0-5", - # Local version case insensitivity "1.0+AbC" - # Integer Normalization "1.01", "1.0a05", @@ -195,7 +195,6 @@ class TestSpecifier: "1.1.dev09000", "00!1.2", "0100!0.0", - # Various other normalizations "v1.0", " \r \f \v v1.0\t\n", @@ -221,7 +220,6 @@ class TestSpecifier: (">2.0", ">2.0"), (">=2.0", ">=2.0"), ("~=2.0", "~=2.0"), - # Spaces should be removed ("< 2", "<2"), ], @@ -241,19 +239,14 @@ class TestSpecifier: itertools.chain( * # Verify that the equal (==) operator works correctly - [ - [(x, x, operator.eq) for x in SPECIFIERS] - ] + + [[(x, x, operator.eq) for x in SPECIFIERS]] + + # Verify that the not equal (!=) operator works correctly [ - [ - (x, y, operator.ne) - for j, y in enumerate(SPECIFIERS) - if i != j - ] + [(x, y, operator.ne) for j, y in enumerate(SPECIFIERS) if i != j] for i, x in enumerate(SPECIFIERS) ] - ) + ), ) def test_comparison_true(self, left, right, op): assert op(Specifier(left), Specifier(right)) @@ -265,19 +258,14 @@ class TestSpecifier: itertools.chain( * # Verify that the equal (==) operator works correctly - [ - [(x, x, operator.ne) for x in SPECIFIERS] - ] + + [[(x, x, operator.ne) for x in SPECIFIERS]] + + # Verify that the not equal (!=) operator works correctly [ - [ - (x, y, operator.eq) - for j, y in enumerate(SPECIFIERS) - if i != j - ] + [(x, y, operator.eq) for j, y in enumerate(SPECIFIERS) if i != j] for i, x in enumerate(SPECIFIERS) ] - ) + ), ) def test_comparison_false(self, left, right, op): assert not op(Specifier(left), Specifier(right)) @@ -306,7 +294,6 @@ class TestSpecifier: ("2.0+deadbeef", "==2.0+deadbeef"), ("2.0+deadbeef", "==2.0.0+deadbeef"), ("2.0+deadbeef.0", "==2.0.0+deadbeef.00"), - # Test the equality operation with a prefix ("2.dev1", "==2.*"), ("2a1", "==2.*"), @@ -322,7 +309,6 @@ class TestSpecifier: ("2.0.post1", "==2.0.post1.*"), ("2.0.post1.dev1", "==2.0.post1.*"), ("2.1+local.version", "==2.1.*"), - # Test the in-equality operation ("2.1", "!=2"), ("2.1", "!=2.0"), @@ -330,11 +316,9 @@ class TestSpecifier: ("2.0.1", "!=2.0"), ("2.0.1", "!=2.0.0"), ("2.0", "!=2.0+deadbeef"), - # Test the in-equality operation with a prefix ("2.0", "!=3.*"), ("2.1", "!=2.0.*"), - # Test the greater than equal operation ("2.0", ">=2"), ("2.0", ">=2.0"), @@ -342,7 +326,6 @@ class TestSpecifier: ("2.0.post1", ">=2"), ("2.0.post1.dev1", ">=2"), ("3", ">=2"), - # Test the less than equal operation ("2.0", "<=2"), ("2.0", "<=2.0"), @@ -356,25 +339,21 @@ class TestSpecifier: ("2.0c1.post1.dev1", "<=2"), ("2.0rc1", "<=2"), ("1", "<=2"), - # Test the greater than operation ("3", ">2"), ("2.1", ">2.0"), ("2.0.1", ">2"), ("2.1.post1", ">2"), ("2.1+local.version", ">2"), - # Test the less than operation ("1", "<2"), ("2.0", "<2.1"), ("2.0.dev0", "<2.1"), - # Test the compatibility operation ("1", "~=1.0"), ("1.0.1", "~=1.0"), ("1.1", "~=1.0"), ("1.9999999", "~=1.0"), - # Test that epochs are handled sanely ("2!1.0", "~=2!1.0"), ("2!1.0", "==2!1.*"), @@ -385,11 +364,11 @@ class TestSpecifier: ("2!1.0", ">=2.0"), ("1.0", "<2!0.1"), ("2!1.0", ">2.0"), - # Test some normalization rules ("2.0.5", ">2.0dev"), ] - ] + [ + ] + + [ (v, s, False) for v, s in [ # Test the equality operation @@ -397,11 +376,9 @@ class TestSpecifier: ("2.1", "==2.0"), ("2.1", "==2.0.0"), ("2.0", "==2.0+deadbeef"), - # Test the equality operation with a prefix ("2.0", "==3.*"), ("2.1", "==2.0.*"), - # Test the in-equality operation ("2.0", "!=2"), ("2.0", "!=2.0"), @@ -413,7 +390,6 @@ class TestSpecifier: ("2.0+deadbeef", "!=2.0+deadbeef"), ("2.0+deadbeef", "!=2.0.0+deadbeef"), ("2.0+deadbeef.0", "!=2.0.0+deadbeef.00"), - # Test the in-equality operation with a prefix ("2.dev1", "!=2.*"), ("2a1", "!=2.*"), @@ -428,7 +404,6 @@ class TestSpecifier: ("2.0.0", "!=2.*"), ("2.0.post1", "!=2.0.post1.*"), ("2.0.post1.dev1", "!=2.0.post1.*"), - # Test the greater than equal operation ("2.0.dev1", ">=2"), ("2.0a1", ">=2"), @@ -439,12 +414,10 @@ class TestSpecifier: ("2.0c1.post1.dev1", ">=2"), ("2.0rc1", ">=2"), ("1", ">=2"), - # Test the less than equal operation ("2.0.post1", "<=2"), ("2.0.post1.dev1", "<=2"), ("3", "<=2"), - # Test the greater than operation ("1", ">2"), ("2.0.dev1", ">2"), @@ -459,7 +432,6 @@ class TestSpecifier: ("2.0.post1", ">2"), ("2.0.post1.dev1", ">2"), ("2.0+local.version", ">2"), - # Test the less than operation ("2.0.dev1", "<2"), ("2.0a1", "<2"), @@ -473,12 +445,10 @@ class TestSpecifier: ("2.post1", "<2"), ("2.post1.dev1", "<2"), ("3", "<2"), - # Test the compatibility operation ("2.0", "~=1.0"), ("1.1.0", "~=1.0.0"), ("1.1.post1", "~=1.0.0"), - # Test that epochs are handled sanely ("1.0", "~=2!1.0"), ("2!1.0", "~=1.0"), @@ -587,9 +557,7 @@ class TestSpecifier: def test_specifier_filter(self, specifier, prereleases, input, expected): spec = Specifier(specifier) - kwargs = ( - {"prereleases": prereleases} if prereleases is not None else {} - ) + kwargs = {"prereleases": prereleases} if prereleases is not None else {} assert list(spec.filter(input, **kwargs)) == expected @@ -598,50 +566,44 @@ class TestSpecifier: assert Specifier("==1.0").contains(LegacyVersion("1.0")) @pytest.mark.parametrize( - ('spec', 'op'), + ("spec", "op"), [ - ('~=2.0', '~='), - ('==2.1.*', '=='), - ('==2.1.0.3', '=='), - ('!=2.2.*', '!='), - ('!=2.2.0.5', '!='), - ('<=5', '<='), - ('>=7.9a1', '>='), - ('<1.0.dev1', '<'), - ('>2.0.post1', '>'), - ('===lolwat', '==='), - ] + ("~=2.0", "~="), + ("==2.1.*", "=="), + ("==2.1.0.3", "=="), + ("!=2.2.*", "!="), + ("!=2.2.0.5", "!="), + ("<=5", "<="), + (">=7.9a1", ">="), + ("<1.0.dev1", "<"), + (">2.0.post1", ">"), + ("===lolwat", "==="), + ], ) def test_specifier_operator_property(self, spec, op): assert Specifier(spec).operator == op @pytest.mark.parametrize( - ('spec', 'version'), + ("spec", "version"), [ - ('~=2.0', '2.0'), - ('==2.1.*', '2.1.*'), - ('==2.1.0.3', '2.1.0.3'), - ('!=2.2.*', '2.2.*'), - ('!=2.2.0.5', '2.2.0.5'), - ('<=5', '5'), - ('>=7.9a1', '7.9a1'), - ('<1.0.dev1', '1.0.dev1'), - ('>2.0.post1', '2.0.post1'), - ('===lolwat', 'lolwat'), - ] + ("~=2.0", "2.0"), + ("==2.1.*", "2.1.*"), + ("==2.1.0.3", "2.1.0.3"), + ("!=2.2.*", "2.2.*"), + ("!=2.2.0.5", "2.2.0.5"), + ("<=5", "5"), + (">=7.9a1", "7.9a1"), + ("<1.0.dev1", "1.0.dev1"), + (">2.0.post1", "2.0.post1"), + ("===lolwat", "lolwat"), + ], ) def test_specifier_version_property(self, spec, version): assert Specifier(spec).version == version @pytest.mark.parametrize( ("spec", "expected_length"), - [ - ("", 0), - ("==2.0", 1), - (">=2.0", 1), - (">=2.0,<3", 2), - (">=2.0,<3,==2.4", 3), - ], + [("", 0), ("==2.0", 1), (">=2.0", 1), (">=2.0,<3", 2), (">=2.0,<3,==2.4", 3)], ) def test_length(self, spec, expected_length): spec = SpecifierSet(spec) @@ -664,7 +626,6 @@ class TestSpecifier: class TestLegacySpecifier: - @pytest.mark.parametrize( ("version", "spec", "expected"), [ @@ -674,14 +635,12 @@ class TestLegacySpecifier: ("2.0", "==2"), ("2.0", "==2.0"), ("2.0", "==2.0.0"), - # Test the in-equality operation ("2.1", "!=2"), ("2.1", "!=2.0"), ("2.0.1", "!=2"), ("2.0.1", "!=2.0"), ("2.0.1", "!=2.0.0"), - # Test the greater than equal operation ("2.0", ">=2"), ("2.0", ">=2.0"), @@ -689,7 +648,6 @@ class TestLegacySpecifier: ("2.0.post1", ">=2"), ("2.0.post1.dev1", ">=2"), ("3", ">=2"), - # Test the less than equal operation ("2.0", "<=2"), ("2.0", "<=2.0"), @@ -703,28 +661,25 @@ class TestLegacySpecifier: ("2.0c1.post1.dev1", "<=2"), ("2.0rc1", "<=2"), ("1", "<=2"), - # Test the greater than operation ("3", ">2"), ("2.1", ">2.0"), - # Test the less than operation ("1", "<2"), ("2.0", "<2.1"), ] - ] + [ + ] + + [ (v, s, False) for v, s in [ # Test the equality operation ("2.1", "==2"), ("2.1", "==2.0"), ("2.1", "==2.0.0"), - # Test the in-equality operation ("2.0", "!=2"), ("2.0", "!=2.0"), ("2.0", "!=2.0.0"), - # Test the greater than equal operation ("2.0.dev1", ">=2"), ("2.0a1", ">=2"), @@ -735,12 +690,10 @@ class TestLegacySpecifier: ("2.0c1.post1.dev1", ">=2"), ("2.0rc1", ">=2"), ("1", ">=2"), - # Test the less than equal operation ("2.0.post1", "<=2"), ("2.0.post1.dev1", "<=2"), ("3", "<=2"), - # Test the greater than operation ("1", ">2"), ("2.0.dev1", ">2"), @@ -752,7 +705,6 @@ class TestLegacySpecifier: ("2.0c1.post1.dev1", ">2"), ("2.0rc1", ">2"), ("2.0", ">2"), - # Test the less than operation ("3", "<2"), ] @@ -801,11 +753,7 @@ class TestLegacySpecifier: class TestSpecifierSet: - - @pytest.mark.parametrize( - "version", - VERSIONS + LEGACY_VERSIONS, - ) + @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS) def test_empty_specifier(self, version): spec = SpecifierSet(prereleases=True) @@ -854,10 +802,7 @@ class TestSpecifierSet: assert not spec.contains("1.0.dev1", prereleases=False) @pytest.mark.parametrize( - ( - "specifier", "specifier_prereleases", "prereleases", "input", - "expected", - ), + ("specifier", "specifier_prereleases", "prereleases", "input", "expected"), [ # General test of the filter method ("", None, None, ["1.0", "2.0a1"], ["1.0"]), @@ -865,12 +810,10 @@ class TestSpecifierSet: ("", None, None, ["1.0a1"], ["1.0a1"]), ("", None, None, ["1.0", Version("2.0")], ["1.0", Version("2.0")]), ("", None, None, ["2.0dog", "1.0"], ["1.0"]), - # Test overriding with the prereleases parameter on filter ("", None, False, ["1.0a1"], []), (">=1.0.dev1", None, False, ["1.0", "2.0a1"], ["1.0"]), ("", None, True, ["1.0", "2.0a1"], ["1.0", "2.0a1"]), - # Test overriding with the overall specifier ("", True, None, ["1.0", "2.0a1"], ["1.0", "2.0a1"]), ("", False, None, ["1.0", "2.0a1"], ["1.0"]), @@ -880,16 +823,15 @@ class TestSpecifierSet: ("", False, None, ["1.0a1"], []), ], ) - def test_specifier_filter(self, specifier_prereleases, specifier, - prereleases, input, expected): + def test_specifier_filter( + self, specifier_prereleases, specifier, prereleases, input, expected + ): if specifier_prereleases is None: spec = SpecifierSet(specifier) else: spec = SpecifierSet(specifier, prereleases=specifier_prereleases) - kwargs = ( - {"prereleases": prereleases} if prereleases is not None else {} - ) + kwargs = {"prereleases": prereleases} if prereleases is not None else {} assert list(spec.filter(input, **kwargs)) == expected @@ -908,10 +850,8 @@ class TestSpecifierSet: (">2.0", ">2.0"), (">=2.0", ">=2.0"), ("~=2.0", "~=2.0"), - # Spaces should be removed ("< 2", "<2"), - # Multiple item specifiers should work ("!=2.0,>1.0", "!=2.0,>1.0"), ("!=2.0 ,>1.0", "!=2.0,>1.0"), @@ -928,10 +868,7 @@ class TestSpecifierSet: assert hash(SpecifierSet(specifier)) == hash(SpecifierSet(specifier)) @pytest.mark.parametrize( - ("left", "right", "expected"), - [ - (">2.0", "<5.0", ">2.0,<5.0"), - ], + ("left", "right", "expected"), [(">2.0", "<5.0", ">2.0,<5.0")] ) def test_specifiers_combine(self, left, right, expected): result = SpecifierSet(left) & SpecifierSet(right) @@ -956,30 +893,26 @@ class TestSpecifierSet: assert result == SpecifierSet(expected) assert not result.prereleases - result = ( - SpecifierSet(left, prereleases=True) & - SpecifierSet(right, prereleases=True) + result = SpecifierSet(left, prereleases=True) & SpecifierSet( + right, prereleases=True ) assert result == SpecifierSet(expected) assert result.prereleases - result = ( - SpecifierSet(left, prereleases=False) & - SpecifierSet(right, prereleases=False) + result = SpecifierSet(left, prereleases=False) & SpecifierSet( + right, prereleases=False ) assert result == SpecifierSet(expected) assert not result.prereleases with pytest.raises(ValueError): - result = ( - SpecifierSet(left, prereleases=True) & - SpecifierSet(right, prereleases=False) + result = SpecifierSet(left, prereleases=True) & SpecifierSet( + right, prereleases=False ) with pytest.raises(ValueError): - result = ( - SpecifierSet(left, prereleases=False) & - SpecifierSet(right, prereleases=True) + result = SpecifierSet(left, prereleases=False) & SpecifierSet( + right, prereleases=True ) def test_specifiers_combine_not_implemented(self): @@ -991,19 +924,14 @@ class TestSpecifierSet: itertools.chain( * # Verify that the equal (==) operator works correctly - [ - [(x, x, operator.eq) for x in SPECIFIERS] - ] + + [[(x, x, operator.eq) for x in SPECIFIERS]] + + # Verify that the not equal (!=) operator works correctly [ - [ - (x, y, operator.ne) - for j, y in enumerate(SPECIFIERS) - if i != j - ] + [(x, y, operator.ne) for j, y in enumerate(SPECIFIERS) if i != j] for i, x in enumerate(SPECIFIERS) ] - ) + ), ) def test_comparison_true(self, left, right, op): assert op(SpecifierSet(left), SpecifierSet(right)) @@ -1017,19 +945,14 @@ class TestSpecifierSet: itertools.chain( * # Verify that the equal (==) operator works correctly - [ - [(x, x, operator.ne) for x in SPECIFIERS] - ] + + [[(x, x, operator.ne) for x in SPECIFIERS]] + + # Verify that the not equal (!=) operator works correctly [ - [ - (x, y, operator.eq) - for j, y in enumerate(SPECIFIERS) - if i != j - ] + [(x, y, operator.eq) for j, y in enumerate(SPECIFIERS) if i != j] for i, x in enumerate(SPECIFIERS) ] - ) + ), ) def test_comparison_false(self, left, right, op): assert not op(SpecifierSet(left), SpecifierSet(right)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 2fcb30a..8458ffb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -30,18 +30,18 @@ def test_canonicalize_name(name, expected): @pytest.mark.parametrize( ("version", "expected"), [ - ('1.4.0', '1.4'), - ('1.40.0', '1.40'), - ('1.4.0.0.00.000.0000', '1.4'), - ('1.0', '1'), - ('1.0+abc', '1+abc'), - ('1.0.dev0', '1.dev0'), - ('1.0.post0', '1.post0'), - ('1.0a0', '1a0'), - ('1.0rc0', '1rc0'), - ('100!0.0', '100!0'), - ('1.0.1-test7', '1.0.1-test7'), # LegacyVersion is unchanged - ] + ("1.4.0", "1.4"), + ("1.40.0", "1.40"), + ("1.4.0.0.00.000.0000", "1.4"), + ("1.0", "1"), + ("1.0+abc", "1+abc"), + ("1.0.dev0", "1.dev0"), + ("1.0.post0", "1.post0"), + ("1.0a0", "1a0"), + ("1.0rc0", "1rc0"), + ("100!0.0", "100!0"), + ("1.0.1-test7", "1.0.1-test7"), # LegacyVersion is unchanged + ], ) def test_canonicalize_version(version, expected): assert canonicalize_version(version) == expected diff --git a/tests/test_version.py b/tests/test_version.py index ba4a11d..4e8079d 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -13,11 +13,7 @@ from packaging.version import Version, LegacyVersion, InvalidVersion, parse @pytest.mark.parametrize( - ("version", "klass"), - [ - ("1.0", Version), - ("1-1-1", LegacyVersion), - ], + ("version", "klass"), [("1.0", Version), ("1-1-1", LegacyVersion)] ) def test_parse(version, klass): assert isinstance(parse(version), klass) @@ -26,26 +22,65 @@ def test_parse(version, klass): # This list must be in the correct sorting order VERSIONS = [ # Implicit epoch of 0 - "1.0.dev456", "1.0a1", "1.0a2.dev456", "1.0a12.dev456", "1.0a12", - "1.0b1.dev456", "1.0b2", "1.0b2.post345.dev456", "1.0b2.post345", - "1.0b2-346", "1.0c1.dev456", "1.0c1", "1.0rc2", "1.0c3", "1.0", - "1.0.post456.dev34", "1.0.post456", "1.1.dev1", "1.2+123abc", - "1.2+123abc456", "1.2+abc", "1.2+abc123", "1.2+abc123def", "1.2+1234.abc", - "1.2+123456", "1.2.r32+123456", "1.2.rev33+123456", - + "1.0.dev456", + "1.0a1", + "1.0a2.dev456", + "1.0a12.dev456", + "1.0a12", + "1.0b1.dev456", + "1.0b2", + "1.0b2.post345.dev456", + "1.0b2.post345", + "1.0b2-346", + "1.0c1.dev456", + "1.0c1", + "1.0rc2", + "1.0c3", + "1.0", + "1.0.post456.dev34", + "1.0.post456", + "1.1.dev1", + "1.2+123abc", + "1.2+123abc456", + "1.2+abc", + "1.2+abc123", + "1.2+abc123def", + "1.2+1234.abc", + "1.2+123456", + "1.2.r32+123456", + "1.2.rev33+123456", # Explicit epoch of 1 - "1!1.0.dev456", "1!1.0a1", "1!1.0a2.dev456", "1!1.0a12.dev456", "1!1.0a12", - "1!1.0b1.dev456", "1!1.0b2", "1!1.0b2.post345.dev456", "1!1.0b2.post345", - "1!1.0b2-346", "1!1.0c1.dev456", "1!1.0c1", "1!1.0rc2", "1!1.0c3", "1!1.0", - "1!1.0.post456.dev34", "1!1.0.post456", "1!1.1.dev1", "1!1.2+123abc", - "1!1.2+123abc456", "1!1.2+abc", "1!1.2+abc123", "1!1.2+abc123def", - "1!1.2+1234.abc", "1!1.2+123456", "1!1.2.r32+123456", "1!1.2.rev33+123456", - + "1!1.0.dev456", + "1!1.0a1", + "1!1.0a2.dev456", + "1!1.0a12.dev456", + "1!1.0a12", + "1!1.0b1.dev456", + "1!1.0b2", + "1!1.0b2.post345.dev456", + "1!1.0b2.post345", + "1!1.0b2-346", + "1!1.0c1.dev456", + "1!1.0c1", + "1!1.0rc2", + "1!1.0c3", + "1!1.0", + "1!1.0.post456.dev34", + "1!1.0.post456", + "1!1.1.dev1", + "1!1.2+123abc", + "1!1.2+123abc456", + "1!1.2+abc", + "1!1.2+abc123", + "1!1.2+abc123def", + "1!1.2+1234.abc", + "1!1.2+123456", + "1!1.2.r32+123456", + "1!1.2.rev33+123456", ] class TestVersion: - @pytest.mark.parametrize("version", VERSIONS) def test_valid_versions(self, version): Version(version) @@ -55,14 +90,13 @@ class TestVersion: [ # Non sensical versions should be invalid "french toast", - # Versions with invalid local versions "1.0+a+", "1.0++", "1.0+_foobar", "1.0+foo&asd", "1.0+1+1", - ] + ], ) def test_invalid_versions(self, version): with pytest.raises(InvalidVersion): @@ -85,7 +119,6 @@ class TestVersion: ("1.0.DEV1", "1.0.dev1"), ("1.0-DEV", "1.0.dev0"), ("1.0-DEV1", "1.0.dev1"), - # Various alpha incarnations ("1.0a", "1.0a0"), ("1.0.a", "1.0a0"), @@ -107,7 +140,6 @@ class TestVersion: ("1.0.ALPHA1", "1.0a1"), ("1.0-ALPHA", "1.0a0"), ("1.0-ALPHA1", "1.0a1"), - # Various beta incarnations ("1.0b", "1.0b0"), ("1.0.b", "1.0b0"), @@ -129,7 +161,6 @@ class TestVersion: ("1.0.BETA1", "1.0b1"), ("1.0-BETA", "1.0b0"), ("1.0-BETA1", "1.0b1"), - # Various release candidate incarnations ("1.0c", "1.0rc0"), ("1.0.c", "1.0rc0"), @@ -151,7 +182,6 @@ class TestVersion: ("1.0.RC1", "1.0rc1"), ("1.0-RC", "1.0rc0"), ("1.0-RC1", "1.0rc1"), - # Various post release incarnations ("1.0post", "1.0.post0"), ("1.0.post", "1.0.post0"), @@ -173,10 +203,8 @@ class TestVersion: ("1.0-5", "1.0.post5"), ("1.0-r5", "1.0.post5"), ("1.0-rev5", "1.0.post5"), - # Local version case insensitivity ("1.0+AbC", "1.0+abc"), - # Integer Normalization ("1.01", "1.1"), ("1.0a05", "1.0a5"), @@ -187,7 +215,6 @@ class TestVersion: ("1.1.dev09000", "1.1.dev9000"), ("00!1.2", "1.2"), ("0100!0.0", "100!0.0"), - # Various other normalizations ("v1.0", "1.0"), (" v1.0\t\n", "1.0"), @@ -250,8 +277,7 @@ class TestVersion: ) def test_version_str_repr(self, version, expected): assert str(Version(version)) == expected - assert (repr(Version(version)) == - "".format(repr(expected))) + assert repr(Version(version)) == "".format(repr(expected)) def test_version_rc_and_c_equals(self): assert Version("1.0rc1") == Version("1.0c1") @@ -451,31 +477,31 @@ class TestVersion: ("1.0", None), ("1.0.dev0", None), ("1.0.dev6", None), - ("1.0a1", ('a', 1)), - ("1.0a1.post5", ('a', 1)), - ("1.0a1.post5.dev6", ('a', 1)), - ("1.0rc4", ('rc', 4)), + ("1.0a1", ("a", 1)), + ("1.0a1.post5", ("a", 1)), + ("1.0a1.post5.dev6", ("a", 1)), + ("1.0rc4", ("rc", 4)), ("1.0.post5", None), ("1!1.0", None), ("1!1.0.dev6", None), - ("1!1.0a1", ('a', 1)), - ("1!1.0a1.post5", ('a', 1)), - ("1!1.0a1.post5.dev6", ('a', 1)), - ("1!1.0rc4", ('rc', 4)), + ("1!1.0a1", ("a", 1)), + ("1!1.0a1.post5", ("a", 1)), + ("1!1.0a1.post5.dev6", ("a", 1)), + ("1!1.0rc4", ("rc", 4)), ("1!1.0.post5", None), ("1.0+deadbeef", None), ("1.0.dev6+deadbeef", None), - ("1.0a1+deadbeef", ('a', 1)), - ("1.0a1.post5+deadbeef", ('a', 1)), - ("1.0a1.post5.dev6+deadbeef", ('a', 1)), - ("1.0rc4+deadbeef", ('rc', 4)), + ("1.0a1+deadbeef", ("a", 1)), + ("1.0a1.post5+deadbeef", ("a", 1)), + ("1.0a1.post5.dev6+deadbeef", ("a", 1)), + ("1.0rc4+deadbeef", ("rc", 4)), ("1.0.post5+deadbeef", None), ("1!1.0+deadbeef", None), ("1!1.0.dev6+deadbeef", None), - ("1!1.0a1+deadbeef", ('a', 1)), - ("1!1.0a1.post5+deadbeef", ('a', 1)), - ("1!1.0a1.post5.dev6+deadbeef", ('a', 1)), - ("1!1.0rc4+deadbeef", ('rc', 4)), + ("1!1.0a1+deadbeef", ("a", 1)), + ("1!1.0a1.post5+deadbeef", ("a", 1)), + ("1!1.0a1.post5.dev6+deadbeef", ("a", 1)), + ("1!1.0rc4+deadbeef", ("rc", 4)), ("1!1.0.post5+deadbeef", None), ], ) @@ -630,7 +656,7 @@ class TestVersion: ("1.0", False), ("1.0+foo", False), ("1.0.post1.dev1", True), - ("1.0.post1", True) + ("1.0.post1", True), ], ) def test_version_is_postrelease(self, version, expected): @@ -644,34 +670,37 @@ class TestVersion: * # Verify that the less than (<) operator works correctly [ - [(x, y, operator.lt) for y in VERSIONS[i + 1:]] + [(x, y, operator.lt) for y in VERSIONS[i + 1 :]] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the less than equal (<=) operator works correctly [ [(x, y, operator.le) for y in VERSIONS[i:]] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the equal (==) operator works correctly - [ - [(x, x, operator.eq) for x in VERSIONS] - ] + + [[(x, x, operator.eq) for x in VERSIONS]] + + # Verify that the not equal (!=) operator works correctly [ [(x, y, operator.ne) for j, y in enumerate(VERSIONS) if i != j] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the greater than equal (>=) operator works correctly [ - [(x, y, operator.ge) for y in VERSIONS[:i + 1]] + [(x, y, operator.ge) for y in VERSIONS[: i + 1]] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the greater than (>) operator works correctly [ [(x, y, operator.gt) for y in VERSIONS[:i]] for i, x in enumerate(VERSIONS) ] - ) + ), ) def test_comparison_true(self, left, right, op): assert op(Version(left), Version(right)) @@ -684,43 +713,44 @@ class TestVersion: * # Verify that the less than (<) operator works correctly [ - [(x, y, operator.lt) for y in VERSIONS[:i + 1]] + [(x, y, operator.lt) for y in VERSIONS[: i + 1]] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the less than equal (<=) operator works correctly [ [(x, y, operator.le) for y in VERSIONS[:i]] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the equal (==) operator works correctly [ [(x, y, operator.eq) for j, y in enumerate(VERSIONS) if i != j] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the not equal (!=) operator works correctly - [ - [(x, x, operator.ne) for x in VERSIONS] - ] + + [[(x, x, operator.ne) for x in VERSIONS]] + + # Verify that the greater than equal (>=) operator works correctly [ - [(x, y, operator.ge) for y in VERSIONS[i + 1:]] + [(x, y, operator.ge) for y in VERSIONS[i + 1 :]] for i, x in enumerate(VERSIONS) - ] + + ] + + # Verify that the greater than (>) operator works correctly [ [(x, y, operator.gt) for y in VERSIONS[i:]] for i, x in enumerate(VERSIONS) ] - ) + ), ) def test_comparison_false(self, left, right, op): assert not op(Version(left), Version(right)) @pytest.mark.parametrize(("op", "expected"), [("eq", False), ("ne", True)]) def test_compare_other(self, op, expected): - other = pretend.stub( - **{"__{0}__".format(op): lambda other: NotImplemented} - ) + other = pretend.stub(**{"__{0}__".format(op): lambda other: NotImplemented}) assert getattr(operator, op)(Version("1"), other) is expected @@ -733,7 +763,6 @@ LEGACY_VERSIONS = ["foobar", "a cat is fine too", "lolwut", "1-0", "2.0-a1"] class TestLegacyVersion: - @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS) def test_valid_legacy_versions(self, version): LegacyVersion(version) @@ -741,8 +770,9 @@ class TestLegacyVersion: @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS) def test_legacy_version_str_repr(self, version): assert str(LegacyVersion(version)) == version - assert (repr(LegacyVersion(version)) == - "".format(repr(version))) + assert repr(LegacyVersion(version)) == "".format( + repr(version) + ) @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS) def test_legacy_version_hash(self, version): @@ -799,9 +829,8 @@ class TestLegacyVersion: itertools.chain( * # Verify that the equal (==) operator works correctly - [ - [(x, x, operator.eq) for x in VERSIONS + LEGACY_VERSIONS] - ] + + [[(x, x, operator.eq) for x in VERSIONS + LEGACY_VERSIONS]] + + # Verify that the not equal (!=) operator works correctly [ [ @@ -811,7 +840,7 @@ class TestLegacyVersion: ] for i, x in enumerate(VERSIONS + LEGACY_VERSIONS) ] - ) + ), ) def test_comparison_true(self, left, right, op): assert op(LegacyVersion(left), LegacyVersion(right)) @@ -831,20 +860,17 @@ class TestLegacyVersion: if i != j ] for i, x in enumerate(VERSIONS + LEGACY_VERSIONS) - ] + - # Verify that the not equal (!=) operator works correctly - [ - [(x, x, operator.ne) for x in VERSIONS + LEGACY_VERSIONS] ] - ) + + + # Verify that the not equal (!=) operator works correctly + [[(x, x, operator.ne) for x in VERSIONS + LEGACY_VERSIONS]] + ), ) def test_comparison_false(self, left, right, op): assert not op(LegacyVersion(left), LegacyVersion(right)) @pytest.mark.parametrize(("op", "expected"), [("eq", False), ("ne", True)]) def test_compare_other(self, op, expected): - other = pretend.stub( - **{"__{0}__".format(op): lambda other: NotImplemented} - ) + other = pretend.stub(**{"__{0}__".format(op): lambda other: NotImplemented}) assert getattr(operator, op)(LegacyVersion("1"), other) is expected diff --git a/tox.ini b/tox.ini index 5af0a58..c44acad 100644 --- a/tox.ini +++ b/tox.ini @@ -1,16 +1,12 @@ [tox] -envlist = py27,pypy,py34,py35,py36,py37,docs,pep8,py2pep8 +envlist = py27,pypy,pypy3,py34,py35,py36,py37,docs,lint [testenv] deps = coverage pretend pytest - https://github.com/pypa/pip/archive/master.zip#egg=pip -# The --ignore-installed install options is needed to install pip url -# (needed to issue #95). This can be removed once pip 10.0 is out. -install_command = - pip install {opts} {packages} --ignore-installed + pip>=9.0.2 commands = python -m coverage run --source packaging/ -m pytest --strict {posargs} python -m coverage report -m --fail-under 100 @@ -20,7 +16,7 @@ commands = py.test --capture=no --strict {posargs} [testenv:docs] -basepython = python3.6 +basepython = python3 deps = sphinx sphinx_rtd_theme @@ -29,19 +25,15 @@ commands = sphinx-build -W -b latex -d {envtmpdir}/doctrees docs docs/_build/latex sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html -[testenv:pep8] -basepython = python3.4 +[testenv:lint] +basepython = python3 deps = flake8 pep8-naming -commands = flake8 . - -[testenv:py2pep8] -basepython = python2.7 -deps = - flake8 - pep8-naming -commands = flake8 . + black +commands = + flake8 . + black --check . [testenv:packaging] deps = @@ -54,3 +46,4 @@ commands = [flake8] exclude = .tox,*.egg select = E,W,F,N +ignore = W504