From 635601a07a39f0c2ea0bd84496e974ab0d40180e Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 31 Aug 2022 17:44:52 +0530 Subject: [PATCH 01/54] Add --insecure option #62 Reference: https://github.com/nexB/python-inspector/issues/62 Signed-off-by: Tushar Goel --- CHANGELOG.rst | 4 + src/python_inspector/resolution.py | 26 ++- src/python_inspector/resolve_cli.py | 9 + tests/data/insecure-setup/rdflib/__init__.py | 215 +++++++++++++++++++ tests/data/insecure-setup/setup.py | 99 +++++++++ tests/data/setup/spdx-setup.py | 48 +++++ tests/data/setup/spdx-setup.py-expected.json | 40 ++++ tests/test_cli.py | 13 ++ tests/test_resolution.py | 13 ++ 9 files changed, 463 insertions(+), 4 deletions(-) create mode 100644 tests/data/insecure-setup/rdflib/__init__.py create mode 100644 tests/data/insecure-setup/setup.py create mode 100644 tests/data/setup/spdx-setup.py create mode 100644 tests/data/setup/spdx-setup.py-expected.json diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b88b0f28..34d1c8f0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,10 @@ Changelog ========= +v0.6.6 +------ +- Add --insecure option to compute arguments. + v0.6.5 ------ diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index e35eb232..9a3627d9 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -26,6 +26,7 @@ from packaging.version import LegacyVersion from packaging.version import Version from packaging.version import parse as parse_version +from requirements_builder.requirements_builder import iter_requirements from resolvelib import AbstractProvider from resolvelib import Resolver from resolvelib.reporters import BaseReporter @@ -134,6 +135,17 @@ def is_requirements_file_in_setup_files(setup_files: List[str]) -> bool: return False +def parse_setup_py_insecurely(setup_py): + """ + Yield requirements from the setup.py file at ``setup_py``. + """ + if not os.path.exists(setup_py): + return [] + unparsed_requirements = iter_requirements(level="", extras=[], setup_file=setup_py) + for requirement in unparsed_requirements: + yield Requirement(requirement) + + def is_valid_version( parsed_version: Union[LegacyVersion, Version], requirements: Dict, @@ -239,13 +251,14 @@ def remove_extras(identifier: str) -> str: class PythonInputProvider(AbstractProvider): - def __init__(self, environment=None, repos=tuple()): + def __init__(self, environment=None, repos=tuple(), insecure=False): self.environment = environment self.environment_marker = get_environment_marker_from_environment(environment) self.repos = repos or [] self.versions_by_package = {} self.dependencies_by_purl = {} self.wheel_or_sdist_by_package = {} + self.insecure = insecure def identify(self, requirement_or_candidate: Union[Candidate, Requirement]) -> str: """Given a requirement, return an identifier for it. Overridden.""" @@ -361,12 +374,10 @@ def get_requirements_for_package_from_pypi_simple( if deps: has_wheels = True yield from deps - if not has_wheels: sdist_location = fetch_and_extract_sdist( repos=self.repos, candidate=candidate, python_version=python_version ) - if sdist_location: setup_py_location = os.path.join( sdist_location, @@ -397,6 +408,8 @@ def get_requirements_for_package_from_pypi_simple( sdist_location, "requirements.txt", ) + + has_deps_yielded = False if not deps_in_setup and is_requirements_file_in_setup_files( setup_files=[setup_py_location, setup_cfg_location] ): @@ -405,8 +418,12 @@ def get_requirements_for_package_from_pypi_simple( location=requirement_location, ) if deps: + has_deps_yielded = True yield from deps + if not has_deps_yielded and self.insecure: + yield from parse_setup_py_insecurely(setup_py=setup_py_location) + def get_requirements_for_package_from_pypi_json_api( self, purl: PackageURL ) -> List[Requirement]: @@ -713,6 +730,7 @@ def get_resolved_dependencies( max_rounds: int = 200000, verbose: bool = False, pdt_output: bool = False, + insecure: bool = False, ): """ Return resolved dependencies of a ``requirements`` list of Requirement for @@ -724,7 +742,7 @@ def get_resolved_dependencies( """ try: resolver = Resolver( - provider=PythonInputProvider(environment=environment, repos=repos), + provider=PythonInputProvider(environment=environment, repos=repos, insecure=insecure), reporter=BaseReporter(), ) resolver_results = resolver.resolve(requirements=requirements, max_rounds=max_rounds) diff --git a/src/python_inspector/resolve_cli.py b/src/python_inspector/resolve_cli.py index 15166a7e..c825e2f3 100644 --- a/src/python_inspector/resolve_cli.py +++ b/src/python_inspector/resolve_cli.py @@ -157,6 +157,11 @@ def print_version(ctx, param, value): help="Use PyPI JSON API to fetch dependency data. Faster but not always correct. " "--index-url are ignored when this option is active.", ) +@click.option( + "--insecure", + is_flag=True, + help="Resolve insecurely", +) @click.option( "--verbose", is_flag=True, @@ -187,6 +192,7 @@ def resolve_dependencies( max_rounds, use_cached_index=False, use_pypi_json_api=False, + insecure=False, verbose=TRACE, ): """ @@ -330,6 +336,7 @@ def resolve_dependencies( max_rounds=max_rounds, verbose=verbose, pdt_output=pdt_output, + insecure=insecure, ) cli_options = [f"--requirement {rf}" for rf in requirement_files] @@ -393,6 +400,7 @@ def resolve( max_rounds=200000, verbose=False, pdt_output=False, + insecure=False, ): """ Resolve dependencies given a ``direct_dependencies`` list of @@ -418,6 +426,7 @@ def resolve( max_rounds=max_rounds, verbose=verbose, pdt_output=pdt_output, + insecure=insecure, ) initial_requirements = [d.to_dict() for d in direct_dependencies] diff --git a/tests/data/insecure-setup/rdflib/__init__.py b/tests/data/insecure-setup/rdflib/__init__.py new file mode 100644 index 00000000..2fca5c41 --- /dev/null +++ b/tests/data/insecure-setup/rdflib/__init__.py @@ -0,0 +1,215 @@ +import logging +import sys + +import six +from rdflib import plugin +from rdflib import query +from rdflib import util +from rdflib.graph import ConjunctiveGraph +from rdflib.graph import Dataset +from rdflib.graph import Graph +from rdflib.namespace import CSVW +from rdflib.namespace import DC +from rdflib.namespace import DCAT +from rdflib.namespace import DCTERMS +from rdflib.namespace import DOAP +from rdflib.namespace import FOAF +from rdflib.namespace import ODRL2 +from rdflib.namespace import ORG +from rdflib.namespace import OWL +from rdflib.namespace import PROF +from rdflib.namespace import PROV +from rdflib.namespace import RDF +from rdflib.namespace import RDFS +from rdflib.namespace import SDO +from rdflib.namespace import SH +from rdflib.namespace import SKOS +from rdflib.namespace import SOSA +from rdflib.namespace import SSN +from rdflib.namespace import TIME +from rdflib.namespace import VOID +from rdflib.namespace import XMLNS +from rdflib.namespace import XSD +from rdflib.namespace import Namespace +from rdflib.term import BNode +from rdflib.term import Literal +from rdflib.term import URIRef +from rdflib.term import Variable + +# tedious sop to flake8 +assert plugin +assert query + + +"""A pure Python package providing the core RDF constructs. + +The packages is intended to provide the core RDF types and interfaces +for working with RDF. The package defines a plugin interface for +parsers, stores, and serializers that other packages can use to +implement parsers, stores, and serializers that will plug into the +rdflib package. + +The primary interface `rdflib` exposes to work with RDF is +`rdflib.graph.Graph`. + +A tiny example: + + >>> from rdflib import Graph, URIRef, Literal + + >>> g = Graph() + >>> result = g.parse("http://www.w3.org/2000/10/swap/test/meet/blue.rdf") + + >>> print("graph has %s statements." % len(g)) + graph has 4 statements. + >>> + >>> for s, p, o in g: + ... if (s, p, o) not in g: + ... raise Exception("It better be!") + + >>> s = g.serialize(format='nt') + >>> + >>> sorted(g) == [ + ... (URIRef(u'http://meetings.example.com/cal#m1'), + ... URIRef(u'http://www.example.org/meeting_organization#homePage'), + ... URIRef(u'http://meetings.example.com/m1/hp')), + ... (URIRef(u'http://www.example.org/people#fred'), + ... URIRef(u'http://www.example.org/meeting_organization#attending'), + ... URIRef(u'http://meetings.example.com/cal#m1')), + ... (URIRef(u'http://www.example.org/people#fred'), + ... URIRef(u'http://www.example.org/personal_details#GivenName'), + ... Literal(u'Fred')), + ... (URIRef(u'http://www.example.org/people#fred'), + ... URIRef(u'http://www.example.org/personal_details#hasEmail'), + ... URIRef(u'mailto:fred@example.com')) + ... ] + True + +""" +__docformat__ = "restructuredtext en" + +# The format of the __version__ line is matched by a regex in setup.py +__version__ = "5.0.0" +__date__ = "2020-04-18" + +__all__ = [ + "URIRef", + "BNode", + "Literal", + "Variable", + "Namespace", + "Dataset", + "Graph", + "ConjunctiveGraph", + "CSVW", + "DC", + "DCAT", + "DCTERMS", + "DOAP", + "FOAF", + "ODRL2", + "ORG", + "OWL", + "PROF", + "PROV", + "RDF", + "RDFS", + "SDO", + "SH", + "SKOS", + "SOSA", + "SSN", + "TIME", + "VOID", + "XMLNS", + "XSD", + "util", +] + + +assert sys.version_info >= (2, 7, 0), "rdflib requires Python 2.7 or higher" + +logger = logging.getLogger(__name__) +_interactive_mode = False +try: + import __main__ + + if not hasattr(__main__, "__file__") and sys.stdout is not None and sys.stderr.isatty(): + # show log messages in interactive mode + _interactive_mode = True + logger.setLevel(logging.INFO) + logger.addHandler(logging.StreamHandler()) + del __main__ +except ImportError: + # Main already imported from elsewhere + import warnings + + warnings.warn("__main__ already imported", ImportWarning) + del warnings + +if _interactive_mode: + logger.info("RDFLib Version: %s" % __version__) +else: + logger.debug("RDFLib Version: %s" % __version__) +del _interactive_mode +del sys + +try: + six.unichr(0x10FFFF) +except ValueError: + import warnings + + warnings.warn( + "You are using a narrow Python build!\n" + "This means that your Python does not properly support chars > 16bit.\n" + 'On your system chars like c=u"\\U0010FFFF" will have a len(c)==2.\n' + "As this can cause hard to debug problems with string processing\n" + "(slicing, regexp, ...) later on, we strongly advise to use a wide\n" + "Python build in production systems.", + ImportWarning, + ) + del warnings +del six + + +NORMALIZE_LITERALS = True +""" +If True - Literals lexical forms are normalized when created. +I.e. the lexical forms is parsed according to data-type, then the +stored lexical form is the re-serialized value that was parsed. + +Illegal values for a datatype are simply kept. The normalized keyword +for Literal.__new__ can override this. + +For example: + +>>> from rdflib import Literal,XSD +>>> Literal("01", datatype=XSD.int) +rdflib.term.Literal(u'1', datatype=rdflib.term.URIRef(u'http://www.w3.org/2001/XMLSchema#integer')) + +This flag may be changed at any time, but will only affect literals +created after that time, previously created literals will remain +(un)normalized. + +""" + + +DAWG_LITERAL_COLLATION = False +""" +DAWG_LITERAL_COLLATION determines how literals are ordered or compared +to each other. + +In SPARQL, applying the >,<,>=,<= operators to literals of +incompatible data-types is an error, i.e: + +Literal(2)>Literal('cake') is neither true nor false, but an error. + +This is a problem in PY3, where lists of Literals of incompatible +types can no longer be sorted. + +Setting this flag to True gives you strict DAWG/SPARQL compliance, +setting it to False will order Literals with incompatible datatypes by +datatype URI + +In particular, this determines how the rich comparison operators for +Literal work, eq, __neq__, __lt__, etc. +""" diff --git a/tests/data/insecure-setup/setup.py b/tests/data/insecure-setup/setup.py new file mode 100644 index 00000000..be97fb4d --- /dev/null +++ b/tests/data/insecure-setup/setup.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +import os +import re + +from setuptools import find_packages +from setuptools import setup + +kwargs = {} +kwargs["install_requires"] = ["six", "isodate", "pyparsing"] +kwargs["tests_require"] = ["html5lib", "networkx", "nose", "doctest-ignore-unicode"] +kwargs["test_suite"] = "nose.collector" +kwargs["extras_require"] = { + "html": ["html5lib"], + "sparql": ["requests"], + "tests": kwargs["tests_require"], + "docs": ["sphinx < 3", "sphinxcontrib-apidoc"], +} + + +def find_version(filename): + _version_re = re.compile(r'__version__ = "(.*)"') + for line in open(filename): + version_match = _version_re.match(line) + if version_match: + return version_match.group(1) + + +version = find_version("rdflib/__init__.py") + +packages = find_packages(exclude=("examples*", "test*")) + +if os.environ.get("READTHEDOCS", None): + # if building docs for RTD + # install examples, to get docstrings + packages.append("examples") + +setup( + name="rdflib", + version=version, + description="RDFLib is a Python library for working with RDF, a " + "simple yet powerful language for representing information.", + author="Daniel 'eikeon' Krech", + author_email="eikeon@eikeon.com", + maintainer="RDFLib Team", + maintainer_email="rdflib-dev@google.com", + url="https://github.com/RDFLib/rdflib", + license="BSD-3-Clause", + platforms=["any"], + classifiers=[ + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "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", + "License :: OSI Approved :: BSD License", + "Topic :: Software Development :: Libraries :: Python Modules", + "Operating System :: OS Independent", + "Natural Language :: English", + ], + long_description="""\ +RDFLib is a Python library for working with +RDF, a simple yet powerful language for representing information. + +The library contains parsers and serializers for RDF/XML, N3, +NTriples, Turtle, TriX, RDFa and Microdata . The library presents +a Graph interface which can be backed by any one of a number of +Store implementations. The core rdflib includes store +implementations for in memory storage, persistent storage on top +of the Berkeley DB, and a wrapper for remote SPARQL endpoints. + +A SPARQL 1.1 engine is also included. + +If you have recently reported a bug marked as fixed, or have a craving for +the very latest, you may want the development version instead: + + pip install git+https://github.com/rdflib/rdflib + + +Read the docs at: + + http://rdflib.readthedocs.io + + """, + packages=packages, + entry_points={ + "console_scripts": [ + "rdfpipe = rdflib.tools.rdfpipe:main", + "csv2rdf = rdflib.tools.csv2rdf:main", + "rdf2dot = rdflib.tools.rdf2dot:main", + "rdfs2dot = rdflib.tools.rdfs2dot:main", + "rdfgraphisomorphism = rdflib.tools.graphisomorphism:main", + ], + }, + **kwargs +) diff --git a/tests/data/setup/spdx-setup.py b/tests/data/setup/spdx-setup.py new file mode 100644 index 00000000..e7eb90d9 --- /dev/null +++ b/tests/data/setup/spdx-setup.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +from __future__ import absolute_import +from __future__ import print_function + +import unittest + +from setuptools import setup + + +def test_suite(): + return unittest.TestLoader().discover("tests", pattern="test_*.py") + + +setup( + name="spdx-tools", + version="0.5.4", + description="SPDX parser and tools.", + packages=["spdx", "spdx.parsers", "spdx.writers", "spdx.parsers.lexers"], + package_data={"spdx": ["spdx_licenselist.csv"]}, + include_package_data=True, + zip_safe=False, + test_suite="setup.test_suite", + install_requires=[ + "ply", + "rdflib", + "six", + ], + entry_points={ + "console_scripts": [ + "spdx-tv2rdf = spdx.tv_to_rdf:main", + ], + }, + tests_require=[ + "xmltodict", + ], + author="Ahmed H. Ismail", + author_email="ahm3d.hisham@gmail.com", + maintainer="Philippe Ombredanne, SPDX group at the Linux Foundation and others", + maintainer_email="pombredanne@gmail.com", + url="https://github.com/spdx/tools-python", + license="Apache-2.0", + classifiers=[ + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 2.7", + ], +) diff --git a/tests/data/setup/spdx-setup.py-expected.json b/tests/data/setup/spdx-setup.py-expected.json new file mode 100644 index 00000000..9436efa6 --- /dev/null +++ b/tests/data/setup/spdx-setup.py-expected.json @@ -0,0 +1,40 @@ +[ + { + "key": "ply", + "package_name": "ply", + "installed_version": "3.11", + "dependencies": [] + }, + { + "key": "rdflib", + "package_name": "rdflib", + "installed_version": "5.0.0", + "dependencies": [ + { + "key": "isodate", + "package_name": "isodate", + "installed_version": "0.6.1", + "dependencies": [ + { + "key": "six", + "package_name": "six", + "installed_version": "1.16.0", + "dependencies": [] + } + ] + }, + { + "key": "pyparsing", + "package_name": "pyparsing", + "installed_version": "2.4.7", + "dependencies": [] + }, + { + "key": "six", + "package_name": "six", + "installed_version": "1.16.0", + "dependencies": [] + } + ] + } +] \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py index 2bb9b6a9..f593e237 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -227,6 +227,19 @@ def test_cli_with_setup_py_failure(): ) +@pytest.mark.online +def test_cli_with_insecure_option(): + setup_py_file = setup_test_env.get_test_loc("spdx-setup.py") + expected_file = setup_test_env.get_test_loc("spdx-setup.py-expected.json", must_exist=False) + check_setup_py_resolution( + setup_py=setup_py_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + extra_options=["--python-version", "27", "--insecure"], + pdt_output=True, + ) + + @pytest.mark.online def test_cli_with_setup_py(): setup_py_file = setup_test_env.get_test_loc("simple-setup.py") diff --git a/tests/test_resolution.py b/tests/test_resolution.py index bf1e7df2..d49360d8 100644 --- a/tests/test_resolution.py +++ b/tests/test_resolution.py @@ -8,17 +8,24 @@ # See https://github.com/nexB/python-inspector for support or download. # See https://aboutcode.org for more information about nexB OSS projects. # +import os + import packaging import pytest +from commoncode.testcase import FileDrivenTesting from packaging.requirements import Requirement from _packagedcode import models from python_inspector.resolution import get_requirements_from_dependencies from python_inspector.resolution import get_resolved_dependencies from python_inspector.resolution import is_valid_version +from python_inspector.resolution import parse_setup_py_insecurely from python_inspector.utils_pypi import PYPI_PUBLIC_REPO from python_inspector.utils_pypi import Environment +setup_test_env = FileDrivenTesting() +setup_test_env.test_data_dir = os.path.join(os.path.dirname(__file__), "data") + @pytest.mark.online def test_get_resolved_dependencies_with_flask_and_python_310(): @@ -213,3 +220,9 @@ def test_get_requirements_from_dependencies_with_editable_requirements(): requirements = [str(r) for r in get_requirements_from_dependencies(dependencies)] assert requirements == [] + + +def test_setup_py_parsing_insecure(): + setup_py_file = setup_test_env.get_test_loc("insecure-setup/setup.py") + reqs = [str(req) for req in list(parse_setup_py_insecurely(setup_py=setup_py_file))] + assert reqs == ["isodate", "pyparsing", "six"] From af5e12013943d0f8d57f95b1675836464cd5dd6d Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 31 Aug 2022 18:54:25 +0530 Subject: [PATCH 02/54] Address review comments Signed-off-by: Tushar Goel --- src/python_inspector/resolution.py | 121 ++++++++++++++++------------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index 9a3627d9..72b341bf 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -117,20 +117,16 @@ def get_environment_marker_from_environment(environment): } -def is_requirements_file_in_setup_files(setup_files: List[str]) -> bool: +def contain_string(string: str, files: List) -> bool: """ - Return True if the string ``requirements.txt`` is found in any of the ``setup_files`` location - strings to either a setup.py or setup.cfg file. - This is an indication that a requirements.txt is likely loaded in the setup.py - code and we use this as a hint to treat requirements.txt requirements - as being for the setup.py file. + Return True if the string is contains in any of the files. """ - for setup_file in setup_files: - if not os.path.exists(setup_file): + for file in files: + if not os.path.exists(file): continue - with open(setup_file, encoding="utf-8") as f: + with open(file, encoding="utf-8") as f: # TODO also consider other file names - if "requirements.txt" in f.read(): + if string in f.read(): return True return False @@ -363,66 +359,79 @@ def get_requirements_for_package_from_pypi_simple( python_version=python_version, ) - has_wheels = False + if wheels: + for wheel in wheels: + wheel_location = os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, wheel) + deps = get_requirements_from_distribution( + handler=PypiWheelHandler, + location=wheel_location, + ) + if deps: + yield from deps + # We are only looking at the first wheel and not other wheels + break - for wheel in wheels: - wheel_location = os.path.join(utils_pypi.CACHE_THIRDPARTY_DIR, wheel) - deps = get_requirements_from_distribution( - handler=PypiWheelHandler, - location=wheel_location, - ) - if deps: - has_wheels = True - yield from deps - if not has_wheels: + else: sdist_location = fetch_and_extract_sdist( repos=self.repos, candidate=candidate, python_version=python_version ) - if sdist_location: - setup_py_location = os.path.join( - sdist_location, - "setup.py", - ) - setup_cfg_location = os.path.join( - sdist_location, - "setup.cfg", - ) + if not sdist_location: + return + setup_py_location = os.path.join( + sdist_location, + "setup.py", + ) + setup_cfg_location = os.path.join( + sdist_location, + "setup.cfg", + ) + + if not os.path.exists(setup_py_location) and not os.path.exists(setup_cfg_location): + raise Exception(f"No setup.py or setup.cfg found in pypi sdist {sdist_location}") - location_by_sdist_parser = { - PythonSetupPyHandler: setup_py_location, - SetupCfgHandler: setup_cfg_location, - } + # Some commonon packages like flask may have some dependencies in setup.cfg + # and some dependencies in setup.py. We are going to check both. + location_by_sdist_parser = { + PythonSetupPyHandler: setup_py_location, + SetupCfgHandler: setup_cfg_location, + } - deps_in_setup = False + # Set to True if we found any dependencies in setup.py or setup.cfg + has_deps = False - for handler, location in location_by_sdist_parser.items(): - deps = get_requirements_from_distribution( - handler=handler, - location=location, - ) - if deps: - deps_in_setup = True - yield from deps + for handler, location in location_by_sdist_parser.items(): + deps = get_requirements_from_distribution( + handler=handler, + location=location, + ) + if deps: + has_deps = True + yield from deps + if not has_deps and contain_string( + string="requirements.txt", files=[setup_py_location, setup_cfg_location] + ): + # Look in requirements file if and only if thy are refered in setup.py or setup.cfg + # And no deps have been yielded by requirements file. requirement_location = os.path.join( sdist_location, "requirements.txt", ) + deps = get_requirements_from_distribution( + handler=PipRequirementsFileHandler, + location=requirement_location, + ) + if deps: + has_deps = True + yield from deps - has_deps_yielded = False - if not deps_in_setup and is_requirements_file_in_setup_files( - setup_files=[setup_py_location, setup_cfg_location] - ): - deps = get_requirements_from_distribution( - handler=PipRequirementsFileHandler, - location=requirement_location, - ) - if deps: - has_deps_yielded = True - yield from deps - - if not has_deps_yielded and self.insecure: + if not has_deps and contain_string( + string="_require", files=[setup_py_location, setup_cfg_location] + ): + if self.insecure: yield from parse_setup_py_insecurely(setup_py=setup_py_location) + else: + raise Exception("Unable to collect setup.py dependencies securely") def get_requirements_for_package_from_pypi_json_api( self, purl: PackageURL From 87d6971e519bfa1779ae7242958c07b4698fe972 Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Mon, 5 Oct 2015 11:07:10 +0200 Subject: [PATCH 03/54] global: initial project structure Signed-off-by: Lars Holm Nielsen --- .coveragerc | 9 ++++ .travis.yml | 52 ++++++++++++++++++++ LICENSE | 34 +++++++++++++ RELEASE-NOTES.rst | 29 +++++++++++ requirements_builder/requirements_builder.py | 12 +++++ tests/test_requirements-builder.py | 18 +++++++ 6 files changed, 154 insertions(+) create mode 100644 .coveragerc create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 RELEASE-NOTES.rst create mode 100755 requirements_builder/requirements_builder.py create mode 100755 tests/test_requirements-builder.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..a954aea8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,9 @@ +# This file is part of Requirements Builder +# Copyright (C) 2015 CERN. +# +# Requirements Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + +[run] +source = requirements_builder diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..5bff8ea9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,52 @@ +# This file is part of Requirements Builder +# Copyright (C) 2015 CERN. +# +# Requirements Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + +sudo: false + +language: python + +python: + - "3.5" + - "3.4" + - "3.3" + - "2.7" + - "2.6" + - "pypy" + +env: + - REQUIREMENTS=devel + - REQUIREMENTS=latest + - REQUIREMENTS=lowest + +cache: + - pip + +install: + # Install test dependencies + - "travis_retry pip install coveralls pep257 Sphinx twine wheel" + - "travis_retry pip install pytest pytest-pep8 pytest-cov pytest-cache" + - "travis_retry pip install -r requirements.${REQUIREMENTS}.txt" + - "travis_retry pip install -e ." + +script: ./run-tests.sh + +after_success: + - coveralls + +notifications: + email: false + +deploy: + provider: pypi + user: inveniosoftware + password: + secure: CHANGEME + distributions: "sdist bdist_wheel" + on: + tags: true + python: "2.7" + condition: $REQUIREMENTS = latest diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..36aa8336 --- /dev/null +++ b/LICENSE @@ -0,0 +1,34 @@ +Requirements Builder is free software; you can redistribute it and/or +modify it under the terms of the Revised BSD License; see LICENSE +file for more details. + +Copyright (C) 2015, CERN +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst new file mode 100644 index 00000000..cff409e3 --- /dev/null +++ b/RELEASE-NOTES.rst @@ -0,0 +1,29 @@ +============================= + Requirements Builder v0.1.0 +============================= + +Requirements Builder v0.1.0 was released on 2015-01-11 + +About +----- + +BBuild requirements files from setup.py requirements. + +Installation +------------ + + $ pip install requirements-builder + +What's new +---------- + +Documentation +------------- + + http://pythonhosted.org/requirements-builder/ + +Website +------- + + https://github.com/inveniosoftware/requirements-builder + diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py new file mode 100755 index 00000000..c1418d4b --- /dev/null +++ b/requirements_builder/requirements_builder.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Requirements Builder +# Copyright (C) 2015 CERN. +# +# Requirements Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + +"""Generate minimal requirements from `setup.py` + `requirements-devel.txt`.""" + +from __future__ import absolute_import, print_function diff --git a/tests/test_requirements-builder.py b/tests/test_requirements-builder.py new file mode 100755 index 00000000..beb64df5 --- /dev/null +++ b/tests/test_requirements-builder.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Requirements Builder +# Copyright (C) 2015 CERN. +# +# Requirements Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + + +"""Tests for `requirements-builder` module.""" + +from requirements_builder import __version__ + + +def test_version(): + """Test requirements-builder.""" + assert __version__ From e3b85bbd1c4dd3b8f41b86305f1acc82a376d377 Mon Sep 17 00:00:00 2001 From: Marco Neumann Date: Mon, 5 Oct 2015 11:09:33 +0200 Subject: [PATCH 04/54] global: initial import of requirements * Imports requirements.py from cookiecutter-invenio-module. Co-authored-by: Jiri Kuncar Signed-off-by: Lars Holm Nielsen --- requirements_builder/requirements_builder.py | 160 +++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index c1418d4b..09a23285 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -10,3 +10,163 @@ """Generate minimal requirements from `setup.py` + `requirements-devel.txt`.""" from __future__ import absolute_import, print_function + +import argparse +import re +import sys + +import mock +import pkg_resources +import setuptools + + +def parse_set(string): + """Parse set from comma separated string.""" + string = string.strip() + if string: + return set(string.split(",")) + else: + return set() + + +def minver_error(pkg_name): + """Report error about missing minimum version constraint and exit.""" + print( + 'ERROR: specify minimal version of "{}" using ' + '">=" or "=="'.format(pkg_name), + file=sys.stderr + ) + sys.exit(1) + + +def parse_pip_file(path): + """Parse pip requirements file.""" + # requirement lines sorted by importance + # also collect other pip commands + rdev = dict() + rnormal = [] + stuff = [] + + try: + with open(path) as f: + for line in f: + line = line.strip() + + # see https://pip.readthedocs.org/en/1.1/requirements.html + if line.startswith('-e'): + # devel requirement + splitted = line.split('#egg=') + rdev[splitted[1].lower()] = line + + elif line.startswith('-r'): + # recursive file command + splitted = re.split('-r\\s+', line) + subrdev, subrnormal, substuff = parse_pip_file(splitted[1]) + for k, v in subrdev.iteritems(): + if k not in rdev: + rdev[k] = v + rnormal.extend(subrnormal) + result.extend(substuff) + + elif line.startswith('-'): + # another special command we don't recognize + stuff.append(line) + + else: + # ordenary requirement, similary to them used in setup.py + rnormal.append(line) + except IOError: + print( + 'Warning: could not parse requirements file "{}"!', + file=sys.stderr + ) + + return rdev, rnormal, stuff + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Calculates requirements for different purposes', + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument( + '-l', '--level', + choices=['min', 'pypi', 'dev'], + default='pypi', + help='Specifies desired requirements level.' + '"min" requests the minimal requirement that is specified, ' + '"pypi" requests the maximum version that satisfies the ' + 'constrains and is available in PyPi. ' + '"dev" includes experimental developer versions for VCSs.' + ) + parser.add_argument( + '-e', '--extras', + default='', + help='Comma separated list of extras.', + type=parse_set + ) + args = parser.parse_args() + + result = dict() + requires = [] + stuff = [] + if args.level == 'dev': + result, requires, stuff = parse_pip_file('requirements-devel.txt') + + with mock.patch.object(setuptools, 'setup') as mock_setup: + import setup + assert setup # silence warning about unused imports + + # called arguments are in `mock_setup.call_args` + mock_args, mock_kwargs = mock_setup.call_args + requires = mock_kwargs.get('install_requires', []) + + requires_extras = mock_kwargs.get('extras_require', {}) + for e in args.extras: + if e in requires_extras: + requires.extend(requires_extras[e]) + + for pkg in pkg_resources.parse_requirements(requires): + # skip things we already know + # FIXME be smarter about merging things + if pkg.key in result: + continue + + specs = dict(pkg.specs) + if (('>=' in specs) and ('>' in specs)) \ + or (('<=' in specs) and ('<' in specs)): + print( + 'ERROR: Do not specify such weird constraints! ' + '("{}")'.format(pkg), + file=sys.stderr + ) + sys.exit(1) + + if '==' in specs: + result[pkg.key] = '{}=={}'.format(pkg.project_name, specs['==']) + + elif '>=' in specs: + if args.level == 'min': + result[pkg.key] = '{}=={}'.format( + pkg.project_name, + specs['>='] + ) + else: + result[pkg.key] = pkg + + elif '>' in specs: + if args.level == 'min': + minver_error(pkg.project_name) + else: + result[pkg.key] = pkg + + else: + if args.level == 'min': + minver_error(pkg.project_name) + else: + result[pkg.key] = pkg + + for s in stuff: + print(s) + + for k in sorted(result.iterkeys()): + print(result[k]) From fbcc778d37b0132f6ace8aa8f9633ae24e769898 Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Mon, 5 Oct 2015 15:14:17 +0200 Subject: [PATCH 05/54] cli: click instead of argparse support * Migrates from argparse to click. * Add support for specifying path to setup.py and requirements file. Signed-off-by: Lars Holm Nielsen --- .travis.yml | 7 ++- RELEASE-NOTES.rst | 2 +- requirements_builder/requirements_builder.py | 56 ++++++------------- .coveragerc => tests/data/req.txt | 3 +- .../setup.py} | 16 ++++-- tests/test_requirements_builder.py | 42 ++++++++++++++ 6 files changed, 75 insertions(+), 51 deletions(-) rename .coveragerc => tests/data/req.txt (81%) rename tests/{test_requirements-builder.py => data/setup.py} (56%) mode change 100755 => 100644 create mode 100755 tests/test_requirements_builder.py diff --git a/.travis.yml b/.travis.yml index 5bff8ea9..26c85a53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ python: - "3.4" - "3.3" - "2.7" - - "2.6" - "pypy" env: @@ -25,10 +24,12 @@ env: cache: - pip -install: - # Install test dependencies +before_install: + - "travis_retry pip install --upgrade pip setuptools py" - "travis_retry pip install coveralls pep257 Sphinx twine wheel" + - "travis_retry pip install isort check-manifest coverage Sphinx" - "travis_retry pip install pytest pytest-pep8 pytest-cov pytest-cache" +install: - "travis_retry pip install -r requirements.${REQUIREMENTS}.txt" - "travis_retry pip install -e ." diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index cff409e3..a8c4dcf9 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -2,7 +2,7 @@ Requirements Builder v0.1.0 ============================= -Requirements Builder v0.1.0 was released on 2015-01-11 +Requirements Builder v0.1.0 was released on 2015-10-05 About ----- diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 09a23285..548b6588 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -7,11 +7,10 @@ # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. -"""Generate minimal requirements from `setup.py` + `requirements-devel.txt`.""" +"""Generate requirements from `setup.py` and `requirements-devel.txt`.""" from __future__ import absolute_import, print_function -import argparse import re import sys @@ -66,12 +65,9 @@ def parse_pip_file(path): if k not in rdev: rdev[k] = v rnormal.extend(subrnormal) - result.extend(substuff) - elif line.startswith('-'): # another special command we don't recognize stuff.append(line) - else: # ordenary requirement, similary to them used in setup.py rnormal.append(line) @@ -83,45 +79,26 @@ def parse_pip_file(path): return rdev, rnormal, stuff -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description='Calculates requirements for different purposes', - formatter_class=argparse.ArgumentDefaultsHelpFormatter - ) - parser.add_argument( - '-l', '--level', - choices=['min', 'pypi', 'dev'], - default='pypi', - help='Specifies desired requirements level.' - '"min" requests the minimal requirement that is specified, ' - '"pypi" requests the maximum version that satisfies the ' - 'constrains and is available in PyPi. ' - '"dev" includes experimental developer versions for VCSs.' - ) - parser.add_argument( - '-e', '--extras', - default='', - help='Comma separated list of extras.', - type=parse_set - ) - args = parser.parse_args() +def iter_requirements(level, extras, pip_file, setup_fp): + """Iterate over requirements.""" result = dict() requires = [] stuff = [] - if args.level == 'dev': - result, requires, stuff = parse_pip_file('requirements-devel.txt') + if level == 'dev': + result, requires, stuff = parse_pip_file(pip_file) with mock.patch.object(setuptools, 'setup') as mock_setup: - import setup - assert setup # silence warning about unused imports + g = {} + exec(setup_fp.read(), g) + assert g['setup'] # silence warning about unused imports # called arguments are in `mock_setup.call_args` mock_args, mock_kwargs = mock_setup.call_args requires = mock_kwargs.get('install_requires', []) requires_extras = mock_kwargs.get('extras_require', {}) - for e in args.extras: + for e in extras: if e in requires_extras: requires.extend(requires_extras[e]) @@ -142,10 +119,11 @@ def parse_pip_file(path): sys.exit(1) if '==' in specs: - result[pkg.key] = '{}=={}'.format(pkg.project_name, specs['==']) + result[pkg.key] = '{}=={}'.format( + pkg.project_name, specs['==']) elif '>=' in specs: - if args.level == 'min': + if level == 'min': result[pkg.key] = '{}=={}'.format( pkg.project_name, specs['>='] @@ -154,19 +132,19 @@ def parse_pip_file(path): result[pkg.key] = pkg elif '>' in specs: - if args.level == 'min': + if level == 'min': minver_error(pkg.project_name) else: result[pkg.key] = pkg else: - if args.level == 'min': + if level == 'min': minver_error(pkg.project_name) else: result[pkg.key] = pkg for s in stuff: - print(s) + yield s - for k in sorted(result.iterkeys()): - print(result[k]) + for k in sorted(result.keys()): + yield str(result[k]) diff --git a/.coveragerc b/tests/data/req.txt similarity index 81% rename from .coveragerc rename to tests/data/req.txt index a954aea8..5c5060f3 100644 --- a/.coveragerc +++ b/tests/data/req.txt @@ -5,5 +5,4 @@ # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. -[run] -source = requirements_builder +-e git+https://github.com/mitsuhiko/click.git#egg=click diff --git a/tests/test_requirements-builder.py b/tests/data/setup.py old mode 100755 new mode 100644 similarity index 56% rename from tests/test_requirements-builder.py rename to tests/data/setup.py index beb64df5..c8955f7f --- a/tests/test_requirements-builder.py +++ b/tests/data/setup.py @@ -7,12 +7,16 @@ # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. +"""Build requirements files from setup.py requirements.""" -"""Tests for `requirements-builder` module.""" +from setuptools import setup -from requirements_builder import __version__ +requirements = [ + 'click>=5.0.0', + 'mock>=1.3.0', +] - -def test_version(): - """Test requirements-builder.""" - assert __version__ +setup( + name='testpkh', + install_requires=requirements, +) diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py new file mode 100755 index 00000000..173d720f --- /dev/null +++ b/tests/test_requirements_builder.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Requirements Builder +# Copyright (C) 2015 CERN. +# +# Requirements Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + + +"""Tests for `requirements-builder` module.""" + +from os.path import abspath, dirname, join + +from requirements_builder import __version__, iter_requirements + +REQ = abspath(join(dirname(__file__), "../requirements.devel.txt")) +SETUP = abspath(join(dirname(__file__), "../setup.py")) + + +def test_version(): + """Test requirements-builder.""" + assert __version__ + + +def test_iter_requirements(): + """Test requirements-builder.""" + # Min + with open(SETUP) as f: + assert list(iter_requirements("min", [], '', f)) == \ + ['click==5.0.0', 'mock==1.3.0'] + + # PyPI + with open(SETUP) as f: + assert list(iter_requirements("pypi", [], '', f)) == \ + ['click>=5.0.0', 'mock>=1.3.0'] + + # Dev + with open(SETUP) as f: + assert list(iter_requirements("dev", [], REQ, f)) == \ + ['-e git+https://github.com/mitsuhiko/click.git#egg=click', + 'mock>=1.3.0'] From 771d1191e2ff4ea4914b58dd73bc200475eb3875 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Tue, 6 Oct 2015 11:18:34 +0200 Subject: [PATCH 06/54] global: package style improvements Signed-off-by: Jiri Kuncar --- .travis.yml | 11 ++++---- LICENSE | 2 +- RELEASE-NOTES.rst | 27 ++++++++++++-------- requirements_builder/requirements_builder.py | 4 +-- tests/data/req.txt | 4 +-- tests/data/setup.py | 4 +-- tests/test_requirements_builder.py | 4 +-- 7 files changed, 30 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26c85a53..912be0c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ -# This file is part of Requirements Builder +# This file is part of Requirements-Builder # Copyright (C) 2015 CERN. # -# Requirements Builder is free software; you can redistribute it and/or +# Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. @@ -26,12 +26,11 @@ cache: before_install: - "travis_retry pip install --upgrade pip setuptools py" - - "travis_retry pip install coveralls pep257 Sphinx twine wheel" - - "travis_retry pip install isort check-manifest coverage Sphinx" - - "travis_retry pip install pytest pytest-pep8 pytest-cov pytest-cache" + - "travis_retry pip install coveralls twine wheel" + install: - "travis_retry pip install -r requirements.${REQUIREMENTS}.txt" - - "travis_retry pip install -e ." + - "travis_retry pip install -e .[all]" script: ./run-tests.sh diff --git a/LICENSE b/LICENSE index 36aa8336..b4896967 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Requirements Builder is free software; you can redistribute it and/or +Requirements-Builder is free software; you can redistribute it and/or modify it under the terms of the Revised BSD License; see LICENSE file for more details. diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index a8c4dcf9..feead89b 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,29 +1,34 @@ ============================= - Requirements Builder v0.1.0 + Requirements-Builder v0.1.0 ============================= -Requirements Builder v0.1.0 was released on 2015-10-05 +Requirements-Builder v0.1.0 was released on October 6, 2015. About ----- -BBuild requirements files from setup.py requirements. +Build requirements files from setup.py requirements. + +What's new +---------- + +- Initial public release Installation ------------ - $ pip install requirements-builder - -What's new ----------- + $ pip install requirements-builder==0.1.0 Documentation ------------- http://pythonhosted.org/requirements-builder/ -Website -------- - - https://github.com/inveniosoftware/requirements-builder +Happy hacking and thanks for flying Requirements-Builder. +| Invenio Development Team +| Email: info@invenio-software.org +| IRC: #invenio on irc.freenode.net +| Twitter: http://twitter.com/inveniosoftware +| GitHub: https://github.com/inveniosoftware/requirements-builder +| URL: http://invenio-software.org diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 548b6588..519f1314 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # -# This file is part of Requirements Builder +# This file is part of Requirements-Builder # Copyright (C) 2015 CERN. # -# Requirements Builder is free software; you can redistribute it and/or +# Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. diff --git a/tests/data/req.txt b/tests/data/req.txt index 5c5060f3..532ca6ae 100644 --- a/tests/data/req.txt +++ b/tests/data/req.txt @@ -1,7 +1,7 @@ -# This file is part of Requirements Builder +# This file is part of Requirements-Builder # Copyright (C) 2015 CERN. # -# Requirements Builder is free software; you can redistribute it and/or +# Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. diff --git a/tests/data/setup.py b/tests/data/setup.py index c8955f7f..517974ff 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # -# This file is part of Requirements Builder +# This file is part of Requirements-Builder # Copyright (C) 2015 CERN. # -# Requirements Builder is free software; you can redistribute it and/or +# Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index 173d720f..0a141ad4 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # -# This file is part of Requirements Builder +# This file is part of Requirements-Builder # Copyright (C) 2015 CERN. # -# Requirements Builder is free software; you can redistribute it and/or +# Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. From e0517a95e974f7b2583d769504288d3ef2adab66 Mon Sep 17 00:00:00 2001 From: Tibor Simko Date: Tue, 28 Jun 2016 14:49:26 +0200 Subject: [PATCH 07/54] global: inveniosoftware.org * Changes `invenio-software.org` to `inveniosoftware.org` to use the same dashless canonical ID everywhere (GitHub, Twitter, Web). Signed-off-by: Tibor Simko --- RELEASE-NOTES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index feead89b..53760a3c 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -27,8 +27,8 @@ Documentation Happy hacking and thanks for flying Requirements-Builder. | Invenio Development Team -| Email: info@invenio-software.org +| Email: info@inveniosoftware.org | IRC: #invenio on irc.freenode.net | Twitter: http://twitter.com/inveniosoftware | GitHub: https://github.com/inveniosoftware/requirements-builder -| URL: http://invenio-software.org +| URL: http://inveniosoftware.org From f6fb25778d40628779abf4b81fce3548f53c89c9 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Wed, 7 Sep 2016 09:10:12 +0200 Subject: [PATCH 08/54] global: prefill globals with the '__file__' constant * FIX Fixes problem when the setup.py command plays with `__file__` to read, exec, or whatever. Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 4 ++-- tests/data/setup.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 519f1314..6199ab0e 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Requirements-Builder -# Copyright (C) 2015 CERN. +# Copyright (C) 2015, 2016 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -89,7 +89,7 @@ def iter_requirements(level, extras, pip_file, setup_fp): result, requires, stuff = parse_pip_file(pip_file) with mock.patch.object(setuptools, 'setup') as mock_setup: - g = {} + g = {'__file__': setup_fp.name} exec(setup_fp.read(), g) assert g['setup'] # silence warning about unused imports diff --git a/tests/data/setup.py b/tests/data/setup.py index 517974ff..c1b4ce06 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Requirements-Builder -# Copyright (C) 2015 CERN. +# Copyright (C) 2015, 2016 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -11,6 +11,10 @@ from setuptools import setup +import os + +dirname = os.path.dirname(__file__) + requirements = [ 'click>=5.0.0', 'mock>=1.3.0', From c87641cf57d7754fea1f0de71960f008c764a5fa Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Wed, 7 Sep 2016 11:14:30 +0200 Subject: [PATCH 09/54] global: add setup.py directory into the PYTHONPATH * FIX Fixes problem when the setup.py command try to import the package its about to install in order to get the information like the version. E.g. Django does that. Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 3 +++ tests/data/setup.py | 3 +++ tests/data/testpkh/__init__.py | 1 + 3 files changed, 7 insertions(+) create mode 100644 tests/data/testpkh/__init__.py diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 6199ab0e..badb4f44 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -11,6 +11,7 @@ from __future__ import absolute_import, print_function +import os import re import sys @@ -89,8 +90,10 @@ def iter_requirements(level, extras, pip_file, setup_fp): result, requires, stuff = parse_pip_file(pip_file) with mock.patch.object(setuptools, 'setup') as mock_setup: + sys.path.append(os.path.dirname(setup_fp.name)) g = {'__file__': setup_fp.name} exec(setup_fp.read(), g) + sys.path.pop() assert g['setup'] # silence warning about unused imports # called arguments are in `mock_setup.call_args` diff --git a/tests/data/setup.py b/tests/data/setup.py index c1b4ce06..fae6a22b 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -12,6 +12,8 @@ from setuptools import setup import os +import testpkh + dirname = os.path.dirname(__file__) @@ -22,5 +24,6 @@ setup( name='testpkh', + version=testpkh.__version__, install_requires=requirements, ) diff --git a/tests/data/testpkh/__init__.py b/tests/data/testpkh/__init__.py new file mode 100644 index 00000000..b8023d8b --- /dev/null +++ b/tests/data/testpkh/__init__.py @@ -0,0 +1 @@ +__version__ = '0.0.1' From 78e57bafc49348861f6bf0e93f50a95578f9af25 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Wed, 7 Sep 2016 09:17:41 +0200 Subject: [PATCH 10/54] travis: secure PyPI deploy password Signed-off-by: Jiri Kuncar --- .travis.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 912be0c5..f720c337 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ # This file is part of Requirements-Builder -# Copyright (C) 2015 CERN. +# Copyright (C) 2015, 2016 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -21,6 +21,9 @@ env: - REQUIREMENTS=latest - REQUIREMENTS=lowest +matrix: + fast_finish: true + cache: - pip @@ -42,11 +45,11 @@ notifications: deploy: provider: pypi - user: inveniosoftware + user: jirikuncar password: - secure: CHANGEME - distributions: "sdist bdist_wheel" + secure: bOutxlDnZsYDQoq1IiIK9VbTT6hI36gIuWUWfPw6weBX5WdEA7oIM5CCapb7sHcMMHmz57LTJYT9QVjqLXk8Oe7S3ndD7n5BI4M2ysz6o0ZIlQJ8YVChLr0TT7O4i+Xxw5MTUuiOpNtS7r0wEImmWI64PoQHS8VeHJfviUZEZcUxghZUAuGXtq7WiSzzhQH7uEPpbD+6WePB2FOZ2wfxIXJPQ/jiPN6wEaf5nZoyvm/N/bnvKaGYxKWHAjCviYKYzWmK3AlPAq9ELTgeeCHmteKZoUmFur4GbF6z5pT3D5olfA2K2Sv60dx0Bxbr3DOBULUlst2rsnFYsrQw18a3dsy30/rgjDixxPtxxy9fzB4X60QIzsTmFXbXMKBr1aMKYjmwaXhBkxX/S99g1tsuctwUQwj41CW5XVEF154ZSmghyJ1XTqbqKG9tn7zkJHTGrS3Xd+GI9FNBI2/QXZCDWPpsnPxvoSq4giQK8UzPnrnoKDzu25xlftqd9UXkqVMiVYfXGXcjtlL5l28grC/TyLEjxogYFGSiJFChuVPpcS65dgkjSw4M6tDWgSctR1E8VA755ZQ+wPtMSi0C5oemL2gvsB2maeK2i37ytv+Y3Pq6tlZ5c7Lcjb72ktwoqvmGmfsiB8jEoAUyZB4R5OL6utkewMGaEP+QuShxHPDo2/Y= + distributions: sdist bdist_wheel on: tags: true - python: "2.7" - condition: $REQUIREMENTS = latest + python: '2.7' + condition: "$REQUIREMENTS = latest" From 71f864fd7cd6372922d0e944cb954380fa5b5bd3 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Wed, 7 Sep 2016 09:26:17 +0200 Subject: [PATCH 11/54] release: v0.2.0 Signed-off-by: Jiri Kuncar --- RELEASE-NOTES.rst | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 53760a3c..4358c9a7 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,23 +1,34 @@ ============================= - Requirements-Builder v0.1.0 + Requirements-Builder v0.2.0 ============================= -Requirements-Builder v0.1.0 was released on October 6, 2015. +Requirements-Builder v0.2.0 was released on September 13, 2016. About ----- Build requirements files from setup.py requirements. -What's new ----------- +New features +~~~~~~~~~~~~ -- Initial public release +- Adds an output option which is useful in the tox context where one + cannot redirect the output to a file. See more at + https://bitbucket.org/hpk42/tox/issues/73/pipe-output-of-command-into-file + +Bug fixes +--------- + +- Fixes problem when the setup.py command try to import the package + its about to install in order to get the information like the + version. E.g. Django does that. +- Fixes problem when the setup.py command plays with `__file__` to + read, exec, or whatever. Installation ------------ - $ pip install requirements-builder==0.1.0 + $ pip install requirements-builder==0.2.0 Documentation ------------- From b771d77917e214a5d9e8c8b49090b21f3f831aaa Mon Sep 17 00:00:00 2001 From: Tibor Simko Date: Sun, 25 Sep 2016 13:32:29 +0200 Subject: [PATCH 12/54] docs: move to readthedocs.io Signed-off-by: Tibor Simko --- RELEASE-NOTES.rst | 2 +- requirements_builder/requirements_builder.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 4358c9a7..6951e778 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -33,7 +33,7 @@ Installation Documentation ------------- - http://pythonhosted.org/requirements-builder/ + http://requirements-builder.readthedocs.io/ Happy hacking and thanks for flying Requirements-Builder. diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index badb4f44..2944091c 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -52,7 +52,7 @@ def parse_pip_file(path): for line in f: line = line.strip() - # see https://pip.readthedocs.org/en/1.1/requirements.html + # see https://pip.readthedocs.io/en/1.1/requirements.html if line.startswith('-e'): # devel requirement splitted = line.split('#egg=') From 2c291b1ea615fe310362819f5e5a1e9cb6de9144 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Fri, 30 Sep 2016 15:00:38 +0200 Subject: [PATCH 13/54] docs: MAINTAINERS file and LGTM configuration Signed-off-by: Jiri Kuncar --- .lgtm | 3 +++ MAINTAINERS | 1 + 2 files changed, 4 insertions(+) create mode 100644 .lgtm create mode 100644 MAINTAINERS diff --git a/.lgtm b/.lgtm new file mode 100644 index 00000000..865e3786 --- /dev/null +++ b/.lgtm @@ -0,0 +1,3 @@ +approvals = 1 +pattern = "(?i)LGTM" +self_approval_off = false diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 00000000..4570456c --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1 @@ +Jiri Kuncar (@jirikuncar) From 9d7bd5e1a44279cfc8f2183e207add136c447b0d Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Fri, 21 Oct 2016 15:58:12 +0200 Subject: [PATCH 14/54] cli: "extras" accepting comma separated values * FIX Makes `--extras` option accepting comma separated values as described in help. (closes #14) Signed-off-by: Jiri Kuncar --- tests/data/setup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/data/setup.py b/tests/data/setup.py index fae6a22b..c09a56cd 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -22,8 +22,15 @@ 'mock>=1.3.0', ] +extras_require = { + 'docs': ['Sphinx>=1.4.2'], + 'tests': ['pytest>=2.7'], + 'flask': ['Flask>=0.11'], +} + setup( name='testpkh', version=testpkh.__version__, install_requires=requirements, + extras_require=extras_require, ) From 7175461c3066e51d3284ebf4f3b01d2c4bf42d32 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Mon, 23 Jan 2017 10:41:58 +0100 Subject: [PATCH 15/54] global: support for Python 2.6 and 3.6 * FIX Sphinx 1.5+ drops support for Python 2.6 and 3.3. * NEW Adds Python 3.6 support. Signed-off-by: Yoan Blanc --- .travis.yml | 4 +++- requirements_builder/requirements_builder.py | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f720c337..bddb6a77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016 CERN. +# Copyright (C) 2015, 2016, 2017 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -10,10 +10,12 @@ sudo: false language: python python: + - "3.6" - "3.5" - "3.4" - "3.3" - "2.7" + - "2.6" - "pypy" env: diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 2944091c..70db648c 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016 CERN. +# Copyright (C) 2015, 2016, 2017 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -32,7 +32,7 @@ def parse_set(string): def minver_error(pkg_name): """Report error about missing minimum version constraint and exit.""" print( - 'ERROR: specify minimal version of "{}" using ' + 'ERROR: specify minimal version of "{0}" using ' '">=" or "=="'.format(pkg_name), file=sys.stderr ) @@ -74,7 +74,7 @@ def parse_pip_file(path): rnormal.append(line) except IOError: print( - 'Warning: could not parse requirements file "{}"!', + 'Warning: could not parse requirements file "{0}"!', file=sys.stderr ) @@ -116,18 +116,20 @@ def iter_requirements(level, extras, pip_file, setup_fp): or (('<=' in specs) and ('<' in specs)): print( 'ERROR: Do not specify such weird constraints! ' - '("{}")'.format(pkg), + '("{0}")'.format(pkg), file=sys.stderr ) sys.exit(1) if '==' in specs: - result[pkg.key] = '{}=={}'.format( - pkg.project_name, specs['==']) + result[pkg.key] = '{0}=={1}'.format( + pkg.project_name, + specs['=='] + ) elif '>=' in specs: if level == 'min': - result[pkg.key] = '{}=={}'.format( + result[pkg.key] = '{0}=={1}'.format( pkg.project_name, specs['>='] ) From 8eea5deda78f717aaac43b15575bb81f888b6c30 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Mon, 23 Jan 2017 17:47:18 +0100 Subject: [PATCH 16/54] cli: accepting extra package from devel * FIX Accepts non-`-e` packages from devel file. Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 9 +++++---- tests/data/req.txt | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 70db648c..ec4274e2 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -70,7 +70,7 @@ def parse_pip_file(path): # another special command we don't recognize stuff.append(line) else: - # ordenary requirement, similary to them used in setup.py + # ordinary requirement, similarly to them used in setup.py rnormal.append(line) except IOError: print( @@ -98,14 +98,15 @@ def iter_requirements(level, extras, pip_file, setup_fp): # called arguments are in `mock_setup.call_args` mock_args, mock_kwargs = mock_setup.call_args - requires = mock_kwargs.get('install_requires', []) + install_requires = mock_kwargs.get('install_requires', []) + install_requires.extend(requires) requires_extras = mock_kwargs.get('extras_require', {}) for e in extras: if e in requires_extras: - requires.extend(requires_extras[e]) + install_requires.extend(requires_extras[e]) - for pkg in pkg_resources.parse_requirements(requires): + for pkg in pkg_resources.parse_requirements(install_requires): # skip things we already know # FIXME be smarter about merging things if pkg.key in result: diff --git a/tests/data/req.txt b/tests/data/req.txt index 532ca6ae..0d507915 100644 --- a/tests/data/req.txt +++ b/tests/data/req.txt @@ -1,8 +1,9 @@ # This file is part of Requirements-Builder -# Copyright (C) 2015 CERN. +# Copyright (C) 2015, 2017 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. -e git+https://github.com/mitsuhiko/click.git#egg=click +Cython>=0.20 From 9fb0bdd458bf91afc9458efd01e1ac8d10852dc1 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Mon, 23 Jan 2017 17:58:30 +0100 Subject: [PATCH 17/54] release: v0.2.1 Signed-off-by: Jiri Kuncar --- RELEASE-NOTES.rst | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 6951e778..20362778 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,34 +1,27 @@ ============================= - Requirements-Builder v0.2.0 + Requirements-Builder v0.2.1 ============================= -Requirements-Builder v0.2.0 was released on September 13, 2016. +Requirements-Builder v0.2.1 was released on January 23, 2017. About ----- Build requirements files from setup.py requirements. -New features -~~~~~~~~~~~~ - -- Adds an output option which is useful in the tox context where one - cannot redirect the output to a file. See more at - https://bitbucket.org/hpk42/tox/issues/73/pipe-output-of-command-into-file - Bug fixes --------- -- Fixes problem when the setup.py command try to import the package - its about to install in order to get the information like the - version. E.g. Django does that. -- Fixes problem when the setup.py command plays with `__file__` to - read, exec, or whatever. +- Accepts non-`-e` packages from devel file. +- Sphinx 1.5+ drops support for Python 2.6 and 3.3. +- Adds Python 3.6 support. +- Makes `--extras` option accepting comma separated values as + described in help. (#14) Installation ------------ - $ pip install requirements-builder==0.2.0 + $ pip install requirements-builder==0.2.1 Documentation ------------- From 0b02ccfac7b0bbf2fa68c1c2cbe1f1e4ff3ba40f Mon Sep 17 00:00:00 2001 From: Krzysztof Nowak Date: Tue, 31 Jan 2017 10:27:46 +0100 Subject: [PATCH 18/54] global: support for version markers * Adds support for markers Signed-off-by: Krzysztof Nowak --- requirements_builder/requirements_builder.py | 5 +++++ tests/data/setup.py | 1 + 2 files changed, 6 insertions(+) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index ec4274e2..4551f216 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -109,6 +109,11 @@ def iter_requirements(level, extras, pip_file, setup_fp): for pkg in pkg_resources.parse_requirements(install_requires): # skip things we already know # FIXME be smarter about merging things + + # Check for markers and skip if not applicable + if pkg.marker is not None and not pkg.marker.evaluate(): + continue + if pkg.key in result: continue diff --git a/tests/data/setup.py b/tests/data/setup.py index c09a56cd..d0114a0a 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -20,6 +20,7 @@ requirements = [ 'click>=5.0.0', 'mock>=1.3.0', + 'functools32>=3.2.3-2; python_version=="2.7"', ] extras_require = { From dc93d3d4beda89cc8f7ccdb76c8fe7727bf79034 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Fri, 27 Jan 2017 09:53:10 +0100 Subject: [PATCH 19/54] Yapf auto-formatting configuration. - Add configuration for yapf auto-formatting. (closes #19) Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 12 ++++-------- tests/data/setup.py | 5 ++--- tests/test_requirements_builder.py | 3 +-- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 4551f216..e070a7a0 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -6,7 +6,7 @@ # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. - +# """Generate requirements from `setup.py` and `requirements-devel.txt`.""" from __future__ import absolute_import, print_function @@ -43,7 +43,7 @@ def parse_pip_file(path): """Parse pip requirements file.""" # requirement lines sorted by importance # also collect other pip commands - rdev = dict() + rdev = {} rnormal = [] stuff = [] @@ -128,16 +128,12 @@ def iter_requirements(level, extras, pip_file, setup_fp): sys.exit(1) if '==' in specs: - result[pkg.key] = '{0}=={1}'.format( - pkg.project_name, - specs['=='] - ) + result[pkg.key] = '{0}=={1}'.format(pkg.project_name, specs['==']) elif '>=' in specs: if level == 'min': result[pkg.key] = '{0}=={1}'.format( - pkg.project_name, - specs['>='] + pkg.project_name, specs['>='] ) else: result[pkg.key] = pkg diff --git a/tests/data/setup.py b/tests/data/setup.py index d0114a0a..c7e575d0 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- # # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016 CERN. +# Copyright (C) 2015, 2016, 2017 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. - +# """Build requirements files from setup.py requirements.""" from setuptools import setup @@ -14,7 +14,6 @@ import os import testpkh - dirname = os.path.dirname(__file__) requirements = [ diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index 0a141ad4..be395311 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -6,8 +6,7 @@ # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. - - +# """Tests for `requirements-builder` module.""" from os.path import abspath, dirname, join From 149374dded74d7fbb3a62404a513f294076ddc2d Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Wed, 1 Feb 2017 08:48:16 +0100 Subject: [PATCH 20/54] release: v0.2.2 Signed-off-by: Lars Holm Nielsen --- RELEASE-NOTES.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 20362778..f908a2a4 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,8 +1,8 @@ ============================= - Requirements-Builder v0.2.1 + Requirements-Builder v0.2.2 ============================= -Requirements-Builder v0.2.1 was released on January 23, 2017. +Requirements-Builder v0.2.2 was released on February 1, 2017. About ----- @@ -12,16 +12,18 @@ Build requirements files from setup.py requirements. Bug fixes --------- -- Accepts non-`-e` packages from devel file. -- Sphinx 1.5+ drops support for Python 2.6 and 3.3. -- Adds Python 3.6 support. -- Makes `--extras` option accepting comma separated values as - described in help. (#14) +- Fixes issue with properly building requirements for packages with version + markers. + +Improvements +------------ + +- Adds YAPF auto-formatting configuration. Installation ------------ - $ pip install requirements-builder==0.2.1 + $ pip install requirements-builder==0.2.2 Documentation ------------- From 5fc74e72ac37c0d1b1456ef6a00c768c33c5777f Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Thu, 9 Mar 2017 06:59:57 +0100 Subject: [PATCH 21/54] global: environment markers in extra_require * Fixes issue with conditions on extra_require not being taken into account. Signed-off-by: Lars Holm Nielsen --- requirements_builder/requirements_builder.py | 27 +++++++++++++++----- tests/data/setup.py | 3 +++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index e070a7a0..f4f9c9d0 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -102,16 +102,25 @@ def iter_requirements(level, extras, pip_file, setup_fp): install_requires.extend(requires) requires_extras = mock_kwargs.get('extras_require', {}) - for e in extras: - if e in requires_extras: - install_requires.extend(requires_extras[e]) + for e, reqs in requires_extras.items(): + # Handle conditions on extras. See pkginfo_to_metadata function + # in Wheel for details. + condition = '' + if ':' in e: + e, condition = e.split(':', 1) + if not e or e in extras: + if condition: + reqs = ['{0}; {1}'.format(r, condition) for r in reqs] + install_requires.extend(reqs) for pkg in pkg_resources.parse_requirements(install_requires): # skip things we already know # FIXME be smarter about merging things # Check for markers and skip if not applicable - if pkg.marker is not None and not pkg.marker.evaluate(): + + if hasattr(pkg, 'marker') and pkg.marker is not None \ + and not pkg.marker.evaluate(): continue if pkg.key in result: @@ -136,19 +145,23 @@ def iter_requirements(level, extras, pip_file, setup_fp): pkg.project_name, specs['>='] ) else: - result[pkg.key] = pkg + result[pkg.key] = '{0}>={1}'.format( + pkg.project_name, specs['>='] + ) elif '>' in specs: if level == 'min': minver_error(pkg.project_name) else: - result[pkg.key] = pkg + result[pkg.key] = '{0}>{1}'.format( + pkg.project_name, specs['>'] + ) else: if level == 'min': minver_error(pkg.project_name) else: - result[pkg.key] = pkg + result[pkg.key] = pkg.project_name for s in stuff: yield s diff --git a/tests/data/setup.py b/tests/data/setup.py index c7e575d0..dfafcca2 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -26,6 +26,9 @@ 'docs': ['Sphinx>=1.4.2'], 'tests': ['pytest>=2.7'], 'flask': ['Flask>=0.11'], + ':python_version=="2.7"': [ + 'ipaddr>=2.1.11' + ] } setup( From 20ac9c8fe5f4d5b0ab0f2e328875ecd8af37a7a3 Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Thu, 9 Mar 2017 12:46:44 +0100 Subject: [PATCH 22/54] release: v0.2.3 Signed-off-by: Lars Holm Nielsen --- RELEASE-NOTES.rst | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index f908a2a4..e5220bd3 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,8 +1,8 @@ ============================= - Requirements-Builder v0.2.2 + Requirements-Builder v0.2.3 ============================= -Requirements-Builder v0.2.2 was released on February 1, 2017. +Requirements-Builder v0.2.3 was released on March 9, 2017. About ----- @@ -12,18 +12,13 @@ Build requirements files from setup.py requirements. Bug fixes --------- -- Fixes issue with properly building requirements for packages with version - markers. - -Improvements ------------- - -- Adds YAPF auto-formatting configuration. +- Fixes the issue with conditions on extra_require not being taken into + account. Installation ------------ - $ pip install requirements-builder==0.2.2 + $ pip install requirements-builder==0.2.3 Documentation ------------- From d8aa53e710bc3b85f87b303cd91522ae9736514a Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Fri, 10 Mar 2017 08:47:17 +0100 Subject: [PATCH 23/54] global: upper requirements fix * Fixes issue with upper version requirements being stripped from the output. Signed-off-by: Lars Holm Nielsen --- requirements_builder/requirements_builder.py | 20 +++++++++----------- tests/data/setup.py | 1 + 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index f4f9c9d0..bd7b54c9 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -117,11 +117,13 @@ def iter_requirements(level, extras, pip_file, setup_fp): # skip things we already know # FIXME be smarter about merging things - # Check for markers and skip if not applicable - - if hasattr(pkg, 'marker') and pkg.marker is not None \ - and not pkg.marker.evaluate(): - continue + # Evaluate environment markers skip if not applicable + if hasattr(pkg, 'marker') and pkg.marker is not None: + if not pkg.marker.evaluate(): + continue + else: + # Remove markers from the output + pkg.marker = None if pkg.key in result: continue @@ -145,17 +147,13 @@ def iter_requirements(level, extras, pip_file, setup_fp): pkg.project_name, specs['>='] ) else: - result[pkg.key] = '{0}>={1}'.format( - pkg.project_name, specs['>='] - ) + result[pkg.key] = pkg elif '>' in specs: if level == 'min': minver_error(pkg.project_name) else: - result[pkg.key] = '{0}>{1}'.format( - pkg.project_name, specs['>'] - ) + result[pkg.key] = pkg else: if level == 'min': diff --git a/tests/data/setup.py b/tests/data/setup.py index dfafcca2..8b800f9a 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -19,6 +19,7 @@ requirements = [ 'click>=5.0.0', 'mock>=1.3.0', + 'CairoSVG<2.0.0,>=1.0.20', 'functools32>=3.2.3-2; python_version=="2.7"', ] From d61edc0b5952c98f359efc06230fdd605a2332d6 Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Fri, 10 Mar 2017 08:49:59 +0100 Subject: [PATCH 24/54] release: v0.2.4 Signed-off-by: Lars Holm Nielsen --- RELEASE-NOTES.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index e5220bd3..3bc1bc04 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,8 +1,8 @@ ============================= - Requirements-Builder v0.2.3 + Requirements-Builder v0.2.4 ============================= -Requirements-Builder v0.2.3 was released on March 9, 2017. +Requirements-Builder v0.2.4 was released on March 10, 2017. About ----- @@ -12,13 +12,12 @@ Build requirements files from setup.py requirements. Bug fixes --------- -- Fixes the issue with conditions on extra_require not being taken into - account. +- Fixes issue with upper version requirements being stripped from the output. Installation ------------ - $ pip install requirements-builder==0.2.3 + $ pip install requirements-builder==0.2.4 Documentation ------------- From 665d74449d95188fc81cc89c02bd2d1ecd048902 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Sun, 2 Apr 2017 12:20:31 +0200 Subject: [PATCH 25/54] cli: no setup.py if req.txt is provided. Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 28 ++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index bd7b54c9..3f696c25 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -86,22 +86,28 @@ def iter_requirements(level, extras, pip_file, setup_fp): result = dict() requires = [] stuff = [] - if level == 'dev': + if level == 'dev' or setup_fp is None: result, requires, stuff = parse_pip_file(pip_file) - with mock.patch.object(setuptools, 'setup') as mock_setup: - sys.path.append(os.path.dirname(setup_fp.name)) - g = {'__file__': setup_fp.name} - exec(setup_fp.read(), g) - sys.path.pop() - assert g['setup'] # silence warning about unused imports + install_requires = [] + requires_extras = {} + if setup_fp is not None: + with mock.patch.object(setuptools, 'setup') as mock_setup: + sys.path.append(os.path.dirname(setup_fp.name)) + g = {'__file__': setup_fp.name} + exec(setup_fp.read(), g) + sys.path.pop() + assert g['setup'] # silence warning about unused imports + + # called arguments are in `mock_setup.call_args` + mock_args, mock_kwargs = mock_setup.call_args + install_requires = mock_kwargs.get( + 'install_requires', install_requires + ) + requires_extras = mock_kwargs.get('extras_require', requires_extras) - # called arguments are in `mock_setup.call_args` - mock_args, mock_kwargs = mock_setup.call_args - install_requires = mock_kwargs.get('install_requires', []) install_requires.extend(requires) - requires_extras = mock_kwargs.get('extras_require', {}) for e, reqs in requires_extras.items(): # Handle conditions on extras. See pkginfo_to_metadata function # in Wheel for details. From 71a353e6d7bb3b45226bbc89009382ffdd7d6cb9 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Sun, 2 Apr 2017 12:27:21 +0200 Subject: [PATCH 26/54] installation: upgrade click to >=6.1.0 Signed-off-by: Yoan Blanc --- tests/data/setup.py | 4 +--- tests/test_requirements_builder.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/data/setup.py b/tests/data/setup.py index 8b800f9a..0d6c41e4 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -27,9 +27,7 @@ 'docs': ['Sphinx>=1.4.2'], 'tests': ['pytest>=2.7'], 'flask': ['Flask>=0.11'], - ':python_version=="2.7"': [ - 'ipaddr>=2.1.11' - ] + ':python_version=="2.7"': ['ipaddr>=2.1.11'] } setup( diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index be395311..a2a40039 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -27,12 +27,12 @@ def test_iter_requirements(): # Min with open(SETUP) as f: assert list(iter_requirements("min", [], '', f)) == \ - ['click==5.0.0', 'mock==1.3.0'] + ['click==6.1.0', 'mock==1.3.0'] # PyPI with open(SETUP) as f: assert list(iter_requirements("pypi", [], '', f)) == \ - ['click>=5.0.0', 'mock>=1.3.0'] + ['click>=6.1.0', 'mock>=1.3.0'] # Dev with open(SETUP) as f: From 42cd9d6388769f6fac25a864b5fde6eeeadaafa0 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Sun, 2 Apr 2017 13:44:31 +0200 Subject: [PATCH 27/54] global: recursive requirements files fix * Fixes support of recursive requirements files. Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 8 +++++--- tests/data/other_req.txt | 8 ++++++++ tests/data/req.txt | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 tests/data/other_req.txt diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 3f696c25..c2d4e215 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -61,8 +61,10 @@ def parse_pip_file(path): elif line.startswith('-r'): # recursive file command splitted = re.split('-r\\s+', line) - subrdev, subrnormal, substuff = parse_pip_file(splitted[1]) - for k, v in subrdev.iteritems(): + subrdev, subrnormal, substuff = parse_pip_file( + os.path.join(os.path.dirname(path), splitted[1]) + ) + for k, v in subrdev.items(): if k not in rdev: rdev[k] = v rnormal.extend(subrnormal) @@ -74,7 +76,7 @@ def parse_pip_file(path): rnormal.append(line) except IOError: print( - 'Warning: could not parse requirements file "{0}"!', + 'Warning: could not parse requirements file "{0}"!'.format(path), file=sys.stderr ) diff --git a/tests/data/other_req.txt b/tests/data/other_req.txt new file mode 100644 index 00000000..923ff44f --- /dev/null +++ b/tests/data/other_req.txt @@ -0,0 +1,8 @@ +# This file is part of Requirements-Builder +# Copyright (C) 2017 CERN. +# +# Requirements-Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + +-e git+https://github.com/mitsuhiko/click.git#egg=click diff --git a/tests/data/req.txt b/tests/data/req.txt index 0d507915..c3b2ab1f 100644 --- a/tests/data/req.txt +++ b/tests/data/req.txt @@ -5,5 +5,5 @@ # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. --e git+https://github.com/mitsuhiko/click.git#egg=click +-r other_req.txt Cython>=0.20 From 59ff2bc46baf6f3ca03887c21086e2d19ab420aa Mon Sep 17 00:00:00 2001 From: "Esteban J. G. Gabancho" Date: Tue, 11 Apr 2017 15:47:17 +0200 Subject: [PATCH 28/54] release: v0.2.5 Signed-off-by: Esteban J. G. Gabancho --- RELEASE-NOTES.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 3bc1bc04..5168275a 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,8 +1,8 @@ ============================= - Requirements-Builder v0.2.4 + Requirements-Builder v0.2.5 ============================= -Requirements-Builder v0.2.4 was released on March 10, 2017. +Requirements-Builder v0.2.5 was released on April 11, 2017. About ----- @@ -12,12 +12,12 @@ Build requirements files from setup.py requirements. Bug fixes --------- -- Fixes issue with upper version requirements being stripped from the output. +- Fixes support of recursive requirements files. Installation ------------ - $ pip install requirements-builder==0.2.4 + $ pip install requirements-builder==0.2.5 Documentation ------------- From 39260a8babb61062ae9aaece316f2eeb0aa94159 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Tue, 27 Jun 2017 10:29:48 +0200 Subject: [PATCH 29/54] global: removal of Python 2.6 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bddb6a77..6050102a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,6 @@ python: - "3.4" - "3.3" - "2.7" - - "2.6" - "pypy" env: From dee256d03d98321fc500f387766f70a168703dc0 Mon Sep 17 00:00:00 2001 From: Lars Hupfeldt Nielsen Date: Mon, 10 Jul 2017 15:30:52 +0200 Subject: [PATCH 30/54] global: setup() under __main__ fix * Fixes fatal error if setup is called under 'if __name__ == "__main__":' --- requirements_builder/requirements_builder.py | 2 +- tests/data/setup_if_main.py | 31 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/data/setup_if_main.py diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index c2d4e215..0dec9632 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -96,7 +96,7 @@ def iter_requirements(level, extras, pip_file, setup_fp): if setup_fp is not None: with mock.patch.object(setuptools, 'setup') as mock_setup: sys.path.append(os.path.dirname(setup_fp.name)) - g = {'__file__': setup_fp.name} + g = {'__file__': setup_fp.name, '__name__': '__main__'} exec(setup_fp.read(), g) sys.path.pop() assert g['setup'] # silence warning about unused imports diff --git a/tests/data/setup_if_main.py b/tests/data/setup_if_main.py new file mode 100644 index 00000000..6df7ecab --- /dev/null +++ b/tests/data/setup_if_main.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Requirements-Builder +# Copyright (C) 2017 CERN. +# +# Requirements-Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. +# +"""Build requirements files from setup.py requirements.""" + +from setuptools import setup + +import testpkh + + +requirements = [ + 'click>=5.0.0', +] + +extras_require = { + 'docs': ['Sphinx>=1.4.2'], +} + +if __name__ == "__main__": + setup( + name='testpkh', + version=testpkh.__version__, + install_requires=requirements, + extras_require=extras_require, + ) From 169b0a89cac2adb9d1738ca5d3ab6999cfacab46 Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Thu, 13 Jul 2017 21:03:20 +0200 Subject: [PATCH 31/54] release: v0.2.6 --- RELEASE-NOTES.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 5168275a..130564bb 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,8 +1,8 @@ ============================= - Requirements-Builder v0.2.5 + Requirements-Builder v0.2.6 ============================= -Requirements-Builder v0.2.5 was released on April 11, 2017. +Requirements-Builder v0.2.6 was released on July 13, 2017. About ----- @@ -12,12 +12,12 @@ Build requirements files from setup.py requirements. Bug fixes --------- -- Fixes support of recursive requirements files. +- Fixes fatal error if setup() is called under 'if __name__ == "__main__":' Installation ------------ - $ pip install requirements-builder==0.2.5 + $ pip install requirements-builder==0.2.6 Documentation ------------- From 902eaff53c161c63578070b47e2403a9d91dc8f0 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Thu, 22 Mar 2018 22:47:23 +0100 Subject: [PATCH 32/54] global: accepts ~= selector Closes #35 Signed-off-by: Yoan Blanc --- .travis.yml | 4 ++-- requirements_builder/requirements_builder.py | 11 ++++++++++- tests/data/setup.py | 3 ++- tests/test_requirements_builder.py | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6050102a..27cb4d37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016, 2017 CERN. +# Copyright (C) 2015, 2016, 2017, 2018 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -10,10 +10,10 @@ sudo: false language: python python: + - "3.7-dev" - "3.6" - "3.5" - "3.4" - - "3.3" - "2.7" - "pypy" diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 0dec9632..d33b8b9b 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016, 2017 CERN. +# Copyright (C) 2015, 2016, 2017, 2018 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -163,6 +163,15 @@ def iter_requirements(level, extras, pip_file, setup_fp): else: result[pkg.key] = pkg + elif '~=' in specs: + if level == 'min': + result[pkg.key] = '{0}=={1}'.format( + pkg.project_name, specs['~=']) + else: + ver, _ = os.path.splitext(specs['~=']) + result[pkg.key] = '{0}=={1}.*'.format( + pkg.project_name, ver) + else: if level == 'min': minver_error(pkg.project_name) diff --git a/tests/data/setup.py b/tests/data/setup.py index 0d6c41e4..fe2d112a 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016, 2017 CERN. +# Copyright (C) 2015, 2016, 2017, 2018 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE @@ -21,6 +21,7 @@ 'mock>=1.3.0', 'CairoSVG<2.0.0,>=1.0.20', 'functools32>=3.2.3-2; python_version=="2.7"', + 'invenio-records~=1.0.0', ] extras_require = { diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index a2a40039..e787ff84 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -37,5 +37,5 @@ def test_iter_requirements(): # Dev with open(SETUP) as f: assert list(iter_requirements("dev", [], REQ, f)) == \ - ['-e git+https://github.com/mitsuhiko/click.git#egg=click', + ['-e git+https://github.com/pallets/click.git#egg=click', 'mock>=1.3.0'] From c0206602fb9b7b8542c529a25b681404fbd9c8f9 Mon Sep 17 00:00:00 2001 From: Alexander Ioannidis Date: Wed, 16 May 2018 11:25:40 +0200 Subject: [PATCH 33/54] global: fix ~= selector output * Includes the minimum version additionally to the X.* specifier. --- requirements_builder/requirements_builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index d33b8b9b..72c9942a 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -169,8 +169,8 @@ def iter_requirements(level, extras, pip_file, setup_fp): pkg.project_name, specs['~=']) else: ver, _ = os.path.splitext(specs['~=']) - result[pkg.key] = '{0}=={1}.*'.format( - pkg.project_name, ver) + result[pkg.key] = '{0}>={1},=={2}.*'.format( + pkg.project_name, specs['~='], ver) else: if level == 'min': From 289c232757d8b4bbbdc70047a6cc3686a8055fe8 Mon Sep 17 00:00:00 2001 From: Alexander Ioannidis Date: Wed, 16 May 2018 13:32:31 +0200 Subject: [PATCH 34/54] global: inclusions of package extras in results --- requirements_builder/requirements_builder.py | 23 ++++++++++++++------ tests/data/setup.py | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index 72c9942a..cc990b15 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -39,6 +39,14 @@ def minver_error(pkg_name): sys.exit(1) +def build_pkg_name(pkg): + """Build package name, including extras if present.""" + if pkg.extras: + return '{0}[{1}]'.format( + pkg.project_name, ','.join(sorted(pkg.extras))) + return pkg.project_name + + def parse_pip_file(path): """Parse pip requirements file.""" # requirement lines sorted by importance @@ -147,36 +155,37 @@ def iter_requirements(level, extras, pip_file, setup_fp): sys.exit(1) if '==' in specs: - result[pkg.key] = '{0}=={1}'.format(pkg.project_name, specs['==']) + result[pkg.key] = '{0}=={1}'.format( + build_pkg_name(pkg), specs['==']) elif '>=' in specs: if level == 'min': result[pkg.key] = '{0}=={1}'.format( - pkg.project_name, specs['>='] + build_pkg_name(pkg), specs['>='] ) else: result[pkg.key] = pkg elif '>' in specs: if level == 'min': - minver_error(pkg.project_name) + minver_error(build_pkg_name(pkg)) else: result[pkg.key] = pkg elif '~=' in specs: if level == 'min': result[pkg.key] = '{0}=={1}'.format( - pkg.project_name, specs['~=']) + build_pkg_name(pkg), specs['~=']) else: ver, _ = os.path.splitext(specs['~=']) result[pkg.key] = '{0}>={1},=={2}.*'.format( - pkg.project_name, specs['~='], ver) + build_pkg_name(pkg), specs['~='], ver) else: if level == 'min': - minver_error(pkg.project_name) + minver_error(build_pkg_name(pkg)) else: - result[pkg.key] = pkg.project_name + result[pkg.key] = build_pkg_name(pkg) for s in stuff: yield s diff --git a/tests/data/setup.py b/tests/data/setup.py index fe2d112a..98bd7093 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -22,6 +22,7 @@ 'CairoSVG<2.0.0,>=1.0.20', 'functools32>=3.2.3-2; python_version=="2.7"', 'invenio-records~=1.0.0', + 'invenio[base,auth,metadata]>=3.0.0', ] extras_require = { From 6a01d6c851f9c2f5ec4c0258ded4314ee45bcb1c Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Thu, 17 May 2018 10:18:52 +0200 Subject: [PATCH 35/54] release: v0.3.0 --- RELEASE-NOTES.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst index 130564bb..74eb4ca7 100644 --- a/RELEASE-NOTES.rst +++ b/RELEASE-NOTES.rst @@ -1,23 +1,29 @@ ============================= - Requirements-Builder v0.2.6 + Requirements-Builder v0.3.0 ============================= -Requirements-Builder v0.2.6 was released on July 13, 2017. +Requirements-Builder v0.3.0 was released on May 17, 2018. About ----- Build requirements files from setup.py requirements. +New features +~~~~~~~~~~~~ + +- Includes package extras in the generated result. + Bug fixes --------- -- Fixes fatal error if setup() is called under 'if __name__ == "__main__":' +- Fixes ``~=`` selector output by including the minimum version + additionally to the X.* specifier. Installation ------------ - $ pip install requirements-builder==0.2.6 + $ pip install requirements-builder==0.3.0 Documentation ------------- From 4b624a4393c393f8af971dd68a03d3d0c28a5fed Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Mon, 6 Aug 2018 19:22:57 +0200 Subject: [PATCH 36/54] travis: enable build on pypy3 Signed-off-by: Yoan Blanc --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 27cb4d37..9133c9bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ python: - "3.5" - "3.4" - "2.7" + - "pypy3" - "pypy" env: From 9da3daca1e90d55296bcb2a774aa0cb57952683b Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Fri, 6 Mar 2020 14:43:08 +0100 Subject: [PATCH 37/54] tox: remove Python 3.4 and 2.7 Signed-off-by: Yoan Blanc Co-authored-by: Christian Clauss --- .travis.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9133c9bc..49ebbd9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,18 @@ # This file is part of Requirements-Builder -# Copyright (C) 2015, 2016, 2017, 2018 CERN. +# Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 CERN. # # Requirements-Builder is free software; you can redistribute it and/or # modify it under the terms of the Revised BSD License; see LICENSE # file for more details. -sudo: false - language: python python: - - "3.7-dev" + - "3.8" + - "3.7" - "3.6" - "3.5" - - "3.4" - - "2.7" - "pypy3" - - "pypy" env: - REQUIREMENTS=devel @@ -53,5 +49,5 @@ deploy: distributions: sdist bdist_wheel on: tags: true - python: '2.7' + python: "3.7" condition: "$REQUIREMENTS = latest" From ab1682d50906ffcc77ef0b608133a98dc77486ed Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Sat, 2 May 2020 14:32:11 +0200 Subject: [PATCH 38/54] migrate to setup.cfg Signed-off-by: Yoan Blanc --- tests/fixtures/requirements.devel.txt | 8 +++ tests/fixtures/setup.txt | 86 +++++++++++++++++++++++++++ tests/test_requirements_builder.py | 4 +- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/requirements.devel.txt create mode 100644 tests/fixtures/setup.txt diff --git a/tests/fixtures/requirements.devel.txt b/tests/fixtures/requirements.devel.txt new file mode 100644 index 00000000..eebb0091 --- /dev/null +++ b/tests/fixtures/requirements.devel.txt @@ -0,0 +1,8 @@ +# This file is part of Requirements-Builder +# Copyright (C) 2015, 2018, 2020 CERN. +# +# Requirements-Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. + +-e git+https://github.com/pallets/click.git#egg=click diff --git a/tests/fixtures/setup.txt b/tests/fixtures/setup.txt new file mode 100644 index 00000000..2336ef3a --- /dev/null +++ b/tests/fixtures/setup.txt @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Requirements-Builder +# Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 CERN. +# +# Requirements-Builder is free software; you can redistribute it and/or +# modify it under the terms of the Revised BSD License; see LICENSE +# file for more details. +# +"""Build requirements files from setup.py requirements.""" + +import os + +from setuptools import setup + +# Get the version string. Cannot be done with import! +g = {} +with open(os.path.join('requirements_builder', 'version.py'), 'rt') as fp: + exec(fp.read(), g) + version = g['__version__'] + +with open('README.rst') as readme_file: + readme = readme_file.read() + +with open('CHANGES.rst') as history_file: + history = history_file.read().replace('.. :changes:', '') + +install_requires = [ + 'click>=6.1.0', + 'mock>=1.3.0', +] + +tests_require = [ + 'check-manifest>=0.25', + 'coverage>=4.0', + 'isort>=4.0.0', + 'pydocstyle>=1.0.0', + 'pytest-cache>=1.0', + 'pytest-cov>=2.0.0', + 'pytest-pep8>=1.0.6', + 'pytest>=2.8.0', +] + +extras_require = { + 'docs': [ + 'Sphinx>=2.4', + ], + 'tests': tests_require, +} + +extras_require['all'] = extras_require['tests'] + extras_require['docs'] + +setup_requires = ['pytest-runner>=2.6.2', ] + +setup( + name='requirements-builder', + version=version, + description=__doc__, + long_description=readme + '\n\n' + history, + author="Invenio Collaboration", + author_email='info@inveniosoftware.org', + url='https://github.com/inveniosoftware/requirements-builder', + entry_points={ + 'console_scripts': + ["requirements-builder = requirements_builder.cli:cli"] + }, + packages=['requirements_builder', ], + include_package_data=True, + extras_require=extras_require, + install_requires=install_requires, + setup_requires=setup_requires, + tests_require=tests_require, + license='BSD', + zip_safe=False, + keywords='requirements-builder', + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Natural Language :: English', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], +) diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index e787ff84..843fcb73 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -13,8 +13,8 @@ from requirements_builder import __version__, iter_requirements -REQ = abspath(join(dirname(__file__), "../requirements.devel.txt")) -SETUP = abspath(join(dirname(__file__), "../setup.py")) +REQ = abspath(join(dirname(__file__), "./fixtures/requirements.devel.txt")) +SETUP = abspath(join(dirname(__file__), "./fixtures/setup.txt")) def test_version(): From a2df7c374ff476d63613b67f7cc9629d8bd76309 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Mon, 4 May 2020 10:23:14 +0200 Subject: [PATCH 39/54] read requirements from setup.cfg too Signed-off-by: Yoan Blanc --- requirements_builder/requirements_builder.py | 19 ++++++++++++++- tests/test_requirements_builder.py | 25 ++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index cc990b15..a7f4d2d8 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -14,6 +14,7 @@ import os import re import sys +import configparser import mock import pkg_resources @@ -91,7 +92,7 @@ def parse_pip_file(path): return rdev, rnormal, stuff -def iter_requirements(level, extras, pip_file, setup_fp): +def iter_requirements(level, extras, pip_file, setup_fp, setup_cfg_fp=None): """Iterate over requirements.""" result = dict() requires = [] @@ -116,6 +117,22 @@ def iter_requirements(level, extras, pip_file, setup_fp): ) requires_extras = mock_kwargs.get('extras_require', requires_extras) + if setup_cfg_fp is not None: + parser = configparser.ConfigParser() + parser.read_file(setup_cfg_fp) + + if parser.has_section("options"): + value = parser.get("options", "install_requires", + fallback="").strip() + + if value: + install_requires = [s.strip() for s in value.splitlines()] + + if parser.has_section("options.extras_require"): + for name, value in parser.items("options.extras_require"): + requires_extras[name] = [s.strip() + for s in value.strip().splitlines()] + install_requires.extend(requires) for e, reqs in requires_extras.items(): diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index 843fcb73..3d199ace 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -39,3 +39,28 @@ def test_iter_requirements(): assert list(iter_requirements("dev", [], REQ, f)) == \ ['-e git+https://github.com/pallets/click.git#egg=click', 'mock>=1.3.0'] + + +def test_iter_requirements_cfg(): + """Test requirements-builder.""" + req = abspath(join(dirname(__file__), "../requirements.devel.txt")) + setup = abspath(join(dirname(__file__), "../setup.py")) + setup_cfg = abspath(join(dirname(__file__), "../setup.cfg")) + + # Min + with open(setup) as f: + with open(setup_cfg) as g: + assert list(iter_requirements("min", [], '', f, g)) == \ + ['click==6.1.0', 'mock==1.3.0'] + + # PyPI + with open(setup) as f: + with open(setup_cfg) as g: + assert list(iter_requirements("pypi", [], '', f, g)) == \ + ['click>=6.1.0', 'mock>=1.3.0'] + + # Dev + with open(setup) as f: + with open(setup_cfg) as g: + assert list(iter_requirements("dev", [], req, f, g)) == \ + ['click>=6.1.0', 'mock>=1.3.0'] From 5e4e37c5620dda03f3726b24cc8d2025317ff138 Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Mon, 25 May 2020 08:41:30 +0200 Subject: [PATCH 40/54] fix: versions working with python2 Signed-off-by: Yoan Blanc --- tests/test_requirements_builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index 3d199ace..138f22e4 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -57,10 +57,10 @@ def test_iter_requirements_cfg(): with open(setup) as f: with open(setup_cfg) as g: assert list(iter_requirements("pypi", [], '', f, g)) == \ - ['click>=6.1.0', 'mock>=1.3.0'] + ['click>=6.1.0', 'mock<4,>=1.3.0'] # Dev with open(setup) as f: with open(setup_cfg) as g: assert list(iter_requirements("dev", [], req, f, g)) == \ - ['click>=6.1.0', 'mock>=1.3.0'] + ['click>=6.1.0', 'mock<4,>=1.3.0'] From a3092b89b0afcf4791cb1d2bda2ddd88d3fadc29 Mon Sep 17 00:00:00 2001 From: Lars Holm Nielsen Date: Mon, 25 May 2020 11:59:21 +0200 Subject: [PATCH 41/54] global: remove unused files --- .lgtm | 3 --- MAINTAINERS | 1 - RELEASE-NOTES.rst | 40 ---------------------------------------- 3 files changed, 44 deletions(-) delete mode 100644 .lgtm delete mode 100644 MAINTAINERS delete mode 100644 RELEASE-NOTES.rst diff --git a/.lgtm b/.lgtm deleted file mode 100644 index 865e3786..00000000 --- a/.lgtm +++ /dev/null @@ -1,3 +0,0 @@ -approvals = 1 -pattern = "(?i)LGTM" -self_approval_off = false diff --git a/MAINTAINERS b/MAINTAINERS deleted file mode 100644 index 4570456c..00000000 --- a/MAINTAINERS +++ /dev/null @@ -1 +0,0 @@ -Jiri Kuncar (@jirikuncar) diff --git a/RELEASE-NOTES.rst b/RELEASE-NOTES.rst deleted file mode 100644 index 74eb4ca7..00000000 --- a/RELEASE-NOTES.rst +++ /dev/null @@ -1,40 +0,0 @@ -============================= - Requirements-Builder v0.3.0 -============================= - -Requirements-Builder v0.3.0 was released on May 17, 2018. - -About ------ - -Build requirements files from setup.py requirements. - -New features -~~~~~~~~~~~~ - -- Includes package extras in the generated result. - -Bug fixes ---------- - -- Fixes ``~=`` selector output by including the minimum version - additionally to the X.* specifier. - -Installation ------------- - - $ pip install requirements-builder==0.3.0 - -Documentation -------------- - - http://requirements-builder.readthedocs.io/ - -Happy hacking and thanks for flying Requirements-Builder. - -| Invenio Development Team -| Email: info@inveniosoftware.org -| IRC: #invenio on irc.freenode.net -| Twitter: http://twitter.com/inveniosoftware -| GitHub: https://github.com/inveniosoftware/requirements-builder -| URL: http://inveniosoftware.org From e41544c6c0dd40a141c03e88f6faddefaf5807aa Mon Sep 17 00:00:00 2001 From: Alexander Ioannidis Date: Mon, 25 May 2020 15:03:25 +0200 Subject: [PATCH 42/54] global: fix configparser import for Python 2 --- requirements_builder/requirements_builder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index a7f4d2d8..c76b5674 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -14,8 +14,10 @@ import os import re import sys -import configparser - +try: + import configparser +except ImportError: # pragma: no cover + import ConfigParser as configparser import mock import pkg_resources import setuptools From e9d6946183ace0f1938a0d8978c6cc669aff8fff Mon Sep 17 00:00:00 2001 From: Antonio Vivace Date: Tue, 8 Dec 2020 15:36:32 +0100 Subject: [PATCH 43/54] global: migrate from Travis CI to GitHub Actions --- .travis.yml | 53 ----------------------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 49ebbd9b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,53 +0,0 @@ -# This file is part of Requirements-Builder -# Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 CERN. -# -# Requirements-Builder is free software; you can redistribute it and/or -# modify it under the terms of the Revised BSD License; see LICENSE -# file for more details. - -language: python - -python: - - "3.8" - - "3.7" - - "3.6" - - "3.5" - - "pypy3" - -env: - - REQUIREMENTS=devel - - REQUIREMENTS=latest - - REQUIREMENTS=lowest - -matrix: - fast_finish: true - -cache: - - pip - -before_install: - - "travis_retry pip install --upgrade pip setuptools py" - - "travis_retry pip install coveralls twine wheel" - -install: - - "travis_retry pip install -r requirements.${REQUIREMENTS}.txt" - - "travis_retry pip install -e .[all]" - -script: ./run-tests.sh - -after_success: - - coveralls - -notifications: - email: false - -deploy: - provider: pypi - user: jirikuncar - password: - secure: bOutxlDnZsYDQoq1IiIK9VbTT6hI36gIuWUWfPw6weBX5WdEA7oIM5CCapb7sHcMMHmz57LTJYT9QVjqLXk8Oe7S3ndD7n5BI4M2ysz6o0ZIlQJ8YVChLr0TT7O4i+Xxw5MTUuiOpNtS7r0wEImmWI64PoQHS8VeHJfviUZEZcUxghZUAuGXtq7WiSzzhQH7uEPpbD+6WePB2FOZ2wfxIXJPQ/jiPN6wEaf5nZoyvm/N/bnvKaGYxKWHAjCviYKYzWmK3AlPAq9ELTgeeCHmteKZoUmFur4GbF6z5pT3D5olfA2K2Sv60dx0Bxbr3DOBULUlst2rsnFYsrQw18a3dsy30/rgjDixxPtxxy9fzB4X60QIzsTmFXbXMKBr1aMKYjmwaXhBkxX/S99g1tsuctwUQwj41CW5XVEF154ZSmghyJ1XTqbqKG9tn7zkJHTGrS3Xd+GI9FNBI2/QXZCDWPpsnPxvoSq4giQK8UzPnrnoKDzu25xlftqd9UXkqVMiVYfXGXcjtlL5l28grC/TyLEjxogYFGSiJFChuVPpcS65dgkjSw4M6tDWgSctR1E8VA755ZQ+wPtMSi0C5oemL2gvsB2maeK2i37ytv+Y3Pq6tlZ5c7Lcjb72ktwoqvmGmfsiB8jEoAUyZB4R5OL6utkewMGaEP+QuShxHPDo2/Y= - distributions: sdist bdist_wheel - on: - tags: true - python: "3.7" - condition: "$REQUIREMENTS = latest" From e92a168e7ff81e8f9fa2aeee755456114794f457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Vidal=20Garc=C3=ADa?= Date: Fri, 11 Dec 2020 16:07:19 +0100 Subject: [PATCH 44/54] setup: update dependencies in setup.cfg - fix pydocstyle and pycodestyle issues - adapt tests to new dependencies closes #52 --- requirements_builder/requirements_builder.py | 2 ++ tests/data/setup.py | 4 ++-- tests/data/setup_if_main.py | 4 +--- tests/data/testpkh/__init__.py | 1 + tests/test_requirements_builder.py | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/requirements_builder/requirements_builder.py b/requirements_builder/requirements_builder.py index c76b5674..f260f516 100755 --- a/requirements_builder/requirements_builder.py +++ b/requirements_builder/requirements_builder.py @@ -14,10 +14,12 @@ import os import re import sys + try: import configparser except ImportError: # pragma: no cover import ConfigParser as configparser + import mock import pkg_resources import setuptools diff --git a/tests/data/setup.py b/tests/data/setup.py index 98bd7093..31bb2b53 100644 --- a/tests/data/setup.py +++ b/tests/data/setup.py @@ -9,10 +9,10 @@ # """Build requirements files from setup.py requirements.""" -from setuptools import setup - import os + import testpkh +from setuptools import setup dirname = os.path.dirname(__file__) diff --git a/tests/data/setup_if_main.py b/tests/data/setup_if_main.py index 6df7ecab..c853c8ec 100644 --- a/tests/data/setup_if_main.py +++ b/tests/data/setup_if_main.py @@ -9,10 +9,8 @@ # """Build requirements files from setup.py requirements.""" -from setuptools import setup - import testpkh - +from setuptools import setup requirements = [ 'click>=5.0.0', diff --git a/tests/data/testpkh/__init__.py b/tests/data/testpkh/__init__.py index b8023d8b..aa079fd6 100644 --- a/tests/data/testpkh/__init__.py +++ b/tests/data/testpkh/__init__.py @@ -1 +1,2 @@ +"""Version.""" __version__ = '0.0.1' diff --git a/tests/test_requirements_builder.py b/tests/test_requirements_builder.py index 138f22e4..5f7f56ec 100755 --- a/tests/test_requirements_builder.py +++ b/tests/test_requirements_builder.py @@ -51,16 +51,16 @@ def test_iter_requirements_cfg(): with open(setup) as f: with open(setup_cfg) as g: assert list(iter_requirements("min", [], '', f, g)) == \ - ['click==6.1.0', 'mock==1.3.0'] + ['click==7.0', 'mock==1.3.0'] # PyPI with open(setup) as f: with open(setup_cfg) as g: assert list(iter_requirements("pypi", [], '', f, g)) == \ - ['click>=6.1.0', 'mock<4,>=1.3.0'] + ['click>=7.0', 'mock<4,>=1.3.0'] # Dev with open(setup) as f: with open(setup_cfg) as g: assert list(iter_requirements("dev", [], req, f, g)) == \ - ['click>=6.1.0', 'mock<4,>=1.3.0'] + ['click>=7.0', 'mock<4,>=1.3.0'] From bc4b210ec5ccd2632e86f3f978042e7748f35aee Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Tue, 30 Aug 2022 09:49:21 +0200 Subject: [PATCH 45/54] Move files to their target locations Signed-off-by: Philippe Ombredanne --- setup.cfg => requirements_builder.ABOUT | 0 LICENSE => requirements_builder.LICENSE | 0 .../package_inspector/setup_py_live_eval.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename setup.cfg => requirements_builder.ABOUT (100%) rename LICENSE => requirements_builder.LICENSE (100%) rename requirements_builder/requirements_builder.py => src/package_inspector/setup_py_live_eval.py (100%) diff --git a/setup.cfg b/requirements_builder.ABOUT similarity index 100% rename from setup.cfg rename to requirements_builder.ABOUT diff --git a/LICENSE b/requirements_builder.LICENSE similarity index 100% rename from LICENSE rename to requirements_builder.LICENSE diff --git a/requirements_builder/requirements_builder.py b/src/package_inspector/setup_py_live_eval.py similarity index 100% rename from requirements_builder/requirements_builder.py rename to src/package_inspector/setup_py_live_eval.py From ebb125b8273174abd0b60efa001d245f64c83190 Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Wed, 31 Aug 2022 15:27:56 +0200 Subject: [PATCH 46/54] Add ABOUT file and move to target location Signed-off-by: Philippe Ombredanne --- src/package_inspector/setup_py_live_eval.py.ABOUT | 8 ++++++++ .../package_inspector/setup_py_live_eval.py.LICENSE | 0 tests/{test_cli.py => test_reqbuilder_cli.py} | 0 3 files changed, 8 insertions(+) create mode 100644 src/package_inspector/setup_py_live_eval.py.ABOUT rename requirements_builder.LICENSE => src/package_inspector/setup_py_live_eval.py.LICENSE (100%) rename tests/{test_cli.py => test_reqbuilder_cli.py} (100%) diff --git a/src/package_inspector/setup_py_live_eval.py.ABOUT b/src/package_inspector/setup_py_live_eval.py.ABOUT new file mode 100644 index 00000000..01b38c1a --- /dev/null +++ b/src/package_inspector/setup_py_live_eval.py.ABOUT @@ -0,0 +1,8 @@ +name: requirements-builder +version: 597340d1e84138af64786d45e74fc9f03315bf2d +copyright: Copyright (C) CERN. +homepage_url: https://github.com/inveniosoftware/requirements-builder/ +description: Build requirements files from setup.py requirements. +license_expression: bsd-new +license_file: requirements_builder.LICENSE +notes: this is a subset of requirements-builder that has been heavily modified. diff --git a/requirements_builder.LICENSE b/src/package_inspector/setup_py_live_eval.py.LICENSE similarity index 100% rename from requirements_builder.LICENSE rename to src/package_inspector/setup_py_live_eval.py.LICENSE diff --git a/tests/test_cli.py b/tests/test_reqbuilder_cli.py similarity index 100% rename from tests/test_cli.py rename to tests/test_reqbuilder_cli.py From 7da295ab8148c3ab043f1746a5bddd0a47f1bc10 Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Wed, 31 Aug 2022 15:37:31 +0200 Subject: [PATCH 47/54] Move file to correct location and add ABOUT files Signed-off-by: Philippe Ombredanne --- .../setup_py_live_eval.py | 0 .../setup_py_live_eval.py.ABOUT | 0 .../setup_py_live_eval.py.LICENSE | 0 ..._builder.py => test_setup_py_live_eval.py} | 0 tests/test_setup_py_live_eval.py.ABOUT | 8 +++++ tests/test_setup_py_live_eval.py.LICENSE | 34 +++++++++++++++++++ ..._cli.py => test_setup_py_live_eval_cli.py} | 0 tests/test_setup_py_live_eval_cli.py.ABOUT | 8 +++++ tests/test_setup_py_live_eval_cli.py.LICENSE | 34 +++++++++++++++++++ 9 files changed, 84 insertions(+) rename src/{package_inspector => python_inspector}/setup_py_live_eval.py (100%) rename src/{package_inspector => python_inspector}/setup_py_live_eval.py.ABOUT (100%) rename src/{package_inspector => python_inspector}/setup_py_live_eval.py.LICENSE (100%) rename tests/{test_requirements_builder.py => test_setup_py_live_eval.py} (100%) create mode 100644 tests/test_setup_py_live_eval.py.ABOUT create mode 100644 tests/test_setup_py_live_eval.py.LICENSE rename tests/{test_reqbuilder_cli.py => test_setup_py_live_eval_cli.py} (100%) create mode 100644 tests/test_setup_py_live_eval_cli.py.ABOUT create mode 100644 tests/test_setup_py_live_eval_cli.py.LICENSE diff --git a/src/package_inspector/setup_py_live_eval.py b/src/python_inspector/setup_py_live_eval.py similarity index 100% rename from src/package_inspector/setup_py_live_eval.py rename to src/python_inspector/setup_py_live_eval.py diff --git a/src/package_inspector/setup_py_live_eval.py.ABOUT b/src/python_inspector/setup_py_live_eval.py.ABOUT similarity index 100% rename from src/package_inspector/setup_py_live_eval.py.ABOUT rename to src/python_inspector/setup_py_live_eval.py.ABOUT diff --git a/src/package_inspector/setup_py_live_eval.py.LICENSE b/src/python_inspector/setup_py_live_eval.py.LICENSE similarity index 100% rename from src/package_inspector/setup_py_live_eval.py.LICENSE rename to src/python_inspector/setup_py_live_eval.py.LICENSE diff --git a/tests/test_requirements_builder.py b/tests/test_setup_py_live_eval.py similarity index 100% rename from tests/test_requirements_builder.py rename to tests/test_setup_py_live_eval.py diff --git a/tests/test_setup_py_live_eval.py.ABOUT b/tests/test_setup_py_live_eval.py.ABOUT new file mode 100644 index 00000000..01b38c1a --- /dev/null +++ b/tests/test_setup_py_live_eval.py.ABOUT @@ -0,0 +1,8 @@ +name: requirements-builder +version: 597340d1e84138af64786d45e74fc9f03315bf2d +copyright: Copyright (C) CERN. +homepage_url: https://github.com/inveniosoftware/requirements-builder/ +description: Build requirements files from setup.py requirements. +license_expression: bsd-new +license_file: requirements_builder.LICENSE +notes: this is a subset of requirements-builder that has been heavily modified. diff --git a/tests/test_setup_py_live_eval.py.LICENSE b/tests/test_setup_py_live_eval.py.LICENSE new file mode 100644 index 00000000..b4896967 --- /dev/null +++ b/tests/test_setup_py_live_eval.py.LICENSE @@ -0,0 +1,34 @@ +Requirements-Builder is free software; you can redistribute it and/or +modify it under the terms of the Revised BSD License; see LICENSE +file for more details. + +Copyright (C) 2015, CERN +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/tests/test_reqbuilder_cli.py b/tests/test_setup_py_live_eval_cli.py similarity index 100% rename from tests/test_reqbuilder_cli.py rename to tests/test_setup_py_live_eval_cli.py diff --git a/tests/test_setup_py_live_eval_cli.py.ABOUT b/tests/test_setup_py_live_eval_cli.py.ABOUT new file mode 100644 index 00000000..01b38c1a --- /dev/null +++ b/tests/test_setup_py_live_eval_cli.py.ABOUT @@ -0,0 +1,8 @@ +name: requirements-builder +version: 597340d1e84138af64786d45e74fc9f03315bf2d +copyright: Copyright (C) CERN. +homepage_url: https://github.com/inveniosoftware/requirements-builder/ +description: Build requirements files from setup.py requirements. +license_expression: bsd-new +license_file: requirements_builder.LICENSE +notes: this is a subset of requirements-builder that has been heavily modified. diff --git a/tests/test_setup_py_live_eval_cli.py.LICENSE b/tests/test_setup_py_live_eval_cli.py.LICENSE new file mode 100644 index 00000000..b4896967 --- /dev/null +++ b/tests/test_setup_py_live_eval_cli.py.LICENSE @@ -0,0 +1,34 @@ +Requirements-Builder is free software; you can redistribute it and/or +modify it under the terms of the Revised BSD License; see LICENSE +file for more details. + +Copyright (C) 2015, CERN +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. From 1ba76018a94dc7ac6316da7162f9d15e2183516d Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 1 Sep 2022 18:46:38 +0530 Subject: [PATCH 48/54] Add tests for live_eval Signed-off-by: Tushar Goel --- requirements_builder.ABOUT | 1 + src/python_inspector/resolution.py | 129 +-- src/python_inspector/setup_py_live_eval.py | 131 ++- tests/data/{ => insecure-setup-2}/setup.py | 21 +- .../insecure-setup-2/setup.py-expected.json | 774 ++++++++++++++++++ .../data/insecure-setup-2/testpkh/__init__.py | 2 + tests/data/setup_if_main.py | 6 +- tests/data/testpkh/__init__.py | 2 - tests/fixtures/setup.txt | 13 +- tests/test_cli.py | 504 ++++++++++++ tests/test_resolution.py | 12 + tests/test_setup_py_live_eval.py | 51 +- 12 files changed, 1438 insertions(+), 208 deletions(-) rename tests/data/{ => insecure-setup-2}/setup.py (62%) create mode 100644 tests/data/insecure-setup-2/setup.py-expected.json create mode 100644 tests/data/insecure-setup-2/testpkh/__init__.py delete mode 100644 tests/data/testpkh/__init__.py create mode 100644 tests/test_cli.py diff --git a/requirements_builder.ABOUT b/requirements_builder.ABOUT index 2be4c51c..926b14fe 100644 --- a/requirements_builder.ABOUT +++ b/requirements_builder.ABOUT @@ -68,6 +68,7 @@ install_requires = saneyaml >= 0.5.2 tinynetrc >= 1.3.1 toml >= 0.10.0 + mock >= 3.0.5 [options.packages.find] where = src diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index 72b341bf..2b5c547d 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -26,7 +26,6 @@ from packaging.version import LegacyVersion from packaging.version import Version from packaging.version import parse as parse_version -from requirements_builder.requirements_builder import iter_requirements from resolvelib import AbstractProvider from resolvelib import Resolver from resolvelib.reporters import BaseReporter @@ -40,6 +39,7 @@ from _packagedcode.pypi import SetupCfgHandler from _packagedcode.pypi import can_process_dependent_package from python_inspector import utils_pypi +from python_inspector.setup_py_live_eval import iter_requirements from python_inspector.utils_pypi import Environment from python_inspector.utils_pypi import PypiSimpleRepository @@ -97,6 +97,8 @@ def get_requirements_from_distribution( Return a list of requirements from a source distribution or wheel at ``location`` using the provided ``handler`` DatafileHandler for parsing. """ + if not location: + return [] if not os.path.exists(location): return [] deps = [] @@ -137,9 +139,8 @@ def parse_setup_py_insecurely(setup_py): """ if not os.path.exists(setup_py): return [] - unparsed_requirements = iter_requirements(level="", extras=[], setup_file=setup_py) - for requirement in unparsed_requirements: - yield Requirement(requirement) + for req in iter_requirements(level="", extras=[], setup_file=setup_py): + yield Requirement(req) def is_valid_version( @@ -377,61 +378,7 @@ def get_requirements_for_package_from_pypi_simple( ) if not sdist_location: return - setup_py_location = os.path.join( - sdist_location, - "setup.py", - ) - setup_cfg_location = os.path.join( - sdist_location, - "setup.cfg", - ) - - if not os.path.exists(setup_py_location) and not os.path.exists(setup_cfg_location): - raise Exception(f"No setup.py or setup.cfg found in pypi sdist {sdist_location}") - - # Some commonon packages like flask may have some dependencies in setup.cfg - # and some dependencies in setup.py. We are going to check both. - location_by_sdist_parser = { - PythonSetupPyHandler: setup_py_location, - SetupCfgHandler: setup_cfg_location, - } - - # Set to True if we found any dependencies in setup.py or setup.cfg - has_deps = False - - for handler, location in location_by_sdist_parser.items(): - deps = get_requirements_from_distribution( - handler=handler, - location=location, - ) - if deps: - has_deps = True - yield from deps - - if not has_deps and contain_string( - string="requirements.txt", files=[setup_py_location, setup_cfg_location] - ): - # Look in requirements file if and only if thy are refered in setup.py or setup.cfg - # And no deps have been yielded by requirements file. - requirement_location = os.path.join( - sdist_location, - "requirements.txt", - ) - deps = get_requirements_from_distribution( - handler=PipRequirementsFileHandler, - location=requirement_location, - ) - if deps: - has_deps = True - yield from deps - - if not has_deps and contain_string( - string="_require", files=[setup_py_location, setup_cfg_location] - ): - if self.insecure: - yield from parse_setup_py_insecurely(setup_py=setup_py_location) - else: - raise Exception("Unable to collect setup.py dependencies securely") + yield from get_setup_dependencies(location=sdist_location, insecure=self.insecure) def get_requirements_for_package_from_pypi_json_api( self, purl: PackageURL @@ -731,6 +678,70 @@ def get_package_list(results): return list(sorted(packages)) +def get_setup_dependencies(location, insecure=False, use_requirements=True): + """ + Yield dependencies from the given setup.py and setup.cfg location. + """ + + setup_py_location = os.path.join( + location, + "setup.py", + ) + setup_cfg_location = os.path.join( + location, + "setup.cfg", + ) + + if not os.path.exists(setup_py_location) and not os.path.exists(setup_cfg_location): + raise Exception(f"No setup.py or setup.cfg found in pypi sdist {location}") + + # Some commonon packages like flask may have some dependencies in setup.cfg + # and some dependencies in setup.py. We are going to check both. + location_by_sdist_parser = { + PythonSetupPyHandler: setup_py_location, + SetupCfgHandler: setup_cfg_location, + } + + # Set to True if we found any dependencies in setup.py or setup.cfg + has_deps = False + + for handler, location in location_by_sdist_parser.items(): + deps = get_requirements_from_distribution( + handler=handler, + location=location, + ) + if deps: + has_deps = True + yield from deps + + if ( + use_requirements + and not has_deps + and contain_string(string="requirements.txt", files=[setup_py_location, setup_cfg_location]) + ): + # Look in requirements file if and only if thy are refered in setup.py or setup.cfg + # And no deps have been yielded by requirements file. + requirement_location = os.path.join( + location, + "requirements.txt", + ) + deps = get_requirements_from_distribution( + handler=PipRequirementsFileHandler, + location=requirement_location, + ) + if deps: + has_deps = True + yield from deps + + if not has_deps and contain_string( + string="_require", files=[setup_py_location, setup_cfg_location] + ): + if insecure: + yield from parse_setup_py_insecurely(setup_py=setup_py_location) + else: + raise Exception("Unable to collect setup.py dependencies securely") + + def get_resolved_dependencies( requirements: List[Requirement], environment: Environment = None, diff --git a/src/python_inspector/setup_py_live_eval.py b/src/python_inspector/setup_py_live_eval.py index f260f516..340aa04a 100755 --- a/src/python_inspector/setup_py_live_eval.py +++ b/src/python_inspector/setup_py_live_eval.py @@ -9,7 +9,8 @@ # """Generate requirements from `setup.py` and `requirements-devel.txt`.""" -from __future__ import absolute_import, print_function +from __future__ import absolute_import +from __future__ import print_function import os import re @@ -37,9 +38,8 @@ def parse_set(string): def minver_error(pkg_name): """Report error about missing minimum version constraint and exit.""" print( - 'ERROR: specify minimal version of "{0}" using ' - '">=" or "=="'.format(pkg_name), - file=sys.stderr + 'ERROR: specify minimal version of "{0}" using ' '">=" or "=="'.format(pkg_name), + file=sys.stderr, ) sys.exit(1) @@ -47,8 +47,7 @@ def minver_error(pkg_name): def build_pkg_name(pkg): """Build package name, including extras if present.""" if pkg.extras: - return '{0}[{1}]'.format( - pkg.project_name, ','.join(sorted(pkg.extras))) + return "{0}[{1}]".format(pkg.project_name, ",".join(sorted(pkg.extras))) return pkg.project_name @@ -66,14 +65,14 @@ def parse_pip_file(path): line = line.strip() # see https://pip.readthedocs.io/en/1.1/requirements.html - if line.startswith('-e'): + if line.startswith("-e"): # devel requirement - splitted = line.split('#egg=') + splitted = line.split("#egg=") rdev[splitted[1].lower()] = line - elif line.startswith('-r'): + elif line.startswith("-r"): # recursive file command - splitted = re.split('-r\\s+', line) + splitted = re.split("-r\\s+", line) subrdev, subrnormal, substuff = parse_pip_file( os.path.join(os.path.dirname(path), splitted[1]) ) @@ -81,73 +80,54 @@ def parse_pip_file(path): if k not in rdev: rdev[k] = v rnormal.extend(subrnormal) - elif line.startswith('-'): + elif line.startswith("-"): # another special command we don't recognize stuff.append(line) else: # ordinary requirement, similarly to them used in setup.py rnormal.append(line) except IOError: - print( - 'Warning: could not parse requirements file "{0}"!'.format(path), - file=sys.stderr - ) + print('Warning: could not parse requirements file "{0}"!'.format(path), file=sys.stderr) return rdev, rnormal, stuff -def iter_requirements(level, extras, pip_file, setup_fp, setup_cfg_fp=None): +def iter_requirements(level, extras, setup_file): """Iterate over requirements.""" + from pathlib import Path + + setup_file = str(Path(setup_file).absolute()) result = dict() requires = [] stuff = [] - if level == 'dev' or setup_fp is None: - result, requires, stuff = parse_pip_file(pip_file) - + cd = os.getcwd() + os.chdir(os.path.dirname(setup_file)) install_requires = [] requires_extras = {} - if setup_fp is not None: - with mock.patch.object(setuptools, 'setup') as mock_setup: - sys.path.append(os.path.dirname(setup_fp.name)) - g = {'__file__': setup_fp.name, '__name__': '__main__'} - exec(setup_fp.read(), g) - sys.path.pop() - assert g['setup'] # silence warning about unused imports - - # called arguments are in `mock_setup.call_args` - mock_args, mock_kwargs = mock_setup.call_args - install_requires = mock_kwargs.get( - 'install_requires', install_requires - ) - requires_extras = mock_kwargs.get('extras_require', requires_extras) - - if setup_cfg_fp is not None: - parser = configparser.ConfigParser() - parser.read_file(setup_cfg_fp) - - if parser.has_section("options"): - value = parser.get("options", "install_requires", - fallback="").strip() - - if value: - install_requires = [s.strip() for s in value.splitlines()] - - if parser.has_section("options.extras_require"): - for name, value in parser.items("options.extras_require"): - requires_extras[name] = [s.strip() - for s in value.strip().splitlines()] - - install_requires.extend(requires) + # change directory to setup.py path + with mock.patch.object(setuptools, "setup") as mock_setup: + sys.path.append(os.path.dirname(setup_file)) + g = {"__file__": setup_file, "__name__": "__main__"} + with open(setup_file) as sf: + exec(sf.read(), g) + sys.path.pop() + assert g["setup"] # silence warning about unused imports + # called arguments are in `mock_setup.call_args` + os.chdir(cd) + mock_args, mock_kwargs = mock_setup.call_args + install_requires = mock_kwargs.get("install_requires", install_requires) + + requires_extras = mock_kwargs.get("extras_require", requires_extras) for e, reqs in requires_extras.items(): # Handle conditions on extras. See pkginfo_to_metadata function # in Wheel for details. - condition = '' - if ':' in e: - e, condition = e.split(':', 1) + condition = "" + if ":" in e: + e, condition = e.split(":", 1) if not e or e in extras: if condition: - reqs = ['{0}; {1}'.format(r, condition) for r in reqs] + reqs = ["{0}; {1}".format(r, condition) for r in reqs] install_requires.extend(reqs) for pkg in pkg_resources.parse_requirements(install_requires): @@ -155,7 +135,7 @@ def iter_requirements(level, extras, pip_file, setup_fp, setup_cfg_fp=None): # FIXME be smarter about merging things # Evaluate environment markers skip if not applicable - if hasattr(pkg, 'marker') and pkg.marker is not None: + if hasattr(pkg, "marker") and pkg.marker is not None: if not pkg.marker.evaluate(): continue else: @@ -166,44 +146,37 @@ def iter_requirements(level, extras, pip_file, setup_fp, setup_cfg_fp=None): continue specs = dict(pkg.specs) - if (('>=' in specs) and ('>' in specs)) \ - or (('<=' in specs) and ('<' in specs)): + if ((">=" in specs) and (">" in specs)) or (("<=" in specs) and ("<" in specs)): print( - 'ERROR: Do not specify such weird constraints! ' - '("{0}")'.format(pkg), - file=sys.stderr + "ERROR: Do not specify such weird constraints! " '("{0}")'.format(pkg), + file=sys.stderr, ) sys.exit(1) - if '==' in specs: - result[pkg.key] = '{0}=={1}'.format( - build_pkg_name(pkg), specs['==']) + if "==" in specs: + result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs["=="]) - elif '>=' in specs: - if level == 'min': - result[pkg.key] = '{0}=={1}'.format( - build_pkg_name(pkg), specs['>='] - ) + elif ">=" in specs: + if level == "min": + result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs[">="]) else: result[pkg.key] = pkg - elif '>' in specs: - if level == 'min': + elif ">" in specs: + if level == "min": minver_error(build_pkg_name(pkg)) else: result[pkg.key] = pkg - elif '~=' in specs: - if level == 'min': - result[pkg.key] = '{0}=={1}'.format( - build_pkg_name(pkg), specs['~=']) + elif "~=" in specs: + if level == "min": + result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs["~="]) else: - ver, _ = os.path.splitext(specs['~=']) - result[pkg.key] = '{0}>={1},=={2}.*'.format( - build_pkg_name(pkg), specs['~='], ver) + ver, _ = os.path.splitext(specs["~="]) + result[pkg.key] = "{0}>={1},=={2}.*".format(build_pkg_name(pkg), specs["~="], ver) else: - if level == 'min': + if level == "min": minver_error(build_pkg_name(pkg)) else: result[pkg.key] = build_pkg_name(pkg) diff --git a/tests/data/setup.py b/tests/data/insecure-setup-2/setup.py similarity index 62% rename from tests/data/setup.py rename to tests/data/insecure-setup-2/setup.py index 31bb2b53..d418135c 100644 --- a/tests/data/setup.py +++ b/tests/data/insecure-setup-2/setup.py @@ -17,23 +17,22 @@ dirname = os.path.dirname(__file__) requirements = [ - 'click>=5.0.0', - 'mock>=1.3.0', - 'CairoSVG<2.0.0,>=1.0.20', - 'functools32>=3.2.3-2; python_version=="2.7"', - 'invenio-records~=1.0.0', - 'invenio[base,auth,metadata]>=3.0.0', + "click>=5.0.0", + "mock>=1.3.0", + "CairoSVG<2.0.0,>=1.0.20", + "invenio-records~=1.0.0", + "invenio[base,auth,metadata]>=3.0.0", ] extras_require = { - 'docs': ['Sphinx>=1.4.2'], - 'tests': ['pytest>=2.7'], - 'flask': ['Flask>=0.11'], - ':python_version=="2.7"': ['ipaddr>=2.1.11'] + "docs": ["Sphinx>=1.4.2"], + "tests": ["pytest>=2.7"], + "flask": ["Flask>=0.11"], + ':python_version=="2.7"': ["ipaddr>=2.1.11"], } setup( - name='testpkh', + name="testpkh", version=testpkh.__version__, install_requires=requirements, extras_require=extras_require, diff --git a/tests/data/insecure-setup-2/setup.py-expected.json b/tests/data/insecure-setup-2/setup.py-expected.json new file mode 100644 index 00000000..88519eeb --- /dev/null +++ b/tests/data/insecure-setup-2/setup.py-expected.json @@ -0,0 +1,774 @@ +{ + "headers": { + "tool_name": "python-inspector", + "tool_homepageurl": "https://github.com/nexB/python-inspector", + "tool_version": "0.6.5", + "options": [ + "--index-url https://pypi.org/simple", + "--python-version 27", + "--operating-system linux", + "--json " + ], + "notice": "Dependency tree generated with python-inspector.\npython-inspector is a free software tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", + "warnings": [], + "errors": [] + }, + "requirements": [ + { + "purl": "pkg:pypi/click", + "extracted_requirement": "click>=5.0.0", + "scope": "install", + "is_runtime": true, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:pypi/mock", + "extracted_requirement": "mock>=1.3.0", + "scope": "install", + "is_runtime": true, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:pypi/cairosvg", + "extracted_requirement": "CairoSVG<2.0.0,>=1.0.20", + "scope": "install", + "is_runtime": true, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:pypi/invenio-records", + "extracted_requirement": "invenio-records~=1.0.0", + "scope": "install", + "is_runtime": true, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:pypi/invenio", + "extracted_requirement": "invenio[auth,base,metadata]>=3.0.0", + "scope": "install", + "is_runtime": true, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + } + ], + "resolved_dependencies": [ + { + "package": "pkg:pypi/amqp@2.6.1", + "dependencies": [ + "pkg:pypi/vine@1.3.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/bc/90/bb5ce93521772f083cb2d7a413bb82eda5afc62b4192adb7ea4c7b4858b9/amqp-2.6.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/37/9f/d54494a157d0dcd1673fe7a1bcce7ac70d3eb6d5d6149749450c87a2c959/amqp-2.6.1.tar.gz" + }, + { + "package": "pkg:pypi/attrs@21.4.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/be/be/7abce643bfdf8ca01c48afa2ddf8308c2308b0c3b239a44e57d020afa0ef/attrs-21.4.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/d7/77/ebb15fc26d0f815839ecd897b919ed6d85c050feeb83e100e020df9153d2/attrs-21.4.0.tar.gz" + }, + { + "package": "pkg:pypi/babel@2.9.1", + "dependencies": [ + "pkg:pypi/pytz@2022.2.1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/aa/96/4ba93c5f40459dc850d25f9ba93f869a623e77aaecc7a9344e19c01942cf/Babel-2.9.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/17/e6/ec9aa6ac3d00c383a5731cc97ed7c619d3996232c977bb8326bcbb6c687e/Babel-2.9.1.tar.gz" + }, + { + "package": "pkg:pypi/backports-functools-lru-cache@1.6.4", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/e5/c1/1a48a4bb9b515480d6c666977eeca9243be9fa9e6fb5a34be0ad9627f737/backports.functools_lru_cache-1.6.4-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/95/9f/122a41912932c77d5b8e6cab6bd456e6270211a3ed7248a80c235179a012/backports.functools_lru_cache-1.6.4.tar.gz" + }, + { + "package": "pkg:pypi/backports-shutil-get-terminal-size@1.0.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/7d/cd/1750d6c35fe86d35f8562091737907f234b78fdffab42b29c72b1dd861f4/backports.shutil_get_terminal_size-1.0.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz" + }, + { + "package": "pkg:pypi/billiard@3.6.4.0", + "dependencies": [], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/92/91/40de1901da8ec9eeb7c6a22143ba5d55d8aaa790761ca31342cedcd5c793/billiard-3.6.4.0.tar.gz" + }, + { + "package": "pkg:pypi/blinker@1.5", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/30/41/caa5da2dbe6d26029dfe11d31dfa8132b4d6d30b6d6b61a24824075a5f06/blinker-1.5-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/2b/12/82786486cefb68685bb1c151730f510b0f4e5d621d77f245bc0daf9a6c64/blinker-1.5.tar.gz" + }, + { + "package": "pkg:pypi/cairocffi@0.9.0", + "dependencies": [ + "pkg:pypi/cffi@1.15.1" + ], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/62/be/ad4d422b6f38d99b09ad6d046ab725e8ccac5fefd9ca256ca35a80dbf3c6/cairocffi-0.9.0.tar.gz" + }, + { + "package": "pkg:pypi/cairosvg@1.0.22", + "dependencies": [ + "pkg:pypi/cairocffi@0.9.0" + ], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/d9/9b/c241990c86faa9e52a01f0570bba4247ba0f3d66eae2607e179cb9ae773a/CairoSVG-1.0.22.tar.gz" + }, + { + "package": "pkg:pypi/celery@4.4.7", + "dependencies": [ + "pkg:pypi/billiard@3.6.4.0", + "pkg:pypi/kombu@4.6.11", + "pkg:pypi/pytz@2022.2.1", + "pkg:pypi/vine@1.3.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/c8/0c/609e3611d20c9f8d883852d1be5516671f630fb08c8c1e56911567dfba7b/celery-4.4.7-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/fe/58/c7ced9705c2cedf526e183e428d1b145910cb8bc7ea537a2ec9a6552c056/celery-4.4.7.tar.gz" + }, + { + "package": "pkg:pypi/cffi@1.15.1", + "dependencies": [ + "pkg:pypi/pycparser@2.21" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/1d/76/bcebbbab689f5f6fc8a91e361038a3001ee2e48c5f9dbad0a3b64a64cc9e/cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/2b/a8/050ab4f0c3d4c1b8aaa805f70e26e84d0e27004907c5b8ecc1d31815f92a/cffi-1.15.1.tar.gz" + }, + { + "package": "pkg:pypi/click@7.1.2", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/d2/3d/fa76db83bf75c4f8d338c2fd15c8d33fdd7ad23a9b5e57eb6c5de26b430e/click-7.1.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz" + }, + { + "package": "pkg:pypi/configparser@4.0.2", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/7a/2a/95ed0501cf5d8709490b1d3a3f9b5cf340da6c433f896bbe9ce08dbe6785/configparser-4.0.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/16/4f/48975536bd488d3a272549eb795ac4a13a5f7fcdc8995def77fbef3532ee/configparser-4.0.2.tar.gz" + }, + { + "package": "pkg:pypi/contextlib2@0.6.0.post1", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/85/60/370352f7ef6aa96c52fb001831622f50f923c1d575427d021b8ab3311236/contextlib2-0.6.0.post1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/02/54/669207eb72e3d8ae8b38aa1f0703ee87a0e9f88f30d3c0a47bebdb6de242/contextlib2-0.6.0.post1.tar.gz" + }, + { + "package": "pkg:pypi/decorator@4.4.2", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/ed/1b/72a1821152d07cf1d8b6fce298aeb06a7eb90f4d6d41acec9861e7cc6df0/decorator-4.4.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/da/93/84fa12f2dc341f8cf5f022ee09e109961055749df2d0c75c5f98746cfe6c/decorator-4.4.2.tar.gz" + }, + { + "package": "pkg:pypi/enum34@1.1.10", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/6f/2c/a9386903ece2ea85e9807e0e062174dc26fdce8b05f216d00491be29fad5/enum34-1.1.10-py2-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz" + }, + { + "package": "pkg:pypi/flask-babelex@0.9.4", + "dependencies": [ + "pkg:pypi/babel@2.9.1", + "pkg:pypi/flask@1.1.4", + "pkg:pypi/jinja2@2.11.3", + "pkg:pypi/speaklater@1.3" + ], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/85/e7/217fb37ccd4bd93cd0f002028fb7c5fdf6ee0063a6beb83e43cd903da46e/Flask-BabelEx-0.9.4.tar.gz" + }, + { + "package": "pkg:pypi/flask-caching@1.9.0", + "dependencies": [ + "pkg:pypi/flask@1.1.4" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/d1/9f/135bcf47fdb585dffcf9f918664ab9d63585aae8e722948b2abca041312f/Flask_Caching-1.9.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/41/c9/472486c62f22a1dad273a132b9484189e1a22eb8358883249e4955f8e464/Flask-Caching-1.9.0.tar.gz" + }, + { + "package": "pkg:pypi/flask-celeryext@0.3.4", + "dependencies": [ + "pkg:pypi/celery@4.4.7", + "pkg:pypi/flask@1.1.4" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/70/a0/74c30a11f96be5ad3bf5609b00fe91755b9eee2ce6cc0bba59c32614afe8/Flask_CeleryExt-0.3.4-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/3f/67/a048930d1c6349b7cc038738140a258c443c5b8f83043311972a53364833/Flask-CeleryExt-0.3.4.tar.gz" + }, + { + "package": "pkg:pypi/flask-limiter@1.1.0", + "dependencies": [ + "pkg:pypi/flask@1.1.4", + "pkg:pypi/limits@1.6", + "pkg:pypi/six@1.16.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/72/f3/68596cb061e1c7d5a7dfb3694de3f8845b908ea16296e762136f34727a65/Flask_Limiter-1.1.0-py2-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/96/a6/35fe99ef33b44ae33c212da20e8f545354f58cb0c77f8b6cdcfda9f5e9ad/Flask-Limiter-1.1.0.tar.gz" + }, + { + "package": "pkg:pypi/flask-shell-ipython@0.4.1", + "dependencies": [ + "pkg:pypi/click@7.1.2", + "pkg:pypi/flask@1.1.4", + "pkg:pypi/ipython@5.10.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/10/6b/a45278cbff711cc7b8904bd38679a8cc249c4db825a76ae332302f59d398/flask_shell_ipython-0.4.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/48/8e/ba861448b7590282519aea82ae39107c77d001fdc37b7af4c14ed0e0db77/flask-shell-ipython-0.4.1.tar.gz" + }, + { + "package": "pkg:pypi/flask-talisman@0.8.1", + "dependencies": [ + "pkg:pypi/six@1.16.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/0a/f1/3c2a37a8053149521407bff4573cecca93d86b1f15a027e8cc4463da6261/flask_talisman-0.8.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/dd/1a/9f21ccb72a0d09594eb704da7f87a6d373ad7e9d4ac18693d1a3c275afb2/flask-talisman-0.8.1.tar.gz" + }, + { + "package": "pkg:pypi/flask@1.1.4", + "dependencies": [ + "pkg:pypi/click@7.1.2", + "pkg:pypi/itsdangerous@1.1.0", + "pkg:pypi/jinja2@2.11.3", + "pkg:pypi/werkzeug@1.0.1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/e8/6d/994208daa354f68fd89a34a8bafbeaab26fda84e7af1e35bdaed02b667e6/Flask-1.1.4-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/4d/5b/2d145f5fe718b2f15ebe69240538f06faa8bbb76488bf962091db1f7a26d/Flask-1.1.4.tar.gz" + }, + { + "package": "pkg:pypi/funcsigs@1.0.2", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz" + }, + { + "package": "pkg:pypi/importlib-metadata@2.1.3", + "dependencies": [ + "pkg:pypi/configparser@4.0.2", + "pkg:pypi/contextlib2@0.6.0.post1", + "pkg:pypi/pathlib2@2.3.7.post1", + "pkg:pypi/zipp@1.2.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/cf/b4/877779cd7b5a15536ecbe0655cfb35a0de0ede6d888151fd7356d278c47d/importlib_metadata-2.1.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/4c/d8/af92ca59b33366b3b9d230d17772d728d7b064f3de6d6150332d5fa8dae3/importlib_metadata-2.1.3.tar.gz" + }, + { + "package": "pkg:pypi/invenio%5bauth%2cbase%2cmetadata%5d@3.4.1", + "dependencies": [ + "pkg:pypi/invenio-app@1.3.3", + "pkg:pypi/invenio-base@1.2.5", + "pkg:pypi/invenio-cache@1.1.0", + "pkg:pypi/invenio-celery@1.2.2", + "pkg:pypi/invenio-config@1.0.3", + "pkg:pypi/invenio-i18n@1.3.1", + "pkg:pypi/invenio@3.4.1" + ], + "wheel_urls": [], + "sdist_url": null + }, + { + "package": "pkg:pypi/invenio-app@1.3.3", + "dependencies": [ + "pkg:pypi/flask-celeryext@0.3.4", + "pkg:pypi/flask-limiter@1.1.0", + "pkg:pypi/flask-shell-ipython@0.4.1", + "pkg:pypi/flask-talisman@0.8.1", + "pkg:pypi/invenio-base@1.2.5", + "pkg:pypi/invenio-cache@1.1.0", + "pkg:pypi/invenio-config@1.0.3", + "pkg:pypi/limits@1.6", + "pkg:pypi/uritools@2.2.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/25/ee/bfe28143e98e24a27ca8d6c4a464591e8006766a60fe8468a9b2061468e2/invenio_app-1.3.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/ef/e6/f7e3e2c79520a0046008aa0cb5381734f681d040793e5b8d1b995deaca5d/invenio-app-1.3.3.tar.gz" + }, + { + "package": "pkg:pypi/invenio-base@1.2.5", + "dependencies": [ + "pkg:pypi/blinker@1.5", + "pkg:pypi/flask@1.1.4", + "pkg:pypi/six@1.16.0", + "pkg:pypi/werkzeug@1.0.1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/47/7c/89fe1cd7263c8d67a366c568dcc6f5050518f06ca7695f7b719f3f340229/invenio_base-1.2.5-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/fd/3b/135f8f839f696ec9f163083625b5111310108fd86b0a9668f7da4a0b6f92/invenio-base-1.2.5.tar.gz" + }, + { + "package": "pkg:pypi/invenio-cache@1.1.0", + "dependencies": [ + "pkg:pypi/flask-caching@1.9.0", + "pkg:pypi/invenio-base@1.2.5" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/04/f0/22b7426ac2b821d93bc6066410fac652c924330bfbd5b64a82e1e36b0ed6/invenio_cache-1.1.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/4f/f8/aee6e433a4b94882b8089cf5ff17ab1612a6ecc9231b3103cc4e6a533c98/invenio-cache-1.1.0.tar.gz" + }, + { + "package": "pkg:pypi/invenio-celery@1.2.2", + "dependencies": [ + "pkg:pypi/celery@4.4.7", + "pkg:pypi/flask-celeryext@0.3.4", + "pkg:pypi/invenio-base@1.2.5", + "pkg:pypi/msgpack@1.0.4", + "pkg:pypi/redis@3.5.3" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/b8/a7/ec89de1fd4615aebc0e59c25ab8d8152bb04f03fa21b1c50bdd08239e79e/invenio_celery-1.2.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/67/db/7d7dffe8f620697461a4f680505c1d04904ca5b6805c10fd9a9677b5e0b6/invenio-celery-1.2.2.tar.gz" + }, + { + "package": "pkg:pypi/invenio-config@1.0.3", + "dependencies": [ + "pkg:pypi/flask@1.1.4" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/5a/67/c751f8b8cf6ec5c4415bccaeed7f6b7eab836178ca4faa9f4d7088616735/invenio_config-1.0.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/cd/8e/36d33ec84f29bbeed5593ea8e89b421e0cbcc6fafd0d8c300d187f4a6d9d/invenio-config-1.0.3.tar.gz" + }, + { + "package": "pkg:pypi/invenio-i18n@1.3.1", + "dependencies": [ + "pkg:pypi/flask-babelex@0.9.4", + "pkg:pypi/invenio-base@1.2.5" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/81/56/4398e45ba27c8436e38b8027881d72d1e02ab199d0eb69cb425759173ec4/invenio_i18n-1.3.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/06/70/b65b7ffef1daf8c864329af034c06a56ce8c889d887261553aad75ecd3dc/invenio-i18n-1.3.1.tar.gz" + }, + { + "package": "pkg:pypi/invenio-records@1.0.2", + "dependencies": [ + "pkg:pypi/blinker@1.5", + "pkg:pypi/flask-celeryext@0.3.4", + "pkg:pypi/flask@1.1.4", + "pkg:pypi/jsonpatch@1.32", + "pkg:pypi/jsonref@0.2", + "pkg:pypi/jsonresolver@0.3.1", + "pkg:pypi/jsonschema@4.0.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/50/97/fd03b1bbc220643230bf9e1f621d4043d583043d0cf5506a8ccb0551029c/invenio_records-1.0.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/88/8a/b4bd1e9b493fc119d972ac89920c32fa4573205e8fa2e893e53effc51f1c/invenio-records-1.0.2.tar.gz" + }, + { + "package": "pkg:pypi/invenio@3.4.1", + "dependencies": [ + "pkg:pypi/invenio-app@1.3.3", + "pkg:pypi/invenio-base@1.2.5", + "pkg:pypi/invenio-cache@1.1.0", + "pkg:pypi/invenio-celery@1.2.2", + "pkg:pypi/invenio-config@1.0.3", + "pkg:pypi/invenio-i18n@1.3.1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/31/59/5c20981a3f371b59c0545905a800cc5f3f066cb9a14a126ba6b96a255933/invenio-3.4.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/fe/e9/bf37c72c3e231ba5dafefb134b0f2cb6fbfc26d7a504047d7277097b6bb8/invenio-3.4.1.tar.gz" + }, + { + "package": "pkg:pypi/ipaddress@1.0.23", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/c2/f8/49697181b1651d8347d24c095ce46c7346c37335ddc7d255833e7cde674d/ipaddress-1.0.23-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz" + }, + { + "package": "pkg:pypi/ipython-genutils@0.2.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/fa/bc/9bd3b5c2b4774d5f33b2d544f1460be9df7df2fe42f352135381c347c69a/ipython_genutils-0.2.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz" + }, + { + "package": "pkg:pypi/ipython@5.10.0", + "dependencies": [ + "pkg:pypi/backports-shutil-get-terminal-size@1.0.0", + "pkg:pypi/decorator@4.4.2", + "pkg:pypi/pathlib2@2.3.7.post1", + "pkg:pypi/pexpect@4.8.0", + "pkg:pypi/pickleshare@0.7.5", + "pkg:pypi/prompt-toolkit@1.0.18", + "pkg:pypi/pygments@2.5.2", + "pkg:pypi/setuptools@44.1.1", + "pkg:pypi/simplegeneric@0.8.1", + "pkg:pypi/traitlets@4.3.3" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/ce/2c/2849a2b37024a01a847c87d81825c0489eb22ffc6416cac009bf281ea838/ipython-5.10.0-py2-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/b6/73/c8f68b3a7d0deece3d2f7ab727fbf262bfca7475330b44043a5503b3aa7a/ipython-5.10.0.tar.gz" + }, + { + "package": "pkg:pypi/itsdangerous@1.1.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz" + }, + { + "package": "pkg:pypi/jinja2@2.11.3", + "dependencies": [ + "pkg:pypi/markupsafe@1.1.1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/7e/c2/1eece8c95ddbc9b1aeb64f5783a9e07a286de42191b7204d67b7496ddf35/Jinja2-2.11.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz" + }, + { + "package": "pkg:pypi/jsonpatch@1.32", + "dependencies": [ + "pkg:pypi/jsonpointer@2.3" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/a3/55/f7c93bae36d869292aedfbcbae8b091386194874f16390d680136edd2b28/jsonpatch-1.32-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/21/67/83452af2a6db7c4596d1e2ecaa841b9a900980103013b867f2865e5e1cf0/jsonpatch-1.32.tar.gz" + }, + { + "package": "pkg:pypi/jsonpointer@2.3", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/a3/be/8dc9d31b50e38172c8020c40f497ce8debdb721545ddb9fcb7cca89ea9e6/jsonpointer-2.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/a0/6c/c52556b957a0f904e7c45585444feef206fe5cb1ff656303a1d6d922a53b/jsonpointer-2.3.tar.gz" + }, + { + "package": "pkg:pypi/jsonref@0.2", + "dependencies": [], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/b3/cf/93d4f34d76863d4fb995cb8e3e4f29908304065ce6381e0349700c44ad0c/jsonref-0.2.tar.gz" + }, + { + "package": "pkg:pypi/jsonresolver@0.3.1", + "dependencies": [ + "pkg:pypi/pluggy@0.13.1", + "pkg:pypi/six@1.16.0", + "pkg:pypi/werkzeug@1.0.1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/0d/8e/e63fba33303e2062eb1fbd38a5483538ad1b5eee73e04ac1d1f632ac2b76/jsonresolver-0.3.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/2c/45/30a9704607f4f868bb44a1f04d133157b8355cc1b99e083e9d7d47df3d24/jsonresolver-0.3.1.tar.gz" + }, + { + "package": "pkg:pypi/jsonschema@4.0.0", + "dependencies": [ + "pkg:pypi/attrs@21.4.0", + "pkg:pypi/importlib-metadata@2.1.3", + "pkg:pypi/pyrsistent@0.16.1" + ], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/9c/99/9789c7fd0bb8876a7d624d903195ce11e5618b421bdb1bf7c975d17a9bc3/jsonschema-4.0.0.tar.gz" + }, + { + "package": "pkg:pypi/kombu@4.6.11", + "dependencies": [ + "pkg:pypi/amqp@2.6.1", + "pkg:pypi/importlib-metadata@2.1.3" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/9e/34/3eea6a3a9ff81b0c7ddbdceb22a1ffc1b5907d863f27ca19a68777d2211d/kombu-4.6.11-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/45/e1/00f2e3f6f5575aa2f7ee41e2fd884ce573f8146e136bde37daf45ef7ca5e/kombu-4.6.11.tar.gz" + }, + { + "package": "pkg:pypi/limits@1.6", + "dependencies": [ + "pkg:pypi/six@1.16.0" + ], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/f8/80/3e208b8cfdcf9bf85006097a53c93f53a0c23bc711f1e6abfc180ebb4bca/limits-1.6.tar.gz" + }, + { + "package": "pkg:pypi/markupsafe@1.1.1", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/d8/1f/e97c4c6b182e59562f99c207f0f621d15a42fc82a6532a98e0b2d38b7c4e/MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz" + }, + { + "package": "pkg:pypi/mock@3.0.5", + "dependencies": [ + "pkg:pypi/funcsigs@1.0.2", + "pkg:pypi/six@1.16.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/05/d2/f94e68be6b17f46d2c353564da56e6fb89ef09faeeff3313a046cb810ca9/mock-3.0.5-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/2e/ab/4fe657d78b270aa6a32f027849513b829b41b0f28d9d8d7f8c3d29ea559a/mock-3.0.5.tar.gz" + }, + { + "package": "pkg:pypi/msgpack@1.0.4", + "dependencies": [], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/22/44/0829b19ac243211d1d2bd759999aa92196c546518b0be91de9cacc98122a/msgpack-1.0.4.tar.gz" + }, + { + "package": "pkg:pypi/pathlib2@2.3.7.post1", + "dependencies": [ + "pkg:pypi/scandir@1.10.0", + "pkg:pypi/six@1.16.0", + "pkg:pypi/typing@3.10.0.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/09/eb/4af4bcd5b8731366b676192675221c5324394a580dfae469d498313b5c4a/pathlib2-2.3.7.post1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/31/51/99caf463dc7c18eb18dad1fffe465a3cf3ee50ac3d1dccbd1781336fe9c7/pathlib2-2.3.7.post1.tar.gz" + }, + { + "package": "pkg:pypi/pexpect@4.8.0", + "dependencies": [ + "pkg:pypi/ptyprocess@0.7.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/39/7b/88dbb785881c28a102619d46423cb853b46dbccc70d3ac362d99773a78ce/pexpect-4.8.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz" + }, + { + "package": "pkg:pypi/pickleshare@0.7.5", + "dependencies": [ + "pkg:pypi/pathlib2@2.3.7.post1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz" + }, + { + "package": "pkg:pypi/pluggy@0.13.1", + "dependencies": [ + "pkg:pypi/importlib-metadata@2.1.3" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz" + }, + { + "package": "pkg:pypi/prompt-toolkit@1.0.18", + "dependencies": [ + "pkg:pypi/six@1.16.0", + "pkg:pypi/wcwidth@0.2.5" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/9d/d2/2f099b5cd62dab819ce7a9f1431c09a9032fbfbb6474f442722e88935376/prompt_toolkit-1.0.18-py2-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/c5/64/c170e5b1913b540bf0c8ab7676b21fdd1d25b65ddeb10025c6ca43cccd4c/prompt_toolkit-1.0.18.tar.gz" + }, + { + "package": "pkg:pypi/ptyprocess@0.7.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz" + }, + { + "package": "pkg:pypi/pycparser@2.21", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz" + }, + { + "package": "pkg:pypi/pygments@2.5.2", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/be/39/32da3184734730c0e4d3fa3b2b5872104668ad6dc1b5a73d8e477e5fe967/Pygments-2.5.2-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/cb/9f/27d4844ac5bf158a33900dbad7985951e2910397998e85712da03ce125f0/Pygments-2.5.2.tar.gz" + }, + { + "package": "pkg:pypi/pyrsistent@0.16.1", + "dependencies": [ + "pkg:pypi/six@1.16.0" + ], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/80/18/1492d651693ef7d40e0a40377ed56a8cc5c5fe86073eb6c56e53513f4480/pyrsistent-0.16.1.tar.gz" + }, + { + "package": "pkg:pypi/pytz@2022.2.1", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/d5/50/54451e88e3da4616286029a3a17fc377de817f66a0f50e1faaee90161724/pytz-2022.2.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/24/0c/401283bb1499768e33ddd2e1a35817c775405c1f047a9dc088a29ce2ea5d/pytz-2022.2.1.tar.gz" + }, + { + "package": "pkg:pypi/redis@3.5.3", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/a7/7c/24fb0511df653cf1a5d938d8f5d19802a88cef255706fdda242ff97e91b7/redis-3.5.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/b3/17/1e567ff78c83854e16b98694411fe6e08c3426af866ad11397cddceb80d3/redis-3.5.3.tar.gz" + }, + { + "package": "pkg:pypi/scandir@1.10.0", + "dependencies": [], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz" + }, + { + "package": "pkg:pypi/setuptools@44.1.1", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/e1/b7/182161210a13158cd3ccc41ee19aadef54496b74f2817cc147006ec932b4/setuptools-44.1.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/b2/40/4e00501c204b457f10fe410da0c97537214b2265247bc9a5bc6edd55b9e4/setuptools-44.1.1.zip" + }, + { + "package": "pkg:pypi/simplegeneric@0.8.1", + "dependencies": [], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip" + }, + { + "package": "pkg:pypi/six@1.16.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz" + }, + { + "package": "pkg:pypi/speaklater@1.3", + "dependencies": [], + "wheel_urls": [], + "sdist_url": "https://files.pythonhosted.org/packages/11/92/5ae1effe0ccb8561c034a0111d53c8788660ddb7ed4992f0da1bb5c525e5/speaklater-1.3.tar.gz" + }, + { + "package": "pkg:pypi/traitlets@4.3.3", + "dependencies": [ + "pkg:pypi/decorator@4.4.2", + "pkg:pypi/enum34@1.1.10", + "pkg:pypi/ipython-genutils@0.2.0", + "pkg:pypi/six@1.16.0" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/ca/ab/872a23e29cec3cf2594af7e857f18b687ad21039c1f9b922fac5b9b142d5/traitlets-4.3.3-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/75/b0/43deb021bc943f18f07cbe3dac1d681626a48997b7ffa1e7fb14ef922b21/traitlets-4.3.3.tar.gz" + }, + { + "package": "pkg:pypi/typing@3.10.0.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/0b/cb/da856e81731833b94da70a08712f658416266a5fb2a9d9e426c8061becef/typing-3.10.0.0-py2-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/b0/1b/835d4431805939d2996f8772aca1d2313a57e8860fec0e48e8e7dfe3a477/typing-3.10.0.0.tar.gz" + }, + { + "package": "pkg:pypi/uritools@2.2.0", + "dependencies": [ + "pkg:pypi/ipaddress@1.0.23" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/8c/5d/ef3cd3c40b4b97f0cb50cee8e4c5a8a4abc30953e1c7ce7e0d25cb2534c3/uritools-2.2.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/ab/1c/e9aa4a907806743298171510042447adc20cd5cf5b95436206a067e14496/uritools-2.2.0.tar.gz" + }, + { + "package": "pkg:pypi/vine@1.3.0", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/7f/60/82c03047396126c8331ceb64da1dc52d4f1317209f32e8fe286d0c07365a/vine-1.3.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/1c/e1/79fb8046e607dd6c2ad05c9b8ebac9d0bd31d086a08f02699e96fc5b3046/vine-1.3.0.tar.gz" + }, + { + "package": "pkg:pypi/wcwidth@0.2.5", + "dependencies": [ + "pkg:pypi/backports-functools-lru-cache@1.6.4" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/59/7c/e39aca596badaf1b78e8f547c807b04dae603a433d3e7a7e04d67f2ef3e5/wcwidth-0.2.5-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/89/38/459b727c381504f361832b9e5ace19966de1a235d73cdbdea91c771a1155/wcwidth-0.2.5.tar.gz" + }, + { + "package": "pkg:pypi/werkzeug@1.0.1", + "dependencies": [], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/cc/94/5f7079a0e00bd6863ef8f1da638721e9da21e5bacee597595b318f71d62e/Werkzeug-1.0.1-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz" + }, + { + "package": "pkg:pypi/zipp@1.2.0", + "dependencies": [ + "pkg:pypi/contextlib2@0.6.0.post1" + ], + "wheel_urls": [ + "https://files.pythonhosted.org/packages/96/0a/67556e9b7782df7118c1f49bdc494da5e5e429c93aa77965f33e81287c8c/zipp-1.2.0-py2.py3-none-any.whl" + ], + "sdist_url": "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz" + } + ] +} \ No newline at end of file diff --git a/tests/data/insecure-setup-2/testpkh/__init__.py b/tests/data/insecure-setup-2/testpkh/__init__.py new file mode 100644 index 00000000..ab39f078 --- /dev/null +++ b/tests/data/insecure-setup-2/testpkh/__init__.py @@ -0,0 +1,2 @@ +"""Version.""" +__version__ = "0.0.1" diff --git a/tests/data/setup_if_main.py b/tests/data/setup_if_main.py index c853c8ec..b9447161 100644 --- a/tests/data/setup_if_main.py +++ b/tests/data/setup_if_main.py @@ -13,16 +13,16 @@ from setuptools import setup requirements = [ - 'click>=5.0.0', + "click>=5.0.0", ] extras_require = { - 'docs': ['Sphinx>=1.4.2'], + "docs": ["Sphinx>=1.4.2"], } if __name__ == "__main__": setup( - name='testpkh', + name="testpkh", version=testpkh.__version__, install_requires=requirements, extras_require=extras_require, diff --git a/tests/data/testpkh/__init__.py b/tests/data/testpkh/__init__.py deleted file mode 100644 index aa079fd6..00000000 --- a/tests/data/testpkh/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Version.""" -__version__ = '0.0.1' diff --git a/tests/fixtures/setup.txt b/tests/fixtures/setup.txt index 2336ef3a..a93a9740 100644 --- a/tests/fixtures/setup.txt +++ b/tests/fixtures/setup.txt @@ -15,15 +15,6 @@ from setuptools import setup # Get the version string. Cannot be done with import! g = {} -with open(os.path.join('requirements_builder', 'version.py'), 'rt') as fp: - exec(fp.read(), g) - version = g['__version__'] - -with open('README.rst') as readme_file: - readme = readme_file.read() - -with open('CHANGES.rst') as history_file: - history = history_file.read().replace('.. :changes:', '') install_requires = [ 'click>=6.1.0', @@ -54,9 +45,9 @@ setup_requires = ['pytest-runner>=2.6.2', ] setup( name='requirements-builder', - version=version, + version="0.1.0", description=__doc__, - long_description=readme + '\n\n' + history, + long_description='\n\n', author="Invenio Collaboration", author_email='info@inveniosoftware.org', url='https://github.com/inveniosoftware/requirements-builder', diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..3c664aac --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,504 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# ScanCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/python-inspector for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import json +import os + +import pytest +from click.testing import CliRunner +from commoncode.testcase import FileDrivenTesting + +from _packagedcode import models +from python_inspector.resolve_cli import get_requirements_from_direct_dependencies +from python_inspector.resolve_cli import resolve_dependencies + +# Used for tests to regenerate fixtures with regen=True +REGEN_TEST_FIXTURES = os.getenv("PYINSP_REGEN_TEST_FIXTURES", False) + +test_env = FileDrivenTesting() +test_env.test_data_dir = os.path.join(os.path.dirname(__file__), "data") +setup_test_env = FileDrivenTesting() +setup_test_env.test_data_dir = os.path.join(os.path.dirname(__file__), "data", "setup") + + +@pytest.mark.online +def test_cli_with_default_urls(): + expected_file = test_env.get_test_loc("default-url-expected.json", must_exist=False) + specifier = "zipp==3.8.0" + extra_options = [ + "--use-pypi-json-api", + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_pdt_output(): + requirements_file = test_env.get_test_loc("pdt-requirements.txt") + expected_file = test_env.get_test_loc("pdt-requirements.txt-expected.json", must_exist=False) + extra_options = [] + check_requirements_resolution( + requirements_file=requirements_file, + expected_file=expected_file, + extra_options=extra_options, + pdt_output=True, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_pdt_output_with_pinned_requirements(): + requirements_file = test_env.get_test_loc("pinned-pdt-requirements.txt") + expected_file = test_env.get_test_loc( + "pinned-pdt-requirements.txt-expected.json", must_exist=False + ) + extra_options = [] + check_requirements_resolution( + requirements_file=requirements_file, + expected_file=expected_file, + extra_options=extra_options, + pdt_output=True, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_pdt_output_with_frozen_requirements(): + requirements_file = test_env.get_test_loc("frozen-requirements.txt") + expected_file = test_env.get_test_loc("frozen-requirements.txt-expected.json", must_exist=False) + extra_options = [] + check_requirements_resolution( + requirements_file=requirements_file, + expected_file=expected_file, + extra_options=extra_options, + pdt_output=True, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_single_index_url(): + expected_file = test_env.get_test_loc("single-url-expected.json", must_exist=False) + specifier = "zipp==3.8.0" + extra_options = [ + "--index-url", + "https://pypi.org/simple", + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_single_index_url_except_pypi_simple(): + expected_file = test_env.get_test_loc( + "single-url-except-simple-expected.json", must_exist=False + ) + # using flask since it's not present in thirdparty + specifier = "flask" + extra_options = [ + "--index-url", + "https://thirdparty.aboutcode.org/pypi/simple/", + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_multiple_index_url_and_tilde_req(): + expected_file = test_env.get_test_loc("tilde_req-expected.json", must_exist=False) + specifier = "zipp~=3.8.0" + extra_options = [ + "--index-url", + "https://pypi.org/simple", + "--index-url", + "https://thirdparty.aboutcode.org/pypi/simple/", + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_environment_marker_and_complex_ranges(): + requirements_file = test_env.get_test_loc("environment-marker-test-requirements.txt") + expected_file = test_env.get_test_loc( + "environment-marker-test-requirements.txt-expected.json", must_exist=False + ) + extra_options = [ + "--operating-system", + "linux", + "--python-version", + "37", + ] + check_requirements_resolution( + requirements_file=requirements_file, + expected_file=expected_file, + extra_options=extra_options, + pdt_output=True, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_multiple_index_url_and_tilde_req_with_max_rounds(): + expected_file = test_env.get_test_loc("tilde_req-expected.json", must_exist=False) + specifier = "zipp~=3.8.0" + extra_options = [ + "--index-url", + "https://pypi.org/simple", + "--index-url", + "https://thirdparty.aboutcode.org/pypi/simple/", + "--max-rounds", + "100", + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_multiple_index_url_and_tilde_req_and_netrc_file_without_matching_url(): + expected_file = test_env.get_test_loc("tilde_req-expected.json", must_exist=False) + netrc_file = test_env.get_test_loc("test.netrc", must_exist=False) + specifier = "zipp~=3.8.0" + extra_options = [ + "--index-url", + "https://pypi.org/simple", + "--index-url", + "https://thirdparty.aboutcode.org/pypi/simple/", + "--netrc", + netrc_file, + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_pinned_requirements_file(): + requirements_file = test_env.get_test_loc("pinned-requirements.txt") + expected_file = test_env.get_test_loc("pinned-requirements.txt-expected.json", must_exist=False) + check_requirements_resolution( + requirements_file=requirements_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + ) + + +@pytest.mark.online +def test_cli_with_setup_py_failure(): + setup_py_file = setup_test_env.get_test_loc("simple-setup.py") + expected_file = setup_test_env.get_test_loc("simple-setup.py-expected.json", must_exist=False) + check_setup_py_resolution( + setup_py=setup_py_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + expected_rc=1, + message=f"Python version 3.8 is not compatible with setup.py {setup_py_file} python_requires >2, <=3", + ) + + +@pytest.mark.online +def test_cli_with_insecure_option(): + setup_py_file = setup_test_env.get_test_loc("spdx-setup.py") + expected_file = setup_test_env.get_test_loc("spdx-setup.py-expected.json", must_exist=False) + check_setup_py_resolution( + setup_py=setup_py_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + extra_options=["--python-version", "27", "--insecure"], + pdt_output=True, + ) + + +@pytest.mark.online +def test_cli_with_insecure_option_testpkh(): + setup_py_file = test_env.get_test_loc("insecure-setup-2/setup.py") + expected_file = test_env.get_test_loc( + "insecure-setup-2/setup.py-expected.json", must_exist=False + ) + check_setup_py_resolution( + setup_py=setup_py_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + extra_options=["--python-version", "27", "--insecure"], + ) + + +@pytest.mark.online +def test_cli_with_setup_py(): + setup_py_file = setup_test_env.get_test_loc("simple-setup.py") + expected_file = setup_test_env.get_test_loc("simple-setup.py-expected.json", must_exist=False) + check_setup_py_resolution( + setup_py=setup_py_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + extra_options=["--python-version", "27"], + ) + + +def check_specs_resolution( + specifier, + expected_file, + extra_options=tuple(), + regen=REGEN_TEST_FIXTURES, +): + result_file = test_env.get_temp_file("json") + options = ["--specifier", specifier, "--json", result_file] + options.extend(extra_options) + run_cli(options=options) + check_json_results( + result_file=result_file, + expected_file=expected_file, + regen=regen, + ) + + +def test_passing_of_json_pdt_and_json_flags(): + result_file = test_env.get_temp_file("json") + options = ["--specifier", "foo", "--json", result_file, "--json-pdt", result_file] + run_cli(options=options, expected_rc=1) + + +def test_version_option(): + options = ["--version"] + result = run_cli(options=options) + assert "0.6.5" in result.output + + +def test_passing_of_netrc_file_that_does_not_exist(): + options = ["--specifier", "foo", "--netrc", "bar.txt", "--json", "-"] + run_cli(options=options, expected_rc=2) + + +def test_passing_of_wrong_requirements_file(): + test_file = test_env.get_temp_file(file_name="pdt.txt", extension="") + with open(test_file, "w") as f: + f.write("") + test_file_2 = test_env.get_temp_file(file_name="setup.py", extension="") + with open(test_file_2, "w") as f: + f.write("") + options = ["--requirement", test_file, "--json", "-", "--requirement", test_file_2] + result = run_cli(options=options, expected_rc=1) + assert "Error: no requirements requested" in result.output + + +def test_passing_of_no_json_output_flag(): + options = ["--specifier", "foo"] + run_cli(options=options, expected_rc=1) + + +def check_requirements_resolution( + requirements_file, + expected_file, + extra_options=tuple(), + regen=REGEN_TEST_FIXTURES, + pdt_output=False, +): + result_file = test_env.get_temp_file("json") + if pdt_output: + options = ["--requirement", requirements_file, "--json-pdt", result_file] + else: + options = ["--requirement", requirements_file, "--json", result_file] + options.extend(extra_options) + run_cli(options=options) + check_json_results( + result_file=result_file, expected_file=expected_file, regen=regen, clean=not pdt_output + ) + + +def check_setup_py_resolution( + setup_py, + expected_file, + extra_options=tuple(), + regen=REGEN_TEST_FIXTURES, + pdt_output=False, + expected_rc=0, + message="", +): + result_file = setup_test_env.get_temp_file(file_name="json") + if pdt_output: + options = ["--setup-py", setup_py, "--json-pdt", result_file] + else: + options = ["--setup-py", setup_py, "--json", result_file] + options.extend(extra_options) + result = run_cli(options=options, expected_rc=expected_rc) + if message: + assert message in result.output + if expected_rc == 0: + check_json_results( + result_file=result_file, expected_file=expected_file, regen=regen, clean=not pdt_output + ) + + +def check_json_results(result_file, expected_file, clean=True, regen=REGEN_TEST_FIXTURES): + """ + Check the ``result_file`` JSON results against the ``expected_file`` + expected JSON results. + + If ``clean`` is True, remove headers data that can change across runs to + provide stable test resultys. + + If ``regen`` is True the expected_file WILL BE overwritten with the new + results from ``results_file``. This is convenient for updating tests + expectations. + """ + with open(result_file) as res: + results = json.load(res) + + if clean: + clean_results(results) + + if regen: + with open(expected_file, "w") as reg: + json.dump(results, reg, indent=2, separators=(",", ": ")) + expected = results + else: + with open(expected_file) as res: + expected = json.load(res) + + if clean: + clean_results(expected) + + assert results == expected + + +def clean_results(results): + """ + Return cleaned results removing transient values that can change across test + runs. + """ + headers = results.get("headers", {}) + options = headers.get("options", []) + headers["options"] = [o for o in options if not o.startswith("--requirement")] + return results + + +def run_cli(options, cli=resolve_dependencies, expected_rc=0, env=None): + """ + Run a command line resolution. Return a click.testing.Result object. + """ + + if not env: + env = dict(os.environ) + + runner = CliRunner() + result = runner.invoke(cli, options, catch_exceptions=False, env=env) + + if result.exit_code != expected_rc: + output = result.output + error = f""" +Failure to run: +rc: {result.exit_code} +python-inspector {options} +output: +{output} +""" + assert result.exit_code == expected_rc, error + return result + + +def test_get_requirements_from_direct_dependencies(): + direct_dependencies = [ + models.DependentPackage( + purl="pkg:pypi/django", + scope="install", + is_runtime=True, + is_optional=False, + is_resolved=False, + extracted_requirement="django>=1.11.11", + extra_data=dict( + is_editable=False, + link=None, + hash_options=[], + is_constraint=False, + is_archive=False, + is_wheel=False, + is_url=False, + is_vcs_url=False, + is_name_at_url=False, + is_local_path=False, + ), + ) + ] + + requirements = [ + str(r) + for r in get_requirements_from_direct_dependencies( + direct_dependencies=direct_dependencies, environment_marker={} + ) + ] + + assert requirements == ["django>=1.11.11"] + + +def test_get_requirements_from_direct_dependencies_with_empty_list(): + assert ( + list( + get_requirements_from_direct_dependencies(direct_dependencies=[], environment_marker={}) + ) + == [] + ) + + +def test_get_requirements_from_direct_dependencies_with_editable_requirements(): + direct_dependencies = [ + models.DependentPackage( + purl="pkg:pypi/django", + scope="install", + is_runtime=True, + is_optional=False, + is_resolved=False, + extracted_requirement="django>=1.11.11", + extra_data=dict( + is_editable=True, + link=None, + hash_options=[], + is_constraint=False, + is_archive=False, + is_wheel=False, + is_url=False, + is_vcs_url=False, + is_name_at_url=False, + is_local_path=False, + ), + ) + ] + + requirements = [ + str(r) + for r in get_requirements_from_direct_dependencies( + direct_dependencies=direct_dependencies, environment_marker={} + ) + ] + + assert requirements == [] diff --git a/tests/test_resolution.py b/tests/test_resolution.py index d49360d8..6a41594e 100644 --- a/tests/test_resolution.py +++ b/tests/test_resolution.py @@ -226,3 +226,15 @@ def test_setup_py_parsing_insecure(): setup_py_file = setup_test_env.get_test_loc("insecure-setup/setup.py") reqs = [str(req) for req in list(parse_setup_py_insecurely(setup_py=setup_py_file))] assert reqs == ["isodate", "pyparsing", "six"] + + +def test_setup_py_parsing_insecure_testpkh(): + setup_py_file = setup_test_env.get_test_loc("insecure-setup-2/setup.py") + reqs = [str(req) for req in list(parse_setup_py_insecurely(setup_py=setup_py_file))] + assert reqs == [ + "CairoSVG<2.0.0,>=1.0.20", + "click>=5.0.0", + "invenio[auth,base,metadata]>=3.0.0", + "invenio-records==1.0.*,>=1.0.0", + "mock>=1.3.0", + ] diff --git a/tests/test_setup_py_live_eval.py b/tests/test_setup_py_live_eval.py index 5f7f56ec..4d91663e 100755 --- a/tests/test_setup_py_live_eval.py +++ b/tests/test_setup_py_live_eval.py @@ -9,58 +9,23 @@ # """Tests for `requirements-builder` module.""" -from os.path import abspath, dirname, join +from os.path import abspath +from os.path import dirname +from os.path import join -from requirements_builder import __version__, iter_requirements +from python_inspector.setup_py_live_eval import iter_requirements REQ = abspath(join(dirname(__file__), "./fixtures/requirements.devel.txt")) SETUP = abspath(join(dirname(__file__), "./fixtures/setup.txt")) -def test_version(): +def test_iter_requirements_with_setup_py(): """Test requirements-builder.""" - assert __version__ - - -def test_iter_requirements(): - """Test requirements-builder.""" - # Min - with open(SETUP) as f: - assert list(iter_requirements("min", [], '', f)) == \ - ['click==6.1.0', 'mock==1.3.0'] - - # PyPI - with open(SETUP) as f: - assert list(iter_requirements("pypi", [], '', f)) == \ - ['click>=6.1.0', 'mock>=1.3.0'] - - # Dev - with open(SETUP) as f: - assert list(iter_requirements("dev", [], REQ, f)) == \ - ['-e git+https://github.com/pallets/click.git#egg=click', - 'mock>=1.3.0'] - - -def test_iter_requirements_cfg(): - """Test requirements-builder.""" - req = abspath(join(dirname(__file__), "../requirements.devel.txt")) - setup = abspath(join(dirname(__file__), "../setup.py")) - setup_cfg = abspath(join(dirname(__file__), "../setup.cfg")) - # Min - with open(setup) as f: - with open(setup_cfg) as g: - assert list(iter_requirements("min", [], '', f, g)) == \ - ['click==7.0', 'mock==1.3.0'] + assert list(iter_requirements("min", [], SETUP)) == ["click==6.1.0", "mock==1.3.0"] # PyPI - with open(setup) as f: - with open(setup_cfg) as g: - assert list(iter_requirements("pypi", [], '', f, g)) == \ - ['click>=7.0', 'mock<4,>=1.3.0'] + assert list(iter_requirements("pypi", [], SETUP)) == ["click>=6.1.0", "mock>=1.3.0"] # Dev - with open(setup) as f: - with open(setup_cfg) as g: - assert list(iter_requirements("dev", [], req, f, g)) == \ - ['click>=7.0', 'mock<4,>=1.3.0'] + assert list(iter_requirements("dev", [], SETUP)) == ["click>=6.1.0", "mock>=1.3.0"] From 37375d2357ebf57546c3f92448b9101658545fae Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Tue, 20 Sep 2022 17:44:48 +0530 Subject: [PATCH 49/54] Address review comments Signed-off-by: Tushar Goel --- CHANGELOG.rst | 15 +- setup.cfg | 93 + src/python_inspector/package_data.py | 2 + src/python_inspector/resolve_cli.py | 5 +- .../insecure-setup-2/setup.py-expected.json | 7034 ++++++++++++++++- tests/data/setup/spdx-setup.py-expected.json | 543 +- tests/test_cli.py | 81 +- tests/test_setup_py_live_eval_cli.py | 2 +- 8 files changed, 7319 insertions(+), 456 deletions(-) create mode 100644 setup.cfg diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 34d1c8f0..9a0aa66e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,32 +1,43 @@ Changelog ========= -v0.6.6 + +v0.7.0 ------ -- Add --insecure option to compute arguments. + +- Enable live evaluation of the "setup.py" that use computed arguments. + When this occurs, a live evaluation of the Python code is the only working + solution short of a full installation. Because this can be a security issue, + there is a new "--analyze-setup-py-insecurely" command line option to enable this feature. + Note that this not more insecure than actually installing a PyPI package. + v0.6.5 ------ - Add --version option. + v0.6.4 ------ - Add support for setup.py + v0.6.3 ------ - Ensure to filter out top level dependencies on the basis of their environment markers - Do not ignore files on basis of name + v0.6.2 ------ - Ignore invalid requirement files on basis of name - Use netrc file from home directory if not present + v0.6.1 ------ diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..926b14fe --- /dev/null +++ b/setup.cfg @@ -0,0 +1,93 @@ +[metadata] +name = python-inspector +license = Apache-2.0 + +# description must be on ONE line https://github.com/pypa/setuptools/issues/1390 +description = python-inspector is is a collection of utilities to collect PyPI package metadata and resolve packages dependencies. +long_description = file:README.rst +long_description_content_type = text/x-rst +url = https://github.com/nexB/python-inspector + +author = nexB. Inc. and others +author_email = info@aboutcode.org + +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Topic :: Software Development + Topic :: Utilities + +keywords = + open source + scan + package + dependency + pypi + python + SBOM + sca + dependencies + dependency resolution + resolver + resolvelib + pip + requirements + +license_files = + apache-2.0.LICENSE + NOTICE + AUTHORS.rst + CHANGELOG.rst + CODE_OF_CONDUCT.rst + +[options] +package_dir = + =src +packages = find: +include_package_data = true +zip_safe = false + +setup_requires = setuptools_scm[toml] >= 4 + +python_requires = >=3.6.* + +install_requires = + attrs >= 18.1, !=20.1.0 + click > 7.0 + colorama >= 0.3.9 + commoncode >= 30.0.0 + dparse2 >= 0.6.1 + importlib_metadata >= 4.12.0 + packageurl_python >= 0.9.0 + pkginfo2 >= 30.0.0 + pip-requirements-parser >= 31.2.0 + requests >= 2.18.0 + resolvelib >= 0.8.1 + saneyaml >= 0.5.2 + tinynetrc >= 1.3.1 + toml >= 0.10.0 + mock >= 3.0.5 + +[options.packages.find] +where = src + +[options.entry_points] +console_scripts = + python-inspector = python_inspector.resolve_cli:resolve_dependencies + +[options.extras_require] +testing = + pytest >= 6, != 7.0.0 + pytest-xdist >= 2 + aboutcode-toolkit >= 7.0.2 + twine + black + isort + pycodestyle + +docs = + Sphinx >= 3.3.1 + sphinx-rtd-theme >= 0.5.0 + doc8 >= 0.8.1 diff --git a/src/python_inspector/package_data.py b/src/python_inspector/package_data.py index 146371c9..72bfe7f3 100644 --- a/src/python_inspector/package_data.py +++ b/src/python_inspector/package_data.py @@ -92,6 +92,8 @@ def get_pypi_data_from_purl( from python_inspector.resolution import get_response response = get_response(api_url) + if not response: + return [] info = response.get("info") or {} homepage_url = info.get("home_page") license = info.get("license") diff --git a/src/python_inspector/resolve_cli.py b/src/python_inspector/resolve_cli.py index c825e2f3..c6c191ed 100644 --- a/src/python_inspector/resolve_cli.py +++ b/src/python_inspector/resolve_cli.py @@ -158,9 +158,10 @@ def print_version(ctx, param, value): "--index-url are ignored when this option is active.", ) @click.option( - "--insecure", + "--analyze-setup-py-insecurely", is_flag=True, - help="Resolve insecurely", + help="Enable collection of requirements in setup.py that compute these" + " dynamically. This is an insecure operation as it can run arbitrary code.", ) @click.option( "--verbose", diff --git a/tests/data/insecure-setup-2/setup.py-expected.json b/tests/data/insecure-setup-2/setup.py-expected.json index 88519eeb..5d73e273 100644 --- a/tests/data/insecure-setup-2/setup.py-expected.json +++ b/tests/data/insecure-setup-2/setup.py-expected.json @@ -70,75 +70,45 @@ "package": "pkg:pypi/amqp@2.6.1", "dependencies": [ "pkg:pypi/vine@1.3.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/bc/90/bb5ce93521772f083cb2d7a413bb82eda5afc62b4192adb7ea4c7b4858b9/amqp-2.6.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/37/9f/d54494a157d0dcd1673fe7a1bcce7ac70d3eb6d5d6149749450c87a2c959/amqp-2.6.1.tar.gz" + ] }, { "package": "pkg:pypi/attrs@21.4.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/be/be/7abce643bfdf8ca01c48afa2ddf8308c2308b0c3b239a44e57d020afa0ef/attrs-21.4.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/d7/77/ebb15fc26d0f815839ecd897b919ed6d85c050feeb83e100e020df9153d2/attrs-21.4.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/babel@2.9.1", "dependencies": [ "pkg:pypi/pytz@2022.2.1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/aa/96/4ba93c5f40459dc850d25f9ba93f869a623e77aaecc7a9344e19c01942cf/Babel-2.9.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/17/e6/ec9aa6ac3d00c383a5731cc97ed7c619d3996232c977bb8326bcbb6c687e/Babel-2.9.1.tar.gz" + ] }, { "package": "pkg:pypi/backports-functools-lru-cache@1.6.4", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/e5/c1/1a48a4bb9b515480d6c666977eeca9243be9fa9e6fb5a34be0ad9627f737/backports.functools_lru_cache-1.6.4-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/95/9f/122a41912932c77d5b8e6cab6bd456e6270211a3ed7248a80c235179a012/backports.functools_lru_cache-1.6.4.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/backports-shutil-get-terminal-size@1.0.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/7d/cd/1750d6c35fe86d35f8562091737907f234b78fdffab42b29c72b1dd861f4/backports.shutil_get_terminal_size-1.0.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/billiard@3.6.4.0", - "dependencies": [], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/92/91/40de1901da8ec9eeb7c6a22143ba5d55d8aaa790761ca31342cedcd5c793/billiard-3.6.4.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/blinker@1.5", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/30/41/caa5da2dbe6d26029dfe11d31dfa8132b4d6d30b6d6b61a24824075a5f06/blinker-1.5-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/2b/12/82786486cefb68685bb1c151730f510b0f4e5d621d77f245bc0daf9a6c64/blinker-1.5.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/cairocffi@0.9.0", "dependencies": [ "pkg:pypi/cffi@1.15.1" - ], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/62/be/ad4d422b6f38d99b09ad6d046ab725e8ccac5fefd9ca256ca35a80dbf3c6/cairocffi-0.9.0.tar.gz" + ] }, { "package": "pkg:pypi/cairosvg@1.0.22", "dependencies": [ "pkg:pypi/cairocffi@0.9.0" - ], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/d9/9b/c241990c86faa9e52a01f0570bba4247ba0f3d66eae2607e179cb9ae773a/CairoSVG-1.0.22.tar.gz" + ] }, { "package": "pkg:pypi/celery@4.4.7", @@ -147,61 +117,33 @@ "pkg:pypi/kombu@4.6.11", "pkg:pypi/pytz@2022.2.1", "pkg:pypi/vine@1.3.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/c8/0c/609e3611d20c9f8d883852d1be5516671f630fb08c8c1e56911567dfba7b/celery-4.4.7-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/fe/58/c7ced9705c2cedf526e183e428d1b145910cb8bc7ea537a2ec9a6552c056/celery-4.4.7.tar.gz" + ] }, { "package": "pkg:pypi/cffi@1.15.1", "dependencies": [ "pkg:pypi/pycparser@2.21" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/1d/76/bcebbbab689f5f6fc8a91e361038a3001ee2e48c5f9dbad0a3b64a64cc9e/cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/2b/a8/050ab4f0c3d4c1b8aaa805f70e26e84d0e27004907c5b8ecc1d31815f92a/cffi-1.15.1.tar.gz" + ] }, { "package": "pkg:pypi/click@7.1.2", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/d2/3d/fa76db83bf75c4f8d338c2fd15c8d33fdd7ad23a9b5e57eb6c5de26b430e/click-7.1.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/configparser@4.0.2", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/7a/2a/95ed0501cf5d8709490b1d3a3f9b5cf340da6c433f896bbe9ce08dbe6785/configparser-4.0.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/16/4f/48975536bd488d3a272549eb795ac4a13a5f7fcdc8995def77fbef3532ee/configparser-4.0.2.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/contextlib2@0.6.0.post1", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/85/60/370352f7ef6aa96c52fb001831622f50f923c1d575427d021b8ab3311236/contextlib2-0.6.0.post1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/02/54/669207eb72e3d8ae8b38aa1f0703ee87a0e9f88f30d3c0a47bebdb6de242/contextlib2-0.6.0.post1.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/decorator@4.4.2", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/ed/1b/72a1821152d07cf1d8b6fce298aeb06a7eb90f4d6d41acec9861e7cc6df0/decorator-4.4.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/da/93/84fa12f2dc341f8cf5f022ee09e109961055749df2d0c75c5f98746cfe6c/decorator-4.4.2.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/enum34@1.1.10", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/6f/2c/a9386903ece2ea85e9807e0e062174dc26fdce8b05f216d00491be29fad5/enum34-1.1.10-py2-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/flask-babelex@0.9.4", @@ -210,30 +152,20 @@ "pkg:pypi/flask@1.1.4", "pkg:pypi/jinja2@2.11.3", "pkg:pypi/speaklater@1.3" - ], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/85/e7/217fb37ccd4bd93cd0f002028fb7c5fdf6ee0063a6beb83e43cd903da46e/Flask-BabelEx-0.9.4.tar.gz" + ] }, { "package": "pkg:pypi/flask-caching@1.9.0", "dependencies": [ "pkg:pypi/flask@1.1.4" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/d1/9f/135bcf47fdb585dffcf9f918664ab9d63585aae8e722948b2abca041312f/Flask_Caching-1.9.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/41/c9/472486c62f22a1dad273a132b9484189e1a22eb8358883249e4955f8e464/Flask-Caching-1.9.0.tar.gz" + ] }, { "package": "pkg:pypi/flask-celeryext@0.3.4", "dependencies": [ "pkg:pypi/celery@4.4.7", "pkg:pypi/flask@1.1.4" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/70/a0/74c30a11f96be5ad3bf5609b00fe91755b9eee2ce6cc0bba59c32614afe8/Flask_CeleryExt-0.3.4-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/3f/67/a048930d1c6349b7cc038738140a258c443c5b8f83043311972a53364833/Flask-CeleryExt-0.3.4.tar.gz" + ] }, { "package": "pkg:pypi/flask-limiter@1.1.0", @@ -241,11 +173,7 @@ "pkg:pypi/flask@1.1.4", "pkg:pypi/limits@1.6", "pkg:pypi/six@1.16.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/72/f3/68596cb061e1c7d5a7dfb3694de3f8845b908ea16296e762136f34727a65/Flask_Limiter-1.1.0-py2-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/96/a6/35fe99ef33b44ae33c212da20e8f545354f58cb0c77f8b6cdcfda9f5e9ad/Flask-Limiter-1.1.0.tar.gz" + ] }, { "package": "pkg:pypi/flask-shell-ipython@0.4.1", @@ -253,21 +181,13 @@ "pkg:pypi/click@7.1.2", "pkg:pypi/flask@1.1.4", "pkg:pypi/ipython@5.10.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/10/6b/a45278cbff711cc7b8904bd38679a8cc249c4db825a76ae332302f59d398/flask_shell_ipython-0.4.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/48/8e/ba861448b7590282519aea82ae39107c77d001fdc37b7af4c14ed0e0db77/flask-shell-ipython-0.4.1.tar.gz" + ] }, { "package": "pkg:pypi/flask-talisman@0.8.1", "dependencies": [ "pkg:pypi/six@1.16.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/0a/f1/3c2a37a8053149521407bff4573cecca93d86b1f15a027e8cc4463da6261/flask_talisman-0.8.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/dd/1a/9f21ccb72a0d09594eb704da7f87a6d373ad7e9d4ac18693d1a3c275afb2/flask-talisman-0.8.1.tar.gz" + ] }, { "package": "pkg:pypi/flask@1.1.4", @@ -276,19 +196,11 @@ "pkg:pypi/itsdangerous@1.1.0", "pkg:pypi/jinja2@2.11.3", "pkg:pypi/werkzeug@1.0.1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/e8/6d/994208daa354f68fd89a34a8bafbeaab26fda84e7af1e35bdaed02b667e6/Flask-1.1.4-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/4d/5b/2d145f5fe718b2f15ebe69240538f06faa8bbb76488bf962091db1f7a26d/Flask-1.1.4.tar.gz" + ] }, { "package": "pkg:pypi/funcsigs@1.0.2", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/importlib-metadata@2.1.3", @@ -297,11 +209,7 @@ "pkg:pypi/contextlib2@0.6.0.post1", "pkg:pypi/pathlib2@2.3.7.post1", "pkg:pypi/zipp@1.2.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/cf/b4/877779cd7b5a15536ecbe0655cfb35a0de0ede6d888151fd7356d278c47d/importlib_metadata-2.1.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/4c/d8/af92ca59b33366b3b9d230d17772d728d7b064f3de6d6150332d5fa8dae3/importlib_metadata-2.1.3.tar.gz" + ] }, { "package": "pkg:pypi/invenio%5bauth%2cbase%2cmetadata%5d@3.4.1", @@ -313,9 +221,7 @@ "pkg:pypi/invenio-config@1.0.3", "pkg:pypi/invenio-i18n@1.3.1", "pkg:pypi/invenio@3.4.1" - ], - "wheel_urls": [], - "sdist_url": null + ] }, { "package": "pkg:pypi/invenio-app@1.3.3", @@ -329,11 +235,7 @@ "pkg:pypi/invenio-config@1.0.3", "pkg:pypi/limits@1.6", "pkg:pypi/uritools@2.2.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/25/ee/bfe28143e98e24a27ca8d6c4a464591e8006766a60fe8468a9b2061468e2/invenio_app-1.3.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/ef/e6/f7e3e2c79520a0046008aa0cb5381734f681d040793e5b8d1b995deaca5d/invenio-app-1.3.3.tar.gz" + ] }, { "package": "pkg:pypi/invenio-base@1.2.5", @@ -342,22 +244,14 @@ "pkg:pypi/flask@1.1.4", "pkg:pypi/six@1.16.0", "pkg:pypi/werkzeug@1.0.1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/47/7c/89fe1cd7263c8d67a366c568dcc6f5050518f06ca7695f7b719f3f340229/invenio_base-1.2.5-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/fd/3b/135f8f839f696ec9f163083625b5111310108fd86b0a9668f7da4a0b6f92/invenio-base-1.2.5.tar.gz" + ] }, { "package": "pkg:pypi/invenio-cache@1.1.0", "dependencies": [ "pkg:pypi/flask-caching@1.9.0", "pkg:pypi/invenio-base@1.2.5" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/04/f0/22b7426ac2b821d93bc6066410fac652c924330bfbd5b64a82e1e36b0ed6/invenio_cache-1.1.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/4f/f8/aee6e433a4b94882b8089cf5ff17ab1612a6ecc9231b3103cc4e6a533c98/invenio-cache-1.1.0.tar.gz" + ] }, { "package": "pkg:pypi/invenio-celery@1.2.2", @@ -367,32 +261,20 @@ "pkg:pypi/invenio-base@1.2.5", "pkg:pypi/msgpack@1.0.4", "pkg:pypi/redis@3.5.3" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/b8/a7/ec89de1fd4615aebc0e59c25ab8d8152bb04f03fa21b1c50bdd08239e79e/invenio_celery-1.2.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/67/db/7d7dffe8f620697461a4f680505c1d04904ca5b6805c10fd9a9677b5e0b6/invenio-celery-1.2.2.tar.gz" + ] }, { "package": "pkg:pypi/invenio-config@1.0.3", "dependencies": [ "pkg:pypi/flask@1.1.4" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/5a/67/c751f8b8cf6ec5c4415bccaeed7f6b7eab836178ca4faa9f4d7088616735/invenio_config-1.0.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/cd/8e/36d33ec84f29bbeed5593ea8e89b421e0cbcc6fafd0d8c300d187f4a6d9d/invenio-config-1.0.3.tar.gz" + ] }, { "package": "pkg:pypi/invenio-i18n@1.3.1", "dependencies": [ "pkg:pypi/flask-babelex@0.9.4", "pkg:pypi/invenio-base@1.2.5" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/81/56/4398e45ba27c8436e38b8027881d72d1e02ab199d0eb69cb425759173ec4/invenio_i18n-1.3.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/06/70/b65b7ffef1daf8c864329af034c06a56ce8c889d887261553aad75ecd3dc/invenio-i18n-1.3.1.tar.gz" + ] }, { "package": "pkg:pypi/invenio-records@1.0.2", @@ -404,11 +286,7 @@ "pkg:pypi/jsonref@0.2", "pkg:pypi/jsonresolver@0.3.1", "pkg:pypi/jsonschema@4.0.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/50/97/fd03b1bbc220643230bf9e1f621d4043d583043d0cf5506a8ccb0551029c/invenio_records-1.0.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/88/8a/b4bd1e9b493fc119d972ac89920c32fa4573205e8fa2e893e53effc51f1c/invenio-records-1.0.2.tar.gz" + ] }, { "package": "pkg:pypi/invenio@3.4.1", @@ -419,27 +297,15 @@ "pkg:pypi/invenio-celery@1.2.2", "pkg:pypi/invenio-config@1.0.3", "pkg:pypi/invenio-i18n@1.3.1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/31/59/5c20981a3f371b59c0545905a800cc5f3f066cb9a14a126ba6b96a255933/invenio-3.4.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/fe/e9/bf37c72c3e231ba5dafefb134b0f2cb6fbfc26d7a504047d7277097b6bb8/invenio-3.4.1.tar.gz" + ] }, { "package": "pkg:pypi/ipaddress@1.0.23", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/c2/f8/49697181b1651d8347d24c095ce46c7346c37335ddc7d255833e7cde674d/ipaddress-1.0.23-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/ipython-genutils@0.2.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/fa/bc/9bd3b5c2b4774d5f33b2d544f1460be9df7df2fe42f352135381c347c69a/ipython_genutils-0.2.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/ipython@5.10.0", @@ -454,53 +320,31 @@ "pkg:pypi/setuptools@44.1.1", "pkg:pypi/simplegeneric@0.8.1", "pkg:pypi/traitlets@4.3.3" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/ce/2c/2849a2b37024a01a847c87d81825c0489eb22ffc6416cac009bf281ea838/ipython-5.10.0-py2-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/b6/73/c8f68b3a7d0deece3d2f7ab727fbf262bfca7475330b44043a5503b3aa7a/ipython-5.10.0.tar.gz" + ] }, { "package": "pkg:pypi/itsdangerous@1.1.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/jinja2@2.11.3", "dependencies": [ "pkg:pypi/markupsafe@1.1.1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/7e/c2/1eece8c95ddbc9b1aeb64f5783a9e07a286de42191b7204d67b7496ddf35/Jinja2-2.11.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz" + ] }, { "package": "pkg:pypi/jsonpatch@1.32", "dependencies": [ "pkg:pypi/jsonpointer@2.3" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/a3/55/f7c93bae36d869292aedfbcbae8b091386194874f16390d680136edd2b28/jsonpatch-1.32-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/21/67/83452af2a6db7c4596d1e2ecaa841b9a900980103013b867f2865e5e1cf0/jsonpatch-1.32.tar.gz" + ] }, { "package": "pkg:pypi/jsonpointer@2.3", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/a3/be/8dc9d31b50e38172c8020c40f497ce8debdb721545ddb9fcb7cca89ea9e6/jsonpointer-2.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/a0/6c/c52556b957a0f904e7c45585444feef206fe5cb1ff656303a1d6d922a53b/jsonpointer-2.3.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/jsonref@0.2", - "dependencies": [], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/b3/cf/93d4f34d76863d4fb995cb8e3e4f29908304065ce6381e0349700c44ad0c/jsonref-0.2.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/jsonresolver@0.3.1", @@ -508,11 +352,7 @@ "pkg:pypi/pluggy@0.13.1", "pkg:pypi/six@1.16.0", "pkg:pypi/werkzeug@1.0.1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/0d/8e/e63fba33303e2062eb1fbd38a5483538ad1b5eee73e04ac1d1f632ac2b76/jsonresolver-0.3.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/2c/45/30a9704607f4f868bb44a1f04d133157b8355cc1b99e083e9d7d47df3d24/jsonresolver-0.3.1.tar.gz" + ] }, { "package": "pkg:pypi/jsonschema@4.0.0", @@ -520,53 +360,35 @@ "pkg:pypi/attrs@21.4.0", "pkg:pypi/importlib-metadata@2.1.3", "pkg:pypi/pyrsistent@0.16.1" - ], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/9c/99/9789c7fd0bb8876a7d624d903195ce11e5618b421bdb1bf7c975d17a9bc3/jsonschema-4.0.0.tar.gz" + ] }, { "package": "pkg:pypi/kombu@4.6.11", "dependencies": [ "pkg:pypi/amqp@2.6.1", "pkg:pypi/importlib-metadata@2.1.3" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/9e/34/3eea6a3a9ff81b0c7ddbdceb22a1ffc1b5907d863f27ca19a68777d2211d/kombu-4.6.11-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/45/e1/00f2e3f6f5575aa2f7ee41e2fd884ce573f8146e136bde37daf45ef7ca5e/kombu-4.6.11.tar.gz" + ] }, { "package": "pkg:pypi/limits@1.6", "dependencies": [ "pkg:pypi/six@1.16.0" - ], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/f8/80/3e208b8cfdcf9bf85006097a53c93f53a0c23bc711f1e6abfc180ebb4bca/limits-1.6.tar.gz" + ] }, { "package": "pkg:pypi/markupsafe@1.1.1", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/d8/1f/e97c4c6b182e59562f99c207f0f621d15a42fc82a6532a98e0b2d38b7c4e/MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/mock@3.0.5", "dependencies": [ "pkg:pypi/funcsigs@1.0.2", "pkg:pypi/six@1.16.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/05/d2/f94e68be6b17f46d2c353564da56e6fb89ef09faeeff3313a046cb810ca9/mock-3.0.5-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/2e/ab/4fe657d78b270aa6a32f027849513b829b41b0f28d9d8d7f8c3d29ea559a/mock-3.0.5.tar.gz" + ] }, { "package": "pkg:pypi/msgpack@1.0.4", - "dependencies": [], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/22/44/0829b19ac243211d1d2bd759999aa92196c546518b0be91de9cacc98122a/msgpack-1.0.4.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/pathlib2@2.3.7.post1", @@ -574,134 +396,78 @@ "pkg:pypi/scandir@1.10.0", "pkg:pypi/six@1.16.0", "pkg:pypi/typing@3.10.0.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/09/eb/4af4bcd5b8731366b676192675221c5324394a580dfae469d498313b5c4a/pathlib2-2.3.7.post1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/31/51/99caf463dc7c18eb18dad1fffe465a3cf3ee50ac3d1dccbd1781336fe9c7/pathlib2-2.3.7.post1.tar.gz" + ] }, { "package": "pkg:pypi/pexpect@4.8.0", "dependencies": [ "pkg:pypi/ptyprocess@0.7.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/39/7b/88dbb785881c28a102619d46423cb853b46dbccc70d3ac362d99773a78ce/pexpect-4.8.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz" + ] }, { "package": "pkg:pypi/pickleshare@0.7.5", "dependencies": [ "pkg:pypi/pathlib2@2.3.7.post1" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz" + ] }, { "package": "pkg:pypi/pluggy@0.13.1", "dependencies": [ "pkg:pypi/importlib-metadata@2.1.3" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz" + ] }, { "package": "pkg:pypi/prompt-toolkit@1.0.18", "dependencies": [ "pkg:pypi/six@1.16.0", "pkg:pypi/wcwidth@0.2.5" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/9d/d2/2f099b5cd62dab819ce7a9f1431c09a9032fbfbb6474f442722e88935376/prompt_toolkit-1.0.18-py2-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/c5/64/c170e5b1913b540bf0c8ab7676b21fdd1d25b65ddeb10025c6ca43cccd4c/prompt_toolkit-1.0.18.tar.gz" + ] }, { "package": "pkg:pypi/ptyprocess@0.7.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/pycparser@2.21", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/pygments@2.5.2", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/be/39/32da3184734730c0e4d3fa3b2b5872104668ad6dc1b5a73d8e477e5fe967/Pygments-2.5.2-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/cb/9f/27d4844ac5bf158a33900dbad7985951e2910397998e85712da03ce125f0/Pygments-2.5.2.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/pyrsistent@0.16.1", "dependencies": [ "pkg:pypi/six@1.16.0" - ], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/80/18/1492d651693ef7d40e0a40377ed56a8cc5c5fe86073eb6c56e53513f4480/pyrsistent-0.16.1.tar.gz" + ] }, { "package": "pkg:pypi/pytz@2022.2.1", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/d5/50/54451e88e3da4616286029a3a17fc377de817f66a0f50e1faaee90161724/pytz-2022.2.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/24/0c/401283bb1499768e33ddd2e1a35817c775405c1f047a9dc088a29ce2ea5d/pytz-2022.2.1.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/redis@3.5.3", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/a7/7c/24fb0511df653cf1a5d938d8f5d19802a88cef255706fdda242ff97e91b7/redis-3.5.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/b3/17/1e567ff78c83854e16b98694411fe6e08c3426af866ad11397cddceb80d3/redis-3.5.3.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/scandir@1.10.0", - "dependencies": [], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/setuptools@44.1.1", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/e1/b7/182161210a13158cd3ccc41ee19aadef54496b74f2817cc147006ec932b4/setuptools-44.1.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/b2/40/4e00501c204b457f10fe410da0c97537214b2265247bc9a5bc6edd55b9e4/setuptools-44.1.1.zip" + "dependencies": [] }, { "package": "pkg:pypi/simplegeneric@0.8.1", - "dependencies": [], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip" + "dependencies": [] }, { "package": "pkg:pypi/six@1.16.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/speaklater@1.3", - "dependencies": [], - "wheel_urls": [], - "sdist_url": "https://files.pythonhosted.org/packages/11/92/5ae1effe0ccb8561c034a0111d53c8788660ddb7ed4992f0da1bb5c525e5/speaklater-1.3.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/traitlets@4.3.3", @@ -710,65 +476,6669 @@ "pkg:pypi/enum34@1.1.10", "pkg:pypi/ipython-genutils@0.2.0", "pkg:pypi/six@1.16.0" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/ca/ab/872a23e29cec3cf2594af7e857f18b687ad21039c1f9b922fac5b9b142d5/traitlets-4.3.3-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/75/b0/43deb021bc943f18f07cbe3dac1d681626a48997b7ffa1e7fb14ef922b21/traitlets-4.3.3.tar.gz" + ] }, { "package": "pkg:pypi/typing@3.10.0.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/0b/cb/da856e81731833b94da70a08712f658416266a5fb2a9d9e426c8061becef/typing-3.10.0.0-py2-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/b0/1b/835d4431805939d2996f8772aca1d2313a57e8860fec0e48e8e7dfe3a477/typing-3.10.0.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/uritools@2.2.0", "dependencies": [ "pkg:pypi/ipaddress@1.0.23" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/8c/5d/ef3cd3c40b4b97f0cb50cee8e4c5a8a4abc30953e1c7ce7e0d25cb2534c3/uritools-2.2.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/ab/1c/e9aa4a907806743298171510042447adc20cd5cf5b95436206a067e14496/uritools-2.2.0.tar.gz" + ] }, { "package": "pkg:pypi/vine@1.3.0", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/7f/60/82c03047396126c8331ceb64da1dc52d4f1317209f32e8fe286d0c07365a/vine-1.3.0-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/1c/e1/79fb8046e607dd6c2ad05c9b8ebac9d0bd31d086a08f02699e96fc5b3046/vine-1.3.0.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/wcwidth@0.2.5", "dependencies": [ "pkg:pypi/backports-functools-lru-cache@1.6.4" - ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/59/7c/e39aca596badaf1b78e8f547c807b04dae603a433d3e7a7e04d67f2ef3e5/wcwidth-0.2.5-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/89/38/459b727c381504f361832b9e5ace19966de1a235d73cdbdea91c771a1155/wcwidth-0.2.5.tar.gz" + ] }, { "package": "pkg:pypi/werkzeug@1.0.1", - "dependencies": [], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/cc/94/5f7079a0e00bd6863ef8f1da638721e9da21e5bacee597595b318f71d62e/Werkzeug-1.0.1-py2.py3-none-any.whl" - ], - "sdist_url": "https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz" + "dependencies": [] }, { "package": "pkg:pypi/zipp@1.2.0", "dependencies": [ "pkg:pypi/contextlib2@0.6.0.post1" + ] + } + ], + "packages": [ + { + "type": "pypi", + "namespace": null, + "name": "amqp", + "version": "2.6.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "", + "release_date": "2020-07-31T16:32:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Barry Pederson", + "email": "pyamqp@celeryproject.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Asif Saif Uddin, Matus Valo", + "email": "", + "url": null + } + ], + "keywords": "amqp rabbitmq cloudamqp messaging", + "homepage_url": "http://github.com/celery/py-amqp", + "download_url": "https://files.pythonhosted.org/packages/bc/90/bb5ce93521772f083cb2d7a413bb82eda5afc62b4192adb7ea4c7b4858b9/amqp-2.6.1-py2.py3-none-any.whl", + "size": 48006, + "sha1": null, + "md5": "7e7552f78e16e1d0467af9cfb91ac9d0", + "sha256": "aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/amqp/2.6.1/json", + "datasource_id": null, + "purl": "pkg:pypi/amqp@2.6.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "amqp", + "version": "2.6.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "", + "release_date": "2020-07-31T16:32:31", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Barry Pederson", + "email": "pyamqp@celeryproject.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Asif Saif Uddin, Matus Valo", + "email": "", + "url": null + } + ], + "keywords": "amqp rabbitmq cloudamqp messaging", + "homepage_url": "http://github.com/celery/py-amqp", + "download_url": "https://files.pythonhosted.org/packages/37/9f/d54494a157d0dcd1673fe7a1bcce7ac70d3eb6d5d6149749450c87a2c959/amqp-2.6.1.tar.gz", + "size": 119956, + "sha1": null, + "md5": "c8cf9c75d7cd2e747fa49f3e3c47b3b1", + "sha256": "70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/amqp/2.6.1/json", + "datasource_id": null, + "purl": "pkg:pypi/amqp@2.6.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "attrs", + "version": "21.4.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\n.. image:: https://www.attrs.org/en/stable/_static/attrs_logo.png\n :alt: attrs logo\n :align: center\n\n\n``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder methods `_).\n`Trusted by NASA `_ for Mars missions since 2020!\n\nIts main goal is to help you to write **concise** and **correct** software without slowing down your code.\n\n.. teaser-end\n\nFor that, it gives you a class decorator and a way to declaratively define the attributes on that class:\n\n.. -code-begin-\n\n.. code-block:: pycon\n\n >>> from attrs import asdict, define, make_class, Factory\n\n >>> @define\n ... class SomeClass:\n ... a_number: int = 42\n ... list_of_numbers: list[int] = Factory(list)\n ...\n ... def hard_math(self, another_number):\n ... return self.a_number + sum(self.list_of_numbers) * another_number\n\n\n >>> sc = SomeClass(1, [1, 2, 3])\n >>> sc\n SomeClass(a_number=1, list_of_numbers=[1, 2, 3])\n\n >>> sc.hard_math(3)\n 19\n >>> sc == SomeClass(1, [1, 2, 3])\n True\n >>> sc != SomeClass(2, [3, 2, 1])\n True\n\n >>> asdict(sc)\n {'a_number': 1, 'list_of_numbers': [1, 2, 3]}\n\n >>> SomeClass()\n SomeClass(a_number=42, list_of_numbers=[])\n\n >>> C = make_class(\"C\", [\"a\", \"b\"])\n >>> C(\"foo\", \"bar\")\n C(a='foo', b='bar')\n\n\nAfter *declaring* your attributes ``attrs`` gives you:\n\n- a concise and explicit overview of the class's attributes,\n- a nice human-readable ``__repr__``,\n- a equality-checking methods,\n- an initializer,\n- and much more,\n\n*without* writing dull boilerplate code again and again and *without* runtime performance penalties.\n\n**Hate type annotations**!?\nNo problem!\nTypes are entirely **optional** with ``attrs``.\nSimply assign ``attrs.field()`` to the attributes instead of annotating them with types.\n\n----\n\nThis example uses ``attrs``'s modern APIs that have been introduced in version 20.1.0, and the ``attrs`` package import name that has been added in version 21.3.0.\nThe classic APIs (``@attr.s``, ``attr.ib``, plus their serious business aliases) and the ``attr`` package import name will remain **indefinitely**.\n\nPlease check out `On The Core API Names `_ for a more in-depth explanation.\n\n\nData Classes\n============\n\nOn the tin, ``attrs`` might remind you of ``dataclasses`` (and indeed, ``dataclasses`` are a descendant of ``attrs``).\nIn practice it does a lot more and is more flexible.\nFor instance it allows you to define `special handling of NumPy arrays for equality checks `_, or allows more ways to `plug into the initialization process `_.\n\nFor more details, please refer to our `comparison page `_.\n\n\n.. -getting-help-\n\nGetting Help\n============\n\nPlease use the ``python-attrs`` tag on `Stack Overflow `_ to get help.\n\nAnswering questions of your fellow developers is also a great way to help the project!\n\n\n.. -project-information-\n\nProject Information\n===================\n\n``attrs`` is released under the `MIT `_ license,\nits documentation lives at `Read the Docs `_,\nthe code on `GitHub `_,\nand the latest release on `PyPI `_.\nIt\u2019s rigorously tested on Python 2.7, 3.5+, and PyPy.\n\nWe collect information on **third-party extensions** in our `wiki `_.\nFeel free to browse and add your own!\n\nIf you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide `_ to get you started!\n\n\n``attrs`` for Enterprise\n------------------------\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of ``attrs`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications.\nSave time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.\n`Learn more. `_\n\n\nRelease Information\n===================\n\n21.4.0 (2021-12-29)\n-------------------\n\nChanges\n^^^^^^^\n\n- Fixed the test suite on PyPy3.8 where ``cloudpickle`` does not work.\n `#892 `_\n- Fixed ``coverage report`` for projects that use ``attrs`` and don't set a ``--source``.\n `#895 `_,\n `#896 `_\n\n`Full changelog `_.\n\nCredits\n=======\n\n``attrs`` is written and maintained by `Hynek Schlawack `_.\n\nThe development is kindly supported by `Variomedia AG `_.\n\nA full list of contributors can be found in `GitHub's overview `_.\n\nIt\u2019s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions.\nBoth were inspired by Twisted\u2019s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m\u2019kay?\n\n\n", + "release_date": "2021-12-29T13:15:06", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Hynek Schlawack", + "email": "hs@ox.cx", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Hynek Schlawack", + "email": "hs@ox.cx", + "url": null + } + ], + "keywords": "class,attribute,boilerplate", + "homepage_url": "https://www.attrs.org/", + "download_url": "https://files.pythonhosted.org/packages/be/be/7abce643bfdf8ca01c48afa2ddf8308c2308b0c3b239a44e57d020afa0ef/attrs-21.4.0-py2.py3-none-any.whl", + "size": 60567, + "sha1": null, + "md5": "ad5a10e4dd479f5b1f4b258acf661163", + "sha256": "2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", + "sha512": null, + "bug_tracking_url": "https://github.com/python-attrs/attrs/issues", + "code_view_url": "https://github.com/python-attrs/attrs", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/attrs/21.4.0/json", + "datasource_id": null, + "purl": "pkg:pypi/attrs@21.4.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "attrs", + "version": "21.4.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\n.. image:: https://www.attrs.org/en/stable/_static/attrs_logo.png\n :alt: attrs logo\n :align: center\n\n\n``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder methods `_).\n`Trusted by NASA `_ for Mars missions since 2020!\n\nIts main goal is to help you to write **concise** and **correct** software without slowing down your code.\n\n.. teaser-end\n\nFor that, it gives you a class decorator and a way to declaratively define the attributes on that class:\n\n.. -code-begin-\n\n.. code-block:: pycon\n\n >>> from attrs import asdict, define, make_class, Factory\n\n >>> @define\n ... class SomeClass:\n ... a_number: int = 42\n ... list_of_numbers: list[int] = Factory(list)\n ...\n ... def hard_math(self, another_number):\n ... return self.a_number + sum(self.list_of_numbers) * another_number\n\n\n >>> sc = SomeClass(1, [1, 2, 3])\n >>> sc\n SomeClass(a_number=1, list_of_numbers=[1, 2, 3])\n\n >>> sc.hard_math(3)\n 19\n >>> sc == SomeClass(1, [1, 2, 3])\n True\n >>> sc != SomeClass(2, [3, 2, 1])\n True\n\n >>> asdict(sc)\n {'a_number': 1, 'list_of_numbers': [1, 2, 3]}\n\n >>> SomeClass()\n SomeClass(a_number=42, list_of_numbers=[])\n\n >>> C = make_class(\"C\", [\"a\", \"b\"])\n >>> C(\"foo\", \"bar\")\n C(a='foo', b='bar')\n\n\nAfter *declaring* your attributes ``attrs`` gives you:\n\n- a concise and explicit overview of the class's attributes,\n- a nice human-readable ``__repr__``,\n- a equality-checking methods,\n- an initializer,\n- and much more,\n\n*without* writing dull boilerplate code again and again and *without* runtime performance penalties.\n\n**Hate type annotations**!?\nNo problem!\nTypes are entirely **optional** with ``attrs``.\nSimply assign ``attrs.field()`` to the attributes instead of annotating them with types.\n\n----\n\nThis example uses ``attrs``'s modern APIs that have been introduced in version 20.1.0, and the ``attrs`` package import name that has been added in version 21.3.0.\nThe classic APIs (``@attr.s``, ``attr.ib``, plus their serious business aliases) and the ``attr`` package import name will remain **indefinitely**.\n\nPlease check out `On The Core API Names `_ for a more in-depth explanation.\n\n\nData Classes\n============\n\nOn the tin, ``attrs`` might remind you of ``dataclasses`` (and indeed, ``dataclasses`` are a descendant of ``attrs``).\nIn practice it does a lot more and is more flexible.\nFor instance it allows you to define `special handling of NumPy arrays for equality checks `_, or allows more ways to `plug into the initialization process `_.\n\nFor more details, please refer to our `comparison page `_.\n\n\n.. -getting-help-\n\nGetting Help\n============\n\nPlease use the ``python-attrs`` tag on `Stack Overflow `_ to get help.\n\nAnswering questions of your fellow developers is also a great way to help the project!\n\n\n.. -project-information-\n\nProject Information\n===================\n\n``attrs`` is released under the `MIT `_ license,\nits documentation lives at `Read the Docs `_,\nthe code on `GitHub `_,\nand the latest release on `PyPI `_.\nIt\u2019s rigorously tested on Python 2.7, 3.5+, and PyPy.\n\nWe collect information on **third-party extensions** in our `wiki `_.\nFeel free to browse and add your own!\n\nIf you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide `_ to get you started!\n\n\n``attrs`` for Enterprise\n------------------------\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of ``attrs`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications.\nSave time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.\n`Learn more. `_\n\n\nRelease Information\n===================\n\n21.4.0 (2021-12-29)\n-------------------\n\nChanges\n^^^^^^^\n\n- Fixed the test suite on PyPy3.8 where ``cloudpickle`` does not work.\n `#892 `_\n- Fixed ``coverage report`` for projects that use ``attrs`` and don't set a ``--source``.\n `#895 `_,\n `#896 `_\n\n`Full changelog `_.\n\nCredits\n=======\n\n``attrs`` is written and maintained by `Hynek Schlawack `_.\n\nThe development is kindly supported by `Variomedia AG `_.\n\nA full list of contributors can be found in `GitHub's overview `_.\n\nIt\u2019s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions.\nBoth were inspired by Twisted\u2019s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m\u2019kay?\n\n\n", + "release_date": "2021-12-29T13:15:09", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Hynek Schlawack", + "email": "hs@ox.cx", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Hynek Schlawack", + "email": "hs@ox.cx", + "url": null + } + ], + "keywords": "class,attribute,boilerplate", + "homepage_url": "https://www.attrs.org/", + "download_url": "https://files.pythonhosted.org/packages/d7/77/ebb15fc26d0f815839ecd897b919ed6d85c050feeb83e100e020df9153d2/attrs-21.4.0.tar.gz", + "size": 201839, + "sha1": null, + "md5": "5a9b5e9ceebc380a13fb93235b11bbda", + "sha256": "626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd", + "sha512": null, + "bug_tracking_url": "https://github.com/python-attrs/attrs/issues", + "code_view_url": "https://github.com/python-attrs/attrs", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/attrs/21.4.0/json", + "datasource_id": null, + "purl": "pkg:pypi/attrs@21.4.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "babel", + "version": "2.9.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "A collection of tools for internationalizing Python applications.\n\n\n", + "release_date": "2021-04-28T19:31:38", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://babel.pocoo.org/", + "download_url": "https://files.pythonhosted.org/packages/aa/96/4ba93c5f40459dc850d25f9ba93f869a623e77aaecc7a9344e19c01942cf/Babel-2.9.1-py2.py3-none-any.whl", + "size": 8832555, + "sha1": null, + "md5": "dc992f572b37ba07b36daaba79ca6aed", + "sha256": "ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/babel/2.9.1/json", + "datasource_id": null, + "purl": "pkg:pypi/babel@2.9.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "babel", + "version": "2.9.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "A collection of tools for internationalizing Python applications.\n\n\n", + "release_date": "2021-04-28T19:31:41", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://babel.pocoo.org/", + "download_url": "https://files.pythonhosted.org/packages/17/e6/ec9aa6ac3d00c383a5731cc97ed7c619d3996232c977bb8326bcbb6c687e/Babel-2.9.1.tar.gz", + "size": 8683505, + "sha1": null, + "md5": "7166099733d78aa857d74fa50d8ff58c", + "sha256": "bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/babel/2.9.1/json", + "datasource_id": null, + "purl": "pkg:pypi/babel@2.9.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "backports-functools-lru-cache", + "version": "1.6.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/backports.functools_lru_cache.svg\n :target: `PyPI link`_\n\n.. image:: https://img.shields.io/pypi/pyversions/backports.functools_lru_cache.svg\n :target: `PyPI link`_\n\n.. _PyPI link: https://pypi.org/project/backports.functools_lru_cache\n\n.. image:: https://github.com/jaraco/backports.functools_lru_cache/workflows/tests/badge.svg\n :target: https://github.com/jaraco/backports.functools_lru_cache/actions?query=workflow%3A%22tests%22\n :alt: tests\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/psf/black\n :alt: Code style: Black\n\n.. image:: https://readthedocs.org/projects/backportsfunctools_lru_cache/badge/?version=latest\n :target: https://backportsfunctools_lru_cache.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://tidelift.com/badges/package/pypi/backports.functools_lru_cache\n :target: https://tidelift.com/subscription/pkg/pypi-backports.functools_lru_cache?utm_source=pypi-backports.functools_lru_cache&utm_medium=readme\n\nBackport of functools.lru_cache from Python 3.3 as published at `ActiveState\n`_.\n\nUsage\n=====\n\nConsider using this technique for importing the 'lru_cache' function::\n\n try:\n from functools import lru_cache\n except ImportError:\n from backports.functools_lru_cache import lru_cache\n\n\nFor Enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nThis project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.\n\n`Learn more `_.\n\nSecurity Contact\n================\n\nTo report a security vulnerability, please use the\n`Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\n\n", + "release_date": "2021-04-11T19:31:38", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Raymond Hettinger", + "email": "raymond.hettinger@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jaraco/backports.functools_lru_cache", + "download_url": "https://files.pythonhosted.org/packages/e5/c1/1a48a4bb9b515480d6c666977eeca9243be9fa9e6fb5a34be0ad9627f737/backports.functools_lru_cache-1.6.4-py2.py3-none-any.whl", + "size": 5922, + "sha1": null, + "md5": "1794e5b35e6abf85c7c611091fec8286", + "sha256": "dbead04b9daa817909ec64e8d2855fb78feafe0b901d4568758e3a60559d8978", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/backports-functools-lru-cache/1.6.4/json", + "datasource_id": null, + "purl": "pkg:pypi/backports-functools-lru-cache@1.6.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "backports-functools-lru-cache", + "version": "1.6.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/backports.functools_lru_cache.svg\n :target: `PyPI link`_\n\n.. image:: https://img.shields.io/pypi/pyversions/backports.functools_lru_cache.svg\n :target: `PyPI link`_\n\n.. _PyPI link: https://pypi.org/project/backports.functools_lru_cache\n\n.. image:: https://github.com/jaraco/backports.functools_lru_cache/workflows/tests/badge.svg\n :target: https://github.com/jaraco/backports.functools_lru_cache/actions?query=workflow%3A%22tests%22\n :alt: tests\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/psf/black\n :alt: Code style: Black\n\n.. image:: https://readthedocs.org/projects/backportsfunctools_lru_cache/badge/?version=latest\n :target: https://backportsfunctools_lru_cache.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://tidelift.com/badges/package/pypi/backports.functools_lru_cache\n :target: https://tidelift.com/subscription/pkg/pypi-backports.functools_lru_cache?utm_source=pypi-backports.functools_lru_cache&utm_medium=readme\n\nBackport of functools.lru_cache from Python 3.3 as published at `ActiveState\n`_.\n\nUsage\n=====\n\nConsider using this technique for importing the 'lru_cache' function::\n\n try:\n from functools import lru_cache\n except ImportError:\n from backports.functools_lru_cache import lru_cache\n\n\nFor Enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nThis project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.\n\n`Learn more `_.\n\nSecurity Contact\n================\n\nTo report a security vulnerability, please use the\n`Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\n\n", + "release_date": "2021-04-11T19:31:40", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Raymond Hettinger", + "email": "raymond.hettinger@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jaraco/backports.functools_lru_cache", + "download_url": "https://files.pythonhosted.org/packages/95/9f/122a41912932c77d5b8e6cab6bd456e6270211a3ed7248a80c235179a012/backports.functools_lru_cache-1.6.4.tar.gz", + "size": 13904, + "sha1": null, + "md5": "8fed424f30bf9554235aa02997b7574c", + "sha256": "d5ed2169378b67d3c545e5600d363a923b09c456dab1593914935a68ad478271", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/backports-functools-lru-cache/1.6.4/json", + "datasource_id": null, + "purl": "pkg:pypi/backports-functools-lru-cache@1.6.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "backports-shutil-get-terminal-size", + "version": "1.0.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "backports.shutil_get_terminal_size\r\n==================================\r\n\r\nA backport of the `get_terminal_size`_ function from Python 3.3's shutil.\r\n\r\nUnlike the original version it is written in pure Python rather than C,\r\nso it might be a tiny bit slower.\r\n\r\n.. _get_terminal_size: https://docs.python.org/3/library/shutil.html#shutil.get_terminal_size\r\n\r\n\r\nExample usage\r\n-------------\r\n\r\n::\r\n\r\n >>> from backports.shutil_get_terminal_size import get_terminal_size\r\n >>> get_terminal_size()\r\n terminal_size(columns=105, lines=33)\r\n\r\n\r\nHistory\r\n=======\r\n\r\n1.0.0 (2014-08-19)\r\n------------------\r\n\r\nFirst release.", + "release_date": "2014-08-19T18:42:51", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Christopher Rosell", + "email": "chrippa@tanuki.se", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/chrippa/backports.shutil_get_terminal_size", + "download_url": "https://files.pythonhosted.org/packages/7d/cd/1750d6c35fe86d35f8562091737907f234b78fdffab42b29c72b1dd861f4/backports.shutil_get_terminal_size-1.0.0-py2.py3-none-any.whl", + "size": 6497, + "sha1": null, + "md5": "5ca283ed87cdba75602d1f276b2519a1", + "sha256": "0975ba55054c15e346944b38956a4c9cbee9009391e41b86c68990effb8c1f64", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/backports-shutil-get-terminal-size/1.0.0/json", + "datasource_id": null, + "purl": "pkg:pypi/backports-shutil-get-terminal-size@1.0.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "backports-shutil-get-terminal-size", + "version": "1.0.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "backports.shutil_get_terminal_size\r\n==================================\r\n\r\nA backport of the `get_terminal_size`_ function from Python 3.3's shutil.\r\n\r\nUnlike the original version it is written in pure Python rather than C,\r\nso it might be a tiny bit slower.\r\n\r\n.. _get_terminal_size: https://docs.python.org/3/library/shutil.html#shutil.get_terminal_size\r\n\r\n\r\nExample usage\r\n-------------\r\n\r\n::\r\n\r\n >>> from backports.shutil_get_terminal_size import get_terminal_size\r\n >>> get_terminal_size()\r\n terminal_size(columns=105, lines=33)\r\n\r\n\r\nHistory\r\n=======\r\n\r\n1.0.0 (2014-08-19)\r\n------------------\r\n\r\nFirst release.", + "release_date": "2014-08-19T18:42:49", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Christopher Rosell", + "email": "chrippa@tanuki.se", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/chrippa/backports.shutil_get_terminal_size", + "download_url": "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz", + "size": 4279, + "sha1": null, + "md5": "03267762480bd86b50580dc19dff3c66", + "sha256": "713e7a8228ae80341c70586d1cc0a8caa5207346927e23d09dcbcaf18eadec80", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/backports-shutil-get-terminal-size/1.0.0/json", + "datasource_id": null, + "purl": "pkg:pypi/backports-shutil-get-terminal-size@1.0.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "billiard", + "version": "3.6.4.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "========\nbilliard\n========\n:version: 3.6.4.0\n\n|build-status-lin| |build-status-win| |license| |wheel| |pyversion| |pyimp|\n\n.. |build-status-lin| image:: https://secure.travis-ci.org/celery/billiard.png?branch=master\n :alt: Build status on Linux\n :target: https://travis-ci.org/celery/billiard\n\n.. |build-status-win| image:: https://ci.appveyor.com/api/projects/status/github/celery/billiard?png=true&branch=master\n :alt: Build status on Windows\n :target: https://ci.appveyor.com/project/ask/billiard\n\n.. |license| image:: https://img.shields.io/pypi/l/billiard.svg\n :alt: BSD License\n :target: https://opensource.org/licenses/BSD-3-Clause\n\n.. |wheel| image:: https://img.shields.io/pypi/wheel/billiard.svg\n :alt: Billiard can be installed via wheel\n :target: https://pypi.org/project/billiard/\n\n.. |pyversion| image:: https://img.shields.io/pypi/pyversions/billiard.svg\n :alt: Supported Python versions.\n :target: https://pypi.org/project/billiard/\n\n.. |pyimp| image:: https://img.shields.io/pypi/implementation/billiard.svg\n :alt: Support Python implementations.\n :target: https://pypi.org/project/billiard/\n\nAbout\n-----\n\n``billiard`` is a fork of the Python 2.7 `multiprocessing `_\npackage. The multiprocessing package itself is a renamed and updated version of\nR Oudkerk's `pyprocessing `_ package.\nThis standalone variant draws its fixes/improvements from python-trunk and provides\nadditional bug fixes and improvements.\n\n- This package would not be possible if not for the contributions of not only\n the current maintainers but all of the contributors to the original pyprocessing\n package listed `here `_.\n\n- Also, it is a fork of the multiprocessing backport package by Christian Heims.\n\n- It includes the no-execv patch contributed by R. Oudkerk.\n\n- And the Pool improvements previously located in `Celery`_.\n\n- Billiard is used in and is a dependency for `Celery`_ and is maintained by the\n Celery team.\n\n.. _`Celery`: http://celeryproject.org\n\nDocumentation\n-------------\n\nThe documentation for ``billiard`` is available on `Read the Docs `_.\n\nBug reporting\n-------------\n\nPlease report bugs related to multiprocessing at the\n`Python bug tracker `_. Issues related to billiard\nshould be reported at https://github.com/celery/billiard/issues.\n\nbilliard is part of the Tidelift Subscription\n---------------------------------------------\n\nThe maintainers of ``billiard`` and thousands of other packages are working\nwith Tidelift to deliver commercial support and maintenance for the open source\ndependencies you use to build your applications. Save time, reduce risk, and\nimprove code health, while paying the maintainers of the exact dependencies you\nuse. `Learn more`_.\n\n.. _`Learn more`: https://tidelift.com/subscription/pkg/pypi-billiard?utm_source=pypi-billiard&utm_medium=referral&utm_campaign=readme&utm_term=repo\n\n\n", + "release_date": "2021-04-01T09:23:50", + "parties": [ + { + "type": "person", + "role": "author", + "name": "R Oudkerk / Python Software Foundation", + "email": "python-dev@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Asif Saif Uddin", + "email": "auvipy@gmail.com", + "url": null + } + ], + "keywords": "multiprocessing pool process", + "homepage_url": "https://github.com/celery/billiard", + "download_url": "https://files.pythonhosted.org/packages/92/91/40de1901da8ec9eeb7c6a22143ba5d55d8aaa790761ca31342cedcd5c793/billiard-3.6.4.0.tar.gz", + "size": 155303, + "sha1": null, + "md5": "b49503b8a78743dcb6a86accea379357", + "sha256": "299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/billiard/3.6.4.0/json", + "datasource_id": null, + "purl": "pkg:pypi/billiard@3.6.4.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "blinker", + "version": "1.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Blinker\n=======\n\nBlinker provides a fast dispatching system that allows any number of\ninterested parties to subscribe to events, or \"signals\".\n\nSignal receivers can subscribe to specific senders or receive signals\nsent by any sender.\n\n.. code-block:: pycon\n\n >>> from blinker import signal\n >>> started = signal('round-started')\n >>> def each(round):\n ... print \"Round %s!\" % round\n ...\n >>> started.connect(each)\n\n >>> def round_two(round):\n ... print \"This is round two.\"\n ...\n >>> started.connect(round_two, sender=2)\n\n >>> for round in range(1, 4):\n ... started.send(round)\n ...\n Round 1!\n Round 2!\n This is round two.\n Round 3!\n\n\nLinks\n-----\n\n- Documentation: https://blinker.readthedocs.io/\n- Changes: https://blinker.readthedocs.io/#changes\n- PyPI Releases: https://pypi.org/project/blinker/\n- Source Code: https://github.com/pallets-eco/blinker/\n- Issue Tracker: https://github.com/pallets-eco/blinker/issues/\n", + "release_date": "2022-07-17T17:40:02", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason Kirtland", + "email": "jek@discorporate.us", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets Ecosystem", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": "signal emit events broadcast", + "homepage_url": "https://blinker.readthedocs.io", + "download_url": "https://files.pythonhosted.org/packages/30/41/caa5da2dbe6d26029dfe11d31dfa8132b4d6d30b6d6b61a24824075a5f06/blinker-1.5-py2.py3-none-any.whl", + "size": 12529, + "sha1": null, + "md5": "b1303e0bd4d64f34cb7e9be8214a4522", + "sha256": "1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets-eco/blinker", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/blinker/1.5/json", + "datasource_id": null, + "purl": "pkg:pypi/blinker@1.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "blinker", + "version": "1.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Blinker\n=======\n\nBlinker provides a fast dispatching system that allows any number of\ninterested parties to subscribe to events, or \"signals\".\n\nSignal receivers can subscribe to specific senders or receive signals\nsent by any sender.\n\n.. code-block:: pycon\n\n >>> from blinker import signal\n >>> started = signal('round-started')\n >>> def each(round):\n ... print \"Round %s!\" % round\n ...\n >>> started.connect(each)\n\n >>> def round_two(round):\n ... print \"This is round two.\"\n ...\n >>> started.connect(round_two, sender=2)\n\n >>> for round in range(1, 4):\n ... started.send(round)\n ...\n Round 1!\n Round 2!\n This is round two.\n Round 3!\n\n\nLinks\n-----\n\n- Documentation: https://blinker.readthedocs.io/\n- Changes: https://blinker.readthedocs.io/#changes\n- PyPI Releases: https://pypi.org/project/blinker/\n- Source Code: https://github.com/pallets-eco/blinker/\n- Issue Tracker: https://github.com/pallets-eco/blinker/issues/\n", + "release_date": "2022-07-17T17:40:05", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason Kirtland", + "email": "jek@discorporate.us", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets Ecosystem", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": "signal emit events broadcast", + "homepage_url": "https://blinker.readthedocs.io", + "download_url": "https://files.pythonhosted.org/packages/2b/12/82786486cefb68685bb1c151730f510b0f4e5d621d77f245bc0daf9a6c64/blinker-1.5.tar.gz", + "size": 27022, + "sha1": null, + "md5": "e1c3eec8e52210f69ef59d299c6cca07", + "sha256": "923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets-eco/blinker", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/blinker/1.5/json", + "datasource_id": null, + "purl": "pkg:pypi/blinker@1.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "cairocffi", + "version": "0.9.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "cairocffi\n=========\n\ncairocffi is a `CFFI`_-based drop-in replacement for Pycairo_,\na set of Python bindings and object-oriented API for cairo_.\nCairo is a 2D vector graphics library with support for multiple backends\nincluding image buffers, PNG, PostScript, PDF, and SVG file output.\n\nAdditionally, the :mod:`cairocffi.pixbuf` module uses GDK-PixBuf_\nto decode various image formats for use in cairo.\n\n.. _CFFI: https://cffi.readthedocs.org/\n.. _Pycairo: https://pycairo.readthedocs.io/\n.. _cairo: http://cairographics.org/\n.. _GDK-PixBuf: https://gitlab.gnome.org/GNOME/gdk-pixbuf\n\n* `Latest documentation `_\n* `Source code and issue tracker `_\n on GitHub.\n* Install with ``pip install cairocffi``\n* Python 2.6, 2.7 and 3.4+. `Tested with CPython, PyPy and PyPy3\n `_.\n* API partially compatible with Pycairo.\n* Works with any version of cairo.\n", + "release_date": "2018-08-06T15:48:20", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Simon Sapin", + "email": "simon.sapin@exyr.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/SimonSapin/cairocffi", + "download_url": "https://files.pythonhosted.org/packages/62/be/ad4d422b6f38d99b09ad6d046ab725e8ccac5fefd9ca256ca35a80dbf3c6/cairocffi-0.9.0.tar.gz", + "size": 84652, + "sha1": null, + "md5": "6022aadfba3b1316a1fdd57adf1e7392", + "sha256": "15386c3a9e08823d6826c4491eaccc7b7254b1dc587a3b9ce60c350c3f990337", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/cairocffi/0.9.0/json", + "datasource_id": null, + "purl": "pkg:pypi/cairocffi@0.9.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "cairosvg", + "version": "1.0.22", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "CairoSVG - A Simple SVG Converter for Cairo\n===========================================\n\nCairoSVG is a SVG converter based on Cairo. It can export SVG files to PDF,\nPostScript and PNG files.\n\nFor further information, please visit the `CairoSVG Website\n`_.", + "release_date": "2016-06-16T11:50:38", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Kozea", + "email": "guillaume.ayoub@kozea.fr", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": null, + "email": null, + "url": null + } + ], + "keywords": "svg,cairo,pdf,png,postscript", + "homepage_url": "http://www.cairosvg.org/", + "download_url": "https://files.pythonhosted.org/packages/d9/9b/c241990c86faa9e52a01f0570bba4247ba0f3d66eae2607e179cb9ae773a/CairoSVG-1.0.22.tar.gz", + "size": 30896, + "sha1": null, + "md5": "3f68e59cfe0576de7af6c99e8cf7eb18", + "sha256": "f66e0f3a2711d2e36952bb370fcd45837bfedce2f7882935c46c45c018a21557", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "GNU LGPL v3+", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/cairosvg/1.0.22/json", + "datasource_id": null, + "purl": "pkg:pypi/cairosvg@1.0.22" + }, + { + "type": "pypi", + "namespace": null, + "name": "celery", + "version": "4.4.7", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: http://docs.celeryproject.org/en/latest/_images/celery-banner-small.png\n\n|build-status| |coverage| |license| |wheel| |pyversion| |pyimp| |ocbackerbadge| |ocsponsorbadge|\n\n:Version: 4.4.7 (cliffs)\n:Web: http://celeryproject.org/\n:Download: https://pypi.org/project/celery/\n:Source: https://github.com/celery/celery/\n:Keywords: task, queue, job, async, rabbitmq, amqp, redis,\n python, distributed, actors\n\nDonations\n=========\n\nThis project relies on your generous donations.\n\nIf you are using Celery to create a commercial product, please consider becoming our `backer`_ or our `sponsor`_ to ensure Celery's future.\n\n.. _`backer`: https://opencollective.com/celery#backer\n.. _`sponsor`: https://opencollective.com/celery#sponsor\n\nFor enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of ``celery`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. `Learn more. `_\n\nWhat's a Task Queue?\n====================\n\nTask queues are used as a mechanism to distribute work across threads or\nmachines.\n\nA task queue's input is a unit of work, called a task, dedicated worker\nprocesses then constantly monitor the queue for new work to perform.\n\nCelery communicates via messages, usually using a broker\nto mediate between clients and workers. To initiate a task a client puts a\nmessage on the queue, the broker then delivers the message to a worker.\n\nA Celery system can consist of multiple workers and brokers, giving way\nto high availability and horizontal scaling.\n\nCelery is written in Python, but the protocol can be implemented in any\nlanguage. In addition to Python there's node-celery_ for Node.js,\na `PHP client`_, `gocelery`_ for golang, and rusty-celery_ for Rust.\n\nLanguage interoperability can also be achieved by using webhooks\nin such a way that the client enqueues an URL to be requested by a worker.\n\n.. _node-celery: https://github.com/mher/node-celery\n.. _`PHP client`: https://github.com/gjedeer/celery-php\n.. _`gocelery`: https://github.com/gocelery/gocelery\n.. _rusty-celery: https://github.com/rusty-celery/rusty-celery\n\nWhat do I need?\n===============\n\nCelery version 4.4.0 runs on,\n\n- Python (2.7, 3.5, 3.6, 3.7, 3.8)\n- PyPy2.7 (7.2)\n- PyPy3.5 (7.1)\n- PyPy3.6 (7.6)\n\n\n4.x.x is the last version to support Python 2.7,\nand from the next major version (Celery 5.x) Python 3.6 or newer is required.\n\nIf you're running an older version of Python, you need to be running\nan older version of Celery:\n\n- Python 2.6: Celery series 3.1 or earlier.\n- Python 2.5: Celery series 3.0 or earlier.\n- Python 2.4 was Celery series 2.2 or earlier.\n\nCelery is a project with minimal funding,\nso we don't support Microsoft Windows.\nPlease don't open any issues related to that platform.\n\n*Celery* is usually used with a message broker to send and receive messages.\nThe RabbitMQ, Redis transports are feature complete,\nbut there's also experimental support for a myriad of other solutions, including\nusing SQLite for local development.\n\n*Celery* can run on a single machine, on multiple machines, or even\nacross datacenters.\n\nGet Started\n===========\n\nIf this is the first time you're trying to use Celery, or you're\nnew to Celery 4.4 coming from previous versions then you should read our\ngetting started tutorials:\n\n- `First steps with Celery`_\n\n Tutorial teaching you the bare minimum needed to get started with Celery.\n\n- `Next steps`_\n\n A more complete overview, showing more features.\n\n.. _`First steps with Celery`:\n http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html\n\n.. _`Next steps`:\n http://docs.celeryproject.org/en/latest/getting-started/next-steps.html\n\nCelery is...\n=============\n\n- **Simple**\n\n Celery is easy to use and maintain, and does *not need configuration files*.\n\n It has an active, friendly community you can talk to for support,\n like at our `mailing-list`_, or the IRC channel.\n\n Here's one of the simplest applications you can make::\n\n from celery import Celery\n\n app = Celery('hello', broker='amqp://guest@localhost//')\n\n @app.task\n def hello():\n return 'hello world'\n\n- **Highly Available**\n\n Workers and clients will automatically retry in the event\n of connection loss or failure, and some brokers support\n HA in way of *Primary/Primary* or *Primary/Replica* replication.\n\n- **Fast**\n\n A single Celery process can process millions of tasks a minute,\n with sub-millisecond round-trip latency (using RabbitMQ,\n py-librabbitmq, and optimized settings).\n\n- **Flexible**\n\n Almost every part of *Celery* can be extended or used on its own,\n Custom pool implementations, serializers, compression schemes, logging,\n schedulers, consumers, producers, broker transports, and much more.\n\nIt supports...\n================\n\n - **Message Transports**\n\n - RabbitMQ_, Redis_, Amazon SQS\n\n - **Concurrency**\n\n - Prefork, Eventlet_, gevent_, single threaded (``solo``)\n\n - **Result Stores**\n\n - AMQP, Redis\n - memcached\n - SQLAlchemy, Django ORM\n - Apache Cassandra, IronCache, Elasticsearch\n\n - **Serialization**\n\n - *pickle*, *json*, *yaml*, *msgpack*.\n - *zlib*, *bzip2* compression.\n - Cryptographic message signing.\n\n.. _`Eventlet`: http://eventlet.net/\n.. _`gevent`: http://gevent.org/\n\n.. _RabbitMQ: https://rabbitmq.com\n.. _Redis: https://redis.io\n.. _SQLAlchemy: http://sqlalchemy.org\n\nFramework Integration\n=====================\n\nCelery is easy to integrate with web frameworks, some of which even have\nintegration packages:\n\n +--------------------+------------------------+\n | `Django`_ | not needed |\n +--------------------+------------------------+\n | `Pyramid`_ | `pyramid_celery`_ |\n +--------------------+------------------------+\n | `Pylons`_ | `celery-pylons`_ |\n +--------------------+------------------------+\n | `Flask`_ | not needed |\n +--------------------+------------------------+\n | `web2py`_ | `web2py-celery`_ |\n +--------------------+------------------------+\n | `Tornado`_ | `tornado-celery`_ |\n +--------------------+------------------------+\n\nThe integration packages aren't strictly necessary, but they can make\ndevelopment easier, and sometimes they add important hooks like closing\ndatabase connections at ``fork``.\n\n.. _`Django`: https://djangoproject.com/\n.. _`Pylons`: http://pylonsproject.org/\n.. _`Flask`: http://flask.pocoo.org/\n.. _`web2py`: http://web2py.com/\n.. _`Bottle`: https://bottlepy.org/\n.. _`Pyramid`: http://docs.pylonsproject.org/en/latest/docs/pyramid.html\n.. _`pyramid_celery`: https://pypi.org/project/pyramid_celery/\n.. _`celery-pylons`: https://pypi.org/project/celery-pylons/\n.. _`web2py-celery`: https://code.google.com/p/web2py-celery/\n.. _`Tornado`: http://www.tornadoweb.org/\n.. _`tornado-celery`: https://github.com/mher/tornado-celery/\n\n.. _celery-documentation:\n\nDocumentation\n=============\n\nThe `latest documentation`_ is hosted at Read The Docs, containing user guides,\ntutorials, and an API reference.\n\n\u6700\u65b0\u7684\u4e2d\u6587\u6587\u6863\u6258\u7ba1\u5728 https://www.celerycn.io/ \u4e2d\uff0c\u5305\u542b\u7528\u6237\u6307\u5357\u3001\u6559\u7a0b\u3001API\u63a5\u53e3\u7b49\u3002\n\n.. _`latest documentation`: http://docs.celeryproject.org/en/latest/\n\n.. _celery-installation:\n\nInstallation\n============\n\nYou can install Celery either via the Python Package Index (PyPI)\nor from source.\n\nTo install using ``pip``:\n\n::\n\n\n $ pip install -U Celery\n\n.. _bundles:\n\nBundles\n-------\n\nCelery also defines a group of bundles that can be used\nto install Celery and the dependencies for a given feature.\n\nYou can specify these in your requirements or on the ``pip``\ncommand-line by using brackets. Multiple bundles can be specified by\nseparating them by commas.\n\n::\n\n\n $ pip install \"celery[librabbitmq]\"\n\n $ pip install \"celery[librabbitmq,redis,auth,msgpack]\"\n\nThe following bundles are available:\n\nSerializers\n~~~~~~~~~~~\n\n:``celery[auth]``:\n for using the ``auth`` security serializer.\n\n:``celery[msgpack]``:\n for using the msgpack serializer.\n\n:``celery[yaml]``:\n for using the yaml serializer.\n\nConcurrency\n~~~~~~~~~~~\n\n:``celery[eventlet]``:\n for using the ``eventlet`` pool.\n\n:``celery[gevent]``:\n for using the ``gevent`` pool.\n\nTransports and Backends\n~~~~~~~~~~~~~~~~~~~~~~~\n\n:``celery[librabbitmq]``:\n for using the librabbitmq C library.\n\n:``celery[redis]``:\n for using Redis as a message transport or as a result backend.\n\n:``celery[sqs]``:\n for using Amazon SQS as a message transport.\n\n:``celery[tblib``]:\n for using the ``task_remote_tracebacks`` feature.\n\n:``celery[memcache]``:\n for using Memcached as a result backend (using ``pylibmc``)\n\n:``celery[pymemcache]``:\n for using Memcached as a result backend (pure-Python implementation).\n\n:``celery[cassandra]``:\n for using Apache Cassandra as a result backend with DataStax driver.\n\n:``celery[azureblockblob]``:\n for using Azure Storage as a result backend (using ``azure-storage``)\n\n:``celery[s3]``:\n for using S3 Storage as a result backend.\n\n:``celery[couchbase]``:\n for using Couchbase as a result backend.\n\n:``celery[arangodb]``:\n for using ArangoDB as a result backend.\n\n:``celery[elasticsearch]``:\n for using Elasticsearch as a result backend.\n\n:``celery[riak]``:\n for using Riak as a result backend.\n\n:``celery[cosmosdbsql]``:\n for using Azure Cosmos DB as a result backend (using ``pydocumentdb``)\n\n:``celery[zookeeper]``:\n for using Zookeeper as a message transport.\n\n:``celery[sqlalchemy]``:\n for using SQLAlchemy as a result backend (*supported*).\n\n:``celery[pyro]``:\n for using the Pyro4 message transport (*experimental*).\n\n:``celery[slmq]``:\n for using the SoftLayer Message Queue transport (*experimental*).\n\n:``celery[consul]``:\n for using the Consul.io Key/Value store as a message transport or result backend (*experimental*).\n\n:``celery[django]``:\n specifies the lowest version possible for Django support.\n\n You should probably not use this in your requirements, it's here\n for informational purposes only.\n\n\n.. _celery-installing-from-source:\n\nDownloading and installing from source\n--------------------------------------\n\nDownload the latest version of Celery from PyPI:\n\nhttps://pypi.org/project/celery/\n\nYou can install it by doing the following,:\n\n::\n\n\n $ tar xvfz celery-0.0.0.tar.gz\n $ cd celery-0.0.0\n $ python setup.py build\n # python setup.py install\n\nThe last command must be executed as a privileged user if\nyou aren't currently using a virtualenv.\n\n.. _celery-installing-from-git:\n\nUsing the development version\n-----------------------------\n\nWith pip\n~~~~~~~~\n\nThe Celery development version also requires the development\nversions of ``kombu``, ``amqp``, ``billiard``, and ``vine``.\n\nYou can install the latest snapshot of these using the following\npip commands:\n\n::\n\n\n $ pip install https://github.com/celery/celery/zipball/master#egg=celery\n $ pip install https://github.com/celery/billiard/zipball/master#egg=billiard\n $ pip install https://github.com/celery/py-amqp/zipball/master#egg=amqp\n $ pip install https://github.com/celery/kombu/zipball/master#egg=kombu\n $ pip install https://github.com/celery/vine/zipball/master#egg=vine\n\nWith git\n~~~~~~~~\n\nPlease see the Contributing section.\n\n.. _getting-help:\n\nGetting Help\n============\n\n.. _mailing-list:\n\nMailing list\n------------\n\nFor discussions about the usage, development, and future of Celery,\nplease join the `celery-users`_ mailing list.\n\n.. _`celery-users`: https://groups.google.com/group/celery-users/\n\n.. _irc-channel:\n\nIRC\n---\n\nCome chat with us on IRC. The **#celery** channel is located at the `Freenode`_\nnetwork.\n\n.. _`Freenode`: https://freenode.net\n\n.. _bug-tracker:\n\nBug tracker\n===========\n\nIf you have any suggestions, bug reports, or annoyances please report them\nto our issue tracker at https://github.com/celery/celery/issues/\n\n.. _wiki:\n\nWiki\n====\n\nhttps://github.com/celery/celery/wiki\n\nCredits\n=======\n\n.. _contributing-short:\n\nContributors\n------------\n\nThis project exists thanks to all the people who contribute. Development of\n`celery` happens at GitHub: https://github.com/celery/celery\n\nYou're highly encouraged to participate in the development\nof `celery`. If you don't like GitHub (for some reason) you're welcome\nto send regular patches.\n\nBe sure to also read the `Contributing to Celery`_ section in the\ndocumentation.\n\n.. _`Contributing to Celery`:\n http://docs.celeryproject.org/en/master/contributing.html\n\n|oc-contributors|\n\n.. |oc-contributors| image:: https://opencollective.com/celery/contributors.svg?width=890&button=false\n :target: https://github.com/celery/celery/graphs/contributors\n\nBackers\n-------\n\nThank you to all our backers! \ud83d\ude4f [`Become a backer`_]\n\n.. _`Become a backer`: https://opencollective.com/celery#backer\n\n|oc-backers|\n\n.. |oc-backers| image:: https://opencollective.com/celery/backers.svg?width=890\n :target: https://opencollective.com/celery#backers\n\nSponsors\n--------\n\nSupport this project by becoming a sponsor. Your logo will show up here with a\nlink to your website. [`Become a sponsor`_]\n\n.. _`Become a sponsor`: https://opencollective.com/celery#sponsor\n\n|oc-sponsors|\n\n.. |oc-sponsors| image:: https://opencollective.com/celery/sponsor/0/avatar.svg\n :target: https://opencollective.com/celery/sponsor/0/website\n\n.. _license:\n\nLicense\n=======\n\nThis software is licensed under the `New BSD License`. See the ``LICENSE``\nfile in the top distribution directory for the full license text.\n\n.. # vim: syntax=rst expandtab tabstop=4 shiftwidth=4 shiftround\n\n.. |build-status| image:: https://secure.travis-ci.org/celery/celery.png?branch=master\n :alt: Build status\n :target: https://travis-ci.org/celery/celery\n\n.. |coverage| image:: https://codecov.io/github/celery/celery/coverage.svg?branch=master\n :target: https://codecov.io/github/celery/celery?branch=master\n\n.. |license| image:: https://img.shields.io/pypi/l/celery.svg\n :alt: BSD License\n :target: https://opensource.org/licenses/BSD-3-Clause\n\n.. |wheel| image:: https://img.shields.io/pypi/wheel/celery.svg\n :alt: Celery can be installed via wheel\n :target: https://pypi.org/project/celery/\n\n.. |pyversion| image:: https://img.shields.io/pypi/pyversions/celery.svg\n :alt: Supported Python versions.\n :target: https://pypi.org/project/celery/\n\n.. |pyimp| image:: https://img.shields.io/pypi/implementation/celery.svg\n :alt: Support Python implementations.\n :target: https://pypi.org/project/celery/\n\n.. |ocbackerbadge| image:: https://opencollective.com/celery/backers/badge.svg\n :alt: Backers on Open Collective\n :target: #backers\n\n.. |ocsponsorbadge| image:: https://opencollective.com/celery/sponsors/badge.svg\n :alt: Sponsors on Open Collective\n :target: #sponsors\n\n.. |downloads| image:: https://pepy.tech/badge/celery\n :alt: Downloads\n :target: https://pepy.tech/project/celery\n\n\n", + "release_date": "2020-07-31T17:41:39", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ask Solem", + "email": "auvipy@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } ], - "wheel_urls": [ - "https://files.pythonhosted.org/packages/96/0a/67556e9b7782df7118c1f49bdc494da5e5e429c93aa77965f33e81287c8c/zipp-1.2.0-py2.py3-none-any.whl" + "keywords": "task job queue distributed messaging actor", + "homepage_url": "http://celeryproject.org", + "download_url": "https://files.pythonhosted.org/packages/c8/0c/609e3611d20c9f8d883852d1be5516671f630fb08c8c1e56911567dfba7b/celery-4.4.7-py2.py3-none-any.whl", + "size": 427577, + "sha1": null, + "md5": "47e3db7a5255406eda40645cf6d62409", + "sha256": "a92e1d56e650781fb747032a3997d16236d037c8199eacd5217d1a72893bca45", + "sha512": null, + "bug_tracking_url": "https://github.com/celery/celery/issues", + "code_view_url": "https://github.com/celery/celery", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/celery/4.4.7/json", + "datasource_id": null, + "purl": "pkg:pypi/celery@4.4.7" + }, + { + "type": "pypi", + "namespace": null, + "name": "celery", + "version": "4.4.7", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: http://docs.celeryproject.org/en/latest/_images/celery-banner-small.png\n\n|build-status| |coverage| |license| |wheel| |pyversion| |pyimp| |ocbackerbadge| |ocsponsorbadge|\n\n:Version: 4.4.7 (cliffs)\n:Web: http://celeryproject.org/\n:Download: https://pypi.org/project/celery/\n:Source: https://github.com/celery/celery/\n:Keywords: task, queue, job, async, rabbitmq, amqp, redis,\n python, distributed, actors\n\nDonations\n=========\n\nThis project relies on your generous donations.\n\nIf you are using Celery to create a commercial product, please consider becoming our `backer`_ or our `sponsor`_ to ensure Celery's future.\n\n.. _`backer`: https://opencollective.com/celery#backer\n.. _`sponsor`: https://opencollective.com/celery#sponsor\n\nFor enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of ``celery`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. `Learn more. `_\n\nWhat's a Task Queue?\n====================\n\nTask queues are used as a mechanism to distribute work across threads or\nmachines.\n\nA task queue's input is a unit of work, called a task, dedicated worker\nprocesses then constantly monitor the queue for new work to perform.\n\nCelery communicates via messages, usually using a broker\nto mediate between clients and workers. To initiate a task a client puts a\nmessage on the queue, the broker then delivers the message to a worker.\n\nA Celery system can consist of multiple workers and brokers, giving way\nto high availability and horizontal scaling.\n\nCelery is written in Python, but the protocol can be implemented in any\nlanguage. In addition to Python there's node-celery_ for Node.js,\na `PHP client`_, `gocelery`_ for golang, and rusty-celery_ for Rust.\n\nLanguage interoperability can also be achieved by using webhooks\nin such a way that the client enqueues an URL to be requested by a worker.\n\n.. _node-celery: https://github.com/mher/node-celery\n.. _`PHP client`: https://github.com/gjedeer/celery-php\n.. _`gocelery`: https://github.com/gocelery/gocelery\n.. _rusty-celery: https://github.com/rusty-celery/rusty-celery\n\nWhat do I need?\n===============\n\nCelery version 4.4.0 runs on,\n\n- Python (2.7, 3.5, 3.6, 3.7, 3.8)\n- PyPy2.7 (7.2)\n- PyPy3.5 (7.1)\n- PyPy3.6 (7.6)\n\n\n4.x.x is the last version to support Python 2.7,\nand from the next major version (Celery 5.x) Python 3.6 or newer is required.\n\nIf you're running an older version of Python, you need to be running\nan older version of Celery:\n\n- Python 2.6: Celery series 3.1 or earlier.\n- Python 2.5: Celery series 3.0 or earlier.\n- Python 2.4 was Celery series 2.2 or earlier.\n\nCelery is a project with minimal funding,\nso we don't support Microsoft Windows.\nPlease don't open any issues related to that platform.\n\n*Celery* is usually used with a message broker to send and receive messages.\nThe RabbitMQ, Redis transports are feature complete,\nbut there's also experimental support for a myriad of other solutions, including\nusing SQLite for local development.\n\n*Celery* can run on a single machine, on multiple machines, or even\nacross datacenters.\n\nGet Started\n===========\n\nIf this is the first time you're trying to use Celery, or you're\nnew to Celery 4.4 coming from previous versions then you should read our\ngetting started tutorials:\n\n- `First steps with Celery`_\n\n Tutorial teaching you the bare minimum needed to get started with Celery.\n\n- `Next steps`_\n\n A more complete overview, showing more features.\n\n.. _`First steps with Celery`:\n http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html\n\n.. _`Next steps`:\n http://docs.celeryproject.org/en/latest/getting-started/next-steps.html\n\nCelery is...\n=============\n\n- **Simple**\n\n Celery is easy to use and maintain, and does *not need configuration files*.\n\n It has an active, friendly community you can talk to for support,\n like at our `mailing-list`_, or the IRC channel.\n\n Here's one of the simplest applications you can make::\n\n from celery import Celery\n\n app = Celery('hello', broker='amqp://guest@localhost//')\n\n @app.task\n def hello():\n return 'hello world'\n\n- **Highly Available**\n\n Workers and clients will automatically retry in the event\n of connection loss or failure, and some brokers support\n HA in way of *Primary/Primary* or *Primary/Replica* replication.\n\n- **Fast**\n\n A single Celery process can process millions of tasks a minute,\n with sub-millisecond round-trip latency (using RabbitMQ,\n py-librabbitmq, and optimized settings).\n\n- **Flexible**\n\n Almost every part of *Celery* can be extended or used on its own,\n Custom pool implementations, serializers, compression schemes, logging,\n schedulers, consumers, producers, broker transports, and much more.\n\nIt supports...\n================\n\n - **Message Transports**\n\n - RabbitMQ_, Redis_, Amazon SQS\n\n - **Concurrency**\n\n - Prefork, Eventlet_, gevent_, single threaded (``solo``)\n\n - **Result Stores**\n\n - AMQP, Redis\n - memcached\n - SQLAlchemy, Django ORM\n - Apache Cassandra, IronCache, Elasticsearch\n\n - **Serialization**\n\n - *pickle*, *json*, *yaml*, *msgpack*.\n - *zlib*, *bzip2* compression.\n - Cryptographic message signing.\n\n.. _`Eventlet`: http://eventlet.net/\n.. _`gevent`: http://gevent.org/\n\n.. _RabbitMQ: https://rabbitmq.com\n.. _Redis: https://redis.io\n.. _SQLAlchemy: http://sqlalchemy.org\n\nFramework Integration\n=====================\n\nCelery is easy to integrate with web frameworks, some of which even have\nintegration packages:\n\n +--------------------+------------------------+\n | `Django`_ | not needed |\n +--------------------+------------------------+\n | `Pyramid`_ | `pyramid_celery`_ |\n +--------------------+------------------------+\n | `Pylons`_ | `celery-pylons`_ |\n +--------------------+------------------------+\n | `Flask`_ | not needed |\n +--------------------+------------------------+\n | `web2py`_ | `web2py-celery`_ |\n +--------------------+------------------------+\n | `Tornado`_ | `tornado-celery`_ |\n +--------------------+------------------------+\n\nThe integration packages aren't strictly necessary, but they can make\ndevelopment easier, and sometimes they add important hooks like closing\ndatabase connections at ``fork``.\n\n.. _`Django`: https://djangoproject.com/\n.. _`Pylons`: http://pylonsproject.org/\n.. _`Flask`: http://flask.pocoo.org/\n.. _`web2py`: http://web2py.com/\n.. _`Bottle`: https://bottlepy.org/\n.. _`Pyramid`: http://docs.pylonsproject.org/en/latest/docs/pyramid.html\n.. _`pyramid_celery`: https://pypi.org/project/pyramid_celery/\n.. _`celery-pylons`: https://pypi.org/project/celery-pylons/\n.. _`web2py-celery`: https://code.google.com/p/web2py-celery/\n.. _`Tornado`: http://www.tornadoweb.org/\n.. _`tornado-celery`: https://github.com/mher/tornado-celery/\n\n.. _celery-documentation:\n\nDocumentation\n=============\n\nThe `latest documentation`_ is hosted at Read The Docs, containing user guides,\ntutorials, and an API reference.\n\n\u6700\u65b0\u7684\u4e2d\u6587\u6587\u6863\u6258\u7ba1\u5728 https://www.celerycn.io/ \u4e2d\uff0c\u5305\u542b\u7528\u6237\u6307\u5357\u3001\u6559\u7a0b\u3001API\u63a5\u53e3\u7b49\u3002\n\n.. _`latest documentation`: http://docs.celeryproject.org/en/latest/\n\n.. _celery-installation:\n\nInstallation\n============\n\nYou can install Celery either via the Python Package Index (PyPI)\nor from source.\n\nTo install using ``pip``:\n\n::\n\n\n $ pip install -U Celery\n\n.. _bundles:\n\nBundles\n-------\n\nCelery also defines a group of bundles that can be used\nto install Celery and the dependencies for a given feature.\n\nYou can specify these in your requirements or on the ``pip``\ncommand-line by using brackets. Multiple bundles can be specified by\nseparating them by commas.\n\n::\n\n\n $ pip install \"celery[librabbitmq]\"\n\n $ pip install \"celery[librabbitmq,redis,auth,msgpack]\"\n\nThe following bundles are available:\n\nSerializers\n~~~~~~~~~~~\n\n:``celery[auth]``:\n for using the ``auth`` security serializer.\n\n:``celery[msgpack]``:\n for using the msgpack serializer.\n\n:``celery[yaml]``:\n for using the yaml serializer.\n\nConcurrency\n~~~~~~~~~~~\n\n:``celery[eventlet]``:\n for using the ``eventlet`` pool.\n\n:``celery[gevent]``:\n for using the ``gevent`` pool.\n\nTransports and Backends\n~~~~~~~~~~~~~~~~~~~~~~~\n\n:``celery[librabbitmq]``:\n for using the librabbitmq C library.\n\n:``celery[redis]``:\n for using Redis as a message transport or as a result backend.\n\n:``celery[sqs]``:\n for using Amazon SQS as a message transport.\n\n:``celery[tblib``]:\n for using the ``task_remote_tracebacks`` feature.\n\n:``celery[memcache]``:\n for using Memcached as a result backend (using ``pylibmc``)\n\n:``celery[pymemcache]``:\n for using Memcached as a result backend (pure-Python implementation).\n\n:``celery[cassandra]``:\n for using Apache Cassandra as a result backend with DataStax driver.\n\n:``celery[azureblockblob]``:\n for using Azure Storage as a result backend (using ``azure-storage``)\n\n:``celery[s3]``:\n for using S3 Storage as a result backend.\n\n:``celery[couchbase]``:\n for using Couchbase as a result backend.\n\n:``celery[arangodb]``:\n for using ArangoDB as a result backend.\n\n:``celery[elasticsearch]``:\n for using Elasticsearch as a result backend.\n\n:``celery[riak]``:\n for using Riak as a result backend.\n\n:``celery[cosmosdbsql]``:\n for using Azure Cosmos DB as a result backend (using ``pydocumentdb``)\n\n:``celery[zookeeper]``:\n for using Zookeeper as a message transport.\n\n:``celery[sqlalchemy]``:\n for using SQLAlchemy as a result backend (*supported*).\n\n:``celery[pyro]``:\n for using the Pyro4 message transport (*experimental*).\n\n:``celery[slmq]``:\n for using the SoftLayer Message Queue transport (*experimental*).\n\n:``celery[consul]``:\n for using the Consul.io Key/Value store as a message transport or result backend (*experimental*).\n\n:``celery[django]``:\n specifies the lowest version possible for Django support.\n\n You should probably not use this in your requirements, it's here\n for informational purposes only.\n\n\n.. _celery-installing-from-source:\n\nDownloading and installing from source\n--------------------------------------\n\nDownload the latest version of Celery from PyPI:\n\nhttps://pypi.org/project/celery/\n\nYou can install it by doing the following,:\n\n::\n\n\n $ tar xvfz celery-0.0.0.tar.gz\n $ cd celery-0.0.0\n $ python setup.py build\n # python setup.py install\n\nThe last command must be executed as a privileged user if\nyou aren't currently using a virtualenv.\n\n.. _celery-installing-from-git:\n\nUsing the development version\n-----------------------------\n\nWith pip\n~~~~~~~~\n\nThe Celery development version also requires the development\nversions of ``kombu``, ``amqp``, ``billiard``, and ``vine``.\n\nYou can install the latest snapshot of these using the following\npip commands:\n\n::\n\n\n $ pip install https://github.com/celery/celery/zipball/master#egg=celery\n $ pip install https://github.com/celery/billiard/zipball/master#egg=billiard\n $ pip install https://github.com/celery/py-amqp/zipball/master#egg=amqp\n $ pip install https://github.com/celery/kombu/zipball/master#egg=kombu\n $ pip install https://github.com/celery/vine/zipball/master#egg=vine\n\nWith git\n~~~~~~~~\n\nPlease see the Contributing section.\n\n.. _getting-help:\n\nGetting Help\n============\n\n.. _mailing-list:\n\nMailing list\n------------\n\nFor discussions about the usage, development, and future of Celery,\nplease join the `celery-users`_ mailing list.\n\n.. _`celery-users`: https://groups.google.com/group/celery-users/\n\n.. _irc-channel:\n\nIRC\n---\n\nCome chat with us on IRC. The **#celery** channel is located at the `Freenode`_\nnetwork.\n\n.. _`Freenode`: https://freenode.net\n\n.. _bug-tracker:\n\nBug tracker\n===========\n\nIf you have any suggestions, bug reports, or annoyances please report them\nto our issue tracker at https://github.com/celery/celery/issues/\n\n.. _wiki:\n\nWiki\n====\n\nhttps://github.com/celery/celery/wiki\n\nCredits\n=======\n\n.. _contributing-short:\n\nContributors\n------------\n\nThis project exists thanks to all the people who contribute. Development of\n`celery` happens at GitHub: https://github.com/celery/celery\n\nYou're highly encouraged to participate in the development\nof `celery`. If you don't like GitHub (for some reason) you're welcome\nto send regular patches.\n\nBe sure to also read the `Contributing to Celery`_ section in the\ndocumentation.\n\n.. _`Contributing to Celery`:\n http://docs.celeryproject.org/en/master/contributing.html\n\n|oc-contributors|\n\n.. |oc-contributors| image:: https://opencollective.com/celery/contributors.svg?width=890&button=false\n :target: https://github.com/celery/celery/graphs/contributors\n\nBackers\n-------\n\nThank you to all our backers! \ud83d\ude4f [`Become a backer`_]\n\n.. _`Become a backer`: https://opencollective.com/celery#backer\n\n|oc-backers|\n\n.. |oc-backers| image:: https://opencollective.com/celery/backers.svg?width=890\n :target: https://opencollective.com/celery#backers\n\nSponsors\n--------\n\nSupport this project by becoming a sponsor. Your logo will show up here with a\nlink to your website. [`Become a sponsor`_]\n\n.. _`Become a sponsor`: https://opencollective.com/celery#sponsor\n\n|oc-sponsors|\n\n.. |oc-sponsors| image:: https://opencollective.com/celery/sponsor/0/avatar.svg\n :target: https://opencollective.com/celery/sponsor/0/website\n\n.. _license:\n\nLicense\n=======\n\nThis software is licensed under the `New BSD License`. See the ``LICENSE``\nfile in the top distribution directory for the full license text.\n\n.. # vim: syntax=rst expandtab tabstop=4 shiftwidth=4 shiftround\n\n.. |build-status| image:: https://secure.travis-ci.org/celery/celery.png?branch=master\n :alt: Build status\n :target: https://travis-ci.org/celery/celery\n\n.. |coverage| image:: https://codecov.io/github/celery/celery/coverage.svg?branch=master\n :target: https://codecov.io/github/celery/celery?branch=master\n\n.. |license| image:: https://img.shields.io/pypi/l/celery.svg\n :alt: BSD License\n :target: https://opensource.org/licenses/BSD-3-Clause\n\n.. |wheel| image:: https://img.shields.io/pypi/wheel/celery.svg\n :alt: Celery can be installed via wheel\n :target: https://pypi.org/project/celery/\n\n.. |pyversion| image:: https://img.shields.io/pypi/pyversions/celery.svg\n :alt: Supported Python versions.\n :target: https://pypi.org/project/celery/\n\n.. |pyimp| image:: https://img.shields.io/pypi/implementation/celery.svg\n :alt: Support Python implementations.\n :target: https://pypi.org/project/celery/\n\n.. |ocbackerbadge| image:: https://opencollective.com/celery/backers/badge.svg\n :alt: Backers on Open Collective\n :target: #backers\n\n.. |ocsponsorbadge| image:: https://opencollective.com/celery/sponsors/badge.svg\n :alt: Sponsors on Open Collective\n :target: #sponsors\n\n.. |downloads| image:: https://pepy.tech/badge/celery\n :alt: Downloads\n :target: https://pepy.tech/project/celery\n\n\n", + "release_date": "2020-07-31T17:42:18", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ask Solem", + "email": "auvipy@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "task job queue distributed messaging actor", + "homepage_url": "http://celeryproject.org", + "download_url": "https://files.pythonhosted.org/packages/fe/58/c7ced9705c2cedf526e183e428d1b145910cb8bc7ea537a2ec9a6552c056/celery-4.4.7.tar.gz", + "size": 1469812, + "sha1": null, + "md5": "62906067bd50c4e7e97f0b27f44f6bac", + "sha256": "d220b13a8ed57c78149acf82c006785356071844afe0b27012a4991d44026f9f", + "sha512": null, + "bug_tracking_url": "https://github.com/celery/celery/issues", + "code_view_url": "https://github.com/celery/celery", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/celery/4.4.7/json", + "datasource_id": null, + "purl": "pkg:pypi/celery@4.4.7" + }, + { + "type": "pypi", + "namespace": null, + "name": "cffi", + "version": "1.15.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "CFFI\n====\n\nForeign Function Interface for Python calling C code.\nPlease see the `Documentation `_.\n\nContact\n-------\n\n`Mailing list `_\n\n\n", + "release_date": "2022-06-30T18:15:15", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Rigo, Maciej Fijalkowski", + "email": "python-cffi@googlegroups.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://cffi.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/1d/76/bcebbbab689f5f6fc8a91e361038a3001ee2e48c5f9dbad0a3b64a64cc9e/cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", + "size": 390399, + "sha1": null, + "md5": "2557b1c370446dda9ab66b8d9d8fb246", + "sha256": "9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/cffi/1.15.1/json", + "datasource_id": null, + "purl": "pkg:pypi/cffi@1.15.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "cffi", + "version": "1.15.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "CFFI\n====\n\nForeign Function Interface for Python calling C code.\nPlease see the `Documentation `_.\n\nContact\n-------\n\n`Mailing list `_\n\n\n", + "release_date": "2022-06-30T18:18:32", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Rigo, Maciej Fijalkowski", + "email": "python-cffi@googlegroups.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://cffi.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/2b/a8/050ab4f0c3d4c1b8aaa805f70e26e84d0e27004907c5b8ecc1d31815f92a/cffi-1.15.1.tar.gz", + "size": 508501, + "sha1": null, + "md5": "f493860a6e98cd0c4178149568a6b4f6", + "sha256": "d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/cffi/1.15.1/json", + "datasource_id": null, + "purl": "pkg:pypi/cffi@1.15.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "click", + "version": "7.1.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\\$ click\\_\n==========\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n $ pip install -U click\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nA Simple Example\n----------------\n\n.. code-block:: python\n\n import click\n\n @click.command()\n @click.option(\"--count\", default=1, help=\"Number of greetings.\")\n @click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\n def hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\n if __name__ == '__main__':\n hello()\n\n.. code-block:: text\n\n $ python hello.py --count=3\n Your name: Click\n Hello, Click!\n Hello, Click!\n Hello, Click!\n\n\nDonate\n------\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, `please\ndonate today`_.\n\n.. _please donate today: https://palletsprojects.com/donate\n\n\nLinks\n-----\n\n- Website: https://palletsprojects.com/p/click/\n- Documentation: https://click.palletsprojects.com/\n- Releases: https://pypi.org/project/click/\n- Code: https://github.com/pallets/click\n- Issue tracker: https://github.com/pallets/click/issues\n- Test status: https://dev.azure.com/pallets/click/_build\n- Official chat: https://discord.gg/t6rrQZH\n\n\n", + "release_date": "2020-04-27T20:22:42", + "parties": [ + { + "type": "person", + "role": "author", + "name": "", + "email": "", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/click/", + "download_url": "https://files.pythonhosted.org/packages/d2/3d/fa76db83bf75c4f8d338c2fd15c8d33fdd7ad23a9b5e57eb6c5de26b430e/click-7.1.2-py2.py3-none-any.whl", + "size": 82780, + "sha1": null, + "md5": "b4233221cacc473acd422a1d54ff4c41", + "sha256": "dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/click", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/click/7.1.2/json", + "datasource_id": null, + "purl": "pkg:pypi/click@7.1.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "click", + "version": "7.1.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\\$ click\\_\n==========\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n $ pip install -U click\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nA Simple Example\n----------------\n\n.. code-block:: python\n\n import click\n\n @click.command()\n @click.option(\"--count\", default=1, help=\"Number of greetings.\")\n @click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\n def hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\n if __name__ == '__main__':\n hello()\n\n.. code-block:: text\n\n $ python hello.py --count=3\n Your name: Click\n Hello, Click!\n Hello, Click!\n Hello, Click!\n\n\nDonate\n------\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, `please\ndonate today`_.\n\n.. _please donate today: https://palletsprojects.com/donate\n\n\nLinks\n-----\n\n- Website: https://palletsprojects.com/p/click/\n- Documentation: https://click.palletsprojects.com/\n- Releases: https://pypi.org/project/click/\n- Code: https://github.com/pallets/click\n- Issue tracker: https://github.com/pallets/click/issues\n- Test status: https://dev.azure.com/pallets/click/_build\n- Official chat: https://discord.gg/t6rrQZH\n\n\n", + "release_date": "2020-04-27T20:22:45", + "parties": [ + { + "type": "person", + "role": "author", + "name": "", + "email": "", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/click/", + "download_url": "https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz", + "size": 297279, + "sha1": null, + "md5": "53692f62cb99a1a10c59248f1776d9c0", + "sha256": "d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/click", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/click/7.1.2/json", + "datasource_id": null, + "purl": "pkg:pypi/click@7.1.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "configparser", + "version": "4.0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/configparser.svg\n :target: https://pypi.org/project/configparser\n\n.. image:: https://img.shields.io/pypi/pyversions/configparser.svg\n\n.. image:: https://img.shields.io/travis/jaraco/configparser/master.svg\n :target: https://travis-ci.org/jaraco/configparser\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n :alt: Code style: Black\n\n.. .. image:: https://img.shields.io/appveyor/ci/jaraco/configparser/master.svg\n.. :target: https://ci.appveyor.com/project/jaraco/configparser/branch/master\n\n.. image:: https://readthedocs.org/projects/configparser/badge/?version=latest\n :target: https://configparser.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://tidelift.com/badges/package/pypi/configparser\n :target: https://tidelift.com/subscription/pkg/pypi-configparser?utm_source=pypi-configparser&utm_medium=readme\n\n\nThe ancient ``ConfigParser`` module available in the standard library 2.x has\nseen a major update in Python 3.2. This is a backport of those changes so that\nthey can be used directly in Python 2.6 - 3.5.\n\nTo use the ``configparser`` backport instead of the built-in version on both\nPython 2 and Python 3, simply import it explicitly as a backport::\n\n from backports import configparser\n\nIf you'd like to use the backport on Python 2 and the built-in version on\nPython 3, use that invocation instead::\n\n import configparser\n\nFor detailed documentation consult the vanilla version at\nhttp://docs.python.org/3/library/configparser.html.\n\nWhy you'll love ``configparser``\n--------------------------------\n\nWhereas almost completely compatible with its older brother, ``configparser``\nsports a bunch of interesting new features:\n\n* full mapping protocol access (`more info\n `_)::\n\n >>> parser = ConfigParser()\n >>> parser.read_string(\"\"\"\n [DEFAULT]\n location = upper left\n visible = yes\n editable = no\n color = blue\n\n [main]\n title = Main Menu\n color = green\n\n [options]\n title = Options\n \"\"\")\n >>> parser['main']['color']\n 'green'\n >>> parser['main']['editable']\n 'no'\n >>> section = parser['options']\n >>> section['title']\n 'Options'\n >>> section['title'] = 'Options (editable: %(editable)s)'\n >>> section['title']\n 'Options (editable: no)'\n\n* there's now one default ``ConfigParser`` class, which basically is the old\n ``SafeConfigParser`` with a bunch of tweaks which make it more predictable for\n users. Don't need interpolation? Simply use\n ``ConfigParser(interpolation=None)``, no need to use a distinct\n ``RawConfigParser`` anymore.\n\n* the parser is highly `customizable upon instantiation\n `__\n supporting things like changing option delimiters, comment characters, the\n name of the DEFAULT section, the interpolation syntax, etc.\n\n* you can easily create your own interpolation syntax but there are two powerful\n implementations built-in (`more info\n `__):\n\n * the classic ``%(string-like)s`` syntax (called ``BasicInterpolation``)\n\n * a new ``${buildout:like}`` syntax (called ``ExtendedInterpolation``)\n\n* fallback values may be specified in getters (`more info\n `__)::\n\n >>> config.get('closet', 'monster',\n ... fallback='No such things as monsters')\n 'No such things as monsters'\n\n* ``ConfigParser`` objects can now read data directly `from strings\n `__\n and `from dictionaries\n `__.\n That means importing configuration from JSON or specifying default values for\n the whole configuration (multiple sections) is now a single line of code. Same\n goes for copying data from another ``ConfigParser`` instance, thanks to its\n mapping protocol support.\n\n* many smaller tweaks, updates and fixes\n\nA few words about Unicode\n-------------------------\n\n``configparser`` comes from Python 3 and as such it works well with Unicode.\nThe library is generally cleaned up in terms of internal data storage and\nreading/writing files. There are a couple of incompatibilities with the old\n``ConfigParser`` due to that. However, the work required to migrate is well\nworth it as it shows the issues that would likely come up during migration of\nyour project to Python 3.\n\nThe design assumes that Unicode strings are used whenever possible [1]_. That\ngives you the certainty that what's stored in a configuration object is text.\nOnce your configuration is read, the rest of your application doesn't have to\ndeal with encoding issues. All you have is text [2]_. The only two phases when\nyou should explicitly state encoding is when you either read from an external\nsource (e.g. a file) or write back.\n\nVersioning\n----------\n\nThis project uses `semver `_ to\ncommunicate the impact of various releases while periodically syncing\nwith the upstream implementation in CPython.\n`The changelog `_\nserves as a reference indicating which versions incorporate\nwhich upstream functionality.\n\nPrior to the ``4.0.0`` release, `another scheme\n`_\nwas used to associate the CPython and backports releases.\n\nMaintenance\n-----------\n\nThis backport was originally authored by \u0141ukasz Langa, the current vanilla\n``configparser`` maintainer for CPython and is currently maintained by\nJason R. Coombs:\n\n* `configparser repository `_\n\n* `configparser issue tracker `_\n\nSecurity Contact\n----------------\n\nTo report a security vulnerability, please use the\n`Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\nConversion Process\n------------------\n\nThis section is technical and should bother you only if you are wondering how\nthis backport is produced. If the implementation details of this backport are\nnot important for you, feel free to ignore the following content.\n\n``configparser`` is converted using `python-future\n`_. The project takes the following\nbranching approach:\n\n* the ``3.x`` branch holds unchanged files synchronized from the upstream\n CPython repository. The synchronization is currently done by manually copying\n the required files and stating from which CPython changeset they come from.\n\n* the ``master`` branch holds a version of the ``3.x`` code with some tweaks\n that make it independent from libraries and constructions unavailable on 2.x.\n Code on this branch still *must* work on the corresponding Python 3.x but\n will also work on Python 2.6 and 2.7 (including PyPy). You can check this\n running the supplied unit tests with ``tox``.\n\nThe process works like this:\n\n1. In the ``3.x`` branch, run ``pip-run -- sync-upstream.py``, which\n downloads the latest stable release of Python and copies the relevant\n files from there into their new locations here and then commits those\n changes with a nice reference to the relevant upstream commit hash.\n\n2. I check for new names in ``__all__`` and update imports in\n ``configparser.py`` accordingly. I run the tests on Python 3. Commit.\n\n3. I merge the new commit to ``master``. I run ``tox``. Commit.\n\n4. If there are necessary changes, I do them now (on ``master``). Note that\n the changes should be written in the syntax subset supported by Python\n 2.6.\n\n5. I run ``tox``. If it works, I update the docs and release the new version.\n Otherwise, I go back to point 3. I might use ``pasteurize`` to suggest me\n required changes but usually I do them manually to keep resulting code in\n a nicer form.\n\n\nFootnotes\n---------\n\n.. [1] To somewhat ease migration, passing bytestrings is still supported but\n they are converted to Unicode for internal storage anyway. This means\n that for the vast majority of strings used in configuration files, it\n won't matter if you pass them as bytestrings or Unicode. However, if you\n pass a bytestring that cannot be converted to Unicode using the naive\n ASCII codec, a ``UnicodeDecodeError`` will be raised. This is purposeful\n and helps you manage proper encoding for all content you store in\n memory, read from various sources and write back.\n\n.. [2] Life gets much easier when you understand that you basically manage\n **text** in your application. You don't care about bytes but about\n letters. In that regard the concept of content encoding is meaningless.\n The only time when you deal with raw bytes is when you write the data to\n a file. Then you have to specify how your text should be encoded. On\n the other end, to get meaningful text from a file, the application\n reading it has to know which encoding was used during its creation. But\n once the bytes are read and properly decoded, all you have is text. This\n is especially powerful when you start interacting with multiple data\n sources. Even if each of them uses a different encoding, inside your\n application data is held in abstract text form. You can program your\n business logic without worrying about which data came from which source.\n You can freely exchange the data you store between sources. Only\n reading/writing files requires encoding your text to bytes.\n\n\n", + "release_date": "2019-09-12T07:46:33", + "parties": [ + { + "type": "person", + "role": "author", + "name": "\u0141ukasz Langa", + "email": "lukasz@langa.pl", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + } + ], + "keywords": "configparser ini parsing conf cfg configuration file", + "homepage_url": "https://github.com/jaraco/configparser/", + "download_url": "https://files.pythonhosted.org/packages/7a/2a/95ed0501cf5d8709490b1d3a3f9b5cf340da6c433f896bbe9ce08dbe6785/configparser-4.0.2-py2.py3-none-any.whl", + "size": 22828, + "sha1": null, + "md5": "7f64f596556950d557e9da5255da81d7", + "sha256": "254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/configparser/4.0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/configparser@4.0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "configparser", + "version": "4.0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/configparser.svg\n :target: https://pypi.org/project/configparser\n\n.. image:: https://img.shields.io/pypi/pyversions/configparser.svg\n\n.. image:: https://img.shields.io/travis/jaraco/configparser/master.svg\n :target: https://travis-ci.org/jaraco/configparser\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n :alt: Code style: Black\n\n.. .. image:: https://img.shields.io/appveyor/ci/jaraco/configparser/master.svg\n.. :target: https://ci.appveyor.com/project/jaraco/configparser/branch/master\n\n.. image:: https://readthedocs.org/projects/configparser/badge/?version=latest\n :target: https://configparser.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://tidelift.com/badges/package/pypi/configparser\n :target: https://tidelift.com/subscription/pkg/pypi-configparser?utm_source=pypi-configparser&utm_medium=readme\n\n\nThe ancient ``ConfigParser`` module available in the standard library 2.x has\nseen a major update in Python 3.2. This is a backport of those changes so that\nthey can be used directly in Python 2.6 - 3.5.\n\nTo use the ``configparser`` backport instead of the built-in version on both\nPython 2 and Python 3, simply import it explicitly as a backport::\n\n from backports import configparser\n\nIf you'd like to use the backport on Python 2 and the built-in version on\nPython 3, use that invocation instead::\n\n import configparser\n\nFor detailed documentation consult the vanilla version at\nhttp://docs.python.org/3/library/configparser.html.\n\nWhy you'll love ``configparser``\n--------------------------------\n\nWhereas almost completely compatible with its older brother, ``configparser``\nsports a bunch of interesting new features:\n\n* full mapping protocol access (`more info\n `_)::\n\n >>> parser = ConfigParser()\n >>> parser.read_string(\"\"\"\n [DEFAULT]\n location = upper left\n visible = yes\n editable = no\n color = blue\n\n [main]\n title = Main Menu\n color = green\n\n [options]\n title = Options\n \"\"\")\n >>> parser['main']['color']\n 'green'\n >>> parser['main']['editable']\n 'no'\n >>> section = parser['options']\n >>> section['title']\n 'Options'\n >>> section['title'] = 'Options (editable: %(editable)s)'\n >>> section['title']\n 'Options (editable: no)'\n\n* there's now one default ``ConfigParser`` class, which basically is the old\n ``SafeConfigParser`` with a bunch of tweaks which make it more predictable for\n users. Don't need interpolation? Simply use\n ``ConfigParser(interpolation=None)``, no need to use a distinct\n ``RawConfigParser`` anymore.\n\n* the parser is highly `customizable upon instantiation\n `__\n supporting things like changing option delimiters, comment characters, the\n name of the DEFAULT section, the interpolation syntax, etc.\n\n* you can easily create your own interpolation syntax but there are two powerful\n implementations built-in (`more info\n `__):\n\n * the classic ``%(string-like)s`` syntax (called ``BasicInterpolation``)\n\n * a new ``${buildout:like}`` syntax (called ``ExtendedInterpolation``)\n\n* fallback values may be specified in getters (`more info\n `__)::\n\n >>> config.get('closet', 'monster',\n ... fallback='No such things as monsters')\n 'No such things as monsters'\n\n* ``ConfigParser`` objects can now read data directly `from strings\n `__\n and `from dictionaries\n `__.\n That means importing configuration from JSON or specifying default values for\n the whole configuration (multiple sections) is now a single line of code. Same\n goes for copying data from another ``ConfigParser`` instance, thanks to its\n mapping protocol support.\n\n* many smaller tweaks, updates and fixes\n\nA few words about Unicode\n-------------------------\n\n``configparser`` comes from Python 3 and as such it works well with Unicode.\nThe library is generally cleaned up in terms of internal data storage and\nreading/writing files. There are a couple of incompatibilities with the old\n``ConfigParser`` due to that. However, the work required to migrate is well\nworth it as it shows the issues that would likely come up during migration of\nyour project to Python 3.\n\nThe design assumes that Unicode strings are used whenever possible [1]_. That\ngives you the certainty that what's stored in a configuration object is text.\nOnce your configuration is read, the rest of your application doesn't have to\ndeal with encoding issues. All you have is text [2]_. The only two phases when\nyou should explicitly state encoding is when you either read from an external\nsource (e.g. a file) or write back.\n\nVersioning\n----------\n\nThis project uses `semver `_ to\ncommunicate the impact of various releases while periodically syncing\nwith the upstream implementation in CPython.\n`The changelog `_\nserves as a reference indicating which versions incorporate\nwhich upstream functionality.\n\nPrior to the ``4.0.0`` release, `another scheme\n`_\nwas used to associate the CPython and backports releases.\n\nMaintenance\n-----------\n\nThis backport was originally authored by \u0141ukasz Langa, the current vanilla\n``configparser`` maintainer for CPython and is currently maintained by\nJason R. Coombs:\n\n* `configparser repository `_\n\n* `configparser issue tracker `_\n\nSecurity Contact\n----------------\n\nTo report a security vulnerability, please use the\n`Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\nConversion Process\n------------------\n\nThis section is technical and should bother you only if you are wondering how\nthis backport is produced. If the implementation details of this backport are\nnot important for you, feel free to ignore the following content.\n\n``configparser`` is converted using `python-future\n`_. The project takes the following\nbranching approach:\n\n* the ``3.x`` branch holds unchanged files synchronized from the upstream\n CPython repository. The synchronization is currently done by manually copying\n the required files and stating from which CPython changeset they come from.\n\n* the ``master`` branch holds a version of the ``3.x`` code with some tweaks\n that make it independent from libraries and constructions unavailable on 2.x.\n Code on this branch still *must* work on the corresponding Python 3.x but\n will also work on Python 2.6 and 2.7 (including PyPy). You can check this\n running the supplied unit tests with ``tox``.\n\nThe process works like this:\n\n1. In the ``3.x`` branch, run ``pip-run -- sync-upstream.py``, which\n downloads the latest stable release of Python and copies the relevant\n files from there into their new locations here and then commits those\n changes with a nice reference to the relevant upstream commit hash.\n\n2. I check for new names in ``__all__`` and update imports in\n ``configparser.py`` accordingly. I run the tests on Python 3. Commit.\n\n3. I merge the new commit to ``master``. I run ``tox``. Commit.\n\n4. If there are necessary changes, I do them now (on ``master``). Note that\n the changes should be written in the syntax subset supported by Python\n 2.6.\n\n5. I run ``tox``. If it works, I update the docs and release the new version.\n Otherwise, I go back to point 3. I might use ``pasteurize`` to suggest me\n required changes but usually I do them manually to keep resulting code in\n a nicer form.\n\n\nFootnotes\n---------\n\n.. [1] To somewhat ease migration, passing bytestrings is still supported but\n they are converted to Unicode for internal storage anyway. This means\n that for the vast majority of strings used in configuration files, it\n won't matter if you pass them as bytestrings or Unicode. However, if you\n pass a bytestring that cannot be converted to Unicode using the naive\n ASCII codec, a ``UnicodeDecodeError`` will be raised. This is purposeful\n and helps you manage proper encoding for all content you store in\n memory, read from various sources and write back.\n\n.. [2] Life gets much easier when you understand that you basically manage\n **text** in your application. You don't care about bytes but about\n letters. In that regard the concept of content encoding is meaningless.\n The only time when you deal with raw bytes is when you write the data to\n a file. Then you have to specify how your text should be encoded. On\n the other end, to get meaningful text from a file, the application\n reading it has to know which encoding was used during its creation. But\n once the bytes are read and properly decoded, all you have is text. This\n is especially powerful when you start interacting with multiple data\n sources. Even if each of them uses a different encoding, inside your\n application data is held in abstract text form. You can program your\n business logic without worrying about which data came from which source.\n You can freely exchange the data you store between sources. Only\n reading/writing files requires encoding your text to bytes.\n\n\n", + "release_date": "2019-09-12T07:46:40", + "parties": [ + { + "type": "person", + "role": "author", + "name": "\u0141ukasz Langa", + "email": "lukasz@langa.pl", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + } + ], + "keywords": "configparser ini parsing conf cfg configuration file", + "homepage_url": "https://github.com/jaraco/configparser/", + "download_url": "https://files.pythonhosted.org/packages/16/4f/48975536bd488d3a272549eb795ac4a13a5f7fcdc8995def77fbef3532ee/configparser-4.0.2.tar.gz", + "size": 72498, + "sha1": null, + "md5": "35926cc4b9133f1f9ca70a1fd2fdf237", + "sha256": "c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/configparser/4.0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/configparser@4.0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "contextlib2", + "version": "0.6.0.post1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://jazzband.co/static/img/badge.svg\n :target: https://jazzband.co/\n :alt: Jazzband\n\n.. image:: https://readthedocs.org/projects/contextlib2/badge/?version=latest\n :target: https://contextlib2.readthedocs.org/\n :alt: Latest Docs\n\n.. image:: https://img.shields.io/travis/jazzband/contextlib2/master.svg\n :target: http://travis-ci.org/jazzband/contextlib2\n\n.. image:: https://coveralls.io/repos/github/jazzband/contextlib2/badge.svg?branch=master\n :target: https://coveralls.io/github/jazzband/contextlib2?branch=master\n\n.. image:: https://landscape.io/github/jazzband/contextlib2/master/landscape.svg\n :target: https://landscape.io/github/jazzband/contextlib2/\n\ncontextlib2 is a backport of the `standard library's contextlib\nmodule `_ to\nearlier Python versions.\n\nIt also serves as a real world proving ground for possible future\nenhancements to the standard library version.\n\nDevelopment\n-----------\n\ncontextlib2 has no runtime dependencies, but requires ``unittest2`` for testing\non Python 2.x, as well as ``setuptools`` and ``wheel`` to generate universal\nwheel archives.\n\nLocal testing is just a matter of running ``python test_contextlib2.py``.\n\nYou can test against multiple versions of Python with\n`tox `_::\n\n pip install tox\n tox\n\nVersions currently tested in both tox and Travis CI are:\n\n* CPython 2.7\n* CPython 3.4\n* CPython 3.5\n* CPython 3.6\n* CPython 3.7\n* PyPy\n* PyPy3\n\n\n", + "release_date": "2019-10-10T12:47:48", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Nick Coghlan", + "email": "ncoghlan@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://contextlib2.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/85/60/370352f7ef6aa96c52fb001831622f50f923c1d575427d021b8ab3311236/contextlib2-0.6.0.post1-py2.py3-none-any.whl", + "size": 9770, + "sha1": null, + "md5": "3cbfdffaa11f340df1ea6345013bcbd4", + "sha256": "3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "PSF License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/contextlib2/0.6.0.post1/json", + "datasource_id": null, + "purl": "pkg:pypi/contextlib2@0.6.0.post1" + }, + { + "type": "pypi", + "namespace": null, + "name": "contextlib2", + "version": "0.6.0.post1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://jazzband.co/static/img/badge.svg\n :target: https://jazzband.co/\n :alt: Jazzband\n\n.. image:: https://readthedocs.org/projects/contextlib2/badge/?version=latest\n :target: https://contextlib2.readthedocs.org/\n :alt: Latest Docs\n\n.. image:: https://img.shields.io/travis/jazzband/contextlib2/master.svg\n :target: http://travis-ci.org/jazzband/contextlib2\n\n.. image:: https://coveralls.io/repos/github/jazzband/contextlib2/badge.svg?branch=master\n :target: https://coveralls.io/github/jazzband/contextlib2?branch=master\n\n.. image:: https://landscape.io/github/jazzband/contextlib2/master/landscape.svg\n :target: https://landscape.io/github/jazzband/contextlib2/\n\ncontextlib2 is a backport of the `standard library's contextlib\nmodule `_ to\nearlier Python versions.\n\nIt also serves as a real world proving ground for possible future\nenhancements to the standard library version.\n\nDevelopment\n-----------\n\ncontextlib2 has no runtime dependencies, but requires ``unittest2`` for testing\non Python 2.x, as well as ``setuptools`` and ``wheel`` to generate universal\nwheel archives.\n\nLocal testing is just a matter of running ``python test_contextlib2.py``.\n\nYou can test against multiple versions of Python with\n`tox `_::\n\n pip install tox\n tox\n\nVersions currently tested in both tox and Travis CI are:\n\n* CPython 2.7\n* CPython 3.4\n* CPython 3.5\n* CPython 3.6\n* CPython 3.7\n* PyPy\n* PyPy3\n\n\n", + "release_date": "2019-10-10T12:48:44", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Nick Coghlan", + "email": "ncoghlan@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://contextlib2.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/02/54/669207eb72e3d8ae8b38aa1f0703ee87a0e9f88f30d3c0a47bebdb6de242/contextlib2-0.6.0.post1.tar.gz", + "size": 29670, + "sha1": null, + "md5": "d634281c2e61e575d8a68b9c56f8303a", + "sha256": "01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "PSF License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/contextlib2/0.6.0.post1/json", + "datasource_id": null, + "purl": "pkg:pypi/contextlib2@0.6.0.post1" + }, + { + "type": "pypi", + "namespace": null, + "name": "decorator", + "version": "4.4.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Decorators for Humans\n=====================\n\nThe goal of the decorator module is to make it easy to define\nsignature-preserving function decorators and decorator factories.\nIt also includes an implementation of multiple dispatch and other niceties\n(please check the docs). It is released under a two-clauses\nBSD license, i.e. basically you can do whatever you want with it but I am not\nresponsible.\n\nInstallation\n-------------\n\nIf you are lazy, just perform\n\n ``$ pip install decorator``\n\nwhich will install just the module on your system.\n\nIf you prefer to install the full distribution from source, including\nthe documentation, clone the `GitHub repo`_ or download the tarball_, unpack it and run\n\n ``$ pip install .``\n\nin the main directory, possibly as superuser.\n\n.. _tarball: https://pypi.org/project/decorator/#files\n.. _GitHub repo: https://github.com/micheles/decorator\n\nTesting\n--------\n\nIf you have the source code installation you can run the tests with\n\n `$ python src/tests/test.py -v`\n\nor (if you have setuptools installed)\n\n `$ python setup.py test`\n\nNotice that you may run into trouble if in your system there\nis an older version of the decorator module; in such a case remove the\nold version. It is safe even to copy the module `decorator.py` over\nan existing one, since we kept backward-compatibility for a long time.\n\nRepository\n---------------\n\nThe project is hosted on GitHub. You can look at the source here:\n\n https://github.com/micheles/decorator\n\nDocumentation\n---------------\n\nThe documentation has been moved to https://github.com/micheles/decorator/blob/master/docs/documentation.md\n\nFrom there you can get a PDF version by simply using the print\nfunctionality of your browser.\n\nHere is the documentation for previous versions of the module:\n\nhttps://github.com/micheles/decorator/blob/4.3.2/docs/tests.documentation.rst\nhttps://github.com/micheles/decorator/blob/4.2.1/docs/tests.documentation.rst\nhttps://github.com/micheles/decorator/blob/4.1.2/docs/tests.documentation.rst\nhttps://github.com/micheles/decorator/blob/4.0.0/documentation.rst\nhttps://github.com/micheles/decorator/blob/3.4.2/documentation.rst\n\nFor the impatient\n-----------------\n\nHere is an example of how to define a family of decorators tracing slow\noperations:\n\n.. code-block:: python\n\n from decorator import decorator\n\n @decorator\n def warn_slow(func, timelimit=60, *args, **kw):\n t0 = time.time()\n result = func(*args, **kw)\n dt = time.time() - t0\n if dt > timelimit:\n logging.warn('%s took %d seconds', func.__name__, dt)\n else:\n logging.info('%s took %d seconds', func.__name__, dt)\n return result\n\n @warn_slow # warn if it takes more than 1 minute\n def preprocess_input_files(inputdir, tempdir):\n ...\n\n @warn_slow(timelimit=600) # warn if it takes more than 10 minutes\n def run_calculation(tempdir, outdir):\n ...\n\nEnjoy!\n", + "release_date": "2020-02-29T05:24:45", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Michele Simionato", + "email": "michele.simionato@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "decorators generic utility", + "homepage_url": "https://github.com/micheles/decorator", + "download_url": "https://files.pythonhosted.org/packages/ed/1b/72a1821152d07cf1d8b6fce298aeb06a7eb90f4d6d41acec9861e7cc6df0/decorator-4.4.2-py2.py3-none-any.whl", + "size": 9239, + "sha1": null, + "md5": "19f0b49e62cece91b14359c12d1ff78e", + "sha256": "41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "new BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/decorator/4.4.2/json", + "datasource_id": null, + "purl": "pkg:pypi/decorator@4.4.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "decorator", + "version": "4.4.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Decorators for Humans\n=====================\n\nThe goal of the decorator module is to make it easy to define\nsignature-preserving function decorators and decorator factories.\nIt also includes an implementation of multiple dispatch and other niceties\n(please check the docs). It is released under a two-clauses\nBSD license, i.e. basically you can do whatever you want with it but I am not\nresponsible.\n\nInstallation\n-------------\n\nIf you are lazy, just perform\n\n ``$ pip install decorator``\n\nwhich will install just the module on your system.\n\nIf you prefer to install the full distribution from source, including\nthe documentation, clone the `GitHub repo`_ or download the tarball_, unpack it and run\n\n ``$ pip install .``\n\nin the main directory, possibly as superuser.\n\n.. _tarball: https://pypi.org/project/decorator/#files\n.. _GitHub repo: https://github.com/micheles/decorator\n\nTesting\n--------\n\nIf you have the source code installation you can run the tests with\n\n `$ python src/tests/test.py -v`\n\nor (if you have setuptools installed)\n\n `$ python setup.py test`\n\nNotice that you may run into trouble if in your system there\nis an older version of the decorator module; in such a case remove the\nold version. It is safe even to copy the module `decorator.py` over\nan existing one, since we kept backward-compatibility for a long time.\n\nRepository\n---------------\n\nThe project is hosted on GitHub. You can look at the source here:\n\n https://github.com/micheles/decorator\n\nDocumentation\n---------------\n\nThe documentation has been moved to https://github.com/micheles/decorator/blob/master/docs/documentation.md\n\nFrom there you can get a PDF version by simply using the print\nfunctionality of your browser.\n\nHere is the documentation for previous versions of the module:\n\nhttps://github.com/micheles/decorator/blob/4.3.2/docs/tests.documentation.rst\nhttps://github.com/micheles/decorator/blob/4.2.1/docs/tests.documentation.rst\nhttps://github.com/micheles/decorator/blob/4.1.2/docs/tests.documentation.rst\nhttps://github.com/micheles/decorator/blob/4.0.0/documentation.rst\nhttps://github.com/micheles/decorator/blob/3.4.2/documentation.rst\n\nFor the impatient\n-----------------\n\nHere is an example of how to define a family of decorators tracing slow\noperations:\n\n.. code-block:: python\n\n from decorator import decorator\n\n @decorator\n def warn_slow(func, timelimit=60, *args, **kw):\n t0 = time.time()\n result = func(*args, **kw)\n dt = time.time() - t0\n if dt > timelimit:\n logging.warn('%s took %d seconds', func.__name__, dt)\n else:\n logging.info('%s took %d seconds', func.__name__, dt)\n return result\n\n @warn_slow # warn if it takes more than 1 minute\n def preprocess_input_files(inputdir, tempdir):\n ...\n\n @warn_slow(timelimit=600) # warn if it takes more than 10 minutes\n def run_calculation(tempdir, outdir):\n ...\n\nEnjoy!\n", + "release_date": "2020-02-29T05:24:43", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Michele Simionato", + "email": "michele.simionato@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "decorators generic utility", + "homepage_url": "https://github.com/micheles/decorator", + "download_url": "https://files.pythonhosted.org/packages/da/93/84fa12f2dc341f8cf5f022ee09e109961055749df2d0c75c5f98746cfe6c/decorator-4.4.2.tar.gz", + "size": 33629, + "sha1": null, + "md5": "d83c624cce93e6bdfab144821b526e1d", + "sha256": "e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "new BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/decorator/4.4.2/json", + "datasource_id": null, + "purl": "pkg:pypi/decorator@4.4.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "enum34", + "version": "1.1.10", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "enum --- support for enumerations\n========================================\n\nAn enumeration is a set of symbolic names (members) bound to unique, constant\nvalues. Within an enumeration, the members can be compared by identity, and\nthe enumeration itself can be iterated over.\n\n from enum import Enum\n\n class Fruit(Enum):\n apple = 1\n banana = 2\n orange = 3\n\n list(Fruit)\n # [, , ]\n\n len(Fruit)\n # 3\n\n Fruit.banana\n # \n\n Fruit['banana']\n # \n\n Fruit(2)\n # \n\n Fruit.banana is Fruit['banana'] is Fruit(2)\n # True\n\n Fruit.banana.name\n # 'banana'\n\n Fruit.banana.value\n # 2\n\nRepository and Issue Tracker at https://bitbucket.org/stoneleaf/enum34.\n\n\n", + "release_date": "2020-03-10T17:47:58", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ethan Furman", + "email": "ethan@stoneleaf.us", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://bitbucket.org/stoneleaf/enum34", + "download_url": "https://files.pythonhosted.org/packages/6f/2c/a9386903ece2ea85e9807e0e062174dc26fdce8b05f216d00491be29fad5/enum34-1.1.10-py2-none-any.whl", + "size": 11223, + "sha1": null, + "md5": "85f9f5509176e863bb723e10f44cd317", + "sha256": "a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/enum34/1.1.10/json", + "datasource_id": null, + "purl": "pkg:pypi/enum34@1.1.10" + }, + { + "type": "pypi", + "namespace": null, + "name": "enum34", + "version": "1.1.10", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "enum --- support for enumerations\n========================================\n\nAn enumeration is a set of symbolic names (members) bound to unique, constant\nvalues. Within an enumeration, the members can be compared by identity, and\nthe enumeration itself can be iterated over.\n\n from enum import Enum\n\n class Fruit(Enum):\n apple = 1\n banana = 2\n orange = 3\n\n list(Fruit)\n # [, , ]\n\n len(Fruit)\n # 3\n\n Fruit.banana\n # \n\n Fruit['banana']\n # \n\n Fruit(2)\n # \n\n Fruit.banana is Fruit['banana'] is Fruit(2)\n # True\n\n Fruit.banana.name\n # 'banana'\n\n Fruit.banana.value\n # 2\n\nRepository and Issue Tracker at https://bitbucket.org/stoneleaf/enum34.\n\n\n", + "release_date": "2020-03-10T17:48:00", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ethan Furman", + "email": "ethan@stoneleaf.us", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://bitbucket.org/stoneleaf/enum34", + "download_url": "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz", + "size": 28187, + "sha1": null, + "md5": "b5ac0bb5ea9e830029599e410d09d3b5", + "sha256": "cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/enum34/1.1.10/json", + "datasource_id": null, + "purl": "pkg:pypi/enum34@1.1.10" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-babelex", + "version": "0.9.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nFlask-BabelEx\n-------------\n\nAdds i18n/l10n support to Flask applications with the help of the\n`Babel`_ library.\n\nThis is fork of official Flask-Babel extension with following features:\n\n1. It is possible to use multiple language catalogs in one Flask application;\n2. Localization domains: your extension can package localization file(s) and use them\n if necessary;\n3. Does not reload localizations for each request.\n\nLinks\n`````\n\n* `documentation `_\n* `development version\n `_\n* `original Flask-Babel extension `_.\n\n.. _Babel: http://babel.edgewall.org/\n\n\n\n", + "release_date": "2020-02-07T15:18:20", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Serge S. Koval", + "email": "serge.koval+github@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://github.com/mrjoes/flask-babelex", + "download_url": "https://files.pythonhosted.org/packages/85/e7/217fb37ccd4bd93cd0f002028fb7c5fdf6ee0063a6beb83e43cd903da46e/Flask-BabelEx-0.9.4.tar.gz", + "size": 43423, + "sha1": null, + "md5": "8b1dac7c14377fb256c0201fe9353fa5", + "sha256": "39a59ccee9386a9d52d80b9101224402036aedc2c7873b11deef6e4e21cace27", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-babelex/0.9.4/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-babelex@0.9.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-caching", + "version": "1.9.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nFlask-Caching\n=============\n\nAdds easy cache support to Flask.\n\nSetup\n-----\n\nThe Cache Extension can either be initialized directly:\n\n.. code:: python\n\n from flask import Flask\n from flask_caching import Cache\n\n app = Flask(__name__)\n # For more configuration options, check out the documentation\n cache = Cache(app, config={\"CACHE_TYPE\": \"simple\"})\n\nOr through the factory method:\n\n.. code:: python\n\n cache = Cache(config={\"CACHE_TYPE\": \"simple\"})\n\n app = Flask(__name__)\n cache.init_app(app)\n\nLinks\n=====\n\n* `Documentation `_\n* `Source Code `_\n* `Issues `_\n* `original Flask-Cache Extension `_\n\n\n\n", + "release_date": "2020-06-02T16:01:49", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Peter Justin", + "email": "peter.justin@outlook.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/sh4nks/flask-caching", + "download_url": "https://files.pythonhosted.org/packages/d1/9f/135bcf47fdb585dffcf9f918664ab9d63585aae8e722948b2abca041312f/Flask_Caching-1.9.0-py2.py3-none-any.whl", + "size": 33886, + "sha1": null, + "md5": "3d9b009d15a56d21c0cba5ca245bc327", + "sha256": "e6ef2e2af84e13c4fd32c1839c1943a42f11b6b0fbcfdd6bf46547ea5482dbfe", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-caching/1.9.0/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-caching@1.9.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-caching", + "version": "1.9.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nFlask-Caching\n=============\n\nAdds easy cache support to Flask.\n\nSetup\n-----\n\nThe Cache Extension can either be initialized directly:\n\n.. code:: python\n\n from flask import Flask\n from flask_caching import Cache\n\n app = Flask(__name__)\n # For more configuration options, check out the documentation\n cache = Cache(app, config={\"CACHE_TYPE\": \"simple\"})\n\nOr through the factory method:\n\n.. code:: python\n\n cache = Cache(config={\"CACHE_TYPE\": \"simple\"})\n\n app = Flask(__name__)\n cache.init_app(app)\n\nLinks\n=====\n\n* `Documentation `_\n* `Source Code `_\n* `Issues `_\n* `original Flask-Cache Extension `_\n\n\n\n", + "release_date": "2020-06-02T16:01:52", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Peter Justin", + "email": "peter.justin@outlook.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } ], - "sdist_url": "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz" + "keywords": [], + "homepage_url": "https://github.com/sh4nks/flask-caching", + "download_url": "https://files.pythonhosted.org/packages/41/c9/472486c62f22a1dad273a132b9484189e1a22eb8358883249e4955f8e464/Flask-Caching-1.9.0.tar.gz", + "size": 71618, + "sha1": null, + "md5": "49eea208256c63947ecd33de273afac3", + "sha256": "a0356ad868b1d8ec2d0e675a6fe891c41303128f8904d5d79e180d8b3f952aff", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-caching/1.9.0/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-caching@1.9.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-celeryext", + "version": "0.3.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "=================\n Flask-CeleryExt\n=================\n\n.. image:: https://img.shields.io/travis/inveniosoftware/flask-celeryext.svg\n :target: https://travis-ci.org/inveniosoftware/flask-celeryext\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/flask-celeryext.svg\n :target: https://coveralls.io/r/inveniosoftware/flask-celeryext\n\n.. image:: https://img.shields.io/github/tag/inveniosoftware/flask-celeryext.svg\n :target: https://github.com/inveniosoftware/flask-celeryext/releases\n\n.. image:: https://img.shields.io/pypi/dm/flask-celeryext.svg\n :target: https://pypi.python.org/pypi/flask-celeryext\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/flask-celeryext.svg\n :target: https://github.com/inveniosoftware/flask-celeryext/blob/master/LICENSE\n\nAbout\n=====\n\nFlask-CeleryExt is a simple integration layer between Celery and Flask.\n\nInstallation\n============\n\nFlask-CeleryExt is on PyPI so all you need is: ::\n\n pip install flask-celeryext\n\nDocumentation\n=============\n\nDocumentation is readable at https://flask-celeryext.readthedocs.io/ or can be\nbuild using Sphinx: ::\n\n pip install Sphinx\n python setup.py build_sphinx\n\nTesting\n=======\n\nRunning the test suite is as simple as: ::\n\n python setup.py test\n\n\nChanges\n=======\n\nVersion 0.3.4 (released 2020-02-17)\n\n- Adds support for Python 3.8\n- Fixes pin for Celery on Python <3.7.\n\nVersion 0.3.3 (released 2020-02-13)\n\n- Fix celery version for Python < 3.7\n\nVersion 0.3.2 (released 2019-06-25)\n\n- Uses correct Celery version for Python 3.7.\n- Prevents multiple creation and pushing of Flask application contexts.\n\nVersion 0.3.1 (released 2018-03-26)\n\n- Accounts for non-strict Celery versions.\n\nVersion 0.3.0 (released 2017-03-24)\n\n- Adds support for Celery v4.\n\nVersion 0.2.2 (released 2016-11-07)\n\n- Forces celery version to v3.1-4.0 due to problem with 4.x.\n\nVersion 0.2.1 (released 2016-07-25)\n\nImproved features\n\n- Improves documentation structure and its automatic generation.\n\nVersion 0.2.0 (released 2016-02-02)\n\nIncompatible changes\n\n- Changes celery application creation to use the default current\n celery application instead creating a new celery application. This\n addresses an issue with tasks using the shared_task decorator and\n having Flask-CeleryExt initialized multiple times.\n\nVersion 0.1.0 (released 2015-08-17)\n\n- Initial public release\n\n\n", + "release_date": "2020-02-17T10:27:20", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "flask celery", + "homepage_url": "https://github.com/inveniosoftware/flask-celeryext", + "download_url": "https://files.pythonhosted.org/packages/70/a0/74c30a11f96be5ad3bf5609b00fe91755b9eee2ce6cc0bba59c32614afe8/Flask_CeleryExt-0.3.4-py2.py3-none-any.whl", + "size": 10210, + "sha1": null, + "md5": "a2711529816648eaffbfd6934e7c9e74", + "sha256": "1c84b35462d41d1317800d256b2ce30031b7d3d20dd8dc680ce4f4cc88029867", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-celeryext/0.3.4/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-celeryext@0.3.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-celeryext", + "version": "0.3.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "=================\n Flask-CeleryExt\n=================\n\n.. image:: https://img.shields.io/travis/inveniosoftware/flask-celeryext.svg\n :target: https://travis-ci.org/inveniosoftware/flask-celeryext\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/flask-celeryext.svg\n :target: https://coveralls.io/r/inveniosoftware/flask-celeryext\n\n.. image:: https://img.shields.io/github/tag/inveniosoftware/flask-celeryext.svg\n :target: https://github.com/inveniosoftware/flask-celeryext/releases\n\n.. image:: https://img.shields.io/pypi/dm/flask-celeryext.svg\n :target: https://pypi.python.org/pypi/flask-celeryext\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/flask-celeryext.svg\n :target: https://github.com/inveniosoftware/flask-celeryext/blob/master/LICENSE\n\nAbout\n=====\n\nFlask-CeleryExt is a simple integration layer between Celery and Flask.\n\nInstallation\n============\n\nFlask-CeleryExt is on PyPI so all you need is: ::\n\n pip install flask-celeryext\n\nDocumentation\n=============\n\nDocumentation is readable at https://flask-celeryext.readthedocs.io/ or can be\nbuild using Sphinx: ::\n\n pip install Sphinx\n python setup.py build_sphinx\n\nTesting\n=======\n\nRunning the test suite is as simple as: ::\n\n python setup.py test\n\n\nChanges\n=======\n\nVersion 0.3.4 (released 2020-02-17)\n\n- Adds support for Python 3.8\n- Fixes pin for Celery on Python <3.7.\n\nVersion 0.3.3 (released 2020-02-13)\n\n- Fix celery version for Python < 3.7\n\nVersion 0.3.2 (released 2019-06-25)\n\n- Uses correct Celery version for Python 3.7.\n- Prevents multiple creation and pushing of Flask application contexts.\n\nVersion 0.3.1 (released 2018-03-26)\n\n- Accounts for non-strict Celery versions.\n\nVersion 0.3.0 (released 2017-03-24)\n\n- Adds support for Celery v4.\n\nVersion 0.2.2 (released 2016-11-07)\n\n- Forces celery version to v3.1-4.0 due to problem with 4.x.\n\nVersion 0.2.1 (released 2016-07-25)\n\nImproved features\n\n- Improves documentation structure and its automatic generation.\n\nVersion 0.2.0 (released 2016-02-02)\n\nIncompatible changes\n\n- Changes celery application creation to use the default current\n celery application instead creating a new celery application. This\n addresses an issue with tasks using the shared_task decorator and\n having Flask-CeleryExt initialized multiple times.\n\nVersion 0.1.0 (released 2015-08-17)\n\n- Initial public release\n\n\n", + "release_date": "2020-02-17T10:27:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "flask celery", + "homepage_url": "https://github.com/inveniosoftware/flask-celeryext", + "download_url": "https://files.pythonhosted.org/packages/3f/67/a048930d1c6349b7cc038738140a258c443c5b8f83043311972a53364833/Flask-CeleryExt-0.3.4.tar.gz", + "size": 17861, + "sha1": null, + "md5": "519373a4ec1742ae8295bccb0056590b", + "sha256": "47d5d18daebad300b215faca0d1c6da24625f333020482e27591634c05792c98", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-celeryext/0.3.4/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-celeryext@0.3.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-limiter", + "version": "1.1.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. |travis-ci| image:: https://img.shields.io/travis/alisaifee/flask-limiter/master.svg?style=flat-square\n :target: https://travis-ci.org/#!/alisaifee/flask-limiter?branch=master\n.. |coveralls| image:: https://img.shields.io/coveralls/alisaifee/flask-limiter/master.svg?style=flat-square\n :target: https://coveralls.io/r/alisaifee/flask-limiter?branch=master\n.. |pypi| image:: https://img.shields.io/pypi/v/Flask-Limiter.svg?style=flat-square\n :target: https://pypi.python.org/pypi/Flask-Limiter\n.. |license| image:: https://img.shields.io/pypi/l/Flask-Limiter.svg?style=flat-square\n :target: https://pypi.python.org/pypi/Flask-Limiter\n.. |hound| image:: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg?style=flat-square&longCache=true\n :target: https://houndci.com\n\n*************\nFlask-Limiter\n*************\n|travis-ci| |coveralls| |pypi| |license| |hound|\n\nFlask-Limiter provides rate limiting features to flask routes.\nIt has support for a configurable backend for storage\nwith current implementations for in-memory, redis and memcache.\n\nQuickstart\n===========\n\nAdd the rate limiter to your flask app. The following example uses the default\nin memory implementation for storage.\n\n.. code-block:: python\n\n from flask import Flask\n from flask_limiter import Limiter\n from flask_limiter.util import get_remote_address\n\n app = Flask(__name__)\n limiter = Limiter(\n app,\n key_func=get_remote_address,\n default_limits=[\"2 per minute\", \"1 per second\"],\n )\n\n @app.route(\"/slow\")\n @limiter.limit(\"1 per day\")\n def slow():\n return \"24\"\n\n @app.route(\"/fast\")\n def fast():\n return \"42\"\n\n @app.route(\"/ping\")\n @limiter.exempt\n def ping():\n return 'PONG'\n\n app.run()\n\n\n\nTest it out. The ``fast`` endpoint respects the default rate limit while the\n``slow`` endpoint uses the decorated one. ``ping`` has no rate limit associated\nwith it.\n\n.. code-block:: bash\n\n $ curl localhost:5000/fast\n 42\n $ curl localhost:5000/fast\n 42\n $ curl localhost:5000/fast\n \n 429 Too Many Requests\n

Too Many Requests

\n

2 per 1 minute

\n $ curl localhost:5000/slow\n 24\n $ curl localhost:5000/slow\n \n 429 Too Many Requests\n

Too Many Requests

\n

1 per 1 day

\n $ curl localhost:5000/ping\n PONG\n $ curl localhost:5000/ping\n PONG\n $ curl localhost:5000/ping\n PONG\n $ curl localhost:5000/ping\n PONG\n\n\n\n\n`Read the docs `_\n\n\n\n\n\n.. :changelog:\n\nChangelog\n=========\n\n1.1.0 2019-10-02\n----------------\n* Provide Rate limit information with Exception (`Pull Request 202 `_)\n* Respect existing Retry-After header values (`Pull Request 143 `_)\n* Documentation improvements\n\n1.0.1 2017-12-08\n----------------\n* Bug fix\n\n * Duplicate rate limits applied via application limits (`Issue 108 `_)\n\n1.0.0 2017-11-06\n----------------\n* Improved documentation for handling ip addresses for applications behind proxiues (`Issue 41 `_)\n* Execute rate limits for decorated routes in decorator instead of `before_request` (`Issue 67 `_)\n* Bug Fix\n\n * Python 3.5 Errors (`Issue 82 `_)\n * RATELIMIT_KEY_PREFIX configuration constant not used (`Issue 88 `_)\n * Can't use dynamic limit in `default_limits` (`Issue 94 `_)\n * Retry-After header always zero when using key prefix (`Issue 99 `_)\n\n0.9.5.1 2017-08-18\n------------------\n* Upgrade versioneer\n\n0.9.5 2017-07-26\n----------------\n* Add support for key prefixes\n\n0.9.4 2017-05-01\n----------------\n* Implemented application wide shared limits\n\n0.9.3 2016-03-14\n----------------\n* Allow `reset` of limiter storage if available\n\n0.9.2 2016-03-04\n----------------\n* Deprecation warning for default `key_func` `get_ipaddr`\n* Support for `Retry-After` header\n\n0.9.1 2015-11-21\n----------------\n* Re-expose `enabled` property on `Limiter` instance.\n\n0.9 2015-11-13\n--------------\n* In-memory fallback option for unresponsive storage\n* Rate limit exemption option per limit\n\n0.8.5 2015-10-05\n----------------\n* Bug fix for reported issues of missing (limits) dependency upon installation.\n\n0.8.4 2015-10-03\n----------------\n* Documentation tweaks.\n\n0.8.2 2015-09-17\n----------------\n* Remove outdated files from egg\n\n0.8.1 2015-08-06\n----------------\n* Fixed compatibility with latest version of **Flask-Restful**\n\n0.8 2015-06-07\n--------------\n* No functional change\n\n0.7.9 2015-04-02\n----------------\n* Bug fix for case sensitive `methods` whitelist for `limits` decorator\n\n0.7.8 2015-03-20\n----------------\n* Hotfix for dynamic limits with blueprints\n* Undocumented feature to pass storage options to underlying storage backend.\n\n0.7.6 2015-03-02\n----------------\n* `methods` keyword argument for `limits` decorator to specify specific http\n methods to apply the rate limit to.\n\n0.7.5 2015-02-16\n----------------\n* `Custom error messages `_.\n\n0.7.4 2015-02-03\n----------------\n* Use Werkzeug TooManyRequests as the exception raised when available.\n\n0.7.3 2015-01-30\n----------------\n* Bug Fix\n\n * Fix for version comparison when monkey patching Werkzeug\n (`Issue 24 `_)\n\n0.7.1 2015-01-09\n----------------\n* Refactor core storage & ratelimiting strategy out into the `limits `_ package.\n* Remove duplicate hits when stacked rate limits are in use and a rate limit is hit.\n\n0.7 2015-01-09\n--------------\n* Refactoring of RedisStorage for extensibility (`Issue 18 `_)\n* Bug fix: Correct default setting for enabling rate limit headers. (`Issue 22 `_)\n\n0.6.6 2014-10-21\n----------------\n* Bug fix\n\n * Fix for responses slower than rate limiting window.\n (`Issue 17 `_.)\n\n0.6.5 2014-10-01\n----------------\n* Bug fix: in memory storage thread safety\n\n0.6.4 2014-08-31\n----------------\n* Support for manually triggering rate limit check\n\n0.6.3 2014-08-26\n----------------\n* Header name overrides\n\n0.6.2 2014-07-13\n----------------\n* `Rate limiting for blueprints\n `_\n\n0.6.1 2014-07-11\n----------------\n* per http method rate limit separation (`Recipe\n `_)\n* documentation improvements\n\n0.6 2014-06-24\n--------------\n* `Shared limits between routes\n `_\n\n0.5 2014-06-13\n--------------\n* `Request Filters\n `_\n\n0.4.4 2014-06-13\n----------------\n* Bug fix\n\n * Werkzeug < 0.9 Compatibility\n (`Issue 6 `_.)\n\n0.4.3 2014-06-12\n----------------\n* Hotfix : use HTTPException instead of abort to play well with other\n extensions.\n\n0.4.2 2014-06-12\n----------------\n* Allow configuration overrides via extension constructor\n\n0.4.1 2014-06-04\n----------------\n* Improved implementation of moving-window X-RateLimit-Reset value.\n\n0.4 2014-05-28\n--------------\n* `Rate limiting headers\n `_\n\n0.3.2 2014-05-26\n----------------\n* Bug fix\n\n * Memory leak when using ``Limiter.storage.MemoryStorage``\n (`Issue 4 `_.)\n* Improved test coverage\n\n0.3.1 2014-02-20\n----------------\n* Strict version requirement on six\n* documentation tweaks\n\n0.3.0 2014-02-19\n----------------\n* improved logging support for multiple handlers\n* allow callables to be passed to ``Limiter.limit`` decorator to dynamically\n load rate limit strings.\n* add a global kill switch in flask config for all rate limits.\n* Bug fixes\n\n * default key function for rate limit domain wasn't accounting for\n X-Forwarded-For header.\n\n\n\n0.2.2 2014-02-18\n----------------\n* add new decorator to exempt routes from limiting.\n* Bug fixes\n\n * versioneer.py wasn't included in manifest.\n * configuration string for strategy was out of sync with docs.\n\n0.2.1 2014-02-15\n----------------\n* python 2.6 support via counter backport\n* source docs.\n\n0.2 2014-02-15\n--------------\n* Implemented configurable strategies for rate limiting.\n* Bug fixes\n\n * better locking for in-memory storage\n * multi threading support for memcached storage\n\n\n0.1.1 2014-02-14\n----------------\n* Bug fixes\n\n * fix initializing the extension without an app\n * don't rate limit static files\n\n\n0.1.0 2014-02-13\n----------------\n* first release.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "release_date": "2019-10-03T02:10:17", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ali-Akber Saifee", + "email": "ali@indydevs.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://flask-limiter.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/72/f3/68596cb061e1c7d5a7dfb3694de3f8845b908ea16296e762136f34727a65/Flask_Limiter-1.1.0-py2-none-any.whl", + "size": 13815, + "sha1": null, + "md5": "cba11edf61a190167e225aafeacedb33", + "sha256": "9087984ae7eeb862f93bf5b18477a5e5b1e0c907647ae74fba1c7e3f1de63d6f", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-limiter/1.1.0/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-limiter@1.1.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-limiter", + "version": "1.1.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. |travis-ci| image:: https://img.shields.io/travis/alisaifee/flask-limiter/master.svg?style=flat-square\n :target: https://travis-ci.org/#!/alisaifee/flask-limiter?branch=master\n.. |coveralls| image:: https://img.shields.io/coveralls/alisaifee/flask-limiter/master.svg?style=flat-square\n :target: https://coveralls.io/r/alisaifee/flask-limiter?branch=master\n.. |pypi| image:: https://img.shields.io/pypi/v/Flask-Limiter.svg?style=flat-square\n :target: https://pypi.python.org/pypi/Flask-Limiter\n.. |license| image:: https://img.shields.io/pypi/l/Flask-Limiter.svg?style=flat-square\n :target: https://pypi.python.org/pypi/Flask-Limiter\n.. |hound| image:: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg?style=flat-square&longCache=true\n :target: https://houndci.com\n\n*************\nFlask-Limiter\n*************\n|travis-ci| |coveralls| |pypi| |license| |hound|\n\nFlask-Limiter provides rate limiting features to flask routes.\nIt has support for a configurable backend for storage\nwith current implementations for in-memory, redis and memcache.\n\nQuickstart\n===========\n\nAdd the rate limiter to your flask app. The following example uses the default\nin memory implementation for storage.\n\n.. code-block:: python\n\n from flask import Flask\n from flask_limiter import Limiter\n from flask_limiter.util import get_remote_address\n\n app = Flask(__name__)\n limiter = Limiter(\n app,\n key_func=get_remote_address,\n default_limits=[\"2 per minute\", \"1 per second\"],\n )\n\n @app.route(\"/slow\")\n @limiter.limit(\"1 per day\")\n def slow():\n return \"24\"\n\n @app.route(\"/fast\")\n def fast():\n return \"42\"\n\n @app.route(\"/ping\")\n @limiter.exempt\n def ping():\n return 'PONG'\n\n app.run()\n\n\n\nTest it out. The ``fast`` endpoint respects the default rate limit while the\n``slow`` endpoint uses the decorated one. ``ping`` has no rate limit associated\nwith it.\n\n.. code-block:: bash\n\n $ curl localhost:5000/fast\n 42\n $ curl localhost:5000/fast\n 42\n $ curl localhost:5000/fast\n \n 429 Too Many Requests\n

Too Many Requests

\n

2 per 1 minute

\n $ curl localhost:5000/slow\n 24\n $ curl localhost:5000/slow\n \n 429 Too Many Requests\n

Too Many Requests

\n

1 per 1 day

\n $ curl localhost:5000/ping\n PONG\n $ curl localhost:5000/ping\n PONG\n $ curl localhost:5000/ping\n PONG\n $ curl localhost:5000/ping\n PONG\n\n\n\n\n`Read the docs `_\n\n\n\n\n\n.. :changelog:\n\nChangelog\n=========\n\n1.1.0 2019-10-02\n----------------\n* Provide Rate limit information with Exception (`Pull Request 202 `_)\n* Respect existing Retry-After header values (`Pull Request 143 `_)\n* Documentation improvements\n\n1.0.1 2017-12-08\n----------------\n* Bug fix\n\n * Duplicate rate limits applied via application limits (`Issue 108 `_)\n\n1.0.0 2017-11-06\n----------------\n* Improved documentation for handling ip addresses for applications behind proxiues (`Issue 41 `_)\n* Execute rate limits for decorated routes in decorator instead of `before_request` (`Issue 67 `_)\n* Bug Fix\n\n * Python 3.5 Errors (`Issue 82 `_)\n * RATELIMIT_KEY_PREFIX configuration constant not used (`Issue 88 `_)\n * Can't use dynamic limit in `default_limits` (`Issue 94 `_)\n * Retry-After header always zero when using key prefix (`Issue 99 `_)\n\n0.9.5.1 2017-08-18\n------------------\n* Upgrade versioneer\n\n0.9.5 2017-07-26\n----------------\n* Add support for key prefixes\n\n0.9.4 2017-05-01\n----------------\n* Implemented application wide shared limits\n\n0.9.3 2016-03-14\n----------------\n* Allow `reset` of limiter storage if available\n\n0.9.2 2016-03-04\n----------------\n* Deprecation warning for default `key_func` `get_ipaddr`\n* Support for `Retry-After` header\n\n0.9.1 2015-11-21\n----------------\n* Re-expose `enabled` property on `Limiter` instance.\n\n0.9 2015-11-13\n--------------\n* In-memory fallback option for unresponsive storage\n* Rate limit exemption option per limit\n\n0.8.5 2015-10-05\n----------------\n* Bug fix for reported issues of missing (limits) dependency upon installation.\n\n0.8.4 2015-10-03\n----------------\n* Documentation tweaks.\n\n0.8.2 2015-09-17\n----------------\n* Remove outdated files from egg\n\n0.8.1 2015-08-06\n----------------\n* Fixed compatibility with latest version of **Flask-Restful**\n\n0.8 2015-06-07\n--------------\n* No functional change\n\n0.7.9 2015-04-02\n----------------\n* Bug fix for case sensitive `methods` whitelist for `limits` decorator\n\n0.7.8 2015-03-20\n----------------\n* Hotfix for dynamic limits with blueprints\n* Undocumented feature to pass storage options to underlying storage backend.\n\n0.7.6 2015-03-02\n----------------\n* `methods` keyword argument for `limits` decorator to specify specific http\n methods to apply the rate limit to.\n\n0.7.5 2015-02-16\n----------------\n* `Custom error messages `_.\n\n0.7.4 2015-02-03\n----------------\n* Use Werkzeug TooManyRequests as the exception raised when available.\n\n0.7.3 2015-01-30\n----------------\n* Bug Fix\n\n * Fix for version comparison when monkey patching Werkzeug\n (`Issue 24 `_)\n\n0.7.1 2015-01-09\n----------------\n* Refactor core storage & ratelimiting strategy out into the `limits `_ package.\n* Remove duplicate hits when stacked rate limits are in use and a rate limit is hit.\n\n0.7 2015-01-09\n--------------\n* Refactoring of RedisStorage for extensibility (`Issue 18 `_)\n* Bug fix: Correct default setting for enabling rate limit headers. (`Issue 22 `_)\n\n0.6.6 2014-10-21\n----------------\n* Bug fix\n\n * Fix for responses slower than rate limiting window.\n (`Issue 17 `_.)\n\n0.6.5 2014-10-01\n----------------\n* Bug fix: in memory storage thread safety\n\n0.6.4 2014-08-31\n----------------\n* Support for manually triggering rate limit check\n\n0.6.3 2014-08-26\n----------------\n* Header name overrides\n\n0.6.2 2014-07-13\n----------------\n* `Rate limiting for blueprints\n `_\n\n0.6.1 2014-07-11\n----------------\n* per http method rate limit separation (`Recipe\n `_)\n* documentation improvements\n\n0.6 2014-06-24\n--------------\n* `Shared limits between routes\n `_\n\n0.5 2014-06-13\n--------------\n* `Request Filters\n `_\n\n0.4.4 2014-06-13\n----------------\n* Bug fix\n\n * Werkzeug < 0.9 Compatibility\n (`Issue 6 `_.)\n\n0.4.3 2014-06-12\n----------------\n* Hotfix : use HTTPException instead of abort to play well with other\n extensions.\n\n0.4.2 2014-06-12\n----------------\n* Allow configuration overrides via extension constructor\n\n0.4.1 2014-06-04\n----------------\n* Improved implementation of moving-window X-RateLimit-Reset value.\n\n0.4 2014-05-28\n--------------\n* `Rate limiting headers\n `_\n\n0.3.2 2014-05-26\n----------------\n* Bug fix\n\n * Memory leak when using ``Limiter.storage.MemoryStorage``\n (`Issue 4 `_.)\n* Improved test coverage\n\n0.3.1 2014-02-20\n----------------\n* Strict version requirement on six\n* documentation tweaks\n\n0.3.0 2014-02-19\n----------------\n* improved logging support for multiple handlers\n* allow callables to be passed to ``Limiter.limit`` decorator to dynamically\n load rate limit strings.\n* add a global kill switch in flask config for all rate limits.\n* Bug fixes\n\n * default key function for rate limit domain wasn't accounting for\n X-Forwarded-For header.\n\n\n\n0.2.2 2014-02-18\n----------------\n* add new decorator to exempt routes from limiting.\n* Bug fixes\n\n * versioneer.py wasn't included in manifest.\n * configuration string for strategy was out of sync with docs.\n\n0.2.1 2014-02-15\n----------------\n* python 2.6 support via counter backport\n* source docs.\n\n0.2 2014-02-15\n--------------\n* Implemented configurable strategies for rate limiting.\n* Bug fixes\n\n * better locking for in-memory storage\n * multi threading support for memcached storage\n\n\n0.1.1 2014-02-14\n----------------\n* Bug fixes\n\n * fix initializing the extension without an app\n * don't rate limit static files\n\n\n0.1.0 2014-02-13\n----------------\n* first release.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "release_date": "2019-10-03T02:10:24", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ali-Akber Saifee", + "email": "ali@indydevs.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://flask-limiter.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/96/a6/35fe99ef33b44ae33c212da20e8f545354f58cb0c77f8b6cdcfda9f5e9ad/Flask-Limiter-1.1.0.tar.gz", + "size": 92428, + "sha1": null, + "md5": "e96f02fa9092207eaeae96cf180f479a", + "sha256": "905c35cd87bf60c92fd87922ae23fe27aa5fb31980bab31fc00807adee9f5a55", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-limiter/1.1.0/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-limiter@1.1.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-shell-ipython", + "version": "0.4.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "# flask-shell-ipython\n\nReplace default `flask shell` command by similar command running IPython.\n\n## Usage\n\nJust install:\n\n pip install flask-shell-ipython\n\nAnd here are you go:\n\n > flask shell\n Python 3.5.1 (default, Mar 3 2016, 09:29:07)\n [GCC 5.3.0] on linux\n IPython: 5.0.0\n App: discharges [debug]\n Instance: /home/ei-grad/repos/discharges/instance\n\n In [1]:\n\n\n", + "release_date": "2019-05-06T07:59:29", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Andrew Grigorev", + "email": "andrew@ei-grad.ru", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://github.com/ei-grad/flask-shell-ipython", + "download_url": "https://files.pythonhosted.org/packages/10/6b/a45278cbff711cc7b8904bd38679a8cc249c4db825a76ae332302f59d398/flask_shell_ipython-0.4.1-py2.py3-none-any.whl", + "size": 3493, + "sha1": null, + "md5": "11e90b76846c933d8b752ccb1cd10c90", + "sha256": "f212b4fad6831edf652799c719cd05fd0716edfaa5506eb41ff9ef09109890d3", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-shell-ipython/0.4.1/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-shell-ipython@0.4.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-shell-ipython", + "version": "0.4.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "# flask-shell-ipython\n\nReplace default `flask shell` command by similar command running IPython.\n\n## Usage\n\nJust install:\n\n pip install flask-shell-ipython\n\nAnd here are you go:\n\n > flask shell\n Python 3.5.1 (default, Mar 3 2016, 09:29:07)\n [GCC 5.3.0] on linux\n IPython: 5.0.0\n App: discharges [debug]\n Instance: /home/ei-grad/repos/discharges/instance\n\n In [1]:\n\n\n", + "release_date": "2019-05-06T07:59:31", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Andrew Grigorev", + "email": "andrew@ei-grad.ru", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://github.com/ei-grad/flask-shell-ipython", + "download_url": "https://files.pythonhosted.org/packages/48/8e/ba861448b7590282519aea82ae39107c77d001fdc37b7af4c14ed0e0db77/flask-shell-ipython-0.4.1.tar.gz", + "size": 2090, + "sha1": null, + "md5": "fdb3375a368f3b26e03f2aad7c659b9b", + "sha256": "fb3b390f4dc03d7a960c62c5b51ce4deca19ceff77e4db3d4670012adc529ebd", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-shell-ipython/0.4.1/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-shell-ipython@0.4.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-talisman", + "version": "0.8.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Talisman: HTTP security headers for Flask\n=========================================\n\n|PyPI Version|\n\nTalisman is a small Flask extension that handles setting HTTP headers\nthat can help protect against a few common web application security\nissues.\n\nThe default configuration:\n\n- Forces all connects to ``https``, unless running with debug enabled.\n- Enables `HTTP Strict Transport\n Security `_.\n- Sets Flask's session cookie to ``secure``, so it will never be set if\n your application is somehow accessed via a non-secure connection.\n- Sets Flask's session cookie to ``httponly``, preventing JavaScript\n from being able to access its content. CSRF via Ajax uses a separate\n cookie and should be unaffected.\n- Sets Flask's session cookie to ``Lax``, preventing the cookie to be leaked\n in CSRF-prone request methods.\n- Sets\n `X-Frame-Options `_\n to ``SAMEORIGIN`` to avoid\n `clickjacking `_.\n- Sets `X-XSS-Protection\n `_\n to enable a cross site scripting filter for IE and Safari (note Chrome has\n removed this and Firefox never supported it).\n- Sets `X-Content-Type-Options\n `_\n to prevent content type sniffing.\n- Sets a strict `Content Security\n Policy `__\n of ``default-src: 'self'``. This is intended to almost completely\n prevent Cross Site Scripting (XSS) attacks. This is probably the only\n setting that you should reasonably change. See the\n `Content Security Policy`_ section.\n- Sets a strict `Referrer-Policy `_\n of ``strict-origin-when-cross-origin`` that governs which referrer information should be included with\n requests made.\n- Disables interest-cohort by default in the `Permissions-Policy `_\n like `Drupal `_ to enhance privacy protection.\n\n\nIn addition to Talisman, you **should always use a cross-site request\nforgery (CSRF) library**. It's highly recommended to use\n`Flask-SeaSurf `_,\nwhich is based on Django's excellent library.\n\nInstallation & Basic Usage\n--------------------------\n\nInstall via `pip `_:\n\n::\n\n pip install flask-talisman\n\nAfter installing, wrap your Flask app with a ``Talisman``:\n\n.. code:: python\n\n from flask import Flask\n from flask_talisman import Talisman\n\n app = Flask(__name__)\n Talisman(app)\n\n\nThere is also a full `Example App `_.\n\nOptions\n-------\n\n- ``force_https``, default ``True``, forces all non-debug connects to\n ``https`` (`about HTTPS `_).\n- ``force_https_permanent``, default ``False``, uses ``301`` instead of\n ``302`` for ``https`` redirects.\n\n- ``frame_options``, default ``SAMEORIGIN``, can be ``SAMEORIGIN``,\n ``DENY``, or ``ALLOWFROM`` (`about Frame Options `_).\n- ``frame_options_allow_from``, default ``None``, a string indicating\n the domains that are allowed to embed the site via iframe.\n\n- ``strict_transport_security``, default ``True``, whether to send HSTS\n headers (`about HSTS `_).\n- ``strict_transport_security_preload``, default ``False``, enables HSTS\n preloading. If you register your application with\n `Google's HSTS preload list `_,\n Firefox and Chrome will never load your site over a non-secure\n connection.\n- ``strict_transport_security_max_age``, default ``ONE_YEAR_IN_SECS``,\n length of time the browser will respect the HSTS header.\n- ``strict_transport_security_include_subdomains``, default ``True``,\n whether subdomains should also use HSTS.\n\n- ``content_security_policy``, default ``default-src: 'self'``, see the\n `Content Security Policy`_ section (`about Content Security Policy `_).\n- ``content_security_policy_nonce_in``, default ``[]``. Adds a per-request nonce\n value to the flask request object and also to the specified CSP header section.\n I.e. ``['script-src', 'style-src']``\n- ``content_security_policy_report_only``, default ``False``, whether to set\n the CSP header as \"report-only\" (as `Content-Security-Policy-Report-Only`)\n to ease deployment by disabling the policy enforcement by the browser,\n requires passing a value with the ``content_security_policy_report_uri``\n parameter\n- ``content_security_policy_report_uri``, default ``None``, a string\n indicating the report URI used for `CSP violation reports\n `_\n\n- ``referrer_policy``, default ``strict-origin-when-cross-origin``, a string\n that sets the Referrer Policy header to send a full URL when performing a same-origin\n request, only send the origin of the document to an equally secure destination\n (HTTPS->HTTPS), and send no header to a less secure destination (HTTPS->HTTP) (`about Referrer Policy `_).\n\n- ``feature_policy``, default ``{}``, see the `Feature Policy`_ section (`about Feature Policy `_).\n\n- ``permissions_policy``, default ``{'interest-cohort': '()'}``, see the `Permissions Policy`_ section (`about Permissions Policy `_).\n- ``document_policy``, default ``{}``, see the `Document Policy`_ section (`about Document Policy `_).\n\n- ``session_cookie_secure``, default ``True``, set the session cookie\n to ``secure``, preventing it from being sent over plain ``http`` (`about cookies (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)_`).\n- ``session_cookie_http_only``, default ``True``, set the session\n cookie to ``httponly``, preventing it from being read by JavaScript.\n- ``session_cookie_samesite``, default ``Lax``, set this to ``Strict`` to prevent the cookie from being sent by the browser to the target site in all cross-site browsing context, even when following a regular link.\n\n\n- ``force_file_save``, default ``False``, whether to set the\n `X-Download-Options `_\n header to ``noopen`` to prevent IE >= 8 to from opening file downloads\n directly and only save them instead.\n\n- ``x_content_type_options``, default ``True``, Protects against MIME sniffing vulnerabilities (`about Content Type Options `_).\n- ``x_xss_protection``, default ``True``, Protects against cross-site scripting (XSS) attacks (`about XSS Protection `_).\n\nFor a full list of (security) headers, check out: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers.\n\nPer-view options\n~~~~~~~~~~~~~~~~\n\nSometimes you want to change the policy for a specific view. The\n``force_https``, ``frame_options``, ``frame_options_allow_from``,\n`content_security_policy``, ``feature_policy``, ``permissions_policy``\nand ``document_policy`` options can be changed on a per-view basis.\n\n.. code:: python\n\n from flask import Flask\n from flask_talisman import Talisman, ALLOW_FROM\n\n app = Flask(__name__)\n talisman = Talisman(app)\n\n @app.route('/normal')\n def normal():\n return 'Normal'\n\n @app.route('/embeddable')\n @talisman(frame_options=ALLOW_FROM, frame_options_allow_from='*')\n def embeddable():\n return 'Embeddable'\n\nContent Security Policy\n-----------------------\n\nThe default content security policy is extremely strict and will\nprevent loading any resources that are not in the same domain as the\napplication. Most web applications will need to change this policy.\n\nA slightly more permissive policy is available at\n``flask_talisman.GOOGLE_CSP_POLICY``, which allows loading Google-hosted JS\nlibraries, fonts, and embeding media from YouTube and Maps.\n\nYou can and should create your own policy to suit your site's needs.\nHere's a few examples adapted from\n`MDN `_:\n\nExample 1\n~~~~~~~~~\n\nThis is the default policy. A web site administrator wants all content\nto come from the site's own origin (this excludes subdomains.)\n\n.. code:: python\n\n csp = {\n 'default-src': '\\'self\\''\n }\n talisman = Talisman(app, content_security_policy=csp)\n\nExample 2\n~~~~~~~~~\n\nA web site administrator wants to allow content from a trusted domain\nand all its subdomains (it doesn't have to be the same domain that the\nCSP is set on.)\n\n.. code:: python\n\n csp = {\n 'default-src': [\n '\\'self\\'',\n '*.trusted.com'\n ]\n }\n\nExample 3\n~~~~~~~~~\n\nA web site administrator wants to allow users of a web application to\ninclude images from any origin in their own content, but to restrict\naudio or video media to trusted providers, and all scripts only to a\nspecific server that hosts trusted code.\n\n.. code:: python\n\n csp = {\n 'default-src': '\\'self\\'',\n 'img-src': '*',\n 'media-src': [\n 'media1.com',\n 'media2.com',\n ],\n 'script-src': 'userscripts.example.com'\n }\n\nIn this example content is only permitted from the document's origin\nwith the following exceptions:\n\n- Images may loaded from anywhere (note the ``*`` wildcard).\n- Media is only allowed from media1.com and media2.com (and not from\n subdomains of those sites).\n- Executable script is only allowed from userscripts.example.com.\n\nExample 4\n~~~~~~~~~\n\nA web site administrator for an online banking site wants to ensure that\nall its content is loaded using SSL, in order to prevent attackers from\neavesdropping on requests.\n\n.. code:: python\n\n csp = {\n 'default-src': 'https://onlinebanking.jumbobank.com'\n }\n\nThe server only permits access to documents being loaded specifically\nover HTTPS through the single origin onlinebanking.jumbobank.com.\n\nExample 5\n~~~~~~~~~\n\nA web site administrator of a web mail site wants to allow HTML in\nemail, as well as images loaded from anywhere, but not JavaScript or\nother potentially dangerous content.\n\n.. code:: python\n\n csp = {\n 'default-src': [\n '\\'self\\'',\n '*.mailsite.com',\n ],\n 'img-src': '*'\n }\n\nNote that this example doesn't specify a ``script-src``; with the\nexample CSP, this site uses the setting specified by the ``default-src``\ndirective, which means that scripts can be loaded only from the\noriginating server.\n\nExample 6\n~~~~~~~~~\n\nA web site administrator wants to allow embedded scripts (which might\nbe generated dynamicially).\n\n.. code:: python\n\n csp = {\n 'default-src': '\\'self\\'',\n 'script-src': '\\'self\\'',\n }\n talisman = Talisman(\n app,\n content_security_policy=csp,\n content_security_policy_nonce_in=['script-src']\n )\n\nThe nonce needs to be added to the script tag in the template:\n\n.. code:: html\n\n \n\nNote that the CSP directive (`script-src` in the example) to which the `nonce-...`\nsource should be added needs to be defined explicitly.\n\nExample 7\n~~~~~~~~~\n\nA web site adminstrator wants to override the CSP directives via an\nenvironment variable which doesn't support specifying the policy as\na Python dictionary, e.g.:\n\n.. code:: bash\n\n export CSP_DIRECTIVES=\"default-src 'self'; image-src *\"\n python app.py\n\nThen in the app code you can read the CSP directives from the environment:\n\n.. code:: python\n\n import os\n from flask_talisman import Talisman, DEFAULT_CSP_POLICY\n\n talisman = Talisman(\n app,\n content_security_policy=os.environ.get(\"CSP_DIRECTIVES\", DEFAULT_CSP_POLICY),\n )\n\nAs you can see above the policy can be defined simply just like the official\nspecification requires the HTTP header to be set: As a semicolon separated\nlist of individual CSP directives.\n\nFeature Policy\n--------------\n\n**Note:** Feature Policy has largely been `renamed Permissions Policy `_\nin the latest draft and some features are likely to move to Document Policy.\nAt this writing, most browsers support the ``Feature-Policy`` HTTP Header name.\nSee the `Permissions Policy`_ and `Document Policy`_ sections below should you wish\nto set these.\n\nAlso note that the Feature Policy specification did not progress beyond the `draft https://wicg.github.io/feature-policy/`\nstage before being renamed, but is `supported in some form in most browsers\n`_.\n\nThe default feature policy is empty, as this is the default expected behaviour.\n\nGeolocation Example\n~~~~~~~~~~~~~~~~~~~\n\nDisable access to Geolocation interface.\n\n.. code:: python\n\n feature_policy = {\n 'geolocation': '\\'none\\''\n }\n talisman = Talisman(app, feature_policy=feature_policy)\n\nPermissions Policy\n------------------\n\nFeature Policy has been split into Permissions Policy and Document Policy but\nat this writing `browser support of Permissions Policy is very limited `_,\nand it is recommended to still set the ``Feature-Policy`` HTTP Header.\nPermission Policy support is included in Talisman for when this becomes more\nwidely supported.\n\nNote that the `Permission Policy is still an Working Draft `_.\n\nWhen the same feature or permission is set in both Feature Policy and Permission Policy,\nthe Permission Policy setting will take precedence in browsers that support both.\n\nIt should be noted that the syntax differs between Feature Policy and Permission Policy\nas can be seen from the ``geolocation`` examples provided.\n\nThe default Permissions Policy is ``interest-cohort=()``, which opts sites out of\n`Federated Learning of Cohorts `_ an interest-based advertising initiative.\n\nPermission Policy can be set either using a dictionary, or using a string.\n\nGeolocation and Microphone Example\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDisable access to Geolocation interface and Microphone using dictionary syntax\n\n.. code:: python\n\n permission_policy = {\n 'geolocation': '()',\n 'microphone': '()'\n }\n talisman = Talisman(app, permission_policy=permission_policy)\n\nDisable access to Geolocation interface and Microphone using string syntax\n\n.. code:: python\n\n permission_policy = 'geolocation=(), microphone=()'\n talisman = Talisman(app, permission_policy=permission_policy)\n\nDocument Policy\n---------------\n\nFeature Policy has been split into Permissions Policy and Document Policy but\nat this writing `browser support of Document Policy is very limited `_,\nand it is recommended to still set the ``Feature-Policy`` HTTP Header.\nDocument Policy support is included in Talisman for when this becomes more\nwidely supported.\n\nNote that the `Document Policy is still an Unofficial Draft `_.\n\nThe default Document Policy is empty, as this is the default expected behaviour.\n\nDocument Policy can be set either using a dictionary, or using a string.\n\nOversized-Images Example\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nForbid oversized-images using dictionary syntax:\n\n.. code:: python\n\n document_policy = {\n 'oversized-images': '?0'\n }\n talisman = Talisman(app, document_policy=document_policy)\n\nForbid oversized-images using string syntax:\n\n.. code:: python\n\n document_policy = 'oversized-images=?0'\n talisman = Talisman(app, document_policy=document_policy)\n\nDisclaimer\n----------\n\nThis code originated at Google, but is not an official Google product,\nexperimental or otherwise. It was forked on June 6th, 2021 from the\nunmaintained GoogleCloudPlatform/flask-talisman.\n\nThere is no silver bullet for web application security. Talisman can\nhelp, but security is more than just setting a few headers. Any\npublic-facing web application should have a comprehensive approach to\nsecurity.\n\n\nContributing changes\n--------------------\n\n- See `CONTRIBUTING.md`_\n\nLicensing\n---------\n\n- Apache 2.0 - See `LICENSE`_\n\n.. _LICENSE: https://github.com/wntrblm/flask-talisman/blob/master/LICENSE\n.. _CONTRIBUTING.md: https://github.com/wntrblm/flask-talisman/blob/master/CONTRIBUTING.md\n.. |PyPI Version| image:: https://img.shields.io/pypi/v/flask-talisman.svg\n :target: https://pypi.python.org/pypi/flask-talisman\n\n\n", + "release_date": "2021-06-14T12:28:49", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Alethea Katherine Flowers", + "email": "me@thea.codes", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "flask security https xss", + "homepage_url": "https://github.com/wntrblm/flask-talisman", + "download_url": "https://files.pythonhosted.org/packages/0a/f1/3c2a37a8053149521407bff4573cecca93d86b1f15a027e8cc4463da6261/flask_talisman-0.8.1-py2.py3-none-any.whl", + "size": 18899, + "sha1": null, + "md5": "dd316576c1854219466ca0b90b33c19d", + "sha256": "08a25360c771f7a79d6d4db2abfa71f7422e62a714418b671d69d6a201764d05", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Apache Software License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-talisman/0.8.1/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-talisman@0.8.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask-talisman", + "version": "0.8.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Talisman: HTTP security headers for Flask\n=========================================\n\n|PyPI Version|\n\nTalisman is a small Flask extension that handles setting HTTP headers\nthat can help protect against a few common web application security\nissues.\n\nThe default configuration:\n\n- Forces all connects to ``https``, unless running with debug enabled.\n- Enables `HTTP Strict Transport\n Security `_.\n- Sets Flask's session cookie to ``secure``, so it will never be set if\n your application is somehow accessed via a non-secure connection.\n- Sets Flask's session cookie to ``httponly``, preventing JavaScript\n from being able to access its content. CSRF via Ajax uses a separate\n cookie and should be unaffected.\n- Sets Flask's session cookie to ``Lax``, preventing the cookie to be leaked\n in CSRF-prone request methods.\n- Sets\n `X-Frame-Options `_\n to ``SAMEORIGIN`` to avoid\n `clickjacking `_.\n- Sets `X-XSS-Protection\n `_\n to enable a cross site scripting filter for IE and Safari (note Chrome has\n removed this and Firefox never supported it).\n- Sets `X-Content-Type-Options\n `_\n to prevent content type sniffing.\n- Sets a strict `Content Security\n Policy `__\n of ``default-src: 'self'``. This is intended to almost completely\n prevent Cross Site Scripting (XSS) attacks. This is probably the only\n setting that you should reasonably change. See the\n `Content Security Policy`_ section.\n- Sets a strict `Referrer-Policy `_\n of ``strict-origin-when-cross-origin`` that governs which referrer information should be included with\n requests made.\n- Disables interest-cohort by default in the `Permissions-Policy `_\n like `Drupal `_ to enhance privacy protection.\n\n\nIn addition to Talisman, you **should always use a cross-site request\nforgery (CSRF) library**. It's highly recommended to use\n`Flask-SeaSurf `_,\nwhich is based on Django's excellent library.\n\nInstallation & Basic Usage\n--------------------------\n\nInstall via `pip `_:\n\n::\n\n pip install flask-talisman\n\nAfter installing, wrap your Flask app with a ``Talisman``:\n\n.. code:: python\n\n from flask import Flask\n from flask_talisman import Talisman\n\n app = Flask(__name__)\n Talisman(app)\n\n\nThere is also a full `Example App `_.\n\nOptions\n-------\n\n- ``force_https``, default ``True``, forces all non-debug connects to\n ``https`` (`about HTTPS `_).\n- ``force_https_permanent``, default ``False``, uses ``301`` instead of\n ``302`` for ``https`` redirects.\n\n- ``frame_options``, default ``SAMEORIGIN``, can be ``SAMEORIGIN``,\n ``DENY``, or ``ALLOWFROM`` (`about Frame Options `_).\n- ``frame_options_allow_from``, default ``None``, a string indicating\n the domains that are allowed to embed the site via iframe.\n\n- ``strict_transport_security``, default ``True``, whether to send HSTS\n headers (`about HSTS `_).\n- ``strict_transport_security_preload``, default ``False``, enables HSTS\n preloading. If you register your application with\n `Google's HSTS preload list `_,\n Firefox and Chrome will never load your site over a non-secure\n connection.\n- ``strict_transport_security_max_age``, default ``ONE_YEAR_IN_SECS``,\n length of time the browser will respect the HSTS header.\n- ``strict_transport_security_include_subdomains``, default ``True``,\n whether subdomains should also use HSTS.\n\n- ``content_security_policy``, default ``default-src: 'self'``, see the\n `Content Security Policy`_ section (`about Content Security Policy `_).\n- ``content_security_policy_nonce_in``, default ``[]``. Adds a per-request nonce\n value to the flask request object and also to the specified CSP header section.\n I.e. ``['script-src', 'style-src']``\n- ``content_security_policy_report_only``, default ``False``, whether to set\n the CSP header as \"report-only\" (as `Content-Security-Policy-Report-Only`)\n to ease deployment by disabling the policy enforcement by the browser,\n requires passing a value with the ``content_security_policy_report_uri``\n parameter\n- ``content_security_policy_report_uri``, default ``None``, a string\n indicating the report URI used for `CSP violation reports\n `_\n\n- ``referrer_policy``, default ``strict-origin-when-cross-origin``, a string\n that sets the Referrer Policy header to send a full URL when performing a same-origin\n request, only send the origin of the document to an equally secure destination\n (HTTPS->HTTPS), and send no header to a less secure destination (HTTPS->HTTP) (`about Referrer Policy `_).\n\n- ``feature_policy``, default ``{}``, see the `Feature Policy`_ section (`about Feature Policy `_).\n\n- ``permissions_policy``, default ``{'interest-cohort': '()'}``, see the `Permissions Policy`_ section (`about Permissions Policy `_).\n- ``document_policy``, default ``{}``, see the `Document Policy`_ section (`about Document Policy `_).\n\n- ``session_cookie_secure``, default ``True``, set the session cookie\n to ``secure``, preventing it from being sent over plain ``http`` (`about cookies (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)_`).\n- ``session_cookie_http_only``, default ``True``, set the session\n cookie to ``httponly``, preventing it from being read by JavaScript.\n- ``session_cookie_samesite``, default ``Lax``, set this to ``Strict`` to prevent the cookie from being sent by the browser to the target site in all cross-site browsing context, even when following a regular link.\n\n\n- ``force_file_save``, default ``False``, whether to set the\n `X-Download-Options `_\n header to ``noopen`` to prevent IE >= 8 to from opening file downloads\n directly and only save them instead.\n\n- ``x_content_type_options``, default ``True``, Protects against MIME sniffing vulnerabilities (`about Content Type Options `_).\n- ``x_xss_protection``, default ``True``, Protects against cross-site scripting (XSS) attacks (`about XSS Protection `_).\n\nFor a full list of (security) headers, check out: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers.\n\nPer-view options\n~~~~~~~~~~~~~~~~\n\nSometimes you want to change the policy for a specific view. The\n``force_https``, ``frame_options``, ``frame_options_allow_from``,\n`content_security_policy``, ``feature_policy``, ``permissions_policy``\nand ``document_policy`` options can be changed on a per-view basis.\n\n.. code:: python\n\n from flask import Flask\n from flask_talisman import Talisman, ALLOW_FROM\n\n app = Flask(__name__)\n talisman = Talisman(app)\n\n @app.route('/normal')\n def normal():\n return 'Normal'\n\n @app.route('/embeddable')\n @talisman(frame_options=ALLOW_FROM, frame_options_allow_from='*')\n def embeddable():\n return 'Embeddable'\n\nContent Security Policy\n-----------------------\n\nThe default content security policy is extremely strict and will\nprevent loading any resources that are not in the same domain as the\napplication. Most web applications will need to change this policy.\n\nA slightly more permissive policy is available at\n``flask_talisman.GOOGLE_CSP_POLICY``, which allows loading Google-hosted JS\nlibraries, fonts, and embeding media from YouTube and Maps.\n\nYou can and should create your own policy to suit your site's needs.\nHere's a few examples adapted from\n`MDN `_:\n\nExample 1\n~~~~~~~~~\n\nThis is the default policy. A web site administrator wants all content\nto come from the site's own origin (this excludes subdomains.)\n\n.. code:: python\n\n csp = {\n 'default-src': '\\'self\\''\n }\n talisman = Talisman(app, content_security_policy=csp)\n\nExample 2\n~~~~~~~~~\n\nA web site administrator wants to allow content from a trusted domain\nand all its subdomains (it doesn't have to be the same domain that the\nCSP is set on.)\n\n.. code:: python\n\n csp = {\n 'default-src': [\n '\\'self\\'',\n '*.trusted.com'\n ]\n }\n\nExample 3\n~~~~~~~~~\n\nA web site administrator wants to allow users of a web application to\ninclude images from any origin in their own content, but to restrict\naudio or video media to trusted providers, and all scripts only to a\nspecific server that hosts trusted code.\n\n.. code:: python\n\n csp = {\n 'default-src': '\\'self\\'',\n 'img-src': '*',\n 'media-src': [\n 'media1.com',\n 'media2.com',\n ],\n 'script-src': 'userscripts.example.com'\n }\n\nIn this example content is only permitted from the document's origin\nwith the following exceptions:\n\n- Images may loaded from anywhere (note the ``*`` wildcard).\n- Media is only allowed from media1.com and media2.com (and not from\n subdomains of those sites).\n- Executable script is only allowed from userscripts.example.com.\n\nExample 4\n~~~~~~~~~\n\nA web site administrator for an online banking site wants to ensure that\nall its content is loaded using SSL, in order to prevent attackers from\neavesdropping on requests.\n\n.. code:: python\n\n csp = {\n 'default-src': 'https://onlinebanking.jumbobank.com'\n }\n\nThe server only permits access to documents being loaded specifically\nover HTTPS through the single origin onlinebanking.jumbobank.com.\n\nExample 5\n~~~~~~~~~\n\nA web site administrator of a web mail site wants to allow HTML in\nemail, as well as images loaded from anywhere, but not JavaScript or\nother potentially dangerous content.\n\n.. code:: python\n\n csp = {\n 'default-src': [\n '\\'self\\'',\n '*.mailsite.com',\n ],\n 'img-src': '*'\n }\n\nNote that this example doesn't specify a ``script-src``; with the\nexample CSP, this site uses the setting specified by the ``default-src``\ndirective, which means that scripts can be loaded only from the\noriginating server.\n\nExample 6\n~~~~~~~~~\n\nA web site administrator wants to allow embedded scripts (which might\nbe generated dynamicially).\n\n.. code:: python\n\n csp = {\n 'default-src': '\\'self\\'',\n 'script-src': '\\'self\\'',\n }\n talisman = Talisman(\n app,\n content_security_policy=csp,\n content_security_policy_nonce_in=['script-src']\n )\n\nThe nonce needs to be added to the script tag in the template:\n\n.. code:: html\n\n \n\nNote that the CSP directive (`script-src` in the example) to which the `nonce-...`\nsource should be added needs to be defined explicitly.\n\nExample 7\n~~~~~~~~~\n\nA web site adminstrator wants to override the CSP directives via an\nenvironment variable which doesn't support specifying the policy as\na Python dictionary, e.g.:\n\n.. code:: bash\n\n export CSP_DIRECTIVES=\"default-src 'self'; image-src *\"\n python app.py\n\nThen in the app code you can read the CSP directives from the environment:\n\n.. code:: python\n\n import os\n from flask_talisman import Talisman, DEFAULT_CSP_POLICY\n\n talisman = Talisman(\n app,\n content_security_policy=os.environ.get(\"CSP_DIRECTIVES\", DEFAULT_CSP_POLICY),\n )\n\nAs you can see above the policy can be defined simply just like the official\nspecification requires the HTTP header to be set: As a semicolon separated\nlist of individual CSP directives.\n\nFeature Policy\n--------------\n\n**Note:** Feature Policy has largely been `renamed Permissions Policy `_\nin the latest draft and some features are likely to move to Document Policy.\nAt this writing, most browsers support the ``Feature-Policy`` HTTP Header name.\nSee the `Permissions Policy`_ and `Document Policy`_ sections below should you wish\nto set these.\n\nAlso note that the Feature Policy specification did not progress beyond the `draft https://wicg.github.io/feature-policy/`\nstage before being renamed, but is `supported in some form in most browsers\n`_.\n\nThe default feature policy is empty, as this is the default expected behaviour.\n\nGeolocation Example\n~~~~~~~~~~~~~~~~~~~\n\nDisable access to Geolocation interface.\n\n.. code:: python\n\n feature_policy = {\n 'geolocation': '\\'none\\''\n }\n talisman = Talisman(app, feature_policy=feature_policy)\n\nPermissions Policy\n------------------\n\nFeature Policy has been split into Permissions Policy and Document Policy but\nat this writing `browser support of Permissions Policy is very limited `_,\nand it is recommended to still set the ``Feature-Policy`` HTTP Header.\nPermission Policy support is included in Talisman for when this becomes more\nwidely supported.\n\nNote that the `Permission Policy is still an Working Draft `_.\n\nWhen the same feature or permission is set in both Feature Policy and Permission Policy,\nthe Permission Policy setting will take precedence in browsers that support both.\n\nIt should be noted that the syntax differs between Feature Policy and Permission Policy\nas can be seen from the ``geolocation`` examples provided.\n\nThe default Permissions Policy is ``interest-cohort=()``, which opts sites out of\n`Federated Learning of Cohorts `_ an interest-based advertising initiative.\n\nPermission Policy can be set either using a dictionary, or using a string.\n\nGeolocation and Microphone Example\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDisable access to Geolocation interface and Microphone using dictionary syntax\n\n.. code:: python\n\n permission_policy = {\n 'geolocation': '()',\n 'microphone': '()'\n }\n talisman = Talisman(app, permission_policy=permission_policy)\n\nDisable access to Geolocation interface and Microphone using string syntax\n\n.. code:: python\n\n permission_policy = 'geolocation=(), microphone=()'\n talisman = Talisman(app, permission_policy=permission_policy)\n\nDocument Policy\n---------------\n\nFeature Policy has been split into Permissions Policy and Document Policy but\nat this writing `browser support of Document Policy is very limited `_,\nand it is recommended to still set the ``Feature-Policy`` HTTP Header.\nDocument Policy support is included in Talisman for when this becomes more\nwidely supported.\n\nNote that the `Document Policy is still an Unofficial Draft `_.\n\nThe default Document Policy is empty, as this is the default expected behaviour.\n\nDocument Policy can be set either using a dictionary, or using a string.\n\nOversized-Images Example\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nForbid oversized-images using dictionary syntax:\n\n.. code:: python\n\n document_policy = {\n 'oversized-images': '?0'\n }\n talisman = Talisman(app, document_policy=document_policy)\n\nForbid oversized-images using string syntax:\n\n.. code:: python\n\n document_policy = 'oversized-images=?0'\n talisman = Talisman(app, document_policy=document_policy)\n\nDisclaimer\n----------\n\nThis code originated at Google, but is not an official Google product,\nexperimental or otherwise. It was forked on June 6th, 2021 from the\nunmaintained GoogleCloudPlatform/flask-talisman.\n\nThere is no silver bullet for web application security. Talisman can\nhelp, but security is more than just setting a few headers. Any\npublic-facing web application should have a comprehensive approach to\nsecurity.\n\n\nContributing changes\n--------------------\n\n- See `CONTRIBUTING.md`_\n\nLicensing\n---------\n\n- Apache 2.0 - See `LICENSE`_\n\n.. _LICENSE: https://github.com/wntrblm/flask-talisman/blob/master/LICENSE\n.. _CONTRIBUTING.md: https://github.com/wntrblm/flask-talisman/blob/master/CONTRIBUTING.md\n.. |PyPI Version| image:: https://img.shields.io/pypi/v/flask-talisman.svg\n :target: https://pypi.python.org/pypi/flask-talisman\n\n\n", + "release_date": "2021-06-14T12:28:50", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Alethea Katherine Flowers", + "email": "me@thea.codes", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "flask security https xss", + "homepage_url": "https://github.com/wntrblm/flask-talisman", + "download_url": "https://files.pythonhosted.org/packages/dd/1a/9f21ccb72a0d09594eb704da7f87a6d373ad7e9d4ac18693d1a3c275afb2/flask-talisman-0.8.1.tar.gz", + "size": 23799, + "sha1": null, + "md5": "306909acdb85448cd33bab9d101e793e", + "sha256": "5d502ec0c51bf1755a797b8cffbe4e94f8684af712ba0b56f3d80b79277ef285", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Apache Software License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask-talisman/0.8.1/json", + "datasource_id": null, + "purl": "pkg:pypi/flask-talisman@0.8.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask", + "version": "1.1.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Flask\n=====\n\nFlask is a lightweight `WSGI`_ web application framework. It is designed\nto make getting started quick and easy, with the ability to scale up to\ncomplex applications. It began as a simple wrapper around `Werkzeug`_\nand `Jinja`_ and has become one of the most popular Python web\napplication frameworks.\n\nFlask offers suggestions, but doesn't enforce any dependencies or\nproject layout. It is up to the developer to choose the tools and\nlibraries they want to use. There are many extensions provided by the\ncommunity that make adding new functionality easy.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U Flask\n\n\nA Simple Example\n----------------\n\n.. code-block:: python\n\n from flask import Flask\n\n app = Flask(__name__)\n\n @app.route(\"/\")\n def hello():\n return \"Hello, World!\"\n\n.. code-block:: text\n\n $ env FLASK_APP=hello.py flask run\n * Serving Flask app \"hello\"\n * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n\n\nContributing\n------------\n\nFor guidance on setting up a development environment and how to make a\ncontribution to Flask, see the `contributing guidelines`_.\n\n.. _contributing guidelines: https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst\n\n\nDonate\n------\n\nThe Pallets organization develops and supports Flask and the libraries\nit uses. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, `please\ndonate today`_.\n\n.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20\n\n\nLinks\n-----\n\n* Website: https://palletsprojects.com/p/flask/\n* Documentation: https://flask.palletsprojects.com/\n* Releases: https://pypi.org/project/Flask/\n* Code: https://github.com/pallets/flask\n* Issue tracker: https://github.com/pallets/flask/issues\n* Test status: https://dev.azure.com/pallets/flask/_build\n* Official chat: https://discord.gg/t6rrQZH\n\n.. _WSGI: https://wsgi.readthedocs.io\n.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/\n.. _Jinja: https://www.palletsprojects.com/p/jinja/\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\n", + "release_date": "2021-05-14T01:45:55", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/flask/", + "download_url": "https://files.pythonhosted.org/packages/e8/6d/994208daa354f68fd89a34a8bafbeaab26fda84e7af1e35bdaed02b667e6/Flask-1.1.4-py2.py3-none-any.whl", + "size": 94591, + "sha1": null, + "md5": "6e579a6228c0333dd5b61c5bd214ea05", + "sha256": "c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/flask", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask/1.1.4/json", + "datasource_id": null, + "purl": "pkg:pypi/flask@1.1.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "flask", + "version": "1.1.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Flask\n=====\n\nFlask is a lightweight `WSGI`_ web application framework. It is designed\nto make getting started quick and easy, with the ability to scale up to\ncomplex applications. It began as a simple wrapper around `Werkzeug`_\nand `Jinja`_ and has become one of the most popular Python web\napplication frameworks.\n\nFlask offers suggestions, but doesn't enforce any dependencies or\nproject layout. It is up to the developer to choose the tools and\nlibraries they want to use. There are many extensions provided by the\ncommunity that make adding new functionality easy.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U Flask\n\n\nA Simple Example\n----------------\n\n.. code-block:: python\n\n from flask import Flask\n\n app = Flask(__name__)\n\n @app.route(\"/\")\n def hello():\n return \"Hello, World!\"\n\n.. code-block:: text\n\n $ env FLASK_APP=hello.py flask run\n * Serving Flask app \"hello\"\n * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n\n\nContributing\n------------\n\nFor guidance on setting up a development environment and how to make a\ncontribution to Flask, see the `contributing guidelines`_.\n\n.. _contributing guidelines: https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst\n\n\nDonate\n------\n\nThe Pallets organization develops and supports Flask and the libraries\nit uses. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, `please\ndonate today`_.\n\n.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20\n\n\nLinks\n-----\n\n* Website: https://palletsprojects.com/p/flask/\n* Documentation: https://flask.palletsprojects.com/\n* Releases: https://pypi.org/project/Flask/\n* Code: https://github.com/pallets/flask\n* Issue tracker: https://github.com/pallets/flask/issues\n* Test status: https://dev.azure.com/pallets/flask/_build\n* Official chat: https://discord.gg/t6rrQZH\n\n.. _WSGI: https://wsgi.readthedocs.io\n.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/\n.. _Jinja: https://www.palletsprojects.com/p/jinja/\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\n", + "release_date": "2021-05-14T01:45:58", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/flask/", + "download_url": "https://files.pythonhosted.org/packages/4d/5b/2d145f5fe718b2f15ebe69240538f06faa8bbb76488bf962091db1f7a26d/Flask-1.1.4.tar.gz", + "size": 635920, + "sha1": null, + "md5": "49c23fb3096ee548f9737bbddc934c41", + "sha256": "0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/flask", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/flask/1.1.4/json", + "datasource_id": null, + "purl": "pkg:pypi/flask@1.1.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "funcsigs", + "version": "1.0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. funcsigs documentation master file, created by\n sphinx-quickstart on Fri Apr 20 20:27:52 2012.\n You can adapt this file completely to your liking, but it should at least\n contain the root `toctree` directive.\n\nIntroducing funcsigs\n====================\n\nThe Funcsigs Package\n--------------------\n\n``funcsigs`` is a backport of the `PEP 362`_ function signature features from\nPython 3.3's `inspect`_ module. The backport is compatible with Python 2.6, 2.7\nas well as 3.3 and up. 3.2 was supported by version 0.4, but with setuptools and\npip no longer supporting 3.2, we cannot make any statement about 3.2\ncompatibility.\n\nCompatibility\n`````````````\n\nThe ``funcsigs`` backport has been tested against:\n\n* CPython 2.6\n* CPython 2.7\n* CPython 3.3\n* CPython 3.4\n* CPython 3.5\n* CPython nightlies\n* PyPy and PyPy3(currently failing CI)\n\nContinuous integration testing is provided by `Travis CI`_.\n\nUnder Python 2.x there is a compatibility issue when a function is assigned to\nthe ``__wrapped__`` property of a class after it has been constructed.\nSimiliarily there under PyPy directly passing the ``__call__`` method of a\nbuiltin is also a compatibility issues. Otherwise the functionality is\nbelieved to be uniform between both Python2 and Python3.\n\nIssues\n``````\n\nSource code for ``funcsigs`` is hosted on `GitHub`_. Any bug reports or feature\nrequests can be made using GitHub's `issues system`_. |build_status| |coverage|\n\nExample\n-------\n\nTo obtain a `Signature` object, pass the target function to the\n``funcsigs.signature`` function.\n\n.. code-block:: python\n\n >>> from funcsigs import signature\n >>> def foo(a, b=None, *args, **kwargs):\n ... pass\n ...\n >>> sig = signature(foo)\n >>> sig\n \n >>> sig.parameters\n OrderedDict([('a', ), ('b', ), ('args', ), ('kwargs', )])\n >>> sig.return_annotation\n \n\nIntrospecting callables with the Signature object\n-------------------------------------------------\n\n.. note::\n\n This section of documentation is a direct reproduction of the Python\n standard library documentation for the inspect module.\n\nThe Signature object represents the call signature of a callable object and its\nreturn annotation. To retrieve a Signature object, use the :func:`signature`\nfunction.\n\n.. function:: signature(callable)\n\n Return a :class:`Signature` object for the given ``callable``::\n\n >>> from funcsigs import signature\n >>> def foo(a, *, b:int, **kwargs):\n ... pass\n\n >>> sig = signature(foo)\n\n >>> str(sig)\n '(a, *, b:int, **kwargs)'\n\n >>> str(sig.parameters['b'])\n 'b:int'\n\n >>> sig.parameters['b'].annotation\n \n\n Accepts a wide range of python callables, from plain functions and classes to\n :func:`functools.partial` objects.\n\n .. note::\n\n Some callables may not be introspectable in certain implementations of\n Python. For example, in CPython, built-in functions defined in C provide\n no metadata about their arguments.\n\n\n.. class:: Signature\n\n A Signature object represents the call signature of a function and its return\n annotation. For each parameter accepted by the function it stores a\n :class:`Parameter` object in its :attr:`parameters` collection.\n\n Signature objects are *immutable*. Use :meth:`Signature.replace` to make a\n modified copy.\n\n .. attribute:: Signature.empty\n\n A special class-level marker to specify absence of a return annotation.\n\n .. attribute:: Signature.parameters\n\n An ordered mapping of parameters' names to the corresponding\n :class:`Parameter` objects.\n\n .. attribute:: Signature.return_annotation\n\n The \"return\" annotation for the callable. If the callable has no \"return\"\n annotation, this attribute is set to :attr:`Signature.empty`.\n\n .. method:: Signature.bind(*args, **kwargs)\n\n Create a mapping from positional and keyword arguments to parameters.\n Returns :class:`BoundArguments` if ``*args`` and ``**kwargs`` match the\n signature, or raises a :exc:`TypeError`.\n\n .. method:: Signature.bind_partial(*args, **kwargs)\n\n Works the same way as :meth:`Signature.bind`, but allows the omission of\n some required arguments (mimics :func:`functools.partial` behavior.)\n Returns :class:`BoundArguments`, or raises a :exc:`TypeError` if the\n passed arguments do not match the signature.\n\n .. method:: Signature.replace(*[, parameters][, return_annotation])\n\n Create a new Signature instance based on the instance replace was invoked\n on. It is possible to pass different ``parameters`` and/or\n ``return_annotation`` to override the corresponding properties of the base\n signature. To remove return_annotation from the copied Signature, pass in\n :attr:`Signature.empty`.\n\n ::\n\n >>> def test(a, b):\n ... pass\n >>> sig = signature(test)\n >>> new_sig = sig.replace(return_annotation=\"new return anno\")\n >>> str(new_sig)\n \"(a, b) -> 'new return anno'\"\n\n\n.. class:: Parameter\n\n Parameter objects are *immutable*. Instead of modifying a Parameter object,\n you can use :meth:`Parameter.replace` to create a modified copy.\n\n .. attribute:: Parameter.empty\n\n A special class-level marker to specify absence of default values and\n annotations.\n\n .. attribute:: Parameter.name\n\n The name of the parameter as a string. Must be a valid python identifier\n name (with the exception of ``POSITIONAL_ONLY`` parameters, which can have\n it set to ``None``).\n\n .. attribute:: Parameter.default\n\n The default value for the parameter. If the parameter has no default\n value, this attribute is set to :attr:`Parameter.empty`.\n\n .. attribute:: Parameter.annotation\n\n The annotation for the parameter. If the parameter has no annotation,\n this attribute is set to :attr:`Parameter.empty`.\n\n .. attribute:: Parameter.kind\n\n Describes how argument values are bound to the parameter. Possible values\n (accessible via :class:`Parameter`, like ``Parameter.KEYWORD_ONLY``):\n\n +------------------------+----------------------------------------------+\n | Name | Meaning |\n +========================+==============================================+\n | *POSITIONAL_ONLY* | Value must be supplied as a positional |\n | | argument. |\n | | |\n | | Python has no explicit syntax for defining |\n | | positional-only parameters, but many built-in|\n | | and extension module functions (especially |\n | | those that accept only one or two parameters)|\n | | accept them. |\n +------------------------+----------------------------------------------+\n | *POSITIONAL_OR_KEYWORD*| Value may be supplied as either a keyword or |\n | | positional argument (this is the standard |\n | | binding behaviour for functions implemented |\n | | in Python.) |\n +------------------------+----------------------------------------------+\n | *VAR_POSITIONAL* | A tuple of positional arguments that aren't |\n | | bound to any other parameter. This |\n | | corresponds to a ``*args`` parameter in a |\n | | Python function definition. |\n +------------------------+----------------------------------------------+\n | *KEYWORD_ONLY* | Value must be supplied as a keyword argument.|\n | | Keyword only parameters are those which |\n | | appear after a ``*`` or ``*args`` entry in a |\n | | Python function definition. |\n +------------------------+----------------------------------------------+\n | *VAR_KEYWORD* | A dict of keyword arguments that aren't bound|\n | | to any other parameter. This corresponds to a|\n | | ``**kwargs`` parameter in a Python function |\n | | definition. |\n +------------------------+----------------------------------------------+\n\n Example: print all keyword-only arguments without default values::\n\n >>> def foo(a, b, *, c, d=10):\n ... pass\n\n >>> sig = signature(foo)\n >>> for param in sig.parameters.values():\n ... if (param.kind == param.KEYWORD_ONLY and\n ... param.default is param.empty):\n ... print('Parameter:', param)\n Parameter: c\n\n .. method:: Parameter.replace(*[, name][, kind][, default][, annotation])\n\n Create a new Parameter instance based on the instance replaced was invoked\n on. To override a :class:`Parameter` attribute, pass the corresponding\n argument. To remove a default value or/and an annotation from a\n Parameter, pass :attr:`Parameter.empty`.\n\n ::\n\n >>> from funcsigs import Parameter\n >>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42)\n >>> str(param)\n 'foo=42'\n\n >>> str(param.replace()) # Will create a shallow copy of 'param'\n 'foo=42'\n\n >>> str(param.replace(default=Parameter.empty, annotation='spam'))\n \"foo:'spam'\"\n\n\n.. class:: BoundArguments\n\n Result of a :meth:`Signature.bind` or :meth:`Signature.bind_partial` call.\n Holds the mapping of arguments to the function's parameters.\n\n .. attribute:: BoundArguments.arguments\n\n An ordered, mutable mapping (:class:`collections.OrderedDict`) of\n parameters' names to arguments' values. Contains only explicitly bound\n arguments. Changes in :attr:`arguments` will reflect in :attr:`args` and\n :attr:`kwargs`.\n\n Should be used in conjunction with :attr:`Signature.parameters` for any\n argument processing purposes.\n\n .. note::\n\n Arguments for which :meth:`Signature.bind` or\n :meth:`Signature.bind_partial` relied on a default value are skipped.\n However, if needed, it is easy to include them.\n\n ::\n\n >>> def foo(a, b=10):\n ... pass\n\n >>> sig = signature(foo)\n >>> ba = sig.bind(5)\n\n >>> ba.args, ba.kwargs\n ((5,), {})\n\n >>> for param in sig.parameters.values():\n ... if param.name not in ba.arguments:\n ... ba.arguments[param.name] = param.default\n\n >>> ba.args, ba.kwargs\n ((5, 10), {})\n\n\n .. attribute:: BoundArguments.args\n\n A tuple of positional arguments values. Dynamically computed from the\n :attr:`arguments` attribute.\n\n .. attribute:: BoundArguments.kwargs\n\n A dict of keyword arguments values. Dynamically computed from the\n :attr:`arguments` attribute.\n\n The :attr:`args` and :attr:`kwargs` properties can be used to invoke\n functions::\n\n def test(a, *, b):\n ...\n\n sig = signature(test)\n ba = sig.bind(10, b=20)\n test(*ba.args, **ba.kwargs)\n\n\n.. seealso::\n\n :pep:`362` - Function Signature Object.\n The detailed specification, implementation details and examples.\n\nCopyright\n---------\n\n*funcsigs* is a derived work of CPython under the terms of the `PSF License\nAgreement`_. The original CPython inspect module, its unit tests and\ndocumentation are the copyright of the Python Software Foundation. The derived\nwork is distributed under the `Apache License Version 2.0`_.\n\n.. _PSF License Agreement: http://docs.python.org/3/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python\n.. _Apache License Version 2.0: http://opensource.org/licenses/Apache-2.0\n.. _GitHub: https://github.com/testing-cabal/funcsigs\n.. _PSF License Agreement: http://docs.python.org/3/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python\n.. _Travis CI: http://travis-ci.org/\n.. _Read The Docs: http://funcsigs.readthedocs.org/\n.. _PEP 362: http://www.python.org/dev/peps/pep-0362/\n.. _inspect: http://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object\n.. _issues system: https://github.com/testing-cabal/funcsigs/issues\n\n.. |build_status| image:: https://secure.travis-ci.org/aliles/funcsigs.png?branch=master\n :target: http://travis-ci.org/#!/aliles/funcsigs\n :alt: Current build status\n\n.. |coverage| image:: https://coveralls.io/repos/aliles/funcsigs/badge.png?branch=master\n :target: https://coveralls.io/r/aliles/funcsigs?branch=master\n :alt: Coverage status\n\n.. |pypi_version| image:: https://pypip.in/v/funcsigs/badge.png\n :target: https://crate.io/packages/funcsigs/\n :alt: Latest PyPI version", + "release_date": "2016-04-25T22:22:05", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Testing Cabal", + "email": "testing-in-python@lists.idyll.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": null, + "email": null, + "url": null + } + ], + "keywords": [], + "homepage_url": "http://funcsigs.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl", + "size": 17697, + "sha1": null, + "md5": "701d58358171f34b6d1197de2923a35a", + "sha256": "330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "ASL", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/funcsigs/1.0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/funcsigs@1.0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "funcsigs", + "version": "1.0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. funcsigs documentation master file, created by\n sphinx-quickstart on Fri Apr 20 20:27:52 2012.\n You can adapt this file completely to your liking, but it should at least\n contain the root `toctree` directive.\n\nIntroducing funcsigs\n====================\n\nThe Funcsigs Package\n--------------------\n\n``funcsigs`` is a backport of the `PEP 362`_ function signature features from\nPython 3.3's `inspect`_ module. The backport is compatible with Python 2.6, 2.7\nas well as 3.3 and up. 3.2 was supported by version 0.4, but with setuptools and\npip no longer supporting 3.2, we cannot make any statement about 3.2\ncompatibility.\n\nCompatibility\n`````````````\n\nThe ``funcsigs`` backport has been tested against:\n\n* CPython 2.6\n* CPython 2.7\n* CPython 3.3\n* CPython 3.4\n* CPython 3.5\n* CPython nightlies\n* PyPy and PyPy3(currently failing CI)\n\nContinuous integration testing is provided by `Travis CI`_.\n\nUnder Python 2.x there is a compatibility issue when a function is assigned to\nthe ``__wrapped__`` property of a class after it has been constructed.\nSimiliarily there under PyPy directly passing the ``__call__`` method of a\nbuiltin is also a compatibility issues. Otherwise the functionality is\nbelieved to be uniform between both Python2 and Python3.\n\nIssues\n``````\n\nSource code for ``funcsigs`` is hosted on `GitHub`_. Any bug reports or feature\nrequests can be made using GitHub's `issues system`_. |build_status| |coverage|\n\nExample\n-------\n\nTo obtain a `Signature` object, pass the target function to the\n``funcsigs.signature`` function.\n\n.. code-block:: python\n\n >>> from funcsigs import signature\n >>> def foo(a, b=None, *args, **kwargs):\n ... pass\n ...\n >>> sig = signature(foo)\n >>> sig\n \n >>> sig.parameters\n OrderedDict([('a', ), ('b', ), ('args', ), ('kwargs', )])\n >>> sig.return_annotation\n \n\nIntrospecting callables with the Signature object\n-------------------------------------------------\n\n.. note::\n\n This section of documentation is a direct reproduction of the Python\n standard library documentation for the inspect module.\n\nThe Signature object represents the call signature of a callable object and its\nreturn annotation. To retrieve a Signature object, use the :func:`signature`\nfunction.\n\n.. function:: signature(callable)\n\n Return a :class:`Signature` object for the given ``callable``::\n\n >>> from funcsigs import signature\n >>> def foo(a, *, b:int, **kwargs):\n ... pass\n\n >>> sig = signature(foo)\n\n >>> str(sig)\n '(a, *, b:int, **kwargs)'\n\n >>> str(sig.parameters['b'])\n 'b:int'\n\n >>> sig.parameters['b'].annotation\n \n\n Accepts a wide range of python callables, from plain functions and classes to\n :func:`functools.partial` objects.\n\n .. note::\n\n Some callables may not be introspectable in certain implementations of\n Python. For example, in CPython, built-in functions defined in C provide\n no metadata about their arguments.\n\n\n.. class:: Signature\n\n A Signature object represents the call signature of a function and its return\n annotation. For each parameter accepted by the function it stores a\n :class:`Parameter` object in its :attr:`parameters` collection.\n\n Signature objects are *immutable*. Use :meth:`Signature.replace` to make a\n modified copy.\n\n .. attribute:: Signature.empty\n\n A special class-level marker to specify absence of a return annotation.\n\n .. attribute:: Signature.parameters\n\n An ordered mapping of parameters' names to the corresponding\n :class:`Parameter` objects.\n\n .. attribute:: Signature.return_annotation\n\n The \"return\" annotation for the callable. If the callable has no \"return\"\n annotation, this attribute is set to :attr:`Signature.empty`.\n\n .. method:: Signature.bind(*args, **kwargs)\n\n Create a mapping from positional and keyword arguments to parameters.\n Returns :class:`BoundArguments` if ``*args`` and ``**kwargs`` match the\n signature, or raises a :exc:`TypeError`.\n\n .. method:: Signature.bind_partial(*args, **kwargs)\n\n Works the same way as :meth:`Signature.bind`, but allows the omission of\n some required arguments (mimics :func:`functools.partial` behavior.)\n Returns :class:`BoundArguments`, or raises a :exc:`TypeError` if the\n passed arguments do not match the signature.\n\n .. method:: Signature.replace(*[, parameters][, return_annotation])\n\n Create a new Signature instance based on the instance replace was invoked\n on. It is possible to pass different ``parameters`` and/or\n ``return_annotation`` to override the corresponding properties of the base\n signature. To remove return_annotation from the copied Signature, pass in\n :attr:`Signature.empty`.\n\n ::\n\n >>> def test(a, b):\n ... pass\n >>> sig = signature(test)\n >>> new_sig = sig.replace(return_annotation=\"new return anno\")\n >>> str(new_sig)\n \"(a, b) -> 'new return anno'\"\n\n\n.. class:: Parameter\n\n Parameter objects are *immutable*. Instead of modifying a Parameter object,\n you can use :meth:`Parameter.replace` to create a modified copy.\n\n .. attribute:: Parameter.empty\n\n A special class-level marker to specify absence of default values and\n annotations.\n\n .. attribute:: Parameter.name\n\n The name of the parameter as a string. Must be a valid python identifier\n name (with the exception of ``POSITIONAL_ONLY`` parameters, which can have\n it set to ``None``).\n\n .. attribute:: Parameter.default\n\n The default value for the parameter. If the parameter has no default\n value, this attribute is set to :attr:`Parameter.empty`.\n\n .. attribute:: Parameter.annotation\n\n The annotation for the parameter. If the parameter has no annotation,\n this attribute is set to :attr:`Parameter.empty`.\n\n .. attribute:: Parameter.kind\n\n Describes how argument values are bound to the parameter. Possible values\n (accessible via :class:`Parameter`, like ``Parameter.KEYWORD_ONLY``):\n\n +------------------------+----------------------------------------------+\n | Name | Meaning |\n +========================+==============================================+\n | *POSITIONAL_ONLY* | Value must be supplied as a positional |\n | | argument. |\n | | |\n | | Python has no explicit syntax for defining |\n | | positional-only parameters, but many built-in|\n | | and extension module functions (especially |\n | | those that accept only one or two parameters)|\n | | accept them. |\n +------------------------+----------------------------------------------+\n | *POSITIONAL_OR_KEYWORD*| Value may be supplied as either a keyword or |\n | | positional argument (this is the standard |\n | | binding behaviour for functions implemented |\n | | in Python.) |\n +------------------------+----------------------------------------------+\n | *VAR_POSITIONAL* | A tuple of positional arguments that aren't |\n | | bound to any other parameter. This |\n | | corresponds to a ``*args`` parameter in a |\n | | Python function definition. |\n +------------------------+----------------------------------------------+\n | *KEYWORD_ONLY* | Value must be supplied as a keyword argument.|\n | | Keyword only parameters are those which |\n | | appear after a ``*`` or ``*args`` entry in a |\n | | Python function definition. |\n +------------------------+----------------------------------------------+\n | *VAR_KEYWORD* | A dict of keyword arguments that aren't bound|\n | | to any other parameter. This corresponds to a|\n | | ``**kwargs`` parameter in a Python function |\n | | definition. |\n +------------------------+----------------------------------------------+\n\n Example: print all keyword-only arguments without default values::\n\n >>> def foo(a, b, *, c, d=10):\n ... pass\n\n >>> sig = signature(foo)\n >>> for param in sig.parameters.values():\n ... if (param.kind == param.KEYWORD_ONLY and\n ... param.default is param.empty):\n ... print('Parameter:', param)\n Parameter: c\n\n .. method:: Parameter.replace(*[, name][, kind][, default][, annotation])\n\n Create a new Parameter instance based on the instance replaced was invoked\n on. To override a :class:`Parameter` attribute, pass the corresponding\n argument. To remove a default value or/and an annotation from a\n Parameter, pass :attr:`Parameter.empty`.\n\n ::\n\n >>> from funcsigs import Parameter\n >>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42)\n >>> str(param)\n 'foo=42'\n\n >>> str(param.replace()) # Will create a shallow copy of 'param'\n 'foo=42'\n\n >>> str(param.replace(default=Parameter.empty, annotation='spam'))\n \"foo:'spam'\"\n\n\n.. class:: BoundArguments\n\n Result of a :meth:`Signature.bind` or :meth:`Signature.bind_partial` call.\n Holds the mapping of arguments to the function's parameters.\n\n .. attribute:: BoundArguments.arguments\n\n An ordered, mutable mapping (:class:`collections.OrderedDict`) of\n parameters' names to arguments' values. Contains only explicitly bound\n arguments. Changes in :attr:`arguments` will reflect in :attr:`args` and\n :attr:`kwargs`.\n\n Should be used in conjunction with :attr:`Signature.parameters` for any\n argument processing purposes.\n\n .. note::\n\n Arguments for which :meth:`Signature.bind` or\n :meth:`Signature.bind_partial` relied on a default value are skipped.\n However, if needed, it is easy to include them.\n\n ::\n\n >>> def foo(a, b=10):\n ... pass\n\n >>> sig = signature(foo)\n >>> ba = sig.bind(5)\n\n >>> ba.args, ba.kwargs\n ((5,), {})\n\n >>> for param in sig.parameters.values():\n ... if param.name not in ba.arguments:\n ... ba.arguments[param.name] = param.default\n\n >>> ba.args, ba.kwargs\n ((5, 10), {})\n\n\n .. attribute:: BoundArguments.args\n\n A tuple of positional arguments values. Dynamically computed from the\n :attr:`arguments` attribute.\n\n .. attribute:: BoundArguments.kwargs\n\n A dict of keyword arguments values. Dynamically computed from the\n :attr:`arguments` attribute.\n\n The :attr:`args` and :attr:`kwargs` properties can be used to invoke\n functions::\n\n def test(a, *, b):\n ...\n\n sig = signature(test)\n ba = sig.bind(10, b=20)\n test(*ba.args, **ba.kwargs)\n\n\n.. seealso::\n\n :pep:`362` - Function Signature Object.\n The detailed specification, implementation details and examples.\n\nCopyright\n---------\n\n*funcsigs* is a derived work of CPython under the terms of the `PSF License\nAgreement`_. The original CPython inspect module, its unit tests and\ndocumentation are the copyright of the Python Software Foundation. The derived\nwork is distributed under the `Apache License Version 2.0`_.\n\n.. _PSF License Agreement: http://docs.python.org/3/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python\n.. _Apache License Version 2.0: http://opensource.org/licenses/Apache-2.0\n.. _GitHub: https://github.com/testing-cabal/funcsigs\n.. _PSF License Agreement: http://docs.python.org/3/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python\n.. _Travis CI: http://travis-ci.org/\n.. _Read The Docs: http://funcsigs.readthedocs.org/\n.. _PEP 362: http://www.python.org/dev/peps/pep-0362/\n.. _inspect: http://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object\n.. _issues system: https://github.com/testing-cabal/funcsigs/issues\n\n.. |build_status| image:: https://secure.travis-ci.org/aliles/funcsigs.png?branch=master\n :target: http://travis-ci.org/#!/aliles/funcsigs\n :alt: Current build status\n\n.. |coverage| image:: https://coveralls.io/repos/aliles/funcsigs/badge.png?branch=master\n :target: https://coveralls.io/r/aliles/funcsigs?branch=master\n :alt: Coverage status\n\n.. |pypi_version| image:: https://pypip.in/v/funcsigs/badge.png\n :target: https://crate.io/packages/funcsigs/\n :alt: Latest PyPI version", + "release_date": "2016-04-25T22:22:33", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Testing Cabal", + "email": "testing-in-python@lists.idyll.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": null, + "email": null, + "url": null + } + ], + "keywords": [], + "homepage_url": "http://funcsigs.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz", + "size": 27947, + "sha1": null, + "md5": "7e583285b1fb8a76305d6d68f4ccc14e", + "sha256": "a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "ASL", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/funcsigs/1.0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/funcsigs@1.0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "importlib-metadata", + "version": "2.1.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "=========================\n ``importlib_metadata``\n=========================\n\n``importlib_metadata`` is a library to access the metadata for a\nPython package.\n\nAs of Python 3.8, this functionality has been added to the\n`Python standard library\n`_.\nThis package supplies backports of that functionality including\nimprovements added to subsequent Python versions.\n\n\nUsage\n=====\n\nSee the `online documentation `_\nfor usage details.\n\n`Finder authors\n`_ can\nalso add support for custom package installers. See the above documentation\nfor details.\n\n\nCaveats\n=======\n\nThis project primarily supports third-party packages installed by PyPA\ntools (or other conforming packages). It does not support:\n\n- Packages in the stdlib.\n- Packages installed without metadata.\n\nProject details\n===============\n\n * Project home: https://github.com/python/importlib_metadata\n * Report bugs at: https://github.com/python/importlib_metadata/issues\n * Code hosting: https://github.com/python/importlib_metadata\n * Documentation: https://importlib_metadata.readthedocs.io/\n\n\n", + "release_date": "2022-01-23T15:25:17", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://importlib-metadata.readthedocs.io/", + "download_url": "https://files.pythonhosted.org/packages/cf/b4/877779cd7b5a15536ecbe0655cfb35a0de0ede6d888151fd7356d278c47d/importlib_metadata-2.1.3-py2.py3-none-any.whl", + "size": 10211, + "sha1": null, + "md5": "212b354bdd29ba0d1d32806d08b1430c", + "sha256": "52e65a0856f9ba7ea8f2c4ced253fb6c88d1a8c352cb1e916cff4eb17d5a693d", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Apache Software License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/importlib-metadata/2.1.3/json", + "datasource_id": null, + "purl": "pkg:pypi/importlib-metadata@2.1.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "importlib-metadata", + "version": "2.1.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "=========================\n ``importlib_metadata``\n=========================\n\n``importlib_metadata`` is a library to access the metadata for a\nPython package.\n\nAs of Python 3.8, this functionality has been added to the\n`Python standard library\n`_.\nThis package supplies backports of that functionality including\nimprovements added to subsequent Python versions.\n\n\nUsage\n=====\n\nSee the `online documentation `_\nfor usage details.\n\n`Finder authors\n`_ can\nalso add support for custom package installers. See the above documentation\nfor details.\n\n\nCaveats\n=======\n\nThis project primarily supports third-party packages installed by PyPA\ntools (or other conforming packages). It does not support:\n\n- Packages in the stdlib.\n- Packages installed without metadata.\n\nProject details\n===============\n\n * Project home: https://github.com/python/importlib_metadata\n * Report bugs at: https://github.com/python/importlib_metadata/issues\n * Code hosting: https://github.com/python/importlib_metadata\n * Documentation: https://importlib_metadata.readthedocs.io/\n\n\n", + "release_date": "2022-01-23T15:25:19", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://importlib-metadata.readthedocs.io/", + "download_url": "https://files.pythonhosted.org/packages/4c/d8/af92ca59b33366b3b9d230d17772d728d7b064f3de6d6150332d5fa8dae3/importlib_metadata-2.1.3.tar.gz", + "size": 30408, + "sha1": null, + "md5": "10bf15d611e8d61d6f7b3aa112196fca", + "sha256": "02a9f62b02e9b1cc43871809ef99947e8f5d94771392d666ada2cafc4cd09d4f", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Apache Software License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/importlib-metadata/2.1.3/json", + "datasource_id": null, + "purl": "pkg:pypi/importlib-metadata@2.1.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-app", + "version": "1.3.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2017-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n=============\n Invenio-App\n=============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-app.svg\n :target: https://github.com/inveniosoftware/invenio-app/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-app/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-app/actions\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-app.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-app\n\n.. image:: https://img.shields.io/pypi/v/invenio-app.svg\n :target: https://pypi.org/pypi/invenio-app\n\nWSGI, Celery and CLI applications for Invenio flavours.\n\nFurther documentation is available on\nhttps://invenio-app.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2017-2019 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.3.3 (released 2021-12-06)\n\n- Pinned Limits library to align with Flask-Limiter.\n\nVersion 1.3.2 (released 2021-10-28)\n\n- Unpins Flask-Talisman to allow newer versions.\n\n- Removes Python 2 support.\n\nVersion 1.3.1 (released 2020-12-07)\n\n- Adds HEAD and OPTIONS HTTP verbs to the /ping endpoint as recommended\n in HAProxy documentation.\n\nVersion 1.3.0 (released 2020-05-13)\n\n- Adds new template theming via allowing Jinja to load templates from different\n theme folders via the new configuration variable ``APP_THEME``.\n\n- Removes the ChoiceLoader used to load templates from the instance folder in\n favour of using Flask instead. Invenio-App sets the application's root_path\n to the instance folder, which makes Flask create the same behavior\n previously achieved with the ChoiceLoader.\n\nVersion 1.2.6 (released 2020-05-06)\n\n- Deprecated Python versions lower than 3.6.0. Now supporting 3.6.0 and 3.7.0.\n\nVersion 1.2.5 (released 2020-02-26)\n\nVersion 1.2.4 (released 2019-11-20)\n\n- Disable ratelimit for celery.\n\nVersion 1.2.3 (released 2019-10-10)\n\n- Make `static_url_path` configurable through environment variable.\n\nVersion 1.2.2 (released 2019-08-29)\n\n- Unpins Invenio packages versions.\n\nVersion 1.2.1 (released 2019-08-21)\n\n- Exempts the \"/ping\" view from rate limiting.\n\nVersion 1.2.0 (released 2019-07-29)\n\n- Fixes issue with instance_path and static_folder being globals. Depends on\n change in Invenio-Base v1.1.0\n\n- Improves rate limiting function to have limits per guest and per\n authenticated users.\n\nVersion 1.1.1 (released 2019-07-15)\n\n- Fixes a security issue where APP_ALLOWED_HOSTS was not always being checked,\n and thus could allow host header injection attacks.\n\n NOTE: you should never route requests to your application with a wrong host\n header. The APP_ALLOWED_HOSTS exists as an extra protective measure, because\n it is easy to misconfigure your web server.\n\n The root cause was that Werkzeug's trusted host feature only works when\n request.host is being evaluated. This means that for instance when only\n url_for (part of the routing system) is used, then the host header check is\n not performed.\n\nVersion 1.1.0 (released 2018-12-14)\n\n- The Flask-DebugToolbar extension is now automatically registered if\n installed.\n\nVersion 1.0.5 (released 2018-12-05)\n\n- Add health check view\n\n- Fix response headers assertion in tests\n\nVersion 1.0.4 (released 2018-10-11)\n\n- Fix Content Security Policy headers when set empty in DEBUG mode.\n\nVersion 1.0.3 (released 2018-10-08)\n\n- Fix Content Security Policy headers when running in DEBUG mode.\n\nVersion 1.0.2 (released 2018-08-24)\n\n- Allows use of Flask-DebugToolbar when running in DEBUG mode.\n\nVersion 1.0.1 (released 2018-06-29)\n\n- Pin Flask-Talisman.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2021-12-06T16:48:11", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio applications", + "homepage_url": "https://github.com/inveniosoftware/invenio-app", + "download_url": "https://files.pythonhosted.org/packages/25/ee/bfe28143e98e24a27ca8d6c4a464591e8006766a60fe8468a9b2061468e2/invenio_app-1.3.3-py2.py3-none-any.whl", + "size": 18908, + "sha1": null, + "md5": "623a44fc541a251d30f02763321cb730", + "sha256": "0bca343edf807896a47bd0f8e3bc61cd7f6b381f6cd720a2b6547e0d11c32853", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-app/1.3.3/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-app@1.3.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-app", + "version": "1.3.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2017-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n=============\n Invenio-App\n=============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-app.svg\n :target: https://github.com/inveniosoftware/invenio-app/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-app/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-app/actions\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-app.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-app\n\n.. image:: https://img.shields.io/pypi/v/invenio-app.svg\n :target: https://pypi.org/pypi/invenio-app\n\nWSGI, Celery and CLI applications for Invenio flavours.\n\nFurther documentation is available on\nhttps://invenio-app.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2017-2019 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.3.3 (released 2021-12-06)\n\n- Pinned Limits library to align with Flask-Limiter.\n\nVersion 1.3.2 (released 2021-10-28)\n\n- Unpins Flask-Talisman to allow newer versions.\n\n- Removes Python 2 support.\n\nVersion 1.3.1 (released 2020-12-07)\n\n- Adds HEAD and OPTIONS HTTP verbs to the /ping endpoint as recommended\n in HAProxy documentation.\n\nVersion 1.3.0 (released 2020-05-13)\n\n- Adds new template theming via allowing Jinja to load templates from different\n theme folders via the new configuration variable ``APP_THEME``.\n\n- Removes the ChoiceLoader used to load templates from the instance folder in\n favour of using Flask instead. Invenio-App sets the application's root_path\n to the instance folder, which makes Flask create the same behavior\n previously achieved with the ChoiceLoader.\n\nVersion 1.2.6 (released 2020-05-06)\n\n- Deprecated Python versions lower than 3.6.0. Now supporting 3.6.0 and 3.7.0.\n\nVersion 1.2.5 (released 2020-02-26)\n\nVersion 1.2.4 (released 2019-11-20)\n\n- Disable ratelimit for celery.\n\nVersion 1.2.3 (released 2019-10-10)\n\n- Make `static_url_path` configurable through environment variable.\n\nVersion 1.2.2 (released 2019-08-29)\n\n- Unpins Invenio packages versions.\n\nVersion 1.2.1 (released 2019-08-21)\n\n- Exempts the \"/ping\" view from rate limiting.\n\nVersion 1.2.0 (released 2019-07-29)\n\n- Fixes issue with instance_path and static_folder being globals. Depends on\n change in Invenio-Base v1.1.0\n\n- Improves rate limiting function to have limits per guest and per\n authenticated users.\n\nVersion 1.1.1 (released 2019-07-15)\n\n- Fixes a security issue where APP_ALLOWED_HOSTS was not always being checked,\n and thus could allow host header injection attacks.\n\n NOTE: you should never route requests to your application with a wrong host\n header. The APP_ALLOWED_HOSTS exists as an extra protective measure, because\n it is easy to misconfigure your web server.\n\n The root cause was that Werkzeug's trusted host feature only works when\n request.host is being evaluated. This means that for instance when only\n url_for (part of the routing system) is used, then the host header check is\n not performed.\n\nVersion 1.1.0 (released 2018-12-14)\n\n- The Flask-DebugToolbar extension is now automatically registered if\n installed.\n\nVersion 1.0.5 (released 2018-12-05)\n\n- Add health check view\n\n- Fix response headers assertion in tests\n\nVersion 1.0.4 (released 2018-10-11)\n\n- Fix Content Security Policy headers when set empty in DEBUG mode.\n\nVersion 1.0.3 (released 2018-10-08)\n\n- Fix Content Security Policy headers when running in DEBUG mode.\n\nVersion 1.0.2 (released 2018-08-24)\n\n- Allows use of Flask-DebugToolbar when running in DEBUG mode.\n\nVersion 1.0.1 (released 2018-06-29)\n\n- Pin Flask-Talisman.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2021-12-06T16:48:12", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio applications", + "homepage_url": "https://github.com/inveniosoftware/invenio-app", + "download_url": "https://files.pythonhosted.org/packages/ef/e6/f7e3e2c79520a0046008aa0cb5381734f681d040793e5b8d1b995deaca5d/invenio-app-1.3.3.tar.gz", + "size": 31186, + "sha1": null, + "md5": "8b4027283bdaeecb44287deb3e67f6e1", + "sha256": "3e811dcc5f873ae63e2ea6be7086d118d3732a83847c4783f63076678278d776", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-app/1.3.3/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-app@1.3.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-base", + "version": "1.2.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n==============\n Invenio-Base\n==============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-base.svg\n :target: https://github.com/inveniosoftware/invenio-base/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-base/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-base/actions?query=workflow%3ACI\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-base.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-base\n\n.. image:: https://img.shields.io/pypi/v/invenio-base.svg\n :target: https://pypi.org/pypi/invenio-base\n\n\nBase package for building Invenio application factories.\n\nFurther documentation is available on https://invenio-base.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.2.5 (released 2021-10-18)\n\n- Unpin Flask <2.0 and Werkzeug <2.0.\n\nVersion 1.2.4 (released 2021-05-12)\n\n- Pins Flask <2.0 and Werkzeug <2.0 due to incompatibilities in the newly\n released versions.\n\nVersion 1.2.3 (released 2020-05-11)\n\n- Adds support for passing ``root_path`` to the base Flask application factory.\n\nVersion 1.2.2 (released 2020-03-05)\n\n- Adds ``six`` dependency.\n- Adds the ``obj_or_import_string`` and ``load_or_import_from_config`` common\n utility functions for general re-use throughout other Invenio modules.\n\nVersion 1.2.1 (released 2020-03-02)\n\n- Bumps Flask minimum version to v1.0.4.\n- Removes ``invenio instance create`` command and ``cokiecutter`` dependency.\n\nVersion 1.2.0 (released 2019-08-28)\n\n- Adds support to trust new proxy headers through the ``PROXYFIX_CONFIG``\n configuration variable. For more information see the\n `full documentation `_.\n\n- Deprecates the usage of ``WSGI_PROXIES`` configuration which only supports\n ``X-Forwarded-For`` headers.\n\nVersion 1.1.0 (released 2019-07-29)\n\n- Add support for allowing instance path and static folder to be callables\n which are evaluated before being passed to the Flask application class. This\n fixes an issue in pytest-invenio and Invenio-App in which a global instance\n path was only evaluated once.\n\n- Fixes deprecation warnings from Werkzeug.\n\nVersion 1.0.2 (released 2018-12-14)\n\nVersion 1.0.1 (released 2018-05-25)\n\n- Added support for blueprint factory functions in the\n ``invenio_base.blueprints`` and the ``invenio_base.api_blueprints`` entry\n point groups. In addition to specifying an import path to an already created\n blueprint, you can now specify an import path of a blueprint factory function\n with the signature create_blueprint(app), that will create and return a\n blueprint. This allows moving dynamic blueprint creation from the extension\n initialization phase to the blueprint registration phase.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2021-10-18T12:34:05", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio", + "homepage_url": "https://github.com/inveniosoftware/invenio-base", + "download_url": "https://files.pythonhosted.org/packages/47/7c/89fe1cd7263c8d67a366c568dcc6f5050518f06ca7695f7b719f3f340229/invenio_base-1.2.5-py2.py3-none-any.whl", + "size": 15348, + "sha1": null, + "md5": "17dcb76abd874239b98e51ba50d86823", + "sha256": "d22a8dee25def2d5e7f10566a12b63e643d6c1267de0baa335dbdfd7382707d6", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-base/1.2.5/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-base@1.2.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-base", + "version": "1.2.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n==============\n Invenio-Base\n==============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-base.svg\n :target: https://github.com/inveniosoftware/invenio-base/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-base/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-base/actions?query=workflow%3ACI\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-base.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-base\n\n.. image:: https://img.shields.io/pypi/v/invenio-base.svg\n :target: https://pypi.org/pypi/invenio-base\n\n\nBase package for building Invenio application factories.\n\nFurther documentation is available on https://invenio-base.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.2.5 (released 2021-10-18)\n\n- Unpin Flask <2.0 and Werkzeug <2.0.\n\nVersion 1.2.4 (released 2021-05-12)\n\n- Pins Flask <2.0 and Werkzeug <2.0 due to incompatibilities in the newly\n released versions.\n\nVersion 1.2.3 (released 2020-05-11)\n\n- Adds support for passing ``root_path`` to the base Flask application factory.\n\nVersion 1.2.2 (released 2020-03-05)\n\n- Adds ``six`` dependency.\n- Adds the ``obj_or_import_string`` and ``load_or_import_from_config`` common\n utility functions for general re-use throughout other Invenio modules.\n\nVersion 1.2.1 (released 2020-03-02)\n\n- Bumps Flask minimum version to v1.0.4.\n- Removes ``invenio instance create`` command and ``cokiecutter`` dependency.\n\nVersion 1.2.0 (released 2019-08-28)\n\n- Adds support to trust new proxy headers through the ``PROXYFIX_CONFIG``\n configuration variable. For more information see the\n `full documentation `_.\n\n- Deprecates the usage of ``WSGI_PROXIES`` configuration which only supports\n ``X-Forwarded-For`` headers.\n\nVersion 1.1.0 (released 2019-07-29)\n\n- Add support for allowing instance path and static folder to be callables\n which are evaluated before being passed to the Flask application class. This\n fixes an issue in pytest-invenio and Invenio-App in which a global instance\n path was only evaluated once.\n\n- Fixes deprecation warnings from Werkzeug.\n\nVersion 1.0.2 (released 2018-12-14)\n\nVersion 1.0.1 (released 2018-05-25)\n\n- Added support for blueprint factory functions in the\n ``invenio_base.blueprints`` and the ``invenio_base.api_blueprints`` entry\n point groups. In addition to specifying an import path to an already created\n blueprint, you can now specify an import path of a blueprint factory function\n with the signature create_blueprint(app), that will create and return a\n blueprint. This allows moving dynamic blueprint creation from the extension\n initialization phase to the blueprint registration phase.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2021-10-18T12:34:07", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio", + "homepage_url": "https://github.com/inveniosoftware/invenio-base", + "download_url": "https://files.pythonhosted.org/packages/fd/3b/135f8f839f696ec9f163083625b5111310108fd86b0a9668f7da4a0b6f92/invenio-base-1.2.5.tar.gz", + "size": 29272, + "sha1": null, + "md5": "15750be7226a1a50182c880a2db4cd14", + "sha256": "375b84ab32ebef15f766b8be58005e89b6ec13ba0567a9a21da9001a23562977", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-base/1.2.5/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-base@1.2.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-cache", + "version": "1.1.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2017-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n===============\n Invenio-Cache\n===============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-cache.svg\n :target: https://github.com/inveniosoftware/invenio-cache/blob/master/LICENSE\n\n.. image:: https://img.shields.io/travis/inveniosoftware/invenio-cache.svg\n :target: https://travis-ci.org/inveniosoftware/invenio-cache\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-cache.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-cache\n\n.. image:: https://img.shields.io/pypi/v/invenio-cache.svg\n :target: https://pypi.org/pypi/invenio-cache\n\nCache module for Invenio.\n\nFurther documentation is available on\nhttps://invenio-cache.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2017-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.1.0 (released 2020-03-10)\n\n- changes flask dependency to centrally managed\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2020-03-10T17:25:42", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio cache", + "homepage_url": "https://github.com/inveniosoftware/invenio-cache", + "download_url": "https://files.pythonhosted.org/packages/04/f0/22b7426ac2b821d93bc6066410fac652c924330bfbd5b64a82e1e36b0ed6/invenio_cache-1.1.0-py2.py3-none-any.whl", + "size": 9071, + "sha1": null, + "md5": "8501d03c713fe7580f9d625ac22e247b", + "sha256": "a4562639f2f63cbc9de1302e159ecd9363f17d53d912a8d0773ffe76e2f153fc", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-cache/1.1.0/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-cache@1.1.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-cache", + "version": "1.1.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2017-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n===============\n Invenio-Cache\n===============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-cache.svg\n :target: https://github.com/inveniosoftware/invenio-cache/blob/master/LICENSE\n\n.. image:: https://img.shields.io/travis/inveniosoftware/invenio-cache.svg\n :target: https://travis-ci.org/inveniosoftware/invenio-cache\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-cache.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-cache\n\n.. image:: https://img.shields.io/pypi/v/invenio-cache.svg\n :target: https://pypi.org/pypi/invenio-cache\n\nCache module for Invenio.\n\nFurther documentation is available on\nhttps://invenio-cache.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2017-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.1.0 (released 2020-03-10)\n\n- changes flask dependency to centrally managed\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2020-03-10T17:25:43", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio cache", + "homepage_url": "https://github.com/inveniosoftware/invenio-cache", + "download_url": "https://files.pythonhosted.org/packages/4f/f8/aee6e433a4b94882b8089cf5ff17ab1612a6ecc9231b3103cc4e6a533c98/invenio-cache-1.1.0.tar.gz", + "size": 16983, + "sha1": null, + "md5": "a166e1fac6a455465b7db232ce0f77c5", + "sha256": "1212a83f98fbe29a936587f7c5b2f838f7f934b0b2a9d9a993e377e5a8ab0cf5", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-cache/1.1.0/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-cache@1.1.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-celery", + "version": "1.2.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n================\n Invenio-Celery\n================\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-celery.svg\n :target: https://github.com/inveniosoftware/invenio-celery/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-celery/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-celery/actions?query=workflow%3ACI\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-celery.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-celery\n\n.. image:: https://img.shields.io/pypi/v/invenio-celery.svg\n :target: https://pypi.org/pypi/invenio-celery\n\n\nCelery distributed task queue module for Invenio.\n\nInvenio-Celery is a small discovery layer that takes care of discovering and\nloading tasks from other Invenio modules, as well as providing configuration\ndefaults for Celery usage in Invenio. Invenio-Celery relies on Flask-CeleryExt\nfor integrating Flask and Celery with application factories.\n\nFurther documentation is available on https://invenio-celery.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2020 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.2.2 (released 2020-12-09)\n\n- Removes the pytest-celery dependency as the package is still in prerelease\n and it only affects tests. If you are using Celery 5 you may need to enable\n the pytest celery plugin - see\n https://docs.celeryproject.org/en/stable/userguide/testing.html#enabling\n\nVersion 1.2.1 (released 2020-09-28)\n\n- Change version bounds on Celery to 4.4 to 5.1.\n\n- Adds dependency on pytest-celery which now installs the celery_config pytest\n fixture.\n\nVersion 1.2.0 (released 2020-03-05)\n\n- added dependency on invenio-base to centralise package management\n\nVersion 1.1.3 (released 2020-02-21)\n\n- Removed redundant version specifier for Celery dependency.\n\nVersion 1.1.2 (released 2020-02-17)\n\n- Unpinned Celery version to allow support of Celery 4.4\n\nVersion 1.1.1 (released 2019-11-19)\n\n- pinned version of celery lower than 4.3 due to Datetime serialization\n issues\n\nVersion 1.1.0 (released 2019-06-21)\n\n- Changed the ``msgpack-python`` dependency to ``msgpack``.\n Please first uninstall ``msgpack-python`` before installing\n the new ``msgpack`` dependency (``pip uninstall msgpack-python``).\n\n\nVersion 1.0.1 (released 2018-12-06)\n\n- Adds support for Celery v4.2. Technically this change is backward\n incompatible because it is no longer possible to load tasks from bare modules\n (e.g. mymodule.py in the Python root). This is a constraint imposed by Celery\n v4.2. We however do not known of any cases where bare modules have been used,\n and also this design is discouraged so we are not flagging it as a backward\n incompatible change, in order to have the change readily available for\n current Invenio version.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2020-12-09T12:33:49", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio celery", + "homepage_url": "https://github.com/inveniosoftware/invenio-celery", + "download_url": "https://files.pythonhosted.org/packages/b8/a7/ec89de1fd4615aebc0e59c25ab8d8152bb04f03fa21b1c50bdd08239e79e/invenio_celery-1.2.2-py2.py3-none-any.whl", + "size": 8399, + "sha1": null, + "md5": "fd454eeeb6da903fc8a97c3ba0203d5e", + "sha256": "f03d9d0bb0d5c3b2e1d53ded0dc8839e83d738d00913854de33cefbfa95c4d8d", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-celery/1.2.2/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-celery@1.2.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-celery", + "version": "1.2.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n================\n Invenio-Celery\n================\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-celery.svg\n :target: https://github.com/inveniosoftware/invenio-celery/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-celery/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-celery/actions?query=workflow%3ACI\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-celery.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-celery\n\n.. image:: https://img.shields.io/pypi/v/invenio-celery.svg\n :target: https://pypi.org/pypi/invenio-celery\n\n\nCelery distributed task queue module for Invenio.\n\nInvenio-Celery is a small discovery layer that takes care of discovering and\nloading tasks from other Invenio modules, as well as providing configuration\ndefaults for Celery usage in Invenio. Invenio-Celery relies on Flask-CeleryExt\nfor integrating Flask and Celery with application factories.\n\nFurther documentation is available on https://invenio-celery.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2020 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.2.2 (released 2020-12-09)\n\n- Removes the pytest-celery dependency as the package is still in prerelease\n and it only affects tests. If you are using Celery 5 you may need to enable\n the pytest celery plugin - see\n https://docs.celeryproject.org/en/stable/userguide/testing.html#enabling\n\nVersion 1.2.1 (released 2020-09-28)\n\n- Change version bounds on Celery to 4.4 to 5.1.\n\n- Adds dependency on pytest-celery which now installs the celery_config pytest\n fixture.\n\nVersion 1.2.0 (released 2020-03-05)\n\n- added dependency on invenio-base to centralise package management\n\nVersion 1.1.3 (released 2020-02-21)\n\n- Removed redundant version specifier for Celery dependency.\n\nVersion 1.1.2 (released 2020-02-17)\n\n- Unpinned Celery version to allow support of Celery 4.4\n\nVersion 1.1.1 (released 2019-11-19)\n\n- pinned version of celery lower than 4.3 due to Datetime serialization\n issues\n\nVersion 1.1.0 (released 2019-06-21)\n\n- Changed the ``msgpack-python`` dependency to ``msgpack``.\n Please first uninstall ``msgpack-python`` before installing\n the new ``msgpack`` dependency (``pip uninstall msgpack-python``).\n\n\nVersion 1.0.1 (released 2018-12-06)\n\n- Adds support for Celery v4.2. Technically this change is backward\n incompatible because it is no longer possible to load tasks from bare modules\n (e.g. mymodule.py in the Python root). This is a constraint imposed by Celery\n v4.2. We however do not known of any cases where bare modules have been used,\n and also this design is discouraged so we are not flagging it as a backward\n incompatible change, in order to have the change readily available for\n current Invenio version.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2020-12-09T12:33:50", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio celery", + "homepage_url": "https://github.com/inveniosoftware/invenio-celery", + "download_url": "https://files.pythonhosted.org/packages/67/db/7d7dffe8f620697461a4f680505c1d04904ca5b6805c10fd9a9677b5e0b6/invenio-celery-1.2.2.tar.gz", + "size": 19898, + "sha1": null, + "md5": "ed586720d46c6f3c73ead8ad5bfb421d", + "sha256": "ac74076f0656a299ad741d6ec2752f7b06bf3b45d742179952267058c868d93d", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-celery/1.2.2/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-celery@1.2.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-config", + "version": "1.0.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n================\n Invenio-Config\n================\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-config.svg\n :target: https://github.com/inveniosoftware/invenio-config/blob/master/LICENSE\n\n.. image:: https://img.shields.io/travis/inveniosoftware/invenio-config.svg\n :target: https://travis-ci.org/inveniosoftware/invenio-config\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-config.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-config\n\n.. image:: https://img.shields.io/pypi/v/invenio-config.svg\n :target: https://pypi.org/pypi/invenio-config\n\n\nInvenio configuration loader.\n\nInvenio-Config is a base package of the Invenio digital library framework. It\nis usually installed automatically as a dependency. It facilitates\nconfiguration loading from various sources such as a Python module, an instance\nfolder or environment variables.\n\nFurther documentation is available on https://invenio-config.readthedocs.io/.\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.0.3 (released 2020-05-06)\n\n- Deprecated Python versions lower than 3.6.0. Now supporting 3.6.0 and 3.7.0.\n\nVersion 1.0.2 (released 2019-07-29)\n\n- Added `ALLOWED_HTML_TAGS` and `ALLOWED_HTML_ATTRS` config keys.\n\nVersion 1.0.1 (released 2018-10-02)\n\n- Application configurations are now sorted and loaded in alphabetical order.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2020-05-06T14:14:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio config", + "homepage_url": "https://github.com/inveniosoftware/invenio-config", + "download_url": "https://files.pythonhosted.org/packages/5a/67/c751f8b8cf6ec5c4415bccaeed7f6b7eab836178ca4faa9f4d7088616735/invenio_config-1.0.3-py2.py3-none-any.whl", + "size": 11585, + "sha1": null, + "md5": "568b56eed58774c4af391def11b972ad", + "sha256": "238ab074991e7f0d6ee7ebc6eb2f5e41658749dd977ab6e86476e862c0efaf28", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-config/1.0.3/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-config@1.0.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-config", + "version": "1.0.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n================\n Invenio-Config\n================\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-config.svg\n :target: https://github.com/inveniosoftware/invenio-config/blob/master/LICENSE\n\n.. image:: https://img.shields.io/travis/inveniosoftware/invenio-config.svg\n :target: https://travis-ci.org/inveniosoftware/invenio-config\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-config.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-config\n\n.. image:: https://img.shields.io/pypi/v/invenio-config.svg\n :target: https://pypi.org/pypi/invenio-config\n\n\nInvenio configuration loader.\n\nInvenio-Config is a base package of the Invenio digital library framework. It\nis usually installed automatically as a dependency. It facilitates\nconfiguration loading from various sources such as a Python module, an instance\nfolder or environment variables.\n\nFurther documentation is available on https://invenio-config.readthedocs.io/.\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.0.3 (released 2020-05-06)\n\n- Deprecated Python versions lower than 3.6.0. Now supporting 3.6.0 and 3.7.0.\n\nVersion 1.0.2 (released 2019-07-29)\n\n- Added `ALLOWED_HTML_TAGS` and `ALLOWED_HTML_ATTRS` config keys.\n\nVersion 1.0.1 (released 2018-10-02)\n\n- Application configurations are now sorted and loaded in alphabetical order.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2020-05-06T14:14:23", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio config", + "homepage_url": "https://github.com/inveniosoftware/invenio-config", + "download_url": "https://files.pythonhosted.org/packages/cd/8e/36d33ec84f29bbeed5593ea8e89b421e0cbcc6fafd0d8c300d187f4a6d9d/invenio-config-1.0.3.tar.gz", + "size": 19593, + "sha1": null, + "md5": "42267dbcde1d8ef8be7d333ee5f74c70", + "sha256": "9d10492b49a46703f0ac028ce8ab78b5ff1c72b180ecb4ffcee5bf49682d1e6c", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-config/1.0.3/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-config@1.0.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-i18n", + "version": "1.3.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n==============\n Invenio-I18N\n==============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-i18n.svg\n :target: https://github.com/inveniosoftware/invenio-i18n/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-i18n/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-i18n/actions\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-i18n.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-i18n\n\n.. image:: https://img.shields.io/pypi/v/invenio-i18n.svg\n :target: https://pypi.org/pypi/invenio-i18n\n\n\nInvenio internationalization module based on\n`Flask-BabelEx `_.\n\nFeatures:\n\n* Loading and merging message catalogs.\n* Algorithm for detecting a user's locale.\n* Views for changing the locale.\n* Jinja2 macros and filters for I18N.\n\nFurther documentation available at https://invenio-i18n.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.3.1 (released 2021-10-06)\n\n- Fixes issue with language selector button not disabling the currently\n selected field.\n\nVersion 1.3.0 (released 2020-12-07)\n\n- Integrates Semantic-UI templates and assets.\n- Removes webassets-based bundles.\n- Adds InvenioI18N extransion to the API level applications.\n\nVersion 1.2.0 (released 2020-03-06)\n\n- Bumps Flask-BabelEx support latest Flask/Werkzeug.\n- Replaces Flask dependency with ``invenio-base``.\n\nVersion 1.1.1 (released 2018-12-12)\n\n- Fix an incorrect JS import.\n\nVersion 1.1.0 (released 2018-11-06)\n\n- Introduce webpack support.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2021-10-06T15:11:28", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio internationalization i18n localization l10n", + "homepage_url": "https://github.com/inveniosoftware/invenio-i18n", + "download_url": "https://files.pythonhosted.org/packages/81/56/4398e45ba27c8436e38b8027881d72d1e02ab199d0eb69cb425759173ec4/invenio_i18n-1.3.1-py2.py3-none-any.whl", + "size": 31391, + "sha1": null, + "md5": "1574ffd388ca0a833232f954ea7fcef5", + "sha256": "67b22098900f4fa79a4db47cd189d3fb993223c26e560fd1c87f69b7cc2c5bc8", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-i18n/1.3.1/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-i18n@1.3.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-i18n", + "version": "1.3.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n==============\n Invenio-I18N\n==============\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-i18n.svg\n :target: https://github.com/inveniosoftware/invenio-i18n/blob/master/LICENSE\n\n.. image:: https://github.com/inveniosoftware/invenio-i18n/workflows/CI/badge.svg\n :target: https://github.com/inveniosoftware/invenio-i18n/actions\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-i18n.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-i18n\n\n.. image:: https://img.shields.io/pypi/v/invenio-i18n.svg\n :target: https://pypi.org/pypi/invenio-i18n\n\n\nInvenio internationalization module based on\n`Flask-BabelEx `_.\n\nFeatures:\n\n* Loading and merging message catalogs.\n* Algorithm for detecting a user's locale.\n* Views for changing the locale.\n* Jinja2 macros and filters for I18N.\n\nFurther documentation available at https://invenio-i18n.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\n\nVersion 1.3.1 (released 2021-10-06)\n\n- Fixes issue with language selector button not disabling the currently\n selected field.\n\nVersion 1.3.0 (released 2020-12-07)\n\n- Integrates Semantic-UI templates and assets.\n- Removes webassets-based bundles.\n- Adds InvenioI18N extransion to the API level applications.\n\nVersion 1.2.0 (released 2020-03-06)\n\n- Bumps Flask-BabelEx support latest Flask/Werkzeug.\n- Replaces Flask dependency with ``invenio-base``.\n\nVersion 1.1.1 (released 2018-12-12)\n\n- Fix an incorrect JS import.\n\nVersion 1.1.0 (released 2018-11-06)\n\n- Introduce webpack support.\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2021-10-06T15:11:30", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio internationalization i18n localization l10n", + "homepage_url": "https://github.com/inveniosoftware/invenio-i18n", + "download_url": "https://files.pythonhosted.org/packages/06/70/b65b7ffef1daf8c864329af034c06a56ce8c889d887261553aad75ecd3dc/invenio-i18n-1.3.1.tar.gz", + "size": 36712, + "sha1": null, + "md5": "e589c98d1a78f5a9443ce27523be6352", + "sha256": "02534002f3bf63706a4c3d3b15c32ce4cce0c341217d4204c7c29486fabece22", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-i18n/1.3.1/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-i18n@1.3.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-records", + "version": "1.0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n================\n Invenio-Records\n================\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-records.svg\n :target: https://github.com/inveniosoftware/invenio-records/blob/master/LICENSE\n\n.. image:: https://img.shields.io/travis/inveniosoftware/invenio-records.svg\n :target: https://travis-ci.org/inveniosoftware/invenio-records\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-records.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-records\n\n.. image:: https://img.shields.io/pypi/v/invenio-records.svg\n :target: https://pypi.org/pypi/invenio-records\n\n\n\nInvenio-Records is a metadata storage module. A *record* is a JSON document with\nrevision history identified by a unique `UUID`_ .\n\n.. _UUID: https://en.wikipedia.org/wiki/Universally_unique_identifier\n\nFeatures:\n\n * Generic JSON document storage with revision history.\n * JSONSchema validation of documents.\n * Records creation, update and deletion.\n * CLI and administration interface for CRUD operations on records.\n\nFurther documentation available Documentation:\nhttps://invenio-records.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\nVersion 1.0.2 (released 2019-07-11)\n\n- Fix XSS vulnerability in admin interface.\n\nVersion 1.0.1 (released 2018-12-14)\n\n- Fix CliRunner exceptions\n- Fix json schema url\n- MIT license and shield badge\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2019-07-15T08:35:18", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio metadata", + "homepage_url": "https://github.com/inveniosoftware/invenio-records", + "download_url": "https://files.pythonhosted.org/packages/50/97/fd03b1bbc220643230bf9e1f621d4043d583043d0cf5506a8ccb0551029c/invenio_records-1.0.2-py2.py3-none-any.whl", + "size": 88791, + "sha1": null, + "md5": "9957fa5aa8d4d5eae377698ef37f9cf6", + "sha256": "5818e2dcef2de1d0b3e827b60c13588ce24f60f368a73b8d70ab75d7275eef63", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-records/1.0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-records@1.0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio-records", + "version": "1.0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n================\n Invenio-Records\n================\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio-records.svg\n :target: https://github.com/inveniosoftware/invenio-records/blob/master/LICENSE\n\n.. image:: https://img.shields.io/travis/inveniosoftware/invenio-records.svg\n :target: https://travis-ci.org/inveniosoftware/invenio-records\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/invenio-records.svg\n :target: https://coveralls.io/r/inveniosoftware/invenio-records\n\n.. image:: https://img.shields.io/pypi/v/invenio-records.svg\n :target: https://pypi.org/pypi/invenio-records\n\n\n\nInvenio-Records is a metadata storage module. A *record* is a JSON document with\nrevision history identified by a unique `UUID`_ .\n\n.. _UUID: https://en.wikipedia.org/wiki/Universally_unique_identifier\n\nFeatures:\n\n * Generic JSON document storage with revision history.\n * JSONSchema validation of documents.\n * Records creation, update and deletion.\n * CLI and administration interface for CRUD operations on records.\n\nFurther documentation available Documentation:\nhttps://invenio-records.readthedocs.io/\n\n\n..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\nChanges\n=======\nVersion 1.0.2 (released 2019-07-11)\n\n- Fix XSS vulnerability in admin interface.\n\nVersion 1.0.1 (released 2018-12-14)\n\n- Fix CliRunner exceptions\n- Fix json schema url\n- MIT license and shield badge\n\nVersion 1.0.0 (released 2018-03-23)\n\n- Initial public release.\n\n\n", + "release_date": "2019-07-15T08:35:20", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "invenio metadata", + "homepage_url": "https://github.com/inveniosoftware/invenio-records", + "download_url": "https://files.pythonhosted.org/packages/88/8a/b4bd1e9b493fc119d972ac89920c32fa4573205e8fa2e893e53effc51f1c/invenio-records-1.0.2.tar.gz", + "size": 97890, + "sha1": null, + "md5": "cfb4b57201cb34b9af50f6e28b3b2b78", + "sha256": "c500e7f8c6cee77ed818e7ce13f610e6dd9f96762369d9a99afdaed4e13aa1b7", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio-records/1.0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio-records@1.0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio", + "version": "3.4.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n\n======================\n Invenio Framework v3\n======================\n\n**Open Source framework for large-scale digital repositories.**\n\n.. image:: https://img.shields.io/pypi/v/invenio.svg\n :target: https://pypi.org/project/invenio/\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio.svg\n :target: https://github.com/inveniosoftware/invenio/blob/master/LICENSE\n\n.. image:: https://travis-ci.org/inveniosoftware/invenio.svg?branch=master\n :target: https://travis-ci.org/inveniosoftware/invenio\n\n.. image:: https://img.shields.io/badge/Discord-join%20chat-%237289da\n :target: https://discord.gg/8qatqBC\n\nInvenio Framework is like a Swiss Army knife of battle-tested, safe and secure\nmodules providing you with all the features you need to build a trusted digital\nrepository.\n\n**Our other products**\n\nLooking for a turn-key Research Data Management platform? Checkout `InvenioRDM `_\n\nLooking for a modern Integrated Library System? Checkout `InvenioILS `_\n\n**Built with Invenio Framework**\n\nSee examples on https://inveniosoftware.org/products/framework/ and https://inveniosoftware.org/showcase/.\n\n\n", + "release_date": "2021-05-12T14:00:40", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Invenio digital library framework", + "homepage_url": "https://github.com/inveniosoftware/invenio", + "download_url": "https://files.pythonhosted.org/packages/31/59/5c20981a3f371b59c0545905a800cc5f3f066cb9a14a126ba6b96a255933/invenio-3.4.1-py2.py3-none-any.whl", + "size": 4748, + "sha1": null, + "md5": "30642e31aa3b6764457a22f73a680893", + "sha256": "eddc4d9e8f0f8a1e2efada5a582c9b45d9fe3e49740051e2fe0fe8ec999f94f0", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio/3.4.1/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio@3.4.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "invenio", + "version": "3.4.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of Invenio.\n Copyright (C) 2015-2018 CERN.\n\n Invenio is free software; you can redistribute it and/or modify it\n under the terms of the MIT License; see LICENSE file for more details.\n\n\n======================\n Invenio Framework v3\n======================\n\n**Open Source framework for large-scale digital repositories.**\n\n.. image:: https://img.shields.io/pypi/v/invenio.svg\n :target: https://pypi.org/project/invenio/\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/invenio.svg\n :target: https://github.com/inveniosoftware/invenio/blob/master/LICENSE\n\n.. image:: https://travis-ci.org/inveniosoftware/invenio.svg?branch=master\n :target: https://travis-ci.org/inveniosoftware/invenio\n\n.. image:: https://img.shields.io/badge/Discord-join%20chat-%237289da\n :target: https://discord.gg/8qatqBC\n\nInvenio Framework is like a Swiss Army knife of battle-tested, safe and secure\nmodules providing you with all the features you need to build a trusted digital\nrepository.\n\n**Our other products**\n\nLooking for a turn-key Research Data Management platform? Checkout `InvenioRDM `_\n\nLooking for a modern Integrated Library System? Checkout `InvenioILS `_\n\n**Built with Invenio Framework**\n\nSee examples on https://inveniosoftware.org/products/framework/ and https://inveniosoftware.org/showcase/.\n\n\n", + "release_date": "2021-05-12T14:00:42", + "parties": [ + { + "type": "person", + "role": "author", + "name": "CERN", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Invenio digital library framework", + "homepage_url": "https://github.com/inveniosoftware/invenio", + "download_url": "https://files.pythonhosted.org/packages/fe/e9/bf37c72c3e231ba5dafefb134b0f2cb6fbfc26d7a504047d7277097b6bb8/invenio-3.4.1.tar.gz", + "size": 4051666, + "sha1": null, + "md5": "3cd9ad36a659c6c3946f65d79677cdfe", + "sha256": "d480e1847c681220624563c28f44a0bcc24eb7457e80b9594ff1c285c86bd69b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/invenio/3.4.1/json", + "datasource_id": null, + "purl": "pkg:pypi/invenio@3.4.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "ipaddress", + "version": "1.0.23", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Port of the 3.3+ ipaddress module to 2.6, 2.7, 3.2", + "release_date": "2019-10-18T01:30:27", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Philipp Hagemeister", + "email": "phihag@phihag.de", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/phihag/ipaddress", + "download_url": "https://files.pythonhosted.org/packages/c2/f8/49697181b1651d8347d24c095ce46c7346c37335ddc7d255833e7cde674d/ipaddress-1.0.23-py2.py3-none-any.whl", + "size": 18159, + "sha1": null, + "md5": "1d1615d1bb0dd32b4f3e1cf6a24dca3a", + "sha256": "6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Python Software Foundation License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ipaddress/1.0.23/json", + "datasource_id": null, + "purl": "pkg:pypi/ipaddress@1.0.23" + }, + { + "type": "pypi", + "namespace": null, + "name": "ipaddress", + "version": "1.0.23", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Port of the 3.3+ ipaddress module to 2.6, 2.7, 3.2", + "release_date": "2019-10-18T01:30:24", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Philipp Hagemeister", + "email": "phihag@phihag.de", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/phihag/ipaddress", + "download_url": "https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz", + "size": 32958, + "sha1": null, + "md5": "aaee67a8026782af1831148beb0d9060", + "sha256": "b7f8e0369580bb4a24d5ba1d7cc29660a4a6987763faf1d8a8046830e020e7e2", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Python Software Foundation License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ipaddress/1.0.23/json", + "datasource_id": null, + "purl": "pkg:pypi/ipaddress@1.0.23" + }, + { + "type": "pypi", + "namespace": null, + "name": "ipython-genutils", + "version": "0.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Pretend this doesn't exist. Nobody should use it.\n\n\n", + "release_date": "2017-03-13T22:12:25", + "parties": [ + { + "type": "person", + "role": "author", + "name": "IPython Development Team", + "email": "ipython-dev@scipy.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Interactive,Interpreter,Shell,Web", + "homepage_url": "http://ipython.org", + "download_url": "https://files.pythonhosted.org/packages/fa/bc/9bd3b5c2b4774d5f33b2d544f1460be9df7df2fe42f352135381c347c69a/ipython_genutils-0.2.0-py2.py3-none-any.whl", + "size": 26343, + "sha1": null, + "md5": "5bf384d999c2e38b696a4d6f162875c3", + "sha256": "72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ipython-genutils/0.2.0/json", + "datasource_id": null, + "purl": "pkg:pypi/ipython-genutils@0.2.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "ipython-genutils", + "version": "0.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Pretend this doesn't exist. Nobody should use it.\n\n\n", + "release_date": "2017-03-13T22:12:26", + "parties": [ + { + "type": "person", + "role": "author", + "name": "IPython Development Team", + "email": "ipython-dev@scipy.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Interactive,Interpreter,Shell,Web", + "homepage_url": "http://ipython.org", + "download_url": "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz", + "size": 22208, + "sha1": null, + "md5": "5a4f9781f78466da0ea1a648f3e1f79f", + "sha256": "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ipython-genutils/0.2.0/json", + "datasource_id": null, + "purl": "pkg:pypi/ipython-genutils@0.2.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "ipython", + "version": "5.10.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "IPython provides a rich toolkit to help you make the most out of using Python\ninteractively. Its main components are:\n\n* A powerful interactive Python shell\n* A `Jupyter `_ kernel to work with Python code in Jupyter\n notebooks and other interactive frontends.\n\nThe enhanced interactive Python shells have the following main features:\n\n* Comprehensive object introspection.\n\n* Input history, persistent across sessions.\n\n* Caching of output results during a session with automatically generated\n references.\n\n* Extensible tab completion, with support by default for completion of python\n variables and keywords, filenames and function keywords.\n\n* Extensible system of 'magic' commands for controlling the environment and\n performing many tasks related either to IPython or the operating system.\n\n* A rich configuration system with easy switching between different setups\n (simpler than changing $PYTHONSTARTUP environment variables every time).\n\n* Session logging and reloading.\n\n* Extensible syntax processing for special purpose situations.\n\n* Access to the system shell with user-extensible alias system.\n\n* Easily embeddable in other Python programs and GUIs.\n\n* Integrated access to the pdb debugger and the Python profiler.\n\nThe latest development version is always available from IPython's `GitHub\nsite `_.\n\n\n", + "release_date": "2020-05-01T18:20:44", + "parties": [ + { + "type": "person", + "role": "author", + "name": "The IPython Development Team", + "email": "ipython-dev@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Interactive,Interpreter,Shell,Embedding", + "homepage_url": "https://ipython.org", + "download_url": "https://files.pythonhosted.org/packages/ce/2c/2849a2b37024a01a847c87d81825c0489eb22ffc6416cac009bf281ea838/ipython-5.10.0-py2-none-any.whl", + "size": 760296, + "sha1": null, + "md5": "b2eb02011835b1254431136a0a2b1be5", + "sha256": "68eb2d70595ea5c3f9f5f8f562c9119e86b0f84de3e5d23407a142cb2a73ba11", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ipython/5.10.0/json", + "datasource_id": null, + "purl": "pkg:pypi/ipython@5.10.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "ipython", + "version": "5.10.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "IPython provides a rich toolkit to help you make the most out of using Python\ninteractively. Its main components are:\n\n* A powerful interactive Python shell\n* A `Jupyter `_ kernel to work with Python code in Jupyter\n notebooks and other interactive frontends.\n\nThe enhanced interactive Python shells have the following main features:\n\n* Comprehensive object introspection.\n\n* Input history, persistent across sessions.\n\n* Caching of output results during a session with automatically generated\n references.\n\n* Extensible tab completion, with support by default for completion of python\n variables and keywords, filenames and function keywords.\n\n* Extensible system of 'magic' commands for controlling the environment and\n performing many tasks related either to IPython or the operating system.\n\n* A rich configuration system with easy switching between different setups\n (simpler than changing $PYTHONSTARTUP environment variables every time).\n\n* Session logging and reloading.\n\n* Extensible syntax processing for special purpose situations.\n\n* Access to the system shell with user-extensible alias system.\n\n* Easily embeddable in other Python programs and GUIs.\n\n* Integrated access to the pdb debugger and the Python profiler.\n\nThe latest development version is always available from IPython's `GitHub\nsite `_.\n\n\n", + "release_date": "2020-05-01T18:21:13", + "parties": [ + { + "type": "person", + "role": "author", + "name": "The IPython Development Team", + "email": "ipython-dev@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Interactive,Interpreter,Shell,Embedding", + "homepage_url": "https://ipython.org", + "download_url": "https://files.pythonhosted.org/packages/b6/73/c8f68b3a7d0deece3d2f7ab727fbf262bfca7475330b44043a5503b3aa7a/ipython-5.10.0.tar.gz", + "size": 4978748, + "sha1": null, + "md5": "eae7393ed47415709df3823422571a49", + "sha256": "d1f9e2d02bb0900ddef7b6af114aca3a5cf3dc43b9de1f19d37c4aedbc724fee", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ipython/5.10.0/json", + "datasource_id": null, + "purl": "pkg:pypi/ipython@5.10.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "itsdangerous", + "version": "1.1.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "itsdangerous\n============\n\n... so better sign this\n\nVarious helpers to pass data to untrusted environments and to get it\nback safe and sound. Data is cryptographically signed to ensure that a\ntoken has not been tampered with.\n\nIt's possible to customize how data is serialized. Data is compressed as\nneeded. A timestamp can be added and verified automatically while\nloading a token.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U itsdangerous\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nA Simple Example\n----------------\n\nHere's how you could generate a token for transmitting a user's id and\nname between web requests.\n\n.. code-block:: python\n\n from itsdangerous import URLSafeSerializer\n auth_s = URLSafeSerializer(\"secret key\", \"auth\")\n token = auth_s.dumps({\"id\": 5, \"name\": \"itsdangerous\"})\n\n print(token)\n # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg\n\n data = auth_s.loads(token)\n print(data[\"name\"])\n # itsdangerous\n\n\nDonate\n------\n\nThe Pallets organization develops and supports itsdangerous and other\npopular packages. In order to grow the community of contributors and\nusers, and allow the maintainers to devote more time to the projects,\n`please donate today`_.\n\n.. _please donate today: https://palletsprojects.com/donate\n\n\nLinks\n-----\n\n* Website: https://palletsprojects.com/p/itsdangerous/\n* Documentation: https://itsdangerous.palletsprojects.com/\n* License: `BSD `_\n* Releases: https://pypi.org/project/itsdangerous/\n* Code: https://github.com/pallets/itsdangerous\n* Issue tracker: https://github.com/pallets/itsdangerous/issues\n* Test status: https://travis-ci.org/pallets/itsdangerous\n* Test coverage: https://codecov.io/gh/pallets/itsdangerous\n\n\n", + "release_date": "2018-10-27T00:17:35", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets Team", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/itsdangerous/", + "download_url": "https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl", + "size": 16743, + "sha1": null, + "md5": "55179072b7f84ae38f16b3fdca33aa4f", + "sha256": "b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/itsdangerous", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/itsdangerous/1.1.0/json", + "datasource_id": null, + "purl": "pkg:pypi/itsdangerous@1.1.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "itsdangerous", + "version": "1.1.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "itsdangerous\n============\n\n... so better sign this\n\nVarious helpers to pass data to untrusted environments and to get it\nback safe and sound. Data is cryptographically signed to ensure that a\ntoken has not been tampered with.\n\nIt's possible to customize how data is serialized. Data is compressed as\nneeded. A timestamp can be added and verified automatically while\nloading a token.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U itsdangerous\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nA Simple Example\n----------------\n\nHere's how you could generate a token for transmitting a user's id and\nname between web requests.\n\n.. code-block:: python\n\n from itsdangerous import URLSafeSerializer\n auth_s = URLSafeSerializer(\"secret key\", \"auth\")\n token = auth_s.dumps({\"id\": 5, \"name\": \"itsdangerous\"})\n\n print(token)\n # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg\n\n data = auth_s.loads(token)\n print(data[\"name\"])\n # itsdangerous\n\n\nDonate\n------\n\nThe Pallets organization develops and supports itsdangerous and other\npopular packages. In order to grow the community of contributors and\nusers, and allow the maintainers to devote more time to the projects,\n`please donate today`_.\n\n.. _please donate today: https://palletsprojects.com/donate\n\n\nLinks\n-----\n\n* Website: https://palletsprojects.com/p/itsdangerous/\n* Documentation: https://itsdangerous.palletsprojects.com/\n* License: `BSD `_\n* Releases: https://pypi.org/project/itsdangerous/\n* Code: https://github.com/pallets/itsdangerous\n* Issue tracker: https://github.com/pallets/itsdangerous/issues\n* Test status: https://travis-ci.org/pallets/itsdangerous\n* Test coverage: https://codecov.io/gh/pallets/itsdangerous\n\n\n", + "release_date": "2018-10-27T00:17:37", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets Team", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/itsdangerous/", + "download_url": "https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz", + "size": 53219, + "sha1": null, + "md5": "9b7f5afa7f1e3acfb7786eeca3d99307", + "sha256": "321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/itsdangerous", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/itsdangerous/1.1.0/json", + "datasource_id": null, + "purl": "pkg:pypi/itsdangerous@1.1.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "jinja2", + "version": "2.11.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Jinja\n=====\n\nJinja is a fast, expressive, extensible templating engine. Special\nplaceholders in the template allow writing code similar to Python\nsyntax. Then the template is passed data to render the final document.\n\nIt includes:\n\n- Template inheritance and inclusion.\n- Define and import macros within templates.\n- HTML templates can use autoescaping to prevent XSS from untrusted\n user input.\n- A sandboxed environment can safely render untrusted templates.\n- AsyncIO support for generating templates and calling async\n functions.\n- I18N support with Babel.\n- Templates are compiled to optimized Python code just-in-time and\n cached, or can be compiled ahead-of-time.\n- Exceptions point to the correct line in templates to make debugging\n easier.\n- Extensible filters, tests, functions, and even syntax.\n\nJinja's philosophy is that while application logic belongs in Python if\npossible, it shouldn't make the template designer's job difficult by\nrestricting functionality too much.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n $ pip install -U Jinja2\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nIn A Nutshell\n-------------\n\n.. code-block:: jinja\n\n {% extends \"base.html\" %}\n {% block title %}Members{% endblock %}\n {% block content %}\n \n {% endblock %}\n\n\nLinks\n-----\n\n- Website: https://palletsprojects.com/p/jinja/\n- Documentation: https://jinja.palletsprojects.com/\n- Releases: https://pypi.org/project/Jinja2/\n- Code: https://github.com/pallets/jinja\n- Issue tracker: https://github.com/pallets/jinja/issues\n- Test status: https://dev.azure.com/pallets/jinja/_build\n- Official chat: https://discord.gg/t6rrQZH\n\n\n", + "release_date": "2021-01-31T16:33:07", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/jinja/", + "download_url": "https://files.pythonhosted.org/packages/7e/c2/1eece8c95ddbc9b1aeb64f5783a9e07a286de42191b7204d67b7496ddf35/Jinja2-2.11.3-py2.py3-none-any.whl", + "size": 125699, + "sha1": null, + "md5": "8e733c6f4cdef7f6a336299e8e548dfa", + "sha256": "03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/jinja", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jinja2/2.11.3/json", + "datasource_id": null, + "purl": "pkg:pypi/jinja2@2.11.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "jinja2", + "version": "2.11.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Jinja\n=====\n\nJinja is a fast, expressive, extensible templating engine. Special\nplaceholders in the template allow writing code similar to Python\nsyntax. Then the template is passed data to render the final document.\n\nIt includes:\n\n- Template inheritance and inclusion.\n- Define and import macros within templates.\n- HTML templates can use autoescaping to prevent XSS from untrusted\n user input.\n- A sandboxed environment can safely render untrusted templates.\n- AsyncIO support for generating templates and calling async\n functions.\n- I18N support with Babel.\n- Templates are compiled to optimized Python code just-in-time and\n cached, or can be compiled ahead-of-time.\n- Exceptions point to the correct line in templates to make debugging\n easier.\n- Extensible filters, tests, functions, and even syntax.\n\nJinja's philosophy is that while application logic belongs in Python if\npossible, it shouldn't make the template designer's job difficult by\nrestricting functionality too much.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n $ pip install -U Jinja2\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nIn A Nutshell\n-------------\n\n.. code-block:: jinja\n\n {% extends \"base.html\" %}\n {% block title %}Members{% endblock %}\n {% block content %}\n \n {% endblock %}\n\n\nLinks\n-----\n\n- Website: https://palletsprojects.com/p/jinja/\n- Documentation: https://jinja.palletsprojects.com/\n- Releases: https://pypi.org/project/Jinja2/\n- Code: https://github.com/pallets/jinja\n- Issue tracker: https://github.com/pallets/jinja/issues\n- Test status: https://dev.azure.com/pallets/jinja/_build\n- Official chat: https://discord.gg/t6rrQZH\n\n\n", + "release_date": "2021-01-31T16:33:09", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/jinja/", + "download_url": "https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz", + "size": 257589, + "sha1": null, + "md5": "231dc00d34afb2672c497713fa9cdaaa", + "sha256": "a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/jinja", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jinja2/2.11.3/json", + "datasource_id": null, + "purl": "pkg:pypi/jinja2@2.11.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonpatch", + "version": "1.32", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "python-json-patch\n=================\n\n|PyPI version| |Supported Python versions| |Build Status| |Coverage\nStatus|\n\nApplying JSON Patches in Python\n-------------------------------\n\nLibrary to apply JSON Patches according to `RFC\n6902 `__\n\nSee source code for examples\n\n- Website: https://github.com/stefankoegl/python-json-patch\n- Repository: https://github.com/stefankoegl/python-json-patch.git\n- Documentation: https://python-json-patch.readthedocs.org/\n- PyPI: https://pypi.python.org/pypi/jsonpatch\n- Travis CI: https://travis-ci.org/stefankoegl/python-json-patch\n- Coveralls: https://coveralls.io/r/stefankoegl/python-json-patch\n\nRunning external tests\n----------------------\n\nTo run external tests (such as those from\nhttps://github.com/json-patch/json-patch-tests) use ext\\_test.py\n\n::\n\n ./ext_tests.py ../json-patch-tests/tests.json\n\n.. |PyPI version| image:: https://img.shields.io/pypi/v/jsonpatch.svg\n :target: https://pypi.python.org/pypi/jsonpatch/\n.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/jsonpatch.svg\n :target: https://pypi.python.org/pypi/jsonpatch/\n.. |Build Status| image:: https://travis-ci.org/stefankoegl/python-json-patch.png?branch=master\n :target: https://travis-ci.org/stefankoegl/python-json-patch\n.. |Coverage Status| image:: https://coveralls.io/repos/stefankoegl/python-json-patch/badge.png?branch=master\n :target: https://coveralls.io/r/stefankoegl/python-json-patch?branch=master\n\n\n", + "release_date": "2021-03-13T19:16:37", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Stefan K\u00f6gl", + "email": "stefan@skoegl.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/stefankoegl/python-json-patch", + "download_url": "https://files.pythonhosted.org/packages/a3/55/f7c93bae36d869292aedfbcbae8b091386194874f16390d680136edd2b28/jsonpatch-1.32-py2.py3-none-any.whl", + "size": 12547, + "sha1": null, + "md5": "a59ecf0282b11c955734f40a053b05c0", + "sha256": "26ac385719ac9f54df8a2f0827bb8253aa3ea8ab7b3368457bcdb8c14595a397", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Modified BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonpatch/1.32/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonpatch@1.32" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonpatch", + "version": "1.32", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "python-json-patch\n=================\n\n|PyPI version| |Supported Python versions| |Build Status| |Coverage\nStatus|\n\nApplying JSON Patches in Python\n-------------------------------\n\nLibrary to apply JSON Patches according to `RFC\n6902 `__\n\nSee source code for examples\n\n- Website: https://github.com/stefankoegl/python-json-patch\n- Repository: https://github.com/stefankoegl/python-json-patch.git\n- Documentation: https://python-json-patch.readthedocs.org/\n- PyPI: https://pypi.python.org/pypi/jsonpatch\n- Travis CI: https://travis-ci.org/stefankoegl/python-json-patch\n- Coveralls: https://coveralls.io/r/stefankoegl/python-json-patch\n\nRunning external tests\n----------------------\n\nTo run external tests (such as those from\nhttps://github.com/json-patch/json-patch-tests) use ext\\_test.py\n\n::\n\n ./ext_tests.py ../json-patch-tests/tests.json\n\n.. |PyPI version| image:: https://img.shields.io/pypi/v/jsonpatch.svg\n :target: https://pypi.python.org/pypi/jsonpatch/\n.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/jsonpatch.svg\n :target: https://pypi.python.org/pypi/jsonpatch/\n.. |Build Status| image:: https://travis-ci.org/stefankoegl/python-json-patch.png?branch=master\n :target: https://travis-ci.org/stefankoegl/python-json-patch\n.. |Coverage Status| image:: https://coveralls.io/repos/stefankoegl/python-json-patch/badge.png?branch=master\n :target: https://coveralls.io/r/stefankoegl/python-json-patch?branch=master\n\n\n", + "release_date": "2021-03-13T19:16:38", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Stefan K\u00f6gl", + "email": "stefan@skoegl.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/stefankoegl/python-json-patch", + "download_url": "https://files.pythonhosted.org/packages/21/67/83452af2a6db7c4596d1e2ecaa841b9a900980103013b867f2865e5e1cf0/jsonpatch-1.32.tar.gz", + "size": 20853, + "sha1": null, + "md5": "ca0a799438fb7d319bd0d7552a13d10f", + "sha256": "b6ddfe6c3db30d81a96aaeceb6baf916094ffa23d7dd5fa2c13e13f8b6e600c2", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Modified BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonpatch/1.32/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonpatch@1.32" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonpointer", + "version": "2.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "python-json-pointer\n===================\n\n[![PyPI version](https://img.shields.io/pypi/v/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)\n[![Build Status](https://travis-ci.org/stefankoegl/python-json-pointer.svg?branch=master)](https://travis-ci.org/stefankoegl/python-json-pointer)\n[![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)\n\n\nResolve JSON Pointers in Python\n-------------------------------\n\nLibrary to resolve JSON Pointers according to\n[RFC 6901](http://tools.ietf.org/html/rfc6901)\n\nSee source code for examples\n* Website: https://github.com/stefankoegl/python-json-pointer\n* Repository: https://github.com/stefankoegl/python-json-pointer.git\n* Documentation: https://python-json-pointer.readthedocs.org/\n* PyPI: https://pypi.python.org/pypi/jsonpointer\n* Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer\n* Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer\n\n\n", + "release_date": "2022-04-10T11:39:28", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Stefan K\u00f6gl", + "email": "stefan@skoegl.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/stefankoegl/python-json-pointer", + "download_url": "https://files.pythonhosted.org/packages/a3/be/8dc9d31b50e38172c8020c40f497ce8debdb721545ddb9fcb7cca89ea9e6/jsonpointer-2.3-py2.py3-none-any.whl", + "size": 7753, + "sha1": null, + "md5": "1ba993a70adde49bfcdd46a1605572f6", + "sha256": "51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Modified BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonpointer/2.3/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonpointer@2.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonpointer", + "version": "2.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "python-json-pointer\n===================\n\n[![PyPI version](https://img.shields.io/pypi/v/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)\n[![Build Status](https://travis-ci.org/stefankoegl/python-json-pointer.svg?branch=master)](https://travis-ci.org/stefankoegl/python-json-pointer)\n[![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)\n\n\nResolve JSON Pointers in Python\n-------------------------------\n\nLibrary to resolve JSON Pointers according to\n[RFC 6901](http://tools.ietf.org/html/rfc6901)\n\nSee source code for examples\n* Website: https://github.com/stefankoegl/python-json-pointer\n* Repository: https://github.com/stefankoegl/python-json-pointer.git\n* Documentation: https://python-json-pointer.readthedocs.org/\n* PyPI: https://pypi.python.org/pypi/jsonpointer\n* Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer\n* Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer\n\n\n", + "release_date": "2022-04-10T11:39:29", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Stefan K\u00f6gl", + "email": "stefan@skoegl.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/stefankoegl/python-json-pointer", + "download_url": "https://files.pythonhosted.org/packages/a0/6c/c52556b957a0f904e7c45585444feef206fe5cb1ff656303a1d6d922a53b/jsonpointer-2.3.tar.gz", + "size": 9295, + "sha1": null, + "md5": "57fd6581e61d56960d8c2027ff33f5c0", + "sha256": "97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Modified BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonpointer/2.3/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonpointer@2.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonref", + "version": "0.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "jsonref\n=======\n\n\n.. image:: https://readthedocs.org/projects/jsonref/badge/?version=latest\n :target: https://jsonref.readthedocs.io/en/latest/\n\n.. image:: https://travis-ci.org/gazpachoking/jsonref.png?branch=master\n :target: https://travis-ci.org/gazpachoking/jsonref\n\n.. image:: https://coveralls.io/repos/gazpachoking/jsonref/badge.png?branch=master\n :target: https://coveralls.io/r/gazpachoking/jsonref\n\n\n``jsonref`` is a library for automatic dereferencing of\n`JSON Reference `_\nobjects for Python (supporting Python 2.6+ and Python 3.3+).\n\nThis library lets you use a data structure with JSON reference objects, as if\nthe references had been replaced with the referent data.\n\n\n.. code-block:: python\n\n >>> from pprint import pprint\n >>> import jsonref\n\n >>> # An example json document\n >>> json_str = \"\"\"{\"real\": [1, 2, 3, 4], \"ref\": {\"$ref\": \"#/real\"}}\"\"\"\n >>> data = jsonref.loads(json_str)\n >>> pprint(data) # Reference is not evaluated until here\n {'real': [1, 2, 3, 4], 'ref': [1, 2, 3, 4]}\n\n\nFeatures\n--------\n\n* References are evaluated lazily. Nothing is dereferenced until it is used.\n\n* Recursive references are supported, and create recursive python data\n structures.\n\n\nReferences objects are actually replaced by lazy lookup proxy objects which are\nalmost completely transparent.\n\n.. code-block:: python\n\n >>> data = jsonref.loads('{\"real\": [1, 2, 3, 4], \"ref\": {\"$ref\": \"#/real\"}}')\n >>> # You can tell it is a proxy by using the type function\n >>> type(data[\"real\"]), type(data[\"ref\"])\n (, )\n >>> # You have direct access to the referent data with the __subject__\n >>> # attribute\n >>> type(data[\"ref\"].__subject__)\n \n >>> # If you need to get at the reference object\n >>> data[\"ref\"].__reference__\n {'$ref': '#/real'}\n >>> # Other than that you can use the proxy just like the underlying object\n >>> ref = data[\"ref\"]\n >>> isinstance(ref, list)\n True\n >>> data[\"real\"] == ref\n True\n >>> ref.append(5)\n >>> del ref[0]\n >>> # Actions on the reference affect the real data (if it is mutable)\n >>> pprint(data)\n {'real': [2, 3, 4, 5], 'ref': [2, 3, 4, 5]}\n\n\n", + "release_date": "2018-10-07T19:24:09", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Chase Sterling", + "email": "chase.sterling@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/gazpachoking/jsonref", + "download_url": "https://files.pythonhosted.org/packages/b3/cf/93d4f34d76863d4fb995cb8e3e4f29908304065ce6381e0349700c44ad0c/jsonref-0.2.tar.gz", + "size": 13032, + "sha1": null, + "md5": "42b518b9ccd6852d1d709749bc96cb70", + "sha256": "f3c45b121cf6257eafabdc3a8008763aed1cd7da06dbabc59a9e4d2a5e4e6697", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonref/0.2/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonref@0.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonresolver", + "version": "0.3.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of jsonresolver\n Copyright (C) 2015 CERN.\n\n jsonresolver is free software; you can redistribute it and/or modify\n it under the terms of the Revised BSD License; see LICENSE file for\n more details.\n\n==============\n JSONResolver\n==============\n\n.. image:: https://img.shields.io/travis/inveniosoftware/jsonresolver.svg\n :target: https://travis-ci.org/inveniosoftware/jsonresolver\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/jsonresolver.svg\n :target: https://coveralls.io/r/inveniosoftware/jsonresolver\n\n.. image:: https://img.shields.io/github/tag/inveniosoftware/jsonresolver.svg\n :target: https://github.com/inveniosoftware/jsonresolver/releases\n\n.. image:: https://img.shields.io/pypi/dm/jsonresolver.svg\n :target: https://pypi.python.org/pypi/jsonresolver\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/jsonresolver.svg\n :target: https://github.com/inveniosoftware/jsonresolver/blob/master/LICENSE\n\n\nJSON data resolver with support for plugins.\n\n*This is an experimental developer preview release.*\n\n* Free software: BSD license\n* Documentation: https://jsonresolver.readthedocs.io/\n\n\n..\n This file is part of jsonresolver\n Copyright (C) 2015, 2016 CERN.\n\n jsonresolver is free software; you can redistribute it and/or modify\n it under the terms of the Revised BSD License; see LICENSE file for\n more details.\n\nChanges\n=======\n\nVersion 0.3.1 (released 2020-05-06)\n-----------------------------------\n\n- Deprecated Python versions lower than 3.6.0. Now supporting 3.6.0 and 3.7.0.\n\n\nVersion 0.3.0 (released 2020-03-12)\n-----------------------------------\n\n- Drops support for Python 2.7\n- Updates testing method\n- Updates python dependencies\n\n\nVersion 0.2.1 (released 2016-04-15)\n-----------------------------------\n\nBug fixes\n~~~~~~~~~\n\n- Fixes issue with exceptions raised during e.g. resolver plugin\n loading being caught and not propagated.\n\nVersion 0.2.0 (released 2016-04-06)\n-----------------------------------\n\nIncompatible changes\n~~~~~~~~~~~~~~~~~~~~\n\n- Changes resolving to be based on hostname without 'http://' prefix.\n\nBug fixes\n~~~~~~~~~\n\n- Fixes issues with the hostname not being matched resulting in the\n same route on two hosts not to work.\n\nVersion 0.1.1 (released 2015-12-11)\n-----------------------------------\n\nImproved features\n~~~~~~~~~~~~~~~~~\n\n- Delays the url_map building until first resolve request.\n\nVersion 0.1.0 (released 2015-11-18)\n-----------------------------------\n\n- Initial public release.\n\n\n", + "release_date": "2020-05-06T13:33:07", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Invenio collaboration", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/inveniosoftware/jsonresolver", + "download_url": "https://files.pythonhosted.org/packages/0d/8e/e63fba33303e2062eb1fbd38a5483538ad1b5eee73e04ac1d1f632ac2b76/jsonresolver-0.3.1-py2.py3-none-any.whl", + "size": 9862, + "sha1": null, + "md5": "885596a6122d059b65db7cf2157c981c", + "sha256": "f17a526988456d9895023ae3580714d6ce6af5656869d11d9860dc4a799cf6d4", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonresolver/0.3.1/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonresolver@0.3.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonresolver", + "version": "0.3.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "..\n This file is part of jsonresolver\n Copyright (C) 2015 CERN.\n\n jsonresolver is free software; you can redistribute it and/or modify\n it under the terms of the Revised BSD License; see LICENSE file for\n more details.\n\n==============\n JSONResolver\n==============\n\n.. image:: https://img.shields.io/travis/inveniosoftware/jsonresolver.svg\n :target: https://travis-ci.org/inveniosoftware/jsonresolver\n\n.. image:: https://img.shields.io/coveralls/inveniosoftware/jsonresolver.svg\n :target: https://coveralls.io/r/inveniosoftware/jsonresolver\n\n.. image:: https://img.shields.io/github/tag/inveniosoftware/jsonresolver.svg\n :target: https://github.com/inveniosoftware/jsonresolver/releases\n\n.. image:: https://img.shields.io/pypi/dm/jsonresolver.svg\n :target: https://pypi.python.org/pypi/jsonresolver\n\n.. image:: https://img.shields.io/github/license/inveniosoftware/jsonresolver.svg\n :target: https://github.com/inveniosoftware/jsonresolver/blob/master/LICENSE\n\n\nJSON data resolver with support for plugins.\n\n*This is an experimental developer preview release.*\n\n* Free software: BSD license\n* Documentation: https://jsonresolver.readthedocs.io/\n\n\n..\n This file is part of jsonresolver\n Copyright (C) 2015, 2016 CERN.\n\n jsonresolver is free software; you can redistribute it and/or modify\n it under the terms of the Revised BSD License; see LICENSE file for\n more details.\n\nChanges\n=======\n\nVersion 0.3.1 (released 2020-05-06)\n-----------------------------------\n\n- Deprecated Python versions lower than 3.6.0. Now supporting 3.6.0 and 3.7.0.\n\n\nVersion 0.3.0 (released 2020-03-12)\n-----------------------------------\n\n- Drops support for Python 2.7\n- Updates testing method\n- Updates python dependencies\n\n\nVersion 0.2.1 (released 2016-04-15)\n-----------------------------------\n\nBug fixes\n~~~~~~~~~\n\n- Fixes issue with exceptions raised during e.g. resolver plugin\n loading being caught and not propagated.\n\nVersion 0.2.0 (released 2016-04-06)\n-----------------------------------\n\nIncompatible changes\n~~~~~~~~~~~~~~~~~~~~\n\n- Changes resolving to be based on hostname without 'http://' prefix.\n\nBug fixes\n~~~~~~~~~\n\n- Fixes issues with the hostname not being matched resulting in the\n same route on two hosts not to work.\n\nVersion 0.1.1 (released 2015-12-11)\n-----------------------------------\n\nImproved features\n~~~~~~~~~~~~~~~~~\n\n- Delays the url_map building until first resolve request.\n\nVersion 0.1.0 (released 2015-11-18)\n-----------------------------------\n\n- Initial public release.\n\n\n", + "release_date": "2020-05-06T13:33:08", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Invenio collaboration", + "email": "info@inveniosoftware.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/inveniosoftware/jsonresolver", + "download_url": "https://files.pythonhosted.org/packages/2c/45/30a9704607f4f868bb44a1f04d133157b8355cc1b99e083e9d7d47df3d24/jsonresolver-0.3.1.tar.gz", + "size": 19766, + "sha1": null, + "md5": "324cc0d097be3a44f1e7234a1a8ad51a", + "sha256": "2d8090f6a1fe92e70f2903f05515415bde7ce46402e528279f865bce4ee689ca", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonresolver/0.3.1/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonresolver@0.3.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "jsonschema", + "version": "4.0.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "==========\njsonschema\n==========\n\n|PyPI| |Pythons| |CI| |ReadTheDocs| |Precommit|\n\n.. |PyPI| image:: https://img.shields.io/pypi/v/jsonschema.svg\n :alt: PyPI version\n :target: https://pypi.org/project/jsonschema/\n\n.. |Pythons| image:: https://img.shields.io/pypi/pyversions/jsonschema.svg\n :alt: Supported Python versions\n :target: https://pypi.org/project/jsonschema/\n\n.. |CI| image:: https://github.com/Julian/jsonschema/workflows/CI/badge.svg\n :alt: Build status\n :target: https://github.com/Julian/jsonschema/actions?query=workflow%3ACI\n\n.. |ReadTheDocs| image:: https://readthedocs.org/projects/python-jsonschema/badge/?version=stable&style=flat\n :alt: ReadTheDocs status\n :target: https://python-jsonschema.readthedocs.io/en/stable/\n\n.. |Precommit| image:: https://results.pre-commit.ci/badge/github/Julian/jsonschema/main.svg\n :alt: pre-commit.ci status\n :target: https://results.pre-commit.ci/latest/github/Julian/jsonschema/main\n\n\n``jsonschema`` is an implementation of `JSON Schema\n`_ for Python.\n\n.. code-block:: python\n\n >>> from jsonschema import validate\n\n >>> # A sample schema, like what we'd get from json.load()\n >>> schema = {\n ... \"type\" : \"object\",\n ... \"properties\" : {\n ... \"price\" : {\"type\" : \"number\"},\n ... \"name\" : {\"type\" : \"string\"},\n ... },\n ... }\n\n >>> # If no exception is raised by validate(), the instance is valid.\n >>> validate(instance={\"name\" : \"Eggs\", \"price\" : 34.99}, schema=schema)\n\n >>> validate(\n ... instance={\"name\" : \"Eggs\", \"price\" : \"Invalid\"}, schema=schema,\n ... ) # doctest: +IGNORE_EXCEPTION_DETAIL\n Traceback (most recent call last):\n ...\n ValidationError: 'Invalid' is not of type 'number'\n\nIt can also be used from console:\n\n.. code-block:: bash\n\n $ jsonschema --instance sample.json sample.schema\n\nFeatures\n--------\n\n* Partial support for\n `Draft 2020-12 `_ and\n `Draft 2019-09 `_,\n except for ``dynamicRef`` / ``recursiveRef`` and ``$vocabulary`` (in-progress).\n Full support for\n `Draft 7 `_,\n `Draft 6 `_,\n `Draft 4 `_\n and\n `Draft 3 `_\n\n* `Lazy validation `_\n that can iteratively report *all* validation errors.\n\n* `Programmatic querying `_\n of which properties or items failed validation.\n\n\nInstallation\n------------\n\n``jsonschema`` is available on `PyPI `_. You can install using `pip `_:\n\n.. code-block:: bash\n\n $ pip install jsonschema\n\n\nRunning the Test Suite\n----------------------\n\nIf you have ``tox`` installed (perhaps via ``pip install tox`` or your\npackage manager), running ``tox`` in the directory of your source\ncheckout will run ``jsonschema``'s test suite on all of the versions\nof Python ``jsonschema`` supports. If you don't have all of the\nversions that ``jsonschema`` is tested under, you'll likely want to run\nusing ``tox``'s ``--skip-missing-interpreters`` option.\n\nOf course you're also free to just run the tests on a single version with your\nfavorite test runner. The tests live in the ``jsonschema.tests`` package.\n\n\nBenchmarks\n----------\n\n``jsonschema``'s benchmarks make use of `pyperf\n`_. Running them can be done via::\n\n $ tox -e perf\n\n\nCommunity\n---------\n\nThe JSON Schema specification has `a Slack\n`_, with an `invite link on its home page\n`_. Many folks knowledgeable on authoring\nschemas can be found there.\n\nOtherwise, asking questions on Stack Overflow is another means of\ngetting help if you're stuck.\n\nContributing\n------------\n\nI'm Julian Berman.\n\n``jsonschema`` is on `GitHub `_.\n\nGet in touch, via GitHub or otherwise, if you've got something to contribute,\nit'd be most welcome!\n\nYou can also generally find me on Libera (nick: ``Julian``) in various\nchannels, including ``#python``.\n\nIf you feel overwhelmingly grateful, you can also `sponsor me\n`_.\n\nAnd for companies who appreciate ``jsonschema`` and its continued support\nand growth, ``jsonschema`` is also now supportable via `TideLift\n`_.\n\n\n", + "release_date": "2021-09-29T23:21:52", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Julian Berman", + "email": "Julian@GrayVines.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/Julian/jsonschema", + "download_url": "https://files.pythonhosted.org/packages/9c/99/9789c7fd0bb8876a7d624d903195ce11e5618b421bdb1bf7c975d17a9bc3/jsonschema-4.0.0.tar.gz", + "size": 290808, + "sha1": null, + "md5": "c34c3dab1d792063972a3da3abb0e49f", + "sha256": "bc51325b929171791c42ebc1c70b9713eb134d3bb8ebd5474c8b659b15be6d86", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/jsonschema/4.0.0/json", + "datasource_id": null, + "purl": "pkg:pypi/jsonschema@4.0.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "kombu", + "version": "4.6.11", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "", + "release_date": "2020-06-24T07:11:18", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ask Solem", + "email": "auvipy@gmail.com, ask@celeryproject.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "messaging message amqp rabbitmq redis actor producer consumer", + "homepage_url": "https://kombu.readthedocs.io", + "download_url": "https://files.pythonhosted.org/packages/9e/34/3eea6a3a9ff81b0c7ddbdceb22a1ffc1b5907d863f27ca19a68777d2211d/kombu-4.6.11-py2.py3-none-any.whl", + "size": 184431, + "sha1": null, + "md5": "897e94e5d08627c9ac649a007dffb9cc", + "sha256": "be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/kombu/4.6.11/json", + "datasource_id": null, + "purl": "pkg:pypi/kombu@4.6.11" + }, + { + "type": "pypi", + "namespace": null, + "name": "kombu", + "version": "4.6.11", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "", + "release_date": "2020-06-24T07:11:39", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ask Solem", + "email": "auvipy@gmail.com, ask@celeryproject.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "messaging message amqp rabbitmq redis actor producer consumer", + "homepage_url": "https://kombu.readthedocs.io", + "download_url": "https://files.pythonhosted.org/packages/45/e1/00f2e3f6f5575aa2f7ee41e2fd884ce573f8146e136bde37daf45ef7ca5e/kombu-4.6.11.tar.gz", + "size": 406968, + "sha1": null, + "md5": "759b31d97fc11c4cb16f6d293723e85e", + "sha256": "ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/kombu/4.6.11/json", + "datasource_id": null, + "purl": "pkg:pypi/kombu@4.6.11" + }, + { + "type": "pypi", + "namespace": null, + "name": "limits", + "version": "1.6", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. |ci| image:: https://github.com/alisaifee/limits/workflows/CI/badge.svg?branch=master\n :target: https://github.com/alisaifee/limits/actions?query=branch%3Amaster+workflow%3ACI\n.. |codecov| image:: https://codecov.io/gh/alisaifee/limits/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/alisaifee/limits\n.. |pypi| image:: https://img.shields.io/pypi/v/limits.svg?style=flat-square\n :target: https://pypi.python.org/pypi/limits\n.. |license| image:: https://img.shields.io/pypi/l/limits.svg?style=flat-square\n :target: https://pypi.python.org/pypi/limits\n\n*************\nlimits\n*************\n|ci| |codecov| |pypi| |license|\n\n*limits* provides utilities to implement rate limiting using\nvarious strategies and storage backends such as redis & memcached.\n\n*****\nLinks\n*****\n\n* `Documentation `_\n* `Changelog `_\n\n\n\n", + "release_date": "2021-11-27T18:06:01", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ali-Akber Saifee", + "email": "ali@indydevs.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://limits.readthedocs.org", + "download_url": "https://files.pythonhosted.org/packages/f8/80/3e208b8cfdcf9bf85006097a53c93f53a0c23bc711f1e6abfc180ebb4bca/limits-1.6.tar.gz", + "size": 37248, + "sha1": null, + "md5": "b81f48fd91a85b5ba96c3b3bc771c808", + "sha256": "6c0a57b42647f1141f5a7a0a8479b49e4367c24937a01bd9d4063a595c2dd48a", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/limits/1.6/json", + "datasource_id": null, + "purl": "pkg:pypi/limits@1.6" + }, + { + "type": "pypi", + "namespace": null, + "name": "markupsafe", + "version": "1.1.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "MarkupSafe\n==========\n\nMarkupSafe implements a text object that escapes characters so it is\nsafe to use in HTML and XML. Characters that have special meanings are\nreplaced so that they display as the actual characters. This mitigates\ninjection attacks, meaning untrusted user input can safely be displayed\non a page.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U MarkupSafe\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nExamples\n--------\n\n.. code-block:: pycon\n\n >>> from markupsafe import Markup, escape\n >>> # escape replaces special characters and wraps in Markup\n >>> escape('')\n Markup(u'<script>alert(document.cookie);</script>')\n >>> # wrap in Markup to mark text \"safe\" and prevent escaping\n >>> Markup('Hello')\n Markup('hello')\n >>> escape(Markup('Hello'))\n Markup('hello')\n >>> # Markup is a text subclass (str on Python 3, unicode on Python 2)\n >>> # methods and operators escape their arguments\n >>> template = Markup(\"Hello %s\")\n >>> template % '\"World\"'\n Markup('Hello "World"')\n\n\nDonate\n------\n\nThe Pallets organization develops and supports MarkupSafe and other\nlibraries that use it. In order to grow the community of contributors\nand users, and allow the maintainers to devote more time to the\nprojects, `please donate today`_.\n\n.. _please donate today: https://palletsprojects.com/donate\n\n\nLinks\n-----\n\n* Website: https://palletsprojects.com/p/markupsafe/\n* Documentation: https://markupsafe.palletsprojects.com/\n* License: `BSD-3-Clause `_\n* Releases: https://pypi.org/project/MarkupSafe/\n* Code: https://github.com/pallets/markupsafe\n* Issue tracker: https://github.com/pallets/markupsafe/issues\n* Test status:\n\n * Linux, Mac: https://travis-ci.org/pallets/markupsafe\n * Windows: https://ci.appveyor.com/project/pallets/markupsafe\n\n* Test coverage: https://codecov.io/gh/pallets/markupsafe\n\n\n", + "release_date": "2019-02-24T01:04:53", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "The Pallets Team", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/markupsafe/", + "download_url": "https://files.pythonhosted.org/packages/d8/1f/e97c4c6b182e59562f99c207f0f621d15a42fc82a6532a98e0b2d38b7c4e/MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", + "size": 24349, + "sha1": null, + "md5": "01e8f1759663535a494b0098c29bb39c", + "sha256": "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/markupsafe", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/markupsafe/1.1.1/json", + "datasource_id": null, + "purl": "pkg:pypi/markupsafe@1.1.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "markupsafe", + "version": "1.1.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "MarkupSafe\n==========\n\nMarkupSafe implements a text object that escapes characters so it is\nsafe to use in HTML and XML. Characters that have special meanings are\nreplaced so that they display as the actual characters. This mitigates\ninjection attacks, meaning untrusted user input can safely be displayed\non a page.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U MarkupSafe\n\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\nExamples\n--------\n\n.. code-block:: pycon\n\n >>> from markupsafe import Markup, escape\n >>> # escape replaces special characters and wraps in Markup\n >>> escape('')\n Markup(u'<script>alert(document.cookie);</script>')\n >>> # wrap in Markup to mark text \"safe\" and prevent escaping\n >>> Markup('Hello')\n Markup('hello')\n >>> escape(Markup('Hello'))\n Markup('hello')\n >>> # Markup is a text subclass (str on Python 3, unicode on Python 2)\n >>> # methods and operators escape their arguments\n >>> template = Markup(\"Hello %s\")\n >>> template % '\"World\"'\n Markup('Hello "World"')\n\n\nDonate\n------\n\nThe Pallets organization develops and supports MarkupSafe and other\nlibraries that use it. In order to grow the community of contributors\nand users, and allow the maintainers to devote more time to the\nprojects, `please donate today`_.\n\n.. _please donate today: https://palletsprojects.com/donate\n\n\nLinks\n-----\n\n* Website: https://palletsprojects.com/p/markupsafe/\n* Documentation: https://markupsafe.palletsprojects.com/\n* License: `BSD-3-Clause `_\n* Releases: https://pypi.org/project/MarkupSafe/\n* Code: https://github.com/pallets/markupsafe\n* Issue tracker: https://github.com/pallets/markupsafe/issues\n* Test status:\n\n * Linux, Mac: https://travis-ci.org/pallets/markupsafe\n * Windows: https://ci.appveyor.com/project/pallets/markupsafe\n\n* Test coverage: https://codecov.io/gh/pallets/markupsafe\n\n\n", + "release_date": "2019-02-24T01:05:32", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "The Pallets Team", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/markupsafe/", + "download_url": "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz", + "size": 19151, + "sha1": null, + "md5": "43fd756864fe42063068e092e220c57b", + "sha256": "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/markupsafe", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/markupsafe/1.1.1/json", + "datasource_id": null, + "purl": "pkg:pypi/markupsafe@1.1.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "mock", + "version": "3.0.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "mock is a library for testing in Python. It allows you to replace parts of\nyour system under test with mock objects and make assertions about how they\nhave been used.\n\nmock is now part of the Python standard library, available as `unittest.mock\n`_ in Python 3.3\nonwards.\n\nThis package contains a rolling backport of the standard library mock code\ncompatible with Python 2.7 and 3.4 and up.\n\nPlease see the standard library documentation for more details.\n\n:Homepage: `Mock Homepage`_\n:Download: `Mock on PyPI`_\n:Documentation: `Python Docs`_\n:License: `BSD License`_\n:Support: `Mailing list (testing-in-python@lists.idyll.org)\n `_\n:Code: `GitHub\n `_\n:Issue tracker: `GitHub Issues\n `_\n:Build status:\n |CircleCI|_ |Docs|_\n\n .. |CircleCI| image:: https://circleci.com/gh/testing-cabal/mock/tree/master.svg?style=shield\n .. _CircleCI: https://circleci.com/gh/testing-cabal/mock/tree/master\n\n .. |Docs| image:: https://readthedocs.org/projects/mock/badge/?version=latest\n .. _Docs: http://mock.readthedocs.org/en/latest/\n\n.. _Mock Homepage: http://mock.readthedocs.org/en/latest/\n.. _BSD License: https://github.com/testing-cabal/mock/blob/master/LICENSE.txt\n.. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html\n.. _mock on PyPI: https://pypi.org/project/mock/\n\n\n", + "release_date": "2019-05-07T21:22:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Testing Cabal", + "email": "testing-in-python@lists.idyll.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://mock.readthedocs.org/en/latest/", + "download_url": "https://files.pythonhosted.org/packages/05/d2/f94e68be6b17f46d2c353564da56e6fb89ef09faeeff3313a046cb810ca9/mock-3.0.5-py2.py3-none-any.whl", + "size": 25027, + "sha1": null, + "md5": "0b273f0d265660e78e96b739c6a813b4", + "sha256": "d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "OSI Approved :: BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/mock/3.0.5/json", + "datasource_id": null, + "purl": "pkg:pypi/mock@3.0.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "mock", + "version": "3.0.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "mock is a library for testing in Python. It allows you to replace parts of\nyour system under test with mock objects and make assertions about how they\nhave been used.\n\nmock is now part of the Python standard library, available as `unittest.mock\n`_ in Python 3.3\nonwards.\n\nThis package contains a rolling backport of the standard library mock code\ncompatible with Python 2.7 and 3.4 and up.\n\nPlease see the standard library documentation for more details.\n\n:Homepage: `Mock Homepage`_\n:Download: `Mock on PyPI`_\n:Documentation: `Python Docs`_\n:License: `BSD License`_\n:Support: `Mailing list (testing-in-python@lists.idyll.org)\n `_\n:Code: `GitHub\n `_\n:Issue tracker: `GitHub Issues\n `_\n:Build status:\n |CircleCI|_ |Docs|_\n\n .. |CircleCI| image:: https://circleci.com/gh/testing-cabal/mock/tree/master.svg?style=shield\n .. _CircleCI: https://circleci.com/gh/testing-cabal/mock/tree/master\n\n .. |Docs| image:: https://readthedocs.org/projects/mock/badge/?version=latest\n .. _Docs: http://mock.readthedocs.org/en/latest/\n\n.. _Mock Homepage: http://mock.readthedocs.org/en/latest/\n.. _BSD License: https://github.com/testing-cabal/mock/blob/master/LICENSE.txt\n.. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html\n.. _mock on PyPI: https://pypi.org/project/mock/\n\n\n", + "release_date": "2019-05-07T21:22:24", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Testing Cabal", + "email": "testing-in-python@lists.idyll.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://mock.readthedocs.org/en/latest/", + "download_url": "https://files.pythonhosted.org/packages/2e/ab/4fe657d78b270aa6a32f027849513b829b41b0f28d9d8d7f8c3d29ea559a/mock-3.0.5.tar.gz", + "size": 28126, + "sha1": null, + "md5": "d834a46d9a129be3e76fdcc99751e82c", + "sha256": "83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "OSI Approved :: BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/mock/3.0.5/json", + "datasource_id": null, + "purl": "pkg:pypi/mock@3.0.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "msgpack", + "version": "1.0.4", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "# MessagePack for Python\n\n[![Build Status](https://travis-ci.org/msgpack/msgpack-python.svg?branch=master)](https://travis-ci.org/msgpack/msgpack-python)\n[![Documentation Status](https://readthedocs.org/projects/msgpack-python/badge/?version=latest)](https://msgpack-python.readthedocs.io/en/latest/?badge=latest)\n\n## What's this\n\n[MessagePack](https://msgpack.org/) is an efficient binary serialization format.\nIt lets you exchange data among multiple languages like JSON.\nBut it's faster and smaller.\nThis package provides CPython bindings for reading and writing MessagePack data.\n\n\n## Very important notes for existing users\n\n### PyPI package name\n\nPackage name on PyPI was changed from `msgpack-python` to `msgpack` from 0.5.\n\nWhen upgrading from msgpack-0.4 or earlier, do `pip uninstall msgpack-python` before\n`pip install -U msgpack`.\n\n\n### Compatibility with the old format\n\nYou can use `use_bin_type=False` option to pack `bytes`\nobject into raw type in the old msgpack spec, instead of bin type in new msgpack spec.\n\nYou can unpack old msgpack format using `raw=True` option.\nIt unpacks str (raw) type in msgpack into Python bytes.\n\nSee note below for detail.\n\n\n### Major breaking changes in msgpack 1.0\n\n* Python 2\n\n * The extension module does not support Python 2 anymore.\n The pure Python implementation (`msgpack.fallback`) is used for Python 2.\n\n* Packer\n\n * `use_bin_type=True` by default. bytes are encoded in bin type in msgpack.\n **If you are still using Python 2, you must use unicode for all string types.**\n You can use `use_bin_type=False` to encode into old msgpack format.\n * `encoding` option is removed. UTF-8 is used always.\n\n* Unpacker\n\n * `raw=False` by default. It assumes str types are valid UTF-8 string\n and decode them to Python str (unicode) object.\n * `encoding` option is removed. You can use `raw=True` to support old format.\n * Default value of `max_buffer_size` is changed from 0 to 100 MiB.\n * Default value of `strict_map_key` is changed to True to avoid hashdos.\n You need to pass `strict_map_key=False` if you have data which contain map keys\n which type is not bytes or str.\n\n\n## Install\n\n```\n$ pip install msgpack\n```\n\n### Pure Python implementation\n\nThe extension module in msgpack (`msgpack._cmsgpack`) does not support\nPython 2 and PyPy.\n\nBut msgpack provides a pure Python implementation (`msgpack.fallback`)\nfor PyPy and Python 2.\n\n\n\n### Windows\n\nWhen you can't use a binary distribution, you need to install Visual Studio\nor Windows SDK on Windows.\nWithout extension, using pure Python implementation on CPython runs slowly.\n\n\n## How to use\n\nNOTE: In examples below, I use `raw=False` and `use_bin_type=True` for users\nusing msgpack < 1.0. These options are default from msgpack 1.0 so you can omit them.\n\n\n### One-shot pack & unpack\n\nUse `packb` for packing and `unpackb` for unpacking.\nmsgpack provides `dumps` and `loads` as an alias for compatibility with\n`json` and `pickle`.\n\n`pack` and `dump` packs to a file-like object.\n`unpack` and `load` unpacks from a file-like object.\n\n```pycon\n>>> import msgpack\n>>> msgpack.packb([1, 2, 3], use_bin_type=True)\n'\\x93\\x01\\x02\\x03'\n>>> msgpack.unpackb(_, raw=False)\n[1, 2, 3]\n```\n\n`unpack` unpacks msgpack's array to Python's list, but can also unpack to tuple:\n\n```pycon\n>>> msgpack.unpackb(b'\\x93\\x01\\x02\\x03', use_list=False, raw=False)\n(1, 2, 3)\n```\n\nYou should always specify the `use_list` keyword argument for backward compatibility.\nSee performance issues relating to `use_list option`_ below.\n\nRead the docstring for other options.\n\n\n### Streaming unpacking\n\n`Unpacker` is a \"streaming unpacker\". It unpacks multiple objects from one\nstream (or from bytes provided through its `feed` method).\n\n```py\nimport msgpack\nfrom io import BytesIO\n\nbuf = BytesIO()\nfor i in range(100):\n buf.write(msgpack.packb(i, use_bin_type=True))\n\nbuf.seek(0)\n\nunpacker = msgpack.Unpacker(buf, raw=False)\nfor unpacked in unpacker:\n print(unpacked)\n```\n\n\n### Packing/unpacking of custom data type\n\nIt is also possible to pack/unpack custom data types. Here is an example for\n`datetime.datetime`.\n\n```py\nimport datetime\nimport msgpack\n\nuseful_dict = {\n \"id\": 1,\n \"created\": datetime.datetime.now(),\n}\n\ndef decode_datetime(obj):\n if '__datetime__' in obj:\n obj = datetime.datetime.strptime(obj[\"as_str\"], \"%Y%m%dT%H:%M:%S.%f\")\n return obj\n\ndef encode_datetime(obj):\n if isinstance(obj, datetime.datetime):\n return {'__datetime__': True, 'as_str': obj.strftime(\"%Y%m%dT%H:%M:%S.%f\")}\n return obj\n\n\npacked_dict = msgpack.packb(useful_dict, default=encode_datetime, use_bin_type=True)\nthis_dict_again = msgpack.unpackb(packed_dict, object_hook=decode_datetime, raw=False)\n```\n\n`Unpacker`'s `object_hook` callback receives a dict; the\n`object_pairs_hook` callback may instead be used to receive a list of\nkey-value pairs.\n\n\n### Extended types\n\nIt is also possible to pack/unpack custom data types using the **ext** type.\n\n```pycon\n>>> import msgpack\n>>> import array\n>>> def default(obj):\n... if isinstance(obj, array.array) and obj.typecode == 'd':\n... return msgpack.ExtType(42, obj.tostring())\n... raise TypeError(\"Unknown type: %r\" % (obj,))\n...\n>>> def ext_hook(code, data):\n... if code == 42:\n... a = array.array('d')\n... a.fromstring(data)\n... return a\n... return ExtType(code, data)\n...\n>>> data = array.array('d', [1.2, 3.4])\n>>> packed = msgpack.packb(data, default=default, use_bin_type=True)\n>>> unpacked = msgpack.unpackb(packed, ext_hook=ext_hook, raw=False)\n>>> data == unpacked\nTrue\n```\n\n\n### Advanced unpacking control\n\nAs an alternative to iteration, `Unpacker` objects provide `unpack`,\n`skip`, `read_array_header` and `read_map_header` methods. The former two\nread an entire message from the stream, respectively de-serialising and returning\nthe result, or ignoring it. The latter two methods return the number of elements\nin the upcoming container, so that each element in an array, or key-value pair\nin a map, can be unpacked or skipped individually.\n\n\n## Notes\n\n### string and binary type\n\nEarly versions of msgpack didn't distinguish string and binary types.\nThe type for representing both string and binary types was named **raw**.\n\nYou can pack into and unpack from this old spec using `use_bin_type=False`\nand `raw=True` options.\n\n```pycon\n>>> import msgpack\n>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs'], use_bin_type=False), raw=True)\n[b'spam', b'eggs']\n>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs'], use_bin_type=True), raw=False)\n[b'spam', 'eggs']\n```\n\n### ext type\n\nTo use the **ext** type, pass `msgpack.ExtType` object to packer.\n\n```pycon\n>>> import msgpack\n>>> packed = msgpack.packb(msgpack.ExtType(42, b'xyzzy'))\n>>> msgpack.unpackb(packed)\nExtType(code=42, data='xyzzy')\n```\n\nYou can use it with `default` and `ext_hook`. See below.\n\n\n### Security\n\nTo unpacking data received from unreliable source, msgpack provides\ntwo security options.\n\n`max_buffer_size` (default: `100*1024*1024`) limits the internal buffer size.\nIt is used to limit the preallocated list size too.\n\n`strict_map_key` (default: `True`) limits the type of map keys to bytes and str.\nWhile msgpack spec doesn't limit the types of the map keys,\nthere is a risk of the hashdos.\nIf you need to support other types for map keys, use `strict_map_key=False`.\n\n\n### Performance tips\n\nCPython's GC starts when growing allocated object.\nThis means unpacking may cause useless GC.\nYou can use `gc.disable()` when unpacking large message.\n\nList is the default sequence type of Python.\nBut tuple is lighter than list.\nYou can use `use_list=False` while unpacking when performance is important.\n", + "release_date": "2022-06-03T07:30:12", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Inada Naoki", + "email": "songofacandy@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://msgpack.org/", + "download_url": "https://files.pythonhosted.org/packages/22/44/0829b19ac243211d1d2bd759999aa92196c546518b0be91de9cacc98122a/msgpack-1.0.4.tar.gz", + "size": 128053, + "sha1": null, + "md5": "1822cdb939e7531f7ad0f7f09b434f22", + "sha256": "f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f", + "sha512": null, + "bug_tracking_url": "https://github.com/msgpack/msgpack-python/issues", + "code_view_url": "https://github.com/msgpack/msgpack-python", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "Apache 2.0", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/msgpack/1.0.4/json", + "datasource_id": null, + "purl": "pkg:pypi/msgpack@1.0.4" + }, + { + "type": "pypi", + "namespace": null, + "name": "pathlib2", + "version": "2.3.7.post1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "The `old pathlib `_\r\nmodule on bitbucket is no longer maintained.\r\nThe goal of pathlib2 is to provide a backport of\r\n`standard pathlib `_\r\nmodule which tracks the standard library module,\r\nso all the newest features of the standard pathlib can be\r\nused also on older Python versions.\r\n\r\nDownload\r\n--------\r\n\r\nStandalone releases are available on PyPI:\r\nhttp://pypi.python.org/pypi/pathlib2/\r\n\r\nDevelopment\r\n-----------\r\n\r\nThe main development takes place in the Python standard library: see\r\nthe `Python developer's guide `_.\r\nIn particular, new features should be submitted to the\r\n`Python bug tracker `_.\r\n\r\nIssues that occur in this backport, but that do not occur not in the\r\nstandard Python pathlib module can be submitted on\r\nthe `pathlib2 bug tracker `_.\r\n\r\nDocumentation\r\n-------------\r\n\r\nRefer to the\r\n`standard pathlib `_\r\ndocumentation.\r\n\r\nKnown Issues\r\n------------\r\n\r\nFor historic reasons, pathlib2 still uses bytes to represent file paths internally.\r\nUnfortunately, on Windows with Python 2.7, the file system encoder (``mcbs``)\r\nhas only poor support for non-ascii characters,\r\nand can silently replace non-ascii characters without warning.\r\nFor example, ``u'\u0442\u0435\u0441\u0442'.encode(sys.getfilesystemencoding())`` results in ``????``\r\nwhich is obviously completely useless.\r\n\r\nTherefore, on Windows with Python 2.7, until this problem is fixed upstream,\r\nunfortunately you cannot rely on pathlib2 to support the full unicode range for filenames.\r\nSee `issue #56 `_ for more details.\r\n\r\n.. |github| image:: https://github.com/jazzband/pathlib2/actions/workflows/python-package.yml/badge.svg\r\n :target: https://github.com/jazzband/pathlib2/actions/workflows/python-package.yml\r\n :alt: github\r\n\r\n.. |codecov| image:: https://codecov.io/gh/jazzband/pathlib2/branch/develop/graph/badge.svg\r\n :target: https://codecov.io/gh/jazzband/pathlib2\r\n :alt: codecov\r\n\r\n.. |jazzband| image:: https://jazzband.co/static/img/badge.svg\r\n :alt: Jazzband\r\n :target: https://jazzband.co/\r\n\r\n\r\n", + "release_date": "2022-02-10T18:01:07", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Matthias C. M. Troffaes", + "email": "matthias.troffaes@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jazzband/pathlib2", + "download_url": "https://files.pythonhosted.org/packages/09/eb/4af4bcd5b8731366b676192675221c5324394a580dfae469d498313b5c4a/pathlib2-2.3.7.post1-py2.py3-none-any.whl", + "size": 18027, + "sha1": null, + "md5": "d38180feae103da9b2cf41e5d0869ee9", + "sha256": "5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pathlib2/2.3.7.post1/json", + "datasource_id": null, + "purl": "pkg:pypi/pathlib2@2.3.7.post1" + }, + { + "type": "pypi", + "namespace": null, + "name": "pathlib2", + "version": "2.3.7.post1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "The `old pathlib `_\r\nmodule on bitbucket is no longer maintained.\r\nThe goal of pathlib2 is to provide a backport of\r\n`standard pathlib `_\r\nmodule which tracks the standard library module,\r\nso all the newest features of the standard pathlib can be\r\nused also on older Python versions.\r\n\r\nDownload\r\n--------\r\n\r\nStandalone releases are available on PyPI:\r\nhttp://pypi.python.org/pypi/pathlib2/\r\n\r\nDevelopment\r\n-----------\r\n\r\nThe main development takes place in the Python standard library: see\r\nthe `Python developer's guide `_.\r\nIn particular, new features should be submitted to the\r\n`Python bug tracker `_.\r\n\r\nIssues that occur in this backport, but that do not occur not in the\r\nstandard Python pathlib module can be submitted on\r\nthe `pathlib2 bug tracker `_.\r\n\r\nDocumentation\r\n-------------\r\n\r\nRefer to the\r\n`standard pathlib `_\r\ndocumentation.\r\n\r\nKnown Issues\r\n------------\r\n\r\nFor historic reasons, pathlib2 still uses bytes to represent file paths internally.\r\nUnfortunately, on Windows with Python 2.7, the file system encoder (``mcbs``)\r\nhas only poor support for non-ascii characters,\r\nand can silently replace non-ascii characters without warning.\r\nFor example, ``u'\u0442\u0435\u0441\u0442'.encode(sys.getfilesystemencoding())`` results in ``????``\r\nwhich is obviously completely useless.\r\n\r\nTherefore, on Windows with Python 2.7, until this problem is fixed upstream,\r\nunfortunately you cannot rely on pathlib2 to support the full unicode range for filenames.\r\nSee `issue #56 `_ for more details.\r\n\r\n.. |github| image:: https://github.com/jazzband/pathlib2/actions/workflows/python-package.yml/badge.svg\r\n :target: https://github.com/jazzband/pathlib2/actions/workflows/python-package.yml\r\n :alt: github\r\n\r\n.. |codecov| image:: https://codecov.io/gh/jazzband/pathlib2/branch/develop/graph/badge.svg\r\n :target: https://codecov.io/gh/jazzband/pathlib2\r\n :alt: codecov\r\n\r\n.. |jazzband| image:: https://jazzband.co/static/img/badge.svg\r\n :alt: Jazzband\r\n :target: https://jazzband.co/\r\n\r\n\r\n", + "release_date": "2022-02-10T18:01:09", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Matthias C. M. Troffaes", + "email": "matthias.troffaes@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jazzband/pathlib2", + "download_url": "https://files.pythonhosted.org/packages/31/51/99caf463dc7c18eb18dad1fffe465a3cf3ee50ac3d1dccbd1781336fe9c7/pathlib2-2.3.7.post1.tar.gz", + "size": 211190, + "sha1": null, + "md5": "a8a4d8f897e709006a4586cf2102edc6", + "sha256": "9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pathlib2/2.3.7.post1/json", + "datasource_id": null, + "purl": "pkg:pypi/pathlib2@2.3.7.post1" + }, + { + "type": "pypi", + "namespace": null, + "name": "pexpect", + "version": "4.8.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nPexpect is a pure Python module for spawning child applications; controlling\nthem; and responding to expected patterns in their output. Pexpect works like\nDon Libes' Expect. Pexpect allows your script to spawn a child application and\ncontrol it as if a human were typing commands.\n\nPexpect can be used for automating interactive applications such as ssh, ftp,\npasswd, telnet, etc. It can be used to a automate setup scripts for duplicating\nsoftware package installations on different servers. It can be used for\nautomated software testing. Pexpect is in the spirit of Don Libes' Expect, but\nPexpect is pure Python.\n\nThe main features of Pexpect require the pty module in the Python standard\nlibrary, which is only available on Unix-like systems. Some features\u2014waiting\nfor patterns from file descriptors or subprocesses\u2014are also available on\nWindows.\n\n\n", + "release_date": "2020-01-21T16:37:03", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Noah Spurrier; Thomas Kluyver; Jeff Quast", + "email": "noah@noah.org, thomas@kluyver.me.uk, contact@jeffquast.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://pexpect.readthedocs.io/", + "download_url": "https://files.pythonhosted.org/packages/39/7b/88dbb785881c28a102619d46423cb853b46dbccc70d3ac362d99773a78ce/pexpect-4.8.0-py2.py3-none-any.whl", + "size": 59024, + "sha1": null, + "md5": "7bf9120209a613a6792c6f0619bd8428", + "sha256": "0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "ISC license", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pexpect/4.8.0/json", + "datasource_id": null, + "purl": "pkg:pypi/pexpect@4.8.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "pexpect", + "version": "4.8.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nPexpect is a pure Python module for spawning child applications; controlling\nthem; and responding to expected patterns in their output. Pexpect works like\nDon Libes' Expect. Pexpect allows your script to spawn a child application and\ncontrol it as if a human were typing commands.\n\nPexpect can be used for automating interactive applications such as ssh, ftp,\npasswd, telnet, etc. It can be used to a automate setup scripts for duplicating\nsoftware package installations on different servers. It can be used for\nautomated software testing. Pexpect is in the spirit of Don Libes' Expect, but\nPexpect is pure Python.\n\nThe main features of Pexpect require the pty module in the Python standard\nlibrary, which is only available on Unix-like systems. Some features\u2014waiting\nfor patterns from file descriptors or subprocesses\u2014are also available on\nWindows.\n\n\n", + "release_date": "2020-01-21T16:37:05", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Noah Spurrier; Thomas Kluyver; Jeff Quast", + "email": "noah@noah.org, thomas@kluyver.me.uk, contact@jeffquast.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://pexpect.readthedocs.io/", + "download_url": "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz", + "size": 157037, + "sha1": null, + "md5": "153eb25184249d6a85fde9acf4804085", + "sha256": "fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "ISC license", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pexpect/4.8.0/json", + "datasource_id": null, + "purl": "pkg:pypi/pexpect@4.8.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "pickleshare", + "version": "0.7.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "PickleShare - a small 'shelve' like datastore with concurrency support\n\nLike shelve, a PickleShareDB object acts like a normal dictionary. Unlike shelve,\nmany processes can access the database simultaneously. Changing a value in \ndatabase is immediately visible to other processes accessing the same database.\n\nConcurrency is possible because the values are stored in separate files. Hence\nthe \"database\" is a directory where *all* files are governed by PickleShare.\n\nExample usage::\n\n from pickleshare import *\n db = PickleShareDB('~/testpickleshare')\n db.clear()\n print(\"Should be empty:\",db.items())\n db['hello'] = 15\n db['aku ankka'] = [1,2,313]\n db['paths/are/ok/key'] = [1,(5,46)]\n print(db.keys())\n\nThis module is certainly not ZODB, but can be used for low-load\n(non-mission-critical) situations where tiny code size trumps the \nadvanced features of a \"real\" object database.\n\nInstallation guide: pip install pickleshare\n\n\n", + "release_date": "2018-09-25T19:17:35", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ville Vainio", + "email": "vivainio@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "database persistence pickle ipc shelve", + "homepage_url": "https://github.com/pickleshare/pickleshare", + "download_url": "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl", + "size": 6877, + "sha1": null, + "md5": "7c68934703b75ccfbaad00d1210f7011", + "sha256": "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pickleshare/0.7.5/json", + "datasource_id": null, + "purl": "pkg:pypi/pickleshare@0.7.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "pickleshare", + "version": "0.7.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "PickleShare - a small 'shelve' like datastore with concurrency support\n\nLike shelve, a PickleShareDB object acts like a normal dictionary. Unlike shelve,\nmany processes can access the database simultaneously. Changing a value in \ndatabase is immediately visible to other processes accessing the same database.\n\nConcurrency is possible because the values are stored in separate files. Hence\nthe \"database\" is a directory where *all* files are governed by PickleShare.\n\nExample usage::\n\n from pickleshare import *\n db = PickleShareDB('~/testpickleshare')\n db.clear()\n print(\"Should be empty:\",db.items())\n db['hello'] = 15\n db['aku ankka'] = [1,2,313]\n db['paths/are/ok/key'] = [1,(5,46)]\n print(db.keys())\n\nThis module is certainly not ZODB, but can be used for low-load\n(non-mission-critical) situations where tiny code size trumps the \nadvanced features of a \"real\" object database.\n\nInstallation guide: pip install pickleshare\n\n\n", + "release_date": "2018-09-25T19:17:37", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ville Vainio", + "email": "vivainio@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "database persistence pickle ipc shelve", + "homepage_url": "https://github.com/pickleshare/pickleshare", + "download_url": "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz", + "size": 6161, + "sha1": null, + "md5": "44ab782615894a812ab96669a122a634", + "sha256": "87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pickleshare/0.7.5/json", + "datasource_id": null, + "purl": "pkg:pypi/pickleshare@0.7.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "pluggy", + "version": "0.13.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "====================================================\npluggy - A minimalist production ready plugin system\n====================================================\n\n|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov|\n\nThis is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.\n\nPlease `read the docs`_ to learn more!\n\nA definitive example\n====================\n.. code-block:: python\n\n import pluggy\n\n hookspec = pluggy.HookspecMarker(\"myproject\")\n hookimpl = pluggy.HookimplMarker(\"myproject\")\n\n\n class MySpec(object):\n \"\"\"A hook specification namespace.\n \"\"\"\n\n @hookspec\n def myhook(self, arg1, arg2):\n \"\"\"My special little hook that you can customize.\n \"\"\"\n\n\n class Plugin_1(object):\n \"\"\"A hook implementation namespace.\n \"\"\"\n\n @hookimpl\n def myhook(self, arg1, arg2):\n print(\"inside Plugin_1.myhook()\")\n return arg1 + arg2\n\n\n class Plugin_2(object):\n \"\"\"A 2nd hook implementation namespace.\n \"\"\"\n\n @hookimpl\n def myhook(self, arg1, arg2):\n print(\"inside Plugin_2.myhook()\")\n return arg1 - arg2\n\n\n # create a manager and add the spec\n pm = pluggy.PluginManager(\"myproject\")\n pm.add_hookspecs(MySpec)\n\n # register plugins\n pm.register(Plugin_1())\n pm.register(Plugin_2())\n\n # call our ``myhook`` hook\n results = pm.hook.myhook(arg1=1, arg2=2)\n print(results)\n\n\nRunning this directly gets us::\n\n $ python docs/examples/toy-example.py\n inside Plugin_2.myhook()\n inside Plugin_1.myhook()\n [-1, 3]\n\n\n.. badges\n\n.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg\n :target: https://pypi.org/pypi/pluggy\n\n.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg\n :target: https://pypi.org/pypi/pluggy\n\n.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg\n :target: https://travis-ci.org/pytest-dev/pluggy\n\n.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg\n :target: https://ci.appveyor.com/project/pytestbot/pluggy\n\n.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg\n :target: https://anaconda.org/conda-forge/pytest\n\n.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg\n :alt: Join the chat at https://gitter.im/pytest-dev/pluggy\n :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge\n\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n\n.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/pytest-dev/pluggy\n :alt: Code coverage Status\n\n.. links\n.. _pytest:\n http://pytest.org\n.. _tox:\n https://tox.readthedocs.org\n.. _devpi:\n http://doc.devpi.net\n.. _read the docs:\n https://pluggy.readthedocs.io/en/latest/\n\n\n=========\nChangelog\n=========\n\n.. towncrier release notes start\n\npluggy 0.13.1 (2019-11-21)\n==========================\n\nTrivial/Internal Changes\n------------------------\n\n- `#236 `_: Improved documentation, especially with regard to references.\n\n\npluggy 0.13.0 (2019-09-10)\n==========================\n\nTrivial/Internal Changes\n------------------------\n\n- `#222 `_: Replace ``importlib_metadata`` backport with ``importlib.metadata`` from the\n standard library on Python 3.8+.\n\n\npluggy 0.12.0 (2019-05-27)\n==========================\n\nFeatures\n--------\n\n- `#215 `_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time. This time with ``.egg`` support.\n\n\npluggy 0.11.0 (2019-05-07)\n==========================\n\nBug Fixes\n---------\n\n- `#205 `_: Revert changes made in 0.10.0 release breaking ``.egg`` installs.\n\n\npluggy 0.10.0 (2019-05-07)\n==========================\n\nFeatures\n--------\n\n- `#199 `_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time.\n\n\npluggy 0.9.0 (2019-02-21)\n=========================\n\nFeatures\n--------\n\n- `#189 `_: ``PluginManager.load_setuptools_entrypoints`` now accepts a ``name`` parameter that when given will\n load only entry points with that name.\n\n ``PluginManager.load_setuptools_entrypoints`` also now returns the number of plugins loaded by the\n call, as opposed to the number of all plugins loaded by all calls to this method.\n\n\n\nBug Fixes\n---------\n\n- `#187 `_: Fix internal ``varnames`` function for PyPy3.\n\n\npluggy 0.8.1 (2018-11-09)\n=========================\n\nTrivial/Internal Changes\n------------------------\n\n- `#166 `_: Add ``stacklevel=2`` to implprefix warning so that the reported location of warning is the caller of PluginManager.\n\n\npluggy 0.8.0 (2018-10-15)\n=========================\n\nFeatures\n--------\n\n- `#177 `_: Add ``get_hookimpls()`` method to hook callers.\n\n\n\nTrivial/Internal Changes\n------------------------\n\n- `#165 `_: Add changelog in long package description and documentation.\n\n\n- `#172 `_: Add a test exemplifying the opt-in nature of spec defined args.\n\n\n- `#57 `_: Encapsulate hook specifications in a type for easier introspection.\n\n\npluggy 0.7.1 (2018-07-28)\n=========================\n\nDeprecations and Removals\n-------------------------\n\n- `#116 `_: Deprecate the ``implprefix`` kwarg to ``PluginManager`` and instead\n expect users to start using explicit ``HookimplMarker`` everywhere.\n\n\n\nFeatures\n--------\n\n- `#122 `_: Add ``.plugin`` member to ``PluginValidationError`` to access failing plugin during post-mortem.\n\n\n- `#138 `_: Add per implementation warnings support for hookspecs allowing for both\n deprecation and future warnings of legacy and (future) experimental hooks\n respectively.\n\n\n\nBug Fixes\n---------\n\n- `#110 `_: Fix a bug where ``_HookCaller.call_historic()`` would call the ``proc``\n arg even when the default is ``None`` resulting in a ``TypeError``.\n\n- `#160 `_: Fix problem when handling ``VersionConflict`` errors when loading setuptools plugins.\n\n\n\nImproved Documentation\n----------------------\n\n- `#123 `_: Document how exceptions are handled and how the hook call loop\n terminates immediately on the first error which is then delivered\n to any surrounding wrappers.\n\n\n- `#136 `_: Docs rework including a much better introduction and comprehensive example\n set for new users. A big thanks goes out to @obestwalter for the great work!\n\n\n\nTrivial/Internal Changes\n------------------------\n\n- `#117 `_: Break up the main monolithic package modules into separate modules by concern\n\n\n- `#131 `_: Automate ``setuptools`` wheels building and PyPi upload using TravisCI.\n\n\n- `#153 `_: Reorganize tests more appropriately by modules relating to each\n internal component/feature. This is in an effort to avoid (future)\n duplication and better separation of concerns in the test set.\n\n\n- `#156 `_: Add ``HookImpl.__repr__()`` for better debugging.\n\n\n- `#66 `_: Start using ``towncrier`` and a custom ``tox`` environment to prepare releases!\n\n\npluggy 0.7.0 (Unreleased)\n=========================\n\n* `#160 `_: We discovered a deployment issue so this version was never released to PyPI, only the tag exists.\n\npluggy 0.6.0 (2017-11-24)\n=========================\n\n- Add CI testing for the features, release, and master\n branches of ``pytest`` (PR `#79`_).\n- Document public API for ``_Result`` objects passed to wrappers\n (PR `#85`_).\n- Document and test hook LIFO ordering (PR `#85`_).\n- Turn warnings into errors in test suite (PR `#89`_).\n- Deprecate ``_Result.result`` (PR `#88`_).\n- Convert ``_Multicall`` to a simple function distinguishing it from\n the legacy version (PR `#90`_).\n- Resolve E741 errors (PR `#96`_).\n- Test and bug fix for unmarked hook collection (PRs `#97`_ and\n `#102`_).\n- Drop support for EOL Python 2.6 and 3.3 (PR `#103`_).\n- Fix ``inspect`` based arg introspection on py3.6 (PR `#94`_).\n\n.. _#79: https://github.com/pytest-dev/pluggy/pull/79\n.. _#85: https://github.com/pytest-dev/pluggy/pull/85\n.. _#88: https://github.com/pytest-dev/pluggy/pull/88\n.. _#89: https://github.com/pytest-dev/pluggy/pull/89\n.. _#90: https://github.com/pytest-dev/pluggy/pull/90\n.. _#94: https://github.com/pytest-dev/pluggy/pull/94\n.. _#96: https://github.com/pytest-dev/pluggy/pull/96\n.. _#97: https://github.com/pytest-dev/pluggy/pull/97\n.. _#102: https://github.com/pytest-dev/pluggy/pull/102\n.. _#103: https://github.com/pytest-dev/pluggy/pull/103\n\n\npluggy 0.5.2 (2017-09-06)\n=========================\n\n- fix bug where ``firstresult`` wrappers were being sent an incorrectly configured\n ``_Result`` (a list was set instead of a single value). Add tests to check for\n this as well as ``_Result.force_result()`` behaviour. Thanks to `@tgoodlet`_\n for the PR `#72`_.\n\n- fix incorrect ``getattr`` of ``DeprecationWarning`` from the ``warnings``\n module. Thanks to `@nicoddemus`_ for the PR `#77`_.\n\n- hide ``pytest`` tracebacks in certain core routines. Thanks to\n `@nicoddemus`_ for the PR `#80`_.\n\n.. _#72: https://github.com/pytest-dev/pluggy/pull/72\n.. _#77: https://github.com/pytest-dev/pluggy/pull/77\n.. _#80: https://github.com/pytest-dev/pluggy/pull/80\n\n\npluggy 0.5.1 (2017-08-29)\n=========================\n\n- fix a bug and add tests for case where ``firstresult`` hooks return\n ``None`` results. Thanks to `@RonnyPfannschmidt`_ and `@tgoodlet`_\n for the issue (`#68`_) and PR (`#69`_) respectively.\n\n.. _#69: https://github.com/pytest-dev/pluggy/pull/69\n.. _#68: https://github.com/pytest-dev/pluggy/issues/68\n\n\npluggy 0.5.0 (2017-08-28)\n=========================\n\n- fix bug where callbacks for historic hooks would not be called for\n already registered plugins. Thanks `@vodik`_ for the PR\n and `@hpk42`_ for further fixes.\n\n- fix `#17`_ by considering only actual functions for hooks\n this removes the ability to register arbitrary callable objects\n which at first glance is a reasonable simplification,\n thanks `@RonnyPfannschmidt`_ for report and pr.\n\n- fix `#19`_: allow registering hookspecs from instances. The PR from\n `@tgoodlet`_ also modernized the varnames implementation.\n\n- resolve `#32`_: split up the test set into multiple modules.\n Thanks to `@RonnyPfannschmidt`_ for the PR and `@tgoodlet`_ for\n the initial request.\n\n- resolve `#14`_: add full sphinx docs. Thanks to `@tgoodlet`_ for\n PR `#39`_.\n\n- add hook call mismatch warnings. Thanks to `@tgoodlet`_ for the\n PR `#42`_.\n\n- resolve `#44`_: move to new-style classes. Thanks to `@MichalTHEDUDE`_\n for PR `#46`_.\n\n- add baseline benchmarking/speed tests using ``pytest-benchmark``\n in PR `#54`_. Thanks to `@tgoodlet`_.\n\n- update the README to showcase the API. Thanks to `@tgoodlet`_ for the\n issue and PR `#55`_.\n\n- deprecate ``__multicall__`` and add a faster call loop implementation.\n Thanks to `@tgoodlet`_ for PR `#58`_.\n\n- raise a comprehensible error when a ``hookimpl`` is called with positional\n args. Thanks to `@RonnyPfannschmidt`_ for the issue and `@tgoodlet`_ for\n PR `#60`_.\n\n- fix the ``firstresult`` test making it more complete\n and remove a duplicate of that test. Thanks to `@tgoodlet`_\n for PR `#62`_.\n\n.. _#62: https://github.com/pytest-dev/pluggy/pull/62\n.. _#60: https://github.com/pytest-dev/pluggy/pull/60\n.. _#58: https://github.com/pytest-dev/pluggy/pull/58\n.. _#55: https://github.com/pytest-dev/pluggy/pull/55\n.. _#54: https://github.com/pytest-dev/pluggy/pull/54\n.. _#46: https://github.com/pytest-dev/pluggy/pull/46\n.. _#44: https://github.com/pytest-dev/pluggy/issues/44\n.. _#42: https://github.com/pytest-dev/pluggy/pull/42\n.. _#39: https://github.com/pytest-dev/pluggy/pull/39\n.. _#32: https://github.com/pytest-dev/pluggy/pull/32\n.. _#19: https://github.com/pytest-dev/pluggy/issues/19\n.. _#17: https://github.com/pytest-dev/pluggy/issues/17\n.. _#14: https://github.com/pytest-dev/pluggy/issues/14\n\n\npluggy 0.4.0 (2016-09-25)\n=========================\n\n- add ``has_plugin(name)`` method to pluginmanager. thanks `@nicoddemus`_.\n\n- fix `#11`_: make plugin parsing more resilient against exceptions\n from ``__getattr__`` functions. Thanks `@nicoddemus`_.\n\n- fix issue `#4`_: specific ``HookCallError`` exception for when a hook call\n provides not enough arguments.\n\n- better error message when loading setuptools entrypoints fails\n due to a ``VersionConflict``. Thanks `@blueyed`_.\n\n.. _#11: https://github.com/pytest-dev/pluggy/issues/11\n.. _#4: https://github.com/pytest-dev/pluggy/issues/4\n\n\npluggy 0.3.1 (2015-09-17)\n=========================\n\n- avoid using deprecated-in-python3.5 getargspec method. Thanks\n `@mdboom`_.\n\n\npluggy 0.3.0 (2015-05-07)\n=========================\n\ninitial release\n\n.. contributors\n.. _@hpk42: https://github.com/hpk42\n.. _@tgoodlet: https://github.com/goodboy\n.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE\n.. _@vodik: https://github.com/vodik\n.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt\n.. _@blueyed: https://github.com/blueyed\n.. _@nicoddemus: https://github.com/nicoddemus\n.. _@mdboom: https://github.com/mdboom\n\n\n", + "release_date": "2019-11-21T20:42:34", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Holger Krekel", + "email": "holger@merlinux.eu", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pytest-dev/pluggy", + "download_url": "https://files.pythonhosted.org/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl", + "size": 18077, + "sha1": null, + "md5": "32a771dc4df48273d4967e5aab043653", + "sha256": "966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT license", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pluggy/0.13.1/json", + "datasource_id": null, + "purl": "pkg:pypi/pluggy@0.13.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "pluggy", + "version": "0.13.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "====================================================\npluggy - A minimalist production ready plugin system\n====================================================\n\n|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov|\n\nThis is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.\n\nPlease `read the docs`_ to learn more!\n\nA definitive example\n====================\n.. code-block:: python\n\n import pluggy\n\n hookspec = pluggy.HookspecMarker(\"myproject\")\n hookimpl = pluggy.HookimplMarker(\"myproject\")\n\n\n class MySpec(object):\n \"\"\"A hook specification namespace.\n \"\"\"\n\n @hookspec\n def myhook(self, arg1, arg2):\n \"\"\"My special little hook that you can customize.\n \"\"\"\n\n\n class Plugin_1(object):\n \"\"\"A hook implementation namespace.\n \"\"\"\n\n @hookimpl\n def myhook(self, arg1, arg2):\n print(\"inside Plugin_1.myhook()\")\n return arg1 + arg2\n\n\n class Plugin_2(object):\n \"\"\"A 2nd hook implementation namespace.\n \"\"\"\n\n @hookimpl\n def myhook(self, arg1, arg2):\n print(\"inside Plugin_2.myhook()\")\n return arg1 - arg2\n\n\n # create a manager and add the spec\n pm = pluggy.PluginManager(\"myproject\")\n pm.add_hookspecs(MySpec)\n\n # register plugins\n pm.register(Plugin_1())\n pm.register(Plugin_2())\n\n # call our ``myhook`` hook\n results = pm.hook.myhook(arg1=1, arg2=2)\n print(results)\n\n\nRunning this directly gets us::\n\n $ python docs/examples/toy-example.py\n inside Plugin_2.myhook()\n inside Plugin_1.myhook()\n [-1, 3]\n\n\n.. badges\n\n.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg\n :target: https://pypi.org/pypi/pluggy\n\n.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg\n :target: https://pypi.org/pypi/pluggy\n\n.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg\n :target: https://travis-ci.org/pytest-dev/pluggy\n\n.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg\n :target: https://ci.appveyor.com/project/pytestbot/pluggy\n\n.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg\n :target: https://anaconda.org/conda-forge/pytest\n\n.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg\n :alt: Join the chat at https://gitter.im/pytest-dev/pluggy\n :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge\n\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n\n.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/pytest-dev/pluggy\n :alt: Code coverage Status\n\n.. links\n.. _pytest:\n http://pytest.org\n.. _tox:\n https://tox.readthedocs.org\n.. _devpi:\n http://doc.devpi.net\n.. _read the docs:\n https://pluggy.readthedocs.io/en/latest/\n\n\n=========\nChangelog\n=========\n\n.. towncrier release notes start\n\npluggy 0.13.1 (2019-11-21)\n==========================\n\nTrivial/Internal Changes\n------------------------\n\n- `#236 `_: Improved documentation, especially with regard to references.\n\n\npluggy 0.13.0 (2019-09-10)\n==========================\n\nTrivial/Internal Changes\n------------------------\n\n- `#222 `_: Replace ``importlib_metadata`` backport with ``importlib.metadata`` from the\n standard library on Python 3.8+.\n\n\npluggy 0.12.0 (2019-05-27)\n==========================\n\nFeatures\n--------\n\n- `#215 `_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time. This time with ``.egg`` support.\n\n\npluggy 0.11.0 (2019-05-07)\n==========================\n\nBug Fixes\n---------\n\n- `#205 `_: Revert changes made in 0.10.0 release breaking ``.egg`` installs.\n\n\npluggy 0.10.0 (2019-05-07)\n==========================\n\nFeatures\n--------\n\n- `#199 `_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time.\n\n\npluggy 0.9.0 (2019-02-21)\n=========================\n\nFeatures\n--------\n\n- `#189 `_: ``PluginManager.load_setuptools_entrypoints`` now accepts a ``name`` parameter that when given will\n load only entry points with that name.\n\n ``PluginManager.load_setuptools_entrypoints`` also now returns the number of plugins loaded by the\n call, as opposed to the number of all plugins loaded by all calls to this method.\n\n\n\nBug Fixes\n---------\n\n- `#187 `_: Fix internal ``varnames`` function for PyPy3.\n\n\npluggy 0.8.1 (2018-11-09)\n=========================\n\nTrivial/Internal Changes\n------------------------\n\n- `#166 `_: Add ``stacklevel=2`` to implprefix warning so that the reported location of warning is the caller of PluginManager.\n\n\npluggy 0.8.0 (2018-10-15)\n=========================\n\nFeatures\n--------\n\n- `#177 `_: Add ``get_hookimpls()`` method to hook callers.\n\n\n\nTrivial/Internal Changes\n------------------------\n\n- `#165 `_: Add changelog in long package description and documentation.\n\n\n- `#172 `_: Add a test exemplifying the opt-in nature of spec defined args.\n\n\n- `#57 `_: Encapsulate hook specifications in a type for easier introspection.\n\n\npluggy 0.7.1 (2018-07-28)\n=========================\n\nDeprecations and Removals\n-------------------------\n\n- `#116 `_: Deprecate the ``implprefix`` kwarg to ``PluginManager`` and instead\n expect users to start using explicit ``HookimplMarker`` everywhere.\n\n\n\nFeatures\n--------\n\n- `#122 `_: Add ``.plugin`` member to ``PluginValidationError`` to access failing plugin during post-mortem.\n\n\n- `#138 `_: Add per implementation warnings support for hookspecs allowing for both\n deprecation and future warnings of legacy and (future) experimental hooks\n respectively.\n\n\n\nBug Fixes\n---------\n\n- `#110 `_: Fix a bug where ``_HookCaller.call_historic()`` would call the ``proc``\n arg even when the default is ``None`` resulting in a ``TypeError``.\n\n- `#160 `_: Fix problem when handling ``VersionConflict`` errors when loading setuptools plugins.\n\n\n\nImproved Documentation\n----------------------\n\n- `#123 `_: Document how exceptions are handled and how the hook call loop\n terminates immediately on the first error which is then delivered\n to any surrounding wrappers.\n\n\n- `#136 `_: Docs rework including a much better introduction and comprehensive example\n set for new users. A big thanks goes out to @obestwalter for the great work!\n\n\n\nTrivial/Internal Changes\n------------------------\n\n- `#117 `_: Break up the main monolithic package modules into separate modules by concern\n\n\n- `#131 `_: Automate ``setuptools`` wheels building and PyPi upload using TravisCI.\n\n\n- `#153 `_: Reorganize tests more appropriately by modules relating to each\n internal component/feature. This is in an effort to avoid (future)\n duplication and better separation of concerns in the test set.\n\n\n- `#156 `_: Add ``HookImpl.__repr__()`` for better debugging.\n\n\n- `#66 `_: Start using ``towncrier`` and a custom ``tox`` environment to prepare releases!\n\n\npluggy 0.7.0 (Unreleased)\n=========================\n\n* `#160 `_: We discovered a deployment issue so this version was never released to PyPI, only the tag exists.\n\npluggy 0.6.0 (2017-11-24)\n=========================\n\n- Add CI testing for the features, release, and master\n branches of ``pytest`` (PR `#79`_).\n- Document public API for ``_Result`` objects passed to wrappers\n (PR `#85`_).\n- Document and test hook LIFO ordering (PR `#85`_).\n- Turn warnings into errors in test suite (PR `#89`_).\n- Deprecate ``_Result.result`` (PR `#88`_).\n- Convert ``_Multicall`` to a simple function distinguishing it from\n the legacy version (PR `#90`_).\n- Resolve E741 errors (PR `#96`_).\n- Test and bug fix for unmarked hook collection (PRs `#97`_ and\n `#102`_).\n- Drop support for EOL Python 2.6 and 3.3 (PR `#103`_).\n- Fix ``inspect`` based arg introspection on py3.6 (PR `#94`_).\n\n.. _#79: https://github.com/pytest-dev/pluggy/pull/79\n.. _#85: https://github.com/pytest-dev/pluggy/pull/85\n.. _#88: https://github.com/pytest-dev/pluggy/pull/88\n.. _#89: https://github.com/pytest-dev/pluggy/pull/89\n.. _#90: https://github.com/pytest-dev/pluggy/pull/90\n.. _#94: https://github.com/pytest-dev/pluggy/pull/94\n.. _#96: https://github.com/pytest-dev/pluggy/pull/96\n.. _#97: https://github.com/pytest-dev/pluggy/pull/97\n.. _#102: https://github.com/pytest-dev/pluggy/pull/102\n.. _#103: https://github.com/pytest-dev/pluggy/pull/103\n\n\npluggy 0.5.2 (2017-09-06)\n=========================\n\n- fix bug where ``firstresult`` wrappers were being sent an incorrectly configured\n ``_Result`` (a list was set instead of a single value). Add tests to check for\n this as well as ``_Result.force_result()`` behaviour. Thanks to `@tgoodlet`_\n for the PR `#72`_.\n\n- fix incorrect ``getattr`` of ``DeprecationWarning`` from the ``warnings``\n module. Thanks to `@nicoddemus`_ for the PR `#77`_.\n\n- hide ``pytest`` tracebacks in certain core routines. Thanks to\n `@nicoddemus`_ for the PR `#80`_.\n\n.. _#72: https://github.com/pytest-dev/pluggy/pull/72\n.. _#77: https://github.com/pytest-dev/pluggy/pull/77\n.. _#80: https://github.com/pytest-dev/pluggy/pull/80\n\n\npluggy 0.5.1 (2017-08-29)\n=========================\n\n- fix a bug and add tests for case where ``firstresult`` hooks return\n ``None`` results. Thanks to `@RonnyPfannschmidt`_ and `@tgoodlet`_\n for the issue (`#68`_) and PR (`#69`_) respectively.\n\n.. _#69: https://github.com/pytest-dev/pluggy/pull/69\n.. _#68: https://github.com/pytest-dev/pluggy/issues/68\n\n\npluggy 0.5.0 (2017-08-28)\n=========================\n\n- fix bug where callbacks for historic hooks would not be called for\n already registered plugins. Thanks `@vodik`_ for the PR\n and `@hpk42`_ for further fixes.\n\n- fix `#17`_ by considering only actual functions for hooks\n this removes the ability to register arbitrary callable objects\n which at first glance is a reasonable simplification,\n thanks `@RonnyPfannschmidt`_ for report and pr.\n\n- fix `#19`_: allow registering hookspecs from instances. The PR from\n `@tgoodlet`_ also modernized the varnames implementation.\n\n- resolve `#32`_: split up the test set into multiple modules.\n Thanks to `@RonnyPfannschmidt`_ for the PR and `@tgoodlet`_ for\n the initial request.\n\n- resolve `#14`_: add full sphinx docs. Thanks to `@tgoodlet`_ for\n PR `#39`_.\n\n- add hook call mismatch warnings. Thanks to `@tgoodlet`_ for the\n PR `#42`_.\n\n- resolve `#44`_: move to new-style classes. Thanks to `@MichalTHEDUDE`_\n for PR `#46`_.\n\n- add baseline benchmarking/speed tests using ``pytest-benchmark``\n in PR `#54`_. Thanks to `@tgoodlet`_.\n\n- update the README to showcase the API. Thanks to `@tgoodlet`_ for the\n issue and PR `#55`_.\n\n- deprecate ``__multicall__`` and add a faster call loop implementation.\n Thanks to `@tgoodlet`_ for PR `#58`_.\n\n- raise a comprehensible error when a ``hookimpl`` is called with positional\n args. Thanks to `@RonnyPfannschmidt`_ for the issue and `@tgoodlet`_ for\n PR `#60`_.\n\n- fix the ``firstresult`` test making it more complete\n and remove a duplicate of that test. Thanks to `@tgoodlet`_\n for PR `#62`_.\n\n.. _#62: https://github.com/pytest-dev/pluggy/pull/62\n.. _#60: https://github.com/pytest-dev/pluggy/pull/60\n.. _#58: https://github.com/pytest-dev/pluggy/pull/58\n.. _#55: https://github.com/pytest-dev/pluggy/pull/55\n.. _#54: https://github.com/pytest-dev/pluggy/pull/54\n.. _#46: https://github.com/pytest-dev/pluggy/pull/46\n.. _#44: https://github.com/pytest-dev/pluggy/issues/44\n.. _#42: https://github.com/pytest-dev/pluggy/pull/42\n.. _#39: https://github.com/pytest-dev/pluggy/pull/39\n.. _#32: https://github.com/pytest-dev/pluggy/pull/32\n.. _#19: https://github.com/pytest-dev/pluggy/issues/19\n.. _#17: https://github.com/pytest-dev/pluggy/issues/17\n.. _#14: https://github.com/pytest-dev/pluggy/issues/14\n\n\npluggy 0.4.0 (2016-09-25)\n=========================\n\n- add ``has_plugin(name)`` method to pluginmanager. thanks `@nicoddemus`_.\n\n- fix `#11`_: make plugin parsing more resilient against exceptions\n from ``__getattr__`` functions. Thanks `@nicoddemus`_.\n\n- fix issue `#4`_: specific ``HookCallError`` exception for when a hook call\n provides not enough arguments.\n\n- better error message when loading setuptools entrypoints fails\n due to a ``VersionConflict``. Thanks `@blueyed`_.\n\n.. _#11: https://github.com/pytest-dev/pluggy/issues/11\n.. _#4: https://github.com/pytest-dev/pluggy/issues/4\n\n\npluggy 0.3.1 (2015-09-17)\n=========================\n\n- avoid using deprecated-in-python3.5 getargspec method. Thanks\n `@mdboom`_.\n\n\npluggy 0.3.0 (2015-05-07)\n=========================\n\ninitial release\n\n.. contributors\n.. _@hpk42: https://github.com/hpk42\n.. _@tgoodlet: https://github.com/goodboy\n.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE\n.. _@vodik: https://github.com/vodik\n.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt\n.. _@blueyed: https://github.com/blueyed\n.. _@nicoddemus: https://github.com/nicoddemus\n.. _@mdboom: https://github.com/mdboom\n\n\n", + "release_date": "2019-11-21T20:42:37", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Holger Krekel", + "email": "holger@merlinux.eu", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pytest-dev/pluggy", + "download_url": "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz", + "size": 57962, + "sha1": null, + "md5": "7f610e28b8b34487336b585a3dfb803d", + "sha256": "15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT license", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pluggy/0.13.1/json", + "datasource_id": null, + "purl": "pkg:pypi/pluggy@0.13.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "prompt-toolkit", + "version": "1.0.18", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Python Prompt Toolkit\n=====================\n\n|Build Status| |PyPI|\n\n``prompt_toolkit`` is a library for building powerful interactive command lines\nand terminal applications in Python.\n\nRead the `documentation on readthedocs\n`_.\n\n\nPtpython\n********\n\n`ptpython `_ is an interactive\nPython Shell, build on top of prompt_toolkit.\n\n.. image :: https://github.com/jonathanslenders/python-prompt-toolkit/raw/master/docs/images/ptpython.png\n\n\nprompt_toolkit features\n***********************\n\n``prompt_toolkit`` could be a replacement for `GNU readline\n`_, but it can be much\nmore than that.\n\nSome features:\n\n- Pure Python.\n- Syntax highlighting of the input while typing. (For instance, with a Pygments lexer.)\n- Multi-line input editing.\n- Advanced code completion.\n- Both Emacs and Vi key bindings. (Similar to readline.)\n- Even some advanced Vi functionality, like named registers and digraphs.\n- Reverse and forward incremental search.\n- Runs on all Python versions from 2.6 up to 3.5.\n- Works well with Unicode double width characters. (Chinese input.)\n- Selecting text for copy/paste. (Both Emacs and Vi style.)\n- Support for `bracketed paste `_.\n- Mouse support for cursor positioning and scrolling.\n- Auto suggestions. (Like `fish shell `_.)\n- Multiple input buffers.\n- No global state.\n- Lightweight, the only dependencies are Pygments, six and wcwidth.\n- Runs on Linux, OS X, FreeBSD, OpenBSD and Windows systems.\n- And much more...\n\nFeel free to create tickets for bugs and feature requests, and create pull\nrequests if you have nice patches that you would like to share with others.\n\n\nAbout Windows support\n*********************\n\n``prompt_toolkit`` is cross platform, and everything that you build on top\nshould run fine on both Unix and Windows systems. On Windows, it uses a\ndifferent event loop (``WaitForMultipleObjects`` instead of ``select``), and\nanother input and output system. (Win32 APIs instead of pseudo-terminals and\nVT100.)\n\nIt's worth noting that the implementation is a \"best effort of what is\npossible\". Both Unix and Windows terminals have their limitations. But in\ngeneral, the Unix experience will still be a little better.\n\nFor Windows, it's recommended to use either `cmder\n`_ or `conemu `_.\n\n\nInstallation\n************\n\n::\n\n pip install prompt_toolkit\n\nFor Conda, do:\n\n::\n\n conda install -c https://conda.anaconda.org/conda-forge prompt_toolkit\n\n\nGetting started\n***************\n\nThe most simple example of the library would look like this:\n\n.. code:: python\n\n from prompt_toolkit import prompt\n\n if __name__ == '__main__':\n answer = prompt('Give me some input: ')\n print('You said: %s' % answer)\n\nFor more complex examples, have a look in the ``examples`` directory. All\nexamples are chosen to demonstrate only one thing. Also, don't be afraid to\nlook at the source code. The implementation of the ``prompt`` function could be\na good start.\n\nNote for Python 2: all strings are expected to be unicode strings. So, either\nput a small ``u`` in front of every string or put ``from __future__ import\nunicode_literals`` at the start of the above example.\n\n\nProjects using prompt_toolkit\n*****************************\n\nShells:\n\n- `ptpython `_: Python REPL\n- `ptpdb `_: Python debugger (pdb replacement)\n- `pgcli `_: Postgres client.\n- `mycli `_: MySql client.\n- `wharfee `_: A Docker command line.\n- `xonsh `_: A Python-ish, BASHwards-compatible shell.\n- `saws `_: A Supercharged AWS Command Line Interface.\n- `cycli `_: A Command Line Interface for Cypher.\n- `crash `_: Crate command line client.\n- `vcli `_: Vertica client.\n- `aws-shell `_: An integrated shell for working with the AWS CLI.\n- `softlayer-python `_: A command-line interface to manage various SoftLayer products and services.\n- `ipython `_: The IPython REPL\n- `click-repl `_: Subcommand REPL for click apps.\n- `haxor-news `_: A Hacker News CLI.\n- `gitsome `_: A Git/Shell Autocompleter with GitHub Integration.\n- `http-prompt `_: An interactive command-line HTTP client.\n- `coconut `_: Functional programming in Python.\n- `Ergonomica `_: A Bash alternative written in Python.\n- `Kube-shell `_: Kubernetes shell: An integrated shell for working with the Kubernetes CLI\n\nFull screen applications:\n\n- `pymux `_: A terminal multiplexer (like tmux) in pure Python.\n- `pyvim `_: A Vim clone in pure Python.\n\n(Want your own project to be listed here? Please create a GitHub issue.)\n\n\nPhilosophy\n**********\n\nThe source code of ``prompt_toolkit`` should be readable, concise and\nefficient. We prefer short functions focussing each on one task and for which\nthe input and output types are clearly specified. We mostly prefer composition\nover inheritance, because inheritance can result in too much functionality in\nthe same object. We prefer immutable objects where possible (objects don't\nchange after initialisation). Reusability is important. We absolutely refrain\nfrom having a changing global state, it should be possible to have multiple\nindependent instances of the same code in the same process. The architecture\nshould be layered: the lower levels operate on primitive operations and data\nstructures giving -- when correctly combined -- all the possible flexibility;\nwhile at the higher level, there should be a simpler API, ready-to-use and\nsufficient for most use cases. Thinking about algorithms and efficiency is\nimportant, but avoid premature optimization.\n\n\nSpecial thanks to\n*****************\n\n- `Pygments `_: Syntax highlighter.\n- `wcwidth `_: Determine columns needed for a wide characters.\n\n.. |Build Status| image:: https://api.travis-ci.org/jonathanslenders/python-prompt-toolkit.svg?branch=master\n :target: https://travis-ci.org/jonathanslenders/python-prompt-toolkit#\n\n.. |PyPI| image:: https://img.shields.io/pypi/v/prompt_toolkit.svg\n :target: https://pypi.python.org/pypi/prompt-toolkit/\n :alt: Latest Version\n", + "release_date": "2019-10-03T20:13:54", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jonathan Slenders", + "email": "", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jonathanslenders/python-prompt-toolkit", + "download_url": "https://files.pythonhosted.org/packages/9d/d2/2f099b5cd62dab819ce7a9f1431c09a9032fbfbb6474f442722e88935376/prompt_toolkit-1.0.18-py2-none-any.whl", + "size": 245351, + "sha1": null, + "md5": "2b38fc7f9d79010c645c538e5951540c", + "sha256": "f7eec66105baf40eda9ab026cd8b2e251337eea8d111196695d82e0c5f0af852", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/prompt-toolkit/1.0.18/json", + "datasource_id": null, + "purl": "pkg:pypi/prompt-toolkit@1.0.18" + }, + { + "type": "pypi", + "namespace": null, + "name": "prompt-toolkit", + "version": "1.0.18", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Python Prompt Toolkit\n=====================\n\n|Build Status| |PyPI|\n\n``prompt_toolkit`` is a library for building powerful interactive command lines\nand terminal applications in Python.\n\nRead the `documentation on readthedocs\n`_.\n\n\nPtpython\n********\n\n`ptpython `_ is an interactive\nPython Shell, build on top of prompt_toolkit.\n\n.. image :: https://github.com/jonathanslenders/python-prompt-toolkit/raw/master/docs/images/ptpython.png\n\n\nprompt_toolkit features\n***********************\n\n``prompt_toolkit`` could be a replacement for `GNU readline\n`_, but it can be much\nmore than that.\n\nSome features:\n\n- Pure Python.\n- Syntax highlighting of the input while typing. (For instance, with a Pygments lexer.)\n- Multi-line input editing.\n- Advanced code completion.\n- Both Emacs and Vi key bindings. (Similar to readline.)\n- Even some advanced Vi functionality, like named registers and digraphs.\n- Reverse and forward incremental search.\n- Runs on all Python versions from 2.6 up to 3.5.\n- Works well with Unicode double width characters. (Chinese input.)\n- Selecting text for copy/paste. (Both Emacs and Vi style.)\n- Support for `bracketed paste `_.\n- Mouse support for cursor positioning and scrolling.\n- Auto suggestions. (Like `fish shell `_.)\n- Multiple input buffers.\n- No global state.\n- Lightweight, the only dependencies are Pygments, six and wcwidth.\n- Runs on Linux, OS X, FreeBSD, OpenBSD and Windows systems.\n- And much more...\n\nFeel free to create tickets for bugs and feature requests, and create pull\nrequests if you have nice patches that you would like to share with others.\n\n\nAbout Windows support\n*********************\n\n``prompt_toolkit`` is cross platform, and everything that you build on top\nshould run fine on both Unix and Windows systems. On Windows, it uses a\ndifferent event loop (``WaitForMultipleObjects`` instead of ``select``), and\nanother input and output system. (Win32 APIs instead of pseudo-terminals and\nVT100.)\n\nIt's worth noting that the implementation is a \"best effort of what is\npossible\". Both Unix and Windows terminals have their limitations. But in\ngeneral, the Unix experience will still be a little better.\n\nFor Windows, it's recommended to use either `cmder\n`_ or `conemu `_.\n\n\nInstallation\n************\n\n::\n\n pip install prompt_toolkit\n\nFor Conda, do:\n\n::\n\n conda install -c https://conda.anaconda.org/conda-forge prompt_toolkit\n\n\nGetting started\n***************\n\nThe most simple example of the library would look like this:\n\n.. code:: python\n\n from prompt_toolkit import prompt\n\n if __name__ == '__main__':\n answer = prompt('Give me some input: ')\n print('You said: %s' % answer)\n\nFor more complex examples, have a look in the ``examples`` directory. All\nexamples are chosen to demonstrate only one thing. Also, don't be afraid to\nlook at the source code. The implementation of the ``prompt`` function could be\na good start.\n\nNote for Python 2: all strings are expected to be unicode strings. So, either\nput a small ``u`` in front of every string or put ``from __future__ import\nunicode_literals`` at the start of the above example.\n\n\nProjects using prompt_toolkit\n*****************************\n\nShells:\n\n- `ptpython `_: Python REPL\n- `ptpdb `_: Python debugger (pdb replacement)\n- `pgcli `_: Postgres client.\n- `mycli `_: MySql client.\n- `wharfee `_: A Docker command line.\n- `xonsh `_: A Python-ish, BASHwards-compatible shell.\n- `saws `_: A Supercharged AWS Command Line Interface.\n- `cycli `_: A Command Line Interface for Cypher.\n- `crash `_: Crate command line client.\n- `vcli `_: Vertica client.\n- `aws-shell `_: An integrated shell for working with the AWS CLI.\n- `softlayer-python `_: A command-line interface to manage various SoftLayer products and services.\n- `ipython `_: The IPython REPL\n- `click-repl `_: Subcommand REPL for click apps.\n- `haxor-news `_: A Hacker News CLI.\n- `gitsome `_: A Git/Shell Autocompleter with GitHub Integration.\n- `http-prompt `_: An interactive command-line HTTP client.\n- `coconut `_: Functional programming in Python.\n- `Ergonomica `_: A Bash alternative written in Python.\n- `Kube-shell `_: Kubernetes shell: An integrated shell for working with the Kubernetes CLI\n\nFull screen applications:\n\n- `pymux `_: A terminal multiplexer (like tmux) in pure Python.\n- `pyvim `_: A Vim clone in pure Python.\n\n(Want your own project to be listed here? Please create a GitHub issue.)\n\n\nPhilosophy\n**********\n\nThe source code of ``prompt_toolkit`` should be readable, concise and\nefficient. We prefer short functions focussing each on one task and for which\nthe input and output types are clearly specified. We mostly prefer composition\nover inheritance, because inheritance can result in too much functionality in\nthe same object. We prefer immutable objects where possible (objects don't\nchange after initialisation). Reusability is important. We absolutely refrain\nfrom having a changing global state, it should be possible to have multiple\nindependent instances of the same code in the same process. The architecture\nshould be layered: the lower levels operate on primitive operations and data\nstructures giving -- when correctly combined -- all the possible flexibility;\nwhile at the higher level, there should be a simpler API, ready-to-use and\nsufficient for most use cases. Thinking about algorithms and efficiency is\nimportant, but avoid premature optimization.\n\n\nSpecial thanks to\n*****************\n\n- `Pygments `_: Syntax highlighter.\n- `wcwidth `_: Determine columns needed for a wide characters.\n\n.. |Build Status| image:: https://api.travis-ci.org/jonathanslenders/python-prompt-toolkit.svg?branch=master\n :target: https://travis-ci.org/jonathanslenders/python-prompt-toolkit#\n\n.. |PyPI| image:: https://img.shields.io/pypi/v/prompt_toolkit.svg\n :target: https://pypi.python.org/pypi/prompt-toolkit/\n :alt: Latest Version\n", + "release_date": "2019-10-03T20:13:52", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jonathan Slenders", + "email": "", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jonathanslenders/python-prompt-toolkit", + "download_url": "https://files.pythonhosted.org/packages/c5/64/c170e5b1913b540bf0c8ab7676b21fdd1d25b65ddeb10025c6ca43cccd4c/prompt_toolkit-1.0.18.tar.gz", + "size": 242335, + "sha1": null, + "md5": "b3f4c92b4a0a7039d8f59d150ee439e2", + "sha256": "dd4fca02c8069497ad931a2d09914c6b0d1b50151ce876bc15bde4c747090126", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/prompt-toolkit/1.0.18/json", + "datasource_id": null, + "purl": "pkg:pypi/prompt-toolkit@1.0.18" + }, + { + "type": "pypi", + "namespace": null, + "name": "ptyprocess", + "version": "0.7.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Launch a subprocess in a pseudo terminal (pty), and interact with both the\nprocess and its pty.\n\nSometimes, piping stdin and stdout is not enough. There might be a password\nprompt that doesn't read from stdin, output that changes when it's going to a\npipe rather than a terminal, or curses-style interfaces that rely on a terminal.\nIf you need to automate these things, running the process in a pseudo terminal\n(pty) is the answer.\n\nInterface::\n\n p = PtyProcessUnicode.spawn(['python'])\n p.read(20)\n p.write('6+6\\n')\n p.read(20)\n", + "release_date": "2020-12-28T15:15:28", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Thomas Kluyver", + "email": "thomas@kluyver.me.uk", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pexpect/ptyprocess", + "download_url": "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", + "size": 13993, + "sha1": null, + "md5": "99ec04989f767cb72ac82ce671b561b0", + "sha256": "4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ptyprocess/0.7.0/json", + "datasource_id": null, + "purl": "pkg:pypi/ptyprocess@0.7.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "ptyprocess", + "version": "0.7.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Launch a subprocess in a pseudo terminal (pty), and interact with both the\nprocess and its pty.\n\nSometimes, piping stdin and stdout is not enough. There might be a password\nprompt that doesn't read from stdin, output that changes when it's going to a\npipe rather than a terminal, or curses-style interfaces that rely on a terminal.\nIf you need to automate these things, running the process in a pseudo terminal\n(pty) is the answer.\n\nInterface::\n\n p = PtyProcessUnicode.spawn(['python'])\n p.read(20)\n p.write('6+6\\n')\n p.read(20)\n", + "release_date": "2020-12-28T15:15:30", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Thomas Kluyver", + "email": "thomas@kluyver.me.uk", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pexpect/ptyprocess", + "download_url": "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", + "size": 70762, + "sha1": null, + "md5": "9da200c397cb1752209a6b718b6cfc68", + "sha256": "5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ptyprocess/0.7.0/json", + "datasource_id": null, + "purl": "pkg:pypi/ptyprocess@0.7.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "pycparser", + "version": "2.21", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "pycparser is a complete parser of the C language, written in\n pure Python using the PLY parsing library.\n It parses C code into an AST and can serve as a front-end for\n C compilers or analysis tools.", + "release_date": "2021-11-06T12:50:13", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Eli Bendersky", + "email": "eliben@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/eliben/pycparser", + "download_url": "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", + "size": 118697, + "sha1": null, + "md5": "763d265dfc20860dfbb4c81458400d03", + "sha256": "8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pycparser/2.21/json", + "datasource_id": null, + "purl": "pkg:pypi/pycparser@2.21" + }, + { + "type": "pypi", + "namespace": null, + "name": "pycparser", + "version": "2.21", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "pycparser is a complete parser of the C language, written in\n pure Python using the PLY parsing library.\n It parses C code into an AST and can serve as a front-end for\n C compilers or analysis tools.", + "release_date": "2021-11-06T12:48:46", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Eli Bendersky", + "email": "eliben@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/eliben/pycparser", + "download_url": "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", + "size": 170877, + "sha1": null, + "md5": "48f7d743bf018f7bb2ffc5fb976d1492", + "sha256": "e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pycparser/2.21/json", + "datasource_id": null, + "purl": "pkg:pypi/pycparser@2.21" + }, + { + "type": "pypi", + "namespace": null, + "name": "pygments", + "version": "2.5.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nPygments\n~~~~~~~~\n\nPygments is a syntax highlighting package written in Python.\n\nIt is a generic syntax highlighter suitable for use in code hosting, forums,\nwikis or other applications that need to prettify source code. Highlights\nare:\n\n* a wide range of over 300 languages and other text formats is supported\n* special attention is paid to details, increasing quality by a fair amount\n* support for new languages and formats are added easily\n* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences\n* it is usable as a command-line tool and as a library\n\n:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.\n:license: BSD, see LICENSE for details.\n\n\n", + "release_date": "2019-11-29T05:27:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Georg Brandl", + "email": "georg@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "syntax highlighting", + "homepage_url": "http://pygments.org/", + "download_url": "https://files.pythonhosted.org/packages/be/39/32da3184734730c0e4d3fa3b2b5872104668ad6dc1b5a73d8e477e5fe967/Pygments-2.5.2-py2.py3-none-any.whl", + "size": 896106, + "sha1": null, + "md5": "044bef3bf7f2ced9d8df7be47d3cfbde", + "sha256": "2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pygments/2.5.2/json", + "datasource_id": null, + "purl": "pkg:pypi/pygments@2.5.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "pygments", + "version": "2.5.2", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nPygments\n~~~~~~~~\n\nPygments is a syntax highlighting package written in Python.\n\nIt is a generic syntax highlighter suitable for use in code hosting, forums,\nwikis or other applications that need to prettify source code. Highlights\nare:\n\n* a wide range of over 300 languages and other text formats is supported\n* special attention is paid to details, increasing quality by a fair amount\n* support for new languages and formats are added easily\n* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences\n* it is usable as a command-line tool and as a library\n\n:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.\n:license: BSD, see LICENSE for details.\n\n\n", + "release_date": "2019-11-29T05:27:46", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Georg Brandl", + "email": "georg@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "syntax highlighting", + "homepage_url": "http://pygments.org/", + "download_url": "https://files.pythonhosted.org/packages/cb/9f/27d4844ac5bf158a33900dbad7985951e2910397998e85712da03ce125f0/Pygments-2.5.2.tar.gz", + "size": 20263984, + "sha1": null, + "md5": "465a35559863089d959d783a69f79b9f", + "sha256": "98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pygments/2.5.2/json", + "datasource_id": null, + "purl": "pkg:pypi/pygments@2.5.2" + }, + { + "type": "pypi", + "namespace": null, + "name": "pyrsistent", + "version": "0.16.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Pyrsistent\n==========\n.. image:: https://travis-ci.org/tobgu/pyrsistent.png?branch=master\n :target: https://travis-ci.org/tobgu/pyrsistent\n\n.. image:: https://badge.fury.io/py/pyrsistent.svg\n :target: https://badge.fury.io/py/pyrsistent\n\n.. image:: https://coveralls.io/repos/tobgu/pyrsistent/badge.svg?branch=master&service=github\n :target: https://coveralls.io/github/tobgu/pyrsistent?branch=master\n\n\n.. _Pyrthon: https://www.github.com/tobgu/pyrthon/\n\nPyrsistent is a number of persistent collections (by some referred to as functional data structures). Persistent in \nthe sense that they are immutable.\n\nAll methods on a data structure that would normally mutate it instead return a new copy of the structure containing the\nrequested updates. The original structure is left untouched.\n\nThis will simplify the reasoning about what a program does since no hidden side effects ever can take place to these\ndata structures. You can rest assured that the object you hold a reference to will remain the same throughout its\nlifetime and need not worry that somewhere five stack levels below you in the darkest corner of your application\nsomeone has decided to remove that element that you expected to be there.\n\nPyrsistent is influenced by persistent data structures such as those found in the standard library of Clojure. The\ndata structures are designed to share common elements through path copying.\nIt aims at taking these concepts and make them as pythonic as possible so that they can be easily integrated into any python\nprogram without hassle.\n\nIf you want to go all in on persistent data structures and use literal syntax to define them in your code rather\nthan function calls check out Pyrthon_.\n\nExamples\n--------\n.. _Sequence: collections_\n.. _Hashable: collections_\n.. _Mapping: collections_\n.. _Mappings: collections_\n.. _Set: collections_\n.. _collections: https://docs.python.org/3/library/collections.abc.html\n.. _documentation: http://pyrsistent.readthedocs.org/\n\nThe collection types and key features currently implemented are:\n\n* PVector_, similar to a python list\n* PMap_, similar to dict\n* PSet_, similar to set\n* PRecord_, a PMap on steroids with fixed fields, optional type and invariant checking and much more\n* PClass_, a Python class fixed fields, optional type and invariant checking and much more\n* `Checked collections`_, PVector, PMap and PSet with optional type and invariance checks and more\n* PBag, similar to collections.Counter\n* PList, a classic singly linked list\n* PDeque, similar to collections.deque\n* Immutable object type (immutable) built on the named tuple\n* freeze_ and thaw_ functions to convert between pythons standard collections and pyrsistent collections.\n* Flexible transformations_ of arbitrarily complex structures built from PMaps and PVectors.\n\nBelow are examples of common usage patterns for some of the structures and features. More information and\nfull documentation for all data structures is available in the documentation_.\n\n.. _PVector:\n\nPVector\n~~~~~~~\nWith full support for the Sequence_ protocol PVector is meant as a drop in replacement to the built in list from a readers\npoint of view. Write operations of course differ since no in place mutation is done but naming should be in line\nwith corresponding operations on the built in list.\n\nSupport for the Hashable_ protocol also means that it can be used as key in Mappings_.\n\nAppends are amortized O(1). Random access and insert is log32(n) where n is the size of the vector.\n\n.. code:: python\n\n >>> from pyrsistent import v, pvector\n\n # No mutation of vectors once created, instead they\n # are \"evolved\" leaving the original untouched\n >>> v1 = v(1, 2, 3)\n >>> v2 = v1.append(4)\n >>> v3 = v2.set(1, 5)\n >>> v1\n pvector([1, 2, 3])\n >>> v2\n pvector([1, 2, 3, 4])\n >>> v3\n pvector([1, 5, 3, 4])\n\n # Random access and slicing\n >>> v3[1]\n 5\n >>> v3[1:3]\n pvector([5, 3])\n\n # Iteration\n >>> list(x + 1 for x in v3)\n [2, 6, 4, 5]\n >>> pvector(2 * x for x in range(3))\n pvector([0, 2, 4])\n\n.. _PMap:\n\nPMap\n~~~~\nWith full support for the Mapping_ protocol PMap is meant as a drop in replacement to the built in dict from a readers point\nof view. Support for the Hashable_ protocol also means that it can be used as key in other Mappings_.\n\nRandom access and insert is log32(n) where n is the size of the map.\n\n.. code:: python\n\n >>> from pyrsistent import m, pmap, v\n\n # No mutation of maps once created, instead they are\n # \"evolved\" leaving the original untouched\n >>> m1 = m(a=1, b=2)\n >>> m2 = m1.set('c', 3)\n >>> m3 = m2.set('a', 5)\n >>> m1\n pmap({'a': 1, 'b': 2})\n >>> m2\n pmap({'a': 1, 'c': 3, 'b': 2})\n >>> m3\n pmap({'a': 5, 'c': 3, 'b': 2})\n >>> m3['a']\n 5\n\n # Evolution of nested persistent structures\n >>> m4 = m(a=5, b=6, c=v(1, 2))\n >>> m4.transform(('c', 1), 17)\n pmap({'a': 5, 'c': pvector([1, 17]), 'b': 6})\n >>> m5 = m(a=1, b=2)\n\n # Evolve by merging with other mappings\n >>> m5.update(m(a=2, c=3), {'a': 17, 'd': 35})\n pmap({'a': 17, 'c': 3, 'b': 2, 'd': 35})\n >>> pmap({'x': 1, 'y': 2}) + pmap({'y': 3, 'z': 4})\n pmap({'y': 3, 'x': 1, 'z': 4})\n\n # Dict-like methods to convert to list and iterate\n >>> m3.items()\n pvector([('a', 5), ('c', 3), ('b', 2)])\n >>> list(m3)\n ['a', 'c', 'b']\n\n.. _PSet:\n\nPSet\n~~~~\nWith full support for the Set_ protocol PSet is meant as a drop in replacement to the built in set from a readers point\nof view. Support for the Hashable_ protocol also means that it can be used as key in Mappings_.\n\nRandom access and insert is log32(n) where n is the size of the set.\n\n.. code:: python\n\n >>> from pyrsistent import s\n\n # No mutation of sets once created, you know the story...\n >>> s1 = s(1, 2, 3, 2)\n >>> s2 = s1.add(4)\n >>> s3 = s1.remove(1)\n >>> s1\n pset([1, 2, 3])\n >>> s2\n pset([1, 2, 3, 4])\n >>> s3\n pset([2, 3])\n\n # Full support for set operations\n >>> s1 | s(3, 4, 5)\n pset([1, 2, 3, 4, 5])\n >>> s1 & s(3, 4, 5)\n pset([3])\n >>> s1 < s2\n True\n >>> s1 < s(3, 4, 5)\n False\n\n.. _PRecord:\n\nPRecord\n~~~~~~~\nA PRecord is a PMap with a fixed set of specified fields. Records are declared as python classes inheriting\nfrom PRecord. Because it is a PMap it has full support for all Mapping methods such as iteration and element\naccess using subscript notation.\n\n.. code:: python\n\n >>> from pyrsistent import PRecord, field\n >>> class ARecord(PRecord):\n ... x = field()\n ...\n >>> r = ARecord(x=3)\n >>> r\n ARecord(x=3)\n >>> r.x\n 3\n >>> r.set(x=2)\n ARecord(x=2)\n >>> r.set(y=2)\n Traceback (most recent call last):\n AttributeError: 'y' is not among the specified fields for ARecord\n\nType information\n****************\nIt is possible to add type information to the record to enforce type checks. Multiple allowed types can be specified\nby providing an iterable of types.\n\n.. code:: python\n\n >>> class BRecord(PRecord):\n ... x = field(type=int)\n ... y = field(type=(int, type(None)))\n ...\n >>> BRecord(x=3, y=None)\n BRecord(y=None, x=3)\n >>> BRecord(x=3.0)\n Traceback (most recent call last):\n PTypeError: Invalid type for field BRecord.x, was float\n\n\nCustom types (classes) that are iterable should be wrapped in a tuple to prevent their\nmembers being added to the set of valid types. Although Enums in particular are now \nsupported without wrapping, see #83 for more information.\n\nMandatory fields\n****************\nFields are not mandatory by default but can be specified as such. If fields are missing an\n*InvariantException* will be thrown which contains information about the missing fields.\n\n.. code:: python\n\n >>> from pyrsistent import InvariantException\n >>> class CRecord(PRecord):\n ... x = field(mandatory=True)\n ...\n >>> r = CRecord(x=3)\n >>> try:\n ... r.discard('x')\n ... except InvariantException as e:\n ... print(e.missing_fields)\n ...\n ('CRecord.x',)\n\nInvariants\n**********\nIt is possible to add invariants that must hold when evolving the record. Invariants can be\nspecified on both field and record level. If invariants fail an *InvariantException* will be\nthrown which contains information about the failing invariants. An invariant function should\nreturn a tuple consisting of a boolean that tells if the invariant holds or not and an object\ndescribing the invariant. This object can later be used to identify which invariant that failed.\n\nThe global invariant function is only executed if all field invariants hold.\n\nGlobal invariants are inherited to subclasses.\n\n.. code:: python\n\n >>> class RestrictedVector(PRecord):\n ... __invariant__ = lambda r: (r.y >= r.x, 'x larger than y')\n ... x = field(invariant=lambda x: (x > 0, 'x negative'))\n ... y = field(invariant=lambda y: (y > 0, 'y negative'))\n ...\n >>> r = RestrictedVector(y=3, x=2)\n >>> try:\n ... r.set(x=-1, y=-2)\n ... except InvariantException as e:\n ... print(e.invariant_errors)\n ...\n ('y negative', 'x negative')\n >>> try:\n ... r.set(x=2, y=1)\n ... except InvariantException as e:\n ... print(e.invariant_errors)\n ...\n ('x larger than y',)\n\nInvariants may also contain multiple assertions. For those cases the invariant function should\nreturn a tuple of invariant tuples as described above. This structure is reflected in the\ninvariant_errors attribute of the exception which will contain tuples with data from all failed\ninvariants. Eg:\n\n.. code:: python\n\n >>> class EvenX(PRecord):\n ... x = field(invariant=lambda x: ((x > 0, 'x negative'), (x % 2 == 0, 'x odd')))\n ...\n >>> try:\n ... EvenX(x=-1)\n ... except InvariantException as e:\n ... print(e.invariant_errors)\n ...\n (('x negative', 'x odd'),)\n\n\nFactories\n*********\nIt's possible to specify factory functions for fields. The factory function receives whatever\nis supplied as field value and the actual returned by the factory is assigned to the field\ngiven that any type and invariant checks hold.\nPRecords have a default factory specified as a static function on the class, create(). It takes\na *Mapping* as argument and returns an instance of the specific record.\nIf a record has fields of type PRecord the create() method of that record will\nbe called to create the \"sub record\" if no factory has explicitly been specified to override\nthis behaviour.\n\n.. code:: python\n\n >>> class DRecord(PRecord):\n ... x = field(factory=int)\n ...\n >>> class ERecord(PRecord):\n ... d = field(type=DRecord)\n ...\n >>> ERecord.create({'d': {'x': '1'}})\n ERecord(d=DRecord(x=1))\n\nCollection fields\n*****************\nIt is also possible to have fields with ``pyrsistent`` collections.\n\n.. code:: python\n\n >>> from pyrsistent import pset_field, pmap_field, pvector_field\n >>> class MultiRecord(PRecord):\n ... set_of_ints = pset_field(int)\n ... map_int_to_str = pmap_field(int, str)\n ... vector_of_strs = pvector_field(str)\n ...\n\t\nSerialization\n*************\nPRecords support serialization back to dicts. Default serialization will take keys and values\n\"as is\" and output them into a dict. It is possible to specify custom serialization functions\nto take care of fields that require special treatment.\n\n.. code:: python\n\n >>> from datetime import date\n >>> class Person(PRecord):\n ... name = field(type=unicode)\n ... birth_date = field(type=date,\n ... serializer=lambda format, d: d.strftime(format['date']))\n ...\n >>> john = Person(name=u'John', birth_date=date(1985, 10, 21))\n >>> john.serialize({'date': '%Y-%m-%d'})\n {'birth_date': '1985-10-21', 'name': u'John'}\n\n\n.. _instar: https://github.com/boxed/instar/\n\n.. _PClass:\n\nPClass\n~~~~~~\nA PClass is a python class with a fixed set of specified fields. PClasses are declared as python classes inheriting\nfrom PClass. It is defined the same way that PRecords are and behaves like a PRecord in all aspects except that it\nis not a PMap and hence not a collection but rather a plain Python object.\n\n.. code:: python\n\n >>> from pyrsistent import PClass, field\n >>> class AClass(PClass):\n ... x = field()\n ...\n >>> a = AClass(x=3)\n >>> a\n AClass(x=3)\n >>> a.x\n 3\n\n\nChecked collections\n~~~~~~~~~~~~~~~~~~~\nChecked collections currently come in three flavors: CheckedPVector, CheckedPMap and CheckedPSet.\n\n.. code:: python\n\n >>> from pyrsistent import CheckedPVector, CheckedPMap, CheckedPSet, thaw\n >>> class Positives(CheckedPSet):\n ... __type__ = (long, int)\n ... __invariant__ = lambda n: (n >= 0, 'Negative')\n ...\n >>> class Lottery(PRecord):\n ... name = field(type=str)\n ... numbers = field(type=Positives, invariant=lambda p: (len(p) > 0, 'No numbers'))\n ...\n >>> class Lotteries(CheckedPVector):\n ... __type__ = Lottery\n ...\n >>> class LotteriesByDate(CheckedPMap):\n ... __key_type__ = date\n ... __value_type__ = Lotteries\n ...\n >>> lotteries = LotteriesByDate.create({date(2015, 2, 15): [{'name': 'SuperLotto', 'numbers': {1, 2, 3}},\n ... {'name': 'MegaLotto', 'numbers': {4, 5, 6}}],\n ... date(2015, 2, 16): [{'name': 'SuperLotto', 'numbers': {3, 2, 1}},\n ... {'name': 'MegaLotto', 'numbers': {6, 5, 4}}]})\n >>> lotteries\n LotteriesByDate({datetime.date(2015, 2, 15): Lotteries([Lottery(numbers=Positives([1, 2, 3]), name='SuperLotto'), Lottery(numbers=Positives([4, 5, 6]), name='MegaLotto')]), datetime.date(2015, 2, 16): Lotteries([Lottery(numbers=Positives([1, 2, 3]), name='SuperLotto'), Lottery(numbers=Positives([4, 5, 6]), name='MegaLotto')])})\n\n # The checked versions support all operations that the corresponding\n # unchecked types do\n >>> lottery_0215 = lotteries[date(2015, 2, 15)]\n >>> lottery_0215.transform([0, 'name'], 'SuperDuperLotto')\n Lotteries([Lottery(numbers=Positives([1, 2, 3]), name='SuperDuperLotto'), Lottery(numbers=Positives([4, 5, 6]), name='MegaLotto')])\n\n # But also makes asserts that types and invariants hold\n >>> lottery_0215.transform([0, 'name'], 999)\n Traceback (most recent call last):\n PTypeError: Invalid type for field Lottery.name, was int\n\n >>> lottery_0215.transform([0, 'numbers'], set())\n Traceback (most recent call last):\n InvariantException: Field invariant failed\n\n # They can be converted back to python built ins with either thaw()\n # or serialize() (which provides possibilities to customize serialization)\n >>> thaw(lottery_0215)\n [{'numbers': set([1, 2, 3]), 'name': 'SuperLotto'}, {'numbers': set([4, 5, 6]), 'name': 'MegaLotto'}]\n >>> lottery_0215.serialize()\n [{'numbers': set([1, 2, 3]), 'name': 'SuperLotto'}, {'numbers': set([4, 5, 6]), 'name': 'MegaLotto'}]\n\n.. _transformations:\n\nTransformations\n~~~~~~~~~~~~~~~\nTransformations are inspired by the cool library instar_ for Clojure. They let you evolve PMaps and PVectors\nwith arbitrarily deep/complex nesting using simple syntax and flexible matching syntax.\n\nThe first argument to transformation is the path that points out the value to transform. The\nsecond is the transformation to perform. If the transformation is callable it will be applied\nto the value(s) matching the path. The path may also contain callables. In that case they are\ntreated as matchers. If the matcher returns True for a specific key it is considered for transformation.\n\n.. code:: python\n\n # Basic examples\n >>> from pyrsistent import inc, freeze, thaw, rex, ny, discard\n >>> v1 = freeze([1, 2, 3, 4, 5])\n >>> v1.transform([2], inc)\n pvector([1, 2, 4, 4, 5])\n >>> v1.transform([lambda ix: 0 < ix < 4], 8)\n pvector([1, 8, 8, 8, 5])\n >>> v1.transform([lambda ix, v: ix == 0 or v == 5], 0)\n pvector([0, 2, 3, 4, 0])\n\n # The (a)ny matcher can be used to match anything\n >>> v1.transform([ny], 8)\n pvector([8, 8, 8, 8, 8])\n\n # Regular expressions can be used for matching\n >>> scores = freeze({'John': 12, 'Joseph': 34, 'Sara': 23})\n >>> scores.transform([rex('^Jo')], 0)\n pmap({'Joseph': 0, 'Sara': 23, 'John': 0})\n\n # Transformations can be done on arbitrarily deep structures\n >>> news_paper = freeze({'articles': [{'author': 'Sara', 'content': 'A short article'},\n ... {'author': 'Steve', 'content': 'A slightly longer article'}],\n ... 'weather': {'temperature': '11C', 'wind': '5m/s'}})\n >>> short_news = news_paper.transform(['articles', ny, 'content'], lambda c: c[:25] + '...' if len(c) > 25 else c)\n >>> very_short_news = news_paper.transform(['articles', ny, 'content'], lambda c: c[:15] + '...' if len(c) > 15 else c)\n >>> very_short_news.articles[0].content\n 'A short article'\n >>> very_short_news.articles[1].content\n 'A slightly long...'\n\n # When nothing has been transformed the original data structure is kept\n >>> short_news is news_paper\n True\n >>> very_short_news is news_paper\n False\n >>> very_short_news.articles[0] is news_paper.articles[0]\n True\n\n # There is a special transformation that can be used to discard elements. Also\n # multiple transformations can be applied in one call\n >>> thaw(news_paper.transform(['weather'], discard, ['articles', ny, 'content'], discard))\n {'articles': [{'author': 'Sara'}, {'author': 'Steve'}]}\n\nEvolvers\n~~~~~~~~\nPVector, PMap and PSet all have support for a concept dubbed *evolvers*. An evolver acts like a mutable\nview of the underlying persistent data structure with \"transaction like\" semantics. No updates of the original\ndata structure is ever performed, it is still fully immutable.\n\nThe evolvers have a very limited API by design to discourage excessive, and inappropriate, usage as that would\ntake us down the mutable road. In principle only basic mutation and element access functions are supported.\nCheck out the documentation_ of each data structure for specific examples.\n\nExamples of when you may want to use an evolver instead of working directly with the data structure include:\n\n* Multiple updates are done to the same data structure and the intermediate results are of no\n interest. In this case using an evolver may be a more efficient and easier to work with.\n* You need to pass a vector into a legacy function or a function that you have no control\n over which performs in place mutations. In this case pass an evolver instance\n instead and then create a new pvector from the evolver once the function returns.\n\n.. code:: python\n\n >>> from pyrsistent import v\n\n # In place mutation as when working with the built in counterpart\n >>> v1 = v(1, 2, 3)\n >>> e = v1.evolver()\n >>> e[1] = 22\n >>> e = e.append(4)\n >>> e = e.extend([5, 6])\n >>> e[5] += 1\n >>> len(e)\n 6\n\n # The evolver is considered *dirty* when it contains changes compared to the underlying vector\n >>> e.is_dirty()\n True\n\n # But the underlying pvector still remains untouched\n >>> v1\n pvector([1, 2, 3])\n\n # Once satisfied with the updates you can produce a new pvector containing the updates.\n # The new pvector will share data with the original pvector in the same way that would have\n # been done if only using operations on the pvector.\n >>> v2 = e.persistent()\n >>> v2\n pvector([1, 22, 3, 4, 5, 7])\n\n # The evolver is now no longer considered *dirty* as it contains no differences compared to the\n # pvector just produced.\n >>> e.is_dirty()\n False\n\n # You may continue to work with the same evolver without affecting the content of v2\n >>> e[0] = 11\n\n # Or create a new evolver from v2. The two evolvers can be updated independently but will both\n # share data with v2 where possible.\n >>> e2 = v2.evolver()\n >>> e2[0] = 1111\n >>> e.persistent()\n pvector([11, 22, 3, 4, 5, 7])\n >>> e2.persistent()\n pvector([1111, 22, 3, 4, 5, 7])\n\n.. _freeze:\n.. _thaw:\n\nfreeze and thaw\n~~~~~~~~~~~~~~~\nThese functions are great when your cozy immutable world has to interact with the evil mutable world outside.\n\n.. code:: python\n\n >>> from pyrsistent import freeze, thaw, v, m\n >>> freeze([1, {'a': 3}])\n pvector([1, pmap({'a': 3})])\n >>> thaw(v(1, m(a=3)))\n [1, {'a': 3}]\n\nCompatibility\n-------------\n\nPyrsistent is developed and tested on Python 2.7, 3.5, 3.6, 3.7 and PyPy (Python 2 and 3 compatible). It will most\nlikely work on all other versions >= 3.4 but no guarantees are given. :)\n\nCompatibility issues\n~~~~~~~~~~~~~~~~~~~~\n\n.. _27: https://github.com/tobgu/pyrsistent/issues/27\n\nThere is currently one known compatibility issue when comparing built in sets and frozensets to PSets as discussed in 27_.\nIt affects python 2 versions < 2.7.8 and python 3 versions < 3.4.0 and is due to a bug described in\nhttp://bugs.python.org/issue8743.\n\nComparisons will fail or be incorrect when using the set/frozenset as left hand side of the comparison. As a workaround\nyou need to either upgrade Python to a more recent version, avoid comparing sets/frozensets with PSets or always make\nsure to convert both sides of the comparison to the same type before performing the comparison.\n\nPerformance\n-----------\n\nPyrsistent is developed with performance in mind. Still, while some operations are nearly on par with their built in, \nmutable, counterparts in terms of speed, other operations are slower. In the cases where attempts at\noptimizations have been done, speed has generally been valued over space.\n\nPyrsistent comes with two API compatible flavors of PVector (on which PMap and PSet are based), one pure Python \nimplementation and one implemented as a C extension. The latter generally being 2 - 20 times faster than the former.\nThe C extension will be used automatically when possible.\n\nThe pure python implementation is fully PyPy compatible. Running it under PyPy speeds operations up considerably if \nthe structures are used heavily (if JITed), for some cases the performance is almost on par with the built in counterparts.\n\nType hints\n----------\n\nPEP 561 style type hints for use with mypy and various editors are available for most types and functions in pyrsistent.\n\nType classes for annotating your own code with pyrsistent types are also available under pyrsistent.typing.\n\nInstallation\n------------\n\npip install pyrsistent\n\nDocumentation\n-------------\n\nAvailable at http://pyrsistent.readthedocs.org/\n\nBrief presentation available at http://slides.com/tobiasgustafsson/immutability-and-python/\n\nContributors\n------------\n\nTobias Gustafsson https://github.com/tobgu\n\nChristopher Armstrong https://github.com/radix\n\nAnders Hovm\u00f6ller https://github.com/boxed\n\nItamar Turner-Trauring https://github.com/itamarst\n\nJonathan Lange https://github.com/jml\n\nRichard Futrell https://github.com/Futrell\n\nJakob Hollenstein https://github.com/jkbjh\n\nDavid Honour https://github.com/foolswood\n\nDavid R. MacIver https://github.com/DRMacIver\n\nMarcus Ewert https://github.com/sarum90\n\nJean-Paul Calderone https://github.com/exarkun\n\nDouglas Treadwell https://github.com/douglas-treadwell\n\nTravis Parker https://github.com/teepark\n\nJulian Berman https://github.com/Julian\n\nDennis Tomas https://github.com/dtomas\n\nNeil Vyas https://github.com/neilvyas\n\ndoozr https://github.com/doozr\n\nKamil Galuszka https://github.com/galuszkak\n\nTsuyoshi Hombashi https://github.com/thombashi\n\nnattofriends https://github.com/nattofriends\n\nagberk https://github.com/agberk\n\nWaleed Khan https://github.com/arxanas\n\nJean-Louis Fuchs https://github.com/ganwell\n\nCarlos Corbacho https://github.com/ccorbacho\n\nFelix Yan https://github.com/felixonmars\n\nbenrg https://github.com/benrg\n\nJere Lahelma https://github.com/je-l\n\nMax Taggart https://github.com/MaxTaggart\n\nVincent Philippon https://github.com/vphilippon\n\nSemen Zhydenko https://github.com/ss18\n\nTill Varoquaux https://github.com/till-varoquaux\n\nMichal Kowalik https://github.com/michalvi\n\nossdev07 https://github.com/ossdev07\n\nKerry Olesen https://github.com/qhesz\n\njohnthagen https://github.com/johnthagen\n\nContributing\n------------\n\nWant to contribute? That's great! If you experience problems please log them on GitHub. If you want to contribute code,\nplease fork the repository and submit a pull request.\n\nRun tests\n~~~~~~~~~\n.. _tox: https://tox.readthedocs.io/en/latest/\n\nTests can be executed using tox_.\n\nInstall tox: ``pip install tox``\n\nRun test for Python 2.7: ``tox -epy27``\n\nRelease\n~~~~~~~\n* `pip install -r requirements.txt`\n* Update CHANGES.txt\n* Update README with any new contributors and potential info needed.\n* Update _pyrsistent_version.py\n* `rm -rf dist/* && python setup.py sdist`\n* (`twine upload -r testpypi dist/*`), if testing the distribution on testpypi\n* `twine upload dist/*`\n* Commit and tag with new version: `git add -u . && git commit -m 'Prepare version vX.Y.Z' && git tag -a vX.Y.Z -m 'vX.Y.Z'`\n* Push commit and tags: `git push && git push --tags`\n\nProject status\n--------------\nPyrsistent can be considered stable and mature (who knows, there may even be a 1.0 some day :-)). The project is\nmaintained, bugs fixed, PRs reviewed and merged and new releases made. I currently do not have time for development\nof new features or functionality which I don't have use for myself. I'm more than happy to take PRs for new\nfunctionality though!\n\nThere are a bunch of issues marked with ``enhancement`` and ``help wanted`` that contain requests for new functionality\nthat would be nice to include. The level of difficulty and extend of the issues varies, please reach out to me if you're\ninterested in working on any of them.\n\nIf you feel that you have a grand master plan for where you would like Pyrsistent to go and have the time to put into\nit please don't hesitate to discuss this with me and submit PRs for it. If all goes well I'd be more than happy to add\nadditional maintainers to the project!", + "release_date": "2020-09-13T06:51:59", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Tobias Gustafsson", + "email": "tobias.l.gustafsson@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://github.com/tobgu/pyrsistent/", + "download_url": "https://files.pythonhosted.org/packages/80/18/1492d651693ef7d40e0a40377ed56a8cc5c5fe86073eb6c56e53513f4480/pyrsistent-0.16.1.tar.gz", + "size": 108176, + "sha1": null, + "md5": "1d6e6ae7f1da8082f39c52b64ed66ac0", + "sha256": "aa2ae1c2e496f4d6777f869ea5de7166a8ccb9c2e06ebcf6c7ff1b670c98c5ef", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pyrsistent/0.16.1/json", + "datasource_id": null, + "purl": "pkg:pypi/pyrsistent@0.16.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "pytz", + "version": "2022.2.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "pytz - World Timezone Definitions for Python\n============================================\n\n:Author: Stuart Bishop \n\nIntroduction\n~~~~~~~~~~~~\n\npytz brings the Olson tz database into Python. This library allows\naccurate and cross platform timezone calculations using Python 2.4\nor higher. It also solves the issue of ambiguous times at the end\nof daylight saving time, which you can read more about in the Python\nLibrary Reference (``datetime.tzinfo``).\n\nAlmost all of the Olson timezones are supported.\n\n.. note::\n\n This library differs from the documented Python API for\n tzinfo implementations; if you want to create local wallclock\n times you need to use the ``localize()`` method documented in this\n document. In addition, if you perform date arithmetic on local\n times that cross DST boundaries, the result may be in an incorrect\n timezone (ie. subtract 1 minute from 2002-10-27 1:00 EST and you get\n 2002-10-27 0:59 EST instead of the correct 2002-10-27 1:59 EDT). A\n ``normalize()`` method is provided to correct this. Unfortunately these\n issues cannot be resolved without modifying the Python datetime\n implementation (see PEP-431).\n\n\nInstallation\n~~~~~~~~~~~~\n\nThis package can either be installed using ``pip`` or from a tarball using the\nstandard Python distutils.\n\nIf you are installing using ``pip``, you don't need to download anything as the\nlatest version will be downloaded for you from PyPI::\n\n pip install pytz\n\nIf you are installing from a tarball, run the following command as an\nadministrative user::\n\n python setup.py install\n\n\npytz for Enterprise\n~~~~~~~~~~~~~~~~~~~\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of pytz and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. `Learn more. `_.\n\n\nExample & Usage\n~~~~~~~~~~~~~~~\n\nLocalized times and date arithmetic\n-----------------------------------\n\n>>> from datetime import datetime, timedelta\n>>> from pytz import timezone\n>>> import pytz\n>>> utc = pytz.utc\n>>> utc.zone\n'UTC'\n>>> eastern = timezone('US/Eastern')\n>>> eastern.zone\n'US/Eastern'\n>>> amsterdam = timezone('Europe/Amsterdam')\n>>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'\n\nThis library only supports two ways of building a localized time. The\nfirst is to use the ``localize()`` method provided by the pytz library.\nThis is used to localize a naive datetime (datetime with no timezone\ninformation):\n\n>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))\n>>> print(loc_dt.strftime(fmt))\n2002-10-27 06:00:00 EST-0500\n\nThe second way of building a localized time is by converting an existing\nlocalized time using the standard ``astimezone()`` method:\n\n>>> ams_dt = loc_dt.astimezone(amsterdam)\n>>> ams_dt.strftime(fmt)\n'2002-10-27 12:00:00 CET+0100'\n\nUnfortunately using the tzinfo argument of the standard datetime\nconstructors ''does not work'' with pytz for many timezones.\n\n>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) # /!\\ Does not work this way!\n'2002-10-27 12:00:00 LMT+0018'\n\nIt is safe for timezones without daylight saving transitions though, such\nas UTC:\n\n>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt) # /!\\ Not recommended except for UTC\n'2002-10-27 12:00:00 UTC+0000'\n\nThe preferred way of dealing with times is to always work in UTC,\nconverting to localtime only when generating output to be read\nby humans.\n\n>>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)\n>>> loc_dt = utc_dt.astimezone(eastern)\n>>> loc_dt.strftime(fmt)\n'2002-10-27 01:00:00 EST-0500'\n\nThis library also allows you to do date arithmetic using local\ntimes, although it is more complicated than working in UTC as you\nneed to use the ``normalize()`` method to handle daylight saving time\nand other timezone transitions. In this example, ``loc_dt`` is set\nto the instant when daylight saving time ends in the US/Eastern\ntimezone.\n\n>>> before = loc_dt - timedelta(minutes=10)\n>>> before.strftime(fmt)\n'2002-10-27 00:50:00 EST-0500'\n>>> eastern.normalize(before).strftime(fmt)\n'2002-10-27 01:50:00 EDT-0400'\n>>> after = eastern.normalize(before + timedelta(minutes=20))\n>>> after.strftime(fmt)\n'2002-10-27 01:10:00 EST-0500'\n\nCreating local times is also tricky, and the reason why working with\nlocal times is not recommended. Unfortunately, you cannot just pass\na ``tzinfo`` argument when constructing a datetime (see the next\nsection for more details)\n\n>>> dt = datetime(2002, 10, 27, 1, 30, 0)\n>>> dt1 = eastern.localize(dt, is_dst=True)\n>>> dt1.strftime(fmt)\n'2002-10-27 01:30:00 EDT-0400'\n>>> dt2 = eastern.localize(dt, is_dst=False)\n>>> dt2.strftime(fmt)\n'2002-10-27 01:30:00 EST-0500'\n\nConverting between timezones is more easily done, using the\nstandard astimezone method.\n\n>>> utc_dt = utc.localize(datetime.utcfromtimestamp(1143408899))\n>>> utc_dt.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n>>> au_tz = timezone('Australia/Sydney')\n>>> au_dt = utc_dt.astimezone(au_tz)\n>>> au_dt.strftime(fmt)\n'2006-03-27 08:34:59 AEDT+1100'\n>>> utc_dt2 = au_dt.astimezone(utc)\n>>> utc_dt2.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n>>> utc_dt == utc_dt2\nTrue\n\nYou can take shortcuts when dealing with the UTC side of timezone\nconversions. ``normalize()`` and ``localize()`` are not really\nnecessary when there are no daylight saving time transitions to\ndeal with.\n\n>>> utc_dt = datetime.utcfromtimestamp(1143408899).replace(tzinfo=utc)\n>>> utc_dt.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n>>> au_tz = timezone('Australia/Sydney')\n>>> au_dt = au_tz.normalize(utc_dt.astimezone(au_tz))\n>>> au_dt.strftime(fmt)\n'2006-03-27 08:34:59 AEDT+1100'\n>>> utc_dt2 = au_dt.astimezone(utc)\n>>> utc_dt2.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n\n\n``tzinfo`` API\n--------------\n\nThe ``tzinfo`` instances returned by the ``timezone()`` function have\nbeen extended to cope with ambiguous times by adding an ``is_dst``\nparameter to the ``utcoffset()``, ``dst()`` && ``tzname()`` methods.\n\n>>> tz = timezone('America/St_Johns')\n\n>>> normal = datetime(2009, 9, 1)\n>>> ambiguous = datetime(2009, 10, 31, 23, 30)\n\nThe ``is_dst`` parameter is ignored for most timestamps. It is only used\nduring DST transition ambiguous periods to resolve that ambiguity.\n\n>>> print(tz.utcoffset(normal, is_dst=True))\n-1 day, 21:30:00\n>>> print(tz.dst(normal, is_dst=True))\n1:00:00\n>>> tz.tzname(normal, is_dst=True)\n'NDT'\n\n>>> print(tz.utcoffset(ambiguous, is_dst=True))\n-1 day, 21:30:00\n>>> print(tz.dst(ambiguous, is_dst=True))\n1:00:00\n>>> tz.tzname(ambiguous, is_dst=True)\n'NDT'\n\n>>> print(tz.utcoffset(normal, is_dst=False))\n-1 day, 21:30:00\n>>> tz.dst(normal, is_dst=False).seconds\n3600\n>>> tz.tzname(normal, is_dst=False)\n'NDT'\n\n>>> print(tz.utcoffset(ambiguous, is_dst=False))\n-1 day, 20:30:00\n>>> tz.dst(ambiguous, is_dst=False)\ndatetime.timedelta(0)\n>>> tz.tzname(ambiguous, is_dst=False)\n'NST'\n\nIf ``is_dst`` is not specified, ambiguous timestamps will raise\nan ``pytz.exceptions.AmbiguousTimeError`` exception.\n\n>>> print(tz.utcoffset(normal))\n-1 day, 21:30:00\n>>> print(tz.dst(normal))\n1:00:00\n>>> tz.tzname(normal)\n'NDT'\n\n>>> import pytz.exceptions\n>>> try:\n... tz.utcoffset(ambiguous)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)\npytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00\n>>> try:\n... tz.dst(ambiguous)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)\npytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00\n>>> try:\n... tz.tzname(ambiguous)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)\npytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00\n\n\nProblems with Localtime\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThe major problem we have to deal with is that certain datetimes\nmay occur twice in a year. For example, in the US/Eastern timezone\non the last Sunday morning in October, the following sequence\nhappens:\n\n - 01:00 EDT occurs\n - 1 hour later, instead of 2:00am the clock is turned back 1 hour\n and 01:00 happens again (this time 01:00 EST)\n\nIn fact, every instant between 01:00 and 02:00 occurs twice. This means\nthat if you try and create a time in the 'US/Eastern' timezone\nthe standard datetime syntax, there is no way to specify if you meant\nbefore of after the end-of-daylight-saving-time transition. Using the\npytz custom syntax, the best you can do is make an educated guess:\n\n>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 1, 30, 00))\n>>> loc_dt.strftime(fmt)\n'2002-10-27 01:30:00 EST-0500'\n\nAs you can see, the system has chosen one for you and there is a 50%\nchance of it being out by one hour. For some applications, this does\nnot matter. However, if you are trying to schedule meetings with people\nin different timezones or analyze log files it is not acceptable.\n\nThe best and simplest solution is to stick with using UTC. The pytz\npackage encourages using UTC for internal timezone representation by\nincluding a special UTC implementation based on the standard Python\nreference implementation in the Python documentation.\n\nThe UTC timezone unpickles to be the same instance, and pickles to a\nsmaller size than other pytz tzinfo instances. The UTC implementation\ncan be obtained as pytz.utc, pytz.UTC, or pytz.timezone('UTC').\n\n>>> import pickle, pytz\n>>> dt = datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc)\n>>> naive = dt.replace(tzinfo=None)\n>>> p = pickle.dumps(dt, 1)\n>>> naive_p = pickle.dumps(naive, 1)\n>>> len(p) - len(naive_p)\n17\n>>> new = pickle.loads(p)\n>>> new == dt\nTrue\n>>> new is dt\nFalse\n>>> new.tzinfo is dt.tzinfo\nTrue\n>>> pytz.utc is pytz.UTC is pytz.timezone('UTC')\nTrue\n\nNote that some other timezones are commonly thought of as the same (GMT,\nGreenwich, Universal, etc.). The definition of UTC is distinct from these\nother timezones, and they are not equivalent. For this reason, they will\nnot compare the same in Python.\n\n>>> utc == pytz.timezone('GMT')\nFalse\n\nSee the section `What is UTC`_, below.\n\nIf you insist on working with local times, this library provides a\nfacility for constructing them unambiguously:\n\n>>> loc_dt = datetime(2002, 10, 27, 1, 30, 00)\n>>> est_dt = eastern.localize(loc_dt, is_dst=True)\n>>> edt_dt = eastern.localize(loc_dt, is_dst=False)\n>>> print(est_dt.strftime(fmt) + ' / ' + edt_dt.strftime(fmt))\n2002-10-27 01:30:00 EDT-0400 / 2002-10-27 01:30:00 EST-0500\n\nIf you pass None as the is_dst flag to localize(), pytz will refuse to\nguess and raise exceptions if you try to build ambiguous or non-existent\ntimes.\n\nFor example, 1:30am on 27th Oct 2002 happened twice in the US/Eastern\ntimezone when the clocks where put back at the end of Daylight Saving\nTime:\n\n>>> dt = datetime(2002, 10, 27, 1, 30, 00)\n>>> try:\n... eastern.localize(dt, is_dst=None)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % dt)\npytz.exceptions.AmbiguousTimeError: 2002-10-27 01:30:00\n\nSimilarly, 2:30am on 7th April 2002 never happened at all in the\nUS/Eastern timezone, as the clocks where put forward at 2:00am skipping\nthe entire hour:\n\n>>> dt = datetime(2002, 4, 7, 2, 30, 00)\n>>> try:\n... eastern.localize(dt, is_dst=None)\n... except pytz.exceptions.NonExistentTimeError:\n... print('pytz.exceptions.NonExistentTimeError: %s' % dt)\npytz.exceptions.NonExistentTimeError: 2002-04-07 02:30:00\n\nBoth of these exceptions share a common base class to make error handling\neasier:\n\n>>> isinstance(pytz.AmbiguousTimeError(), pytz.InvalidTimeError)\nTrue\n>>> isinstance(pytz.NonExistentTimeError(), pytz.InvalidTimeError)\nTrue\n\n\nA special case is where countries change their timezone definitions\nwith no daylight savings time switch. For example, in 1915 Warsaw\nswitched from Warsaw time to Central European time with no daylight savings\ntransition. So at the stroke of midnight on August 5th 1915 the clocks\nwere wound back 24 minutes creating an ambiguous time period that cannot\nbe specified without referring to the timezone abbreviation or the\nactual UTC offset. In this case midnight happened twice, neither time\nduring a daylight saving time period. pytz handles this transition by\ntreating the ambiguous period before the switch as daylight savings\ntime, and the ambiguous period after as standard time.\n\n\n>>> warsaw = pytz.timezone('Europe/Warsaw')\n>>> amb_dt1 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=True)\n>>> amb_dt1.strftime(fmt)\n'1915-08-04 23:59:59 WMT+0124'\n>>> amb_dt2 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=False)\n>>> amb_dt2.strftime(fmt)\n'1915-08-04 23:59:59 CET+0100'\n>>> switch_dt = warsaw.localize(datetime(1915, 8, 5, 00, 00, 00), is_dst=False)\n>>> switch_dt.strftime(fmt)\n'1915-08-05 00:00:00 CET+0100'\n>>> str(switch_dt - amb_dt1)\n'0:24:01'\n>>> str(switch_dt - amb_dt2)\n'0:00:01'\n\nThe best way of creating a time during an ambiguous time period is\nby converting from another timezone such as UTC:\n\n>>> utc_dt = datetime(1915, 8, 4, 22, 36, tzinfo=pytz.utc)\n>>> utc_dt.astimezone(warsaw).strftime(fmt)\n'1915-08-04 23:36:00 CET+0100'\n\nThe standard Python way of handling all these ambiguities is not to\nhandle them, such as demonstrated in this example using the US/Eastern\ntimezone definition from the Python documentation (Note that this\nimplementation only works for dates between 1987 and 2006 - it is\nincluded for tests only!):\n\n>>> from pytz.reference import Eastern # pytz.reference only for tests\n>>> dt = datetime(2002, 10, 27, 0, 30, tzinfo=Eastern)\n>>> str(dt)\n'2002-10-27 00:30:00-04:00'\n>>> str(dt + timedelta(hours=1))\n'2002-10-27 01:30:00-05:00'\n>>> str(dt + timedelta(hours=2))\n'2002-10-27 02:30:00-05:00'\n>>> str(dt + timedelta(hours=3))\n'2002-10-27 03:30:00-05:00'\n\nNotice the first two results? At first glance you might think they are\ncorrect, but taking the UTC offset into account you find that they are\nactually two hours appart instead of the 1 hour we asked for.\n\n>>> from pytz.reference import UTC # pytz.reference only for tests\n>>> str(dt.astimezone(UTC))\n'2002-10-27 04:30:00+00:00'\n>>> str((dt + timedelta(hours=1)).astimezone(UTC))\n'2002-10-27 06:30:00+00:00'\n\n\nCountry Information\n~~~~~~~~~~~~~~~~~~~\n\nA mechanism is provided to access the timezones commonly in use\nfor a particular country, looked up using the ISO 3166 country code.\nIt returns a list of strings that can be used to retrieve the relevant\ntzinfo instance using ``pytz.timezone()``:\n\n>>> print(' '.join(pytz.country_timezones['nz']))\nPacific/Auckland Pacific/Chatham\n\nThe Olson database comes with a ISO 3166 country code to English country\nname mapping that pytz exposes as a dictionary:\n\n>>> print(pytz.country_names['nz'])\nNew Zealand\n\n\nWhat is UTC\n~~~~~~~~~~~\n\n'UTC' is `Coordinated Universal Time`_. It is a successor to, but distinct\nfrom, Greenwich Mean Time (GMT) and the various definitions of Universal\nTime. UTC is now the worldwide standard for regulating clocks and time\nmeasurement.\n\nAll other timezones are defined relative to UTC, and include offsets like\nUTC+0800 - hours to add or subtract from UTC to derive the local time. No\ndaylight saving time occurs in UTC, making it a useful timezone to perform\ndate arithmetic without worrying about the confusion and ambiguities caused\nby daylight saving time transitions, your country changing its timezone, or\nmobile computers that roam through multiple timezones.\n\n.. _Coordinated Universal Time: https://en.wikipedia.org/wiki/Coordinated_Universal_Time\n\n\nHelpers\n~~~~~~~\n\nThere are two lists of timezones provided.\n\n``all_timezones`` is the exhaustive list of the timezone names that can\nbe used.\n\n>>> from pytz import all_timezones\n>>> len(all_timezones) >= 500\nTrue\n>>> 'Etc/Greenwich' in all_timezones\nTrue\n\n``common_timezones`` is a list of useful, current timezones. It doesn't\ncontain deprecated zones or historical zones, except for a few I've\ndeemed in common usage, such as US/Eastern (open a bug report if you\nthink other timezones are deserving of being included here). It is also\na sequence of strings.\n\n>>> from pytz import common_timezones\n>>> len(common_timezones) < len(all_timezones)\nTrue\n>>> 'Etc/Greenwich' in common_timezones\nFalse\n>>> 'Australia/Melbourne' in common_timezones\nTrue\n>>> 'US/Eastern' in common_timezones\nTrue\n>>> 'Canada/Eastern' in common_timezones\nTrue\n>>> 'Australia/Yancowinna' in all_timezones\nTrue\n>>> 'Australia/Yancowinna' in common_timezones\nFalse\n\nBoth ``common_timezones`` and ``all_timezones`` are alphabetically\nsorted:\n\n>>> common_timezones_dupe = common_timezones[:]\n>>> common_timezones_dupe.sort()\n>>> common_timezones == common_timezones_dupe\nTrue\n>>> all_timezones_dupe = all_timezones[:]\n>>> all_timezones_dupe.sort()\n>>> all_timezones == all_timezones_dupe\nTrue\n\n``all_timezones`` and ``common_timezones`` are also available as sets.\n\n>>> from pytz import all_timezones_set, common_timezones_set\n>>> 'US/Eastern' in all_timezones_set\nTrue\n>>> 'US/Eastern' in common_timezones_set\nTrue\n>>> 'Australia/Victoria' in common_timezones_set\nFalse\n\nYou can also retrieve lists of timezones used by particular countries\nusing the ``country_timezones()`` function. It requires an ISO-3166\ntwo letter country code.\n\n>>> from pytz import country_timezones\n>>> print(' '.join(country_timezones('ch')))\nEurope/Zurich\n>>> print(' '.join(country_timezones('CH')))\nEurope/Zurich\n\n\nInternationalization - i18n/l10n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPytz is an interface to the IANA database, which uses ASCII names. The `Unicode Consortium's Unicode Locales (CLDR) `_\nproject provides translations. Thomas Khyn's\n`l18n `_ package can be used to access\nthese translations from Python.\n\n\nLicense\n~~~~~~~\n\nMIT license.\n\nThis code is also available as part of Zope 3 under the Zope Public\nLicense, Version 2.1 (ZPL).\n\nI'm happy to relicense this code if necessary for inclusion in other\nopen source projects.\n\n\nLatest Versions\n~~~~~~~~~~~~~~~\n\nThis package will be updated after releases of the Olson timezone\ndatabase. The latest version can be downloaded from the `Python Package\nIndex `_. The code that is used\nto generate this distribution is hosted on launchpad.net and available\nusing git::\n\n git clone https://git.launchpad.net/pytz\n\nA mirror on github is also available at https://github.com/stub42/pytz\n\nAnnouncements of new releases are made on\n`Launchpad `_, and the\n`Atom feed `_\nhosted there.\n\n\nBugs, Feature Requests & Patches\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBugs can be reported using `Launchpad Bugs `_.\n\n\nSecurity Issues\n~~~~~~~~~~~~~~~\n\nReports about security issues can be made via `Tidelift `_.\n\n\nIssues & Limitations\n~~~~~~~~~~~~~~~~~~~~\n\n- Offsets from UTC are rounded to the nearest whole minute, so timezones\n such as Europe/Amsterdam pre 1937 will be up to 30 seconds out. This\n is a limitation of the Python datetime library.\n\n- If you think a timezone definition is incorrect, I probably can't fix\n it. pytz is a direct translation of the Olson timezone database, and\n changes to the timezone definitions need to be made to this source.\n If you find errors they should be reported to the time zone mailing\n list, linked from http://www.iana.org/time-zones.\n\n\nFurther Reading\n~~~~~~~~~~~~~~~\n\nMore info than you want to know about timezones:\nhttps://data.iana.org/time-zones/tz-link.html\n\n\nContact\n~~~~~~~\n\nStuart Bishop \n\n\n\n\n", + "release_date": "2022-08-13T02:07:57", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Stuart Bishop", + "email": "stuart@stuartbishop.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Stuart Bishop", + "email": "stuart@stuartbishop.net", + "url": null + } + ], + "keywords": "timezone,tzinfo,datetime,olson,time", + "homepage_url": "http://pythonhosted.org/pytz", + "download_url": "https://files.pythonhosted.org/packages/d5/50/54451e88e3da4616286029a3a17fc377de817f66a0f50e1faaee90161724/pytz-2022.2.1-py2.py3-none-any.whl", + "size": 500564, + "sha1": null, + "md5": "8db17bd6a3c4dfb4df5424cdb8434e01", + "sha256": "220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pytz/2022.2.1/json", + "datasource_id": null, + "purl": "pkg:pypi/pytz@2022.2.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "pytz", + "version": "2022.2.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "pytz - World Timezone Definitions for Python\n============================================\n\n:Author: Stuart Bishop \n\nIntroduction\n~~~~~~~~~~~~\n\npytz brings the Olson tz database into Python. This library allows\naccurate and cross platform timezone calculations using Python 2.4\nor higher. It also solves the issue of ambiguous times at the end\nof daylight saving time, which you can read more about in the Python\nLibrary Reference (``datetime.tzinfo``).\n\nAlmost all of the Olson timezones are supported.\n\n.. note::\n\n This library differs from the documented Python API for\n tzinfo implementations; if you want to create local wallclock\n times you need to use the ``localize()`` method documented in this\n document. In addition, if you perform date arithmetic on local\n times that cross DST boundaries, the result may be in an incorrect\n timezone (ie. subtract 1 minute from 2002-10-27 1:00 EST and you get\n 2002-10-27 0:59 EST instead of the correct 2002-10-27 1:59 EDT). A\n ``normalize()`` method is provided to correct this. Unfortunately these\n issues cannot be resolved without modifying the Python datetime\n implementation (see PEP-431).\n\n\nInstallation\n~~~~~~~~~~~~\n\nThis package can either be installed using ``pip`` or from a tarball using the\nstandard Python distutils.\n\nIf you are installing using ``pip``, you don't need to download anything as the\nlatest version will be downloaded for you from PyPI::\n\n pip install pytz\n\nIf you are installing from a tarball, run the following command as an\nadministrative user::\n\n python setup.py install\n\n\npytz for Enterprise\n~~~~~~~~~~~~~~~~~~~\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of pytz and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. `Learn more. `_.\n\n\nExample & Usage\n~~~~~~~~~~~~~~~\n\nLocalized times and date arithmetic\n-----------------------------------\n\n>>> from datetime import datetime, timedelta\n>>> from pytz import timezone\n>>> import pytz\n>>> utc = pytz.utc\n>>> utc.zone\n'UTC'\n>>> eastern = timezone('US/Eastern')\n>>> eastern.zone\n'US/Eastern'\n>>> amsterdam = timezone('Europe/Amsterdam')\n>>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'\n\nThis library only supports two ways of building a localized time. The\nfirst is to use the ``localize()`` method provided by the pytz library.\nThis is used to localize a naive datetime (datetime with no timezone\ninformation):\n\n>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))\n>>> print(loc_dt.strftime(fmt))\n2002-10-27 06:00:00 EST-0500\n\nThe second way of building a localized time is by converting an existing\nlocalized time using the standard ``astimezone()`` method:\n\n>>> ams_dt = loc_dt.astimezone(amsterdam)\n>>> ams_dt.strftime(fmt)\n'2002-10-27 12:00:00 CET+0100'\n\nUnfortunately using the tzinfo argument of the standard datetime\nconstructors ''does not work'' with pytz for many timezones.\n\n>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) # /!\\ Does not work this way!\n'2002-10-27 12:00:00 LMT+0018'\n\nIt is safe for timezones without daylight saving transitions though, such\nas UTC:\n\n>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt) # /!\\ Not recommended except for UTC\n'2002-10-27 12:00:00 UTC+0000'\n\nThe preferred way of dealing with times is to always work in UTC,\nconverting to localtime only when generating output to be read\nby humans.\n\n>>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)\n>>> loc_dt = utc_dt.astimezone(eastern)\n>>> loc_dt.strftime(fmt)\n'2002-10-27 01:00:00 EST-0500'\n\nThis library also allows you to do date arithmetic using local\ntimes, although it is more complicated than working in UTC as you\nneed to use the ``normalize()`` method to handle daylight saving time\nand other timezone transitions. In this example, ``loc_dt`` is set\nto the instant when daylight saving time ends in the US/Eastern\ntimezone.\n\n>>> before = loc_dt - timedelta(minutes=10)\n>>> before.strftime(fmt)\n'2002-10-27 00:50:00 EST-0500'\n>>> eastern.normalize(before).strftime(fmt)\n'2002-10-27 01:50:00 EDT-0400'\n>>> after = eastern.normalize(before + timedelta(minutes=20))\n>>> after.strftime(fmt)\n'2002-10-27 01:10:00 EST-0500'\n\nCreating local times is also tricky, and the reason why working with\nlocal times is not recommended. Unfortunately, you cannot just pass\na ``tzinfo`` argument when constructing a datetime (see the next\nsection for more details)\n\n>>> dt = datetime(2002, 10, 27, 1, 30, 0)\n>>> dt1 = eastern.localize(dt, is_dst=True)\n>>> dt1.strftime(fmt)\n'2002-10-27 01:30:00 EDT-0400'\n>>> dt2 = eastern.localize(dt, is_dst=False)\n>>> dt2.strftime(fmt)\n'2002-10-27 01:30:00 EST-0500'\n\nConverting between timezones is more easily done, using the\nstandard astimezone method.\n\n>>> utc_dt = utc.localize(datetime.utcfromtimestamp(1143408899))\n>>> utc_dt.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n>>> au_tz = timezone('Australia/Sydney')\n>>> au_dt = utc_dt.astimezone(au_tz)\n>>> au_dt.strftime(fmt)\n'2006-03-27 08:34:59 AEDT+1100'\n>>> utc_dt2 = au_dt.astimezone(utc)\n>>> utc_dt2.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n>>> utc_dt == utc_dt2\nTrue\n\nYou can take shortcuts when dealing with the UTC side of timezone\nconversions. ``normalize()`` and ``localize()`` are not really\nnecessary when there are no daylight saving time transitions to\ndeal with.\n\n>>> utc_dt = datetime.utcfromtimestamp(1143408899).replace(tzinfo=utc)\n>>> utc_dt.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n>>> au_tz = timezone('Australia/Sydney')\n>>> au_dt = au_tz.normalize(utc_dt.astimezone(au_tz))\n>>> au_dt.strftime(fmt)\n'2006-03-27 08:34:59 AEDT+1100'\n>>> utc_dt2 = au_dt.astimezone(utc)\n>>> utc_dt2.strftime(fmt)\n'2006-03-26 21:34:59 UTC+0000'\n\n\n``tzinfo`` API\n--------------\n\nThe ``tzinfo`` instances returned by the ``timezone()`` function have\nbeen extended to cope with ambiguous times by adding an ``is_dst``\nparameter to the ``utcoffset()``, ``dst()`` && ``tzname()`` methods.\n\n>>> tz = timezone('America/St_Johns')\n\n>>> normal = datetime(2009, 9, 1)\n>>> ambiguous = datetime(2009, 10, 31, 23, 30)\n\nThe ``is_dst`` parameter is ignored for most timestamps. It is only used\nduring DST transition ambiguous periods to resolve that ambiguity.\n\n>>> print(tz.utcoffset(normal, is_dst=True))\n-1 day, 21:30:00\n>>> print(tz.dst(normal, is_dst=True))\n1:00:00\n>>> tz.tzname(normal, is_dst=True)\n'NDT'\n\n>>> print(tz.utcoffset(ambiguous, is_dst=True))\n-1 day, 21:30:00\n>>> print(tz.dst(ambiguous, is_dst=True))\n1:00:00\n>>> tz.tzname(ambiguous, is_dst=True)\n'NDT'\n\n>>> print(tz.utcoffset(normal, is_dst=False))\n-1 day, 21:30:00\n>>> tz.dst(normal, is_dst=False).seconds\n3600\n>>> tz.tzname(normal, is_dst=False)\n'NDT'\n\n>>> print(tz.utcoffset(ambiguous, is_dst=False))\n-1 day, 20:30:00\n>>> tz.dst(ambiguous, is_dst=False)\ndatetime.timedelta(0)\n>>> tz.tzname(ambiguous, is_dst=False)\n'NST'\n\nIf ``is_dst`` is not specified, ambiguous timestamps will raise\nan ``pytz.exceptions.AmbiguousTimeError`` exception.\n\n>>> print(tz.utcoffset(normal))\n-1 day, 21:30:00\n>>> print(tz.dst(normal))\n1:00:00\n>>> tz.tzname(normal)\n'NDT'\n\n>>> import pytz.exceptions\n>>> try:\n... tz.utcoffset(ambiguous)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)\npytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00\n>>> try:\n... tz.dst(ambiguous)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)\npytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00\n>>> try:\n... tz.tzname(ambiguous)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)\npytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00\n\n\nProblems with Localtime\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThe major problem we have to deal with is that certain datetimes\nmay occur twice in a year. For example, in the US/Eastern timezone\non the last Sunday morning in October, the following sequence\nhappens:\n\n - 01:00 EDT occurs\n - 1 hour later, instead of 2:00am the clock is turned back 1 hour\n and 01:00 happens again (this time 01:00 EST)\n\nIn fact, every instant between 01:00 and 02:00 occurs twice. This means\nthat if you try and create a time in the 'US/Eastern' timezone\nthe standard datetime syntax, there is no way to specify if you meant\nbefore of after the end-of-daylight-saving-time transition. Using the\npytz custom syntax, the best you can do is make an educated guess:\n\n>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 1, 30, 00))\n>>> loc_dt.strftime(fmt)\n'2002-10-27 01:30:00 EST-0500'\n\nAs you can see, the system has chosen one for you and there is a 50%\nchance of it being out by one hour. For some applications, this does\nnot matter. However, if you are trying to schedule meetings with people\nin different timezones or analyze log files it is not acceptable.\n\nThe best and simplest solution is to stick with using UTC. The pytz\npackage encourages using UTC for internal timezone representation by\nincluding a special UTC implementation based on the standard Python\nreference implementation in the Python documentation.\n\nThe UTC timezone unpickles to be the same instance, and pickles to a\nsmaller size than other pytz tzinfo instances. The UTC implementation\ncan be obtained as pytz.utc, pytz.UTC, or pytz.timezone('UTC').\n\n>>> import pickle, pytz\n>>> dt = datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc)\n>>> naive = dt.replace(tzinfo=None)\n>>> p = pickle.dumps(dt, 1)\n>>> naive_p = pickle.dumps(naive, 1)\n>>> len(p) - len(naive_p)\n17\n>>> new = pickle.loads(p)\n>>> new == dt\nTrue\n>>> new is dt\nFalse\n>>> new.tzinfo is dt.tzinfo\nTrue\n>>> pytz.utc is pytz.UTC is pytz.timezone('UTC')\nTrue\n\nNote that some other timezones are commonly thought of as the same (GMT,\nGreenwich, Universal, etc.). The definition of UTC is distinct from these\nother timezones, and they are not equivalent. For this reason, they will\nnot compare the same in Python.\n\n>>> utc == pytz.timezone('GMT')\nFalse\n\nSee the section `What is UTC`_, below.\n\nIf you insist on working with local times, this library provides a\nfacility for constructing them unambiguously:\n\n>>> loc_dt = datetime(2002, 10, 27, 1, 30, 00)\n>>> est_dt = eastern.localize(loc_dt, is_dst=True)\n>>> edt_dt = eastern.localize(loc_dt, is_dst=False)\n>>> print(est_dt.strftime(fmt) + ' / ' + edt_dt.strftime(fmt))\n2002-10-27 01:30:00 EDT-0400 / 2002-10-27 01:30:00 EST-0500\n\nIf you pass None as the is_dst flag to localize(), pytz will refuse to\nguess and raise exceptions if you try to build ambiguous or non-existent\ntimes.\n\nFor example, 1:30am on 27th Oct 2002 happened twice in the US/Eastern\ntimezone when the clocks where put back at the end of Daylight Saving\nTime:\n\n>>> dt = datetime(2002, 10, 27, 1, 30, 00)\n>>> try:\n... eastern.localize(dt, is_dst=None)\n... except pytz.exceptions.AmbiguousTimeError:\n... print('pytz.exceptions.AmbiguousTimeError: %s' % dt)\npytz.exceptions.AmbiguousTimeError: 2002-10-27 01:30:00\n\nSimilarly, 2:30am on 7th April 2002 never happened at all in the\nUS/Eastern timezone, as the clocks where put forward at 2:00am skipping\nthe entire hour:\n\n>>> dt = datetime(2002, 4, 7, 2, 30, 00)\n>>> try:\n... eastern.localize(dt, is_dst=None)\n... except pytz.exceptions.NonExistentTimeError:\n... print('pytz.exceptions.NonExistentTimeError: %s' % dt)\npytz.exceptions.NonExistentTimeError: 2002-04-07 02:30:00\n\nBoth of these exceptions share a common base class to make error handling\neasier:\n\n>>> isinstance(pytz.AmbiguousTimeError(), pytz.InvalidTimeError)\nTrue\n>>> isinstance(pytz.NonExistentTimeError(), pytz.InvalidTimeError)\nTrue\n\n\nA special case is where countries change their timezone definitions\nwith no daylight savings time switch. For example, in 1915 Warsaw\nswitched from Warsaw time to Central European time with no daylight savings\ntransition. So at the stroke of midnight on August 5th 1915 the clocks\nwere wound back 24 minutes creating an ambiguous time period that cannot\nbe specified without referring to the timezone abbreviation or the\nactual UTC offset. In this case midnight happened twice, neither time\nduring a daylight saving time period. pytz handles this transition by\ntreating the ambiguous period before the switch as daylight savings\ntime, and the ambiguous period after as standard time.\n\n\n>>> warsaw = pytz.timezone('Europe/Warsaw')\n>>> amb_dt1 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=True)\n>>> amb_dt1.strftime(fmt)\n'1915-08-04 23:59:59 WMT+0124'\n>>> amb_dt2 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=False)\n>>> amb_dt2.strftime(fmt)\n'1915-08-04 23:59:59 CET+0100'\n>>> switch_dt = warsaw.localize(datetime(1915, 8, 5, 00, 00, 00), is_dst=False)\n>>> switch_dt.strftime(fmt)\n'1915-08-05 00:00:00 CET+0100'\n>>> str(switch_dt - amb_dt1)\n'0:24:01'\n>>> str(switch_dt - amb_dt2)\n'0:00:01'\n\nThe best way of creating a time during an ambiguous time period is\nby converting from another timezone such as UTC:\n\n>>> utc_dt = datetime(1915, 8, 4, 22, 36, tzinfo=pytz.utc)\n>>> utc_dt.astimezone(warsaw).strftime(fmt)\n'1915-08-04 23:36:00 CET+0100'\n\nThe standard Python way of handling all these ambiguities is not to\nhandle them, such as demonstrated in this example using the US/Eastern\ntimezone definition from the Python documentation (Note that this\nimplementation only works for dates between 1987 and 2006 - it is\nincluded for tests only!):\n\n>>> from pytz.reference import Eastern # pytz.reference only for tests\n>>> dt = datetime(2002, 10, 27, 0, 30, tzinfo=Eastern)\n>>> str(dt)\n'2002-10-27 00:30:00-04:00'\n>>> str(dt + timedelta(hours=1))\n'2002-10-27 01:30:00-05:00'\n>>> str(dt + timedelta(hours=2))\n'2002-10-27 02:30:00-05:00'\n>>> str(dt + timedelta(hours=3))\n'2002-10-27 03:30:00-05:00'\n\nNotice the first two results? At first glance you might think they are\ncorrect, but taking the UTC offset into account you find that they are\nactually two hours appart instead of the 1 hour we asked for.\n\n>>> from pytz.reference import UTC # pytz.reference only for tests\n>>> str(dt.astimezone(UTC))\n'2002-10-27 04:30:00+00:00'\n>>> str((dt + timedelta(hours=1)).astimezone(UTC))\n'2002-10-27 06:30:00+00:00'\n\n\nCountry Information\n~~~~~~~~~~~~~~~~~~~\n\nA mechanism is provided to access the timezones commonly in use\nfor a particular country, looked up using the ISO 3166 country code.\nIt returns a list of strings that can be used to retrieve the relevant\ntzinfo instance using ``pytz.timezone()``:\n\n>>> print(' '.join(pytz.country_timezones['nz']))\nPacific/Auckland Pacific/Chatham\n\nThe Olson database comes with a ISO 3166 country code to English country\nname mapping that pytz exposes as a dictionary:\n\n>>> print(pytz.country_names['nz'])\nNew Zealand\n\n\nWhat is UTC\n~~~~~~~~~~~\n\n'UTC' is `Coordinated Universal Time`_. It is a successor to, but distinct\nfrom, Greenwich Mean Time (GMT) and the various definitions of Universal\nTime. UTC is now the worldwide standard for regulating clocks and time\nmeasurement.\n\nAll other timezones are defined relative to UTC, and include offsets like\nUTC+0800 - hours to add or subtract from UTC to derive the local time. No\ndaylight saving time occurs in UTC, making it a useful timezone to perform\ndate arithmetic without worrying about the confusion and ambiguities caused\nby daylight saving time transitions, your country changing its timezone, or\nmobile computers that roam through multiple timezones.\n\n.. _Coordinated Universal Time: https://en.wikipedia.org/wiki/Coordinated_Universal_Time\n\n\nHelpers\n~~~~~~~\n\nThere are two lists of timezones provided.\n\n``all_timezones`` is the exhaustive list of the timezone names that can\nbe used.\n\n>>> from pytz import all_timezones\n>>> len(all_timezones) >= 500\nTrue\n>>> 'Etc/Greenwich' in all_timezones\nTrue\n\n``common_timezones`` is a list of useful, current timezones. It doesn't\ncontain deprecated zones or historical zones, except for a few I've\ndeemed in common usage, such as US/Eastern (open a bug report if you\nthink other timezones are deserving of being included here). It is also\na sequence of strings.\n\n>>> from pytz import common_timezones\n>>> len(common_timezones) < len(all_timezones)\nTrue\n>>> 'Etc/Greenwich' in common_timezones\nFalse\n>>> 'Australia/Melbourne' in common_timezones\nTrue\n>>> 'US/Eastern' in common_timezones\nTrue\n>>> 'Canada/Eastern' in common_timezones\nTrue\n>>> 'Australia/Yancowinna' in all_timezones\nTrue\n>>> 'Australia/Yancowinna' in common_timezones\nFalse\n\nBoth ``common_timezones`` and ``all_timezones`` are alphabetically\nsorted:\n\n>>> common_timezones_dupe = common_timezones[:]\n>>> common_timezones_dupe.sort()\n>>> common_timezones == common_timezones_dupe\nTrue\n>>> all_timezones_dupe = all_timezones[:]\n>>> all_timezones_dupe.sort()\n>>> all_timezones == all_timezones_dupe\nTrue\n\n``all_timezones`` and ``common_timezones`` are also available as sets.\n\n>>> from pytz import all_timezones_set, common_timezones_set\n>>> 'US/Eastern' in all_timezones_set\nTrue\n>>> 'US/Eastern' in common_timezones_set\nTrue\n>>> 'Australia/Victoria' in common_timezones_set\nFalse\n\nYou can also retrieve lists of timezones used by particular countries\nusing the ``country_timezones()`` function. It requires an ISO-3166\ntwo letter country code.\n\n>>> from pytz import country_timezones\n>>> print(' '.join(country_timezones('ch')))\nEurope/Zurich\n>>> print(' '.join(country_timezones('CH')))\nEurope/Zurich\n\n\nInternationalization - i18n/l10n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPytz is an interface to the IANA database, which uses ASCII names. The `Unicode Consortium's Unicode Locales (CLDR) `_\nproject provides translations. Thomas Khyn's\n`l18n `_ package can be used to access\nthese translations from Python.\n\n\nLicense\n~~~~~~~\n\nMIT license.\n\nThis code is also available as part of Zope 3 under the Zope Public\nLicense, Version 2.1 (ZPL).\n\nI'm happy to relicense this code if necessary for inclusion in other\nopen source projects.\n\n\nLatest Versions\n~~~~~~~~~~~~~~~\n\nThis package will be updated after releases of the Olson timezone\ndatabase. The latest version can be downloaded from the `Python Package\nIndex `_. The code that is used\nto generate this distribution is hosted on launchpad.net and available\nusing git::\n\n git clone https://git.launchpad.net/pytz\n\nA mirror on github is also available at https://github.com/stub42/pytz\n\nAnnouncements of new releases are made on\n`Launchpad `_, and the\n`Atom feed `_\nhosted there.\n\n\nBugs, Feature Requests & Patches\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBugs can be reported using `Launchpad Bugs `_.\n\n\nSecurity Issues\n~~~~~~~~~~~~~~~\n\nReports about security issues can be made via `Tidelift `_.\n\n\nIssues & Limitations\n~~~~~~~~~~~~~~~~~~~~\n\n- Offsets from UTC are rounded to the nearest whole minute, so timezones\n such as Europe/Amsterdam pre 1937 will be up to 30 seconds out. This\n is a limitation of the Python datetime library.\n\n- If you think a timezone definition is incorrect, I probably can't fix\n it. pytz is a direct translation of the Olson timezone database, and\n changes to the timezone definitions need to be made to this source.\n If you find errors they should be reported to the time zone mailing\n list, linked from http://www.iana.org/time-zones.\n\n\nFurther Reading\n~~~~~~~~~~~~~~~\n\nMore info than you want to know about timezones:\nhttps://data.iana.org/time-zones/tz-link.html\n\n\nContact\n~~~~~~~\n\nStuart Bishop \n\n\n\n\n", + "release_date": "2022-08-13T02:07:59", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Stuart Bishop", + "email": "stuart@stuartbishop.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Stuart Bishop", + "email": "stuart@stuartbishop.net", + "url": null + } + ], + "keywords": "timezone,tzinfo,datetime,olson,time", + "homepage_url": "http://pythonhosted.org/pytz", + "download_url": "https://files.pythonhosted.org/packages/24/0c/401283bb1499768e33ddd2e1a35817c775405c1f047a9dc088a29ce2ea5d/pytz-2022.2.1.tar.gz", + "size": 316105, + "sha1": null, + "md5": "5c7aa995be1a0091df9774502f84be4b", + "sha256": "cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pytz/2022.2.1/json", + "datasource_id": null, + "purl": "pkg:pypi/pytz@2022.2.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "redis", + "version": "3.5.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "redis-py\n========\n\nThe Python interface to the Redis key-value store.\n\n.. image:: https://secure.travis-ci.org/andymccurdy/redis-py.svg?branch=master\n :target: https://travis-ci.org/andymccurdy/redis-py\n.. image:: https://readthedocs.org/projects/redis-py/badge/?version=stable&style=flat\n :target: https://redis-py.readthedocs.io/en/stable/\n.. image:: https://badge.fury.io/py/redis.svg\n :target: https://pypi.org/project/redis/\n.. image:: https://codecov.io/gh/andymccurdy/redis-py/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/andymccurdy/redis-py\n\n\nPython 2 Compatibility Note\n---------------------------\n\nredis-py 3.5.x will be the last version of redis-py that supports Python 2.\nThe 3.5.x line will continue to get bug fixes and security patches that\nsupport Python 2 until August 1, 2020. redis-py 4.0 will be the next major\nversion and will require Python 3.5+.\n\n\nInstallation\n------------\n\nredis-py requires a running Redis server. See `Redis's quickstart\n`_ for installation instructions.\n\nredis-py can be installed using `pip` similar to other Python packages. Do not use `sudo`\nwith `pip`. It is usually good to work in a\n`virtualenv `_ or\n`venv `_ to avoid conflicts with other package\nmanagers and Python projects. For a quick introduction see\n`Python Virtual Environments in Five Minutes `_.\n\nTo install redis-py, simply:\n\n.. code-block:: bash\n\n $ pip install redis\n\nor from source:\n\n.. code-block:: bash\n\n $ python setup.py install\n\n\nGetting Started\n---------------\n\n.. code-block:: pycon\n\n >>> import redis\n >>> r = redis.Redis(host='localhost', port=6379, db=0)\n >>> r.set('foo', 'bar')\n True\n >>> r.get('foo')\n b'bar'\n\nBy default, all responses are returned as `bytes` in Python 3 and `str` in\nPython 2. The user is responsible for decoding to Python 3 strings or Python 2\nunicode objects.\n\nIf **all** string responses from a client should be decoded, the user can\nspecify `decode_responses=True` to `Redis.__init__`. In this case, any\nRedis command that returns a string type will be decoded with the `encoding`\nspecified.\n\n\nUpgrading from redis-py 2.X to 3.0\n----------------------------------\n\nredis-py 3.0 introduces many new features but required a number of backwards\nincompatible changes to be made in the process. This section attempts to\nprovide an upgrade path for users migrating from 2.X to 3.0.\n\n\nPython Version Support\n^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 supports Python 2.7 and Python 3.5+.\n\n\nClient Classes: Redis and StrictRedis\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 drops support for the legacy \"Redis\" client class. \"StrictRedis\"\nhas been renamed to \"Redis\" and an alias named \"StrictRedis\" is provided so\nthat users previously using \"StrictRedis\" can continue to run unchanged.\n\nThe 2.X \"Redis\" class provided alternative implementations of a few commands.\nThis confused users (rightfully so) and caused a number of support issues. To\nmake things easier going forward, it was decided to drop support for these\nalternate implementations and instead focus on a single client class.\n\n2.X users that are already using StrictRedis don't have to change the class\nname. StrictRedis will continue to work for the foreseeable future.\n\n2.X users that are using the Redis class will have to make changes if they\nuse any of the following commands:\n\n* SETEX: The argument order has changed. The new order is (name, time, value).\n* LREM: The argument order has changed. The new order is (name, num, value).\n* TTL and PTTL: The return value is now always an int and matches the\n official Redis command (>0 indicates the timeout, -1 indicates that the key\n exists but that it has no expire time set, -2 indicates that the key does\n not exist)\n\n\nSSL Connections\n^^^^^^^^^^^^^^^\n\nredis-py 3.0 changes the default value of the `ssl_cert_reqs` option from\n`None` to `'required'`. See\n`Issue 1016 `_. This\nchange enforces hostname validation when accepting a cert from a remote SSL\nterminator. If the terminator doesn't properly set the hostname on the cert\nthis will cause redis-py 3.0 to raise a ConnectionError.\n\nThis check can be disabled by setting `ssl_cert_reqs` to `None`. Note that\ndoing so removes the security check. Do so at your own risk.\n\nIt has been reported that SSL certs received from AWS ElastiCache do not have\nproper hostnames and turning off hostname verification is currently required.\n\n\nMSET, MSETNX and ZADD\n^^^^^^^^^^^^^^^^^^^^^\n\nThese commands all accept a mapping of key/value pairs. In redis-py 2.X\nthis mapping could be specified as ``*args`` or as ``**kwargs``. Both of these\nstyles caused issues when Redis introduced optional flags to ZADD. Relying on\n``*args`` caused issues with the optional argument order, especially in Python\n2.7. Relying on ``**kwargs`` caused potential collision issues of user keys with\nthe argument names in the method signature.\n\nTo resolve this, redis-py 3.0 has changed these three commands to all accept\na single positional argument named mapping that is expected to be a dict. For\nMSET and MSETNX, the dict is a mapping of key-names -> values. For ZADD, the\ndict is a mapping of element-names -> score.\n\nMSET, MSETNX and ZADD now look like:\n\n.. code-block:: python\n\n def mset(self, mapping):\n def msetnx(self, mapping):\n def zadd(self, name, mapping, nx=False, xx=False, ch=False, incr=False):\n\nAll 2.X users that use these commands must modify their code to supply\nkeys and values as a dict to these commands.\n\n\nZINCRBY\n^^^^^^^\n\nredis-py 2.X accidentally modified the argument order of ZINCRBY, swapping the\norder of value and amount. ZINCRBY now looks like:\n\n.. code-block:: python\n\n def zincrby(self, name, amount, value):\n\nAll 2.X users that rely on ZINCRBY must swap the order of amount and value\nfor the command to continue to work as intended.\n\n\nEncoding of User Input\n^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 only accepts user data as bytes, strings or numbers (ints, longs\nand floats). Attempting to specify a key or a value as any other type will\nraise a DataError exception.\n\nredis-py 2.X attempted to coerce any type of input into a string. While\noccasionally convenient, this caused all sorts of hidden errors when users\npassed boolean values (which were coerced to 'True' or 'False'), a None\nvalue (which was coerced to 'None') or other values, such as user defined\ntypes.\n\nAll 2.X users should make sure that the keys and values they pass into\nredis-py are either bytes, strings or numbers.\n\n\nLocks\n^^^^^\n\nredis-py 3.0 drops support for the pipeline-based Lock and now only supports\nthe Lua-based lock. In doing so, LuaLock has been renamed to Lock. This also\nmeans that redis-py Lock objects require Redis server 2.6 or greater.\n\n2.X users that were explicitly referring to \"LuaLock\" will have to now refer\nto \"Lock\" instead.\n\n\nLocks as Context Managers\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 now raises a LockError when using a lock as a context manager and\nthe lock cannot be acquired within the specified timeout. This is more of a\nbug fix than a backwards incompatible change. However, given an error is now\nraised where none was before, this might alarm some users.\n\n2.X users should make sure they're wrapping their lock code in a try/catch\nlike this:\n\n.. code-block:: python\n\n try:\n with r.lock('my-lock-key', blocking_timeout=5) as lock:\n # code you want executed only after the lock has been acquired\n except LockError:\n # the lock wasn't acquired\n\n\nAPI Reference\n-------------\n\nThe `official Redis command documentation `_ does a\ngreat job of explaining each command in detail. redis-py attempts to adhere\nto the official command syntax. There are a few exceptions:\n\n* **SELECT**: Not implemented. See the explanation in the Thread Safety section\n below.\n* **DEL**: 'del' is a reserved keyword in the Python syntax. Therefore redis-py\n uses 'delete' instead.\n* **MULTI/EXEC**: These are implemented as part of the Pipeline class. The\n pipeline is wrapped with the MULTI and EXEC statements by default when it\n is executed, which can be disabled by specifying transaction=False.\n See more about Pipelines below.\n* **SUBSCRIBE/LISTEN**: Similar to pipelines, PubSub is implemented as a separate\n class as it places the underlying connection in a state where it can't\n execute non-pubsub commands. Calling the pubsub method from the Redis client\n will return a PubSub instance where you can subscribe to channels and listen\n for messages. You can only call PUBLISH from the Redis client (see\n `this comment on issue #151\n `_\n for details).\n* **SCAN/SSCAN/HSCAN/ZSCAN**: The \\*SCAN commands are implemented as they\n exist in the Redis documentation. In addition, each command has an equivalent\n iterator method. These are purely for convenience so the user doesn't have\n to keep track of the cursor while iterating. Use the\n scan_iter/sscan_iter/hscan_iter/zscan_iter methods for this behavior.\n\n\nMore Detail\n-----------\n\nConnection Pools\n^^^^^^^^^^^^^^^^\n\nBehind the scenes, redis-py uses a connection pool to manage connections to\na Redis server. By default, each Redis instance you create will in turn create\nits own connection pool. You can override this behavior and use an existing\nconnection pool by passing an already created connection pool instance to the\nconnection_pool argument of the Redis class. You may choose to do this in order\nto implement client side sharding or have fine-grain control of how\nconnections are managed.\n\n.. code-block:: pycon\n\n >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)\n >>> r = redis.Redis(connection_pool=pool)\n\nConnections\n^^^^^^^^^^^\n\nConnectionPools manage a set of Connection instances. redis-py ships with two\ntypes of Connections. The default, Connection, is a normal TCP socket based\nconnection. The UnixDomainSocketConnection allows for clients running on the\nsame device as the server to connect via a unix domain socket. To use a\nUnixDomainSocketConnection connection, simply pass the unix_socket_path\nargument, which is a string to the unix domain socket file. Additionally, make\nsure the unixsocket parameter is defined in your redis.conf file. It's\ncommented out by default.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(unix_socket_path='/tmp/redis.sock')\n\nYou can create your own Connection subclasses as well. This may be useful if\nyou want to control the socket behavior within an async framework. To\ninstantiate a client class using your own connection, you need to create\na connection pool, passing your class to the connection_class argument.\nOther keyword parameters you pass to the pool will be passed to the class\nspecified during initialization.\n\n.. code-block:: pycon\n\n >>> pool = redis.ConnectionPool(connection_class=YourConnectionClass,\n your_arg='...', ...)\n\nConnections maintain an open socket to the Redis server. Sometimes these\nsockets are interrupted or disconnected for a variety of reasons. For example,\nnetwork appliances, load balancers and other services that sit between clients\nand servers are often configured to kill connections that remain idle for a\ngiven threshold.\n\nWhen a connection becomes disconnected, the next command issued on that\nconnection will fail and redis-py will raise a ConnectionError to the caller.\nThis allows each application that uses redis-py to handle errors in a way\nthat's fitting for that specific application. However, constant error\nhandling can be verbose and cumbersome, especially when socket disconnections\nhappen frequently in many production environments.\n\nTo combat this, redis-py can issue regular health checks to assess the\nliveliness of a connection just before issuing a command. Users can pass\n``health_check_interval=N`` to the Redis or ConnectionPool classes or\nas a query argument within a Redis URL. The value of ``health_check_interval``\nmust be an integer. A value of ``0``, the default, disables health checks.\nAny positive integer will enable health checks. Health checks are performed\njust before a command is executed if the underlying connection has been idle\nfor more than ``health_check_interval`` seconds. For example,\n``health_check_interval=30`` will ensure that a health check is run on any\nconnection that has been idle for 30 or more seconds just before a command\nis executed on that connection.\n\nIf your application is running in an environment that disconnects idle\nconnections after 30 seconds you should set the ``health_check_interval``\noption to a value less than 30.\n\nThis option also works on any PubSub connection that is created from a\nclient with ``health_check_interval`` enabled. PubSub users need to ensure\nthat ``get_message()`` or ``listen()`` are called more frequently than\n``health_check_interval`` seconds. It is assumed that most workloads already\ndo this.\n\nIf your PubSub use case doesn't call ``get_message()`` or ``listen()``\nfrequently, you should call ``pubsub.check_health()`` explicitly on a\nregularly basis.\n\nParsers\n^^^^^^^\n\nParser classes provide a way to control how responses from the Redis server\nare parsed. redis-py ships with two parser classes, the PythonParser and the\nHiredisParser. By default, redis-py will attempt to use the HiredisParser if\nyou have the hiredis module installed and will fallback to the PythonParser\notherwise.\n\nHiredis is a C library maintained by the core Redis team. Pieter Noordhuis was\nkind enough to create Python bindings. Using Hiredis can provide up to a\n10x speed improvement in parsing responses from the Redis server. The\nperformance increase is most noticeable when retrieving many pieces of data,\nsuch as from LRANGE or SMEMBERS operations.\n\nHiredis is available on PyPI, and can be installed via pip just like redis-py.\n\n.. code-block:: bash\n\n $ pip install hiredis\n\nResponse Callbacks\n^^^^^^^^^^^^^^^^^^\n\nThe client class uses a set of callbacks to cast Redis responses to the\nappropriate Python type. There are a number of these callbacks defined on\nthe Redis client class in a dictionary called RESPONSE_CALLBACKS.\n\nCustom callbacks can be added on a per-instance basis using the\nset_response_callback method. This method accepts two arguments: a command\nname and the callback. Callbacks added in this manner are only valid on the\ninstance the callback is added to. If you want to define or override a callback\nglobally, you should make a subclass of the Redis client and add your callback\nto its RESPONSE_CALLBACKS class dictionary.\n\nResponse callbacks take at least one parameter: the response from the Redis\nserver. Keyword arguments may also be accepted in order to further control\nhow to interpret the response. These keyword arguments are specified during the\ncommand's call to execute_command. The ZRANGE implementation demonstrates the\nuse of response callback keyword arguments with its \"withscores\" argument.\n\nThread Safety\n^^^^^^^^^^^^^\n\nRedis client instances can safely be shared between threads. Internally,\nconnection instances are only retrieved from the connection pool during\ncommand execution, and returned to the pool directly after. Command execution\nnever modifies state on the client instance.\n\nHowever, there is one caveat: the Redis SELECT command. The SELECT command\nallows you to switch the database currently in use by the connection. That\ndatabase remains selected until another is selected or until the connection is\nclosed. This creates an issue in that connections could be returned to the pool\nthat are connected to a different database.\n\nAs a result, redis-py does not implement the SELECT command on client\ninstances. If you use multiple Redis databases within the same application, you\nshould create a separate client instance (and possibly a separate connection\npool) for each database.\n\nIt is not safe to pass PubSub or Pipeline objects between threads.\n\nPipelines\n^^^^^^^^^\n\nPipelines are a subclass of the base Redis class that provide support for\nbuffering multiple commands to the server in a single request. They can be used\nto dramatically increase the performance of groups of commands by reducing the\nnumber of back-and-forth TCP packets between the client and server.\n\nPipelines are quite simple to use:\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(...)\n >>> r.set('bing', 'baz')\n >>> # Use the pipeline() method to create a pipeline instance\n >>> pipe = r.pipeline()\n >>> # The following SET commands are buffered\n >>> pipe.set('foo', 'bar')\n >>> pipe.get('bing')\n >>> # the EXECUTE call sends all buffered commands to the server, returning\n >>> # a list of responses, one for each command.\n >>> pipe.execute()\n [True, b'baz']\n\nFor ease of use, all commands being buffered into the pipeline return the\npipeline object itself. Therefore calls can be chained like:\n\n.. code-block:: pycon\n\n >>> pipe.set('foo', 'bar').sadd('faz', 'baz').incr('auto_number').execute()\n [True, True, 6]\n\nIn addition, pipelines can also ensure the buffered commands are executed\natomically as a group. This happens by default. If you want to disable the\natomic nature of a pipeline but still want to buffer commands, you can turn\noff transactions.\n\n.. code-block:: pycon\n\n >>> pipe = r.pipeline(transaction=False)\n\nA common issue occurs when requiring atomic transactions but needing to\nretrieve values in Redis prior for use within the transaction. For instance,\nlet's assume that the INCR command didn't exist and we need to build an atomic\nversion of INCR in Python.\n\nThe completely naive implementation could GET the value, increment it in\nPython, and SET the new value back. However, this is not atomic because\nmultiple clients could be doing this at the same time, each getting the same\nvalue from GET.\n\nEnter the WATCH command. WATCH provides the ability to monitor one or more keys\nprior to starting a transaction. If any of those keys change prior the\nexecution of that transaction, the entire transaction will be canceled and a\nWatchError will be raised. To implement our own client-side INCR command, we\ncould do something like this:\n\n.. code-block:: pycon\n\n >>> with r.pipeline() as pipe:\n ... while True:\n ... try:\n ... # put a WATCH on the key that holds our sequence value\n ... pipe.watch('OUR-SEQUENCE-KEY')\n ... # after WATCHing, the pipeline is put into immediate execution\n ... # mode until we tell it to start buffering commands again.\n ... # this allows us to get the current value of our sequence\n ... current_value = pipe.get('OUR-SEQUENCE-KEY')\n ... next_value = int(current_value) + 1\n ... # now we can put the pipeline back into buffered mode with MULTI\n ... pipe.multi()\n ... pipe.set('OUR-SEQUENCE-KEY', next_value)\n ... # and finally, execute the pipeline (the set command)\n ... pipe.execute()\n ... # if a WatchError wasn't raised during execution, everything\n ... # we just did happened atomically.\n ... break\n ... except WatchError:\n ... # another client must have changed 'OUR-SEQUENCE-KEY' between\n ... # the time we started WATCHing it and the pipeline's execution.\n ... # our best bet is to just retry.\n ... continue\n\nNote that, because the Pipeline must bind to a single connection for the\nduration of a WATCH, care must be taken to ensure that the connection is\nreturned to the connection pool by calling the reset() method. If the\nPipeline is used as a context manager (as in the example above) reset()\nwill be called automatically. Of course you can do this the manual way by\nexplicitly calling reset():\n\n.. code-block:: pycon\n\n >>> pipe = r.pipeline()\n >>> while True:\n ... try:\n ... pipe.watch('OUR-SEQUENCE-KEY')\n ... ...\n ... pipe.execute()\n ... break\n ... except WatchError:\n ... continue\n ... finally:\n ... pipe.reset()\n\nA convenience method named \"transaction\" exists for handling all the\nboilerplate of handling and retrying watch errors. It takes a callable that\nshould expect a single parameter, a pipeline object, and any number of keys to\nbe WATCHed. Our client-side INCR command above can be written like this,\nwhich is much easier to read:\n\n.. code-block:: pycon\n\n >>> def client_side_incr(pipe):\n ... current_value = pipe.get('OUR-SEQUENCE-KEY')\n ... next_value = int(current_value) + 1\n ... pipe.multi()\n ... pipe.set('OUR-SEQUENCE-KEY', next_value)\n >>>\n >>> r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')\n [True]\n\nBe sure to call `pipe.multi()` in the callable passed to `Redis.transaction`\nprior to any write commands.\n\nPublish / Subscribe\n^^^^^^^^^^^^^^^^^^^\n\nredis-py includes a `PubSub` object that subscribes to channels and listens\nfor new messages. Creating a `PubSub` object is easy.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(...)\n >>> p = r.pubsub()\n\nOnce a `PubSub` instance is created, channels and patterns can be subscribed\nto.\n\n.. code-block:: pycon\n\n >>> p.subscribe('my-first-channel', 'my-second-channel', ...)\n >>> p.psubscribe('my-*', ...)\n\nThe `PubSub` instance is now subscribed to those channels/patterns. The\nsubscription confirmations can be seen by reading messages from the `PubSub`\ninstance.\n\n.. code-block:: pycon\n\n >>> p.get_message()\n {'pattern': None, 'type': 'subscribe', 'channel': b'my-second-channel', 'data': 1}\n >>> p.get_message()\n {'pattern': None, 'type': 'subscribe', 'channel': b'my-first-channel', 'data': 2}\n >>> p.get_message()\n {'pattern': None, 'type': 'psubscribe', 'channel': b'my-*', 'data': 3}\n\nEvery message read from a `PubSub` instance will be a dictionary with the\nfollowing keys.\n\n* **type**: One of the following: 'subscribe', 'unsubscribe', 'psubscribe',\n 'punsubscribe', 'message', 'pmessage'\n* **channel**: The channel [un]subscribed to or the channel a message was\n published to\n* **pattern**: The pattern that matched a published message's channel. Will be\n `None` in all cases except for 'pmessage' types.\n* **data**: The message data. With [un]subscribe messages, this value will be\n the number of channels and patterns the connection is currently subscribed\n to. With [p]message messages, this value will be the actual published\n message.\n\nLet's send a message now.\n\n.. code-block:: pycon\n\n # the publish method returns the number matching channel and pattern\n # subscriptions. 'my-first-channel' matches both the 'my-first-channel'\n # subscription and the 'my-*' pattern subscription, so this message will\n # be delivered to 2 channels/patterns\n >>> r.publish('my-first-channel', 'some data')\n 2\n >>> p.get_message()\n {'channel': b'my-first-channel', 'data': b'some data', 'pattern': None, 'type': 'message'}\n >>> p.get_message()\n {'channel': b'my-first-channel', 'data': b'some data', 'pattern': b'my-*', 'type': 'pmessage'}\n\nUnsubscribing works just like subscribing. If no arguments are passed to\n[p]unsubscribe, all channels or patterns will be unsubscribed from.\n\n.. code-block:: pycon\n\n >>> p.unsubscribe()\n >>> p.punsubscribe('my-*')\n >>> p.get_message()\n {'channel': b'my-second-channel', 'data': 2, 'pattern': None, 'type': 'unsubscribe'}\n >>> p.get_message()\n {'channel': b'my-first-channel', 'data': 1, 'pattern': None, 'type': 'unsubscribe'}\n >>> p.get_message()\n {'channel': b'my-*', 'data': 0, 'pattern': None, 'type': 'punsubscribe'}\n\nredis-py also allows you to register callback functions to handle published\nmessages. Message handlers take a single argument, the message, which is a\ndictionary just like the examples above. To subscribe to a channel or pattern\nwith a message handler, pass the channel or pattern name as a keyword argument\nwith its value being the callback function.\n\nWhen a message is read on a channel or pattern with a message handler, the\nmessage dictionary is created and passed to the message handler. In this case,\na `None` value is returned from get_message() since the message was already\nhandled.\n\n.. code-block:: pycon\n\n >>> def my_handler(message):\n ... print('MY HANDLER: ', message['data'])\n >>> p.subscribe(**{'my-channel': my_handler})\n # read the subscribe confirmation message\n >>> p.get_message()\n {'pattern': None, 'type': 'subscribe', 'channel': b'my-channel', 'data': 1}\n >>> r.publish('my-channel', 'awesome data')\n 1\n # for the message handler to work, we need tell the instance to read data.\n # this can be done in several ways (read more below). we'll just use\n # the familiar get_message() function for now\n >>> message = p.get_message()\n MY HANDLER: awesome data\n # note here that the my_handler callback printed the string above.\n # `message` is None because the message was handled by our handler.\n >>> print(message)\n None\n\nIf your application is not interested in the (sometimes noisy)\nsubscribe/unsubscribe confirmation messages, you can ignore them by passing\n`ignore_subscribe_messages=True` to `r.pubsub()`. This will cause all\nsubscribe/unsubscribe messages to be read, but they won't bubble up to your\napplication.\n\n.. code-block:: pycon\n\n >>> p = r.pubsub(ignore_subscribe_messages=True)\n >>> p.subscribe('my-channel')\n >>> p.get_message() # hides the subscribe message and returns None\n >>> r.publish('my-channel', 'my data')\n 1\n >>> p.get_message()\n {'channel': b'my-channel', 'data': b'my data', 'pattern': None, 'type': 'message'}\n\nThere are three different strategies for reading messages.\n\nThe examples above have been using `pubsub.get_message()`. Behind the scenes,\n`get_message()` uses the system's 'select' module to quickly poll the\nconnection's socket. If there's data available to be read, `get_message()` will\nread it, format the message and return it or pass it to a message handler. If\nthere's no data to be read, `get_message()` will immediately return None. This\nmakes it trivial to integrate into an existing event loop inside your\napplication.\n\n.. code-block:: pycon\n\n >>> while True:\n >>> message = p.get_message()\n >>> if message:\n >>> # do something with the message\n >>> time.sleep(0.001) # be nice to the system :)\n\nOlder versions of redis-py only read messages with `pubsub.listen()`. listen()\nis a generator that blocks until a message is available. If your application\ndoesn't need to do anything else but receive and act on messages received from\nredis, listen() is an easy way to get up an running.\n\n.. code-block:: pycon\n\n >>> for message in p.listen():\n ... # do something with the message\n\nThe third option runs an event loop in a separate thread.\n`pubsub.run_in_thread()` creates a new thread and starts the event loop. The\nthread object is returned to the caller of `run_in_thread()`. The caller can\nuse the `thread.stop()` method to shut down the event loop and thread. Behind\nthe scenes, this is simply a wrapper around `get_message()` that runs in a\nseparate thread, essentially creating a tiny non-blocking event loop for you.\n`run_in_thread()` takes an optional `sleep_time` argument. If specified, the\nevent loop will call `time.sleep()` with the value in each iteration of the\nloop.\n\nNote: Since we're running in a separate thread, there's no way to handle\nmessages that aren't automatically handled with registered message handlers.\nTherefore, redis-py prevents you from calling `run_in_thread()` if you're\nsubscribed to patterns or channels that don't have message handlers attached.\n\n.. code-block:: pycon\n\n >>> p.subscribe(**{'my-channel': my_handler})\n >>> thread = p.run_in_thread(sleep_time=0.001)\n # the event loop is now running in the background processing messages\n # when it's time to shut it down...\n >>> thread.stop()\n\nA PubSub object adheres to the same encoding semantics as the client instance\nit was created from. Any channel or pattern that's unicode will be encoded\nusing the `charset` specified on the client before being sent to Redis. If the\nclient's `decode_responses` flag is set the False (the default), the\n'channel', 'pattern' and 'data' values in message dictionaries will be byte\nstrings (str on Python 2, bytes on Python 3). If the client's\n`decode_responses` is True, then the 'channel', 'pattern' and 'data' values\nwill be automatically decoded to unicode strings using the client's `charset`.\n\nPubSub objects remember what channels and patterns they are subscribed to. In\nthe event of a disconnection such as a network error or timeout, the\nPubSub object will re-subscribe to all prior channels and patterns when\nreconnecting. Messages that were published while the client was disconnected\ncannot be delivered. When you're finished with a PubSub object, call its\n`.close()` method to shutdown the connection.\n\n.. code-block:: pycon\n\n >>> p = r.pubsub()\n >>> ...\n >>> p.close()\n\n\nThe PUBSUB set of subcommands CHANNELS, NUMSUB and NUMPAT are also\nsupported:\n\n.. code-block:: pycon\n\n >>> r.pubsub_channels()\n [b'foo', b'bar']\n >>> r.pubsub_numsub('foo', 'bar')\n [(b'foo', 9001), (b'bar', 42)]\n >>> r.pubsub_numsub('baz')\n [(b'baz', 0)]\n >>> r.pubsub_numpat()\n 1204\n\nMonitor\n^^^^^^^\nredis-py includes a `Monitor` object that streams every command processed\nby the Redis server. Use `listen()` on the `Monitor` object to block\nuntil a command is received.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(...)\n >>> with r.monitor() as m:\n >>> for command in m.listen():\n >>> print(command)\n\nLua Scripting\n^^^^^^^^^^^^^\n\nredis-py supports the EVAL, EVALSHA, and SCRIPT commands. However, there are\na number of edge cases that make these commands tedious to use in real world\nscenarios. Therefore, redis-py exposes a Script object that makes scripting\nmuch easier to use.\n\nTo create a Script instance, use the `register_script` function on a client\ninstance passing the Lua code as the first argument. `register_script` returns\na Script instance that you can use throughout your code.\n\nThe following trivial Lua script accepts two parameters: the name of a key and\na multiplier value. The script fetches the value stored in the key, multiplies\nit with the multiplier value and returns the result.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis()\n >>> lua = \"\"\"\n ... local value = redis.call('GET', KEYS[1])\n ... value = tonumber(value)\n ... return value * ARGV[1]\"\"\"\n >>> multiply = r.register_script(lua)\n\n`multiply` is now a Script instance that is invoked by calling it like a\nfunction. Script instances accept the following optional arguments:\n\n* **keys**: A list of key names that the script will access. This becomes the\n KEYS list in Lua.\n* **args**: A list of argument values. This becomes the ARGV list in Lua.\n* **client**: A redis-py Client or Pipeline instance that will invoke the\n script. If client isn't specified, the client that initially\n created the Script instance (the one that `register_script` was\n invoked from) will be used.\n\nContinuing the example from above:\n\n.. code-block:: pycon\n\n >>> r.set('foo', 2)\n >>> multiply(keys=['foo'], args=[5])\n 10\n\nThe value of key 'foo' is set to 2. When multiply is invoked, the 'foo' key is\npassed to the script along with the multiplier value of 5. Lua executes the\nscript and returns the result, 10.\n\nScript instances can be executed using a different client instance, even one\nthat points to a completely different Redis server.\n\n.. code-block:: pycon\n\n >>> r2 = redis.Redis('redis2.example.com')\n >>> r2.set('foo', 3)\n >>> multiply(keys=['foo'], args=[5], client=r2)\n 15\n\nThe Script object ensures that the Lua script is loaded into Redis's script\ncache. In the event of a NOSCRIPT error, it will load the script and retry\nexecuting it.\n\nScript objects can also be used in pipelines. The pipeline instance should be\npassed as the client argument when calling the script. Care is taken to ensure\nthat the script is registered in Redis's script cache just prior to pipeline\nexecution.\n\n.. code-block:: pycon\n\n >>> pipe = r.pipeline()\n >>> pipe.set('foo', 5)\n >>> multiply(keys=['foo'], args=[5], client=pipe)\n >>> pipe.execute()\n [True, 25]\n\nSentinel support\n^^^^^^^^^^^^^^^^\n\nredis-py can be used together with `Redis Sentinel `_\nto discover Redis nodes. You need to have at least one Sentinel daemon running\nin order to use redis-py's Sentinel support.\n\nConnecting redis-py to the Sentinel instance(s) is easy. You can use a\nSentinel connection to discover the master and slaves network addresses:\n\n.. code-block:: pycon\n\n >>> from redis.sentinel import Sentinel\n >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)\n >>> sentinel.discover_master('mymaster')\n ('127.0.0.1', 6379)\n >>> sentinel.discover_slaves('mymaster')\n [('127.0.0.1', 6380)]\n\nYou can also create Redis client connections from a Sentinel instance. You can\nconnect to either the master (for write operations) or a slave (for read-only\noperations).\n\n.. code-block:: pycon\n\n >>> master = sentinel.master_for('mymaster', socket_timeout=0.1)\n >>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)\n >>> master.set('foo', 'bar')\n >>> slave.get('foo')\n b'bar'\n\nThe master and slave objects are normal Redis instances with their\nconnection pool bound to the Sentinel instance. When a Sentinel backed client\nattempts to establish a connection, it first queries the Sentinel servers to\ndetermine an appropriate host to connect to. If no server is found,\na MasterNotFoundError or SlaveNotFoundError is raised. Both exceptions are\nsubclasses of ConnectionError.\n\nWhen trying to connect to a slave client, the Sentinel connection pool will\niterate over the list of slaves until it finds one that can be connected to.\nIf no slaves can be connected to, a connection will be established with the\nmaster.\n\nSee `Guidelines for Redis clients with support for Redis Sentinel\n`_ to learn more about Redis Sentinel.\n\nScan Iterators\n^^^^^^^^^^^^^^\n\nThe \\*SCAN commands introduced in Redis 2.8 can be cumbersome to use. While\nthese commands are fully supported, redis-py also exposes the following methods\nthat return Python iterators for convenience: `scan_iter`, `hscan_iter`,\n`sscan_iter` and `zscan_iter`.\n\n.. code-block:: pycon\n\n >>> for key, value in (('A', '1'), ('B', '2'), ('C', '3')):\n ... r.set(key, value)\n >>> for key in r.scan_iter():\n ... print(key, r.get(key))\n A 1\n B 2\n C 3\n\nAuthor\n^^^^^^\n\nredis-py is developed and maintained by Andy McCurdy (sedrik@gmail.com).\nIt can be found here: https://github.com/andymccurdy/redis-py\n\nSpecial thanks to:\n\n* Ludovico Magnocavallo, author of the original Python Redis client, from\n which some of the socket code is still used.\n* Alexander Solovyov for ideas on the generic response callback system.\n* Paul Hubbard for initial packaging support.\n\n\n", + "release_date": "2020-06-01T21:30:33", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Andy McCurdy", + "email": "sedrik@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Andy McCurdy", + "email": "sedrik@gmail.com", + "url": null + } + ], + "keywords": "Redis,key-value store", + "homepage_url": "https://github.com/andymccurdy/redis-py", + "download_url": "https://files.pythonhosted.org/packages/a7/7c/24fb0511df653cf1a5d938d8f5d19802a88cef255706fdda242ff97e91b7/redis-3.5.3-py2.py3-none-any.whl", + "size": 72144, + "sha1": null, + "md5": "c37ac6eeeb79559fe0c4cfe23d79b754", + "sha256": "432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/redis/3.5.3/json", + "datasource_id": null, + "purl": "pkg:pypi/redis@3.5.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "redis", + "version": "3.5.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "redis-py\n========\n\nThe Python interface to the Redis key-value store.\n\n.. image:: https://secure.travis-ci.org/andymccurdy/redis-py.svg?branch=master\n :target: https://travis-ci.org/andymccurdy/redis-py\n.. image:: https://readthedocs.org/projects/redis-py/badge/?version=stable&style=flat\n :target: https://redis-py.readthedocs.io/en/stable/\n.. image:: https://badge.fury.io/py/redis.svg\n :target: https://pypi.org/project/redis/\n.. image:: https://codecov.io/gh/andymccurdy/redis-py/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/andymccurdy/redis-py\n\n\nPython 2 Compatibility Note\n---------------------------\n\nredis-py 3.5.x will be the last version of redis-py that supports Python 2.\nThe 3.5.x line will continue to get bug fixes and security patches that\nsupport Python 2 until August 1, 2020. redis-py 4.0 will be the next major\nversion and will require Python 3.5+.\n\n\nInstallation\n------------\n\nredis-py requires a running Redis server. See `Redis's quickstart\n`_ for installation instructions.\n\nredis-py can be installed using `pip` similar to other Python packages. Do not use `sudo`\nwith `pip`. It is usually good to work in a\n`virtualenv `_ or\n`venv `_ to avoid conflicts with other package\nmanagers and Python projects. For a quick introduction see\n`Python Virtual Environments in Five Minutes `_.\n\nTo install redis-py, simply:\n\n.. code-block:: bash\n\n $ pip install redis\n\nor from source:\n\n.. code-block:: bash\n\n $ python setup.py install\n\n\nGetting Started\n---------------\n\n.. code-block:: pycon\n\n >>> import redis\n >>> r = redis.Redis(host='localhost', port=6379, db=0)\n >>> r.set('foo', 'bar')\n True\n >>> r.get('foo')\n b'bar'\n\nBy default, all responses are returned as `bytes` in Python 3 and `str` in\nPython 2. The user is responsible for decoding to Python 3 strings or Python 2\nunicode objects.\n\nIf **all** string responses from a client should be decoded, the user can\nspecify `decode_responses=True` to `Redis.__init__`. In this case, any\nRedis command that returns a string type will be decoded with the `encoding`\nspecified.\n\n\nUpgrading from redis-py 2.X to 3.0\n----------------------------------\n\nredis-py 3.0 introduces many new features but required a number of backwards\nincompatible changes to be made in the process. This section attempts to\nprovide an upgrade path for users migrating from 2.X to 3.0.\n\n\nPython Version Support\n^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 supports Python 2.7 and Python 3.5+.\n\n\nClient Classes: Redis and StrictRedis\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 drops support for the legacy \"Redis\" client class. \"StrictRedis\"\nhas been renamed to \"Redis\" and an alias named \"StrictRedis\" is provided so\nthat users previously using \"StrictRedis\" can continue to run unchanged.\n\nThe 2.X \"Redis\" class provided alternative implementations of a few commands.\nThis confused users (rightfully so) and caused a number of support issues. To\nmake things easier going forward, it was decided to drop support for these\nalternate implementations and instead focus on a single client class.\n\n2.X users that are already using StrictRedis don't have to change the class\nname. StrictRedis will continue to work for the foreseeable future.\n\n2.X users that are using the Redis class will have to make changes if they\nuse any of the following commands:\n\n* SETEX: The argument order has changed. The new order is (name, time, value).\n* LREM: The argument order has changed. The new order is (name, num, value).\n* TTL and PTTL: The return value is now always an int and matches the\n official Redis command (>0 indicates the timeout, -1 indicates that the key\n exists but that it has no expire time set, -2 indicates that the key does\n not exist)\n\n\nSSL Connections\n^^^^^^^^^^^^^^^\n\nredis-py 3.0 changes the default value of the `ssl_cert_reqs` option from\n`None` to `'required'`. See\n`Issue 1016 `_. This\nchange enforces hostname validation when accepting a cert from a remote SSL\nterminator. If the terminator doesn't properly set the hostname on the cert\nthis will cause redis-py 3.0 to raise a ConnectionError.\n\nThis check can be disabled by setting `ssl_cert_reqs` to `None`. Note that\ndoing so removes the security check. Do so at your own risk.\n\nIt has been reported that SSL certs received from AWS ElastiCache do not have\nproper hostnames and turning off hostname verification is currently required.\n\n\nMSET, MSETNX and ZADD\n^^^^^^^^^^^^^^^^^^^^^\n\nThese commands all accept a mapping of key/value pairs. In redis-py 2.X\nthis mapping could be specified as ``*args`` or as ``**kwargs``. Both of these\nstyles caused issues when Redis introduced optional flags to ZADD. Relying on\n``*args`` caused issues with the optional argument order, especially in Python\n2.7. Relying on ``**kwargs`` caused potential collision issues of user keys with\nthe argument names in the method signature.\n\nTo resolve this, redis-py 3.0 has changed these three commands to all accept\na single positional argument named mapping that is expected to be a dict. For\nMSET and MSETNX, the dict is a mapping of key-names -> values. For ZADD, the\ndict is a mapping of element-names -> score.\n\nMSET, MSETNX and ZADD now look like:\n\n.. code-block:: python\n\n def mset(self, mapping):\n def msetnx(self, mapping):\n def zadd(self, name, mapping, nx=False, xx=False, ch=False, incr=False):\n\nAll 2.X users that use these commands must modify their code to supply\nkeys and values as a dict to these commands.\n\n\nZINCRBY\n^^^^^^^\n\nredis-py 2.X accidentally modified the argument order of ZINCRBY, swapping the\norder of value and amount. ZINCRBY now looks like:\n\n.. code-block:: python\n\n def zincrby(self, name, amount, value):\n\nAll 2.X users that rely on ZINCRBY must swap the order of amount and value\nfor the command to continue to work as intended.\n\n\nEncoding of User Input\n^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 only accepts user data as bytes, strings or numbers (ints, longs\nand floats). Attempting to specify a key or a value as any other type will\nraise a DataError exception.\n\nredis-py 2.X attempted to coerce any type of input into a string. While\noccasionally convenient, this caused all sorts of hidden errors when users\npassed boolean values (which were coerced to 'True' or 'False'), a None\nvalue (which was coerced to 'None') or other values, such as user defined\ntypes.\n\nAll 2.X users should make sure that the keys and values they pass into\nredis-py are either bytes, strings or numbers.\n\n\nLocks\n^^^^^\n\nredis-py 3.0 drops support for the pipeline-based Lock and now only supports\nthe Lua-based lock. In doing so, LuaLock has been renamed to Lock. This also\nmeans that redis-py Lock objects require Redis server 2.6 or greater.\n\n2.X users that were explicitly referring to \"LuaLock\" will have to now refer\nto \"Lock\" instead.\n\n\nLocks as Context Managers\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nredis-py 3.0 now raises a LockError when using a lock as a context manager and\nthe lock cannot be acquired within the specified timeout. This is more of a\nbug fix than a backwards incompatible change. However, given an error is now\nraised where none was before, this might alarm some users.\n\n2.X users should make sure they're wrapping their lock code in a try/catch\nlike this:\n\n.. code-block:: python\n\n try:\n with r.lock('my-lock-key', blocking_timeout=5) as lock:\n # code you want executed only after the lock has been acquired\n except LockError:\n # the lock wasn't acquired\n\n\nAPI Reference\n-------------\n\nThe `official Redis command documentation `_ does a\ngreat job of explaining each command in detail. redis-py attempts to adhere\nto the official command syntax. There are a few exceptions:\n\n* **SELECT**: Not implemented. See the explanation in the Thread Safety section\n below.\n* **DEL**: 'del' is a reserved keyword in the Python syntax. Therefore redis-py\n uses 'delete' instead.\n* **MULTI/EXEC**: These are implemented as part of the Pipeline class. The\n pipeline is wrapped with the MULTI and EXEC statements by default when it\n is executed, which can be disabled by specifying transaction=False.\n See more about Pipelines below.\n* **SUBSCRIBE/LISTEN**: Similar to pipelines, PubSub is implemented as a separate\n class as it places the underlying connection in a state where it can't\n execute non-pubsub commands. Calling the pubsub method from the Redis client\n will return a PubSub instance where you can subscribe to channels and listen\n for messages. You can only call PUBLISH from the Redis client (see\n `this comment on issue #151\n `_\n for details).\n* **SCAN/SSCAN/HSCAN/ZSCAN**: The \\*SCAN commands are implemented as they\n exist in the Redis documentation. In addition, each command has an equivalent\n iterator method. These are purely for convenience so the user doesn't have\n to keep track of the cursor while iterating. Use the\n scan_iter/sscan_iter/hscan_iter/zscan_iter methods for this behavior.\n\n\nMore Detail\n-----------\n\nConnection Pools\n^^^^^^^^^^^^^^^^\n\nBehind the scenes, redis-py uses a connection pool to manage connections to\na Redis server. By default, each Redis instance you create will in turn create\nits own connection pool. You can override this behavior and use an existing\nconnection pool by passing an already created connection pool instance to the\nconnection_pool argument of the Redis class. You may choose to do this in order\nto implement client side sharding or have fine-grain control of how\nconnections are managed.\n\n.. code-block:: pycon\n\n >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)\n >>> r = redis.Redis(connection_pool=pool)\n\nConnections\n^^^^^^^^^^^\n\nConnectionPools manage a set of Connection instances. redis-py ships with two\ntypes of Connections. The default, Connection, is a normal TCP socket based\nconnection. The UnixDomainSocketConnection allows for clients running on the\nsame device as the server to connect via a unix domain socket. To use a\nUnixDomainSocketConnection connection, simply pass the unix_socket_path\nargument, which is a string to the unix domain socket file. Additionally, make\nsure the unixsocket parameter is defined in your redis.conf file. It's\ncommented out by default.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(unix_socket_path='/tmp/redis.sock')\n\nYou can create your own Connection subclasses as well. This may be useful if\nyou want to control the socket behavior within an async framework. To\ninstantiate a client class using your own connection, you need to create\na connection pool, passing your class to the connection_class argument.\nOther keyword parameters you pass to the pool will be passed to the class\nspecified during initialization.\n\n.. code-block:: pycon\n\n >>> pool = redis.ConnectionPool(connection_class=YourConnectionClass,\n your_arg='...', ...)\n\nConnections maintain an open socket to the Redis server. Sometimes these\nsockets are interrupted or disconnected for a variety of reasons. For example,\nnetwork appliances, load balancers and other services that sit between clients\nand servers are often configured to kill connections that remain idle for a\ngiven threshold.\n\nWhen a connection becomes disconnected, the next command issued on that\nconnection will fail and redis-py will raise a ConnectionError to the caller.\nThis allows each application that uses redis-py to handle errors in a way\nthat's fitting for that specific application. However, constant error\nhandling can be verbose and cumbersome, especially when socket disconnections\nhappen frequently in many production environments.\n\nTo combat this, redis-py can issue regular health checks to assess the\nliveliness of a connection just before issuing a command. Users can pass\n``health_check_interval=N`` to the Redis or ConnectionPool classes or\nas a query argument within a Redis URL. The value of ``health_check_interval``\nmust be an integer. A value of ``0``, the default, disables health checks.\nAny positive integer will enable health checks. Health checks are performed\njust before a command is executed if the underlying connection has been idle\nfor more than ``health_check_interval`` seconds. For example,\n``health_check_interval=30`` will ensure that a health check is run on any\nconnection that has been idle for 30 or more seconds just before a command\nis executed on that connection.\n\nIf your application is running in an environment that disconnects idle\nconnections after 30 seconds you should set the ``health_check_interval``\noption to a value less than 30.\n\nThis option also works on any PubSub connection that is created from a\nclient with ``health_check_interval`` enabled. PubSub users need to ensure\nthat ``get_message()`` or ``listen()`` are called more frequently than\n``health_check_interval`` seconds. It is assumed that most workloads already\ndo this.\n\nIf your PubSub use case doesn't call ``get_message()`` or ``listen()``\nfrequently, you should call ``pubsub.check_health()`` explicitly on a\nregularly basis.\n\nParsers\n^^^^^^^\n\nParser classes provide a way to control how responses from the Redis server\nare parsed. redis-py ships with two parser classes, the PythonParser and the\nHiredisParser. By default, redis-py will attempt to use the HiredisParser if\nyou have the hiredis module installed and will fallback to the PythonParser\notherwise.\n\nHiredis is a C library maintained by the core Redis team. Pieter Noordhuis was\nkind enough to create Python bindings. Using Hiredis can provide up to a\n10x speed improvement in parsing responses from the Redis server. The\nperformance increase is most noticeable when retrieving many pieces of data,\nsuch as from LRANGE or SMEMBERS operations.\n\nHiredis is available on PyPI, and can be installed via pip just like redis-py.\n\n.. code-block:: bash\n\n $ pip install hiredis\n\nResponse Callbacks\n^^^^^^^^^^^^^^^^^^\n\nThe client class uses a set of callbacks to cast Redis responses to the\nappropriate Python type. There are a number of these callbacks defined on\nthe Redis client class in a dictionary called RESPONSE_CALLBACKS.\n\nCustom callbacks can be added on a per-instance basis using the\nset_response_callback method. This method accepts two arguments: a command\nname and the callback. Callbacks added in this manner are only valid on the\ninstance the callback is added to. If you want to define or override a callback\nglobally, you should make a subclass of the Redis client and add your callback\nto its RESPONSE_CALLBACKS class dictionary.\n\nResponse callbacks take at least one parameter: the response from the Redis\nserver. Keyword arguments may also be accepted in order to further control\nhow to interpret the response. These keyword arguments are specified during the\ncommand's call to execute_command. The ZRANGE implementation demonstrates the\nuse of response callback keyword arguments with its \"withscores\" argument.\n\nThread Safety\n^^^^^^^^^^^^^\n\nRedis client instances can safely be shared between threads. Internally,\nconnection instances are only retrieved from the connection pool during\ncommand execution, and returned to the pool directly after. Command execution\nnever modifies state on the client instance.\n\nHowever, there is one caveat: the Redis SELECT command. The SELECT command\nallows you to switch the database currently in use by the connection. That\ndatabase remains selected until another is selected or until the connection is\nclosed. This creates an issue in that connections could be returned to the pool\nthat are connected to a different database.\n\nAs a result, redis-py does not implement the SELECT command on client\ninstances. If you use multiple Redis databases within the same application, you\nshould create a separate client instance (and possibly a separate connection\npool) for each database.\n\nIt is not safe to pass PubSub or Pipeline objects between threads.\n\nPipelines\n^^^^^^^^^\n\nPipelines are a subclass of the base Redis class that provide support for\nbuffering multiple commands to the server in a single request. They can be used\nto dramatically increase the performance of groups of commands by reducing the\nnumber of back-and-forth TCP packets between the client and server.\n\nPipelines are quite simple to use:\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(...)\n >>> r.set('bing', 'baz')\n >>> # Use the pipeline() method to create a pipeline instance\n >>> pipe = r.pipeline()\n >>> # The following SET commands are buffered\n >>> pipe.set('foo', 'bar')\n >>> pipe.get('bing')\n >>> # the EXECUTE call sends all buffered commands to the server, returning\n >>> # a list of responses, one for each command.\n >>> pipe.execute()\n [True, b'baz']\n\nFor ease of use, all commands being buffered into the pipeline return the\npipeline object itself. Therefore calls can be chained like:\n\n.. code-block:: pycon\n\n >>> pipe.set('foo', 'bar').sadd('faz', 'baz').incr('auto_number').execute()\n [True, True, 6]\n\nIn addition, pipelines can also ensure the buffered commands are executed\natomically as a group. This happens by default. If you want to disable the\natomic nature of a pipeline but still want to buffer commands, you can turn\noff transactions.\n\n.. code-block:: pycon\n\n >>> pipe = r.pipeline(transaction=False)\n\nA common issue occurs when requiring atomic transactions but needing to\nretrieve values in Redis prior for use within the transaction. For instance,\nlet's assume that the INCR command didn't exist and we need to build an atomic\nversion of INCR in Python.\n\nThe completely naive implementation could GET the value, increment it in\nPython, and SET the new value back. However, this is not atomic because\nmultiple clients could be doing this at the same time, each getting the same\nvalue from GET.\n\nEnter the WATCH command. WATCH provides the ability to monitor one or more keys\nprior to starting a transaction. If any of those keys change prior the\nexecution of that transaction, the entire transaction will be canceled and a\nWatchError will be raised. To implement our own client-side INCR command, we\ncould do something like this:\n\n.. code-block:: pycon\n\n >>> with r.pipeline() as pipe:\n ... while True:\n ... try:\n ... # put a WATCH on the key that holds our sequence value\n ... pipe.watch('OUR-SEQUENCE-KEY')\n ... # after WATCHing, the pipeline is put into immediate execution\n ... # mode until we tell it to start buffering commands again.\n ... # this allows us to get the current value of our sequence\n ... current_value = pipe.get('OUR-SEQUENCE-KEY')\n ... next_value = int(current_value) + 1\n ... # now we can put the pipeline back into buffered mode with MULTI\n ... pipe.multi()\n ... pipe.set('OUR-SEQUENCE-KEY', next_value)\n ... # and finally, execute the pipeline (the set command)\n ... pipe.execute()\n ... # if a WatchError wasn't raised during execution, everything\n ... # we just did happened atomically.\n ... break\n ... except WatchError:\n ... # another client must have changed 'OUR-SEQUENCE-KEY' between\n ... # the time we started WATCHing it and the pipeline's execution.\n ... # our best bet is to just retry.\n ... continue\n\nNote that, because the Pipeline must bind to a single connection for the\nduration of a WATCH, care must be taken to ensure that the connection is\nreturned to the connection pool by calling the reset() method. If the\nPipeline is used as a context manager (as in the example above) reset()\nwill be called automatically. Of course you can do this the manual way by\nexplicitly calling reset():\n\n.. code-block:: pycon\n\n >>> pipe = r.pipeline()\n >>> while True:\n ... try:\n ... pipe.watch('OUR-SEQUENCE-KEY')\n ... ...\n ... pipe.execute()\n ... break\n ... except WatchError:\n ... continue\n ... finally:\n ... pipe.reset()\n\nA convenience method named \"transaction\" exists for handling all the\nboilerplate of handling and retrying watch errors. It takes a callable that\nshould expect a single parameter, a pipeline object, and any number of keys to\nbe WATCHed. Our client-side INCR command above can be written like this,\nwhich is much easier to read:\n\n.. code-block:: pycon\n\n >>> def client_side_incr(pipe):\n ... current_value = pipe.get('OUR-SEQUENCE-KEY')\n ... next_value = int(current_value) + 1\n ... pipe.multi()\n ... pipe.set('OUR-SEQUENCE-KEY', next_value)\n >>>\n >>> r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')\n [True]\n\nBe sure to call `pipe.multi()` in the callable passed to `Redis.transaction`\nprior to any write commands.\n\nPublish / Subscribe\n^^^^^^^^^^^^^^^^^^^\n\nredis-py includes a `PubSub` object that subscribes to channels and listens\nfor new messages. Creating a `PubSub` object is easy.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(...)\n >>> p = r.pubsub()\n\nOnce a `PubSub` instance is created, channels and patterns can be subscribed\nto.\n\n.. code-block:: pycon\n\n >>> p.subscribe('my-first-channel', 'my-second-channel', ...)\n >>> p.psubscribe('my-*', ...)\n\nThe `PubSub` instance is now subscribed to those channels/patterns. The\nsubscription confirmations can be seen by reading messages from the `PubSub`\ninstance.\n\n.. code-block:: pycon\n\n >>> p.get_message()\n {'pattern': None, 'type': 'subscribe', 'channel': b'my-second-channel', 'data': 1}\n >>> p.get_message()\n {'pattern': None, 'type': 'subscribe', 'channel': b'my-first-channel', 'data': 2}\n >>> p.get_message()\n {'pattern': None, 'type': 'psubscribe', 'channel': b'my-*', 'data': 3}\n\nEvery message read from a `PubSub` instance will be a dictionary with the\nfollowing keys.\n\n* **type**: One of the following: 'subscribe', 'unsubscribe', 'psubscribe',\n 'punsubscribe', 'message', 'pmessage'\n* **channel**: The channel [un]subscribed to or the channel a message was\n published to\n* **pattern**: The pattern that matched a published message's channel. Will be\n `None` in all cases except for 'pmessage' types.\n* **data**: The message data. With [un]subscribe messages, this value will be\n the number of channels and patterns the connection is currently subscribed\n to. With [p]message messages, this value will be the actual published\n message.\n\nLet's send a message now.\n\n.. code-block:: pycon\n\n # the publish method returns the number matching channel and pattern\n # subscriptions. 'my-first-channel' matches both the 'my-first-channel'\n # subscription and the 'my-*' pattern subscription, so this message will\n # be delivered to 2 channels/patterns\n >>> r.publish('my-first-channel', 'some data')\n 2\n >>> p.get_message()\n {'channel': b'my-first-channel', 'data': b'some data', 'pattern': None, 'type': 'message'}\n >>> p.get_message()\n {'channel': b'my-first-channel', 'data': b'some data', 'pattern': b'my-*', 'type': 'pmessage'}\n\nUnsubscribing works just like subscribing. If no arguments are passed to\n[p]unsubscribe, all channels or patterns will be unsubscribed from.\n\n.. code-block:: pycon\n\n >>> p.unsubscribe()\n >>> p.punsubscribe('my-*')\n >>> p.get_message()\n {'channel': b'my-second-channel', 'data': 2, 'pattern': None, 'type': 'unsubscribe'}\n >>> p.get_message()\n {'channel': b'my-first-channel', 'data': 1, 'pattern': None, 'type': 'unsubscribe'}\n >>> p.get_message()\n {'channel': b'my-*', 'data': 0, 'pattern': None, 'type': 'punsubscribe'}\n\nredis-py also allows you to register callback functions to handle published\nmessages. Message handlers take a single argument, the message, which is a\ndictionary just like the examples above. To subscribe to a channel or pattern\nwith a message handler, pass the channel or pattern name as a keyword argument\nwith its value being the callback function.\n\nWhen a message is read on a channel or pattern with a message handler, the\nmessage dictionary is created and passed to the message handler. In this case,\na `None` value is returned from get_message() since the message was already\nhandled.\n\n.. code-block:: pycon\n\n >>> def my_handler(message):\n ... print('MY HANDLER: ', message['data'])\n >>> p.subscribe(**{'my-channel': my_handler})\n # read the subscribe confirmation message\n >>> p.get_message()\n {'pattern': None, 'type': 'subscribe', 'channel': b'my-channel', 'data': 1}\n >>> r.publish('my-channel', 'awesome data')\n 1\n # for the message handler to work, we need tell the instance to read data.\n # this can be done in several ways (read more below). we'll just use\n # the familiar get_message() function for now\n >>> message = p.get_message()\n MY HANDLER: awesome data\n # note here that the my_handler callback printed the string above.\n # `message` is None because the message was handled by our handler.\n >>> print(message)\n None\n\nIf your application is not interested in the (sometimes noisy)\nsubscribe/unsubscribe confirmation messages, you can ignore them by passing\n`ignore_subscribe_messages=True` to `r.pubsub()`. This will cause all\nsubscribe/unsubscribe messages to be read, but they won't bubble up to your\napplication.\n\n.. code-block:: pycon\n\n >>> p = r.pubsub(ignore_subscribe_messages=True)\n >>> p.subscribe('my-channel')\n >>> p.get_message() # hides the subscribe message and returns None\n >>> r.publish('my-channel', 'my data')\n 1\n >>> p.get_message()\n {'channel': b'my-channel', 'data': b'my data', 'pattern': None, 'type': 'message'}\n\nThere are three different strategies for reading messages.\n\nThe examples above have been using `pubsub.get_message()`. Behind the scenes,\n`get_message()` uses the system's 'select' module to quickly poll the\nconnection's socket. If there's data available to be read, `get_message()` will\nread it, format the message and return it or pass it to a message handler. If\nthere's no data to be read, `get_message()` will immediately return None. This\nmakes it trivial to integrate into an existing event loop inside your\napplication.\n\n.. code-block:: pycon\n\n >>> while True:\n >>> message = p.get_message()\n >>> if message:\n >>> # do something with the message\n >>> time.sleep(0.001) # be nice to the system :)\n\nOlder versions of redis-py only read messages with `pubsub.listen()`. listen()\nis a generator that blocks until a message is available. If your application\ndoesn't need to do anything else but receive and act on messages received from\nredis, listen() is an easy way to get up an running.\n\n.. code-block:: pycon\n\n >>> for message in p.listen():\n ... # do something with the message\n\nThe third option runs an event loop in a separate thread.\n`pubsub.run_in_thread()` creates a new thread and starts the event loop. The\nthread object is returned to the caller of `run_in_thread()`. The caller can\nuse the `thread.stop()` method to shut down the event loop and thread. Behind\nthe scenes, this is simply a wrapper around `get_message()` that runs in a\nseparate thread, essentially creating a tiny non-blocking event loop for you.\n`run_in_thread()` takes an optional `sleep_time` argument. If specified, the\nevent loop will call `time.sleep()` with the value in each iteration of the\nloop.\n\nNote: Since we're running in a separate thread, there's no way to handle\nmessages that aren't automatically handled with registered message handlers.\nTherefore, redis-py prevents you from calling `run_in_thread()` if you're\nsubscribed to patterns or channels that don't have message handlers attached.\n\n.. code-block:: pycon\n\n >>> p.subscribe(**{'my-channel': my_handler})\n >>> thread = p.run_in_thread(sleep_time=0.001)\n # the event loop is now running in the background processing messages\n # when it's time to shut it down...\n >>> thread.stop()\n\nA PubSub object adheres to the same encoding semantics as the client instance\nit was created from. Any channel or pattern that's unicode will be encoded\nusing the `charset` specified on the client before being sent to Redis. If the\nclient's `decode_responses` flag is set the False (the default), the\n'channel', 'pattern' and 'data' values in message dictionaries will be byte\nstrings (str on Python 2, bytes on Python 3). If the client's\n`decode_responses` is True, then the 'channel', 'pattern' and 'data' values\nwill be automatically decoded to unicode strings using the client's `charset`.\n\nPubSub objects remember what channels and patterns they are subscribed to. In\nthe event of a disconnection such as a network error or timeout, the\nPubSub object will re-subscribe to all prior channels and patterns when\nreconnecting. Messages that were published while the client was disconnected\ncannot be delivered. When you're finished with a PubSub object, call its\n`.close()` method to shutdown the connection.\n\n.. code-block:: pycon\n\n >>> p = r.pubsub()\n >>> ...\n >>> p.close()\n\n\nThe PUBSUB set of subcommands CHANNELS, NUMSUB and NUMPAT are also\nsupported:\n\n.. code-block:: pycon\n\n >>> r.pubsub_channels()\n [b'foo', b'bar']\n >>> r.pubsub_numsub('foo', 'bar')\n [(b'foo', 9001), (b'bar', 42)]\n >>> r.pubsub_numsub('baz')\n [(b'baz', 0)]\n >>> r.pubsub_numpat()\n 1204\n\nMonitor\n^^^^^^^\nredis-py includes a `Monitor` object that streams every command processed\nby the Redis server. Use `listen()` on the `Monitor` object to block\nuntil a command is received.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis(...)\n >>> with r.monitor() as m:\n >>> for command in m.listen():\n >>> print(command)\n\nLua Scripting\n^^^^^^^^^^^^^\n\nredis-py supports the EVAL, EVALSHA, and SCRIPT commands. However, there are\na number of edge cases that make these commands tedious to use in real world\nscenarios. Therefore, redis-py exposes a Script object that makes scripting\nmuch easier to use.\n\nTo create a Script instance, use the `register_script` function on a client\ninstance passing the Lua code as the first argument. `register_script` returns\na Script instance that you can use throughout your code.\n\nThe following trivial Lua script accepts two parameters: the name of a key and\na multiplier value. The script fetches the value stored in the key, multiplies\nit with the multiplier value and returns the result.\n\n.. code-block:: pycon\n\n >>> r = redis.Redis()\n >>> lua = \"\"\"\n ... local value = redis.call('GET', KEYS[1])\n ... value = tonumber(value)\n ... return value * ARGV[1]\"\"\"\n >>> multiply = r.register_script(lua)\n\n`multiply` is now a Script instance that is invoked by calling it like a\nfunction. Script instances accept the following optional arguments:\n\n* **keys**: A list of key names that the script will access. This becomes the\n KEYS list in Lua.\n* **args**: A list of argument values. This becomes the ARGV list in Lua.\n* **client**: A redis-py Client or Pipeline instance that will invoke the\n script. If client isn't specified, the client that initially\n created the Script instance (the one that `register_script` was\n invoked from) will be used.\n\nContinuing the example from above:\n\n.. code-block:: pycon\n\n >>> r.set('foo', 2)\n >>> multiply(keys=['foo'], args=[5])\n 10\n\nThe value of key 'foo' is set to 2. When multiply is invoked, the 'foo' key is\npassed to the script along with the multiplier value of 5. Lua executes the\nscript and returns the result, 10.\n\nScript instances can be executed using a different client instance, even one\nthat points to a completely different Redis server.\n\n.. code-block:: pycon\n\n >>> r2 = redis.Redis('redis2.example.com')\n >>> r2.set('foo', 3)\n >>> multiply(keys=['foo'], args=[5], client=r2)\n 15\n\nThe Script object ensures that the Lua script is loaded into Redis's script\ncache. In the event of a NOSCRIPT error, it will load the script and retry\nexecuting it.\n\nScript objects can also be used in pipelines. The pipeline instance should be\npassed as the client argument when calling the script. Care is taken to ensure\nthat the script is registered in Redis's script cache just prior to pipeline\nexecution.\n\n.. code-block:: pycon\n\n >>> pipe = r.pipeline()\n >>> pipe.set('foo', 5)\n >>> multiply(keys=['foo'], args=[5], client=pipe)\n >>> pipe.execute()\n [True, 25]\n\nSentinel support\n^^^^^^^^^^^^^^^^\n\nredis-py can be used together with `Redis Sentinel `_\nto discover Redis nodes. You need to have at least one Sentinel daemon running\nin order to use redis-py's Sentinel support.\n\nConnecting redis-py to the Sentinel instance(s) is easy. You can use a\nSentinel connection to discover the master and slaves network addresses:\n\n.. code-block:: pycon\n\n >>> from redis.sentinel import Sentinel\n >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)\n >>> sentinel.discover_master('mymaster')\n ('127.0.0.1', 6379)\n >>> sentinel.discover_slaves('mymaster')\n [('127.0.0.1', 6380)]\n\nYou can also create Redis client connections from a Sentinel instance. You can\nconnect to either the master (for write operations) or a slave (for read-only\noperations).\n\n.. code-block:: pycon\n\n >>> master = sentinel.master_for('mymaster', socket_timeout=0.1)\n >>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)\n >>> master.set('foo', 'bar')\n >>> slave.get('foo')\n b'bar'\n\nThe master and slave objects are normal Redis instances with their\nconnection pool bound to the Sentinel instance. When a Sentinel backed client\nattempts to establish a connection, it first queries the Sentinel servers to\ndetermine an appropriate host to connect to. If no server is found,\na MasterNotFoundError or SlaveNotFoundError is raised. Both exceptions are\nsubclasses of ConnectionError.\n\nWhen trying to connect to a slave client, the Sentinel connection pool will\niterate over the list of slaves until it finds one that can be connected to.\nIf no slaves can be connected to, a connection will be established with the\nmaster.\n\nSee `Guidelines for Redis clients with support for Redis Sentinel\n`_ to learn more about Redis Sentinel.\n\nScan Iterators\n^^^^^^^^^^^^^^\n\nThe \\*SCAN commands introduced in Redis 2.8 can be cumbersome to use. While\nthese commands are fully supported, redis-py also exposes the following methods\nthat return Python iterators for convenience: `scan_iter`, `hscan_iter`,\n`sscan_iter` and `zscan_iter`.\n\n.. code-block:: pycon\n\n >>> for key, value in (('A', '1'), ('B', '2'), ('C', '3')):\n ... r.set(key, value)\n >>> for key in r.scan_iter():\n ... print(key, r.get(key))\n A 1\n B 2\n C 3\n\nAuthor\n^^^^^^\n\nredis-py is developed and maintained by Andy McCurdy (sedrik@gmail.com).\nIt can be found here: https://github.com/andymccurdy/redis-py\n\nSpecial thanks to:\n\n* Ludovico Magnocavallo, author of the original Python Redis client, from\n which some of the socket code is still used.\n* Alexander Solovyov for ideas on the generic response callback system.\n* Paul Hubbard for initial packaging support.\n\n\n", + "release_date": "2020-06-01T21:30:35", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Andy McCurdy", + "email": "sedrik@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Andy McCurdy", + "email": "sedrik@gmail.com", + "url": null + } + ], + "keywords": "Redis,key-value store", + "homepage_url": "https://github.com/andymccurdy/redis-py", + "download_url": "https://files.pythonhosted.org/packages/b3/17/1e567ff78c83854e16b98694411fe6e08c3426af866ad11397cddceb80d3/redis-3.5.3.tar.gz", + "size": 141112, + "sha1": null, + "md5": "7a00d4540374f34e152a33faa1fcee5f", + "sha256": "0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/redis/3.5.3/json", + "datasource_id": null, + "purl": "pkg:pypi/redis@3.5.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "scandir", + "version": "1.10.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "scandir, a better directory iterator and faster os.walk()\r\n=========================================================\r\n\r\n.. image:: https://img.shields.io/pypi/v/scandir.svg\r\n :target: https://pypi.python.org/pypi/scandir\r\n :alt: scandir on PyPI (Python Package Index)\r\n\r\n.. image:: https://travis-ci.org/benhoyt/scandir.svg?branch=master\r\n :target: https://travis-ci.org/benhoyt/scandir\r\n :alt: Travis CI tests (Linux)\r\n\r\n.. image:: https://ci.appveyor.com/api/projects/status/github/benhoyt/scandir?branch=master&svg=true\r\n :target: https://ci.appveyor.com/project/benhoyt/scandir\r\n :alt: Appveyor tests (Windows)\r\n\r\n\r\n``scandir()`` is a directory iteration function like ``os.listdir()``,\r\nexcept that instead of returning a list of bare filenames, it yields\r\n``DirEntry`` objects that include file type and stat information along\r\nwith the name. Using ``scandir()`` increases the speed of ``os.walk()``\r\nby 2-20 times (depending on the platform and file system) by avoiding\r\nunnecessary calls to ``os.stat()`` in most cases.\r\n\r\n\r\nNow included in a Python near you!\r\n----------------------------------\r\n\r\n``scandir`` has been included in the Python 3.5 standard library as\r\n``os.scandir()``, and the related performance improvements to\r\n``os.walk()`` have also been included. So if you're lucky enough to be\r\nusing Python 3.5 (release date September 13, 2015) you get the benefit\r\nimmediately, otherwise just\r\n`download this module from PyPI `_,\r\ninstall it with ``pip install scandir``, and then do something like\r\nthis in your code:\r\n\r\n.. code-block:: python\r\n\r\n # Use the built-in version of scandir/walk if possible, otherwise\r\n # use the scandir module version\r\n try:\r\n from os import scandir, walk\r\n except ImportError:\r\n from scandir import scandir, walk\r\n\r\n`PEP 471 `_, which is the\r\nPEP that proposes including ``scandir`` in the Python standard library,\r\nwas `accepted `_\r\nin July 2014 by Victor Stinner, the BDFL-delegate for the PEP.\r\n\r\nThis ``scandir`` module is intended to work on Python 2.7+ and Python\r\n3.4+ (and it has been tested on those versions).\r\n\r\n\r\nBackground\r\n----------\r\n\r\nPython's built-in ``os.walk()`` is significantly slower than it needs to be,\r\nbecause -- in addition to calling ``listdir()`` on each directory -- it calls\r\n``stat()`` on each file to determine whether the filename is a directory or not.\r\nBut both ``FindFirstFile`` / ``FindNextFile`` on Windows and ``readdir`` on Linux/OS\r\nX already tell you whether the files returned are directories or not, so\r\nno further ``stat`` system calls are needed. In short, you can reduce the number\r\nof system calls from about 2N to N, where N is the total number of files and\r\ndirectories in the tree.\r\n\r\nIn practice, removing all those extra system calls makes ``os.walk()`` about\r\n**7-50 times as fast on Windows, and about 3-10 times as fast on Linux and Mac OS\r\nX.** So we're not talking about micro-optimizations. See more benchmarks\r\nin the \"Benchmarks\" section below.\r\n\r\nSomewhat relatedly, many people have also asked for a version of\r\n``os.listdir()`` that yields filenames as it iterates instead of returning them\r\nas one big list. This improves memory efficiency for iterating very large\r\ndirectories.\r\n\r\nSo as well as a faster ``walk()``, scandir adds a new ``scandir()`` function.\r\nThey're pretty easy to use, but see \"The API\" below for the full docs.\r\n\r\n\r\nBenchmarks\r\n----------\r\n\r\nBelow are results showing how many times as fast ``scandir.walk()`` is than\r\n``os.walk()`` on various systems, found by running ``benchmark.py`` with no\r\narguments:\r\n\r\n==================== ============== =============\r\nSystem version Python version Times as fast\r\n==================== ============== =============\r\nWindows 7 64-bit 2.7.7 64-bit 10.4\r\nWindows 7 64-bit SSD 2.7.7 64-bit 10.3\r\nWindows 7 64-bit NFS 2.7.6 64-bit 36.8\r\nWindows 7 64-bit SSD 3.4.1 64-bit 9.9\r\nWindows 7 64-bit SSD 3.5.0 64-bit 9.5\r\nUbuntu 14.04 64-bit 2.7.6 64-bit 5.8\r\nMac OS X 10.9.3 2.7.5 64-bit 3.8\r\n==================== ============== =============\r\n\r\nAll of the above tests were done using the fast C version of scandir\r\n(source code in ``_scandir.c``).\r\n\r\nNote that the gains are less than the above on smaller directories and greater\r\non larger directories. This is why ``benchmark.py`` creates a test directory\r\ntree with a standardized size.\r\n\r\n\r\nThe API\r\n-------\r\n\r\nwalk()\r\n~~~~~~\r\n\r\nThe API for ``scandir.walk()`` is exactly the same as ``os.walk()``, so just\r\n`read the Python docs `_.\r\n\r\nscandir()\r\n~~~~~~~~~\r\n\r\nThe full docs for ``scandir()`` and the ``DirEntry`` objects it yields are\r\navailable in the `Python documentation here `_. \r\nBut below is a brief summary as well.\r\n\r\n scandir(path='.') -> iterator of DirEntry objects for given path\r\n\r\nLike ``listdir``, ``scandir`` calls the operating system's directory\r\niteration system calls to get the names of the files in the given\r\n``path``, but it's different from ``listdir`` in two ways:\r\n\r\n* Instead of returning bare filename strings, it returns lightweight\r\n ``DirEntry`` objects that hold the filename string and provide\r\n simple methods that allow access to the additional data the\r\n operating system may have returned.\r\n\r\n* It returns a generator instead of a list, so that ``scandir`` acts\r\n as a true iterator instead of returning the full list immediately.\r\n\r\n``scandir()`` yields a ``DirEntry`` object for each file and\r\nsub-directory in ``path``. Just like ``listdir``, the ``'.'``\r\nand ``'..'`` pseudo-directories are skipped, and the entries are\r\nyielded in system-dependent order. Each ``DirEntry`` object has the\r\nfollowing attributes and methods:\r\n\r\n* ``name``: the entry's filename, relative to the scandir ``path``\r\n argument (corresponds to the return values of ``os.listdir``)\r\n\r\n* ``path``: the entry's full path name (not necessarily an absolute\r\n path) -- the equivalent of ``os.path.join(scandir_path, entry.name)``\r\n\r\n* ``is_dir(*, follow_symlinks=True)``: similar to\r\n ``pathlib.Path.is_dir()``, but the return value is cached on the\r\n ``DirEntry`` object; doesn't require a system call in most cases;\r\n don't follow symbolic links if ``follow_symlinks`` is False\r\n\r\n* ``is_file(*, follow_symlinks=True)``: similar to\r\n ``pathlib.Path.is_file()``, but the return value is cached on the\r\n ``DirEntry`` object; doesn't require a system call in most cases; \r\n don't follow symbolic links if ``follow_symlinks`` is False\r\n\r\n* ``is_symlink()``: similar to ``pathlib.Path.is_symlink()``, but the\r\n return value is cached on the ``DirEntry`` object; doesn't require a\r\n system call in most cases\r\n\r\n* ``stat(*, follow_symlinks=True)``: like ``os.stat()``, but the\r\n return value is cached on the ``DirEntry`` object; does not require a\r\n system call on Windows (except for symlinks); don't follow symbolic links\r\n (like ``os.lstat()``) if ``follow_symlinks`` is False\r\n\r\n* ``inode()``: return the inode number of the entry; the return value\r\n is cached on the ``DirEntry`` object\r\n\r\nHere's a very simple example of ``scandir()`` showing use of the\r\n``DirEntry.name`` attribute and the ``DirEntry.is_dir()`` method:\r\n\r\n.. code-block:: python\r\n\r\n def subdirs(path):\r\n \"\"\"Yield directory names not starting with '.' under given path.\"\"\"\r\n for entry in os.scandir(path):\r\n if not entry.name.startswith('.') and entry.is_dir():\r\n yield entry.name\r\n\r\nThis ``subdirs()`` function will be significantly faster with scandir\r\nthan ``os.listdir()`` and ``os.path.isdir()`` on both Windows and POSIX\r\nsystems, especially on medium-sized or large directories.\r\n\r\n\r\nFurther reading\r\n---------------\r\n\r\n* `The Python docs for scandir `_\r\n* `PEP 471 `_, the\r\n (now-accepted) Python Enhancement Proposal that proposed adding\r\n ``scandir`` to the standard library -- a lot of details here,\r\n including rejected ideas and previous discussion\r\n\r\n\r\nFlames, comments, bug reports\r\n-----------------------------\r\n\r\nPlease send flames, comments, and questions about scandir to Ben Hoyt:\r\n\r\nhttp://benhoyt.com/\r\n\r\nFile bug reports for the version in the Python 3.5 standard library\r\n`here `_, or file bug reports\r\nor feature requests for this module at the GitHub project page:\r\n\r\nhttps://github.com/benhoyt/scandir\r\n\r\n\r\n", + "release_date": "2019-03-09T17:58:33", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ben Hoyt", + "email": "benhoyt@gmail.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benhoyt/scandir", + "download_url": "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz", + "size": 33311, + "sha1": null, + "md5": "f8378f4d9f95a6a78e97ab01aa900c1d", + "sha256": "4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "New BSD License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/scandir/1.10.0/json", + "datasource_id": null, + "purl": "pkg:pypi/scandir@1.10.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "setuptools", + "version": "44.1.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/setuptools.svg\n :target: https://pypi.org/project/setuptools\n\n.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg\n :target: https://setuptools.readthedocs.io\n\n.. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white\n :target: https://travis-ci.org/pypa/setuptools\n\n.. image:: https://img.shields.io/appveyor/ci/pypa/setuptools/master.svg?label=Windows%20CI&logo=appveyor&logoColor=white\n :target: https://ci.appveyor.com/project/pypa/setuptools/branch/master\n\n.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white\n :target: https://codecov.io/gh/pypa/setuptools\n\n.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat\n :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme\n\n.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg\n\nSee the `Installation Instructions\n`_ in the Python Packaging\nUser's Guide for instructions on installing, upgrading, and uninstalling\nSetuptools.\n\nQuestions and comments should be directed to the `distutils-sig\nmailing list `_.\nBug reports and especially tested patches may be\nsubmitted directly to the `bug tracker\n`_.\n\nTo report a security vulnerability, please use the\n`Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\n\nFor Enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nSetuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.\n\n`Learn more `_.\n\nCode of Conduct\n===============\n\nEveryone interacting in the setuptools project's codebases, issue trackers,\nchat rooms, and mailing lists is expected to follow the\n`PyPA Code of Conduct `_.\n\n\n", + "release_date": "2020-05-29T01:06:05", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Python Packaging Authority", + "email": "distutils-sig@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "CPAN PyPI distutils eggs package management", + "homepage_url": "https://github.com/pypa/setuptools", + "download_url": "https://files.pythonhosted.org/packages/e1/b7/182161210a13158cd3ccc41ee19aadef54496b74f2817cc147006ec932b4/setuptools-44.1.1-py2.py3-none-any.whl", + "size": 583493, + "sha1": null, + "md5": "a5d4ce18230f73d2973afa33b79110ec", + "sha256": "27a714c09253134e60a6fa68130f78c7037e5562c4f21f8f318f2ae900d152d5", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/setuptools/44.1.1/json", + "datasource_id": null, + "purl": "pkg:pypi/setuptools@44.1.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "setuptools", + "version": "44.1.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/setuptools.svg\n :target: https://pypi.org/project/setuptools\n\n.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg\n :target: https://setuptools.readthedocs.io\n\n.. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white\n :target: https://travis-ci.org/pypa/setuptools\n\n.. image:: https://img.shields.io/appveyor/ci/pypa/setuptools/master.svg?label=Windows%20CI&logo=appveyor&logoColor=white\n :target: https://ci.appveyor.com/project/pypa/setuptools/branch/master\n\n.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white\n :target: https://codecov.io/gh/pypa/setuptools\n\n.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat\n :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme\n\n.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg\n\nSee the `Installation Instructions\n`_ in the Python Packaging\nUser's Guide for instructions on installing, upgrading, and uninstalling\nSetuptools.\n\nQuestions and comments should be directed to the `distutils-sig\nmailing list `_.\nBug reports and especially tested patches may be\nsubmitted directly to the `bug tracker\n`_.\n\nTo report a security vulnerability, please use the\n`Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\n\nFor Enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nSetuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.\n\n`Learn more `_.\n\nCode of Conduct\n===============\n\nEveryone interacting in the setuptools project's codebases, issue trackers,\nchat rooms, and mailing lists is expected to follow the\n`PyPA Code of Conduct `_.\n\n\n", + "release_date": "2020-05-29T01:06:07", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Python Packaging Authority", + "email": "distutils-sig@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "CPAN PyPI distutils eggs package management", + "homepage_url": "https://github.com/pypa/setuptools", + "download_url": "https://files.pythonhosted.org/packages/b2/40/4e00501c204b457f10fe410da0c97537214b2265247bc9a5bc6edd55b9e4/setuptools-44.1.1.zip", + "size": 858770, + "sha1": null, + "md5": "2c41f19cfd1f16a7d7bb23689921ac1b", + "sha256": "c67aa55db532a0dadc4d2e20ba9961cbd3ccc84d544e9029699822542b5a476b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/setuptools/44.1.1/json", + "datasource_id": null, + "purl": "pkg:pypi/setuptools@44.1.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "simplegeneric", + "version": "0.8.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "* New in 0.8: Source and tests are compatible with Python 3 (w/o ``setup.py``)\r\n\r\n * 0.8.1: setup.py is now compatible with Python 3 as well\r\n\r\n* New in 0.7: `Multiple Types or Objects`_\r\n\r\n* New in 0.6: `Inspection and Extension`_, and thread-safe method registration\r\n\r\nThe ``simplegeneric`` module lets you define simple single-dispatch\r\ngeneric functions, akin to Python's built-in generic functions like\r\n``len()``, ``iter()`` and so on. However, instead of using\r\nspecially-named methods, these generic functions use simple lookup\r\ntables, akin to those used by e.g. ``pickle.dump()`` and other\r\ngeneric functions found in the Python standard library.\r\n\r\nAs you can see from the above examples, generic functions are actually\r\nquite common in Python already, but there is no standard way to create\r\nsimple ones. This library attempts to fill that gap, as generic\r\nfunctions are an `excellent alternative to the Visitor pattern`_, as\r\nwell as being a great substitute for most common uses of adaptation.\r\n\r\nThis library tries to be the simplest possible implementation of generic\r\nfunctions, and it therefore eschews the use of multiple or predicate\r\ndispatch, as well as avoiding speedup techniques such as C dispatching\r\nor code generation. But it has absolutely no dependencies, other than\r\nPython 2.4, and the implementation is just a single Python module of\r\nless than 100 lines.\r\n\r\n\r\nUsage\r\n-----\r\n\r\nDefining and using a generic function is straightforward::\r\n\r\n >>> from simplegeneric import generic\r\n >>> @generic\r\n ... def move(item, target):\r\n ... \"\"\"Default implementation goes here\"\"\"\r\n ... print(\"what you say?!\")\r\n\r\n >>> @move.when_type(int)\r\n ... def move_int(item, target):\r\n ... print(\"In AD %d, %s was beginning.\" % (item, target))\r\n\r\n >>> @move.when_type(str)\r\n ... def move_str(item, target):\r\n ... print(\"How are you %s!!\" % item)\r\n ... print(\"All your %s are belong to us.\" % (target,))\r\n\r\n >>> zig = object()\r\n >>> @move.when_object(zig)\r\n ... def move_zig(item, target):\r\n ... print(\"You know what you %s.\" % (target,))\r\n ... print(\"For great justice!\")\r\n\r\n >>> move(2101, \"war\")\r\n In AD 2101, war was beginning.\r\n\r\n >>> move(\"gentlemen\", \"base\")\r\n How are you gentlemen!!\r\n All your base are belong to us.\r\n\r\n >>> move(zig, \"doing\")\r\n You know what you doing.\r\n For great justice!\r\n\r\n >>> move(27.0, 56.2)\r\n what you say?!\r\n\r\n\r\nInheritance and Allowed Types\r\n-----------------------------\r\n\r\nDefining multiple methods for the same type or object is an error::\r\n\r\n >>> @move.when_type(str)\r\n ... def this_is_wrong(item, target):\r\n ... pass\r\n Traceback (most recent call last):\r\n ...\r\n TypeError: already has method for type <...'str'>\r\n\r\n >>> @move.when_object(zig)\r\n ... def this_is_wrong(item, target): pass\r\n Traceback (most recent call last):\r\n ...\r\n TypeError: already has method for object \r\n\r\nAnd the ``when_type()`` decorator only accepts classes or types::\r\n\r\n >>> @move.when_type(23)\r\n ... def move_23(item, target):\r\n ... print(\"You have no chance to survive!\")\r\n Traceback (most recent call last):\r\n ...\r\n TypeError: 23 is not a type or class\r\n\r\nMethods defined for supertypes are inherited following MRO order::\r\n\r\n >>> class MyString(str):\r\n ... \"\"\"String subclass\"\"\"\r\n\r\n >>> move(MyString(\"ladies\"), \"drinks\")\r\n How are you ladies!!\r\n All your drinks are belong to us.\r\n\r\nClassic class instances are also supported (although the lookup process\r\nis slower than for new-style instances)::\r\n\r\n >>> class X: pass\r\n >>> class Y(X): pass\r\n\r\n >>> @move.when_type(X)\r\n ... def move_x(item, target):\r\n ... print(\"Someone set us up the %s!!!\" % (target,))\r\n\r\n >>> move(X(), \"bomb\")\r\n Someone set us up the bomb!!!\r\n\r\n >>> move(Y(), \"dance\")\r\n Someone set us up the dance!!!\r\n\r\n\r\nMultiple Types or Objects\r\n-------------------------\r\n\r\nAs a convenience, you can now pass more than one type or object to the\r\nregistration methods::\r\n\r\n >>> @generic\r\n ... def isbuiltin(ob):\r\n ... return False\r\n >>> @isbuiltin.when_type(int, str, float, complex, type)\r\n ... @isbuiltin.when_object(None, Ellipsis)\r\n ... def yes(ob):\r\n ... return True\r\n \r\n >>> isbuiltin(1)\r\n True\r\n >>> isbuiltin(object)\r\n True\r\n >>> isbuiltin(object())\r\n False\r\n >>> isbuiltin(X())\r\n False\r\n >>> isbuiltin(None)\r\n True\r\n >>> isbuiltin(Ellipsis)\r\n True\r\n\r\n\r\nDefaults and Docs\r\n-----------------\r\n\r\nYou can obtain a function's default implementation using its ``default``\r\nattribute::\r\n\r\n >>> @move.when_type(Y)\r\n ... def move_y(item, target):\r\n ... print(\"Someone set us up the %s!!!\" % (target,))\r\n ... move.default(item, target)\r\n\r\n >>> move(Y(), \"dance\")\r\n Someone set us up the dance!!!\r\n what you say?!\r\n\r\n\r\n``help()`` and other documentation tools see generic functions as normal\r\nfunction objects, with the same name, attributes, docstring, and module as\r\nthe prototype/default function::\r\n\r\n >>> help(move)\r\n Help on function move:\r\n ...\r\n move(*args, **kw)\r\n Default implementation goes here\r\n ...\r\n\r\n\r\nInspection and Extension\r\n------------------------\r\n\r\nYou can find out if a generic function has a method for a type or object using\r\nthe ``has_object()`` and ``has_type()`` methods::\r\n\r\n >>> move.has_object(zig)\r\n True\r\n >>> move.has_object(42)\r\n False\r\n\r\n >>> move.has_type(X)\r\n True\r\n >>> move.has_type(float)\r\n False\r\n\r\nNote that ``has_type()`` only queries whether there is a method registered for\r\nthe *exact* type, not subtypes or supertypes::\r\n\r\n >>> class Z(X): pass\r\n >>> move.has_type(Z)\r\n False\r\n\r\nYou can create a generic function that \"inherits\" from an existing generic\r\nfunction by calling ``generic()`` on the existing function::\r\n\r\n >>> move2 = generic(move)\r\n >>> move(2101, \"war\")\r\n In AD 2101, war was beginning.\r\n\r\nAny methods added to the new generic function override *all* methods in the\r\n\"base\" function::\r\n\r\n >>> @move2.when_type(X)\r\n ... def move2_X(item, target):\r\n ... print(\"You have no chance to survive make your %s!\" % (target,))\r\n\r\n >>> move2(X(), \"time\")\r\n You have no chance to survive make your time!\r\n\r\n >>> move2(Y(), \"time\")\r\n You have no chance to survive make your time!\r\n\r\nNotice that even though ``move()`` has a method for type ``Y``, the method\r\ndefined for ``X`` in ``move2()`` takes precedence. This is because the\r\n``move`` function is used as the ``default`` method of ``move2``, and ``move2``\r\nhas no method for type ``Y``::\r\n\r\n >>> move2.default is move\r\n True\r\n >>> move.has_type(Y)\r\n True\r\n >>> move2.has_type(Y)\r\n False\r\n\r\n\r\nLimitations\r\n-----------\r\n\r\n* The first argument is always used for dispatching, and it must always be\r\n passed *positionally* when the function is called.\r\n\r\n* Documentation tools don't see the function's original argument signature, so\r\n you have to describe it in the docstring.\r\n\r\n* If you have optional arguments, you must duplicate them on every method in\r\n order for them to work correctly. (On the plus side, it means you can have\r\n different defaults or required arguments for each method, although relying on\r\n that quirk probably isn't a good idea.)\r\n\r\nThese restrictions may be lifted in later releases, if I feel the need. They\r\nwould require runtime code generation the way I do it in ``RuleDispatch``,\r\nhowever, which is somewhat of a pain. (Alternately I could use the\r\n``BytecodeAssembler`` package to do the code generation, as that's a lot easier\r\nto use than string-based code generation, but that would introduce more\r\ndependencies, and I'm trying to keep this simple so I can just\r\ntoss it into Chandler without a big footprint increase.)\r\n\r\n.. _excellent alternative to the Visitor pattern: http://peak.telecommunity.com/DevCenter/VisitorRevisited", + "release_date": "2012-04-01T23:39:06", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Phillip J. Eby", + "email": "peak@eby-sarna.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://cheeseshop.python.org/pypi/simplegeneric", + "download_url": "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip", + "size": 12663, + "sha1": null, + "md5": "f9c1fab00fd981be588fc32759f474e3", + "sha256": "dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "ZPL 2.1", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/simplegeneric/0.8.1/json", + "datasource_id": null, + "purl": "pkg:pypi/simplegeneric@0.8.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "six", + "version": "1.16.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/six.svg\n :target: https://pypi.org/project/six/\n :alt: six on PyPI\n\n.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master\n :target: https://travis-ci.org/benjaminp/six\n :alt: six on TravisCI\n\n.. image:: https://readthedocs.org/projects/six/badge/?version=latest\n :target: https://six.readthedocs.io/\n :alt: six's documentation on Read the Docs\n\n.. image:: https://img.shields.io/badge/license-MIT-green.svg\n :target: https://github.com/benjaminp/six/blob/master/LICENSE\n :alt: MIT License badge\n\nSix is a Python 2 and 3 compatibility library. It provides utility functions\nfor smoothing over the differences between the Python versions with the goal of\nwriting Python code that is compatible on both Python versions. See the\ndocumentation for more information on what is provided.\n\nSix supports Python 2.7 and 3.3+. It is contained in only one Python\nfile, so it can be easily copied into your project. (The copyright and license\nnotice must be retained.)\n\nOnline documentation is at https://six.readthedocs.io/.\n\nBugs can be reported to https://github.com/benjaminp/six. The code can also\nbe found there.\n\n\n", + "release_date": "2021-05-05T14:18:17", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Benjamin Peterson", + "email": "benjamin@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benjaminp/six", + "download_url": "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", + "size": 11053, + "sha1": null, + "md5": "529d7fd7e14612ccde86417b4402d6f3", + "sha256": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/six/1.16.0/json", + "datasource_id": null, + "purl": "pkg:pypi/six@1.16.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "six", + "version": "1.16.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/six.svg\n :target: https://pypi.org/project/six/\n :alt: six on PyPI\n\n.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master\n :target: https://travis-ci.org/benjaminp/six\n :alt: six on TravisCI\n\n.. image:: https://readthedocs.org/projects/six/badge/?version=latest\n :target: https://six.readthedocs.io/\n :alt: six's documentation on Read the Docs\n\n.. image:: https://img.shields.io/badge/license-MIT-green.svg\n :target: https://github.com/benjaminp/six/blob/master/LICENSE\n :alt: MIT License badge\n\nSix is a Python 2 and 3 compatibility library. It provides utility functions\nfor smoothing over the differences between the Python versions with the goal of\nwriting Python code that is compatible on both Python versions. See the\ndocumentation for more information on what is provided.\n\nSix supports Python 2.7 and 3.3+. It is contained in only one Python\nfile, so it can be easily copied into your project. (The copyright and license\nnotice must be retained.)\n\nOnline documentation is at https://six.readthedocs.io/.\n\nBugs can be reported to https://github.com/benjaminp/six. The code can also\nbe found there.\n\n\n", + "release_date": "2021-05-05T14:18:18", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Benjamin Peterson", + "email": "benjamin@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benjaminp/six", + "download_url": "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", + "size": 34041, + "sha1": null, + "md5": "a7c927740e4964dd29b72cebfc1429bb", + "sha256": "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/six/1.16.0/json", + "datasource_id": null, + "purl": "pkg:pypi/six@1.16.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "speaklater", + "version": "1.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "speaklater\n~~~~~~~~~~\n\nA module that provides lazy strings for translations. Basically you\nget an object that appears to be a string but changes the value every\ntime the value is evaluated based on a callable you provide.\n\nFor example you can have a global `lazy_gettext` function that returns\na lazy string with the value of the current set language.\n\nExample:\n\n>>> from speaklater import make_lazy_string\n>>> sval = u'Hello World'\n>>> string = make_lazy_string(lambda: sval)\n\nThis lazy string will evaluate to the value of the `sval` variable.\n\n>>> string\nlu'Hello World'\n>>> unicode(string)\nu'Hello World'\n>>> string.upper()\nu'HELLO WORLD'\n\nIf you change the value, the lazy string will change as well:\n\n>>> sval = u'Hallo Welt'\n>>> string.upper()\nu'HALLO WELT'\n\nThis is especially handy when combined with a thread local and gettext\ntranslations or dicts of translatable strings:\n\n>>> from speaklater import make_lazy_gettext\n>>> from threading import local\n>>> l = local()\n>>> l.translations = {u'Yes': 'Ja'}\n>>> lazy_gettext = make_lazy_gettext(lambda: l.translations.get)\n>>> yes = lazy_gettext(u'Yes')\n>>> print yes\nJa\n>>> l.translations[u'Yes'] = u'Si'\n>>> print yes\nSi\n\nLazy strings are no real strings so if you pass this sort of string to\na function that performs an instance check, it will fail. In that case\nyou have to explicitly convert it with `unicode` and/or `string` depending\non what string type the lazy string encapsulates.\n\nTo check if a string is lazy, you can use the `is_lazy_string` function:\n\n>>> from speaklater import is_lazy_string\n>>> is_lazy_string(u'yes')\nFalse\n>>> is_lazy_string(yes)\nTrue\n\nNew in version 1.2: It's now also possible to pass keyword arguments to\nthe callback used with `make_lazy_string`.", + "release_date": "2012-07-01T18:01:30", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": null, + "email": null, + "url": null + } + ], + "keywords": [], + "homepage_url": "http://github.com/mitsuhiko/speaklater", + "download_url": "https://files.pythonhosted.org/packages/11/92/5ae1effe0ccb8561c034a0111d53c8788660ddb7ed4992f0da1bb5c525e5/speaklater-1.3.tar.gz", + "size": 3582, + "sha1": null, + "md5": "e8d5dbe36e53d5a35cff227e795e8bbf", + "sha256": "59fea336d0eed38c1f0bf3181ee1222d0ef45f3a9dd34ebe65e6bfffdd6a65a9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "UNKNOWN", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/speaklater/1.3/json", + "datasource_id": null, + "purl": "pkg:pypi/speaklater@1.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "traitlets", + "version": "4.3.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "A configuration system for Python applications.\n\n\n", + "release_date": "2019-10-03T12:52:05", + "parties": [ + { + "type": "person", + "role": "author", + "name": "IPython Development Team", + "email": "ipython-dev@scipy.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Interactive,Interpreter,Shell,Web", + "homepage_url": "http://ipython.org", + "download_url": "https://files.pythonhosted.org/packages/ca/ab/872a23e29cec3cf2594af7e857f18b687ad21039c1f9b922fac5b9b142d5/traitlets-4.3.3-py2.py3-none-any.whl", + "size": 75680, + "sha1": null, + "md5": "ab0e31302283c86359e23c7789d75630", + "sha256": "70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/traitlets/4.3.3/json", + "datasource_id": null, + "purl": "pkg:pypi/traitlets@4.3.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "traitlets", + "version": "4.3.3", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "A configuration system for Python applications.\n\n\n", + "release_date": "2019-10-03T12:52:07", + "parties": [ + { + "type": "person", + "role": "author", + "name": "IPython Development Team", + "email": "ipython-dev@scipy.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "Interactive,Interpreter,Shell,Web", + "homepage_url": "http://ipython.org", + "download_url": "https://files.pythonhosted.org/packages/75/b0/43deb021bc943f18f07cbe3dac1d681626a48997b7ffa1e7fb14ef922b21/traitlets-4.3.3.tar.gz", + "size": 89838, + "sha1": null, + "md5": "3a4f263af65d3d79f1c279f0247077ef", + "sha256": "d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/traitlets/4.3.3/json", + "datasource_id": null, + "purl": "pkg:pypi/traitlets@4.3.3" + }, + { + "type": "pypi", + "namespace": null, + "name": "typing", + "version": "3.10.0.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Typing -- Type Hints for Python\n\nThis is a backport of the standard library typing module to Python\nversions older than 3.5. (See note below for newer versions.)\n\nTyping defines a standard notation for Python function and variable\ntype annotations. The notation can be used for documenting code in a\nconcise, standard format, and it has been designed to also be used by\nstatic and runtime type checkers, static analyzers, IDEs and other\ntools.\n\nNOTE: in Python 3.5 and later, the typing module lives in the stdlib,\nand installing this package has NO EFFECT, because stdlib takes higher\nprecedence than the installation directory. To get a newer version of\nthe typing module in Python 3.5 or later, you have to upgrade to a\nnewer Python (bugfix) version. For example, typing in Python 3.6.0 is\nmissing the definition of 'Type' -- upgrading to 3.6.2 will fix this.\n\nAlso note that most improvements to the typing module in Python 3.7\nwill not be included in this package, since Python 3.7 has some\nbuilt-in support that is not present in older versions (See PEP 560.)\n\nFor package maintainers, it is preferred to use\n``typing;python_version<\"3.5\"`` if your package requires it to support\nearlier Python versions. This will avoid shadowing the stdlib typing\nmodule when your package is installed via ``pip install -t .`` on\nPython 3.5 or later.\n\n\n", + "release_date": "2021-05-01T18:03:55", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Guido van Rossum, Jukka Lehtosalo, \u0141ukasz Langa, Ivan Levkivskyi", + "email": "jukka.lehtosalo@iki.fi", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "typing function annotations type hints hinting checking checker typehints typehinting typechecking backport", + "homepage_url": "https://docs.python.org/3/library/typing.html", + "download_url": "https://files.pythonhosted.org/packages/0b/cb/da856e81731833b94da70a08712f658416266a5fb2a9d9e426c8061becef/typing-3.10.0.0-py2-none-any.whl", + "size": 26511, + "sha1": null, + "md5": "d98360a1877c628b048fecc5da649413", + "sha256": "c7219ef20c5fbf413b4567092adfc46fa6203cb8454eda33c3fc1afe1398a308", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/python/typing", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "PSF", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/typing/3.10.0.0/json", + "datasource_id": null, + "purl": "pkg:pypi/typing@3.10.0.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "typing", + "version": "3.10.0.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Typing -- Type Hints for Python\n\nThis is a backport of the standard library typing module to Python\nversions older than 3.5. (See note below for newer versions.)\n\nTyping defines a standard notation for Python function and variable\ntype annotations. The notation can be used for documenting code in a\nconcise, standard format, and it has been designed to also be used by\nstatic and runtime type checkers, static analyzers, IDEs and other\ntools.\n\nNOTE: in Python 3.5 and later, the typing module lives in the stdlib,\nand installing this package has NO EFFECT, because stdlib takes higher\nprecedence than the installation directory. To get a newer version of\nthe typing module in Python 3.5 or later, you have to upgrade to a\nnewer Python (bugfix) version. For example, typing in Python 3.6.0 is\nmissing the definition of 'Type' -- upgrading to 3.6.2 will fix this.\n\nAlso note that most improvements to the typing module in Python 3.7\nwill not be included in this package, since Python 3.7 has some\nbuilt-in support that is not present in older versions (See PEP 560.)\n\nFor package maintainers, it is preferred to use\n``typing;python_version<\"3.5\"`` if your package requires it to support\nearlier Python versions. This will avoid shadowing the stdlib typing\nmodule when your package is installed via ``pip install -t .`` on\nPython 3.5 or later.\n\n\n", + "release_date": "2021-05-01T18:03:58", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Guido van Rossum, Jukka Lehtosalo, \u0141ukasz Langa, Ivan Levkivskyi", + "email": "jukka.lehtosalo@iki.fi", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "typing function annotations type hints hinting checking checker typehints typehinting typechecking backport", + "homepage_url": "https://docs.python.org/3/library/typing.html", + "download_url": "https://files.pythonhosted.org/packages/b0/1b/835d4431805939d2996f8772aca1d2313a57e8860fec0e48e8e7dfe3a477/typing-3.10.0.0.tar.gz", + "size": 78962, + "sha1": null, + "md5": "d6dd450cfe0c8c6547eef09a0491775d", + "sha256": "13b4ad211f54ddbf93e5901a9967b1e07720c1d1b78d596ac6a439641aa1b130", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/python/typing", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "PSF", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/typing/3.10.0.0/json", + "datasource_id": null, + "purl": "pkg:pypi/typing@3.10.0.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "uritools", + "version": "2.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "uritools\n========================================================================\n\nThis module defines RFC 3986 compliant replacements for the most\ncommonly used functions of the Python 2.7 Standard Library\n``urlparse`` and Python 3 ``urllib.parse`` modules.\n\n.. code-block:: pycon\n\n >>> from uritools import uricompose, urijoin, urisplit, uriunsplit\n >>> uricompose(scheme='foo', host='example.com', port=8042,\n ... path='/over/there', query={'name': 'ferret'},\n ... fragment='nose')\n 'foo://example.com:8042/over/there?name=ferret#nose'\n >>> parts = urisplit(_)\n >>> parts.scheme\n 'foo'\n >>> parts.authority\n 'example.com:8042'\n >>> parts.getport(default=80)\n 8042\n >>> parts.getquerydict().get('name')\n ['ferret']\n >>> urijoin(uriunsplit(parts), '/right/here?name=swallow#beak')\n 'foo://example.com:8042/right/here?name=swallow#beak'\n\nFor various reasons, the Python 2 ``urlparse`` module is not compliant\nwith current Internet standards, does not include Unicode support, and\nis generally unusable with proprietary URI schemes. Python 3's\n``urllib.parse`` improves on Unicode support, but the other issues still\nremain. As stated in `Lib/urllib/parse.py\n`_::\n\n RFC 3986 is considered the current standard and any future changes\n to urlparse module should conform with it. The urlparse module is\n currently not entirely compliant with this RFC due to defacto\n scenarios for parsing, and for backward compatibility purposes,\n some parsing quirks from older RFCs are retained.\n\nThis module aims to provide fully RFC 3986 compliant replacements for\nsome commonly used functions found in ``urlparse`` and\n``urllib.parse``, plus additional functions for conveniently composing\nURIs from their individual components.\n\n\nInstallation\n------------------------------------------------------------------------\n\nInstall uritools using pip::\n\n pip install uritools\n\n\nProject Resources\n------------------------------------------------------------------------\n\n.. image:: http://img.shields.io/pypi/v/uritools.svg?style=flat\n :target: https://pypi.python.org/pypi/uritools/\n :alt: Latest PyPI version\n\n.. image:: http://img.shields.io/travis/tkem/uritools/master.svg?style=flat\n :target: https://travis-ci.org/tkem/uritools/\n :alt: Travis CI build status\n\n.. image:: http://img.shields.io/coveralls/tkem/uritools/master.svg?style=flat\n :target: https://coveralls.io/r/tkem/uritools\n :alt: Test coverage\n\n.. image:: https://readthedocs.org/projects/uritools/badge/?version=latest&style=flat\n :target: http://uritools.readthedocs.io/en/latest/\n :alt: Documentation Status\n\n- `Issue Tracker`_\n- `Source Code`_\n- `Change Log`_\n\n\nLicense\n------------------------------------------------------------------------\n\nCopyright (c) 2014-2018 Thomas Kemmer.\n\nLicensed under the `MIT License`_.\n\n\n.. _Issue Tracker: https://github.com/tkem/uritools/issues/\n.. _Source Code: https://github.com/tkem/uritools/\n.. _Change Log: https://github.com/tkem/uritools/blob/master/CHANGES.rst\n.. _MIT License: http://raw.github.com/tkem/uritools/master/LICENSE\n\n\n", + "release_date": "2018-05-17T20:43:19", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Thomas Kemmer", + "email": "tkemmer@computer.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "uri url urlparse urlsplit urljoin urldefrag", + "homepage_url": "https://github.com/tkem/uritools/", + "download_url": "https://files.pythonhosted.org/packages/8c/5d/ef3cd3c40b4b97f0cb50cee8e4c5a8a4abc30953e1c7ce7e0d25cb2534c3/uritools-2.2.0-py2.py3-none-any.whl", + "size": 14637, + "sha1": null, + "md5": "2c345e371209cd50279b5f04b66685fd", + "sha256": "522c2027e51e70e0cc40aa703fbf8665a879776826317ff1b68069084030975b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/uritools/2.2.0/json", + "datasource_id": null, + "purl": "pkg:pypi/uritools@2.2.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "uritools", + "version": "2.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "uritools\n========================================================================\n\nThis module defines RFC 3986 compliant replacements for the most\ncommonly used functions of the Python 2.7 Standard Library\n``urlparse`` and Python 3 ``urllib.parse`` modules.\n\n.. code-block:: pycon\n\n >>> from uritools import uricompose, urijoin, urisplit, uriunsplit\n >>> uricompose(scheme='foo', host='example.com', port=8042,\n ... path='/over/there', query={'name': 'ferret'},\n ... fragment='nose')\n 'foo://example.com:8042/over/there?name=ferret#nose'\n >>> parts = urisplit(_)\n >>> parts.scheme\n 'foo'\n >>> parts.authority\n 'example.com:8042'\n >>> parts.getport(default=80)\n 8042\n >>> parts.getquerydict().get('name')\n ['ferret']\n >>> urijoin(uriunsplit(parts), '/right/here?name=swallow#beak')\n 'foo://example.com:8042/right/here?name=swallow#beak'\n\nFor various reasons, the Python 2 ``urlparse`` module is not compliant\nwith current Internet standards, does not include Unicode support, and\nis generally unusable with proprietary URI schemes. Python 3's\n``urllib.parse`` improves on Unicode support, but the other issues still\nremain. As stated in `Lib/urllib/parse.py\n`_::\n\n RFC 3986 is considered the current standard and any future changes\n to urlparse module should conform with it. The urlparse module is\n currently not entirely compliant with this RFC due to defacto\n scenarios for parsing, and for backward compatibility purposes,\n some parsing quirks from older RFCs are retained.\n\nThis module aims to provide fully RFC 3986 compliant replacements for\nsome commonly used functions found in ``urlparse`` and\n``urllib.parse``, plus additional functions for conveniently composing\nURIs from their individual components.\n\n\nInstallation\n------------------------------------------------------------------------\n\nInstall uritools using pip::\n\n pip install uritools\n\n\nProject Resources\n------------------------------------------------------------------------\n\n.. image:: http://img.shields.io/pypi/v/uritools.svg?style=flat\n :target: https://pypi.python.org/pypi/uritools/\n :alt: Latest PyPI version\n\n.. image:: http://img.shields.io/travis/tkem/uritools/master.svg?style=flat\n :target: https://travis-ci.org/tkem/uritools/\n :alt: Travis CI build status\n\n.. image:: http://img.shields.io/coveralls/tkem/uritools/master.svg?style=flat\n :target: https://coveralls.io/r/tkem/uritools\n :alt: Test coverage\n\n.. image:: https://readthedocs.org/projects/uritools/badge/?version=latest&style=flat\n :target: http://uritools.readthedocs.io/en/latest/\n :alt: Documentation Status\n\n- `Issue Tracker`_\n- `Source Code`_\n- `Change Log`_\n\n\nLicense\n------------------------------------------------------------------------\n\nCopyright (c) 2014-2018 Thomas Kemmer.\n\nLicensed under the `MIT License`_.\n\n\n.. _Issue Tracker: https://github.com/tkem/uritools/issues/\n.. _Source Code: https://github.com/tkem/uritools/\n.. _Change Log: https://github.com/tkem/uritools/blob/master/CHANGES.rst\n.. _MIT License: http://raw.github.com/tkem/uritools/master/LICENSE\n\n\n", + "release_date": "2018-05-17T20:43:20", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Thomas Kemmer", + "email": "tkemmer@computer.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "uri url urlparse urlsplit urljoin urldefrag", + "homepage_url": "https://github.com/tkem/uritools/", + "download_url": "https://files.pythonhosted.org/packages/ab/1c/e9aa4a907806743298171510042447adc20cd5cf5b95436206a067e14496/uritools-2.2.0.tar.gz", + "size": 23906, + "sha1": null, + "md5": "b233ab8184cacf75c5ab3ffd35dac066", + "sha256": "80e8e23cafad54fd85811b5d9ba0fc595d933f5727c61c3937945eec09f99e2b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/uritools/2.2.0/json", + "datasource_id": null, + "purl": "pkg:pypi/uritools@2.2.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "vine", + "version": "1.3.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "=====================================================================\n vine - Python Promises\n=====================================================================\n\n|build-status| |coverage| |license| |wheel| |pyversion| |pyimp|\n\n:Version: 1.3.0\n:Web: https://vine.readthedocs.io/\n:Download: https://pypi.org/project/vine/\n:Source: http://github.com/celery/vine/\n:Keywords: promise, async, future\n\nAbout\n=====\n\n\n.. |build-status| image:: https://secure.travis-ci.org/celery/vine.png?branch=master\n :alt: Build status\n :target: https://travis-ci.org/celery/vine\n\n.. |coverage| image:: https://codecov.io/github/celery/vine/coverage.svg?branch=master\n :target: https://codecov.io/github/celery/vine?branch=master\n\n.. |license| image:: https://img.shields.io/pypi/l/vine.svg\n :alt: BSD License\n :target: https://opensource.org/licenses/BSD-3-Clause\n\n.. |wheel| image:: https://img.shields.io/pypi/wheel/vine.svg\n :alt: Vine can be installed via wheel\n :target: https://pypi.org/project/vine/\n\n.. |pyversion| image:: https://img.shields.io/pypi/pyversions/vine.svg\n :alt: Supported Python versions.\n :target: https://pypi.org/project/vine/\n\n.. |pyimp| image:: https://img.shields.io/pypi/implementation/vine.svg\n :alt: Support Python implementations.\n :target: https://pypi.org/project/vine/\n\n\n\n", + "release_date": "2019-03-19T08:56:34", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ask Solem", + "email": "ask@celeryproject.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "promise promises lazy future futures", + "homepage_url": "http://github.com/celery/vine", + "download_url": "https://files.pythonhosted.org/packages/7f/60/82c03047396126c8331ceb64da1dc52d4f1317209f32e8fe286d0c07365a/vine-1.3.0-py2.py3-none-any.whl", + "size": 14174, + "sha1": null, + "md5": "b0fd55deefc4e7ae9f04608fe8c238d5", + "sha256": "ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/vine/1.3.0/json", + "datasource_id": null, + "purl": "pkg:pypi/vine@1.3.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "vine", + "version": "1.3.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "=====================================================================\n vine - Python Promises\n=====================================================================\n\n|build-status| |coverage| |license| |wheel| |pyversion| |pyimp|\n\n:Version: 1.3.0\n:Web: https://vine.readthedocs.io/\n:Download: https://pypi.org/project/vine/\n:Source: http://github.com/celery/vine/\n:Keywords: promise, async, future\n\nAbout\n=====\n\n\n.. |build-status| image:: https://secure.travis-ci.org/celery/vine.png?branch=master\n :alt: Build status\n :target: https://travis-ci.org/celery/vine\n\n.. |coverage| image:: https://codecov.io/github/celery/vine/coverage.svg?branch=master\n :target: https://codecov.io/github/celery/vine?branch=master\n\n.. |license| image:: https://img.shields.io/pypi/l/vine.svg\n :alt: BSD License\n :target: https://opensource.org/licenses/BSD-3-Clause\n\n.. |wheel| image:: https://img.shields.io/pypi/wheel/vine.svg\n :alt: Vine can be installed via wheel\n :target: https://pypi.org/project/vine/\n\n.. |pyversion| image:: https://img.shields.io/pypi/pyversions/vine.svg\n :alt: Supported Python versions.\n :target: https://pypi.org/project/vine/\n\n.. |pyimp| image:: https://img.shields.io/pypi/implementation/vine.svg\n :alt: Support Python implementations.\n :target: https://pypi.org/project/vine/\n\n\n\n", + "release_date": "2019-03-19T08:56:37", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Ask Solem", + "email": "ask@celeryproject.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "promise promises lazy future futures", + "homepage_url": "http://github.com/celery/vine", + "download_url": "https://files.pythonhosted.org/packages/1c/e1/79fb8046e607dd6c2ad05c9b8ebac9d0bd31d086a08f02699e96fc5b3046/vine-1.3.0.tar.gz", + "size": 51953, + "sha1": null, + "md5": "5d125e0b4d759b39e03d11902dede8c9", + "sha256": "133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/vine/1.3.0/json", + "datasource_id": null, + "purl": "pkg:pypi/vine@1.3.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "wcwidth", + "version": "0.2.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "|pypi_downloads| |codecov| |license|\n\n============\nIntroduction\n============\n\nThis library is mainly for CLI programs that carefully produce output for\nTerminals, or make pretend to be an emulator.\n\n**Problem Statement**: The printable length of *most* strings are equal to the\nnumber of cells they occupy on the screen ``1 charater : 1 cell``. However,\nthere are categories of characters that *occupy 2 cells* (full-wide), and\nothers that *occupy 0* cells (zero-width).\n\n**Solution**: POSIX.1-2001 and POSIX.1-2008 conforming systems provide\n`wcwidth(3)`_ and `wcswidth(3)`_ C functions of which this python module's\nfunctions precisely copy. *These functions return the number of cells a\nunicode string is expected to occupy.*\n\nInstallation\n------------\n\nThe stable version of this package is maintained on pypi, install using pip::\n\n pip install wcwidth\n\nExample\n-------\n\n**Problem**: given the following phrase (Japanese),\n\n >>> text = u'\u30b3\u30f3\u30cb\u30c1\u30cf'\n\nPython **incorrectly** uses the *string length* of 5 codepoints rather than the\n*printible length* of 10 cells, so that when using the `rjust` function, the\noutput length is wrong::\n\n >>> print(len('\u30b3\u30f3\u30cb\u30c1\u30cf'))\n 5\n\n >>> print('\u30b3\u30f3\u30cb\u30c1\u30cf'.rjust(20, '_'))\n _____\u30b3\u30f3\u30cb\u30c1\u30cf\n\nBy defining our own \"rjust\" function that uses wcwidth, we can correct this::\n\n >>> def wc_rjust(text, length, padding=' '):\n ... from wcwidth import wcswidth\n ... return padding * max(0, (length - wcswidth(text))) + text\n ...\n\nOur **Solution** uses wcswidth to determine the string length correctly::\n\n >>> from wcwidth import wcswidth\n >>> print(wcswidth('\u30b3\u30f3\u30cb\u30c1\u30cf'))\n 10\n\n >>> print(wc_rjust('\u30b3\u30f3\u30cb\u30c1\u30cf', 20, '_'))\n __________\u30b3\u30f3\u30cb\u30c1\u30cf\n\n\nChoosing a Version\n------------------\n\nExport an environment variable, ``UNICODE_VERSION``. This should be done by\n*terminal emulators* or those developers experimenting with authoring one of\ntheir own, from shell::\n\n $ export UNICODE_VERSION=13.0\n\nIf unspecified, the latest version is used. If your Terminal Emulator does not\nexport this variable, you can use the `jquast/ucs-detect`_ utility to\nautomatically detect and export it to your shell.\n\nwcwidth, wcswidth\n-----------------\nUse function ``wcwidth()`` to determine the length of a *single unicode\ncharacter*, and ``wcswidth()`` to determine the length of many, a *string\nof unicode characters*.\n\nBriefly, return values of function ``wcwidth()`` are:\n\n``-1``\n Indeterminate (not printable).\n\n``0``\n Does not advance the cursor, such as NULL or Combining.\n\n``2``\n Characters of category East Asian Wide (W) or East Asian\n Full-width (F) which are displayed using two terminal cells.\n\n``1``\n All others.\n\nFunction ``wcswidth()`` simply returns the sum of all values for each character\nalong a string, or ``-1`` when it occurs anywhere along a string.\n\nFull API Documentation at http://wcwidth.readthedocs.org\n\n==========\nDeveloping\n==========\n\nInstall wcwidth in editable mode::\n\n pip install -e.\n\nExecute unit tests using tox_::\n\n tox\n\nRegenerate python code tables from latest Unicode Specification data files::\n\n tox -eupdate\n\nSupplementary tools for browsing and testing terminals for wide unicode\ncharacters are found in the `bin/`_ of this project's source code. Just ensure\nto first ``pip install -erequirements-develop.txt`` from this projects main\nfolder. For example, an interactive browser for testing::\n\n ./bin/wcwidth-browser.py\n\nUses\n----\n\nThis library is used in:\n\n- `jquast/blessed`_: a thin, practical wrapper around terminal capabilities in\n Python.\n\n- `jonathanslenders/python-prompt-toolkit`_: a Library for building powerful\n interactive command lines in Python.\n\n- `dbcli/pgcli`_: Postgres CLI with autocompletion and syntax highlighting.\n\n- `thomasballinger/curtsies`_: a Curses-like terminal wrapper with a display\n based on compositing 2d arrays of text.\n\n- `selectel/pyte`_: Simple VTXXX-compatible linux terminal emulator.\n\n- `astanin/python-tabulate`_: Pretty-print tabular data in Python, a library\n and a command-line utility.\n\n- `LuminosoInsight/python-ftfy`_: Fixes mojibake and other glitches in Unicode\n text.\n\n- `nbedos/termtosvg`_: Terminal recorder that renders sessions as SVG\n animations.\n\n- `peterbrittain/asciimatics`_: Package to help people create full-screen text\n UIs.\n\nOther Languages\n---------------\n\n- `timoxley/wcwidth`_: JavaScript\n- `janlelis/unicode-display_width`_: Ruby\n- `alecrabbit/php-wcwidth`_: PHP\n- `Text::CharWidth`_: Perl\n- `bluebear94/Terminal-WCWidth`: Perl 6\n- `mattn/go-runewidth`_: Go\n- `emugel/wcwidth`_: Haxe\n- `aperezdc/lua-wcwidth`: Lua\n- `joachimschmidt557/zig-wcwidth`: Zig\n- `fumiyas/wcwidth-cjk`: `LD_PRELOAD` override\n- `joshuarubin/wcwidth9`: Unicode version 9 in C\n\nHistory\n-------\n\n0.2.0 *2020-06-01*\n * **Enhancement**: Unicode version may be selected by exporting the\n Environment variable ``UNICODE_VERSION``, such as ``13.0``, or ``6.3.0``.\n See the `jquast/ucs-detect`_ CLI utility for automatic detection.\n * **Enhancement**:\n API Documentation is published to readthedocs.org.\n * **Updated** tables for *all* Unicode Specifications with files\n published in a programmatically consumable format, versions 4.1.0\n through 13.0\n that are published\n , versions\n\n0.1.9 *2020-03-22*\n * **Performance** optimization by `Avram Lubkin`_, `PR #35`_.\n * **Updated** tables to Unicode Specification 13.0.0.\n\n0.1.8 *2020-01-01*\n * **Updated** tables to Unicode Specification 12.0.0. (`PR #30`_).\n\n0.1.7 *2016-07-01*\n * **Updated** tables to Unicode Specification 9.0.0. (`PR #18`_).\n\n0.1.6 *2016-01-08 Production/Stable*\n * ``LICENSE`` file now included with distribution.\n\n0.1.5 *2015-09-13 Alpha*\n * **Bugfix**:\n Resolution of \"combining_ character width\" issue, most especially\n those that previously returned -1 now often (correctly) return 0.\n resolved by `Philip Craig`_ via `PR #11`_.\n * **Deprecated**:\n The module path ``wcwidth.table_comb`` is no longer available,\n it has been superseded by module path ``wcwidth.table_zero``.\n\n0.1.4 *2014-11-20 Pre-Alpha*\n * **Feature**: ``wcswidth()`` now determines printable length\n for (most) combining_ characters. The developer's tool\n `bin/wcwidth-browser.py`_ is improved to display combining_\n characters when provided the ``--combining`` option\n (`Thomas Ballinger`_ and `Leta Montopoli`_ `PR #5`_).\n * **Feature**: added static analysis (prospector_) to testing\n framework.\n\n0.1.3 *2014-10-29 Pre-Alpha*\n * **Bugfix**: 2nd parameter of wcswidth was not honored.\n (`Thomas Ballinger`_, `PR #4`_).\n\n0.1.2 *2014-10-28 Pre-Alpha*\n * **Updated** tables to Unicode Specification 7.0.0.\n (`Thomas Ballinger`_, `PR #3`_).\n\n0.1.1 *2014-05-14 Pre-Alpha*\n * Initial release to pypi, Based on Unicode Specification 6.3.0\n\nThis code was originally derived directly from C code of the same name,\nwhose latest version is available at\nhttp://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::\n\n * Markus Kuhn -- 2007-05-26 (Unicode 5.0)\n *\n * Permission to use, copy, modify, and distribute this software\n * for any purpose and without fee is hereby granted. The author\n * disclaims all warranties with regard to this software.\n\n.. _`tox`: https://testrun.org/tox/latest/install.html\n.. _`prospector`: https://github.com/landscapeio/prospector\n.. _`combining`: https://en.wikipedia.org/wiki/Combining_character\n.. _`bin/`: https://github.com/jquast/wcwidth/tree/master/bin\n.. _`bin/wcwidth-browser.py`: https://github.com/jquast/wcwidth/tree/master/bin/wcwidth-browser.py\n.. _`Thomas Ballinger`: https://github.com/thomasballinger\n.. _`Leta Montopoli`: https://github.com/lmontopo\n.. _`Philip Craig`: https://github.com/philipc\n.. _`PR #3`: https://github.com/jquast/wcwidth/pull/3\n.. _`PR #4`: https://github.com/jquast/wcwidth/pull/4\n.. _`PR #5`: https://github.com/jquast/wcwidth/pull/5\n.. _`PR #11`: https://github.com/jquast/wcwidth/pull/11\n.. _`PR #18`: https://github.com/jquast/wcwidth/pull/18\n.. _`PR #30`: https://github.com/jquast/wcwidth/pull/30\n.. _`PR #35`: https://github.com/jquast/wcwidth/pull/35\n.. _`jquast/blessed`: https://github.com/jquast/blessed\n.. _`selectel/pyte`: https://github.com/selectel/pyte\n.. _`thomasballinger/curtsies`: https://github.com/thomasballinger/curtsies\n.. _`dbcli/pgcli`: https://github.com/dbcli/pgcli\n.. _`jonathanslenders/python-prompt-toolkit`: https://github.com/jonathanslenders/python-prompt-toolkit\n.. _`timoxley/wcwidth`: https://github.com/timoxley/wcwidth\n.. _`wcwidth(3)`: http://man7.org/linux/man-pages/man3/wcwidth.3.html\n.. _`wcswidth(3)`: http://man7.org/linux/man-pages/man3/wcswidth.3.html\n.. _`astanin/python-tabulate`: https://github.com/astanin/python-tabulate\n.. _`janlelis/unicode-display_width`: https://github.com/janlelis/unicode-display_width\n.. _`LuminosoInsight/python-ftfy`: https://github.com/LuminosoInsight/python-ftfy\n.. _`alecrabbit/php-wcwidth`: https://github.com/alecrabbit/php-wcwidth\n.. _`Text::CharWidth`: https://metacpan.org/pod/Text::CharWidth\n.. _`bluebear94/Terminal-WCWidth`: https://github.com/bluebear94/Terminal-WCWidth\n.. _`mattn/go-runewidth`: https://github.com/mattn/go-runewidth\n.. _`emugel/wcwidth`: https://github.com/emugel/wcwidth\n.. _`jquast/ucs-detect`: https://github.com/jquast/ucs-detect\n.. _`Avram Lubkin`: https://github.com/avylove\n.. _`nbedos/termtosvg`: https://github.com/nbedos/termtosvg\n.. _`peterbrittain/asciimatics`: https://github.com/peterbrittain/asciimatics\n.. _`aperezdc/lua-wcwidth`: https://github.com/aperezdc/lua-wcwidth\n.. _`fumiyas/wcwidth-cjk`: https://github.com/fumiyas/wcwidth-cjk\n.. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi\n :alt: Downloads\n :target: https://pypi.org/project/wcwidth/\n.. |codecov| image:: https://codecov.io/gh/jquast/wcwidth/branch/master/graph/badge.svg\n :alt: codecov.io Code Coverage\n :target: https://codecov.io/gh/jquast/wcwidth/\n.. |license| image:: https://img.shields.io/github/license/jquast/wcwidth.svg\n :target: https://pypi.python.org/pypi/wcwidth/\n :alt: MIT License\n\n\n", + "release_date": "2020-06-23T16:10:28", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jeff Quast", + "email": "contact@jeffquast.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "cjk,combining,console,eastasian,emojiemulator,terminal,unicode,wcswidth,wcwidth,xterm", + "homepage_url": "https://github.com/jquast/wcwidth", + "download_url": "https://files.pythonhosted.org/packages/59/7c/e39aca596badaf1b78e8f547c807b04dae603a433d3e7a7e04d67f2ef3e5/wcwidth-0.2.5-py2.py3-none-any.whl", + "size": 30763, + "sha1": null, + "md5": "8b664207c7e30fc97917d2ac444e2232", + "sha256": "beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/wcwidth/0.2.5/json", + "datasource_id": null, + "purl": "pkg:pypi/wcwidth@0.2.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "wcwidth", + "version": "0.2.5", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "|pypi_downloads| |codecov| |license|\n\n============\nIntroduction\n============\n\nThis library is mainly for CLI programs that carefully produce output for\nTerminals, or make pretend to be an emulator.\n\n**Problem Statement**: The printable length of *most* strings are equal to the\nnumber of cells they occupy on the screen ``1 charater : 1 cell``. However,\nthere are categories of characters that *occupy 2 cells* (full-wide), and\nothers that *occupy 0* cells (zero-width).\n\n**Solution**: POSIX.1-2001 and POSIX.1-2008 conforming systems provide\n`wcwidth(3)`_ and `wcswidth(3)`_ C functions of which this python module's\nfunctions precisely copy. *These functions return the number of cells a\nunicode string is expected to occupy.*\n\nInstallation\n------------\n\nThe stable version of this package is maintained on pypi, install using pip::\n\n pip install wcwidth\n\nExample\n-------\n\n**Problem**: given the following phrase (Japanese),\n\n >>> text = u'\u30b3\u30f3\u30cb\u30c1\u30cf'\n\nPython **incorrectly** uses the *string length* of 5 codepoints rather than the\n*printible length* of 10 cells, so that when using the `rjust` function, the\noutput length is wrong::\n\n >>> print(len('\u30b3\u30f3\u30cb\u30c1\u30cf'))\n 5\n\n >>> print('\u30b3\u30f3\u30cb\u30c1\u30cf'.rjust(20, '_'))\n _____\u30b3\u30f3\u30cb\u30c1\u30cf\n\nBy defining our own \"rjust\" function that uses wcwidth, we can correct this::\n\n >>> def wc_rjust(text, length, padding=' '):\n ... from wcwidth import wcswidth\n ... return padding * max(0, (length - wcswidth(text))) + text\n ...\n\nOur **Solution** uses wcswidth to determine the string length correctly::\n\n >>> from wcwidth import wcswidth\n >>> print(wcswidth('\u30b3\u30f3\u30cb\u30c1\u30cf'))\n 10\n\n >>> print(wc_rjust('\u30b3\u30f3\u30cb\u30c1\u30cf', 20, '_'))\n __________\u30b3\u30f3\u30cb\u30c1\u30cf\n\n\nChoosing a Version\n------------------\n\nExport an environment variable, ``UNICODE_VERSION``. This should be done by\n*terminal emulators* or those developers experimenting with authoring one of\ntheir own, from shell::\n\n $ export UNICODE_VERSION=13.0\n\nIf unspecified, the latest version is used. If your Terminal Emulator does not\nexport this variable, you can use the `jquast/ucs-detect`_ utility to\nautomatically detect and export it to your shell.\n\nwcwidth, wcswidth\n-----------------\nUse function ``wcwidth()`` to determine the length of a *single unicode\ncharacter*, and ``wcswidth()`` to determine the length of many, a *string\nof unicode characters*.\n\nBriefly, return values of function ``wcwidth()`` are:\n\n``-1``\n Indeterminate (not printable).\n\n``0``\n Does not advance the cursor, such as NULL or Combining.\n\n``2``\n Characters of category East Asian Wide (W) or East Asian\n Full-width (F) which are displayed using two terminal cells.\n\n``1``\n All others.\n\nFunction ``wcswidth()`` simply returns the sum of all values for each character\nalong a string, or ``-1`` when it occurs anywhere along a string.\n\nFull API Documentation at http://wcwidth.readthedocs.org\n\n==========\nDeveloping\n==========\n\nInstall wcwidth in editable mode::\n\n pip install -e.\n\nExecute unit tests using tox_::\n\n tox\n\nRegenerate python code tables from latest Unicode Specification data files::\n\n tox -eupdate\n\nSupplementary tools for browsing and testing terminals for wide unicode\ncharacters are found in the `bin/`_ of this project's source code. Just ensure\nto first ``pip install -erequirements-develop.txt`` from this projects main\nfolder. For example, an interactive browser for testing::\n\n ./bin/wcwidth-browser.py\n\nUses\n----\n\nThis library is used in:\n\n- `jquast/blessed`_: a thin, practical wrapper around terminal capabilities in\n Python.\n\n- `jonathanslenders/python-prompt-toolkit`_: a Library for building powerful\n interactive command lines in Python.\n\n- `dbcli/pgcli`_: Postgres CLI with autocompletion and syntax highlighting.\n\n- `thomasballinger/curtsies`_: a Curses-like terminal wrapper with a display\n based on compositing 2d arrays of text.\n\n- `selectel/pyte`_: Simple VTXXX-compatible linux terminal emulator.\n\n- `astanin/python-tabulate`_: Pretty-print tabular data in Python, a library\n and a command-line utility.\n\n- `LuminosoInsight/python-ftfy`_: Fixes mojibake and other glitches in Unicode\n text.\n\n- `nbedos/termtosvg`_: Terminal recorder that renders sessions as SVG\n animations.\n\n- `peterbrittain/asciimatics`_: Package to help people create full-screen text\n UIs.\n\nOther Languages\n---------------\n\n- `timoxley/wcwidth`_: JavaScript\n- `janlelis/unicode-display_width`_: Ruby\n- `alecrabbit/php-wcwidth`_: PHP\n- `Text::CharWidth`_: Perl\n- `bluebear94/Terminal-WCWidth`: Perl 6\n- `mattn/go-runewidth`_: Go\n- `emugel/wcwidth`_: Haxe\n- `aperezdc/lua-wcwidth`: Lua\n- `joachimschmidt557/zig-wcwidth`: Zig\n- `fumiyas/wcwidth-cjk`: `LD_PRELOAD` override\n- `joshuarubin/wcwidth9`: Unicode version 9 in C\n\nHistory\n-------\n\n0.2.0 *2020-06-01*\n * **Enhancement**: Unicode version may be selected by exporting the\n Environment variable ``UNICODE_VERSION``, such as ``13.0``, or ``6.3.0``.\n See the `jquast/ucs-detect`_ CLI utility for automatic detection.\n * **Enhancement**:\n API Documentation is published to readthedocs.org.\n * **Updated** tables for *all* Unicode Specifications with files\n published in a programmatically consumable format, versions 4.1.0\n through 13.0\n that are published\n , versions\n\n0.1.9 *2020-03-22*\n * **Performance** optimization by `Avram Lubkin`_, `PR #35`_.\n * **Updated** tables to Unicode Specification 13.0.0.\n\n0.1.8 *2020-01-01*\n * **Updated** tables to Unicode Specification 12.0.0. (`PR #30`_).\n\n0.1.7 *2016-07-01*\n * **Updated** tables to Unicode Specification 9.0.0. (`PR #18`_).\n\n0.1.6 *2016-01-08 Production/Stable*\n * ``LICENSE`` file now included with distribution.\n\n0.1.5 *2015-09-13 Alpha*\n * **Bugfix**:\n Resolution of \"combining_ character width\" issue, most especially\n those that previously returned -1 now often (correctly) return 0.\n resolved by `Philip Craig`_ via `PR #11`_.\n * **Deprecated**:\n The module path ``wcwidth.table_comb`` is no longer available,\n it has been superseded by module path ``wcwidth.table_zero``.\n\n0.1.4 *2014-11-20 Pre-Alpha*\n * **Feature**: ``wcswidth()`` now determines printable length\n for (most) combining_ characters. The developer's tool\n `bin/wcwidth-browser.py`_ is improved to display combining_\n characters when provided the ``--combining`` option\n (`Thomas Ballinger`_ and `Leta Montopoli`_ `PR #5`_).\n * **Feature**: added static analysis (prospector_) to testing\n framework.\n\n0.1.3 *2014-10-29 Pre-Alpha*\n * **Bugfix**: 2nd parameter of wcswidth was not honored.\n (`Thomas Ballinger`_, `PR #4`_).\n\n0.1.2 *2014-10-28 Pre-Alpha*\n * **Updated** tables to Unicode Specification 7.0.0.\n (`Thomas Ballinger`_, `PR #3`_).\n\n0.1.1 *2014-05-14 Pre-Alpha*\n * Initial release to pypi, Based on Unicode Specification 6.3.0\n\nThis code was originally derived directly from C code of the same name,\nwhose latest version is available at\nhttp://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::\n\n * Markus Kuhn -- 2007-05-26 (Unicode 5.0)\n *\n * Permission to use, copy, modify, and distribute this software\n * for any purpose and without fee is hereby granted. The author\n * disclaims all warranties with regard to this software.\n\n.. _`tox`: https://testrun.org/tox/latest/install.html\n.. _`prospector`: https://github.com/landscapeio/prospector\n.. _`combining`: https://en.wikipedia.org/wiki/Combining_character\n.. _`bin/`: https://github.com/jquast/wcwidth/tree/master/bin\n.. _`bin/wcwidth-browser.py`: https://github.com/jquast/wcwidth/tree/master/bin/wcwidth-browser.py\n.. _`Thomas Ballinger`: https://github.com/thomasballinger\n.. _`Leta Montopoli`: https://github.com/lmontopo\n.. _`Philip Craig`: https://github.com/philipc\n.. _`PR #3`: https://github.com/jquast/wcwidth/pull/3\n.. _`PR #4`: https://github.com/jquast/wcwidth/pull/4\n.. _`PR #5`: https://github.com/jquast/wcwidth/pull/5\n.. _`PR #11`: https://github.com/jquast/wcwidth/pull/11\n.. _`PR #18`: https://github.com/jquast/wcwidth/pull/18\n.. _`PR #30`: https://github.com/jquast/wcwidth/pull/30\n.. _`PR #35`: https://github.com/jquast/wcwidth/pull/35\n.. _`jquast/blessed`: https://github.com/jquast/blessed\n.. _`selectel/pyte`: https://github.com/selectel/pyte\n.. _`thomasballinger/curtsies`: https://github.com/thomasballinger/curtsies\n.. _`dbcli/pgcli`: https://github.com/dbcli/pgcli\n.. _`jonathanslenders/python-prompt-toolkit`: https://github.com/jonathanslenders/python-prompt-toolkit\n.. _`timoxley/wcwidth`: https://github.com/timoxley/wcwidth\n.. _`wcwidth(3)`: http://man7.org/linux/man-pages/man3/wcwidth.3.html\n.. _`wcswidth(3)`: http://man7.org/linux/man-pages/man3/wcswidth.3.html\n.. _`astanin/python-tabulate`: https://github.com/astanin/python-tabulate\n.. _`janlelis/unicode-display_width`: https://github.com/janlelis/unicode-display_width\n.. _`LuminosoInsight/python-ftfy`: https://github.com/LuminosoInsight/python-ftfy\n.. _`alecrabbit/php-wcwidth`: https://github.com/alecrabbit/php-wcwidth\n.. _`Text::CharWidth`: https://metacpan.org/pod/Text::CharWidth\n.. _`bluebear94/Terminal-WCWidth`: https://github.com/bluebear94/Terminal-WCWidth\n.. _`mattn/go-runewidth`: https://github.com/mattn/go-runewidth\n.. _`emugel/wcwidth`: https://github.com/emugel/wcwidth\n.. _`jquast/ucs-detect`: https://github.com/jquast/ucs-detect\n.. _`Avram Lubkin`: https://github.com/avylove\n.. _`nbedos/termtosvg`: https://github.com/nbedos/termtosvg\n.. _`peterbrittain/asciimatics`: https://github.com/peterbrittain/asciimatics\n.. _`aperezdc/lua-wcwidth`: https://github.com/aperezdc/lua-wcwidth\n.. _`fumiyas/wcwidth-cjk`: https://github.com/fumiyas/wcwidth-cjk\n.. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi\n :alt: Downloads\n :target: https://pypi.org/project/wcwidth/\n.. |codecov| image:: https://codecov.io/gh/jquast/wcwidth/branch/master/graph/badge.svg\n :alt: codecov.io Code Coverage\n :target: https://codecov.io/gh/jquast/wcwidth/\n.. |license| image:: https://img.shields.io/github/license/jquast/wcwidth.svg\n :target: https://pypi.python.org/pypi/wcwidth/\n :alt: MIT License\n\n\n", + "release_date": "2020-06-23T16:10:29", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jeff Quast", + "email": "contact@jeffquast.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": "cjk,combining,console,eastasian,emojiemulator,terminal,unicode,wcswidth,wcwidth,xterm", + "homepage_url": "https://github.com/jquast/wcwidth", + "download_url": "https://files.pythonhosted.org/packages/89/38/459b727c381504f361832b9e5ace19966de1a235d73cdbdea91c771a1155/wcwidth-0.2.5.tar.gz", + "size": 34755, + "sha1": null, + "md5": "a07a75f99d316e14838ac760c831ea37", + "sha256": "c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/wcwidth/0.2.5/json", + "datasource_id": null, + "purl": "pkg:pypi/wcwidth@0.2.5" + }, + { + "type": "pypi", + "namespace": null, + "name": "werkzeug", + "version": "1.0.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Werkzeug\n========\n\n*werkzeug* German noun: \"tool\". Etymology: *werk* (\"work\"), *zeug* (\"stuff\")\n\nWerkzeug is a comprehensive `WSGI`_ web application library. It began as\na simple collection of various utilities for WSGI applications and has\nbecome one of the most advanced WSGI utility libraries.\n\nIt includes:\n\n- An interactive debugger that allows inspecting stack traces and\n source code in the browser with an interactive interpreter for any\n frame in the stack.\n- A full-featured request object with objects to interact with\n headers, query args, form data, files, and cookies.\n- A response object that can wrap other WSGI applications and handle\n streaming data.\n- A routing system for matching URLs to endpoints and generating URLs\n for endpoints, with an extensible system for capturing variables\n from URLs.\n- HTTP utilities to handle entity tags, cache control, dates, user\n agents, cookies, files, and more.\n- A threaded WSGI server for use while developing applications\n locally.\n- A test client for simulating HTTP requests during testing without\n requiring running a server.\n\nWerkzeug is Unicode aware and doesn't enforce any dependencies. It is up\nto the developer to choose a template engine, database adapter, and even\nhow to handle requests. It can be used to build all sorts of end user\napplications such as blogs, wikis, or bulletin boards.\n\n`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while\nproviding more structure and patterns for defining powerful\napplications.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U Werkzeug\n\n\nA Simple Example\n----------------\n\n.. code-block:: python\n\n from werkzeug.wrappers import Request, Response\n\n @Request.application\n def application(request):\n return Response('Hello, World!')\n\n if __name__ == '__main__':\n from werkzeug.serving import run_simple\n run_simple('localhost', 4000, application)\n\n\nLinks\n-----\n\n- Website: https://palletsprojects.com/p/werkzeug/\n- Documentation: https://werkzeug.palletsprojects.com/\n- Releases: https://pypi.org/project/Werkzeug/\n- Code: https://github.com/pallets/werkzeug\n- Issue tracker: https://github.com/pallets/werkzeug/issues\n- Test status: https://dev.azure.com/pallets/werkzeug/_build\n- Official chat: https://discord.gg/t6rrQZH\n\n.. _WSGI: https://wsgi.readthedocs.io/en/latest/\n.. _Flask: https://www.palletsprojects.com/p/flask/\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\n", + "release_date": "2020-03-31T18:03:34", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/werkzeug/", + "download_url": "https://files.pythonhosted.org/packages/cc/94/5f7079a0e00bd6863ef8f1da638721e9da21e5bacee597595b318f71d62e/Werkzeug-1.0.1-py2.py3-none-any.whl", + "size": 298631, + "sha1": null, + "md5": "ceaf2433ef66e2d7a3fbe43a4e44e4ca", + "sha256": "2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/werkzeug", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/werkzeug/1.0.1/json", + "datasource_id": null, + "purl": "pkg:pypi/werkzeug@1.0.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "werkzeug", + "version": "1.0.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Werkzeug\n========\n\n*werkzeug* German noun: \"tool\". Etymology: *werk* (\"work\"), *zeug* (\"stuff\")\n\nWerkzeug is a comprehensive `WSGI`_ web application library. It began as\na simple collection of various utilities for WSGI applications and has\nbecome one of the most advanced WSGI utility libraries.\n\nIt includes:\n\n- An interactive debugger that allows inspecting stack traces and\n source code in the browser with an interactive interpreter for any\n frame in the stack.\n- A full-featured request object with objects to interact with\n headers, query args, form data, files, and cookies.\n- A response object that can wrap other WSGI applications and handle\n streaming data.\n- A routing system for matching URLs to endpoints and generating URLs\n for endpoints, with an extensible system for capturing variables\n from URLs.\n- HTTP utilities to handle entity tags, cache control, dates, user\n agents, cookies, files, and more.\n- A threaded WSGI server for use while developing applications\n locally.\n- A test client for simulating HTTP requests during testing without\n requiring running a server.\n\nWerkzeug is Unicode aware and doesn't enforce any dependencies. It is up\nto the developer to choose a template engine, database adapter, and even\nhow to handle requests. It can be used to build all sorts of end user\napplications such as blogs, wikis, or bulletin boards.\n\n`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while\nproviding more structure and patterns for defining powerful\napplications.\n\n\nInstalling\n----------\n\nInstall and update using `pip`_:\n\n.. code-block:: text\n\n pip install -U Werkzeug\n\n\nA Simple Example\n----------------\n\n.. code-block:: python\n\n from werkzeug.wrappers import Request, Response\n\n @Request.application\n def application(request):\n return Response('Hello, World!')\n\n if __name__ == '__main__':\n from werkzeug.serving import run_simple\n run_simple('localhost', 4000, application)\n\n\nLinks\n-----\n\n- Website: https://palletsprojects.com/p/werkzeug/\n- Documentation: https://werkzeug.palletsprojects.com/\n- Releases: https://pypi.org/project/Werkzeug/\n- Code: https://github.com/pallets/werkzeug\n- Issue tracker: https://github.com/pallets/werkzeug/issues\n- Test status: https://dev.azure.com/pallets/werkzeug/_build\n- Official chat: https://discord.gg/t6rrQZH\n\n.. _WSGI: https://wsgi.readthedocs.io/en/latest/\n.. _Flask: https://www.palletsprojects.com/p/flask/\n.. _pip: https://pip.pypa.io/en/stable/quickstart/\n\n\n", + "release_date": "2020-03-31T18:03:37", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Pallets", + "email": "contact@palletsprojects.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://palletsprojects.com/p/werkzeug/", + "download_url": "https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz", + "size": 904455, + "sha1": null, + "md5": "5d499cfdd30de5d9c946994783772efd", + "sha256": "6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": "https://github.com/pallets/werkzeug", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/werkzeug/1.0.1/json", + "datasource_id": null, + "purl": "pkg:pypi/werkzeug@1.0.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "zipp", + "version": "1.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/zipp.svg\n :target: https://pypi.org/project/zipp\n\n.. image:: https://img.shields.io/pypi/pyversions/zipp.svg\n\n.. image:: https://img.shields.io/travis/jaraco/zipp/master.svg\n :target: https://travis-ci.org/jaraco/zipp\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n :alt: Code style: Black\n\n.. image:: https://img.shields.io/appveyor/ci/jaraco/zipp/master.svg\n :target: https://ci.appveyor.com/project/jaraco/zipp/branch/master\n\n.. .. image:: https://readthedocs.org/projects/zipp/badge/?version=latest\n.. :target: https://zipp.readthedocs.io/en/latest/?badge=latest\n\n\nA pathlib-compatible Zipfile object wrapper. A backport of the\n`Path object `_.\n\n\n", + "release_date": "2020-02-17T18:32:52", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jaraco/zipp", + "download_url": "https://files.pythonhosted.org/packages/96/0a/67556e9b7782df7118c1f49bdc494da5e5e429c93aa77965f33e81287c8c/zipp-1.2.0-py2.py3-none-any.whl", + "size": 4821, + "sha1": null, + "md5": "9c32bf0abe6dd409282f9ae30d4b9157", + "sha256": "e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/zipp/1.2.0/json", + "datasource_id": null, + "purl": "pkg:pypi/zipp@1.2.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "zipp", + "version": "1.2.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/zipp.svg\n :target: https://pypi.org/project/zipp\n\n.. image:: https://img.shields.io/pypi/pyversions/zipp.svg\n\n.. image:: https://img.shields.io/travis/jaraco/zipp/master.svg\n :target: https://travis-ci.org/jaraco/zipp\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n :alt: Code style: Black\n\n.. image:: https://img.shields.io/appveyor/ci/jaraco/zipp/master.svg\n :target: https://ci.appveyor.com/project/jaraco/zipp/branch/master\n\n.. .. image:: https://readthedocs.org/projects/zipp/badge/?version=latest\n.. :target: https://zipp.readthedocs.io/en/latest/?badge=latest\n\n\nA pathlib-compatible Zipfile object wrapper. A backport of the\n`Path object `_.\n\n\n", + "release_date": "2020-02-17T18:32:53", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/jaraco/zipp", + "download_url": "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz", + "size": 13357, + "sha1": null, + "md5": "c25d36db01d011eb2067c722cbd56279", + "sha256": "c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/zipp/1.2.0/json", + "datasource_id": null, + "purl": "pkg:pypi/zipp@1.2.0" } ] } \ No newline at end of file diff --git a/tests/data/setup/spdx-setup.py-expected.json b/tests/data/setup/spdx-setup.py-expected.json index 9436efa6..bc929fac 100644 --- a/tests/data/setup/spdx-setup.py-expected.json +++ b/tests/data/setup/spdx-setup.py-expected.json @@ -1,40 +1,503 @@ -[ - { - "key": "ply", - "package_name": "ply", - "installed_version": "3.11", - "dependencies": [] - }, - { - "key": "rdflib", - "package_name": "rdflib", - "installed_version": "5.0.0", - "dependencies": [ - { - "key": "isodate", - "package_name": "isodate", - "installed_version": "0.6.1", - "dependencies": [ - { - "key": "six", - "package_name": "six", - "installed_version": "1.16.0", - "dependencies": [] - } - ] - }, - { - "key": "pyparsing", - "package_name": "pyparsing", - "installed_version": "2.4.7", - "dependencies": [] - }, - { - "key": "six", - "package_name": "six", - "installed_version": "1.16.0", - "dependencies": [] - } - ] - } -] \ No newline at end of file +{ + "resolved_dependencies": [ + { + "key": "ply", + "package_name": "ply", + "installed_version": "3.11", + "dependencies": [] + }, + { + "key": "rdflib", + "package_name": "rdflib", + "installed_version": "5.0.0", + "dependencies": [ + { + "key": "isodate", + "package_name": "isodate", + "installed_version": "0.6.1", + "dependencies": [ + { + "key": "six", + "package_name": "six", + "installed_version": "1.16.0", + "dependencies": [] + } + ] + }, + { + "key": "pyparsing", + "package_name": "pyparsing", + "installed_version": "2.4.7", + "dependencies": [] + }, + { + "key": "six", + "package_name": "six", + "installed_version": "1.16.0", + "dependencies": [] + } + ] + } + ], + "packages": [ + { + "type": "pypi", + "namespace": null, + "name": "isodate", + "version": "0.6.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nISO 8601 date/time parser\n=========================\n\n.. image:: https://travis-ci.org/gweis/isodate.svg?branch=master\n :target: https://travis-ci.org/gweis/isodate\n :alt: Travis-CI\n.. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master\n :target: https://coveralls.io/r/gweis/isodate?branch=master\n :alt: Coveralls\n.. image:: https://img.shields.io/pypi/v/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: Latest Version\n.. image:: https://img.shields.io/pypi/l/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: License\n\n\nThis module implements ISO 8601 date, time and duration parsing.\nThe implementation follows ISO8601:2004 standard, and implements only\ndate/time representations mentioned in the standard. If something is not\nmentioned there, then it is treated as non existent, and not as an allowed\noption.\n\nFor instance, ISO8601:2004 never mentions 2 digit years. So, it is not\nintended by this module to support 2 digit years. (while it may still\nbe valid as ISO date, because it is not explicitly forbidden.)\nAnother example is, when no time zone information is given for a time,\nthen it should be interpreted as local time, and not UTC.\n\nAs this module maps ISO 8601 dates/times to standard Python data types, like\n*date*, *time*, *datetime* and *timedelta*, it is not possible to convert\nall possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are\nnot allowed by the Python *date* and *datetime* classes. Additionally\nfractional seconds are limited to microseconds. That means if the parser finds\nfor instance nanoseconds it will round it to microseconds.\n\nDocumentation\n-------------\n\nCurrently there are four parsing methods available.\n * parse_time:\n parses an ISO 8601 time string into a *time* object\n * parse_date:\n parses an ISO 8601 date string into a *date* object\n * parse_datetime:\n parses an ISO 8601 date-time string into a *datetime* object\n * parse_duration:\n parses an ISO 8601 duration string into a *timedelta* or *Duration*\n object.\n * parse_tzinfo:\n parses the time zone info part of an ISO 8601 string into a\n *tzinfo* object.\n\nAs ISO 8601 allows to define durations in years and months, and *timedelta*\ndoes not handle years and months, this module provides a *Duration* class,\nwhich can be used almost like a *timedelta* object (with some limitations).\nHowever, a *Duration* object can be converted into a *timedelta* object.\n\nThere are also ISO formatting methods for all supported data types. Each\n*xxx_isoformat* method accepts a format parameter. The default format is\nalways the ISO 8601 expanded format. This is the same format used by\n*datetime.isoformat*:\n\n * time_isoformat:\n Intended to create ISO time strings with default format\n *hh:mm:ssZ*.\n * date_isoformat:\n Intended to create ISO date strings with default format\n *yyyy-mm-dd*.\n * datetime_isoformat:\n Intended to create ISO date-time strings with default format\n *yyyy-mm-ddThh:mm:ssZ*.\n * duration_isoformat:\n Intended to create ISO duration strings with default format\n *PnnYnnMnnDTnnHnnMnnS*.\n * tz_isoformat:\n Intended to create ISO time zone strings with default format\n *hh:mm*.\n * strftime:\n A re-implementation mostly compatible with Python's *strftime*, but\n supports only those format strings, which can also be used for dates\n prior 1900. This method also understands how to format *datetime* and\n *Duration* instances.\n\nInstallation:\n-------------\n\nThis module can easily be installed with Python standard installation methods.\n\nEither use *python setup.py install* or in case you have *setuptools* or\n*distribute* available, you can also use *easy_install*.\n\nLimitations:\n------------\n\n * The parser accepts several date/time representation which should be invalid\n according to ISO 8601 standard.\n\n 1. for date and time together, this parser accepts a mixture of basic and extended format.\n e.g. the date could be in basic format, while the time is accepted in extended format.\n It also allows short dates and times in date-time strings.\n 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of\n 1901-01-01.\n 3. negative *Duration* and *timedelta* value are not fully supported yet.\n\nFurther information:\n--------------------\n\nThe doc strings and unit tests should provide rather detailed information about\nthe methods and their limitations.\n\nThe source release provides a *setup.py* script,\nwhich can be used to run the unit tests included.\n\nSource code is available at ``_.\n\nCHANGES\n=======\n\n0.6.1 (2021-12-13)\n------------------\n\n- support python 3.10 ()\n- last version to support py 2.7\n\n\n0.6.0 (2017-10-13)\n------------------\n\n- support incomplete month date (Fabien Loffredo)\n- rely on duck typing when doing duration maths\n- support ':' as separator in fractional time zones (usrenmae)\n\n\n0.5.4 (2015-08-06)\n------------------\n\n- Fix parsing of Periods (Fabien Bochu)\n- Make Duration objects hashable (Geoffrey Fairchild)\n- Add multiplication to duration (Reinoud Elhorst)\n\n\n0.5.1 (2014-11-07)\n------------------\n\n- fixed pickling of Duration objects\n- raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney)\n\n\n0.5.0 (2014-02-23)\n------------------\n\n- ISO8601Error are subclasses of ValueError now (Michael Hrivnak)\n- improve compatibility across various python variants and versions\n- raise exceptions when using fractional years and months in date\n maths with durations\n- renamed method todatetime on Duraction objects to totimedelta\n\n\n0.4.9 (2012-10-30)\n------------------\n\n- support pickling FixedOffset instances\n- make sure parsed fractional seconds are in microseconds\n- add leading zeros when formattig microseconds (Jarom Loveridge)\n\n\n0.4.8 (2012-05-04)\n------------------\n\n- fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7\n and 3.2)\n\n\n0.4.7 (2012-01-26)\n------------------\n\n- fixed tzinfo formatting (never pass None into tzinfo.utcoffset())\n\n\n0.4.6 (2012-01-06)\n------------------\n\n- added Python 3 compatibility via 2to3\n\n0.4.5 (2012-01-06)\n------------------\n\n- made setuptools dependency optional\n\n0.4.4 (2011-04-16)\n------------------\n\n- Fixed formatting of microseconds for datetime objects\n\n0.4.3 (2010-10-29)\n------------------\n\n- Fixed problem with %P formating and fractions (supplied by David Brooks)\n\n0.4.2 (2010-10-28)\n------------------\n\n- Implemented unary - for Duration (supplied by David Brooks)\n- Output fractional seconds with '%P' format. (partly supplied by David Brooks)\n\n0.4.1 (2010-10-13)\n------------------\n\n- fixed bug in comparison between timedelta and Duration.\n- fixed precision problem with microseconds (reported by Tommi Virtanen)\n\n0.4.0 (2009-02-09)\n------------------\n\n- added method to parse ISO 8601 time zone strings\n- added methods to create ISO 8601 conforming strings\n\n0.3.0 (2009-1-05)\n------------------\n\n- Initial release\n\nTODOs\n=====\n\nThis to do list contains some thoughts and ideas about missing features, and\nparts to think about, whether to implement them or not. This list is probably\nnot complete.\n\nMissing features:\n-----------------\n\n * time formating does not allow to create fractional representations.\n * parser for ISO intervals.\n * currently microseconds are always padded to a length of 6 characters.\n trailing 0s should be optional\n\nDocumentation:\n--------------\n\n * parse_datetime:\n - complete documentation to show what this function allows, but ISO forbids.\n and vice verse.\n - support other separators between date and time than 'T'\n\n * parse_date:\n - yeardigits should be always greater than 4\n - dates before 0001-01-01 are not supported\n\n * parse_duration:\n - alternative formats are not fully supported due to parse_date restrictions\n - standard duration format is fully supported but not very restrictive.\n\n * Duration:\n - support fractional years and month in calculations\n - implement w3c order relation? (``_)\n - refactor to have duration mathematics only at one place.\n - localize __str__ method (does timedelta do this?)\n - when is a Duration negative?\n - normalize Durations. months [00-12] and years ]-inf,+inf[\n\n\n", + "release_date": "2021-12-13T20:28:29", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Gerhard Weis", + "email": "gerhard.weis@proclos.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/gweis/isodate/", + "download_url": "https://files.pythonhosted.org/packages/b6/85/7882d311924cbcfc70b1890780763e36ff0b140c7e51c110fc59a532f087/isodate-0.6.1-py2.py3-none-any.whl", + "size": 41722, + "sha1": null, + "md5": "c8a5fcd645030db98daa82b8e56fda89", + "sha256": "0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/isodate/0.6.1/json", + "datasource_id": null, + "purl": "pkg:pypi/isodate@0.6.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "isodate", + "version": "0.6.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nISO 8601 date/time parser\n=========================\n\n.. image:: https://travis-ci.org/gweis/isodate.svg?branch=master\n :target: https://travis-ci.org/gweis/isodate\n :alt: Travis-CI\n.. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master\n :target: https://coveralls.io/r/gweis/isodate?branch=master\n :alt: Coveralls\n.. image:: https://img.shields.io/pypi/v/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: Latest Version\n.. image:: https://img.shields.io/pypi/l/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: License\n\n\nThis module implements ISO 8601 date, time and duration parsing.\nThe implementation follows ISO8601:2004 standard, and implements only\ndate/time representations mentioned in the standard. If something is not\nmentioned there, then it is treated as non existent, and not as an allowed\noption.\n\nFor instance, ISO8601:2004 never mentions 2 digit years. So, it is not\nintended by this module to support 2 digit years. (while it may still\nbe valid as ISO date, because it is not explicitly forbidden.)\nAnother example is, when no time zone information is given for a time,\nthen it should be interpreted as local time, and not UTC.\n\nAs this module maps ISO 8601 dates/times to standard Python data types, like\n*date*, *time*, *datetime* and *timedelta*, it is not possible to convert\nall possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are\nnot allowed by the Python *date* and *datetime* classes. Additionally\nfractional seconds are limited to microseconds. That means if the parser finds\nfor instance nanoseconds it will round it to microseconds.\n\nDocumentation\n-------------\n\nCurrently there are four parsing methods available.\n * parse_time:\n parses an ISO 8601 time string into a *time* object\n * parse_date:\n parses an ISO 8601 date string into a *date* object\n * parse_datetime:\n parses an ISO 8601 date-time string into a *datetime* object\n * parse_duration:\n parses an ISO 8601 duration string into a *timedelta* or *Duration*\n object.\n * parse_tzinfo:\n parses the time zone info part of an ISO 8601 string into a\n *tzinfo* object.\n\nAs ISO 8601 allows to define durations in years and months, and *timedelta*\ndoes not handle years and months, this module provides a *Duration* class,\nwhich can be used almost like a *timedelta* object (with some limitations).\nHowever, a *Duration* object can be converted into a *timedelta* object.\n\nThere are also ISO formatting methods for all supported data types. Each\n*xxx_isoformat* method accepts a format parameter. The default format is\nalways the ISO 8601 expanded format. This is the same format used by\n*datetime.isoformat*:\n\n * time_isoformat:\n Intended to create ISO time strings with default format\n *hh:mm:ssZ*.\n * date_isoformat:\n Intended to create ISO date strings with default format\n *yyyy-mm-dd*.\n * datetime_isoformat:\n Intended to create ISO date-time strings with default format\n *yyyy-mm-ddThh:mm:ssZ*.\n * duration_isoformat:\n Intended to create ISO duration strings with default format\n *PnnYnnMnnDTnnHnnMnnS*.\n * tz_isoformat:\n Intended to create ISO time zone strings with default format\n *hh:mm*.\n * strftime:\n A re-implementation mostly compatible with Python's *strftime*, but\n supports only those format strings, which can also be used for dates\n prior 1900. This method also understands how to format *datetime* and\n *Duration* instances.\n\nInstallation:\n-------------\n\nThis module can easily be installed with Python standard installation methods.\n\nEither use *python setup.py install* or in case you have *setuptools* or\n*distribute* available, you can also use *easy_install*.\n\nLimitations:\n------------\n\n * The parser accepts several date/time representation which should be invalid\n according to ISO 8601 standard.\n\n 1. for date and time together, this parser accepts a mixture of basic and extended format.\n e.g. the date could be in basic format, while the time is accepted in extended format.\n It also allows short dates and times in date-time strings.\n 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of\n 1901-01-01.\n 3. negative *Duration* and *timedelta* value are not fully supported yet.\n\nFurther information:\n--------------------\n\nThe doc strings and unit tests should provide rather detailed information about\nthe methods and their limitations.\n\nThe source release provides a *setup.py* script,\nwhich can be used to run the unit tests included.\n\nSource code is available at ``_.\n\nCHANGES\n=======\n\n0.6.1 (2021-12-13)\n------------------\n\n- support python 3.10 ()\n- last version to support py 2.7\n\n\n0.6.0 (2017-10-13)\n------------------\n\n- support incomplete month date (Fabien Loffredo)\n- rely on duck typing when doing duration maths\n- support ':' as separator in fractional time zones (usrenmae)\n\n\n0.5.4 (2015-08-06)\n------------------\n\n- Fix parsing of Periods (Fabien Bochu)\n- Make Duration objects hashable (Geoffrey Fairchild)\n- Add multiplication to duration (Reinoud Elhorst)\n\n\n0.5.1 (2014-11-07)\n------------------\n\n- fixed pickling of Duration objects\n- raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney)\n\n\n0.5.0 (2014-02-23)\n------------------\n\n- ISO8601Error are subclasses of ValueError now (Michael Hrivnak)\n- improve compatibility across various python variants and versions\n- raise exceptions when using fractional years and months in date\n maths with durations\n- renamed method todatetime on Duraction objects to totimedelta\n\n\n0.4.9 (2012-10-30)\n------------------\n\n- support pickling FixedOffset instances\n- make sure parsed fractional seconds are in microseconds\n- add leading zeros when formattig microseconds (Jarom Loveridge)\n\n\n0.4.8 (2012-05-04)\n------------------\n\n- fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7\n and 3.2)\n\n\n0.4.7 (2012-01-26)\n------------------\n\n- fixed tzinfo formatting (never pass None into tzinfo.utcoffset())\n\n\n0.4.6 (2012-01-06)\n------------------\n\n- added Python 3 compatibility via 2to3\n\n0.4.5 (2012-01-06)\n------------------\n\n- made setuptools dependency optional\n\n0.4.4 (2011-04-16)\n------------------\n\n- Fixed formatting of microseconds for datetime objects\n\n0.4.3 (2010-10-29)\n------------------\n\n- Fixed problem with %P formating and fractions (supplied by David Brooks)\n\n0.4.2 (2010-10-28)\n------------------\n\n- Implemented unary - for Duration (supplied by David Brooks)\n- Output fractional seconds with '%P' format. (partly supplied by David Brooks)\n\n0.4.1 (2010-10-13)\n------------------\n\n- fixed bug in comparison between timedelta and Duration.\n- fixed precision problem with microseconds (reported by Tommi Virtanen)\n\n0.4.0 (2009-02-09)\n------------------\n\n- added method to parse ISO 8601 time zone strings\n- added methods to create ISO 8601 conforming strings\n\n0.3.0 (2009-1-05)\n------------------\n\n- Initial release\n\nTODOs\n=====\n\nThis to do list contains some thoughts and ideas about missing features, and\nparts to think about, whether to implement them or not. This list is probably\nnot complete.\n\nMissing features:\n-----------------\n\n * time formating does not allow to create fractional representations.\n * parser for ISO intervals.\n * currently microseconds are always padded to a length of 6 characters.\n trailing 0s should be optional\n\nDocumentation:\n--------------\n\n * parse_datetime:\n - complete documentation to show what this function allows, but ISO forbids.\n and vice verse.\n - support other separators between date and time than 'T'\n\n * parse_date:\n - yeardigits should be always greater than 4\n - dates before 0001-01-01 are not supported\n\n * parse_duration:\n - alternative formats are not fully supported due to parse_date restrictions\n - standard duration format is fully supported but not very restrictive.\n\n * Duration:\n - support fractional years and month in calculations\n - implement w3c order relation? (``_)\n - refactor to have duration mathematics only at one place.\n - localize __str__ method (does timedelta do this?)\n - when is a Duration negative?\n - normalize Durations. months [00-12] and years ]-inf,+inf[\n\n\n", + "release_date": "2021-12-13T20:28:31", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Gerhard Weis", + "email": "gerhard.weis@proclos.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/gweis/isodate/", + "download_url": "https://files.pythonhosted.org/packages/db/7a/c0a56c7d56c7fa723988f122fa1f1ccf8c5c4ccc48efad0d214b49e5b1af/isodate-0.6.1.tar.gz", + "size": 28443, + "sha1": null, + "md5": "1a310658b30a48641bafb5652ad91c40", + "sha256": "48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/isodate/0.6.1/json", + "datasource_id": null, + "purl": "pkg:pypi/isodate@0.6.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "ply", + "version": "3.11", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nPLY is yet another implementation of lex and yacc for Python. Some notable\nfeatures include the fact that its implemented entirely in Python and it\nuses LALR(1) parsing which is efficient and well suited for larger grammars.\n\nPLY provides most of the standard lex/yacc features including support for empty \nproductions, precedence rules, error recovery, and support for ambiguous grammars. \n\nPLY is extremely easy to use and provides very extensive error checking. \nIt is compatible with both Python 2 and Python 3.\n", + "release_date": "2018-02-15T19:01:27", + "parties": [ + { + "type": "person", + "role": "author", + "name": "David Beazley", + "email": "dave@dabeaz.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://www.dabeaz.com/ply/", + "download_url": "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", + "size": 49567, + "sha1": null, + "md5": "62b6ad5affddc9926ab5571f390cc840", + "sha256": "096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ply/3.11/json", + "datasource_id": null, + "purl": "pkg:pypi/ply@3.11" + }, + { + "type": "pypi", + "namespace": null, + "name": "ply", + "version": "3.11", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nPLY is yet another implementation of lex and yacc for Python. Some notable\nfeatures include the fact that its implemented entirely in Python and it\nuses LALR(1) parsing which is efficient and well suited for larger grammars.\n\nPLY provides most of the standard lex/yacc features including support for empty \nproductions, precedence rules, error recovery, and support for ambiguous grammars. \n\nPLY is extremely easy to use and provides very extensive error checking. \nIt is compatible with both Python 2 and Python 3.\n", + "release_date": "2018-02-15T19:01:31", + "parties": [ + { + "type": "person", + "role": "author", + "name": "David Beazley", + "email": "dave@dabeaz.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "http://www.dabeaz.com/ply/", + "download_url": "https://files.pythonhosted.org/packages/e5/69/882ee5c9d017149285cab114ebeab373308ef0f874fcdac9beb90e0ac4da/ply-3.11.tar.gz", + "size": 159130, + "sha1": null, + "md5": "6465f602e656455affcd7c5734c638f8", + "sha256": "00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/ply/3.11/json", + "datasource_id": null, + "purl": "pkg:pypi/ply@3.11" + }, + { + "type": "pypi", + "namespace": null, + "name": "pyparsing", + "version": "2.4.7", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "PyParsing -- A Python Parsing Module\n====================================\n\n|Build Status|\n\nIntroduction\n============\n\nThe pyparsing module is an alternative approach to creating and\nexecuting simple grammars, vs. the traditional lex/yacc approach, or the\nuse of regular expressions. The pyparsing module provides a library of\nclasses that client code uses to construct the grammar directly in\nPython code.\n\n*[Since first writing this description of pyparsing in late 2003, this\ntechnique for developing parsers has become more widespread, under the\nname Parsing Expression Grammars - PEGs. See more information on PEGs at*\nhttps://en.wikipedia.org/wiki/Parsing_expression_grammar *.]*\n\nHere is a program to parse ``\"Hello, World!\"`` (or any greeting of the form\n``\"salutation, addressee!\"``):\n\n.. code:: python\n\n from pyparsing import Word, alphas\n greet = Word(alphas) + \",\" + Word(alphas) + \"!\"\n hello = \"Hello, World!\"\n print(hello, \"->\", greet.parseString(hello))\n\nThe program outputs the following::\n\n Hello, World! -> ['Hello', ',', 'World', '!']\n\nThe Python representation of the grammar is quite readable, owing to the\nself-explanatory class names, and the use of '+', '|' and '^' operator\ndefinitions.\n\nThe parsed results returned from ``parseString()`` can be accessed as a\nnested list, a dictionary, or an object with named attributes.\n\nThe pyparsing module handles some of the problems that are typically\nvexing when writing text parsers:\n\n- extra or missing whitespace (the above program will also handle ``\"Hello,World!\"``, ``\"Hello , World !\"``, etc.)\n- quoted strings\n- embedded comments\n\nThe examples directory includes a simple SQL parser, simple CORBA IDL\nparser, a config file parser, a chemical formula parser, and a four-\nfunction algebraic notation parser, among many others.\n\nDocumentation\n=============\n\nThere are many examples in the online docstrings of the classes\nand methods in pyparsing. You can find them compiled into online docs\nat https://pyparsing-docs.readthedocs.io/en/latest/. Additional\ndocumentation resources and project info are listed in the online\nGitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An\nentire directory of examples is at\nhttps://github.com/pyparsing/pyparsing/tree/master/examples.\n\nLicense\n=======\n\nMIT License. See header of pyparsing.py\n\nHistory\n=======\n\nSee CHANGES file.\n\n.. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master\n :target: https://travis-ci.org/pyparsing/pyparsing\n\n\n", + "release_date": "2020-04-05T22:21:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Paul McGuire", + "email": "ptmcg@users.sourceforge.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pyparsing/pyparsing/", + "download_url": "https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl", + "size": 67842, + "sha1": null, + "md5": "dbfd0a241aad2595f43377ec7f1836ea", + "sha256": "ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pyparsing/2.4.7/json", + "datasource_id": null, + "purl": "pkg:pypi/pyparsing@2.4.7" + }, + { + "type": "pypi", + "namespace": null, + "name": "pyparsing", + "version": "2.4.7", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "PyParsing -- A Python Parsing Module\n====================================\n\n|Build Status|\n\nIntroduction\n============\n\nThe pyparsing module is an alternative approach to creating and\nexecuting simple grammars, vs. the traditional lex/yacc approach, or the\nuse of regular expressions. The pyparsing module provides a library of\nclasses that client code uses to construct the grammar directly in\nPython code.\n\n*[Since first writing this description of pyparsing in late 2003, this\ntechnique for developing parsers has become more widespread, under the\nname Parsing Expression Grammars - PEGs. See more information on PEGs at*\nhttps://en.wikipedia.org/wiki/Parsing_expression_grammar *.]*\n\nHere is a program to parse ``\"Hello, World!\"`` (or any greeting of the form\n``\"salutation, addressee!\"``):\n\n.. code:: python\n\n from pyparsing import Word, alphas\n greet = Word(alphas) + \",\" + Word(alphas) + \"!\"\n hello = \"Hello, World!\"\n print(hello, \"->\", greet.parseString(hello))\n\nThe program outputs the following::\n\n Hello, World! -> ['Hello', ',', 'World', '!']\n\nThe Python representation of the grammar is quite readable, owing to the\nself-explanatory class names, and the use of '+', '|' and '^' operator\ndefinitions.\n\nThe parsed results returned from ``parseString()`` can be accessed as a\nnested list, a dictionary, or an object with named attributes.\n\nThe pyparsing module handles some of the problems that are typically\nvexing when writing text parsers:\n\n- extra or missing whitespace (the above program will also handle ``\"Hello,World!\"``, ``\"Hello , World !\"``, etc.)\n- quoted strings\n- embedded comments\n\nThe examples directory includes a simple SQL parser, simple CORBA IDL\nparser, a config file parser, a chemical formula parser, and a four-\nfunction algebraic notation parser, among many others.\n\nDocumentation\n=============\n\nThere are many examples in the online docstrings of the classes\nand methods in pyparsing. You can find them compiled into online docs\nat https://pyparsing-docs.readthedocs.io/en/latest/. Additional\ndocumentation resources and project info are listed in the online\nGitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An\nentire directory of examples is at\nhttps://github.com/pyparsing/pyparsing/tree/master/examples.\n\nLicense\n=======\n\nMIT License. See header of pyparsing.py\n\nHistory\n=======\n\nSee CHANGES file.\n\n.. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master\n :target: https://travis-ci.org/pyparsing/pyparsing\n\n\n", + "release_date": "2020-04-05T22:21:25", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Paul McGuire", + "email": "ptmcg@users.sourceforge.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pyparsing/pyparsing/", + "download_url": "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz", + "size": 649718, + "sha1": null, + "md5": "f0953e47a0112f7a65aec2305ffdf7b4", + "sha256": "c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pyparsing/2.4.7/json", + "datasource_id": null, + "purl": "pkg:pypi/pyparsing@2.4.7" + }, + { + "type": "pypi", + "namespace": null, + "name": "rdflib", + "version": "5.0.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "RDFLib is a Python library for working with\nRDF, a simple yet powerful language for representing information.\n\nThe library contains parsers and serializers for RDF/XML, N3,\nNTriples, Turtle, TriX, RDFa and Microdata . The library presents\na Graph interface which can be backed by any one of a number of\nStore implementations. The core rdflib includes store\nimplementations for in memory storage, persistent storage on top\nof the Berkeley DB, and a wrapper for remote SPARQL endpoints.\n\nA SPARQL 1.1 engine is also included.\n\nIf you have recently reported a bug marked as fixed, or have a craving for\nthe very latest, you may want the development version instead:\n\n pip install git+https://github.com/rdflib/rdflib\n\n\nRead the docs at:\n\n http://rdflib.readthedocs.io\n\n\n\n", + "release_date": "2020-04-18T01:33:59", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Daniel 'eikeon' Krech", + "email": "eikeon@eikeon.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "RDFLib Team", + "email": "rdflib-dev@google.com", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/RDFLib/rdflib", + "download_url": "https://files.pythonhosted.org/packages/2f/ae/a50934a7ed4f9d80bbc0e0cf725c7fd2208f2e433efbf881ed0c0317a7f1/rdflib-5.0.0.tar.gz", + "size": 818586, + "sha1": null, + "md5": "80d7c6adc2e4040cdd8dade2e0e61403", + "sha256": "78149dd49d385efec3b3adfbd61c87afaf1281c30d3fcaf1b323b34f603fb155", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD-3-Clause", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/rdflib/5.0.0/json", + "datasource_id": null, + "purl": "pkg:pypi/rdflib@5.0.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "six", + "version": "1.16.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/six.svg\n :target: https://pypi.org/project/six/\n :alt: six on PyPI\n\n.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master\n :target: https://travis-ci.org/benjaminp/six\n :alt: six on TravisCI\n\n.. image:: https://readthedocs.org/projects/six/badge/?version=latest\n :target: https://six.readthedocs.io/\n :alt: six's documentation on Read the Docs\n\n.. image:: https://img.shields.io/badge/license-MIT-green.svg\n :target: https://github.com/benjaminp/six/blob/master/LICENSE\n :alt: MIT License badge\n\nSix is a Python 2 and 3 compatibility library. It provides utility functions\nfor smoothing over the differences between the Python versions with the goal of\nwriting Python code that is compatible on both Python versions. See the\ndocumentation for more information on what is provided.\n\nSix supports Python 2.7 and 3.3+. It is contained in only one Python\nfile, so it can be easily copied into your project. (The copyright and license\nnotice must be retained.)\n\nOnline documentation is at https://six.readthedocs.io/.\n\nBugs can be reported to https://github.com/benjaminp/six. The code can also\nbe found there.\n\n\n", + "release_date": "2021-05-05T14:18:17", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Benjamin Peterson", + "email": "benjamin@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benjaminp/six", + "download_url": "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", + "size": 11053, + "sha1": null, + "md5": "529d7fd7e14612ccde86417b4402d6f3", + "sha256": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/six/1.16.0/json", + "datasource_id": null, + "purl": "pkg:pypi/six@1.16.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "six", + "version": "1.16.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/six.svg\n :target: https://pypi.org/project/six/\n :alt: six on PyPI\n\n.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master\n :target: https://travis-ci.org/benjaminp/six\n :alt: six on TravisCI\n\n.. image:: https://readthedocs.org/projects/six/badge/?version=latest\n :target: https://six.readthedocs.io/\n :alt: six's documentation on Read the Docs\n\n.. image:: https://img.shields.io/badge/license-MIT-green.svg\n :target: https://github.com/benjaminp/six/blob/master/LICENSE\n :alt: MIT License badge\n\nSix is a Python 2 and 3 compatibility library. It provides utility functions\nfor smoothing over the differences between the Python versions with the goal of\nwriting Python code that is compatible on both Python versions. See the\ndocumentation for more information on what is provided.\n\nSix supports Python 2.7 and 3.3+. It is contained in only one Python\nfile, so it can be easily copied into your project. (The copyright and license\nnotice must be retained.)\n\nOnline documentation is at https://six.readthedocs.io/.\n\nBugs can be reported to https://github.com/benjaminp/six. The code can also\nbe found there.\n\n\n", + "release_date": "2021-05-05T14:18:18", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Benjamin Peterson", + "email": "benjamin@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benjaminp/six", + "download_url": "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", + "size": 34041, + "sha1": null, + "md5": "a7c927740e4964dd29b72cebfc1429bb", + "sha256": "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/six/1.16.0/json", + "datasource_id": null, + "purl": "pkg:pypi/six@1.16.0" + } + ] +} \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py index 3c664aac..c4aafe29 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -235,7 +235,7 @@ def test_cli_with_insecure_option(): setup_py=setup_py_file, expected_file=expected_file, regen=REGEN_TEST_FIXTURES, - extra_options=["--python-version", "27", "--insecure"], + extra_options=["--python-version", "27", "--analyze-setup-py-insecurely"], pdt_output=True, ) @@ -250,7 +250,7 @@ def test_cli_with_insecure_option_testpkh(): setup_py=setup_py_file, expected_file=expected_file, regen=REGEN_TEST_FIXTURES, - extra_options=["--python-version", "27", "--insecure"], + extra_options=["--python-version", "27", "--analyze-setup-py-insecurely"], ) @@ -425,80 +425,3 @@ def run_cli(options, cli=resolve_dependencies, expected_rc=0, env=None): """ assert result.exit_code == expected_rc, error return result - - -def test_get_requirements_from_direct_dependencies(): - direct_dependencies = [ - models.DependentPackage( - purl="pkg:pypi/django", - scope="install", - is_runtime=True, - is_optional=False, - is_resolved=False, - extracted_requirement="django>=1.11.11", - extra_data=dict( - is_editable=False, - link=None, - hash_options=[], - is_constraint=False, - is_archive=False, - is_wheel=False, - is_url=False, - is_vcs_url=False, - is_name_at_url=False, - is_local_path=False, - ), - ) - ] - - requirements = [ - str(r) - for r in get_requirements_from_direct_dependencies( - direct_dependencies=direct_dependencies, environment_marker={} - ) - ] - - assert requirements == ["django>=1.11.11"] - - -def test_get_requirements_from_direct_dependencies_with_empty_list(): - assert ( - list( - get_requirements_from_direct_dependencies(direct_dependencies=[], environment_marker={}) - ) - == [] - ) - - -def test_get_requirements_from_direct_dependencies_with_editable_requirements(): - direct_dependencies = [ - models.DependentPackage( - purl="pkg:pypi/django", - scope="install", - is_runtime=True, - is_optional=False, - is_resolved=False, - extracted_requirement="django>=1.11.11", - extra_data=dict( - is_editable=True, - link=None, - hash_options=[], - is_constraint=False, - is_archive=False, - is_wheel=False, - is_url=False, - is_vcs_url=False, - is_name_at_url=False, - is_local_path=False, - ), - ) - ] - - requirements = [ - str(r) - for r in get_requirements_from_direct_dependencies( - direct_dependencies=direct_dependencies, environment_marker={} - ) - ] - - assert requirements == [] diff --git a/tests/test_setup_py_live_eval_cli.py b/tests/test_setup_py_live_eval_cli.py index f593e237..52b0e8df 100644 --- a/tests/test_setup_py_live_eval_cli.py +++ b/tests/test_setup_py_live_eval_cli.py @@ -235,7 +235,7 @@ def test_cli_with_insecure_option(): setup_py=setup_py_file, expected_file=expected_file, regen=REGEN_TEST_FIXTURES, - extra_options=["--python-version", "27", "--insecure"], + extra_options=["--python-version", "27", "--analyze-setup-py-insecurely"], pdt_output=True, ) From 3e59fe5cf03bb3050291ed6e87ccb26a7731a97d Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Tue, 20 Sep 2022 17:56:43 +0530 Subject: [PATCH 50/54] Fix tests Signed-off-by: Tushar Goel --- src/python_inspector/resolution.py | 21 ++++++++++++++------- src/python_inspector/resolve_cli.py | 8 ++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index 2b5c547d..297e89b8 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -248,14 +248,14 @@ def remove_extras(identifier: str) -> str: class PythonInputProvider(AbstractProvider): - def __init__(self, environment=None, repos=tuple(), insecure=False): + def __init__(self, environment=None, repos=tuple(), analyze_setup_py_insecurely=False): self.environment = environment self.environment_marker = get_environment_marker_from_environment(environment) self.repos = repos or [] self.versions_by_package = {} self.dependencies_by_purl = {} self.wheel_or_sdist_by_package = {} - self.insecure = insecure + self.analyze_setup_py_insecurely = analyze_setup_py_insecurely def identify(self, requirement_or_candidate: Union[Candidate, Requirement]) -> str: """Given a requirement, return an identifier for it. Overridden.""" @@ -378,7 +378,10 @@ def get_requirements_for_package_from_pypi_simple( ) if not sdist_location: return - yield from get_setup_dependencies(location=sdist_location, insecure=self.insecure) + yield from get_setup_dependencies( + location=sdist_location, + analyze_setup_py_insecurely=self.analyze_setup_py_insecurely, + ) def get_requirements_for_package_from_pypi_json_api( self, purl: PackageURL @@ -678,7 +681,7 @@ def get_package_list(results): return list(sorted(packages)) -def get_setup_dependencies(location, insecure=False, use_requirements=True): +def get_setup_dependencies(location, analyze_setup_py_insecurely=False, use_requirements=True): """ Yield dependencies from the given setup.py and setup.cfg location. """ @@ -736,7 +739,7 @@ def get_setup_dependencies(location, insecure=False, use_requirements=True): if not has_deps and contain_string( string="_require", files=[setup_py_location, setup_cfg_location] ): - if insecure: + if analyze_setup_py_insecurely: yield from parse_setup_py_insecurely(setup_py=setup_py_location) else: raise Exception("Unable to collect setup.py dependencies securely") @@ -750,7 +753,7 @@ def get_resolved_dependencies( max_rounds: int = 200000, verbose: bool = False, pdt_output: bool = False, - insecure: bool = False, + analyze_setup_py_insecurely: bool = False, ): """ Return resolved dependencies of a ``requirements`` list of Requirement for @@ -762,7 +765,11 @@ def get_resolved_dependencies( """ try: resolver = Resolver( - provider=PythonInputProvider(environment=environment, repos=repos, insecure=insecure), + provider=PythonInputProvider( + environment=environment, + repos=repos, + analyze_setup_py_insecurely=analyze_setup_py_insecurely, + ), reporter=BaseReporter(), ) resolver_results = resolver.resolve(requirements=requirements, max_rounds=max_rounds) diff --git a/src/python_inspector/resolve_cli.py b/src/python_inspector/resolve_cli.py index c6c191ed..c68f5b81 100644 --- a/src/python_inspector/resolve_cli.py +++ b/src/python_inspector/resolve_cli.py @@ -193,7 +193,7 @@ def resolve_dependencies( max_rounds, use_cached_index=False, use_pypi_json_api=False, - insecure=False, + analyze_setup_py_insecurely=False, verbose=TRACE, ): """ @@ -337,7 +337,7 @@ def resolve_dependencies( max_rounds=max_rounds, verbose=verbose, pdt_output=pdt_output, - insecure=insecure, + analyze_setup_py_insecurely=analyze_setup_py_insecurely, ) cli_options = [f"--requirement {rf}" for rf in requirement_files] @@ -401,7 +401,7 @@ def resolve( max_rounds=200000, verbose=False, pdt_output=False, - insecure=False, + analyze_setup_py_insecurely=False, ): """ Resolve dependencies given a ``direct_dependencies`` list of @@ -427,7 +427,7 @@ def resolve( max_rounds=max_rounds, verbose=verbose, pdt_output=pdt_output, - insecure=insecure, + analyze_setup_py_insecurely=analyze_setup_py_insecurely, ) initial_requirements = [d.to_dict() for d in direct_dependencies] From 111b6dbc7b2f4fd509b5cfa3a37605717dffd26a Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Tue, 20 Sep 2022 18:24:24 +0530 Subject: [PATCH 51/54] Add changelog remove unused code Signed-off-by: Tushar Goel --- CHANGELOG.rst | 1 + src/python_inspector/resolution.py | 61 +----------------------------- 2 files changed, 3 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9a0aa66e..b5dc2812 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,7 @@ v0.7.0 solution short of a full installation. Because this can be a security issue, there is a new "--analyze-setup-py-insecurely" command line option to enable this feature. Note that this not more insecure than actually installing a PyPI package. +- Add metadata for packages. v0.6.5 diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index 297e89b8..9cd7e9ac 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -494,43 +494,6 @@ def get_dependencies(self, candidate: Candidate) -> List[Requirement]: return list(self._iter_dependencies(candidate)) -def get_wheel_download_urls( - purl: PackageURL, - repos: List[PypiSimpleRepository], - environment: Environment, - python_version: str, -) -> List[str]: - """ - Return a list of download urls for the given purl. - """ - for repo in repos: - for wheel in utils_pypi.get_supported_and_valid_wheels( - repo=repo, - name=purl.name, - version=purl.version, - environment=environment, - python_version=python_version, - ): - yield wheel.download_url - - -def get_sdist_download_url( - purl: PackageURL, repos: List[PypiSimpleRepository], python_version: str -) -> str: - """ - Return a list of download urls for the given purl. - """ - for repo in repos: - sdist = utils_pypi.get_valid_sdist( - repo=repo, - name=purl.name, - version=purl.version, - python_version=python_version, - ) - if sdist: - return sdist.download_url - - def get_all_srcs(mapping: Dict, graph: DirectedGraph): """ Return a list of all sources in the graph. @@ -559,9 +522,7 @@ def dfs(mapping: Dict, graph: DirectedGraph, src: str): ) -def format_resolution( - results: Result, environment: Environment, repos: List[PypiSimpleRepository], as_tree=False -): +def format_resolution(results: Result, as_tree=False): """ Return a formatted resolution either as a tree or parent/children. """ @@ -586,22 +547,6 @@ def format_resolution( ) dependencies.append(str(dep_purl)) dependencies.sort() - python_version = get_python_version_from_env_tag( - python_version=environment.python_version - ) - wheel_urls = list( - get_wheel_download_urls( - purl=parent_purl, - repos=repos, - environment=environment, - python_version=python_version, - ) - ) - sdist_url = get_sdist_download_url( - purl=parent_purl, - repos=repos, - python_version=python_version, - ) parent_children = dict( package=str(parent_purl), dependencies=dependencies, @@ -777,9 +722,7 @@ def get_resolved_dependencies( if pdt_output: return (format_pdt_tree(resolver_results), package_list) return ( - format_resolution( - resolver_results, as_tree=as_tree, environment=environment, repos=repos - ), + format_resolution(resolver_results, as_tree=as_tree), package_list, ) except Exception as e: From 0a32984dbdcd4b4989c7e339e59de86f68a08f9e Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Tue, 20 Sep 2022 20:40:04 +0530 Subject: [PATCH 52/54] Provide insecure parsing for top level dependencies Signed-off-by: Tushar Goel --- src/python_inspector/resolution.py | 46 ++- src/python_inspector/resolve_cli.py | 31 ++ .../insecure-setup/setup.py-expected.json | 372 ++++++++++++++++++ tests/test_cli.py | 12 + tests/test_resolution.py | 6 +- 5 files changed, 461 insertions(+), 6 deletions(-) create mode 100644 tests/data/insecure-setup/setup.py-expected.json diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index 9cd7e9ac..94df76c2 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -93,6 +93,25 @@ def get_requirements_from_distribution( handler: BasePypiHandler, location: str, ) -> List[Requirement]: + """ + Return a list of requirements from a source distribution or wheel at + ``location`` using the provided ``handler`` DatafileHandler for parsing. + """ + if not location: + return [] + if not os.path.exists(location): + return [] + reqs = [] + for package_data in handler.parse(location): + dependencies = package_data.dependencies + reqs.extend(get_requirements_from_dependencies(dependencies=dependencies)) + return reqs + + +def get_deps_from_distribution( + handler: BasePypiHandler, + location: str, +) -> List[DependentPackage]: """ Return a list of requirements from a source distribution or wheel at ``location`` using the provided ``handler`` DatafileHandler for parsing. @@ -104,7 +123,7 @@ def get_requirements_from_distribution( deps = [] for package_data in handler.parse(location): dependencies = package_data.dependencies - deps.extend(get_requirements_from_dependencies(dependencies=dependencies)) + deps.extend(dependencies=dependencies) return deps @@ -133,7 +152,7 @@ def contain_string(string: str, files: List) -> bool: return False -def parse_setup_py_insecurely(setup_py): +def parse_reqs_from_setup_py_insecurely(setup_py): """ Yield requirements from the setup.py file at ``setup_py``. """ @@ -143,6 +162,27 @@ def parse_setup_py_insecurely(setup_py): yield Requirement(req) +def parse_deps_from_setup_py_insecurely(setup_py): + """ + Yield requirements from the setup.py file at ``setup_py``. + """ + if not os.path.exists(setup_py): + return [] + for req in iter_requirements(level="", extras=[], setup_file=setup_py): + parsed_req = Requirement(req) + yield DependentPackage( + purl=str( + PackageURL( + type="pypi", + name=parsed_req.name, + ) + ), + extracted_requirement=req, + scope="install", + is_runtime=False, + ) + + def is_valid_version( parsed_version: Union[LegacyVersion, Version], requirements: Dict, @@ -685,7 +725,7 @@ def get_setup_dependencies(location, analyze_setup_py_insecurely=False, use_requ string="_require", files=[setup_py_location, setup_cfg_location] ): if analyze_setup_py_insecurely: - yield from parse_setup_py_insecurely(setup_py=setup_py_location) + yield from parse_reqs_from_setup_py_insecurely(setup_py=setup_py_location) else: raise Exception("Unable to collect setup.py dependencies securely") diff --git a/src/python_inspector/resolve_cli.py b/src/python_inspector/resolve_cli.py index c68f5b81..9abb2c18 100644 --- a/src/python_inspector/resolve_cli.py +++ b/src/python_inspector/resolve_cli.py @@ -19,6 +19,7 @@ from tinynetrc import Netrc from _packagedcode.models import DependentPackage +from _packagedcode.pypi import PipRequirementsFileHandler from _packagedcode.pypi import PythonSetupPyHandler from _packagedcode.pypi import can_process_dependent_package from python_inspector import dependencies @@ -26,9 +27,12 @@ from python_inspector import utils_pypi from python_inspector.cli_utils import FileOptionType from python_inspector.package_data import get_pypi_data_from_purl +from python_inspector.resolution import contain_string +from python_inspector.resolution import get_deps_from_distribution from python_inspector.resolution import get_environment_marker_from_environment from python_inspector.resolution import get_python_version_from_env_tag from python_inspector.resolution import get_resolved_dependencies +from python_inspector.resolution import parse_deps_from_setup_py_insecurely TRACE = False @@ -283,6 +287,33 @@ def resolve_dependencies( if dep.scope == "install": direct_dependencies.append(dep) + if not package_data.dependencies: + has_deps = False + if contain_string(string="requirements.txt", files=[setup_py_file]): + # Look in requirements file if and only if thy are refered in setup.py or setup.cfg + # And no deps have been yielded by requirements file. + + location = os.path.dirname(setup_py_file) + requirement_location = os.path.join( + location, + "requirements.txt", + ) + deps = get_deps_from_distribution( + handler=PipRequirementsFileHandler, + location=requirement_location, + ) + if deps: + has_deps = True + direct_dependencies.extend(deps) + + if not has_deps and contain_string(string="_require", files=[setup_py_file]): + if analyze_setup_py_insecurely: + direct_dependencies.extend( + parse_deps_from_setup_py_insecurely(setup_py=setup_py_file) + ) + else: + raise Exception("Unable to collect setup.py dependencies securely") + if not direct_dependencies: click.secho("Error: no requirements requested.") ctx.exit(1) diff --git a/tests/data/insecure-setup/setup.py-expected.json b/tests/data/insecure-setup/setup.py-expected.json new file mode 100644 index 00000000..726aaa4f --- /dev/null +++ b/tests/data/insecure-setup/setup.py-expected.json @@ -0,0 +1,372 @@ +{ + "headers": { + "tool_name": "python-inspector", + "tool_homepageurl": "https://github.com/nexB/python-inspector", + "tool_version": "0.6.5", + "options": [ + "--index-url https://pypi.org/simple", + "--python-version 27", + "--operating-system linux", + "--json " + ], + "notice": "Dependency tree generated with python-inspector.\npython-inspector is a free software tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", + "warnings": [], + "errors": [] + }, + "requirements": [ + { + "purl": "pkg:pypi/isodate", + "extracted_requirement": "isodate", + "scope": "install", + "is_runtime": false, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:pypi/pyparsing", + "extracted_requirement": "pyparsing", + "scope": "install", + "is_runtime": false, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + }, + { + "purl": "pkg:pypi/six", + "extracted_requirement": "six", + "scope": "install", + "is_runtime": false, + "is_optional": false, + "is_resolved": false, + "resolved_package": {}, + "extra_data": {} + } + ], + "resolved_dependencies": [ + { + "package": "pkg:pypi/isodate@0.6.1", + "dependencies": [ + "pkg:pypi/six@1.16.0" + ] + }, + { + "package": "pkg:pypi/pyparsing@2.4.7", + "dependencies": [] + }, + { + "package": "pkg:pypi/six@1.16.0", + "dependencies": [] + } + ], + "packages": [ + { + "type": "pypi", + "namespace": null, + "name": "isodate", + "version": "0.6.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nISO 8601 date/time parser\n=========================\n\n.. image:: https://travis-ci.org/gweis/isodate.svg?branch=master\n :target: https://travis-ci.org/gweis/isodate\n :alt: Travis-CI\n.. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master\n :target: https://coveralls.io/r/gweis/isodate?branch=master\n :alt: Coveralls\n.. image:: https://img.shields.io/pypi/v/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: Latest Version\n.. image:: https://img.shields.io/pypi/l/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: License\n\n\nThis module implements ISO 8601 date, time and duration parsing.\nThe implementation follows ISO8601:2004 standard, and implements only\ndate/time representations mentioned in the standard. If something is not\nmentioned there, then it is treated as non existent, and not as an allowed\noption.\n\nFor instance, ISO8601:2004 never mentions 2 digit years. So, it is not\nintended by this module to support 2 digit years. (while it may still\nbe valid as ISO date, because it is not explicitly forbidden.)\nAnother example is, when no time zone information is given for a time,\nthen it should be interpreted as local time, and not UTC.\n\nAs this module maps ISO 8601 dates/times to standard Python data types, like\n*date*, *time*, *datetime* and *timedelta*, it is not possible to convert\nall possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are\nnot allowed by the Python *date* and *datetime* classes. Additionally\nfractional seconds are limited to microseconds. That means if the parser finds\nfor instance nanoseconds it will round it to microseconds.\n\nDocumentation\n-------------\n\nCurrently there are four parsing methods available.\n * parse_time:\n parses an ISO 8601 time string into a *time* object\n * parse_date:\n parses an ISO 8601 date string into a *date* object\n * parse_datetime:\n parses an ISO 8601 date-time string into a *datetime* object\n * parse_duration:\n parses an ISO 8601 duration string into a *timedelta* or *Duration*\n object.\n * parse_tzinfo:\n parses the time zone info part of an ISO 8601 string into a\n *tzinfo* object.\n\nAs ISO 8601 allows to define durations in years and months, and *timedelta*\ndoes not handle years and months, this module provides a *Duration* class,\nwhich can be used almost like a *timedelta* object (with some limitations).\nHowever, a *Duration* object can be converted into a *timedelta* object.\n\nThere are also ISO formatting methods for all supported data types. Each\n*xxx_isoformat* method accepts a format parameter. The default format is\nalways the ISO 8601 expanded format. This is the same format used by\n*datetime.isoformat*:\n\n * time_isoformat:\n Intended to create ISO time strings with default format\n *hh:mm:ssZ*.\n * date_isoformat:\n Intended to create ISO date strings with default format\n *yyyy-mm-dd*.\n * datetime_isoformat:\n Intended to create ISO date-time strings with default format\n *yyyy-mm-ddThh:mm:ssZ*.\n * duration_isoformat:\n Intended to create ISO duration strings with default format\n *PnnYnnMnnDTnnHnnMnnS*.\n * tz_isoformat:\n Intended to create ISO time zone strings with default format\n *hh:mm*.\n * strftime:\n A re-implementation mostly compatible with Python's *strftime*, but\n supports only those format strings, which can also be used for dates\n prior 1900. This method also understands how to format *datetime* and\n *Duration* instances.\n\nInstallation:\n-------------\n\nThis module can easily be installed with Python standard installation methods.\n\nEither use *python setup.py install* or in case you have *setuptools* or\n*distribute* available, you can also use *easy_install*.\n\nLimitations:\n------------\n\n * The parser accepts several date/time representation which should be invalid\n according to ISO 8601 standard.\n\n 1. for date and time together, this parser accepts a mixture of basic and extended format.\n e.g. the date could be in basic format, while the time is accepted in extended format.\n It also allows short dates and times in date-time strings.\n 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of\n 1901-01-01.\n 3. negative *Duration* and *timedelta* value are not fully supported yet.\n\nFurther information:\n--------------------\n\nThe doc strings and unit tests should provide rather detailed information about\nthe methods and their limitations.\n\nThe source release provides a *setup.py* script,\nwhich can be used to run the unit tests included.\n\nSource code is available at ``_.\n\nCHANGES\n=======\n\n0.6.1 (2021-12-13)\n------------------\n\n- support python 3.10 ()\n- last version to support py 2.7\n\n\n0.6.0 (2017-10-13)\n------------------\n\n- support incomplete month date (Fabien Loffredo)\n- rely on duck typing when doing duration maths\n- support ':' as separator in fractional time zones (usrenmae)\n\n\n0.5.4 (2015-08-06)\n------------------\n\n- Fix parsing of Periods (Fabien Bochu)\n- Make Duration objects hashable (Geoffrey Fairchild)\n- Add multiplication to duration (Reinoud Elhorst)\n\n\n0.5.1 (2014-11-07)\n------------------\n\n- fixed pickling of Duration objects\n- raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney)\n\n\n0.5.0 (2014-02-23)\n------------------\n\n- ISO8601Error are subclasses of ValueError now (Michael Hrivnak)\n- improve compatibility across various python variants and versions\n- raise exceptions when using fractional years and months in date\n maths with durations\n- renamed method todatetime on Duraction objects to totimedelta\n\n\n0.4.9 (2012-10-30)\n------------------\n\n- support pickling FixedOffset instances\n- make sure parsed fractional seconds are in microseconds\n- add leading zeros when formattig microseconds (Jarom Loveridge)\n\n\n0.4.8 (2012-05-04)\n------------------\n\n- fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7\n and 3.2)\n\n\n0.4.7 (2012-01-26)\n------------------\n\n- fixed tzinfo formatting (never pass None into tzinfo.utcoffset())\n\n\n0.4.6 (2012-01-06)\n------------------\n\n- added Python 3 compatibility via 2to3\n\n0.4.5 (2012-01-06)\n------------------\n\n- made setuptools dependency optional\n\n0.4.4 (2011-04-16)\n------------------\n\n- Fixed formatting of microseconds for datetime objects\n\n0.4.3 (2010-10-29)\n------------------\n\n- Fixed problem with %P formating and fractions (supplied by David Brooks)\n\n0.4.2 (2010-10-28)\n------------------\n\n- Implemented unary - for Duration (supplied by David Brooks)\n- Output fractional seconds with '%P' format. (partly supplied by David Brooks)\n\n0.4.1 (2010-10-13)\n------------------\n\n- fixed bug in comparison between timedelta and Duration.\n- fixed precision problem with microseconds (reported by Tommi Virtanen)\n\n0.4.0 (2009-02-09)\n------------------\n\n- added method to parse ISO 8601 time zone strings\n- added methods to create ISO 8601 conforming strings\n\n0.3.0 (2009-1-05)\n------------------\n\n- Initial release\n\nTODOs\n=====\n\nThis to do list contains some thoughts and ideas about missing features, and\nparts to think about, whether to implement them or not. This list is probably\nnot complete.\n\nMissing features:\n-----------------\n\n * time formating does not allow to create fractional representations.\n * parser for ISO intervals.\n * currently microseconds are always padded to a length of 6 characters.\n trailing 0s should be optional\n\nDocumentation:\n--------------\n\n * parse_datetime:\n - complete documentation to show what this function allows, but ISO forbids.\n and vice verse.\n - support other separators between date and time than 'T'\n\n * parse_date:\n - yeardigits should be always greater than 4\n - dates before 0001-01-01 are not supported\n\n * parse_duration:\n - alternative formats are not fully supported due to parse_date restrictions\n - standard duration format is fully supported but not very restrictive.\n\n * Duration:\n - support fractional years and month in calculations\n - implement w3c order relation? (``_)\n - refactor to have duration mathematics only at one place.\n - localize __str__ method (does timedelta do this?)\n - when is a Duration negative?\n - normalize Durations. months [00-12] and years ]-inf,+inf[\n\n\n", + "release_date": "2021-12-13T20:28:29", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Gerhard Weis", + "email": "gerhard.weis@proclos.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/gweis/isodate/", + "download_url": "https://files.pythonhosted.org/packages/b6/85/7882d311924cbcfc70b1890780763e36ff0b140c7e51c110fc59a532f087/isodate-0.6.1-py2.py3-none-any.whl", + "size": 41722, + "sha1": null, + "md5": "c8a5fcd645030db98daa82b8e56fda89", + "sha256": "0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/isodate/0.6.1/json", + "datasource_id": null, + "purl": "pkg:pypi/isodate@0.6.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "isodate", + "version": "0.6.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "\nISO 8601 date/time parser\n=========================\n\n.. image:: https://travis-ci.org/gweis/isodate.svg?branch=master\n :target: https://travis-ci.org/gweis/isodate\n :alt: Travis-CI\n.. image:: https://coveralls.io/repos/gweis/isodate/badge.svg?branch=master\n :target: https://coveralls.io/r/gweis/isodate?branch=master\n :alt: Coveralls\n.. image:: https://img.shields.io/pypi/v/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: Latest Version\n.. image:: https://img.shields.io/pypi/l/isodate.svg\n :target: https://pypi.python.org/pypi/isodate/ \n :alt: License\n\n\nThis module implements ISO 8601 date, time and duration parsing.\nThe implementation follows ISO8601:2004 standard, and implements only\ndate/time representations mentioned in the standard. If something is not\nmentioned there, then it is treated as non existent, and not as an allowed\noption.\n\nFor instance, ISO8601:2004 never mentions 2 digit years. So, it is not\nintended by this module to support 2 digit years. (while it may still\nbe valid as ISO date, because it is not explicitly forbidden.)\nAnother example is, when no time zone information is given for a time,\nthen it should be interpreted as local time, and not UTC.\n\nAs this module maps ISO 8601 dates/times to standard Python data types, like\n*date*, *time*, *datetime* and *timedelta*, it is not possible to convert\nall possible ISO 8601 dates/times. For instance, dates before 0001-01-01 are\nnot allowed by the Python *date* and *datetime* classes. Additionally\nfractional seconds are limited to microseconds. That means if the parser finds\nfor instance nanoseconds it will round it to microseconds.\n\nDocumentation\n-------------\n\nCurrently there are four parsing methods available.\n * parse_time:\n parses an ISO 8601 time string into a *time* object\n * parse_date:\n parses an ISO 8601 date string into a *date* object\n * parse_datetime:\n parses an ISO 8601 date-time string into a *datetime* object\n * parse_duration:\n parses an ISO 8601 duration string into a *timedelta* or *Duration*\n object.\n * parse_tzinfo:\n parses the time zone info part of an ISO 8601 string into a\n *tzinfo* object.\n\nAs ISO 8601 allows to define durations in years and months, and *timedelta*\ndoes not handle years and months, this module provides a *Duration* class,\nwhich can be used almost like a *timedelta* object (with some limitations).\nHowever, a *Duration* object can be converted into a *timedelta* object.\n\nThere are also ISO formatting methods for all supported data types. Each\n*xxx_isoformat* method accepts a format parameter. The default format is\nalways the ISO 8601 expanded format. This is the same format used by\n*datetime.isoformat*:\n\n * time_isoformat:\n Intended to create ISO time strings with default format\n *hh:mm:ssZ*.\n * date_isoformat:\n Intended to create ISO date strings with default format\n *yyyy-mm-dd*.\n * datetime_isoformat:\n Intended to create ISO date-time strings with default format\n *yyyy-mm-ddThh:mm:ssZ*.\n * duration_isoformat:\n Intended to create ISO duration strings with default format\n *PnnYnnMnnDTnnHnnMnnS*.\n * tz_isoformat:\n Intended to create ISO time zone strings with default format\n *hh:mm*.\n * strftime:\n A re-implementation mostly compatible with Python's *strftime*, but\n supports only those format strings, which can also be used for dates\n prior 1900. This method also understands how to format *datetime* and\n *Duration* instances.\n\nInstallation:\n-------------\n\nThis module can easily be installed with Python standard installation methods.\n\nEither use *python setup.py install* or in case you have *setuptools* or\n*distribute* available, you can also use *easy_install*.\n\nLimitations:\n------------\n\n * The parser accepts several date/time representation which should be invalid\n according to ISO 8601 standard.\n\n 1. for date and time together, this parser accepts a mixture of basic and extended format.\n e.g. the date could be in basic format, while the time is accepted in extended format.\n It also allows short dates and times in date-time strings.\n 2. For incomplete dates, the first day is chosen. e.g. 19th century results in a date of\n 1901-01-01.\n 3. negative *Duration* and *timedelta* value are not fully supported yet.\n\nFurther information:\n--------------------\n\nThe doc strings and unit tests should provide rather detailed information about\nthe methods and their limitations.\n\nThe source release provides a *setup.py* script,\nwhich can be used to run the unit tests included.\n\nSource code is available at ``_.\n\nCHANGES\n=======\n\n0.6.1 (2021-12-13)\n------------------\n\n- support python 3.10 ()\n- last version to support py 2.7\n\n\n0.6.0 (2017-10-13)\n------------------\n\n- support incomplete month date (Fabien Loffredo)\n- rely on duck typing when doing duration maths\n- support ':' as separator in fractional time zones (usrenmae)\n\n\n0.5.4 (2015-08-06)\n------------------\n\n- Fix parsing of Periods (Fabien Bochu)\n- Make Duration objects hashable (Geoffrey Fairchild)\n- Add multiplication to duration (Reinoud Elhorst)\n\n\n0.5.1 (2014-11-07)\n------------------\n\n- fixed pickling of Duration objects\n- raise ISO8601Error when there is no 'T' separator in datetime strings (Adrian Coveney)\n\n\n0.5.0 (2014-02-23)\n------------------\n\n- ISO8601Error are subclasses of ValueError now (Michael Hrivnak)\n- improve compatibility across various python variants and versions\n- raise exceptions when using fractional years and months in date\n maths with durations\n- renamed method todatetime on Duraction objects to totimedelta\n\n\n0.4.9 (2012-10-30)\n------------------\n\n- support pickling FixedOffset instances\n- make sure parsed fractional seconds are in microseconds\n- add leading zeros when formattig microseconds (Jarom Loveridge)\n\n\n0.4.8 (2012-05-04)\n------------------\n\n- fixed incompatibility of unittests with python 2.5 and 2.6 (runs fine on 2.7\n and 3.2)\n\n\n0.4.7 (2012-01-26)\n------------------\n\n- fixed tzinfo formatting (never pass None into tzinfo.utcoffset())\n\n\n0.4.6 (2012-01-06)\n------------------\n\n- added Python 3 compatibility via 2to3\n\n0.4.5 (2012-01-06)\n------------------\n\n- made setuptools dependency optional\n\n0.4.4 (2011-04-16)\n------------------\n\n- Fixed formatting of microseconds for datetime objects\n\n0.4.3 (2010-10-29)\n------------------\n\n- Fixed problem with %P formating and fractions (supplied by David Brooks)\n\n0.4.2 (2010-10-28)\n------------------\n\n- Implemented unary - for Duration (supplied by David Brooks)\n- Output fractional seconds with '%P' format. (partly supplied by David Brooks)\n\n0.4.1 (2010-10-13)\n------------------\n\n- fixed bug in comparison between timedelta and Duration.\n- fixed precision problem with microseconds (reported by Tommi Virtanen)\n\n0.4.0 (2009-02-09)\n------------------\n\n- added method to parse ISO 8601 time zone strings\n- added methods to create ISO 8601 conforming strings\n\n0.3.0 (2009-1-05)\n------------------\n\n- Initial release\n\nTODOs\n=====\n\nThis to do list contains some thoughts and ideas about missing features, and\nparts to think about, whether to implement them or not. This list is probably\nnot complete.\n\nMissing features:\n-----------------\n\n * time formating does not allow to create fractional representations.\n * parser for ISO intervals.\n * currently microseconds are always padded to a length of 6 characters.\n trailing 0s should be optional\n\nDocumentation:\n--------------\n\n * parse_datetime:\n - complete documentation to show what this function allows, but ISO forbids.\n and vice verse.\n - support other separators between date and time than 'T'\n\n * parse_date:\n - yeardigits should be always greater than 4\n - dates before 0001-01-01 are not supported\n\n * parse_duration:\n - alternative formats are not fully supported due to parse_date restrictions\n - standard duration format is fully supported but not very restrictive.\n\n * Duration:\n - support fractional years and month in calculations\n - implement w3c order relation? (``_)\n - refactor to have duration mathematics only at one place.\n - localize __str__ method (does timedelta do this?)\n - when is a Duration negative?\n - normalize Durations. months [00-12] and years ]-inf,+inf[\n\n\n", + "release_date": "2021-12-13T20:28:31", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Gerhard Weis", + "email": "gerhard.weis@proclos.com", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/gweis/isodate/", + "download_url": "https://files.pythonhosted.org/packages/db/7a/c0a56c7d56c7fa723988f122fa1f1ccf8c5c4ccc48efad0d214b49e5b1af/isodate-0.6.1.tar.gz", + "size": 28443, + "sha1": null, + "md5": "1a310658b30a48641bafb5652ad91c40", + "sha256": "48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "BSD", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/isodate/0.6.1/json", + "datasource_id": null, + "purl": "pkg:pypi/isodate@0.6.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "pyparsing", + "version": "2.4.7", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "PyParsing -- A Python Parsing Module\n====================================\n\n|Build Status|\n\nIntroduction\n============\n\nThe pyparsing module is an alternative approach to creating and\nexecuting simple grammars, vs. the traditional lex/yacc approach, or the\nuse of regular expressions. The pyparsing module provides a library of\nclasses that client code uses to construct the grammar directly in\nPython code.\n\n*[Since first writing this description of pyparsing in late 2003, this\ntechnique for developing parsers has become more widespread, under the\nname Parsing Expression Grammars - PEGs. See more information on PEGs at*\nhttps://en.wikipedia.org/wiki/Parsing_expression_grammar *.]*\n\nHere is a program to parse ``\"Hello, World!\"`` (or any greeting of the form\n``\"salutation, addressee!\"``):\n\n.. code:: python\n\n from pyparsing import Word, alphas\n greet = Word(alphas) + \",\" + Word(alphas) + \"!\"\n hello = \"Hello, World!\"\n print(hello, \"->\", greet.parseString(hello))\n\nThe program outputs the following::\n\n Hello, World! -> ['Hello', ',', 'World', '!']\n\nThe Python representation of the grammar is quite readable, owing to the\nself-explanatory class names, and the use of '+', '|' and '^' operator\ndefinitions.\n\nThe parsed results returned from ``parseString()`` can be accessed as a\nnested list, a dictionary, or an object with named attributes.\n\nThe pyparsing module handles some of the problems that are typically\nvexing when writing text parsers:\n\n- extra or missing whitespace (the above program will also handle ``\"Hello,World!\"``, ``\"Hello , World !\"``, etc.)\n- quoted strings\n- embedded comments\n\nThe examples directory includes a simple SQL parser, simple CORBA IDL\nparser, a config file parser, a chemical formula parser, and a four-\nfunction algebraic notation parser, among many others.\n\nDocumentation\n=============\n\nThere are many examples in the online docstrings of the classes\nand methods in pyparsing. You can find them compiled into online docs\nat https://pyparsing-docs.readthedocs.io/en/latest/. Additional\ndocumentation resources and project info are listed in the online\nGitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An\nentire directory of examples is at\nhttps://github.com/pyparsing/pyparsing/tree/master/examples.\n\nLicense\n=======\n\nMIT License. See header of pyparsing.py\n\nHistory\n=======\n\nSee CHANGES file.\n\n.. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master\n :target: https://travis-ci.org/pyparsing/pyparsing\n\n\n", + "release_date": "2020-04-05T22:21:22", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Paul McGuire", + "email": "ptmcg@users.sourceforge.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pyparsing/pyparsing/", + "download_url": "https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl", + "size": 67842, + "sha1": null, + "md5": "dbfd0a241aad2595f43377ec7f1836ea", + "sha256": "ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pyparsing/2.4.7/json", + "datasource_id": null, + "purl": "pkg:pypi/pyparsing@2.4.7" + }, + { + "type": "pypi", + "namespace": null, + "name": "pyparsing", + "version": "2.4.7", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "PyParsing -- A Python Parsing Module\n====================================\n\n|Build Status|\n\nIntroduction\n============\n\nThe pyparsing module is an alternative approach to creating and\nexecuting simple grammars, vs. the traditional lex/yacc approach, or the\nuse of regular expressions. The pyparsing module provides a library of\nclasses that client code uses to construct the grammar directly in\nPython code.\n\n*[Since first writing this description of pyparsing in late 2003, this\ntechnique for developing parsers has become more widespread, under the\nname Parsing Expression Grammars - PEGs. See more information on PEGs at*\nhttps://en.wikipedia.org/wiki/Parsing_expression_grammar *.]*\n\nHere is a program to parse ``\"Hello, World!\"`` (or any greeting of the form\n``\"salutation, addressee!\"``):\n\n.. code:: python\n\n from pyparsing import Word, alphas\n greet = Word(alphas) + \",\" + Word(alphas) + \"!\"\n hello = \"Hello, World!\"\n print(hello, \"->\", greet.parseString(hello))\n\nThe program outputs the following::\n\n Hello, World! -> ['Hello', ',', 'World', '!']\n\nThe Python representation of the grammar is quite readable, owing to the\nself-explanatory class names, and the use of '+', '|' and '^' operator\ndefinitions.\n\nThe parsed results returned from ``parseString()`` can be accessed as a\nnested list, a dictionary, or an object with named attributes.\n\nThe pyparsing module handles some of the problems that are typically\nvexing when writing text parsers:\n\n- extra or missing whitespace (the above program will also handle ``\"Hello,World!\"``, ``\"Hello , World !\"``, etc.)\n- quoted strings\n- embedded comments\n\nThe examples directory includes a simple SQL parser, simple CORBA IDL\nparser, a config file parser, a chemical formula parser, and a four-\nfunction algebraic notation parser, among many others.\n\nDocumentation\n=============\n\nThere are many examples in the online docstrings of the classes\nand methods in pyparsing. You can find them compiled into online docs\nat https://pyparsing-docs.readthedocs.io/en/latest/. Additional\ndocumentation resources and project info are listed in the online\nGitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An\nentire directory of examples is at\nhttps://github.com/pyparsing/pyparsing/tree/master/examples.\n\nLicense\n=======\n\nMIT License. See header of pyparsing.py\n\nHistory\n=======\n\nSee CHANGES file.\n\n.. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master\n :target: https://travis-ci.org/pyparsing/pyparsing\n\n\n", + "release_date": "2020-04-05T22:21:25", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Paul McGuire", + "email": "ptmcg@users.sourceforge.net", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/pyparsing/pyparsing/", + "download_url": "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz", + "size": 649718, + "sha1": null, + "md5": "f0953e47a0112f7a65aec2305ffdf7b4", + "sha256": "c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT License", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pyparsing/2.4.7/json", + "datasource_id": null, + "purl": "pkg:pypi/pyparsing@2.4.7" + }, + { + "type": "pypi", + "namespace": null, + "name": "six", + "version": "1.16.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/six.svg\n :target: https://pypi.org/project/six/\n :alt: six on PyPI\n\n.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master\n :target: https://travis-ci.org/benjaminp/six\n :alt: six on TravisCI\n\n.. image:: https://readthedocs.org/projects/six/badge/?version=latest\n :target: https://six.readthedocs.io/\n :alt: six's documentation on Read the Docs\n\n.. image:: https://img.shields.io/badge/license-MIT-green.svg\n :target: https://github.com/benjaminp/six/blob/master/LICENSE\n :alt: MIT License badge\n\nSix is a Python 2 and 3 compatibility library. It provides utility functions\nfor smoothing over the differences between the Python versions with the goal of\nwriting Python code that is compatible on both Python versions. See the\ndocumentation for more information on what is provided.\n\nSix supports Python 2.7 and 3.3+. It is contained in only one Python\nfile, so it can be easily copied into your project. (The copyright and license\nnotice must be retained.)\n\nOnline documentation is at https://six.readthedocs.io/.\n\nBugs can be reported to https://github.com/benjaminp/six. The code can also\nbe found there.\n\n\n", + "release_date": "2021-05-05T14:18:17", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Benjamin Peterson", + "email": "benjamin@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benjaminp/six", + "download_url": "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", + "size": 11053, + "sha1": null, + "md5": "529d7fd7e14612ccde86417b4402d6f3", + "sha256": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/six/1.16.0/json", + "datasource_id": null, + "purl": "pkg:pypi/six@1.16.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "six", + "version": "1.16.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": ".. image:: https://img.shields.io/pypi/v/six.svg\n :target: https://pypi.org/project/six/\n :alt: six on PyPI\n\n.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master\n :target: https://travis-ci.org/benjaminp/six\n :alt: six on TravisCI\n\n.. image:: https://readthedocs.org/projects/six/badge/?version=latest\n :target: https://six.readthedocs.io/\n :alt: six's documentation on Read the Docs\n\n.. image:: https://img.shields.io/badge/license-MIT-green.svg\n :target: https://github.com/benjaminp/six/blob/master/LICENSE\n :alt: MIT License badge\n\nSix is a Python 2 and 3 compatibility library. It provides utility functions\nfor smoothing over the differences between the Python versions with the goal of\nwriting Python code that is compatible on both Python versions. See the\ndocumentation for more information on what is provided.\n\nSix supports Python 2.7 and 3.3+. It is contained in only one Python\nfile, so it can be easily copied into your project. (The copyright and license\nnotice must be retained.)\n\nOnline documentation is at https://six.readthedocs.io/.\n\nBugs can be reported to https://github.com/benjaminp/six. The code can also\nbe found there.\n\n\n", + "release_date": "2021-05-05T14:18:18", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Benjamin Peterson", + "email": "benjamin@python.org", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "", + "email": "", + "url": null + } + ], + "keywords": [], + "homepage_url": "https://github.com/benjaminp/six", + "download_url": "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", + "size": 34041, + "sha1": null, + "md5": "a7c927740e4964dd29b72cebfc1429bb", + "sha256": "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": "MIT", + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/six/1.16.0/json", + "datasource_id": null, + "purl": "pkg:pypi/six@1.16.0" + } + ] +} \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py index c4aafe29..5e0e60ee 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -254,6 +254,18 @@ def test_cli_with_insecure_option_testpkh(): ) +@pytest.mark.online +def test_cli_with_insecure_option_testrdflib(): + setup_py_file = test_env.get_test_loc("insecure-setup/setup.py") + expected_file = test_env.get_test_loc("insecure-setup/setup.py-expected.json", must_exist=False) + check_setup_py_resolution( + setup_py=setup_py_file, + expected_file=expected_file, + regen=REGEN_TEST_FIXTURES, + extra_options=["--python-version", "27", "--analyze-setup-py-insecurely"], + ) + + @pytest.mark.online def test_cli_with_setup_py(): setup_py_file = setup_test_env.get_test_loc("simple-setup.py") diff --git a/tests/test_resolution.py b/tests/test_resolution.py index 6a41594e..e9f85a34 100644 --- a/tests/test_resolution.py +++ b/tests/test_resolution.py @@ -19,7 +19,7 @@ from python_inspector.resolution import get_requirements_from_dependencies from python_inspector.resolution import get_resolved_dependencies from python_inspector.resolution import is_valid_version -from python_inspector.resolution import parse_setup_py_insecurely +from python_inspector.resolution import parse_reqs_from_setup_py_insecurely from python_inspector.utils_pypi import PYPI_PUBLIC_REPO from python_inspector.utils_pypi import Environment @@ -224,13 +224,13 @@ def test_get_requirements_from_dependencies_with_editable_requirements(): def test_setup_py_parsing_insecure(): setup_py_file = setup_test_env.get_test_loc("insecure-setup/setup.py") - reqs = [str(req) for req in list(parse_setup_py_insecurely(setup_py=setup_py_file))] + reqs = [str(req) for req in list(parse_reqs_from_setup_py_insecurely(setup_py=setup_py_file))] assert reqs == ["isodate", "pyparsing", "six"] def test_setup_py_parsing_insecure_testpkh(): setup_py_file = setup_test_env.get_test_loc("insecure-setup-2/setup.py") - reqs = [str(req) for req in list(parse_setup_py_insecurely(setup_py=setup_py_file))] + reqs = [str(req) for req in list(parse_reqs_from_setup_py_insecurely(setup_py=setup_py_file))] assert reqs == [ "CairoSVG<2.0.0,>=1.0.20", "click>=5.0.0", From 638157708b06a2141941a85b87ec254443bc46d7 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Tue, 20 Sep 2022 21:39:28 +0530 Subject: [PATCH 53/54] Address review comments Signed-off-by: Tushar Goel --- tests/{fixtures => data}/requirements.devel.txt | 0 tests/{fixtures => data}/setup.txt | 0 tests/test_setup_py_live_eval.py | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/{fixtures => data}/requirements.devel.txt (100%) rename tests/{fixtures => data}/setup.txt (100%) diff --git a/tests/fixtures/requirements.devel.txt b/tests/data/requirements.devel.txt similarity index 100% rename from tests/fixtures/requirements.devel.txt rename to tests/data/requirements.devel.txt diff --git a/tests/fixtures/setup.txt b/tests/data/setup.txt similarity index 100% rename from tests/fixtures/setup.txt rename to tests/data/setup.txt diff --git a/tests/test_setup_py_live_eval.py b/tests/test_setup_py_live_eval.py index 4d91663e..3deb301d 100755 --- a/tests/test_setup_py_live_eval.py +++ b/tests/test_setup_py_live_eval.py @@ -15,8 +15,8 @@ from python_inspector.setup_py_live_eval import iter_requirements -REQ = abspath(join(dirname(__file__), "./fixtures/requirements.devel.txt")) -SETUP = abspath(join(dirname(__file__), "./fixtures/setup.txt")) +REQ = abspath(join(dirname(__file__), "./data/requirements.devel.txt")) +SETUP = abspath(join(dirname(__file__), "./data/setup.txt")) def test_iter_requirements_with_setup_py(): From 426b9298384c27e687ac48202fdf3275f91a7bf5 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 28 Sep 2022 03:03:48 +0530 Subject: [PATCH 54/54] Address review comments Signed-off-by: Tushar Goel --- src/python_inspector/resolution.py | 12 ++- src/python_inspector/setup_py_live_eval.py | 113 +++++++-------------- tests/test_resolution.py | 2 +- 3 files changed, 45 insertions(+), 82 deletions(-) diff --git a/src/python_inspector/resolution.py b/src/python_inspector/resolution.py index 94df76c2..86266db3 100644 --- a/src/python_inspector/resolution.py +++ b/src/python_inspector/resolution.py @@ -140,7 +140,7 @@ def get_environment_marker_from_environment(environment): def contain_string(string: str, files: List) -> bool: """ - Return True if the string is contains in any of the files. + Return True if the ``string`` is contained in any of the ``files`` list of file paths. """ for file in files: if not os.path.exists(file): @@ -154,7 +154,7 @@ def contain_string(string: str, files: List) -> bool: def parse_reqs_from_setup_py_insecurely(setup_py): """ - Yield requirements from the setup.py file at ``setup_py``. + Yield Requirement(s) from a ``setup_py`` setup.py file location . """ if not os.path.exists(setup_py): return [] @@ -164,7 +164,7 @@ def parse_reqs_from_setup_py_insecurely(setup_py): def parse_deps_from_setup_py_insecurely(setup_py): """ - Yield requirements from the setup.py file at ``setup_py``. + Yield DependentPackage(s) from the ``setup_py`` setup.py file location . """ if not os.path.exists(setup_py): return [] @@ -668,7 +668,11 @@ def get_package_list(results): def get_setup_dependencies(location, analyze_setup_py_insecurely=False, use_requirements=True): """ - Yield dependencies from the given setup.py and setup.cfg location. + Yield Requirement(s) from Pypi in the ``location`` directory that contains + a setup.py and/or a setup.cfg and optionally a requirements.txt file if + ``use_requirements`` is True and this file is used in the setup.py or setup.cfg. + Perform an insecure live evaluation of the Python code if needed and if + ``analyze_setup_py_insecurely`` is True. """ setup_py_location = os.path.join( diff --git a/src/python_inspector/setup_py_live_eval.py b/src/python_inspector/setup_py_live_eval.py index 340aa04a..e80013a4 100755 --- a/src/python_inspector/setup_py_live_eval.py +++ b/src/python_inspector/setup_py_live_eval.py @@ -9,9 +9,6 @@ # """Generate requirements from `setup.py` and `requirements-devel.txt`.""" -from __future__ import absolute_import -from __future__ import print_function - import os import re import sys @@ -22,17 +19,9 @@ import ConfigParser as configparser import mock -import pkg_resources import setuptools - - -def parse_set(string): - """Parse set from comma separated string.""" - string = string.strip() - if string: - return set(string.split(",")) - else: - return set() +from commoncode.command import pushd +from packaging.requirements import Requirement def minver_error(pkg_name): @@ -47,49 +36,8 @@ def minver_error(pkg_name): def build_pkg_name(pkg): """Build package name, including extras if present.""" if pkg.extras: - return "{0}[{1}]".format(pkg.project_name, ",".join(sorted(pkg.extras))) - return pkg.project_name - - -def parse_pip_file(path): - """Parse pip requirements file.""" - # requirement lines sorted by importance - # also collect other pip commands - rdev = {} - rnormal = [] - stuff = [] - - try: - with open(path) as f: - for line in f: - line = line.strip() - - # see https://pip.readthedocs.io/en/1.1/requirements.html - if line.startswith("-e"): - # devel requirement - splitted = line.split("#egg=") - rdev[splitted[1].lower()] = line - - elif line.startswith("-r"): - # recursive file command - splitted = re.split("-r\\s+", line) - subrdev, subrnormal, substuff = parse_pip_file( - os.path.join(os.path.dirname(path), splitted[1]) - ) - for k, v in subrdev.items(): - if k not in rdev: - rdev[k] = v - rnormal.extend(subrnormal) - elif line.startswith("-"): - # another special command we don't recognize - stuff.append(line) - else: - # ordinary requirement, similarly to them used in setup.py - rnormal.append(line) - except IOError: - print('Warning: could not parse requirements file "{0}"!'.format(path), file=sys.stderr) - - return rdev, rnormal, stuff + return "{0}[{1}]".format(str(pkg.name), ",".join(sorted(pkg.extras))) + return str(pkg.name) def iter_requirements(level, extras, setup_file): @@ -100,24 +48,26 @@ def iter_requirements(level, extras, setup_file): result = dict() requires = [] stuff = [] - cd = os.getcwd() - os.chdir(os.path.dirname(setup_file)) install_requires = [] requires_extras = {} + test_requires = {} + setup_requires = {} # change directory to setup.py path - with mock.patch.object(setuptools, "setup") as mock_setup: - sys.path.append(os.path.dirname(setup_file)) - g = {"__file__": setup_file, "__name__": "__main__"} - with open(setup_file) as sf: - exec(sf.read(), g) - sys.path.pop() - assert g["setup"] # silence warning about unused imports + with pushd(os.path.dirname(setup_file)): + with mock.patch.object(setuptools, "setup") as mock_setup: + sys.path.append(os.path.dirname(setup_file)) + g = {"__file__": setup_file, "__name__": "__main__"} + with open(setup_file) as sf: + exec(sf.read(), g) + sys.path.pop() + assert g["setup"] # silence warning about unused imports # called arguments are in `mock_setup.call_args` - os.chdir(cd) mock_args, mock_kwargs = mock_setup.call_args install_requires = mock_kwargs.get("install_requires", install_requires) requires_extras = mock_kwargs.get("extras_require", requires_extras) + test_requires = mock_kwargs.get("test_requires", test_requires) + setup_requires = mock_kwargs.get("setup_requires", setup_requires) for e, reqs in requires_extras.items(): # Handle conditions on extras. See pkginfo_to_metadata function @@ -130,10 +80,18 @@ def iter_requirements(level, extras, setup_file): reqs = ["{0}; {1}".format(r, condition) for r in reqs] install_requires.extend(reqs) - for pkg in pkg_resources.parse_requirements(install_requires): + for reqs in test_requires: + if "test" in extras: + install_requires.extend(reqs) + + for reqs in setup_requires: + if "setup" in extras: + install_requires.extend(reqs) + + for req in install_requires: # skip things we already know # FIXME be smarter about merging things - + pkg = Requirement(req) # Evaluate environment markers skip if not applicable if hasattr(pkg, "marker") and pkg.marker is not None: if not pkg.marker.evaluate(): @@ -142,10 +100,11 @@ def iter_requirements(level, extras, setup_file): # Remove markers from the output pkg.marker = None - if pkg.key in result: + if pkg.name in result: continue - specs = dict(pkg.specs) + specs = pkg.specifier + specs = {s.operator: s.version for s in specs._specs} if ((">=" in specs) and (">" in specs)) or (("<=" in specs) and ("<" in specs)): print( "ERROR: Do not specify such weird constraints! " '("{0}")'.format(pkg), @@ -154,32 +113,32 @@ def iter_requirements(level, extras, setup_file): sys.exit(1) if "==" in specs: - result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs["=="]) + result[pkg.name] = "{0}=={1}".format(build_pkg_name(pkg), specs["=="]) elif ">=" in specs: if level == "min": - result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs[">="]) + result[pkg.name] = "{0}=={1}".format(build_pkg_name(pkg), specs[">="]) else: - result[pkg.key] = pkg + result[pkg.name] = pkg elif ">" in specs: if level == "min": minver_error(build_pkg_name(pkg)) else: - result[pkg.key] = pkg + result[pkg.name] = pkg elif "~=" in specs: if level == "min": - result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs["~="]) + result[pkg.name] = "{0}=={1}".format(build_pkg_name(pkg), specs["~="]) else: ver, _ = os.path.splitext(specs["~="]) - result[pkg.key] = "{0}>={1},=={2}.*".format(build_pkg_name(pkg), specs["~="], ver) + result[pkg.name] = "{0}>={1},=={2}.*".format(build_pkg_name(pkg), specs["~="], ver) else: if level == "min": minver_error(build_pkg_name(pkg)) else: - result[pkg.key] = build_pkg_name(pkg) + result[pkg.name] = build_pkg_name(pkg) for s in stuff: yield s diff --git a/tests/test_resolution.py b/tests/test_resolution.py index e9f85a34..743a8203 100644 --- a/tests/test_resolution.py +++ b/tests/test_resolution.py @@ -147,7 +147,7 @@ def test_without_supported_wheels(): "pkg:pypi/hyperlink@21.0.0", "pkg:pypi/idna@3.4", "pkg:pypi/pycparser@2.21", - "pkg:pypi/setuptools@65.3.0", + "pkg:pypi/setuptools@65.4.0", "pkg:pypi/txaio@22.2.1", ]