--- /dev/null
+[flake8]
+max-line-length = 88
+ignore = E203,W503,W504
.cache/
.coverage
.idea
+.venv
__pycache__/
_build/
build/
dist/
-htmlcov/
\ No newline at end of file
+htmlcov/
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
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
~~~~~~~~~~~~~~~~~
include LICENSE LICENSE.APACHE LICENSE.BSD
include .coveragerc
+include .flake8
include tox.ini
recursive-include docs *
# Install our development requirements
+pretend
+pytest
tox
# Install packaging itself
# 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
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.
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 --------------------------------------------------
# -- 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 -----------------------------------------------
"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"
.. 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.
.. code-block:: console
- $ py.test
+ $ python -m pytest
...
62746 passed in 220.43 seconds
.. _`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
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
--- /dev/null
+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``.
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.`_
* 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/
>>> marker = Marker("python_version>'2'")
>>> marker
<Marker('python_version > "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
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__
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__",
]
# flake8: noqa
if PY3:
- string_types = str,
+ string_types = (str,)
else:
- string_types = basestring,
+ string_types = (basestring,)
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", (), {})
class Infinity(object):
-
def __repr__(self):
return "Infinity"
class NegativeInfinity(object):
-
def __repr__(self):
return "-Infinity"
__all__ = [
- "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName",
- "Marker", "default_environment",
+ "InvalidMarker",
+ "UndefinedComparison",
+ "UndefinedEnvironmentName",
+ "Marker",
+ "default_environment",
]
class Node(object):
-
def __init__(self, value):
self.value = value
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")
# 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):
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,
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):
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")
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
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
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:
if self.url:
parts.append("@ {0}".format(self.url))
+ if self.marker:
+ parts.append(" ")
if self.marker:
parts.append("; {0}".format(self.marker))
class BaseSpecifier(with_metaclass(abc.ABCMeta, object)):
-
@abc.abstractmethod
def __str__(self):
"""
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
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)
# 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.
class LegacySpecifier(_IndividualSpecifier):
- _regex_str = (
- r"""
+ _regex_str = r"""
(?P<operator>(==|!=|<=|>=|<|>))
\s*
(?P<version>
# 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",
if not isinstance(prospective, Version):
return False
return fn(self, prospective, spec)
+
return wrapped
class Specifier(_IndividualSpecifier):
- _regex_str = (
- r"""
+ _regex_str = r"""
(?P<operator>(~=|==|!=|<=|>=|<|>|===))
(?P<version>
(?:
)
)
"""
- )
- _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",
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]
# 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):
# 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.
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.
# 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
# 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:
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"]
)
class _BaseVersion(object):
-
def __hash__(self):
return hash(self._key)
class LegacyVersion(_BaseVersion):
-
def __init__(self, version):
self._version = str(version)
self._key = _legacy_cmpkey(self._version)
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": "@",
}
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
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")),
)
# 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.
# - 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
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",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
],
-
- packages=[
- "packaging",
- ],
+ packages=["packaging"],
)
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:
# 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
(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
)
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(
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
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):
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()
"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")
"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,
"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 == {
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)
],
# 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"',
'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"'),
(
{"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):
[
"{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)
],
[
("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"},
),
],
)
- 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
[
"{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)
],
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) == "<Requirement('name')>"
- 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)
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)")
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):
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)
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
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
import pytest
from packaging.specifiers import (
- InvalidSpecifier, LegacySpecifier, Specifier, SpecifierSet,
+ InvalidSpecifier,
+ LegacySpecifier,
+ Specifier,
+ SpecifierSet,
)
from packaging.version import LegacyVersion, Version, parse
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)
[
# 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.*",
"1.0.DEV1",
"1.0-DEV",
"1.0-DEV1",
-
# Various alpha incarnations
"1.0a",
"1.0.a",
"1.0.ALPHA1",
"1.0-ALPHA",
"1.0-ALPHA1",
-
# Various beta incarnations
"1.0b",
"1.0.b",
"1.0.BETA1",
"1.0-BETA",
"1.0-BETA1",
-
# Various release candidate incarnations
"1.0c",
"1.0.c",
"1.0.RC1",
"1.0-RC",
"1.0-RC1",
-
# Various post release incarnations
"1.0post",
"1.0.post",
"1.0-POST",
"1.0-POST1",
"1.0-5",
-
# Local version case insensitivity
"1.0+AbC"
-
# Integer Normalization
"1.01",
"1.0a05",
"1.1.dev09000",
"00!1.2",
"0100!0.0",
-
# Various other normalizations
"v1.0",
" \r \f \v v1.0\t\n",
(">2.0", ">2.0"),
(">=2.0", ">=2.0"),
("~=2.0", "~=2.0"),
-
# Spaces should be removed
("< 2", "<2"),
],
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))
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))
("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.*"),
("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"),
("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"),
("2.0.post1", ">=2"),
("2.0.post1.dev1", ">=2"),
("3", ">=2"),
-
# Test the less than equal operation
("2.0", "<=2"),
("2.0", "<=2.0"),
("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.*"),
("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
("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"),
("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.*"),
("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"),
("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"),
("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"),
("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"),
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
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)
class TestLegacySpecifier:
-
@pytest.mark.parametrize(
("version", "spec", "expected"),
[
("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"),
("2.0.post1", ">=2"),
("2.0.post1.dev1", ">=2"),
("3", ">=2"),
-
# Test the less than equal operation
("2.0", "<=2"),
("2.0", "<=2.0"),
("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"),
("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"),
("2.0c1.post1.dev1", ">2"),
("2.0rc1", ">2"),
("2.0", ">2"),
-
# Test the less than operation
("3", "<2"),
]
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)
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"]),
("", 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"]),
("", 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
(">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"),
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)
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):
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))
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))
@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
@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)
# 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)
[
# 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):
("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"),
("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"),
("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"),
("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"),
("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"),
("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"),
)
def test_version_str_repr(self, version, expected):
assert str(Version(version)) == expected
- assert (repr(Version(version)) ==
- "<Version({0})>".format(repr(expected)))
+ assert repr(Version(version)) == "<Version({0})>".format(repr(expected))
def test_version_rc_and_c_equals(self):
assert Version("1.0rc1") == Version("1.0c1")
("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),
],
)
("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):
*
# 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))
*
# 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
class TestLegacyVersion:
-
@pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
def test_valid_legacy_versions(self, version):
LegacyVersion(version)
@pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
def test_legacy_version_str_repr(self, version):
assert str(LegacyVersion(version)) == version
- assert (repr(LegacyVersion(version)) ==
- "<LegacyVersion({0})>".format(repr(version)))
+ assert repr(LegacyVersion(version)) == "<LegacyVersion({0})>".format(
+ repr(version)
+ )
@pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
def test_legacy_version_hash(self, version):
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
[
[
]
for i, x in enumerate(VERSIONS + LEGACY_VERSIONS)
]
- )
+ ),
)
def test_comparison_true(self, left, right, op):
assert op(LegacyVersion(left), LegacyVersion(right))
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
[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
py.test --capture=no --strict {posargs}
[testenv:docs]
-basepython = python3.6
+basepython = python3
deps =
sphinx
sphinx_rtd_theme
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 =
[flake8]
exclude = .tox,*.egg
select = E,W,F,N
+ignore = W504