[bumpversion]
-current_version = 65.7.0
+current_version = 66.0.0
commit = True
tag = True
+v66.0.0
+-------
+
+
+Breaking Changes
+^^^^^^^^^^^^^^^^
+* #2497: Support for PEP 440 non-conforming versions has been removed. Environments containing packages with non-conforming versions may fail or the packages may not be recognized.
+
+Changes
+^^^^^^^
+* #3769: Replace 'appdirs' with 'platformdirs'.
+
+
v65.7.0
-------
* the ``sysconfig`` variables ``CC``, ``CXX``, ``CCSHARED``,
``LDSHARED``, and ``CFLAGS``,
* the environment variables ``CC``, ``CPP``,
- ``CXX``, ``LDSHARED`` and ``LDFLAGS``,
- ``CFLAGS``, ``CPPFLAGS``, ``LDFLAGS``,
+ ``CXX``, ``LDSHARED`` and ``CFLAGS``,
+ ``CPPFLAGS``, ``LDFLAGS``,
* the ``Extension`` attributes ``include_dirs``,
``library_dirs``, ``extra_compile_args``, ``extra_link_args``,
``runtime_library_dirs``.
import errno
import tempfile
import textwrap
-import itertools
import inspect
import ntpath
import posixpath
# capture these to bypass sandboxing
from os import utime
+
try:
from os import mkdir, rename, unlink
+
WRITE_SUPPORT = True
except ImportError:
# no write support, probably under GAE
try:
import importlib.machinery as importlib_machinery
+
# access attribute to force import under delayed import mechanisms.
importlib_machinery.__name__
except ImportError:
join_continuation,
)
-from pkg_resources.extern import appdirs
+from pkg_resources.extern import platformdirs
from pkg_resources.extern import packaging
+
__import__('pkg_resources.extern.packaging.version')
__import__('pkg_resources.extern.packaging.specifiers')
__import__('pkg_resources.extern.packaging.requirements')
"""
-def parse_version(v):
- try:
- return packaging.version.Version(v)
- except packaging.version.InvalidVersion:
- warnings.warn(
- f"{v} is an invalid version and will not be supported in "
- "a future release",
- PkgResourcesDeprecationWarning,
- )
- return packaging.version.LegacyVersion(v)
+parse_version = packaging.version.Version
_state_vars = {}
__all__ = [
# Basic resource access and distribution/entry point discovery
- 'require', 'run_script', 'get_provider', 'get_distribution',
- 'load_entry_point', 'get_entry_map', 'get_entry_info',
+ 'require',
+ 'run_script',
+ 'get_provider',
+ 'get_distribution',
+ 'load_entry_point',
+ 'get_entry_map',
+ 'get_entry_info',
'iter_entry_points',
- 'resource_string', 'resource_stream', 'resource_filename',
- 'resource_listdir', 'resource_exists', 'resource_isdir',
-
+ 'resource_string',
+ 'resource_stream',
+ 'resource_filename',
+ 'resource_listdir',
+ 'resource_exists',
+ 'resource_isdir',
# Environmental control
- 'declare_namespace', 'working_set', 'add_activation_listener',
- 'find_distributions', 'set_extraction_path', 'cleanup_resources',
+ 'declare_namespace',
+ 'working_set',
+ 'add_activation_listener',
+ 'find_distributions',
+ 'set_extraction_path',
+ 'cleanup_resources',
'get_default_cache',
-
# Primary implementation classes
- 'Environment', 'WorkingSet', 'ResourceManager',
- 'Distribution', 'Requirement', 'EntryPoint',
-
+ 'Environment',
+ 'WorkingSet',
+ 'ResourceManager',
+ 'Distribution',
+ 'Requirement',
+ 'EntryPoint',
# Exceptions
- 'ResolutionError', 'VersionConflict', 'DistributionNotFound',
- 'UnknownExtra', 'ExtractionError',
-
+ 'ResolutionError',
+ 'VersionConflict',
+ 'DistributionNotFound',
+ 'UnknownExtra',
+ 'ExtractionError',
# Warnings
'PEP440Warning',
-
# Parsing functions and string utilities
- 'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
- 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
- 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',
-
+ 'parse_requirements',
+ 'parse_version',
+ 'safe_name',
+ 'safe_version',
+ 'get_platform',
+ 'compatible_platforms',
+ 'yield_lines',
+ 'split_sections',
+ 'safe_extra',
+ 'to_filename',
+ 'invalid_marker',
+ 'evaluate_marker',
# filesystem utilities
- 'ensure_directory', 'normalize_path',
-
+ 'ensure_directory',
+ 'normalize_path',
# Distribution "precedence" constants
- 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST',
-
+ 'EGG_DIST',
+ 'BINARY_DIST',
+ 'SOURCE_DIST',
+ 'CHECKOUT_DIST',
+ 'DEVELOP_DIST',
# "Provider" interfaces, implementations, and registration/lookup APIs
- 'IMetadataProvider', 'IResourceProvider', 'FileMetadata',
- 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider',
- 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider',
- 'register_finder', 'register_namespace_handler', 'register_loader_type',
- 'fixup_namespace_packages', 'get_importer',
-
+ 'IMetadataProvider',
+ 'IResourceProvider',
+ 'FileMetadata',
+ 'PathMetadata',
+ 'EggMetadata',
+ 'EmptyProvider',
+ 'empty_provider',
+ 'NullProvider',
+ 'EggProvider',
+ 'DefaultProvider',
+ 'ZipProvider',
+ 'register_finder',
+ 'register_namespace_handler',
+ 'register_loader_type',
+ 'fixup_namespace_packages',
+ 'get_importer',
# Warnings
'PkgResourcesDeprecationWarning',
-
# Deprecated/backward compatibility only
- 'run_main', 'AvailableDistributions',
+ 'run_main',
+ 'AvailableDistributions',
]
class DistributionNotFound(ResolutionError):
"""A requested distribution was not found"""
- _template = ("The '{self.req}' distribution was not found "
- "and is required by {self.requirers_str}")
+ _template = (
+ "The '{self.req}' distribution was not found "
+ "and is required by {self.requirers_str}"
+ )
@property
def req(self):
version = _macos_vers()
machine = os.uname()[4].replace(" ", "_")
return "macosx-%d.%d-%s" % (
- int(version[0]), int(version[1]),
+ int(version[0]),
+ int(version[1]),
_macos_arch(machine),
)
except ValueError:
if provDarwin:
dversion = int(provDarwin.group(1))
macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))
- if dversion == 7 and macosversion >= "10.3" or \
- dversion == 8 and macosversion >= "10.4":
+ if (
+ dversion == 7
+ and macosversion >= "10.3"
+ or dversion == 8
+ and macosversion >= "10.4"
+ ):
return True
# egg isn't macOS or legacy darwin
return False
# are they the same major version and machine type?
- if provMac.group(1) != reqMac.group(1) or \
- provMac.group(3) != reqMac.group(3):
+ if provMac.group(1) != reqMac.group(1) or provMac.group(3) != reqMac.group(3):
return False
# is the required OS major update >= the provided one?
def get_metadata_lines(name):
"""Yield named metadata resource as list of non-blank non-comment lines
- Leading and trailing whitespace is stripped from each line, and lines
- with ``#`` as the first non-blank character are omitted."""
+ Leading and trailing whitespace is stripped from each line, and lines
+ with ``#`` as the first non-blank character are omitted."""
def metadata_isdir(name):
"""Is the named metadata a directory? (like ``os.path.isdir()``)"""
keys2.append(dist.key)
self._added_new(dist)
- # FIXME: 'WorkingSet.resolve' is too complex (11)
- def resolve(self, requirements, env=None, installer=None, # noqa: C901
- replace_conflicting=False, extras=None):
+ def resolve(
+ self,
+ requirements,
+ env=None,
+ installer=None,
+ replace_conflicting=False,
+ extras=None,
+ ):
"""List all distributions needed to (recursively) meet `requirements`
`requirements` must be a sequence of ``Requirement`` objects. `env`,
if not req_extras.markers_pass(req, extras):
continue
- dist = best.get(req.key)
- if dist is None:
- # Find the best distribution and add it to the map
- dist = self.by_key.get(req.key)
- if dist is None or (dist not in req and replace_conflicting):
- ws = self
- if env is None:
- if dist is None:
- env = Environment(self.entries)
- else:
- # Use an empty environment and workingset to avoid
- # any further conflicts with the conflicting
- # distribution
- env = Environment([])
- ws = WorkingSet([])
- dist = best[req.key] = env.best_match(
- req, ws, installer,
- replace_conflicting=replace_conflicting
- )
- if dist is None:
- requirers = required_by.get(req, None)
- raise DistributionNotFound(req, requirers)
- to_activate.append(dist)
- if dist not in req:
- # Oops, the "best" so far conflicts with a dependency
- dependent_req = required_by[req]
- raise VersionConflict(dist, req).with_context(dependent_req)
+ dist = self._resolve_dist(
+ req, best, replace_conflicting, env, installer, required_by, to_activate
+ )
# push the new requirements onto the stack
new_requirements = dist.requires(req.extras)[::-1]
# return list of distros to activate
return to_activate
- def find_plugins(
- self, plugin_env, full_env=None, installer=None, fallback=True):
+ def _resolve_dist(
+ self, req, best, replace_conflicting, env, installer, required_by, to_activate
+ ):
+ dist = best.get(req.key)
+ if dist is None:
+ # Find the best distribution and add it to the map
+ dist = self.by_key.get(req.key)
+ if dist is None or (dist not in req and replace_conflicting):
+ ws = self
+ if env is None:
+ if dist is None:
+ env = Environment(self.entries)
+ else:
+ # Use an empty environment and workingset to avoid
+ # any further conflicts with the conflicting
+ # distribution
+ env = Environment([])
+ ws = WorkingSet([])
+ dist = best[req.key] = env.best_match(
+ req, ws, installer, replace_conflicting=replace_conflicting
+ )
+ if dist is None:
+ requirers = required_by.get(req, None)
+ raise DistributionNotFound(req, requirers)
+ to_activate.append(dist)
+ if dist not in req:
+ # Oops, the "best" so far conflicts with a dependency
+ dependent_req = required_by[req]
+ raise VersionConflict(dist, req).with_context(dependent_req)
+ return dist
+
+ def find_plugins(self, plugin_env, full_env=None, installer=None, fallback=True):
"""Find all activatable distributions in `plugin_env`
Example usage::
def __getstate__(self):
return (
- self.entries[:], self.entry_keys.copy(), self.by_key.copy(),
- self.normalized_to_canonical_keys.copy(), self.callbacks[:]
+ self.entries[:],
+ self.entry_keys.copy(),
+ self.by_key.copy(),
+ self.normalized_to_canonical_keys.copy(),
+ self.callbacks[:],
)
def __setstate__(self, e_k_b_n_c):
"""Searchable snapshot of distributions on a search path"""
def __init__(
- self, search_path=None, platform=get_supported_platform(),
- python=PY_MAJOR):
+ self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR
+ ):
"""Snapshot distributions available on a search path
Any distributions found on `search_path` are added to the environment.
return self._distmap.get(distribution_key, [])
def add(self, dist):
- """Add `dist` if we ``can_add()`` it and it has not already been added
- """
+ """Add `dist` if we ``can_add()`` it and it has not already been added"""
if self.can_add(dist) and dist.has_version():
dists = self._distmap.setdefault(dist.key, [])
if dist not in dists:
dists.append(dist)
dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)
- def best_match(
- self, req, working_set, installer=None, replace_conflicting=False):
+ def best_match(self, req, working_set, installer=None, replace_conflicting=False):
"""Find distribution best matching `req` and usable on `working_set`
This calls the ``find(req)`` method of the `working_set` to see if a
class ResourceManager:
"""Manage resource extraction and packages"""
+
extraction_path = None
def __init__(self):
def resource_isdir(self, package_or_requirement, resource_name):
"""Is the named resource an existing directory?"""
- return get_provider(package_or_requirement).resource_isdir(
- resource_name
- )
+ return get_provider(package_or_requirement).resource_isdir(resource_name)
def resource_filename(self, package_or_requirement, resource_name):
"""Return a true filesystem path for specified resource"""
def resource_listdir(self, package_or_requirement, resource_name):
"""List the contents of the named resource directory"""
- return get_provider(package_or_requirement).resource_listdir(
- resource_name
- )
+ return get_provider(package_or_requirement).resource_listdir(resource_name)
def extraction_error(self):
"""Give an error message for problems extracting file(s)"""
old_exc = sys.exc_info()[1]
cache_path = self.extraction_path or get_default_cache()
- tmpl = textwrap.dedent("""
+ tmpl = textwrap.dedent(
+ """
Can't extract file(s) to egg cache
The following error occurred while trying to extract file(s)
Perhaps your account does not have write access to this directory?
You can change the cache directory by setting the PYTHON_EGG_CACHE
environment variable to point to an accessible directory.
- """).lstrip()
+ """
+ ).lstrip()
err = ExtractionError(tmpl.format(**locals()))
err.manager = self
err.cache_path = cache_path
``cleanup_resources()``.)
"""
if self.cached_files:
- raise ValueError(
- "Can't change extraction path, files already extracted"
- )
+ raise ValueError("Can't change extraction path, files already extracted")
self.extraction_path = path
or a platform-relevant user cache dir for an app
named "Python-Eggs".
"""
- return (
- os.environ.get('PYTHON_EGG_CACHE')
- or appdirs.user_cache_dir(appname='Python-Eggs')
+ return os.environ.get('PYTHON_EGG_CACHE') or platformdirs.user_cache_dir(
+ appname='Python-Eggs'
)
script = 'scripts/' + script_name
if not self.has_metadata(script):
raise ResolutionError(
- "Script {script!r} not found in metadata at {self.egg_info!r}"
- .format(**locals()),
+ "Script {script!r} not found in metadata at {self.egg_info!r}".format(
+ **locals()
+ ),
)
script_text = self.get_metadata(script).replace('\r\n', '\n')
script_text = script_text.replace('\r', '\n')
exec(code, namespace, namespace)
else:
from linecache import cache
+
cache[script_filename] = (
- len(script_text), 0, script_text.split('\n'), script_filename
+ len(script_text),
+ 0,
+ script_text.split('\n'),
+ script_filename,
)
script_code = compile(script_text, script_filename, 'exec')
exec(script_code, namespace, namespace)
AttributeError: ...
"""
invalid = (
- os.path.pardir in path.split(posixpath.sep) or
- posixpath.isabs(path) or
- ntpath.isabs(path)
+ os.path.pardir in path.split(posixpath.sep)
+ or posixpath.isabs(path)
+ or ntpath.isabs(path)
)
if not invalid:
return
@classmethod
def _register(cls):
- loader_names = 'SourceFileLoader', 'SourcelessFileLoader',
+ loader_names = (
+ 'SourceFileLoader',
+ 'SourcelessFileLoader',
+ )
for name in loader_names:
loader_cls = getattr(importlib_machinery, name, type(None))
register_loader_type(loader_cls, cls)
"""
Memoized zipfile manifests.
"""
+
manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime')
def load(self, path):
if fspath == self.loader.archive:
return ''
if fspath.startswith(self.zip_pre):
- return fspath[len(self.zip_pre):]
- raise AssertionError(
- "%s is not a subpath of %s" % (fspath, self.zip_pre)
- )
+ return fspath[len(self.zip_pre) :]
+ raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre))
def _parts(self, zip_path):
# Convert a zipfile subpath into an egg-relative path part list.
# pseudo-fs path
fspath = self.zip_pre + zip_path
if fspath.startswith(self.egg_root + os.sep):
- return fspath[len(self.egg_root) + 1:].split(os.sep)
- raise AssertionError(
- "%s is not a subpath of %s" % (fspath, self.egg_root)
- )
+ return fspath[len(self.egg_root) + 1 :].split(os.sep)
+ raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root))
@property
def zipinfo(self):
if zip_path in self._index():
for name in self._index()[zip_path]:
- last = self._extract_resource(
- manager, os.path.join(zip_path, name)
- )
+ last = self._extract_resource(manager, os.path.join(zip_path, name))
# return the extracted directory name
return os.path.dirname(last)
timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
if not WRITE_SUPPORT:
- raise IOError('"os.rename" and "os.unlink" are not supported '
- 'on this platform')
+ raise IOError(
+ '"os.rename" and "os.unlink" are not supported ' 'on this platform'
+ )
try:
- real_path = manager.get_cache_path(
- self.egg_name, self._parts(zip_path)
- )
+ real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path))
if self._is_current(real_path, zip_path):
return real_path
register_finder(object, find_nothing)
-def _by_version_descending(names):
- """
- Given a list of filenames, return them in descending order
- by version number.
-
- >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg'
- >>> _by_version_descending(names)
- ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'bar', 'foo']
- >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg'
- >>> _by_version_descending(names)
- ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg']
- >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg'
- >>> _by_version_descending(names)
- ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg']
- """
- def try_parse(name):
- """
- Attempt to parse as a version or return a null version.
- """
- try:
- return packaging.version.Version(name)
- except Exception:
- return packaging.version.Version('0')
-
- def _by_version(name):
- """
- Parse each component of the filename
- """
- name, ext = os.path.splitext(name)
- parts = itertools.chain(name.split('-'), [ext])
- return [try_parse(part) for part in parts]
-
- return sorted(names, key=_by_version, reverse=True)
-
-
def find_on_path(importer, path_item, only=False):
"""Yield distributions accessible on a sys.path directory"""
path_item = _normalize_cached(path_item)
if _is_unpacked_egg(path_item):
yield Distribution.from_filename(
- path_item, metadata=PathMetadata(
- path_item, os.path.join(path_item, 'EGG-INFO')
- )
+ path_item,
+ metadata=PathMetadata(path_item, os.path.join(path_item, 'EGG-INFO')),
)
return
- entries = (
- os.path.join(path_item, child)
- for child in safe_listdir(path_item)
- )
-
- # for performance, before sorting by version,
- # screen entries for only those that will yield
- # distributions
- filtered = (
- entry
- for entry in entries
- if dist_factory(path_item, entry, only)
- )
+ entries = (os.path.join(path_item, child) for child in safe_listdir(path_item))
# scan for .egg and .egg-info in directory
- path_item_entries = _by_version_descending(filtered)
- for entry in path_item_entries:
+ for entry in sorted(entries):
fullpath = os.path.join(path_item, entry)
factory = dist_factory(path_item, entry, only)
for dist in factory(fullpath):
"""Return a dist_factory for the given entry."""
lower = entry.lower()
is_egg_info = lower.endswith('.egg-info')
- is_dist_info = (
- lower.endswith('.dist-info') and
- os.path.isdir(os.path.join(path_item, entry))
+ is_dist_info = lower.endswith('.dist-info') and os.path.isdir(
+ os.path.join(path_item, entry)
)
is_meta = is_egg_info or is_dist_info
return (
distributions_from_metadata
- if is_meta else
- find_distributions
- if not only and _is_egg_path(entry) else
- resolve_egg_link
- if not only and lower.endswith('.egg-link') else
- NoDists()
+ if is_meta
+ else find_distributions
+ if not only and _is_egg_path(entry)
+ else resolve_egg_link
+ if not only and lower.endswith('.egg-link')
+ else NoDists()
)
>>> list(NoDists()('anything'))
[]
"""
+
def __bool__(self):
return False
metadata = FileMetadata(path)
entry = os.path.basename(path)
yield Distribution.from_location(
- root, entry, metadata, precedence=DEVELOP_DIST,
+ root,
+ entry,
+ metadata,
+ precedence=DEVELOP_DIST,
)
"""
referenced_paths = non_empty_lines(path)
resolved_paths = (
- os.path.join(os.path.dirname(path), ref)
- for ref in referenced_paths
+ os.path.join(os.path.dirname(path), ref) for ref in referenced_paths
)
dist_groups = map(find_distributions, resolved_paths)
return next(dist_groups, ())
def normalize_path(filename):
"""Normalize a file/dir name for comparison purposes"""
- return os.path.normcase(os.path.realpath(os.path.normpath(
- _cygwin_patch(filename))))
+ return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename))))
def _cygwin_patch(filename): # pragma: nocover
def _is_zip_egg(path):
return (
- path.lower().endswith('.egg') and
- os.path.isfile(path) and
- zipfile.is_zipfile(path)
+ path.lower().endswith('.egg')
+ and os.path.isfile(path)
+ and zipfile.is_zipfile(path)
)
"""
Determine if given path appears to be an unpacked egg.
"""
- return (
- path.lower().endswith('.egg') and
- os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO'))
+ return path.lower().endswith('.egg') and os.path.isfile(
+ os.path.join(path, 'EGG-INFO', 'PKG-INFO')
)
Given an iterable of lines from a Metadata file, return
the value of the Version field, if present, or None otherwise.
"""
+
def is_version_line(line):
return line.lower().startswith('version:')
+
version_lines = filter(is_version_line, lines)
line = next(iter(version_lines), '')
_, _, value = line.partition(':')
class Distribution:
"""Wrap an actual or potential sys.path entry w/metadata"""
+
PKG_INFO = 'PKG-INFO'
def __init__(
- self, location=None, metadata=None, project_name=None,
- version=None, py_version=PY_MAJOR, platform=None,
- precedence=EGG_DIST):
+ self,
+ location=None,
+ metadata=None,
+ project_name=None,
+ version=None,
+ py_version=PY_MAJOR,
+ platform=None,
+ precedence=EGG_DIST,
+ ):
self.project_name = safe_name(project_name or 'Unknown')
if version is not None:
self._version = safe_version(version)
'name', 'ver', 'pyver', 'plat'
)
return cls(
- location, metadata, project_name=project_name, version=version,
- py_version=py_version, platform=platform, **kw
+ location,
+ metadata,
+ project_name=project_name,
+ version=version,
+ py_version=py_version,
+ platform=platform,
+ **kw,
)._reload_version()
def _reload_version(self):
return self._parsed_version
- def _warn_legacy_version(self):
- LV = packaging.version.LegacyVersion
- is_legacy = isinstance(self._parsed_version, LV)
- if not is_legacy:
- return
-
- # While an empty version is technically a legacy version and
- # is not a valid PEP 440 version, it's also unlikely to
- # actually come from someone and instead it is more likely that
- # it comes from setuptools attempting to parse a filename and
- # including it in the list. So for that we'll gate this warning
- # on if the version is anything at all or not.
- if not self.version:
- return
-
- tmpl = textwrap.dedent("""
- '{project_name} ({version})' is being parsed as a legacy,
- non PEP 440,
- version. You may find odd behavior and sort order.
- In particular it will be sorted as less than 0.0. It
- is recommended to migrate to PEP 440 compatible
- versions.
- """).strip().replace('\n', ' ')
-
- warnings.warn(tmpl.format(**vars(self)), PEP440Warning)
-
@property
def version(self):
try:
version = self._get_version()
if version is None:
path = self._get_metadata_path_for_display(self.PKG_INFO)
- msg = (
- "Missing 'Version:' header and/or {} file at path: {}"
- ).format(self.PKG_INFO, path)
+ msg = ("Missing 'Version:' header and/or {} file at path: {}").format(
+ self.PKG_INFO, path
+ )
raise ValueError(msg, self) from e
return version
reqs = dm.pop(extra)
new_extra, _, marker = extra.partition(':')
fails_marker = marker and (
- invalid_marker(marker)
- or not evaluate_marker(marker)
+ invalid_marker(marker) or not evaluate_marker(marker)
)
if fails_marker:
reqs = []
def egg_name(self):
"""Return what this distribution's standard .egg filename should be"""
filename = "%s-%s-py%s" % (
- to_filename(self.project_name), to_filename(self.version),
- self.py_version or PY_MAJOR
+ to_filename(self.project_name),
+ to_filename(self.version),
+ self.py_version or PY_MAJOR,
)
if self.platform:
def __dir__(self):
return list(
set(super(Distribution, self).__dir__())
- | set(
- attr for attr in self._provider.__dir__()
- if not attr.startswith('_')
- )
+ | set(attr for attr in self._provider.__dir__() if not attr.startswith('_'))
)
@classmethod
def from_filename(cls, filename, metadata=None, **kw):
return cls.from_location(
- _normalize_cached(filename), os.path.basename(filename), metadata,
- **kw
+ _normalize_cached(filename), os.path.basename(filename), metadata, **kw
)
def as_requirement(self):
nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
loc = normalize_path(self.location)
for modname in self._get_metadata('top_level.txt'):
- if (modname not in sys.modules or modname in nsp
- or modname in _namespace_packages):
+ if (
+ modname not in sys.modules
+ or modname in nsp
+ or modname in _namespace_packages
+ ):
continue
if modname in ('pkg_resources', 'setuptools', 'site'):
continue
fn = getattr(sys.modules[modname], '__file__', None)
- if fn and (normalize_path(fn).startswith(loc) or
- fn.startswith(self.location)):
+ if fn and (
+ normalize_path(fn).startswith(loc) or fn.startswith(self.location)
+ ):
continue
issue_warning(
"Module %s was already imported from %s, but %s is being added"
Wrap an actual or potential sys.path entry
w/metadata, .dist-info style.
"""
+
PKG_INFO = 'METADATA'
EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])")
self.unsafe_name = self.name
project_name = safe_name(self.name)
self.project_name, self.key = project_name, project_name.lower()
- self.specs = [
- (spec.operator, spec.version) for spec in self.specifier]
+ self.specs = [(spec.operator, spec.version) for spec in self.specifier]
self.extras = tuple(map(safe_extra, self.extras))
self.hashCmp = (
self.key,
self.__hash = hash(self.hashCmp)
def __eq__(self, other):
- return (
- isinstance(other, Requirement) and
- self.hashCmp == other.hashCmp
- )
+ return isinstance(other, Requirement) and self.hashCmp == other.hashCmp
def __ne__(self, other):
return not self == other
@staticmethod
def parse(s):
- req, = parse_requirements(s)
+ (req,) = parse_requirements(s)
return req
# ensure that all distributions added to the working set in the future
# (e.g. by calling ``require()``) will get activated as well,
# with higher priority (replace=True).
- tuple(
- dist.activate(replace=False)
- for dist in working_set
- )
+ tuple(dist.activate(replace=False) for dist in working_set)
add_activation_listener(
lambda dist: dist.activate(replace=True),
existing=False,
+++ /dev/null
-
-.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png
- :target: http://travis-ci.org/ActiveState/appdirs
-
-the problem
-===========
-
-What directory should your app use for storing user data? If running on Mac OS X, you
-should use::
-
- ~/Library/Application Support/<AppName>
-
-If on Windows (at least English Win XP) that should be::
-
- C:\Documents and Settings\<User>\Application Data\Local Settings\<AppAuthor>\<AppName>
-
-or possibly::
-
- C:\Documents and Settings\<User>\Application Data\<AppAuthor>\<AppName>
-
-for `roaming profiles <http://bit.ly/9yl3b6>`_ but that is another story.
-
-On Linux (and other Unices) the dir, according to the `XDG
-spec <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_, is::
-
- ~/.local/share/<AppName>
-
-
-``appdirs`` to the rescue
-=========================
-
-This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will
-help you choose an appropriate:
-
-- user data dir (``user_data_dir``)
-- user config dir (``user_config_dir``)
-- user cache dir (``user_cache_dir``)
-- site data dir (``site_data_dir``)
-- site config dir (``site_config_dir``)
-- user log dir (``user_log_dir``)
-
-and also:
-
-- is a single module so other Python packages can include their own private copy
-- is slightly opinionated on the directory names used. Look for "OPINION" in
- documentation and code for when an opinion is being applied.
-
-
-some example output
-===================
-
-On Mac OS X::
-
- >>> from appdirs import *
- >>> appname = "SuperApp"
- >>> appauthor = "Acme"
- >>> user_data_dir(appname, appauthor)
- '/Users/trentm/Library/Application Support/SuperApp'
- >>> site_data_dir(appname, appauthor)
- '/Library/Application Support/SuperApp'
- >>> user_cache_dir(appname, appauthor)
- '/Users/trentm/Library/Caches/SuperApp'
- >>> user_log_dir(appname, appauthor)
- '/Users/trentm/Library/Logs/SuperApp'
-
-On Windows 7::
-
- >>> from appdirs import *
- >>> appname = "SuperApp"
- >>> appauthor = "Acme"
- >>> user_data_dir(appname, appauthor)
- 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
- >>> user_data_dir(appname, appauthor, roaming=True)
- 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
- >>> user_cache_dir(appname, appauthor)
- 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
- >>> user_log_dir(appname, appauthor)
- 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
-
-On Linux::
-
- >>> from appdirs import *
- >>> appname = "SuperApp"
- >>> appauthor = "Acme"
- >>> user_data_dir(appname, appauthor)
- '/home/trentm/.local/share/SuperApp
- >>> site_data_dir(appname, appauthor)
- '/usr/local/share/SuperApp'
- >>> site_data_dir(appname, appauthor, multipath=True)
- '/usr/local/share/SuperApp:/usr/share/SuperApp'
- >>> user_cache_dir(appname, appauthor)
- '/home/trentm/.cache/SuperApp'
- >>> user_log_dir(appname, appauthor)
- '/home/trentm/.cache/SuperApp/log'
- >>> user_config_dir(appname)
- '/home/trentm/.config/SuperApp'
- >>> site_config_dir(appname)
- '/etc/xdg/SuperApp'
- >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc'
- >>> site_config_dir(appname, multipath=True)
- '/etc/SuperApp:/usr/local/etc/SuperApp'
-
-
-``AppDirs`` for convenience
-===========================
-
-::
-
- >>> from appdirs import AppDirs
- >>> dirs = AppDirs("SuperApp", "Acme")
- >>> dirs.user_data_dir
- '/Users/trentm/Library/Application Support/SuperApp'
- >>> dirs.site_data_dir
- '/Library/Application Support/SuperApp'
- >>> dirs.user_cache_dir
- '/Users/trentm/Library/Caches/SuperApp'
- >>> dirs.user_log_dir
- '/Users/trentm/Library/Logs/SuperApp'
-
-
-
-Per-version isolation
-=====================
-
-If you have multiple versions of your app in use that you want to be
-able to run side-by-side, then you may want version-isolation for these
-dirs::
-
- >>> from appdirs import AppDirs
- >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
- >>> dirs.user_data_dir
- '/Users/trentm/Library/Application Support/SuperApp/1.0'
- >>> dirs.site_data_dir
- '/Library/Application Support/SuperApp/1.0'
- >>> dirs.user_cache_dir
- '/Users/trentm/Library/Caches/SuperApp/1.0'
- >>> dirs.user_log_dir
- '/Users/trentm/Library/Logs/SuperApp/1.0'
-
-
-
-appdirs Changelog
-=================
-
-appdirs 1.4.3
--------------
-- [PR #76] Python 3.6 invalid escape sequence deprecation fixes
-- Fix for Python 3.6 support
-
-appdirs 1.4.2
--------------
-- [PR #84] Allow installing without setuptools
-- [PR #86] Fix string delimiters in setup.py description
-- Add Python 3.6 support
-
-appdirs 1.4.1
--------------
-- [issue #38] Fix _winreg import on Windows Py3
-- [issue #55] Make appname optional
-
-appdirs 1.4.0
--------------
-- [PR #42] AppAuthor is now optional on Windows
-- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows
- support requires `JNA <https://github.com/twall/jna>`_.
-- [PR #44] Fix incorrect behaviour of the site_config_dir method
-
-appdirs 1.3.0
--------------
-- [Unix, issue 16] Conform to XDG standard, instead of breaking it for
- everybody
-- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are
- usually case sensitive, so mangling is not wise
-- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result
- based on XDG_DATA_DIRS and make room for respecting the standard which
- specifies XDG_DATA_DIRS is a multiple-value variable
-- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to
- XDG specs; on Windows and Mac return the corresponding ``*_data_dir``
-
-appdirs 1.2.0
--------------
-
-- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more
- typical.
-- [issue 9] Make ``unicode`` work on py3k.
-
-appdirs 1.1.0
--------------
-
-- [issue 4] Add ``AppDirs.user_log_dir``.
-- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec
- <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
-- [Mac, issue 5] Fix ``site_data_dir()`` on Mac.
-- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports
- Python3 now.
-- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use
- ``opinion=False`` option to disable this.
-- Add ``appdirs.AppDirs`` convenience class. Usage:
-
- >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
- >>> dirs.user_data_dir
- '/Users/trentm/Library/Application Support/SuperApp/1.0'
-
-- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short
- paths if there are high bit chars.
-- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g.
- "~/.superapp/cache".
-- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only)
- and change the default ``user_data_dir`` behaviour to use a *non*-roaming
- profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because
- a large roaming profile can cause login speed issues. The "only syncs on
- logout" behaviour can cause surprises in appdata info.
-
-
-appdirs 1.0.1 (never released)
-------------------------------
-
-Started this changelog 27 July 2010. Before that this module originated in the
-`Komodo <http://www.activestate.com/komodo>`_ product as ``applib.py`` and then
-as `applib/location.py
-<http://github.com/ActiveState/applib/blob/master/applib/location.py>`_ (used by
-`PyPM <http://code.activestate.com/pypm/>`_ in `ActivePython
-<http://www.activestate.com/activepython>`_). This is basically a fork of
-applib.py 1.0.1 and applib/location.py 1.0.1.
-
-
-
+++ /dev/null
-Metadata-Version: 2.0
-Name: appdirs
-Version: 1.4.3
-Summary: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir".
-Home-page: http://github.com/ActiveState/appdirs
-Author: Trent Mick; Sridhar Ratnakumar; Jeff Rouse
-Author-email: trentm@gmail.com; github@srid.name; jr@its.to
-License: MIT
-Keywords: application directory log cache user
-Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-
-
-.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png
- :target: http://travis-ci.org/ActiveState/appdirs
-
-the problem
-===========
-
-What directory should your app use for storing user data? If running on Mac OS X, you
-should use::
-
- ~/Library/Application Support/<AppName>
-
-If on Windows (at least English Win XP) that should be::
-
- C:\Documents and Settings\<User>\Application Data\Local Settings\<AppAuthor>\<AppName>
-
-or possibly::
-
- C:\Documents and Settings\<User>\Application Data\<AppAuthor>\<AppName>
-
-for `roaming profiles <http://bit.ly/9yl3b6>`_ but that is another story.
-
-On Linux (and other Unices) the dir, according to the `XDG
-spec <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_, is::
-
- ~/.local/share/<AppName>
-
-
-``appdirs`` to the rescue
-=========================
-
-This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will
-help you choose an appropriate:
-
-- user data dir (``user_data_dir``)
-- user config dir (``user_config_dir``)
-- user cache dir (``user_cache_dir``)
-- site data dir (``site_data_dir``)
-- site config dir (``site_config_dir``)
-- user log dir (``user_log_dir``)
-
-and also:
-
-- is a single module so other Python packages can include their own private copy
-- is slightly opinionated on the directory names used. Look for "OPINION" in
- documentation and code for when an opinion is being applied.
-
-
-some example output
-===================
-
-On Mac OS X::
-
- >>> from appdirs import *
- >>> appname = "SuperApp"
- >>> appauthor = "Acme"
- >>> user_data_dir(appname, appauthor)
- '/Users/trentm/Library/Application Support/SuperApp'
- >>> site_data_dir(appname, appauthor)
- '/Library/Application Support/SuperApp'
- >>> user_cache_dir(appname, appauthor)
- '/Users/trentm/Library/Caches/SuperApp'
- >>> user_log_dir(appname, appauthor)
- '/Users/trentm/Library/Logs/SuperApp'
-
-On Windows 7::
-
- >>> from appdirs import *
- >>> appname = "SuperApp"
- >>> appauthor = "Acme"
- >>> user_data_dir(appname, appauthor)
- 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
- >>> user_data_dir(appname, appauthor, roaming=True)
- 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
- >>> user_cache_dir(appname, appauthor)
- 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
- >>> user_log_dir(appname, appauthor)
- 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
-
-On Linux::
-
- >>> from appdirs import *
- >>> appname = "SuperApp"
- >>> appauthor = "Acme"
- >>> user_data_dir(appname, appauthor)
- '/home/trentm/.local/share/SuperApp
- >>> site_data_dir(appname, appauthor)
- '/usr/local/share/SuperApp'
- >>> site_data_dir(appname, appauthor, multipath=True)
- '/usr/local/share/SuperApp:/usr/share/SuperApp'
- >>> user_cache_dir(appname, appauthor)
- '/home/trentm/.cache/SuperApp'
- >>> user_log_dir(appname, appauthor)
- '/home/trentm/.cache/SuperApp/log'
- >>> user_config_dir(appname)
- '/home/trentm/.config/SuperApp'
- >>> site_config_dir(appname)
- '/etc/xdg/SuperApp'
- >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc'
- >>> site_config_dir(appname, multipath=True)
- '/etc/SuperApp:/usr/local/etc/SuperApp'
-
-
-``AppDirs`` for convenience
-===========================
-
-::
-
- >>> from appdirs import AppDirs
- >>> dirs = AppDirs("SuperApp", "Acme")
- >>> dirs.user_data_dir
- '/Users/trentm/Library/Application Support/SuperApp'
- >>> dirs.site_data_dir
- '/Library/Application Support/SuperApp'
- >>> dirs.user_cache_dir
- '/Users/trentm/Library/Caches/SuperApp'
- >>> dirs.user_log_dir
- '/Users/trentm/Library/Logs/SuperApp'
-
-
-
-Per-version isolation
-=====================
-
-If you have multiple versions of your app in use that you want to be
-able to run side-by-side, then you may want version-isolation for these
-dirs::
-
- >>> from appdirs import AppDirs
- >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
- >>> dirs.user_data_dir
- '/Users/trentm/Library/Application Support/SuperApp/1.0'
- >>> dirs.site_data_dir
- '/Library/Application Support/SuperApp/1.0'
- >>> dirs.user_cache_dir
- '/Users/trentm/Library/Caches/SuperApp/1.0'
- >>> dirs.user_log_dir
- '/Users/trentm/Library/Logs/SuperApp/1.0'
-
-
-
-appdirs Changelog
-=================
-
-appdirs 1.4.3
--------------
-- [PR #76] Python 3.6 invalid escape sequence deprecation fixes
-- Fix for Python 3.6 support
-
-appdirs 1.4.2
--------------
-- [PR #84] Allow installing without setuptools
-- [PR #86] Fix string delimiters in setup.py description
-- Add Python 3.6 support
-
-appdirs 1.4.1
--------------
-- [issue #38] Fix _winreg import on Windows Py3
-- [issue #55] Make appname optional
-
-appdirs 1.4.0
--------------
-- [PR #42] AppAuthor is now optional on Windows
-- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows
- support requires `JNA <https://github.com/twall/jna>`_.
-- [PR #44] Fix incorrect behaviour of the site_config_dir method
-
-appdirs 1.3.0
--------------
-- [Unix, issue 16] Conform to XDG standard, instead of breaking it for
- everybody
-- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are
- usually case sensitive, so mangling is not wise
-- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result
- based on XDG_DATA_DIRS and make room for respecting the standard which
- specifies XDG_DATA_DIRS is a multiple-value variable
-- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to
- XDG specs; on Windows and Mac return the corresponding ``*_data_dir``
-
-appdirs 1.2.0
--------------
-
-- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more
- typical.
-- [issue 9] Make ``unicode`` work on py3k.
-
-appdirs 1.1.0
--------------
-
-- [issue 4] Add ``AppDirs.user_log_dir``.
-- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec
- <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
-- [Mac, issue 5] Fix ``site_data_dir()`` on Mac.
-- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports
- Python3 now.
-- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use
- ``opinion=False`` option to disable this.
-- Add ``appdirs.AppDirs`` convenience class. Usage:
-
- >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
- >>> dirs.user_data_dir
- '/Users/trentm/Library/Application Support/SuperApp/1.0'
-
-- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short
- paths if there are high bit chars.
-- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g.
- "~/.superapp/cache".
-- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only)
- and change the default ``user_data_dir`` behaviour to use a *non*-roaming
- profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because
- a large roaming profile can cause login speed issues. The "only syncs on
- logout" behaviour can cause surprises in appdata info.
-
-
-appdirs 1.0.1 (never released)
-------------------------------
-
-Started this changelog 27 July 2010. Before that this module originated in the
-`Komodo <http://www.activestate.com/komodo>`_ product as ``applib.py`` and then
-as `applib/location.py
-<http://github.com/ActiveState/applib/blob/master/applib/location.py>`_ (used by
-`PyPM <http://code.activestate.com/pypm/>`_ in `ActivePython
-<http://www.activestate.com/activepython>`_). This is basically a fork of
-applib.py 1.0.1 and applib/location.py 1.0.1.
-
-
-
+++ /dev/null
-__pycache__/appdirs.cpython-310.pyc,,\r
-appdirs-1.4.3.dist-info/DESCRIPTION.rst,sha256=77Fe8OIOLSjDSNdLiL5xywMKO-AGE42rdXkqKo4Ee-k,7531\r
-appdirs-1.4.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-appdirs-1.4.3.dist-info/METADATA,sha256=3IFw6jTfImdOqsCb2GYvVR157tL7KEzfRAszn382csk,8773\r
-appdirs-1.4.3.dist-info/RECORD,,\r
-appdirs-1.4.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-appdirs-1.4.3.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110\r
-appdirs-1.4.3.dist-info/metadata.json,sha256=fL_Q-GuFJu3PJxMrwU7SdsI8RGqjIfi2AvouCSF5DSA,1359\r
-appdirs-1.4.3.dist-info/top_level.txt,sha256=nKncE8CUqZERJ6VuQWL4_bkunSPDNfn7KZqb4Tr5YEM,8\r
-appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.29.0)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
+++ /dev/null
-{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "trentm@gmail.com; github@srid.name; jr@its.to", "name": "Trent Mick; Sridhar Ratnakumar; Jeff Rouse", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/ActiveState/appdirs"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["application", "directory", "log", "cache", "user"], "license": "MIT", "metadata_version": "2.0", "name": "appdirs", "summary": "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\".", "test_requires": [{"requires": []}], "version": "1.4.3"}
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (c) 2005-2010 ActiveState Software Inc.
-# Copyright (c) 2013 Eddy Petrișor
-
-"""Utilities for determining application-specific dirs.
-
-See <http://github.com/ActiveState/appdirs> for details and usage.
-"""
-# Dev Notes:
-# - MSDN on where to store app data files:
-# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
-# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
-# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
-
-__version_info__ = (1, 4, 3)
-__version__ = '.'.join(map(str, __version_info__))
-
-
-import sys
-import os
-
-PY3 = sys.version_info[0] == 3
-
-if PY3:
- unicode = str
-
-if sys.platform.startswith('java'):
- import platform
- os_name = platform.java_ver()[3][0]
- if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
- system = 'win32'
- elif os_name.startswith('Mac'): # "Mac OS X", etc.
- system = 'darwin'
- else: # "Linux", "SunOS", "FreeBSD", etc.
- # Setting this to "linux2" is not ideal, but only Windows or Mac
- # are actually checked for and the rest of the module expects
- # *sys.platform* style strings.
- system = 'linux2'
-else:
- system = sys.platform
-
-
-
-def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
- r"""Return full path to the user-specific data dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "roaming" (boolean, default False) can be set True to use the Windows
- roaming appdata directory. That means that for users on a Windows
- network setup for roaming profiles, this user data will be
- sync'd on login. See
- <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
- for a discussion of issues.
-
- Typical user data directories are:
- Mac OS X: ~/Library/Application Support/<AppName>
- Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
- Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
- Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
- Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
- Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
-
- For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
- That means, by default "~/.local/share/<AppName>".
- """
- if system == "win32":
- if appauthor is None:
- appauthor = appname
- const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
- path = os.path.normpath(_get_win_folder(const))
- if appname:
- if appauthor is not False:
- path = os.path.join(path, appauthor, appname)
- else:
- path = os.path.join(path, appname)
- elif system == 'darwin':
- path = os.path.expanduser('~/Library/Application Support/')
- if appname:
- path = os.path.join(path, appname)
- else:
- path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
- if appname:
- path = os.path.join(path, appname)
- if appname and version:
- path = os.path.join(path, version)
- return path
-
-
-def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
- r"""Return full path to the user-shared data dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "multipath" is an optional parameter only applicable to *nix
- which indicates that the entire list of data dirs should be
- returned. By default, the first item from XDG_DATA_DIRS is
- returned, or '/usr/local/share/<AppName>',
- if XDG_DATA_DIRS is not set
-
- Typical site data directories are:
- Mac OS X: /Library/Application Support/<AppName>
- Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
- Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
- Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
- Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
-
- For Unix, this is using the $XDG_DATA_DIRS[0] default.
-
- WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
- """
- if system == "win32":
- if appauthor is None:
- appauthor = appname
- path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
- if appname:
- if appauthor is not False:
- path = os.path.join(path, appauthor, appname)
- else:
- path = os.path.join(path, appname)
- elif system == 'darwin':
- path = os.path.expanduser('/Library/Application Support')
- if appname:
- path = os.path.join(path, appname)
- else:
- # XDG default for $XDG_DATA_DIRS
- # only first, if multipath is False
- path = os.getenv('XDG_DATA_DIRS',
- os.pathsep.join(['/usr/local/share', '/usr/share']))
- pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
- if appname:
- if version:
- appname = os.path.join(appname, version)
- pathlist = [os.sep.join([x, appname]) for x in pathlist]
-
- if multipath:
- path = os.pathsep.join(pathlist)
- else:
- path = pathlist[0]
- return path
-
- if appname and version:
- path = os.path.join(path, version)
- return path
-
-
-def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
- r"""Return full path to the user-specific config dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "roaming" (boolean, default False) can be set True to use the Windows
- roaming appdata directory. That means that for users on a Windows
- network setup for roaming profiles, this user data will be
- sync'd on login. See
- <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
- for a discussion of issues.
-
- Typical user config directories are:
- Mac OS X: same as user_data_dir
- Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
- Win *: same as user_data_dir
-
- For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
- That means, by default "~/.config/<AppName>".
- """
- if system in ["win32", "darwin"]:
- path = user_data_dir(appname, appauthor, None, roaming)
- else:
- path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
- if appname:
- path = os.path.join(path, appname)
- if appname and version:
- path = os.path.join(path, version)
- return path
-
-
-def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
- r"""Return full path to the user-shared data dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "multipath" is an optional parameter only applicable to *nix
- which indicates that the entire list of config dirs should be
- returned. By default, the first item from XDG_CONFIG_DIRS is
- returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
-
- Typical site config directories are:
- Mac OS X: same as site_data_dir
- Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
- $XDG_CONFIG_DIRS
- Win *: same as site_data_dir
- Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
-
- For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
-
- WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
- """
- if system in ["win32", "darwin"]:
- path = site_data_dir(appname, appauthor)
- if appname and version:
- path = os.path.join(path, version)
- else:
- # XDG default for $XDG_CONFIG_DIRS
- # only first, if multipath is False
- path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
- pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
- if appname:
- if version:
- appname = os.path.join(appname, version)
- pathlist = [os.sep.join([x, appname]) for x in pathlist]
-
- if multipath:
- path = os.pathsep.join(pathlist)
- else:
- path = pathlist[0]
- return path
-
-
-def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
- r"""Return full path to the user-specific cache dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "opinion" (boolean) can be False to disable the appending of
- "Cache" to the base app data dir for Windows. See
- discussion below.
-
- Typical user cache directories are:
- Mac OS X: ~/Library/Caches/<AppName>
- Unix: ~/.cache/<AppName> (XDG default)
- Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
- Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
-
- On Windows the only suggestion in the MSDN docs is that local settings go in
- the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
- app data dir (the default returned by `user_data_dir` above). Apps typically
- put cache data somewhere *under* the given dir here. Some examples:
- ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
- ...\Acme\SuperApp\Cache\1.0
- OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
- This can be disabled with the `opinion=False` option.
- """
- if system == "win32":
- if appauthor is None:
- appauthor = appname
- path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
- if appname:
- if appauthor is not False:
- path = os.path.join(path, appauthor, appname)
- else:
- path = os.path.join(path, appname)
- if opinion:
- path = os.path.join(path, "Cache")
- elif system == 'darwin':
- path = os.path.expanduser('~/Library/Caches')
- if appname:
- path = os.path.join(path, appname)
- else:
- path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
- if appname:
- path = os.path.join(path, appname)
- if appname and version:
- path = os.path.join(path, version)
- return path
-
-
-def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
- r"""Return full path to the user-specific state dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "roaming" (boolean, default False) can be set True to use the Windows
- roaming appdata directory. That means that for users on a Windows
- network setup for roaming profiles, this user data will be
- sync'd on login. See
- <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
- for a discussion of issues.
-
- Typical user state directories are:
- Mac OS X: same as user_data_dir
- Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
- Win *: same as user_data_dir
-
- For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
- to extend the XDG spec and support $XDG_STATE_HOME.
-
- That means, by default "~/.local/state/<AppName>".
- """
- if system in ["win32", "darwin"]:
- path = user_data_dir(appname, appauthor, None, roaming)
- else:
- path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
- if appname:
- path = os.path.join(path, appname)
- if appname and version:
- path = os.path.join(path, version)
- return path
-
-
-def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
- r"""Return full path to the user-specific log dir for this application.
-
- "appname" is the name of application.
- If None, just the system directory is returned.
- "appauthor" (only used on Windows) is the name of the
- appauthor or distributing body for this application. Typically
- it is the owning company name. This falls back to appname. You may
- pass False to disable it.
- "version" is an optional version path element to append to the
- path. You might want to use this if you want multiple versions
- of your app to be able to run independently. If used, this
- would typically be "<major>.<minor>".
- Only applied when appname is present.
- "opinion" (boolean) can be False to disable the appending of
- "Logs" to the base app data dir for Windows, and "log" to the
- base cache dir for Unix. See discussion below.
-
- Typical user log directories are:
- Mac OS X: ~/Library/Logs/<AppName>
- Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
- Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
- Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
-
- On Windows the only suggestion in the MSDN docs is that local settings
- go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
- examples of what some windows apps use for a logs dir.)
-
- OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
- value for Windows and appends "log" to the user cache dir for Unix.
- This can be disabled with the `opinion=False` option.
- """
- if system == "darwin":
- path = os.path.join(
- os.path.expanduser('~/Library/Logs'),
- appname)
- elif system == "win32":
- path = user_data_dir(appname, appauthor, version)
- version = False
- if opinion:
- path = os.path.join(path, "Logs")
- else:
- path = user_cache_dir(appname, appauthor, version)
- version = False
- if opinion:
- path = os.path.join(path, "log")
- if appname and version:
- path = os.path.join(path, version)
- return path
-
-
-class AppDirs(object):
- """Convenience wrapper for getting application dirs."""
- def __init__(self, appname=None, appauthor=None, version=None,
- roaming=False, multipath=False):
- self.appname = appname
- self.appauthor = appauthor
- self.version = version
- self.roaming = roaming
- self.multipath = multipath
-
- @property
- def user_data_dir(self):
- return user_data_dir(self.appname, self.appauthor,
- version=self.version, roaming=self.roaming)
-
- @property
- def site_data_dir(self):
- return site_data_dir(self.appname, self.appauthor,
- version=self.version, multipath=self.multipath)
-
- @property
- def user_config_dir(self):
- return user_config_dir(self.appname, self.appauthor,
- version=self.version, roaming=self.roaming)
-
- @property
- def site_config_dir(self):
- return site_config_dir(self.appname, self.appauthor,
- version=self.version, multipath=self.multipath)
-
- @property
- def user_cache_dir(self):
- return user_cache_dir(self.appname, self.appauthor,
- version=self.version)
-
- @property
- def user_state_dir(self):
- return user_state_dir(self.appname, self.appauthor,
- version=self.version)
-
- @property
- def user_log_dir(self):
- return user_log_dir(self.appname, self.appauthor,
- version=self.version)
-
-
-#---- internal support stuff
-
-def _get_win_folder_from_registry(csidl_name):
- """This is a fallback technique at best. I'm not sure if using the
- registry for this guarantees us the correct answer for all CSIDL_*
- names.
- """
- if PY3:
- import winreg as _winreg
- else:
- import _winreg
-
- shell_folder_name = {
- "CSIDL_APPDATA": "AppData",
- "CSIDL_COMMON_APPDATA": "Common AppData",
- "CSIDL_LOCAL_APPDATA": "Local AppData",
- }[csidl_name]
-
- key = _winreg.OpenKey(
- _winreg.HKEY_CURRENT_USER,
- r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
- )
- dir, type = _winreg.QueryValueEx(key, shell_folder_name)
- return dir
-
-
-def _get_win_folder_with_pywin32(csidl_name):
- from win32com.shell import shellcon, shell
- dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
- # Try to make this a unicode path because SHGetFolderPath does
- # not return unicode strings when there is unicode data in the
- # path.
- try:
- dir = unicode(dir)
-
- # Downgrade to short path name if have highbit chars. See
- # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
- has_high_char = False
- for c in dir:
- if ord(c) > 255:
- has_high_char = True
- break
- if has_high_char:
- try:
- import win32api
- dir = win32api.GetShortPathName(dir)
- except ImportError:
- pass
- except UnicodeError:
- pass
- return dir
-
-
-def _get_win_folder_with_ctypes(csidl_name):
- import ctypes
-
- csidl_const = {
- "CSIDL_APPDATA": 26,
- "CSIDL_COMMON_APPDATA": 35,
- "CSIDL_LOCAL_APPDATA": 28,
- }[csidl_name]
-
- buf = ctypes.create_unicode_buffer(1024)
- ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
-
- # Downgrade to short path name if have highbit chars. See
- # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
- has_high_char = False
- for c in buf:
- if ord(c) > 255:
- has_high_char = True
- break
- if has_high_char:
- buf2 = ctypes.create_unicode_buffer(1024)
- if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
- buf = buf2
-
- return buf.value
-
-def _get_win_folder_with_jna(csidl_name):
- import array
- from com.sun import jna
- from com.sun.jna.platform import win32
-
- buf_size = win32.WinDef.MAX_PATH * 2
- buf = array.zeros('c', buf_size)
- shell = win32.Shell32.INSTANCE
- shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
- dir = jna.Native.toString(buf.tostring()).rstrip("\0")
-
- # Downgrade to short path name if have highbit chars. See
- # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
- has_high_char = False
- for c in dir:
- if ord(c) > 255:
- has_high_char = True
- break
- if has_high_char:
- buf = array.zeros('c', buf_size)
- kernel = win32.Kernel32.INSTANCE
- if kernel.GetShortPathName(dir, buf, buf_size):
- dir = jna.Native.toString(buf.tostring()).rstrip("\0")
-
- return dir
-
-if system == "win32":
- try:
- import win32com.shell
- _get_win_folder = _get_win_folder_with_pywin32
- except ImportError:
- try:
- from ctypes import windll
- _get_win_folder = _get_win_folder_with_ctypes
- except ImportError:
- try:
- import com.sun.jna
- _get_win_folder = _get_win_folder_with_jna
- except ImportError:
- _get_win_folder = _get_win_folder_from_registry
-
-
-#---- self test code
-
-if __name__ == "__main__":
- appname = "MyApp"
- appauthor = "MyCompany"
-
- props = ("user_data_dir",
- "user_config_dir",
- "user_cache_dir",
- "user_state_dir",
- "user_log_dir",
- "site_data_dir",
- "site_config_dir")
-
- print("-- app dirs %s --" % __version__)
-
- print("-- app dirs (with optional 'version')")
- dirs = AppDirs(appname, appauthor, version="1.0")
- for prop in props:
- print("%s: %s" % (prop, getattr(dirs, prop)))
-
- print("\n-- app dirs (without optional 'version')")
- dirs = AppDirs(appname, appauthor)
- for prop in props:
- print("%s: %s" % (prop, getattr(dirs, prop)))
-
- print("\n-- app dirs (without optional 'appauthor')")
- dirs = AppDirs(appname)
- for prop in props:
- print("%s: %s" % (prop, getattr(dirs, prop)))
-
- print("\n-- app dirs (with disabled 'appauthor')")
- dirs = AppDirs(appname, appauthor=False)
- for prop in props:
- print("%s: %s" % (prop, getattr(dirs, prop)))
importlib_resources-5.4.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
importlib_resources-5.4.0.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20\r
importlib_resources/__init__.py,sha256=zuA0lbRgtVVCcAztM0z5LuBiOCV9L_3qtI6mW2p5xAg,525\r
-importlib_resources/__pycache__/__init__.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_adapters.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_common.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_compat.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_itertools.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_legacy.cpython-310.pyc,,\r
-importlib_resources/__pycache__/abc.cpython-310.pyc,,\r
-importlib_resources/__pycache__/readers.cpython-310.pyc,,\r
-importlib_resources/__pycache__/simple.cpython-310.pyc,,\r
+importlib_resources/__pycache__/__init__.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_adapters.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_common.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_compat.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_itertools.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_legacy.cpython-311.pyc,,\r
+importlib_resources/__pycache__/abc.cpython-311.pyc,,\r
+importlib_resources/__pycache__/readers.cpython-311.pyc,,\r
+importlib_resources/__pycache__/simple.cpython-311.pyc,,\r
importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504\r
importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741\r
importlib_resources/_compat.py,sha256=3LpkIfeN9x4oXjRea5TxZP5VYhPlzuVRhGe-hEv-S0s,2704\r
importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566\r
importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836\r
importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/__pycache__/__init__.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/_compat.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_contents.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_files.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_open.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_path.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_read.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_reader.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_resource.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/update-zips.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/util.cpython-310.pyc,,\r
+importlib_resources/tests/__pycache__/__init__.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/_compat.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_contents.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_files.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_open.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_path.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_read.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_reader.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_resource.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/update-zips.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/util.cpython-311.pyc,,\r
importlib_resources/tests/_compat.py,sha256=QGI_4p0DXybypoYvw0kr3jfQqvls3p8u4wy4Wvf0Z_o,435\r
importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data01/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20\r
importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data02/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/one/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data02/one/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13\r
importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/two/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data02/two/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13\r
importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
importlib_resources/tests/update-zips.py,sha256=x3iJVqWnMM5qp4Oob2Pl3o6Yi03sUjEv_5Wf-UCg3ps,1415\r
importlib_resources/tests/util.py,sha256=X1j-0C96pu3_tmtJuLhzfBfcfMenOphDLkxtCt5j7t4,5309\r
importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876\r
importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698\r
+++ /dev/null
-Copyright Jason R. Coombs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
+++ /dev/null
-Metadata-Version: 2.1
-Name: jaraco.context
-Version: 4.1.1
-Summary: Context managers by jaraco
-Home-page: https://github.com/jaraco/jaraco.context
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
-License: UNKNOWN
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.6
-License-File: LICENSE
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/jaraco.context.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/jaraco.context
-
-.. image:: https://github.com/jaraco/jaraco.context/workflows/tests/badge.svg
- :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
- :alt: Code style: Black
-
-.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest
- :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
- :target: https://blog.jaraco.com/skeleton
-
-
+++ /dev/null
-jaraco.context-4.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-jaraco.context-4.1.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
-jaraco.context-4.1.1.dist-info/METADATA,sha256=bvqDGCk6Z7TkohUqr5XZm19SbF9mVxrtXjN6uF_BAMQ,2031\r
-jaraco.context-4.1.1.dist-info/RECORD,,\r
-jaraco.context-4.1.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-jaraco.context-4.1.1.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
-jaraco/__pycache__/context.cpython-310.pyc,,\r
-jaraco/context.py,sha256=7X1tpCLc5EN45iWGzGcsH0Unx62REIkvtRvglj0SiUA,5420\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
--- /dev/null
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
--- /dev/null
+Metadata-Version: 2.1
+Name: jaraco.context
+Version: 4.2.0
+Summary: Context managers by jaraco
+Home-page: https://github.com/jaraco/jaraco.context
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.context.svg
+ :target: https://pypi.org/project/jaraco.context
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg
+
+.. image:: https://github.com/jaraco/jaraco.context/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest
+ :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.context
+ :target: https://tidelift.com/subscription/pkg/pypi-jaraco.context?utm_source=pypi-jaraco.context&utm_medium=readme
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This 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.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-jaraco.context?utm_source=pypi-jaraco.context&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
--- /dev/null
+jaraco.context-4.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+jaraco.context-4.2.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
+jaraco.context-4.2.0.dist-info/METADATA,sha256=6gWxpGoBWTzl4e8J1HisvNsL79YB4t1DG7ziQj-0k9Y,2883\r
+jaraco.context-4.2.0.dist-info/RECORD,,\r
+jaraco.context-4.2.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92\r
+jaraco.context-4.2.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
+jaraco/__pycache__/context.cpython-311.pyc,,\r
+jaraco/context.py,sha256=NvdB7ArVCDrhtexOnOwSv4ScDuueGbf9LRiOSCqPn6Y,6488\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.38.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
+++ /dev/null
-Copyright Jason R. Coombs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
+++ /dev/null
-Metadata-Version: 2.1
-Name: jaraco.functools
-Version: 3.5.0
-Summary: Functools like those found in stdlib
-Home-page: https://github.com/jaraco/jaraco.functools
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
-License: UNKNOWN
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.7
-License-File: LICENSE
-Requires-Dist: more-itertools
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: jaraco.classes ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg
-
-.. image:: https://img.shields.io/travis/jaraco/jaraco.functools/master.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/jaraco.functools
-
-.. image:: https://github.com/jaraco/jaraco.functools/workflows/tests/badge.svg
- :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
- :alt: Code style: Black
-
-.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest
- :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
- :target: https://blog.jaraco.com/skeleton
-
-Additional functools in the spirit of stdlib's functools.
-
-
+++ /dev/null
-jaraco.functools-3.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-jaraco.functools-3.5.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
-jaraco.functools-3.5.0.dist-info/METADATA,sha256=cE9C7u9bo_GjLAuw4nML67a25kUaPDiHn4j03lG4jd0,2276\r
-jaraco.functools-3.5.0.dist-info/RECORD,,\r
-jaraco.functools-3.5.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-jaraco.functools-3.5.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
-jaraco/__pycache__/functools.cpython-310.pyc,,\r
-jaraco/functools.py,sha256=PtEHbXZstgVJrwje4GvJOsz5pEbgslOcgEn2EJNpr2c,13494\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
--- /dev/null
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
--- /dev/null
+Metadata-Version: 2.1
+Name: jaraco.functools
+Version: 3.5.2
+Summary: Functools like those found in stdlib
+Home-page: https://github.com/jaraco/jaraco.functools
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: more-itertools
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: jaraco.classes ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg
+
+.. image:: https://img.shields.io/travis/jaraco/jaraco.functools/master.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.functools
+
+.. image:: https://github.com/jaraco/jaraco.functools/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest
+ :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.functools
+ :target: https://tidelift.com/subscription/pkg/pypi-jaraco.functools?utm_source=pypi-jaraco.functools&utm_medium=readme
+
+Additional functools in the spirit of stdlib's functools.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This 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.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-jaraco.functools?utm_source=pypi-jaraco.functools&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
--- /dev/null
+jaraco.functools-3.5.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+jaraco.functools-3.5.2.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
+jaraco.functools-3.5.2.dist-info/METADATA,sha256=ZIViwS4ZOmaWwA5ArwZ_xXQGR9WDnUSzx-0MO5kGPi8,3154\r
+jaraco.functools-3.5.2.dist-info/RECORD,,\r
+jaraco.functools-3.5.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92\r
+jaraco.functools-3.5.2.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
+jaraco/__pycache__/functools.cpython-311.pyc,,\r
+jaraco/functools.py,sha256=PtEHbXZstgVJrwje4GvJOsz5pEbgslOcgEn2EJNpr2c,13494\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
jaraco.text-3.7.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335\r
jaraco/text/__init__.py,sha256=I56MW2ZFwPrYXIxzqxMBe2A1t-T4uZBgEgAKe9-JoqM,15538\r
-jaraco/text/__pycache__/__init__.cpython-310.pyc,,\r
+jaraco/text/__pycache__/__init__.cpython-311.pyc,,\r
... {}['']
>>> key_error()
"""
+
+
+class on_interrupt(contextlib.ContextDecorator):
+ """
+ Replace a KeyboardInterrupt with SystemExit(1)
+
+ >>> def do_interrupt():
+ ... raise KeyboardInterrupt()
+ >>> on_interrupt('error')(do_interrupt)()
+ Traceback (most recent call last):
+ ...
+ SystemExit: 1
+ >>> on_interrupt('error', code=255)(do_interrupt)()
+ Traceback (most recent call last):
+ ...
+ SystemExit: 255
+ >>> on_interrupt('suppress')(do_interrupt)()
+ >>> with __import__('pytest').raises(KeyboardInterrupt):
+ ... on_interrupt('ignore')(do_interrupt)()
+ """
+
+ def __init__(
+ self,
+ action='error',
+ # py3.7 compat
+ # /,
+ code=1,
+ ):
+ self.action = action
+ self.code = code
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exctype, excinst, exctb):
+ if exctype is not KeyboardInterrupt or self.action == 'ignore':
+ return
+ elif self.action == 'error':
+ raise SystemExit(self.code) from excinst
+ return self.action == 'suppress'
+++ /dev/null
-Copyright (c) 2012 Erik Rose
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+++ /dev/null
-Metadata-Version: 2.1
-Name: more-itertools
-Version: 8.12.0
-Summary: More routines for operating on iterables, beyond itertools
-Home-page: https://github.com/more-itertools/more-itertools
-Author: Erik Rose
-Author-email: erikrose@grinchcentral.com
-License: MIT
-Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Software Development :: Libraries
-Requires-Python: >=3.5
-Description-Content-Type: text/x-rst
-License-File: LICENSE
-
-==============
-More Itertools
-==============
-
-.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest
- :target: https://more-itertools.readthedocs.io/en/stable/
-
-Python's ``itertools`` library is a gem - you can compose elegant solutions
-for a variety of problems with the functions it provides. In ``more-itertools``
-we collect additional building blocks, recipes, and routines for working with
-Python iterables.
-
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Grouping | `chunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_, |
-| | `ichunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_, |
-| | `sliced <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliced>`_, |
-| | `distribute <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute>`_, |
-| | `divide <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.divide>`_, |
-| | `split_at <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_at>`_, |
-| | `split_before <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_before>`_, |
-| | `split_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_after>`_, |
-| | `split_into <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_into>`_, |
-| | `split_when <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_when>`_, |
-| | `bucket <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.bucket>`_, |
-| | `unzip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unzip>`_, |
-| | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, |
-| | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, |
-| | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, |
-| | `seekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.seekable>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Windowing | `windowed <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed>`_, |
-| | `substrings <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings>`_, |
-| | `substrings_indexes <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings_indexes>`_, |
-| | `stagger <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.stagger>`_, |
-| | `windowed_complete <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed_complete>`_, |
-| | `pairwise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pairwise>`_, |
-| | `triplewise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.triplewise>`_, |
-| | `sliding_window <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliding_window>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Augmenting | `count_cycle <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle>`_, |
-| | `intersperse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse>`_, |
-| | `padded <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded>`_, |
-| | `mark_ends <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.mark_ends>`_, |
-| | `repeat_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last>`_, |
-| | `adjacent <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent>`_, |
-| | `groupby_transform <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform>`_, |
-| | `pad_none <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pad_none>`_, |
-| | `ncycles <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ncycles>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Combining | `collapse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse>`_, |
-| | `sort_together <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sort_together>`_, |
-| | `interleave <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_, |
-| | `interleave_longest <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_, |
-| | `interleave_evenly <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_evenly>`_, |
-| | `zip_offset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_, |
-| | `zip_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_, |
-| | `zip_broadcast <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_broadcast>`_, |
-| | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, |
-| | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, |
-| | `flatten <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_, |
-| | `roundrobin <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_, |
-| | `prepend <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.prepend>`_, |
-| | `value_chain <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.value_chain>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Summarizing | `ilen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ilen>`_, |
-| | `unique_to_each <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_to_each>`_, |
-| | `sample <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sample>`_, |
-| | `consecutive_groups <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consecutive_groups>`_, |
-| | `run_length <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length>`_, |
-| | `map_reduce <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce>`_, |
-| | `exactly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.exactly_n>`_, |
-| | `is_sorted <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_sorted>`_, |
-| | `all_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_equal>`_, |
-| | `all_unique <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_unique>`_, |
-| | `minmax <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.minmax>`_, |
-| | `first_true <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first_true>`_, |
-| | `quantify <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.quantify>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Selecting | `islice_extended <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.islice_extended>`_, |
-| | `first <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first>`_, |
-| | `last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.last>`_, |
-| | `one <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one>`_, |
-| | `only <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.only>`_, |
-| | `strictly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strictly_n>`_, |
-| | `strip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strip>`_, |
-| | `lstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.lstrip>`_, |
-| | `rstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rstrip>`_, |
-| | `filter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.filter_except>`_, |
-| | `map_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_except>`_, |
-| | `nth_or_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_or_last>`_, |
-| | `unique_in_window <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_in_window>`_, |
-| | `before_and_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.before_and_after>`_, |
-| | `nth <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth>`_, |
-| | `take <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.take>`_, |
-| | `tail <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tail>`_, |
-| | `unique_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertoo ls.unique_everseen>`_, |
-| | `unique_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_justseen>`_, |
-| | `duplicates_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.duplicates_everseen>`_, |
-| | `duplicates_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.duplicates_justseen>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Combinatorics | `distinct_permutations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_permutations>`_, |
-| | `distinct_combinations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_combinations>`_, |
-| | `circular_shifts <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.circular_shifts>`_, |
-| | `partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partitions>`_, |
-| | `set_partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.set_partitions>`_, |
-| | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, |
-| | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, |
-| | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, |
-| | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, |
-| | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, |
-| | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, |
-| | `random_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination>`_, |
-| | `random_combination_with_replacement <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination_with_replacement>`_, |
-| | `nth_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_product>`_, |
-| | `nth_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_permutation>`_, |
-| | `nth_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_combination>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Wrapping | `always_iterable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_, |
-| | `always_reversible <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_reversible>`_, |
-| | `countable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.countable>`_, |
-| | `consumer <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consumer>`_, |
-| | `with_iter <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.with_iter>`_, |
-| | `iter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_except>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| Others | `locate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.locate>`_, |
-| | `rlocate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rlocate>`_, |
-| | `replace <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.replace>`_, |
-| | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, |
-| | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, |
-| | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, |
-| | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, |
-| | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, |
-| | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, |
-| | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, |
-| | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, |
-| | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, |
-| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ |
-+------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-
-
-Getting started
-===============
-
-To get started, install the library with `pip <https://pip.pypa.io/en/stable/>`_:
-
-.. code-block:: shell
-
- pip install more-itertools
-
-The recipes from the `itertools docs <https://docs.python.org/3/library/itertools.html#itertools-recipes>`_
-are included in the top-level package:
-
-.. code-block:: python
-
- >>> from more_itertools import flatten
- >>> iterable = [(0, 1), (2, 3)]
- >>> list(flatten(iterable))
- [0, 1, 2, 3]
-
-Several new recipes are available as well:
-
-.. code-block:: python
-
- >>> from more_itertools import chunked
- >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8]
- >>> list(chunked(iterable, 3))
- [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
-
- >>> from more_itertools import spy
- >>> iterable = (x * x for x in range(1, 6))
- >>> head, iterable = spy(iterable, n=3)
- >>> list(head)
- [1, 4, 9]
- >>> list(iterable)
- [1, 4, 9, 16, 25]
-
-
-
-For the full listing of functions, see the `API documentation <https://more-itertools.readthedocs.io/en/stable/api.html>`_.
-
-
-Links elsewhere
-===============
-
-Blog posts about ``more-itertools``:
-
-* `Yo, I heard you like decorators <https://www.bbayles.com/index/decorator_factory>`__
-* `Tour of Python Itertools <https://martinheinz.dev/blog/16>`__ (`Alternate <https://dev.to/martinheinz/tour-of-python-itertools-4122>`__)
-* `Real-World Python More Itertools <https://www.gidware.com/real-world-more-itertools/>`_
-
-
-Development
-===========
-
-``more-itertools`` is maintained by `@erikrose <https://github.com/erikrose>`_
-and `@bbayles <https://github.com/bbayles>`_, with help from `many others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
-If you have a problem or suggestion, please file a bug or pull request in this
-repository. Thanks for contributing!
-
-
-Version History
-===============
-
-
- :noindex:
-
-8.12.0
-------
-
-* Bug fixes
- * Some documentation issues were fixed (thanks to Masynchin, spookylukey, astrojuanlu, and stephengmatthews)
- * Python 3.5 support was temporarily restored (thanks to mattbonnell)
-
-8.11.0
-------
-
-* New functions
- * The before_and_after, sliding_window, and triplewise recipes from the Python 3.10 docs were added
- * duplicates_everseen and duplicates_justseen (thanks to OrBin and DavidPratt512)
- * minmax (thanks to Ricocotam, MSeifert04, and ruancomelli)
- * strictly_n (thanks to hwalinga and NotWearingPants)
- * unique_in_window
-
-* Changes to existing functions
- * groupby_transform had its type stub improved (thanks to mjk4 and ruancomelli)
- * is_sorted now accepts a ``strict`` parameter (thanks to Dutcho and ruancomelli)
- * zip_broadcast was updated to fix a bug (thanks to kalekundert)
-
-8.10.0
-------
-
-* Changes to existing functions
- * The type stub for iter_except was improved (thanks to MarcinKonowalczyk)
-
-* Other changes:
- * Type stubs now ship with the source release (thanks to saaketp)
- * The Sphinx docs were improved (thanks to MarcinKonowalczyk)
-
-8.9.0
------
-
-* New functions
- * interleave_evenly (thanks to mbugert)
- * repeat_each (thanks to FinalSh4re)
- * chunked_even (thanks to valtron)
- * map_if (thanks to sassbalint)
- * zip_broadcast (thanks to kalekundert)
-
-* Changes to existing functions
- * The type stub for chunked was improved (thanks to PhilMacKay)
- * The type stubs for zip_equal and `zip_offset` were improved (thanks to maffoo)
- * Building Sphinx docs locally was improved (thanks to MarcinKonowalczyk)
-
-8.8.0
------
-
-* New functions
- * countable (thanks to krzysieq)
-
-* Changes to existing functions
- * split_before was updated to handle empy collections (thanks to TiunovNN)
- * unique_everseen got a performance boost (thanks to Numerlor)
- * The type hint for value_chain was corrected (thanks to vr2262)
-
-8.7.0
------
-
-* New functions
- * convolve (from the Python itertools docs)
- * product_index, combination_index, and permutation_index (thanks to N8Brooks)
- * value_chain (thanks to jenstroeger)
-
-* Changes to existing functions
- * distinct_combinations now uses a non-recursive algorithm (thanks to knutdrand)
- * pad_none is now the preferred name for padnone, though the latter remains available.
- * pairwise will now use the Python standard library implementation on Python 3.10+
- * sort_together now accepts a ``key`` argument (thanks to brianmaissy)
- * seekable now has a ``peek`` method, and can indicate whether the iterator it's wrapping is exhausted (thanks to gsakkis)
- * time_limited can now indicate whether its iterator has expired (thanks to roysmith)
- * The implementation of unique_everseen was improved (thanks to plammens)
-
-* Other changes:
- * Various documentation updates (thanks to cthoyt, Evantm, and cyphase)
-
-8.6.0
------
-
-* New itertools
- * all_unique (thanks to brianmaissy)
- * nth_product and nth_permutation (thanks to N8Brooks)
-
-* Changes to existing itertools
- * chunked and sliced now accept a ``strict`` parameter (thanks to shlomif and jtwool)
-
-* Other changes
- * Python 3.5 has reached its end of life and is no longer supported.
- * Python 3.9 is officially supported.
- * Various documentation fixes (thanks to timgates42)
-
-8.5.0
------
-
-* New itertools
- * windowed_complete (thanks to MarcinKonowalczyk)
-
-* Changes to existing itertools:
- * The is_sorted implementation was improved (thanks to cool-RR)
- * The groupby_transform now accepts a ``reducefunc`` parameter.
- * The last implementation was improved (thanks to brianmaissy)
-
-* Other changes
- * Various documentation fixes (thanks to craigrosie, samuelstjean, PiCT0)
- * The tests for distinct_combinations were improved (thanks to Minabsapi)
- * Automated tests now run on GitHub Actions. All commits now check:
- * That unit tests pass
- * That the examples in docstrings work
- * That test coverage remains high (using `coverage`)
- * For linting errors (using `flake8`)
- * For consistent style (using `black`)
- * That the type stubs work (using `mypy`)
- * That the docs build correctly (using `sphinx`)
- * That packages build correctly (using `twine`)
-
-8.4.0
------
-
-* New itertools
- * mark_ends (thanks to kalekundert)
- * is_sorted
-
-* Changes to existing itertools:
- * islice_extended can now be used with real slices (thanks to cool-RR)
- * The implementations for filter_except and map_except were improved (thanks to SergBobrovsky)
-
-* Other changes
- * Automated tests now enforce code style (using `black <https://github.com/psf/black>`__)
- * The various signatures of islice_extended and numeric_range now appear in the docs (thanks to dsfulf)
- * The test configuration for mypy was updated (thanks to blueyed)
-
-
-8.3.0
------
-
-* New itertools
- * zip_equal (thanks to frankier and alexmojaki)
-
-* Changes to existing itertools:
- * split_at, split_before, split_after, and split_when all got a ``maxsplit`` paramter (thanks to jferard and ilai-deutel)
- * split_at now accepts a ``keep_separator`` parameter (thanks to jferard)
- * distinct_permutations can now generate ``r``-length permutations (thanks to SergBobrovsky and ilai-deutel)
- * The windowed implementation was improved (thanks to SergBobrovsky)
- * The spy implementation was improved (thanks to has2k1)
-
-* Other changes
- * Type stubs are now tested with ``stubtest`` (thanks to ilai-deutel)
- * Tests now run with ``python -m unittest`` instead of ``python setup.py test`` (thanks to jdufresne)
-
-8.2.0
------
-
-* Bug fixes
- * The .pyi files for typing were updated. (thanks to blueyed and ilai-deutel)
-
-* Changes to existing itertools:
- * numeric_range now behaves more like the built-in range. (thanks to jferard)
- * bucket now allows for enumerating keys. (thanks to alexchandel)
- * sliced now should now work for numpy arrays. (thanks to sswingle)
- * seekable now has a ``maxlen`` parameter.
-
-8.1.0
------
-
-* Bug fixes
- * partition works with ``pred=None`` again. (thanks to MSeifert04)
-
-* New itertools
- * sample (thanks to tommyod)
- * nth_or_last (thanks to d-ryzhikov)
-
-* Changes to existing itertools:
- * The implementation for divide was improved. (thanks to jferard)
-
-8.0.2
------
-
-* Bug fixes
- * The type stub files are now part of the wheel distribution (thanks to keisheiled)
-
-8.0.1
------
-
-* Bug fixes
- * The type stub files now work for functions imported from the
- root package (thanks to keisheiled)
-
-8.0.0
------
-
-* New itertools and other additions
- * This library now ships type hints for use with mypy.
- (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance)
- * split_when (thanks to jferard)
- * repeat_last (thanks to d-ryzhikov)
-
-* Changes to existing itertools:
- * The implementation for set_partitions was improved. (thanks to jferard)
- * partition was optimized for expensive predicates. (thanks to stevecj)
- * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky)
- * The implementation for difference was improved. (thanks to Jabbey92)
-
-* Other changes
- * Python 3.4 has reached its end of life and is no longer supported.
- * Python 3.8 is officially supported. (thanks to jdufresne)
- * The ``collate`` function has been deprecated.
- It raises a ``DeprecationWarning`` if used, and will be removed in a future release.
- * one and only now provide more informative error messages. (thanks to gabbard)
- * Unit tests were moved outside of the main package (thanks to jdufresne)
- * Various documentation fixes (thanks to kriomant, gabbard, jdufresne)
-
-
-7.2.0
------
-
-* New itertools
- * distinct_combinations
- * set_partitions (thanks to kbarrett)
- * filter_except
- * map_except
-
-7.1.0
------
-
-* New itertools
- * ichunked (thanks davebelais and youtux)
- * only (thanks jaraco)
-
-* Changes to existing itertools:
- * numeric_range now supports ranges specified by
- ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests).
- * difference now supports an *initial* keyword argument.
-
-
-* Other changes
- * Various documentation fixes (thanks raimon49, pylang)
-
-7.0.0
------
-
-* New itertools:
- * time_limited
- * partitions (thanks to rominf and Saluev)
- * substrings_indexes (thanks to rominf)
-
-* Changes to existing itertools:
- * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet)
-
-The major version update is due to the change in the default behavior of
-collapse. It now treats ``bytes`` objects the same as ``str`` objects.
-This aligns its behavior with always_iterable.
-
-.. code-block:: python
-
- >>> from more_itertools import collapse
- >>> iterable = [[1, 2], b'345', [6]]
- >>> print(list(collapse(iterable)))
- [1, 2, b'345', 6]
-
-6.0.0
------
-
-* Major changes:
- * Python 2.7 is no longer supported. The 5.0.0 release will be the last
- version targeting Python 2.7.
- * All future releases will target the active versions of Python 3.
- As of 2019, those are Python 3.4 and above.
- * The ``six`` library is no longer a dependency.
- * The accumulate function is no longer part of this library. You
- may import a better version from the standard ``itertools`` module.
-
-* Changes to existing itertools:
- * The order of the parameters in grouper have changed to match
- the latest recipe in the itertools documentation. Use of the old order
- will be supported in this release, but emit a ``DeprecationWarning``.
- The legacy behavior will be dropped in a future release. (thanks to jaraco)
- * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values <https://stackoverflow.com/questions/6284396/permutations-with-unique-values>`_ at StackOverflow.)
- * An unused parameter was removed from substrings. (thanks to pylang)
-
-* Other changes:
- * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04)
- * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk)
-
-
+++ /dev/null
-more_itertools-8.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-more_itertools-8.12.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053\r
-more_itertools-8.12.0.dist-info/METADATA,sha256=QCCEcisEPr7iSfBIKCukhP-FbG9ehMK8tDIliZ3FBDc,39405\r
-more_itertools-8.12.0.dist-info/RECORD,,\r
-more_itertools-8.12.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-more_itertools-8.12.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15\r
-more_itertools/__init__.py,sha256=ZQYu_9H6stSG7viUgT32TFqslqcZwq82kWRZooKiI8Y,83\r
-more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43\r
-more_itertools/__pycache__/__init__.cpython-310.pyc,,\r
-more_itertools/__pycache__/more.cpython-310.pyc,,\r
-more_itertools/__pycache__/recipes.cpython-310.pyc,,\r
-more_itertools/more.py,sha256=jSrvV9BK-XKa4x7MPPp9yWYRDtRgR5h7yryEqHMU4mg,132578\r
-more_itertools/more.pyi,sha256=kWOkRKx0V8ZwC1D2j0c0DUfy56dazzpmRcm5ZuY_aqo,20006\r
-more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-more_itertools/recipes.py,sha256=N6aCDwoIPvE-aiqpGU-nbFwqiM3X8MKRcxBM84naW88,18410\r
-more_itertools/recipes.pyi,sha256=Lx3vb0p_vY7rF8MQuguvOcVaS9qd1WRL8JO_qVo7hiY,3925\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
+++ /dev/null
-more_itertools
--- /dev/null
+Copyright (c) 2012 Erik Rose
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+Metadata-Version: 2.1
+Name: more-itertools
+Version: 9.0.0
+Summary: More routines for operating on iterables, beyond itertools
+Keywords: itertools,iterator,iteration,filter,peek,peekable,chunk,chunked
+Author-email: Erik Rose <erikrose@grinchcentral.com>
+Requires-Python: >=3.7
+Description-Content-Type: text/x-rst
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Project-URL: Homepage, https://github.com/more-itertools/more-itertools
+
+==============
+More Itertools
+==============
+
+.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+Python's ``itertools`` library is a gem - you can compose elegant solutions
+for a variety of problems with the functions it provides. In ``more-itertools``
+we collect additional building blocks, recipes, and routines for working with
+Python iterables.
+
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Grouping | `chunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_, |
+| | `ichunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_, |
+| | `chunked_even <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked_even>`_, |
+| | `sliced <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliced>`_, |
+| | `constrained_batches <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.constrained_batches>`_, |
+| | `distribute <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute>`_, |
+| | `divide <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.divide>`_, |
+| | `split_at <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_at>`_, |
+| | `split_before <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_before>`_, |
+| | `split_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_after>`_, |
+| | `split_into <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_into>`_, |
+| | `split_when <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_when>`_, |
+| | `bucket <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.bucket>`_, |
+| | `unzip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unzip>`_, |
+| | `batched <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.batched>`_, |
+| | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, |
+| | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, |
+| | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, |
+| | `seekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.seekable>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Windowing | `windowed <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed>`_, |
+| | `substrings <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings>`_, |
+| | `substrings_indexes <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings_indexes>`_, |
+| | `stagger <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.stagger>`_, |
+| | `windowed_complete <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed_complete>`_, |
+| | `pairwise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pairwise>`_, |
+| | `triplewise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.triplewise>`_, |
+| | `sliding_window <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliding_window>`_, |
+| | `subslices <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.subslices>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Augmenting | `count_cycle <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle>`_, |
+| | `intersperse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse>`_, |
+| | `padded <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded>`_, |
+| | `repeat_each <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_each>`_, |
+| | `mark_ends <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.mark_ends>`_, |
+| | `repeat_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last>`_, |
+| | `adjacent <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent>`_, |
+| | `groupby_transform <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform>`_, |
+| | `pad_none <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pad_none>`_, |
+| | `ncycles <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ncycles>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combining | `collapse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse>`_, |
+| | `sort_together <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sort_together>`_, |
+| | `interleave <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_, |
+| | `interleave_longest <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_, |
+| | `interleave_evenly <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_evenly>`_, |
+| | `zip_offset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_, |
+| | `zip_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_, |
+| | `zip_broadcast <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_broadcast>`_, |
+| | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, |
+| | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, |
+| | `flatten <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_, |
+| | `roundrobin <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_, |
+| | `prepend <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.prepend>`_, |
+| | `value_chain <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.value_chain>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Summarizing | `ilen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ilen>`_, |
+| | `unique_to_each <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_to_each>`_, |
+| | `sample <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sample>`_, |
+| | `consecutive_groups <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consecutive_groups>`_, |
+| | `run_length <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length>`_, |
+| | `map_reduce <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce>`_, |
+| | `exactly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.exactly_n>`_, |
+| | `is_sorted <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_sorted>`_, |
+| | `all_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_equal>`_, |
+| | `all_unique <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_unique>`_, |
+| | `minmax <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.minmax>`_, |
+| | `first_true <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first_true>`_, |
+| | `quantify <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.quantify>`_, |
+| | `iequals <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iequals>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Selecting | `islice_extended <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.islice_extended>`_, |
+| | `first <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first>`_, |
+| | `last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.last>`_, |
+| | `one <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one>`_, |
+| | `only <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.only>`_, |
+| | `strictly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strictly_n>`_, |
+| | `strip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strip>`_, |
+| | `lstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.lstrip>`_, |
+| | `rstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rstrip>`_, |
+| | `filter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.filter_except>`_, |
+| | `map_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_except>`_, |
+| | `nth_or_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_or_last>`_, |
+| | `unique_in_window <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_in_window>`_, |
+| | `before_and_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.before_and_after>`_, |
+| | `nth <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth>`_, |
+| | `take <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.take>`_, |
+| | `tail <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tail>`_, |
+| | `unique_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertoo ls.unique_everseen>`_, |
+| | `unique_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_justseen>`_, |
+| | `duplicates_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.duplicates_everseen>`_, |
+| | `duplicates_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.duplicates_justseen>`_, |
+| | `longest_common_prefix <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.longest_common_prefix>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combinatorics | `distinct_permutations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_permutations>`_, |
+| | `distinct_combinations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_combinations>`_, |
+| | `circular_shifts <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.circular_shifts>`_, |
+| | `partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partitions>`_, |
+| | `set_partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.set_partitions>`_, |
+| | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, |
+| | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, |
+| | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, |
+| | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, |
+| | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, |
+| | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, |
+| | `random_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination>`_, |
+| | `random_combination_with_replacement <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination_with_replacement>`_, |
+| | `nth_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_product>`_, |
+| | `nth_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_permutation>`_, |
+| | `nth_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_combination>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Wrapping | `always_iterable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_, |
+| | `always_reversible <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_reversible>`_, |
+| | `countable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.countable>`_, |
+| | `consumer <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consumer>`_, |
+| | `with_iter <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.with_iter>`_, |
+| | `iter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_except>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Others | `locate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.locate>`_, |
+| | `rlocate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rlocate>`_, |
+| | `replace <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.replace>`_, |
+| | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, |
+| | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, |
+| | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, |
+| | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, |
+| | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, |
+| | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, |
+| | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, |
+| | `map_if <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_if>`_, |
+| | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, |
+| | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, |
+| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ |
+| | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_ |
+| | `sieve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sieve>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+Getting started
+===============
+
+To get started, install the library with `pip <https://pip.pypa.io/en/stable/>`_:
+
+.. code-block:: shell
+
+ pip install more-itertools
+
+The recipes from the `itertools docs <https://docs.python.org/3/library/itertools.html#itertools-recipes>`_
+are included in the top-level package:
+
+.. code-block:: python
+
+ >>> from more_itertools import flatten
+ >>> iterable = [(0, 1), (2, 3)]
+ >>> list(flatten(iterable))
+ [0, 1, 2, 3]
+
+Several new recipes are available as well:
+
+.. code-block:: python
+
+ >>> from more_itertools import chunked
+ >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ >>> list(chunked(iterable, 3))
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
+
+ >>> from more_itertools import spy
+ >>> iterable = (x * x for x in range(1, 6))
+ >>> head, iterable = spy(iterable, n=3)
+ >>> list(head)
+ [1, 4, 9]
+ >>> list(iterable)
+ [1, 4, 9, 16, 25]
+
+
+
+For the full listing of functions, see the `API documentation <https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+Links elsewhere
+===============
+
+Blog posts about ``more-itertools``:
+
+* `Yo, I heard you like decorators <https://www.bbayles.com/index/decorator_factory>`__
+* `Tour of Python Itertools <https://martinheinz.dev/blog/16>`__ (`Alternate <https://dev.to/martinheinz/tour-of-python-itertools-4122>`__)
+* `Real-World Python More Itertools <https://www.gidware.com/real-world-more-itertools/>`_
+
+
+Development
+===========
+
+``more-itertools`` is maintained by `@erikrose <https://github.com/erikrose>`_
+and `@bbayles <https://github.com/bbayles>`_, with help from `many others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
+If you have a problem or suggestion, please file a bug or pull request in this
+repository. Thanks for contributing!
+
+
+Version History
+===============
+
+The version history can be found in `documentation <https://more-itertools.readthedocs.io/en/stable/versions.html>`_.
+
--- /dev/null
+more_itertools-9.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+more_itertools-9.0.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053\r
+more_itertools-9.0.0.dist-info/METADATA,sha256=o71Ks93mbZ7709yvfFg09Vxgj-8t2ZZaYIyUprS8GHw,31266\r
+more_itertools-9.0.0.dist-info/RECORD,,\r
+more_itertools-9.0.0.dist-info/WHEEL,sha256=4TfKIB_xu-04bc2iKz6_zFt-gEFEEDU_31HGhqzOCE8,81\r
+more_itertools/__init__.py,sha256=5PNQMpy400s5GB3jcWwzje0RCw8k0bvU9W_C49V0fd0,148\r
+more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43\r
+more_itertools/__pycache__/__init__.cpython-311.pyc,,\r
+more_itertools/__pycache__/more.cpython-311.pyc,,\r
+more_itertools/__pycache__/recipes.cpython-311.pyc,,\r
+more_itertools/more.py,sha256=FmmtkT-j69qILkxEELk5ZRoZK8St1Vg_fOGW0sTFd7g,133336\r
+more_itertools/more.pyi,sha256=hbf2oqg56wctXwf6yM1B0QLWqsNyDrqtX2znvQRAe3Q,20297\r
+more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+more_itertools/recipes.py,sha256=ZX4-2IfbZKlPIVaDITH2buX_fPuMDe1EVc6e2XSsCz8,22975\r
+more_itertools/recipes.pyi,sha256=NA6qqcKMbQ2fly9hCyCzMcx46Tn9TLl-9mFnZsRytZM,3851\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: flit 3.7.1
+Root-Is-Purelib: true
+Tag: py3-none-any
+"""More routines for operating on iterables, beyond itertools"""
+
from .more import * # noqa
from .recipes import * # noqa
-__version__ = '8.12.0'
+__version__ = '9.0.0'
from collections import Counter, defaultdict, deque, abc
from collections.abc import Sequence
from functools import partial, reduce, wraps
-from heapq import merge, heapify, heapreplace, heappop
+from heapq import heapify, heapreplace, heappop
from itertools import (
chain,
compress,
from time import monotonic
from .recipes import (
+ _marker,
+ _zip_equal,
+ UnequalIterablesError,
consume,
flatten,
pairwise,
powerset,
take,
unique_everseen,
+ all_equal,
)
__all__ = [
'chunked_even',
'circular_shifts',
'collapse',
- 'collate',
'combination_index',
'consecutive_groups',
+ 'constrained_batches',
'consumer',
'count_cycle',
'countable',
'first',
'groupby_transform',
'ichunked',
+ 'iequals',
'ilen',
'interleave',
'interleave_evenly',
'iterate',
'last',
'locate',
+ 'longest_common_prefix',
'lstrip',
'make_decorator',
'map_except',
]
-_marker = object()
-
-
def chunked(iterable, n, strict=False):
"""Break *iterable* into lists of length *n*:
return self._cache[index]
-def collate(*iterables, **kwargs):
- """Return a sorted merge of the items from each of several already-sorted
- *iterables*.
-
- >>> list(collate('ACDZ', 'AZ', 'JKL'))
- ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z']
-
- Works lazily, keeping only the next value from each iterable in memory. Use
- :func:`collate` to, for example, perform a n-way mergesort of items that
- don't fit in memory.
-
- If a *key* function is specified, the iterables will be sorted according
- to its result:
-
- >>> key = lambda s: int(s) # Sort by numeric value, not by string
- >>> list(collate(['1', '10'], ['2', '11'], key=key))
- ['1', '2', '10', '11']
-
-
- If the *iterables* are sorted in descending order, set *reverse* to
- ``True``:
-
- >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True))
- [5, 4, 3, 2, 1, 0]
-
- If the elements of the passed-in iterables are out of order, you might get
- unexpected results.
-
- On Python 3.5+, this function is an alias for :func:`heapq.merge`.
-
- """
- warnings.warn(
- "collate is no longer part of more_itertools, use heapq.merge",
- DeprecationWarning,
- )
- return merge(*iterables, **kwargs)
-
-
def consumer(func):
"""Decorator that automatically advances a PEP-342-style "reverse iterator"
to its first yield point so you don't have to call ``next()`` on it
yield tuple(window)
size = len(window)
- if size < n:
+ if size == 0:
+ return
+ elif size < n:
yield tuple(chain(window, repeat(fillvalue, n - size)))
elif 0 < i < min(step, n):
window += (fillvalue,) * i
)
-class UnequalIterablesError(ValueError):
- def __init__(self, details=None):
- msg = 'Iterables have different lengths'
- if details is not None:
- msg += (': index 0 has length {}; index {} has length {}').format(
- *details
- )
-
- super().__init__(msg)
-
-
-def _zip_equal_generator(iterables):
- for combo in zip_longest(*iterables, fillvalue=_marker):
- for val in combo:
- if val is _marker:
- raise UnequalIterablesError()
- yield combo
-
-
-def _zip_equal(*iterables):
- # Check whether the iterables are all the same size.
- try:
- first_size = len(iterables[0])
- for i, it in enumerate(iterables[1:], 1):
- size = len(it)
- if size != first_size:
- break
- else:
- # If we didn't break out, we can use the built-in zip.
- return zip(*iterables)
-
- # If we did break out, there was a mismatch.
- raise UnequalIterablesError(details=(first_size, i, size))
- # If any one of the iterables didn't have a length, start reading
- # them until one runs out.
- except TypeError:
- return _zip_equal_generator(iterables)
-
-
def zip_equal(*iterables):
"""``zip`` the input *iterables* together, but raise
``UnequalIterablesError`` if they aren't all the same length.
of the zipped *iterable*.
The ``i``-th iterable contains the ``i``-th element from each element
- of the zipped iterable. The first element is used to to determine the
+ of the zipped iterable. The first element is used to determine the
length of the remaining elements.
>>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
return compress(count(), starmap(pred, it))
+def longest_common_prefix(iterables):
+ """Yield elements of the longest common prefix amongst given *iterables*.
+
+ >>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf']))
+ 'ab'
+
+ """
+ return (c[0] for c in takewhile(all_equal, zip(*iterables)))
+
+
def lstrip(iterable, pred):
"""Yield the items from *iterable*, but strip any from the beginning
for which *pred* returns ``True``.
if initial is not None:
first = []
- return chain(first, starmap(func, zip(b, a)))
+ return chain(first, map(func, b, a))
class SequenceView(Sequence):
return first_value
+class _IChunk:
+ def __init__(self, iterable, n):
+ self._it = islice(iterable, n)
+ self._cache = deque()
+
+ def fill_cache(self):
+ self._cache.extend(self._it)
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ try:
+ return next(self._it)
+ except StopIteration:
+ if self._cache:
+ return self._cache.popleft()
+ else:
+ raise
+
+
def ichunked(iterable, n):
"""Break *iterable* into sub-iterables with *n* elements each.
:func:`ichunked` is like :func:`chunked`, but it yields iterables
[8, 9, 10, 11]
"""
- source = iter(iterable)
-
+ source = peekable(iter(iterable))
+ ichunk_marker = object()
while True:
# Check to see whether we're at the end of the source iterable
- item = next(source, _marker)
- if item is _marker:
+ item = source.peek(ichunk_marker)
+ if item is ichunk_marker:
return
- # Clone the source and yield an n-length slice
- source, it = tee(chain([item], source))
- yield islice(it, n)
+ chunk = _IChunk(source, n)
+ yield chunk
+
+ # Advance the source iterable and fill previous chunk's cache
+ chunk.fill_cache()
+
+
+def iequals(*iterables):
+ """Return ``True`` if all given *iterables* are equal to each other,
+ which means that they contain the same elements in the same order.
- # Advance the source iterable
- consume(source, n)
+ The function is useful for comparing iterables of different data types
+ or iterables that do not support equality checks.
+
+ >>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc"))
+ True
+
+ >>> iequals("abc", "acb")
+ False
+
+ Not to be confused with :func:`all_equals`, which checks whether all
+ elements of iterable are equal to each other.
+
+ """
+ return all(map(all_equal, zip_longest(*iterables, fillvalue=object())))
def distinct_combinations(iterable, r):
self._aborted = False
self._future = None
self._wait_seconds = wait_seconds
- self._executor = __import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1)
+ # Lazily import concurrent.future
+ self._executor = __import__(
+ ).futures.__import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1)
self._iterator = self._reader()
def __enter__(self):
n, _ = last(pool, default=(n, None))
- # Python versiosn below 3.8 don't have math.comb
+ # Python versions below 3.8 don't have math.comb
index = 1
for i, j in enumerate(reversed(indexes), start=1):
j = n - j
If the *strict* keyword argument is ``True``, then
``UnequalIterablesError`` will be raised if any of the iterables have
- different lengthss.
+ different lengths.
"""
def is_scalar(obj):
hi, hi_key = y, y_key
return lo, hi
+
+
+def constrained_batches(
+ iterable, max_size, max_count=None, get_len=len, strict=True
+):
+ """Yield batches of items from *iterable* with a combined size limited by
+ *max_size*.
+
+ >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']
+ >>> list(constrained_batches(iterable, 10))
+ [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')]
+
+ If a *max_count* is supplied, the number of items per batch is also
+ limited:
+
+ >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']
+ >>> list(constrained_batches(iterable, 10, max_count = 2))
+ [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)]
+
+ If a *get_len* function is supplied, use that instead of :func:`len` to
+ determine item size.
+
+ If *strict* is ``True``, raise ``ValueError`` if any single item is bigger
+ than *max_size*. Otherwise, allow single items to exceed *max_size*.
+ """
+ if max_size <= 0:
+ raise ValueError('maximum size must be greater than zero')
+
+ batch = []
+ batch_size = 0
+ batch_count = 0
+ for item in iterable:
+ item_len = get_len(item)
+ if strict and item_len > max_size:
+ raise ValueError('item size exceeds maximum size')
+
+ reached_count = batch_count == max_count
+ reached_size = item_len + batch_size > max_size
+ if batch_count and (reached_size or reached_count):
+ yield tuple(batch)
+ batch.clear()
+ batch_size = 0
+ batch_count = 0
+
+ batch.append(item)
+ batch_size += item_len
+ batch_count += 1
+
+ if batch:
+ yield tuple(batch)
@overload
def __getitem__(self, index: slice) -> List[_T]: ...
-def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ...
def consumer(func: _GenFn) -> _GenFn: ...
def ilen(iterable: Iterable[object]) -> int: ...
def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ...
iterable: Iterable[_T],
*,
n: Optional[int] = ...,
- next_multiple: bool = ...
+ next_multiple: bool = ...,
) -> Iterator[Optional[_T]]: ...
@overload
def padded(
__iter1: Iterable[_T],
__iter2: Iterable[_T],
__iter3: Iterable[_T],
- *iterables: Iterable[_T]
+ *iterables: Iterable[_T],
) -> Iterator[Tuple[_T, ...]]: ...
@overload
def zip_offset(
*,
offsets: _SizedIterable[int],
longest: bool = ...,
- fillvalue: None = None
+ fillvalue: None = None,
) -> Iterator[Tuple[Optional[_T1]]]: ...
@overload
def zip_offset(
*,
offsets: _SizedIterable[int],
longest: bool = ...,
- fillvalue: None = None
+ fillvalue: None = None,
) -> Iterator[Tuple[Optional[_T1], Optional[_T2]]]: ...
@overload
def zip_offset(
*iterables: Iterable[_T],
offsets: _SizedIterable[int],
longest: bool = ...,
- fillvalue: None = None
+ fillvalue: None = None,
) -> Iterator[Tuple[Optional[_T], ...]]: ...
@overload
def zip_offset(
iterable: Iterable[_T],
func: Callable[[_T, _T], _U] = ...,
*,
- initial: None = ...
+ initial: None = ...,
) -> Iterator[Union[_T, _U]]: ...
@overload
def difference(
def filter_except(
validator: Callable[[Any], object],
iterable: Iterable[_T],
- *exceptions: Type[BaseException]
+ *exceptions: Type[BaseException],
) -> Iterator[_T]: ...
def map_except(
function: Callable[[Any], _U],
iterable: Iterable[_T],
- *exceptions: Type[BaseException]
+ *exceptions: Type[BaseException],
) -> Iterator[_U]: ...
def map_if(
iterable: Iterable[Any],
scalar_types: Union[
type, Tuple[Union[type, Tuple[Any, ...]], ...], None
] = ...,
- strict: bool = ...
+ strict: bool = ...,
) -> Iterable[Tuple[_T, ...]]: ...
def unique_in_window(
iterable: Iterable[_T], n: int, key: Optional[Callable[[_T], _U]] = ...
iterable_or_value: Iterable[_SupportsLessThanT],
*,
key: None = None,
- default: _U
+ default: _U,
) -> Union[_U, Tuple[_SupportsLessThanT, _SupportsLessThanT]]: ...
@overload
def minmax(
def minmax(
iterable_or_value: _SupportsLessThanT,
__other: _SupportsLessThanT,
- *others: _SupportsLessThanT
+ *others: _SupportsLessThanT,
) -> Tuple[_SupportsLessThanT, _SupportsLessThanT]: ...
@overload
def minmax(
iterable_or_value: _T,
__other: _T,
*others: _T,
- key: Callable[[_T], _SupportsLessThan]
+ key: Callable[[_T], _SupportsLessThan],
) -> Tuple[_T, _T]: ...
+def longest_common_prefix(
+ iterables: Iterable[Iterable[_T]],
+) -> Iterator[_T]: ...
+def iequals(*iterables: Iterable[object]) -> bool: ...
+def constrained_batches(
+ iterable: Iterable[object],
+ max_size: int,
+ max_count: Optional[int] = ...,
+ get_len: Callable[[_T], object] = ...,
+ strict: bool = ...,
+) -> Iterator[Tuple[_T]]: ...
.. [1] http://docs.python.org/library/itertools.html#recipes
"""
-import warnings
+import math
+import operator
+
from collections import deque
+from collections.abc import Sized
+from functools import reduce
from itertools import (
chain,
combinations,
+ compress,
count,
cycle,
groupby,
tee,
zip_longest,
)
-import operator
from random import randrange, sample, choice
__all__ = [
'all_equal',
+ 'batched',
'before_and_after',
'consume',
'convolve',
'pad_none',
'pairwise',
'partition',
+ 'polynomial_from_roots',
'powerset',
'prepend',
'quantify',
'random_product',
'repeatfunc',
'roundrobin',
+ 'sieve',
'sliding_window',
+ 'subslices',
'tabulate',
'tail',
'take',
'unique_justseen',
]
+_marker = object()
+
def take(n, iterable):
"""Return first *n* items of the iterable as a list.
['E', 'F', 'G']
"""
- return iter(deque(iterable, maxlen=n))
+ # If the given iterable has a length, then we can use islice to get its
+ # final elements. Note that if the iterable is not actually Iterable,
+ # either islice or deque will throw a TypeError. This is why we don't
+ # check if it is Iterable.
+ if isinstance(iterable, Sized):
+ yield from islice(iterable, max(0, len(iterable) - n), None)
+ else:
+ yield from iter(deque(iterable, maxlen=n))
def consume(iterator, n=None):
pairwise.__doc__ = _pairwise.__doc__
-def grouper(iterable, n, fillvalue=None):
- """Collect data into fixed-length chunks or blocks.
+class UnequalIterablesError(ValueError):
+ def __init__(self, details=None):
+ msg = 'Iterables have different lengths'
+ if details is not None:
+ msg += (': index 0 has length {}; index {} has length {}').format(
+ *details
+ )
+
+ super().__init__(msg)
+
+
+def _zip_equal_generator(iterables):
+ for combo in zip_longest(*iterables, fillvalue=_marker):
+ for val in combo:
+ if val is _marker:
+ raise UnequalIterablesError()
+ yield combo
+
+
+def _zip_equal(*iterables):
+ # Check whether the iterables are all the same size.
+ try:
+ first_size = len(iterables[0])
+ for i, it in enumerate(iterables[1:], 1):
+ size = len(it)
+ if size != first_size:
+ break
+ else:
+ # If we didn't break out, we can use the built-in zip.
+ return zip(*iterables)
- >>> list(grouper('ABCDEFG', 3, 'x'))
+ # If we did break out, there was a mismatch.
+ raise UnequalIterablesError(details=(first_size, i, size))
+ # If any one of the iterables didn't have a length, start reading
+ # them until one runs out.
+ except TypeError:
+ return _zip_equal_generator(iterables)
+
+
+def grouper(iterable, n, incomplete='fill', fillvalue=None):
+ """Group elements from *iterable* into fixed-length groups of length *n*.
+
+ >>> list(grouper('ABCDEF', 3))
+ [('A', 'B', 'C'), ('D', 'E', 'F')]
+
+ The keyword arguments *incomplete* and *fillvalue* control what happens for
+ iterables whose length is not a multiple of *n*.
+
+ When *incomplete* is `'fill'`, the last group will contain instances of
+ *fillvalue*.
+
+ >>> list(grouper('ABCDEFG', 3, incomplete='fill', fillvalue='x'))
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')]
+ When *incomplete* is `'ignore'`, the last group will not be emitted.
+
+ >>> list(grouper('ABCDEFG', 3, incomplete='ignore', fillvalue='x'))
+ [('A', 'B', 'C'), ('D', 'E', 'F')]
+
+ When *incomplete* is `'strict'`, a subclass of `ValueError` will be raised.
+
+ >>> it = grouper('ABCDEFG', 3, incomplete='strict')
+ >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ UnequalIterablesError
+
"""
- if isinstance(iterable, int):
- warnings.warn(
- "grouper expects iterable as first parameter", DeprecationWarning
- )
- n, iterable = iterable, n
args = [iter(iterable)] * n
- return zip_longest(fillvalue=fillvalue, *args)
+ if incomplete == 'fill':
+ return zip_longest(*args, fillvalue=fillvalue)
+ if incomplete == 'strict':
+ return _zip_equal(*args)
+ if incomplete == 'ignore':
+ return zip(*args)
+ else:
+ raise ValueError('Expected fill, strict, or ignore')
def roundrobin(*iterables):
transition.append(elem)
return
- def remainder_iterator():
- yield from transition
- yield from it
+ # Note: this is different from itertools recipes to allow nesting
+ # before_and_after remainders into before_and_after again. See tests
+ # for an example.
+ remainder_iterator = chain(transition, it)
- return true_iterator(), remainder_iterator()
+ return true_iterator(), remainder_iterator
def triplewise(iterable):
for x in it:
window.append(x)
yield tuple(window)
+
+
+def subslices(iterable):
+ """Return all contiguous non-empty subslices of *iterable*.
+
+ >>> list(subslices('ABC'))
+ [['A'], ['A', 'B'], ['A', 'B', 'C'], ['B'], ['B', 'C'], ['C']]
+
+ This is similar to :func:`substrings`, but emits items in a different
+ order.
+ """
+ seq = list(iterable)
+ slices = starmap(slice, combinations(range(len(seq) + 1), 2))
+ return map(operator.getitem, repeat(seq), slices)
+
+
+def polynomial_from_roots(roots):
+ """Compute a polynomial's coefficients from its roots.
+
+ >>> roots = [5, -4, 3] # (x - 5) * (x + 4) * (x - 3)
+ >>> polynomial_from_roots(roots) # x^3 - 4 * x^2 - 17 * x + 60
+ [1, -4, -17, 60]
+ """
+ # Use math.prod for Python 3.8+,
+ prod = getattr(math, 'prod', lambda x: reduce(operator.mul, x, 1))
+ roots = list(map(operator.neg, roots))
+ return [
+ sum(map(prod, combinations(roots, k))) for k in range(len(roots) + 1)
+ ]
+
+
+def sieve(n):
+ """Yield the primes less than n.
+
+ >>> list(sieve(30))
+ [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+ """
+ isqrt = getattr(math, 'isqrt', lambda x: int(math.sqrt(x)))
+ limit = isqrt(n) + 1
+ data = bytearray([1]) * n
+ data[:2] = 0, 0
+ for p in compress(range(limit), data):
+ data[p + p : n : p] = bytearray(len(range(p + p, n, p)))
+
+ return compress(count(), data)
+
+
+def batched(iterable, n):
+ """Batch data into lists of length *n*. The last batch may be shorter.
+
+ >>> list(batched('ABCDEFG', 3))
+ [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
+
+ This recipe is from the ``itertools`` docs. This library also provides
+ :func:`chunked`, which has a different implementation.
+ """
+ it = iter(iterable)
+ while True:
+ batch = list(islice(it, n))
+ if not batch:
+ break
+ yield batch
Iterator,
List,
Optional,
+ Sequence,
Tuple,
TypeVar,
Union,
func: Callable[..., _U], times: Optional[int] = ..., *args: Any
) -> Iterator[_U]: ...
def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ...
-@overload
def grouper(
- iterable: Iterable[_T], n: int
-) -> Iterator[Tuple[Optional[_T], ...]]: ...
-@overload
-def grouper(
- iterable: Iterable[_T], n: int, fillvalue: _U
-) -> Iterator[Tuple[Union[_T, _U], ...]]: ...
-@overload
-def grouper( # Deprecated interface
- iterable: int, n: Iterable[_T]
-) -> Iterator[Tuple[Optional[_T], ...]]: ...
-@overload
-def grouper( # Deprecated interface
- iterable: int, n: Iterable[_T], fillvalue: _U
+ iterable: Iterable[_T],
+ n: int,
+ incomplete: str = ...,
+ fillvalue: _U = ...,
) -> Iterator[Tuple[Union[_T, _U], ...]]: ...
def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ...
def partition(
def sliding_window(
iterable: Iterable[_T], n: int
) -> Iterator[Tuple[_T, ...]]: ...
+def subslices(iterable: Iterable[_T]) -> Iterator[List[_T]]: ...
+def polynomial_from_roots(roots: Sequence[int]) -> List[int]: ...
+def sieve(n: int) -> Iterator[int]: ...
+def batched(
+ iterable: Iterable[_T],
+ n: int,
+) -> Iterator[List[_T]]: ...
packaging-21.3.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10\r
packaging/__about__.py,sha256=ugASIO2w1oUyH8_COqQ2X_s0rDhjbhQC3yJocD03h2c,661\r
packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497\r
-packaging/__pycache__/__about__.cpython-310.pyc,,\r
-packaging/__pycache__/__init__.cpython-310.pyc,,\r
-packaging/__pycache__/_manylinux.cpython-310.pyc,,\r
-packaging/__pycache__/_musllinux.cpython-310.pyc,,\r
-packaging/__pycache__/_structures.cpython-310.pyc,,\r
-packaging/__pycache__/markers.cpython-310.pyc,,\r
-packaging/__pycache__/requirements.cpython-310.pyc,,\r
-packaging/__pycache__/specifiers.cpython-310.pyc,,\r
-packaging/__pycache__/tags.cpython-310.pyc,,\r
-packaging/__pycache__/utils.cpython-310.pyc,,\r
-packaging/__pycache__/version.cpython-310.pyc,,\r
+packaging/__pycache__/__about__.cpython-311.pyc,,\r
+packaging/__pycache__/__init__.cpython-311.pyc,,\r
+packaging/__pycache__/_manylinux.cpython-311.pyc,,\r
+packaging/__pycache__/_musllinux.cpython-311.pyc,,\r
+packaging/__pycache__/_structures.cpython-311.pyc,,\r
+packaging/__pycache__/markers.cpython-311.pyc,,\r
+packaging/__pycache__/requirements.cpython-311.pyc,,\r
+packaging/__pycache__/specifiers.cpython-311.pyc,,\r
+packaging/__pycache__/tags.cpython-311.pyc,,\r
+packaging/__pycache__/utils.cpython-311.pyc,,\r
+packaging/__pycache__/version.cpython-311.pyc,,\r
packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488\r
packaging/_musllinux.py,sha256=_KGgY_qc7vhMGpoqss25n2hiLCNKRtvz9mCrS7gkqyc,4378\r
packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431\r
--- /dev/null
+Metadata-Version: 2.1
+Name: platformdirs
+Version: 2.6.2
+Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a "user data dir".
+Project-URL: Documentation, https://platformdirs.readthedocs.io
+Project-URL: Homepage, https://github.com/platformdirs/platformdirs
+Project-URL: Source, https://github.com/platformdirs/platformdirs
+Project-URL: Tracker, https://github.com/platformdirs/platformdirs/issues
+Maintainer-email: Bernát Gábor <gaborjbernat@gmail.com>, Julian Berman <Julian@GrayVines.com>, Ofek Lev <oss@ofek.dev>, Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
+License-File: LICENSE
+Keywords: application,cache,directory,log,user
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.7
+Requires-Dist: typing-extensions>=4.4; python_version < '3.8'
+Provides-Extra: docs
+Requires-Dist: furo>=2022.12.7; extra == 'docs'
+Requires-Dist: proselint>=0.13; extra == 'docs'
+Requires-Dist: sphinx-autodoc-typehints>=1.19.5; extra == 'docs'
+Requires-Dist: sphinx>=5.3; extra == 'docs'
+Provides-Extra: test
+Requires-Dist: appdirs==1.4.4; extra == 'test'
+Requires-Dist: covdefaults>=2.2.2; extra == 'test'
+Requires-Dist: pytest-cov>=4; extra == 'test'
+Requires-Dist: pytest-mock>=3.10; extra == 'test'
+Requires-Dist: pytest>=7.2; extra == 'test'
+Description-Content-Type: text/x-rst
+
+The problem
+===========
+
+.. image:: https://github.com/platformdirs/platformdirs/workflows/Test/badge.svg
+ :target: https://github.com/platformdirs/platformdirs/actions?query=workflow%3ATest
+
+When writing desktop application, finding the right location to store user data
+and configuration varies per platform. Even for single-platform apps, there
+may by plenty of nuances in figuring out the right location.
+
+For example, if running on macOS, you should use::
+
+ ~/Library/Application Support/<AppName>
+
+If on Windows (at least English Win XP) that should be::
+
+ C:\Documents and Settings\<User>\Application Data\Local Settings\<AppAuthor>\<AppName>
+
+or possibly::
+
+ C:\Documents and Settings\<User>\Application Data\<AppAuthor>\<AppName>
+
+for `roaming profiles <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc766489(v=ws.10)>`_ but that is another story.
+
+On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be::
+
+ ~/.local/share/<AppName>
+
+.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+
+``platformdirs`` to the rescue
+==============================
+
+This kind of thing is what the ``platformdirs`` package is for.
+``platformdirs`` will help you choose an appropriate:
+
+- user data dir (``user_data_dir``)
+- user config dir (``user_config_dir``)
+- user cache dir (``user_cache_dir``)
+- site data dir (``site_data_dir``)
+- site config dir (``site_config_dir``)
+- user log dir (``user_log_dir``)
+- user documents dir (``user_documents_dir``)
+- user runtime dir (``user_runtime_dir``)
+
+And also:
+
+- Is slightly opinionated on the directory names used. Look for "OPINION" in
+ documentation and code for when an opinion is being applied.
+
+Example output
+==============
+
+On macOS:
+
+.. code-block:: pycon
+
+ >>> from platformdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/Users/trentm/Library/Application Support/SuperApp'
+ >>> site_data_dir(appname, appauthor)
+ '/Library/Application Support/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/Users/trentm/Library/Caches/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/Users/trentm/Library/Logs/SuperApp'
+ >>> user_documents_dir()
+ '/Users/trentm/Documents'
+ >>> user_runtime_dir(appname, appauthor)
+ '/Users/trentm/Library/Caches/TemporaryItems/SuperApp'
+
+On Windows 7:
+
+.. code-block:: pycon
+
+ >>> from platformdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
+ >>> user_data_dir(appname, appauthor, roaming=True)
+ 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
+ >>> user_log_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
+ >>> user_documents_dir()
+ 'C:\\Users\\trentm\\Documents'
+ >>> user_runtime_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp'
+
+On Linux:
+
+.. code-block:: pycon
+
+ >>> from platformdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/home/trentm/.local/share/SuperApp'
+ >>> site_data_dir(appname, appauthor)
+ '/usr/local/share/SuperApp'
+ >>> site_data_dir(appname, appauthor, multipath=True)
+ '/usr/local/share/SuperApp:/usr/share/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/home/trentm/.cache/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/home/trentm/.cache/SuperApp/log'
+ >>> user_config_dir(appname)
+ '/home/trentm/.config/SuperApp'
+ >>> user_documents_dir()
+ '/home/trentm/Documents'
+ >>> user_runtime_dir(appname, appauthor)
+ '/run/user/{os.getuid()}/SuperApp'
+ >>> site_config_dir(appname)
+ '/etc/xdg/SuperApp'
+ >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc"
+ >>> site_config_dir(appname, multipath=True)
+ '/etc/SuperApp:/usr/local/etc/SuperApp'
+
+On Android::
+
+ >>> from platformdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/data/data/com.myApp/files/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/data/data/com.myApp/cache/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/data/data/com.myApp/cache/SuperApp/log'
+ >>> user_config_dir(appname)
+ '/data/data/com.myApp/shared_prefs/SuperApp'
+ >>> user_documents_dir()
+ '/storage/emulated/0/Documents'
+ >>> user_runtime_dir(appname, appauthor)
+ '/data/data/com.myApp/cache/SuperApp/tmp'
+
+Note: Some android apps like Termux and Pydroid are used as shells. These
+apps are used by the end user to emulate Linux environment. Presence of
+``SHELL`` environment variable is used by Platformdirs to differentiate
+between general android apps and android apps used as shells. Shell android
+apps also support ``XDG_*`` environment variables.
+
+
+``PlatformDirs`` for convenience
+================================
+
+.. code-block:: pycon
+
+ >>> from platformdirs import PlatformDirs
+ >>> dirs = PlatformDirs("SuperApp", "Acme")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp'
+ >>> dirs.site_data_dir
+ '/Library/Application Support/SuperApp'
+ >>> dirs.user_cache_dir
+ '/Users/trentm/Library/Caches/SuperApp'
+ >>> dirs.user_log_dir
+ '/Users/trentm/Library/Logs/SuperApp'
+ >>> dirs.user_documents_dir
+ '/Users/trentm/Documents'
+ >>> dirs.user_runtime_dir
+ '/Users/trentm/Library/Caches/TemporaryItems/SuperApp'
+
+Per-version isolation
+=====================
+
+If you have multiple versions of your app in use that you want to be
+able to run side-by-side, then you may want version-isolation for these
+dirs::
+
+ >>> from platformdirs import PlatformDirs
+ >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp/1.0'
+ >>> dirs.site_data_dir
+ '/Library/Application Support/SuperApp/1.0'
+ >>> dirs.user_cache_dir
+ '/Users/trentm/Library/Caches/SuperApp/1.0'
+ >>> dirs.user_log_dir
+ '/Users/trentm/Library/Logs/SuperApp/1.0'
+ >>> dirs.user_documents_dir
+ '/Users/trentm/Documents'
+ >>> dirs.user_runtime_dir
+ '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0'
+
+Be wary of using this for configuration files though; you'll need to handle
+migrating configuration files manually.
+
+Why this Fork?
+==============
+
+This repository is a friendly fork of the wonderful work started by
+`ActiveState <https://github.com/ActiveState/appdirs>`_ who created
+``appdirs``, this package's ancestor.
+
+Maintaining an open source project is no easy task, particularly
+from within an organization, and the Python community is indebted
+to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for
+creating an incredibly useful simple module, as evidenced by the wide
+number of users it has attracted over the years.
+
+Nonetheless, given the number of long-standing open issues
+and pull requests, and no clear path towards `ensuring
+that maintenance of the package would continue or grow
+<https://github.com/ActiveState/appdirs/issues/79>`_, this fork was
+created.
+
+Contributions are most welcome.
--- /dev/null
+platformdirs-2.6.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+platformdirs-2.6.2.dist-info/METADATA,sha256=rDoFsb9-2tVym02IIeYCoKgGaCpY2v8xw8WWXywxhIM,9502\r
+platformdirs-2.6.2.dist-info/RECORD,,\r
+platformdirs-2.6.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+platformdirs-2.6.2.dist-info/WHEEL,sha256=NaLmgHHW_f9jTvv_wRh9vcK7c7EK9o5fwsIXMOzoGgM,87\r
+platformdirs-2.6.2.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089\r
+platformdirs/__init__.py,sha256=td0a-fHENmnG8ess2WRoysKv9ud5j6TQ-p_iUM_uE18,12864\r
+platformdirs/__main__.py,sha256=VsC0t5m-6f0YVr96PVks93G3EDF8MSNY4KpUMvPahDA,1164\r
+platformdirs/__pycache__/__init__.cpython-311.pyc,,\r
+platformdirs/__pycache__/__main__.cpython-311.pyc,,\r
+platformdirs/__pycache__/android.cpython-311.pyc,,\r
+platformdirs/__pycache__/api.cpython-311.pyc,,\r
+platformdirs/__pycache__/macos.cpython-311.pyc,,\r
+platformdirs/__pycache__/unix.cpython-311.pyc,,\r
+platformdirs/__pycache__/version.cpython-311.pyc,,\r
+platformdirs/__pycache__/windows.cpython-311.pyc,,\r
+platformdirs/android.py,sha256=GKizhyS7ESRiU67u8UnBJLm46goau9937EchXWbPBlk,4068\r
+platformdirs/api.py,sha256=MXKHXOL3eh_-trSok-JUTjAR_zjmmKF3rjREVABjP8s,4910\r
+platformdirs/macos.py,sha256=-3UXQewbT0yMhMdkzRXfXGAntmLIH7Qt4a9Hlf8I5_Y,2655\r
+platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+platformdirs/unix.py,sha256=P-WQjSSieE38DXjMDa1t4XHnKJQ5idEaKT0PyXwm8KQ,6911\r
+platformdirs/version.py,sha256=qaN-fw_htIgKUVXoAuAEVgKxQu3tZ9qE2eiKkWIS7LA,160\r
+platformdirs/windows.py,sha256=LOrXLgI0CjQldDo2zhOZYGYZ6g4e_cJOCB_pF9aMRWQ,6596\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: hatchling 1.11.1
+Root-Is-Purelib: true
+Tag: py3-none-any
--- /dev/null
+MIT License
+
+Copyright (c) 2010-202x The platformdirs developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+"""
+Utilities for determining application-specific dirs. See <https://github.com/platformdirs/platformdirs> for details and
+usage.
+"""
+from __future__ import annotations
+
+import os
+import sys
+from pathlib import Path
+
+if sys.version_info >= (3, 8): # pragma: no cover (py38+)
+ from typing import Literal
+else: # pragma: no cover (py38+)
+ from ..typing_extensions import Literal
+
+from .api import PlatformDirsABC
+from .version import __version__
+from .version import __version_tuple__ as __version_info__
+
+
+def _set_platform_dir_class() -> type[PlatformDirsABC]:
+ if sys.platform == "win32":
+ from .windows import Windows as Result
+ elif sys.platform == "darwin":
+ from .macos import MacOS as Result
+ else:
+ from .unix import Unix as Result
+
+ if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
+
+ if os.getenv("SHELL") or os.getenv("PREFIX"):
+ return Result
+
+ from .android import _android_folder
+
+ if _android_folder() is not None:
+ from .android import Android
+
+ return Android # return to avoid redefinition of result
+
+ return Result
+
+
+PlatformDirs = _set_platform_dir_class() #: Currently active platform
+AppDirs = PlatformDirs #: Backwards compatibility with appdirs
+
+
+def user_data_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
+ :returns: data directory tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_dir
+
+
+def site_data_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ multipath: bool = False,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`.
+ :returns: data directory shared by users
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_dir
+
+
+def user_config_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
+ :returns: config directory tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_dir
+
+
+def site_config_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ multipath: bool = False,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`.
+ :returns: config directory shared by the users
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_dir
+
+
+def user_cache_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ opinion: bool = True,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
+ :returns: cache directory tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_dir
+
+
+def user_state_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
+ :returns: state directory tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_dir
+
+
+def user_log_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ opinion: bool = True,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
+ :returns: log directory tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir
+
+
+def user_documents_dir() -> str:
+ """
+ :returns: documents directory tied to the user
+ """
+ return PlatformDirs().user_documents_dir
+
+
+def user_runtime_dir(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ opinion: bool = True,
+) -> str:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
+ :returns: runtime directory tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_dir
+
+
+def user_data_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
+ :returns: data path tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path
+
+
+def site_data_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ multipath: bool = False,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
+ :returns: data path shared by users
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path
+
+
+def user_config_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
+ :returns: config path tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path
+
+
+def site_config_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ multipath: bool = False,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`.
+ :returns: config path shared by the users
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path
+
+
+def user_cache_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ opinion: bool = True,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
+ :returns: cache path tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path
+
+
+def user_state_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
+ :returns: state path tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path
+
+
+def user_log_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ opinion: bool = True,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
+ :returns: log path tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path
+
+
+def user_documents_path() -> Path:
+ """
+ :returns: documents path tied to the user
+ """
+ return PlatformDirs().user_documents_path
+
+
+def user_runtime_path(
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ opinion: bool = True,
+) -> Path:
+ """
+ :param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
+ :returns: runtime path tied to the user
+ """
+ return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_path
+
+
+__all__ = [
+ "__version__",
+ "__version_info__",
+ "PlatformDirs",
+ "AppDirs",
+ "PlatformDirsABC",
+ "user_data_dir",
+ "user_config_dir",
+ "user_cache_dir",
+ "user_state_dir",
+ "user_log_dir",
+ "user_documents_dir",
+ "user_runtime_dir",
+ "site_data_dir",
+ "site_config_dir",
+ "user_data_path",
+ "user_config_path",
+ "user_cache_path",
+ "user_state_path",
+ "user_log_path",
+ "user_documents_path",
+ "user_runtime_path",
+ "site_data_path",
+ "site_config_path",
+]
--- /dev/null
+from __future__ import annotations
+
+from platformdirs import PlatformDirs, __version__
+
+PROPS = (
+ "user_data_dir",
+ "user_config_dir",
+ "user_cache_dir",
+ "user_state_dir",
+ "user_log_dir",
+ "user_documents_dir",
+ "user_runtime_dir",
+ "site_data_dir",
+ "site_config_dir",
+)
+
+
+def main() -> None:
+ app_name = "MyApp"
+ app_author = "MyCompany"
+
+ print(f"-- platformdirs {__version__} --")
+
+ print("-- app dirs (with optional 'version')")
+ dirs = PlatformDirs(app_name, app_author, version="1.0")
+ for prop in PROPS:
+ print(f"{prop}: {getattr(dirs, prop)}")
+
+ print("\n-- app dirs (without optional 'version')")
+ dirs = PlatformDirs(app_name, app_author)
+ for prop in PROPS:
+ print(f"{prop}: {getattr(dirs, prop)}")
+
+ print("\n-- app dirs (without optional 'appauthor')")
+ dirs = PlatformDirs(app_name)
+ for prop in PROPS:
+ print(f"{prop}: {getattr(dirs, prop)}")
+
+ print("\n-- app dirs (with disabled 'appauthor')")
+ dirs = PlatformDirs(app_name, appauthor=False)
+ for prop in PROPS:
+ print(f"{prop}: {getattr(dirs, prop)}")
+
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+from __future__ import annotations
+
+import os
+import re
+import sys
+from functools import lru_cache
+from typing import cast
+
+from .api import PlatformDirsABC
+
+
+class Android(PlatformDirsABC):
+ """
+ Follows the guidance `from here <https://android.stackexchange.com/a/216132>`_. Makes use of the
+ `appname <platformdirs.api.PlatformDirsABC.appname>` and
+ `version <platformdirs.api.PlatformDirsABC.version>`.
+ """
+
+ @property
+ def user_data_dir(self) -> str:
+ """:return: data directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/files/<AppName>``"""
+ return self._append_app_name_and_version(cast(str, _android_folder()), "files")
+
+ @property
+ def site_data_dir(self) -> str:
+ """:return: data directory shared by users, same as `user_data_dir`"""
+ return self.user_data_dir
+
+ @property
+ def user_config_dir(self) -> str:
+ """
+ :return: config directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/shared_prefs/<AppName>``
+ """
+ return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs")
+
+ @property
+ def site_config_dir(self) -> str:
+ """:return: config directory shared by the users, same as `user_config_dir`"""
+ return self.user_config_dir
+
+ @property
+ def user_cache_dir(self) -> str:
+ """:return: cache directory tied to the user, e.g. e.g. ``/data/user/<userid>/<packagename>/cache/<AppName>``"""
+ return self._append_app_name_and_version(cast(str, _android_folder()), "cache")
+
+ @property
+ def user_state_dir(self) -> str:
+ """:return: state directory tied to the user, same as `user_data_dir`"""
+ return self.user_data_dir
+
+ @property
+ def user_log_dir(self) -> str:
+ """
+ :return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it,
+ e.g. ``/data/user/<userid>/<packagename>/cache/<AppName>/log``
+ """
+ path = self.user_cache_dir
+ if self.opinion:
+ path = os.path.join(path, "log")
+ return path
+
+ @property
+ def user_documents_dir(self) -> str:
+ """
+ :return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``
+ """
+ return _android_documents_folder()
+
+ @property
+ def user_runtime_dir(self) -> str:
+ """
+ :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it,
+ e.g. ``/data/user/<userid>/<packagename>/cache/<AppName>/tmp``
+ """
+ path = self.user_cache_dir
+ if self.opinion:
+ path = os.path.join(path, "tmp")
+ return path
+
+
+@lru_cache(maxsize=1)
+def _android_folder() -> str | None:
+ """:return: base folder for the Android OS or None if cannot be found"""
+ try:
+ # First try to get path to android app via pyjnius
+ from jnius import autoclass
+
+ Context = autoclass("android.content.Context") # noqa: N806
+ result: str | None = Context.getFilesDir().getParentFile().getAbsolutePath()
+ except Exception:
+ # if fails find an android folder looking path on the sys.path
+ pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
+ for path in sys.path:
+ if pattern.match(path):
+ result = path.split("/files")[0]
+ break
+ else:
+ result = None
+ return result
+
+
+@lru_cache(maxsize=1)
+def _android_documents_folder() -> str:
+ """:return: documents folder for the Android OS"""
+ # Get directories with pyjnius
+ try:
+ from jnius import autoclass
+
+ Context = autoclass("android.content.Context") # noqa: N806
+ Environment = autoclass("android.os.Environment") # noqa: N806
+ documents_dir: str = Context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
+ except Exception:
+ documents_dir = "/storage/emulated/0/Documents"
+
+ return documents_dir
+
+
+__all__ = [
+ "Android",
+]
--- /dev/null
+from __future__ import annotations
+
+import os
+import sys
+from abc import ABC, abstractmethod
+from pathlib import Path
+
+if sys.version_info >= (3, 8): # pragma: no branch
+ from typing import Literal # pragma: no cover
+
+
+class PlatformDirsABC(ABC):
+ """
+ Abstract base class for platform directories.
+ """
+
+ def __init__(
+ self,
+ appname: str | None = None,
+ appauthor: str | None | Literal[False] = None,
+ version: str | None = None,
+ roaming: bool = False,
+ multipath: bool = False,
+ opinion: bool = True,
+ ):
+ """
+ Create a new platform directory.
+
+ :param appname: See `appname`.
+ :param appauthor: See `appauthor`.
+ :param version: See `version`.
+ :param roaming: See `roaming`.
+ :param multipath: See `multipath`.
+ :param opinion: See `opinion`.
+ """
+ self.appname = appname #: The name of application.
+ self.appauthor = appauthor
+ """
+ The name of the app author or distributing body for this application. Typically, it is the owning company name.
+ Defaults to `appname`. You may pass ``False`` to disable it.
+ """
+ self.version = version
+ """
+ An optional version path element to append to the path. You might want to use this if you want multiple versions
+ of your app to be able to run independently. If used, this would typically be ``<major>.<minor>``.
+ """
+ self.roaming = roaming
+ """
+ Whether to use the roaming appdata directory on Windows. That means that for users on a Windows network setup
+ for roaming profiles, this user data will be synced on login (see
+ `here <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>`_).
+ """
+ self.multipath = multipath
+ """
+ An optional parameter only applicable to Unix/Linux which indicates that the entire list of data dirs should be
+ returned. By default, the first item would only be returned.
+ """
+ self.opinion = opinion #: A flag to indicating to use opinionated values.
+
+ def _append_app_name_and_version(self, *base: str) -> str:
+ params = list(base[1:])
+ if self.appname:
+ params.append(self.appname)
+ if self.version:
+ params.append(self.version)
+ return os.path.join(base[0], *params)
+
+ @property
+ @abstractmethod
+ def user_data_dir(self) -> str:
+ """:return: data directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def site_data_dir(self) -> str:
+ """:return: data directory shared by users"""
+
+ @property
+ @abstractmethod
+ def user_config_dir(self) -> str:
+ """:return: config directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def site_config_dir(self) -> str:
+ """:return: config directory shared by the users"""
+
+ @property
+ @abstractmethod
+ def user_cache_dir(self) -> str:
+ """:return: cache directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_state_dir(self) -> str:
+ """:return: state directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_log_dir(self) -> str:
+ """:return: log directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_documents_dir(self) -> str:
+ """:return: documents directory tied to the user"""
+
+ @property
+ @abstractmethod
+ def user_runtime_dir(self) -> str:
+ """:return: runtime directory tied to the user"""
+
+ @property
+ def user_data_path(self) -> Path:
+ """:return: data path tied to the user"""
+ return Path(self.user_data_dir)
+
+ @property
+ def site_data_path(self) -> Path:
+ """:return: data path shared by users"""
+ return Path(self.site_data_dir)
+
+ @property
+ def user_config_path(self) -> Path:
+ """:return: config path tied to the user"""
+ return Path(self.user_config_dir)
+
+ @property
+ def site_config_path(self) -> Path:
+ """:return: config path shared by the users"""
+ return Path(self.site_config_dir)
+
+ @property
+ def user_cache_path(self) -> Path:
+ """:return: cache path tied to the user"""
+ return Path(self.user_cache_dir)
+
+ @property
+ def user_state_path(self) -> Path:
+ """:return: state path tied to the user"""
+ return Path(self.user_state_dir)
+
+ @property
+ def user_log_path(self) -> Path:
+ """:return: log path tied to the user"""
+ return Path(self.user_log_dir)
+
+ @property
+ def user_documents_path(self) -> Path:
+ """:return: documents path tied to the user"""
+ return Path(self.user_documents_dir)
+
+ @property
+ def user_runtime_path(self) -> Path:
+ """:return: runtime path tied to the user"""
+ return Path(self.user_runtime_dir)
--- /dev/null
+from __future__ import annotations
+
+import os
+
+from .api import PlatformDirsABC
+
+
+class MacOS(PlatformDirsABC):
+ """
+ Platform directories for the macOS operating system. Follows the guidance from `Apple documentation
+ <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
+ Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>` and
+ `version <platformdirs.api.PlatformDirsABC.version>`.
+ """
+
+ @property
+ def user_data_dir(self) -> str:
+ """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support/"))
+
+ @property
+ def site_data_dir(self) -> str:
+ """:return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``"""
+ return self._append_app_name_and_version("/Library/Application Support")
+
+ @property
+ def user_config_dir(self) -> str:
+ """:return: config directory tied to the user, e.g. ``~/Library/Preferences/$appname/$version``"""
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences/"))
+
+ @property
+ def site_config_dir(self) -> str:
+ """:return: config directory shared by the users, e.g. ``/Library/Preferences/$appname``"""
+ return self._append_app_name_and_version("/Library/Preferences")
+
+ @property
+ def user_cache_dir(self) -> str:
+ """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))
+
+ @property
+ def user_state_dir(self) -> str:
+ """:return: state directory tied to the user, same as `user_data_dir`"""
+ return self.user_data_dir
+
+ @property
+ def user_log_dir(self) -> str:
+ """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs"))
+
+ @property
+ def user_documents_dir(self) -> str:
+ """:return: documents directory tied to the user, e.g. ``~/Documents``"""
+ return os.path.expanduser("~/Documents")
+
+ @property
+ def user_runtime_dir(self) -> str:
+ """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems"))
+
+
+__all__ = [
+ "MacOS",
+]
--- /dev/null
+from __future__ import annotations
+
+import os
+import sys
+from configparser import ConfigParser
+from pathlib import Path
+
+from .api import PlatformDirsABC
+
+if sys.platform.startswith("linux"): # pragma: no branch # no op check, only to please the type checker
+ from os import getuid
+else:
+
+ def getuid() -> int:
+ raise RuntimeError("should only be used on Linux")
+
+
+class Unix(PlatformDirsABC):
+ """
+ On Unix/Linux, we follow the
+ `XDG Basedir Spec <https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_. The spec allows
+ overriding directories with environment variables. The examples show are the default values, alongside the name of
+ the environment variable that overrides them. Makes use of the
+ `appname <platformdirs.api.PlatformDirsABC.appname>`,
+ `version <platformdirs.api.PlatformDirsABC.version>`,
+ `multipath <platformdirs.api.PlatformDirsABC.multipath>`,
+ `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
+ """
+
+ @property
+ def user_data_dir(self) -> str:
+ """
+ :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or
+ ``$XDG_DATA_HOME/$appname/$version``
+ """
+ path = os.environ.get("XDG_DATA_HOME", "")
+ if not path.strip():
+ path = os.path.expanduser("~/.local/share")
+ return self._append_app_name_and_version(path)
+
+ @property
+ def site_data_dir(self) -> str:
+ """
+ :return: data directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>` is
+ enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS
+ path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version``
+ """
+ # XDG default for $XDG_DATA_DIRS; only first, if multipath is False
+ path = os.environ.get("XDG_DATA_DIRS", "")
+ if not path.strip():
+ path = f"/usr/local/share{os.pathsep}/usr/share"
+ return self._with_multi_path(path)
+
+ def _with_multi_path(self, path: str) -> str:
+ path_list = path.split(os.pathsep)
+ if not self.multipath:
+ path_list = path_list[0:1]
+ path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list]
+ return os.pathsep.join(path_list)
+
+ @property
+ def user_config_dir(self) -> str:
+ """
+ :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or
+ ``$XDG_CONFIG_HOME/$appname/$version``
+ """
+ path = os.environ.get("XDG_CONFIG_HOME", "")
+ if not path.strip():
+ path = os.path.expanduser("~/.config")
+ return self._append_app_name_and_version(path)
+
+ @property
+ def site_config_dir(self) -> str:
+ """
+ :return: config directories shared by users (if `multipath <platformdirs.api.PlatformDirsABC.multipath>`
+ is enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS
+ path separator), e.g. ``/etc/xdg/$appname/$version``
+ """
+ # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False
+ path = os.environ.get("XDG_CONFIG_DIRS", "")
+ if not path.strip():
+ path = "/etc/xdg"
+ return self._with_multi_path(path)
+
+ @property
+ def user_cache_dir(self) -> str:
+ """
+ :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or
+ ``~/$XDG_CACHE_HOME/$appname/$version``
+ """
+ path = os.environ.get("XDG_CACHE_HOME", "")
+ if not path.strip():
+ path = os.path.expanduser("~/.cache")
+ return self._append_app_name_and_version(path)
+
+ @property
+ def user_state_dir(self) -> str:
+ """
+ :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or
+ ``$XDG_STATE_HOME/$appname/$version``
+ """
+ path = os.environ.get("XDG_STATE_HOME", "")
+ if not path.strip():
+ path = os.path.expanduser("~/.local/state")
+ return self._append_app_name_and_version(path)
+
+ @property
+ def user_log_dir(self) -> str:
+ """
+ :return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it
+ """
+ path = self.user_state_dir
+ if self.opinion:
+ path = os.path.join(path, "log")
+ return path
+
+ @property
+ def user_documents_dir(self) -> str:
+ """
+ :return: documents directory tied to the user, e.g. ``~/Documents``
+ """
+ documents_dir = _get_user_dirs_folder("XDG_DOCUMENTS_DIR")
+ if documents_dir is None:
+ documents_dir = os.environ.get("XDG_DOCUMENTS_DIR", "").strip()
+ if not documents_dir:
+ documents_dir = os.path.expanduser("~/Documents")
+
+ return documents_dir
+
+ @property
+ def user_runtime_dir(self) -> str:
+ """
+ :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or
+ ``$XDG_RUNTIME_DIR/$appname/$version``
+ """
+ path = os.environ.get("XDG_RUNTIME_DIR", "")
+ if not path.strip():
+ path = f"/run/user/{getuid()}"
+ return self._append_app_name_and_version(path)
+
+ @property
+ def site_data_path(self) -> Path:
+ """:return: data path shared by users. Only return first item, even if ``multipath`` is set to ``True``"""
+ return self._first_item_as_path_if_multipath(self.site_data_dir)
+
+ @property
+ def site_config_path(self) -> Path:
+ """:return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``"""
+ return self._first_item_as_path_if_multipath(self.site_config_dir)
+
+ def _first_item_as_path_if_multipath(self, directory: str) -> Path:
+ if self.multipath:
+ # If multipath is True, the first path is returned.
+ directory = directory.split(os.pathsep)[0]
+ return Path(directory)
+
+
+def _get_user_dirs_folder(key: str) -> str | None:
+ """Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/"""
+ user_dirs_config_path = os.path.join(Unix().user_config_dir, "user-dirs.dirs")
+ if os.path.exists(user_dirs_config_path):
+ parser = ConfigParser()
+
+ with open(user_dirs_config_path) as stream:
+ # Add fake section header, so ConfigParser doesn't complain
+ parser.read_string(f"[top]\n{stream.read()}")
+
+ if key not in parser["top"]:
+ return None
+
+ path = parser["top"][key].strip('"')
+ # Handle relative home paths
+ path = path.replace("$HOME", os.path.expanduser("~"))
+ return path
+
+ return None
+
+
+__all__ = [
+ "Unix",
+]
--- /dev/null
+# file generated by setuptools_scm
+# don't change, don't track in version control
+__version__ = version = '2.6.2'
+__version_tuple__ = version_tuple = (2, 6, 2)
--- /dev/null
+from __future__ import annotations
+
+import ctypes
+import os
+import sys
+from functools import lru_cache
+from typing import Callable
+
+from .api import PlatformDirsABC
+
+
+class Windows(PlatformDirsABC):
+ """`MSDN on where to store app data files
+ <http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120>`_.
+ Makes use of the
+ `appname <platformdirs.api.PlatformDirsABC.appname>`,
+ `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`,
+ `version <platformdirs.api.PlatformDirsABC.version>`,
+ `roaming <platformdirs.api.PlatformDirsABC.roaming>`,
+ `opinion <platformdirs.api.PlatformDirsABC.opinion>`."""
+
+ @property
+ def user_data_dir(self) -> str:
+ """
+ :return: data directory tied to the user, e.g.
+ ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or
+ ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming)
+ """
+ const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA"
+ path = os.path.normpath(get_win_folder(const))
+ return self._append_parts(path)
+
+ def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str:
+ params = []
+ if self.appname:
+ if self.appauthor is not False:
+ author = self.appauthor or self.appname
+ params.append(author)
+ params.append(self.appname)
+ if opinion_value is not None and self.opinion:
+ params.append(opinion_value)
+ if self.version:
+ params.append(self.version)
+ return os.path.join(path, *params)
+
+ @property
+ def site_data_dir(self) -> str:
+ """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``"""
+ path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
+ return self._append_parts(path)
+
+ @property
+ def user_config_dir(self) -> str:
+ """:return: config directory tied to the user, same as `user_data_dir`"""
+ return self.user_data_dir
+
+ @property
+ def site_config_dir(self) -> str:
+ """:return: config directory shared by the users, same as `site_data_dir`"""
+ return self.site_data_dir
+
+ @property
+ def user_cache_dir(self) -> str:
+ """
+ :return: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g.
+ ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version``
+ """
+ path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA"))
+ return self._append_parts(path, opinion_value="Cache")
+
+ @property
+ def user_state_dir(self) -> str:
+ """:return: state directory tied to the user, same as `user_data_dir`"""
+ return self.user_data_dir
+
+ @property
+ def user_log_dir(self) -> str:
+ """
+ :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it
+ """
+ path = self.user_data_dir
+ if self.opinion:
+ path = os.path.join(path, "Logs")
+ return path
+
+ @property
+ def user_documents_dir(self) -> str:
+ """
+ :return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``
+ """
+ return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
+
+ @property
+ def user_runtime_dir(self) -> str:
+ """
+ :return: runtime directory tied to the user, e.g.
+ ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname``
+ """
+ path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp"))
+ return self._append_parts(path)
+
+
+def get_win_folder_from_env_vars(csidl_name: str) -> str:
+ """Get folder from environment variables."""
+ if csidl_name == "CSIDL_PERSONAL": # does not have an environment name
+ return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents")
+
+ env_var_name = {
+ "CSIDL_APPDATA": "APPDATA",
+ "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE",
+ "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
+ }.get(csidl_name)
+ if env_var_name is None:
+ raise ValueError(f"Unknown CSIDL name: {csidl_name}")
+ result = os.environ.get(env_var_name)
+ if result is None:
+ raise ValueError(f"Unset environment variable: {env_var_name}")
+ return result
+
+
+def get_win_folder_from_registry(csidl_name: str) -> str:
+ """Get folder from the registry.
+
+ This is a fallback technique at best. I'm not sure if using the
+ registry for this guarantees us the correct answer for all CSIDL_*
+ names.
+ """
+ shell_folder_name = {
+ "CSIDL_APPDATA": "AppData",
+ "CSIDL_COMMON_APPDATA": "Common AppData",
+ "CSIDL_LOCAL_APPDATA": "Local AppData",
+ "CSIDL_PERSONAL": "Personal",
+ }.get(csidl_name)
+ if shell_folder_name is None:
+ raise ValueError(f"Unknown CSIDL name: {csidl_name}")
+ if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows
+ raise NotImplementedError
+ import winreg
+
+ key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
+ directory, _ = winreg.QueryValueEx(key, shell_folder_name)
+ return str(directory)
+
+
+def get_win_folder_via_ctypes(csidl_name: str) -> str:
+ """Get folder with ctypes."""
+ csidl_const = {
+ "CSIDL_APPDATA": 26,
+ "CSIDL_COMMON_APPDATA": 35,
+ "CSIDL_LOCAL_APPDATA": 28,
+ "CSIDL_PERSONAL": 5,
+ }.get(csidl_name)
+ if csidl_const is None:
+ raise ValueError(f"Unknown CSIDL name: {csidl_name}")
+
+ buf = ctypes.create_unicode_buffer(1024)
+ windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker
+ windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
+
+ # Downgrade to short path name if it has highbit chars.
+ if any(ord(c) > 255 for c in buf):
+ buf2 = ctypes.create_unicode_buffer(1024)
+ if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
+ buf = buf2
+
+ return buf.value
+
+
+def _pick_get_win_folder() -> Callable[[str], str]:
+ if hasattr(ctypes, "windll"):
+ return get_win_folder_via_ctypes
+ try:
+ import winreg # noqa: F401
+ except ImportError:
+ return get_win_folder_from_env_vars
+ else:
+ return get_win_folder_from_registry
+
+
+get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder())
+
+__all__ = [
+ "Windows",
+]
+++ /dev/null
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+++ /dev/null
-Metadata-Version: 2.1
-Name: pyparsing
-Version: 3.0.8
-Summary: pyparsing module - Classes and methods to define and execute parsing grammars
-Author-email: Paul McGuire <ptmcg.gm+pyparsing@gmail.com>
-Requires-Python: >=3.6.8
-Description-Content-Type: text/x-rst
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Intended Audience :: Information Technology
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Typing :: Typed
-Requires-Dist: railroad-diagrams ; extra == "diagrams"
-Requires-Dist: jinja2 ; extra == "diagrams"
-Project-URL: Homepage, https://github.com/pyparsing/pyparsing/
-Provides-Extra: diagrams
-
-PyParsing -- A Python Parsing Module
-====================================
-
-|Build Status| |Coverage|
-
-Introduction
-============
-
-The pyparsing module is an alternative approach to creating and
-executing simple grammars, vs. the traditional lex/yacc approach, or the
-use of regular expressions. The pyparsing module provides a library of
-classes that client code uses to construct the grammar directly in
-Python code.
-
-*[Since first writing this description of pyparsing in late 2003, this
-technique for developing parsers has become more widespread, under the
-name Parsing Expression Grammars - PEGs. See more information on PEGs*
-`here <https://en.wikipedia.org/wiki/Parsing_expression_grammar>`__
-*.]*
-
-Here is a program to parse ``"Hello, World!"`` (or any greeting of the form
-``"salutation, addressee!"``):
-
-.. code:: python
-
- from pyparsing import Word, alphas
- greet = Word(alphas) + "," + Word(alphas) + "!"
- hello = "Hello, World!"
- print(hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
- Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the
-self-explanatory class names, and the use of '+', '|' and '^' operator
-definitions.
-
-The parsed results returned from ``parseString()`` is a collection of type
-``ParseResults``, which can be accessed as a
-nested list, a dictionary, or an object with named attributes.
-
-The pyparsing module handles some of the problems that are typically
-vexing when writing text parsers:
-
-- extra or missing whitespace (the above program will also handle ``"Hello,World!"``, ``"Hello , World !"``, etc.)
-- quoted strings
-- embedded comments
-
-The examples directory includes a simple SQL parser, simple CORBA IDL
-parser, a config file parser, a chemical formula parser, and a four-
-function algebraic notation parser, among many others.
-
-Documentation
-=============
-
-There are many examples in the online docstrings of the classes
-and methods in pyparsing. You can find them compiled into `online docs <https://pyparsing-docs.readthedocs.io/en/latest/>`__. Additional
-documentation resources and project info are listed in the online
-`GitHub wiki <https://github.com/pyparsing/pyparsing/wiki>`__. An
-entire directory of examples can be found `here <https://github.com/pyparsing/pyparsing/tree/master/examples>`__.
-
-License
-=======
-
-MIT License. See header of the `pyparsing.py <https://github.com/pyparsing/pyparsing/blob/master/pyparsing/__init__.py#L1-L23>`__ file.
-
-History
-=======
-
-See `CHANGES <https://github.com/pyparsing/pyparsing/blob/master/CHANGES>`__ file.
-
-.. |Build Status| image:: https://github.com/pyparsing/pyparsing/actions/workflows/ci.yml/badge.svg
- :target: https://github.com/pyparsing/pyparsing/actions/workflows/ci.yml
-.. |Coverage| image:: https://codecov.io/gh/pyparsing/pyparsing/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/pyparsing/pyparsing
-
+++ /dev/null
-pyparsing-3.0.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-pyparsing-3.0.8.dist-info/LICENSE,sha256=ENUSChaAWAT_2otojCIL-06POXQbVzIGBNRVowngGXI,1023
-pyparsing-3.0.8.dist-info/METADATA,sha256=dEvZBGz3Owm5LYEaqDeKb6e3ZgOrF48WaCI_PG1n5BE,4207
-pyparsing-3.0.8.dist-info/RECORD,,
-pyparsing-3.0.8.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-pyparsing-3.0.8.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81
-pyparsing/__init__.py,sha256=EMa1HCuq9HJhEDR8fUThu2gD0nl6Cs8FFEWZZ0eRCM8,9159
-pyparsing/__pycache__/__init__.cpython-38.pyc,,
-pyparsing/__pycache__/actions.cpython-38.pyc,,
-pyparsing/__pycache__/common.cpython-38.pyc,,
-pyparsing/__pycache__/core.cpython-38.pyc,,
-pyparsing/__pycache__/exceptions.cpython-38.pyc,,
-pyparsing/__pycache__/helpers.cpython-38.pyc,,
-pyparsing/__pycache__/results.cpython-38.pyc,,
-pyparsing/__pycache__/testing.cpython-38.pyc,,
-pyparsing/__pycache__/unicode.cpython-38.pyc,,
-pyparsing/__pycache__/util.cpython-38.pyc,,
-pyparsing/actions.py,sha256=60v7mETOBzc01YPH_qQD5isavgcSJpAfIKpzgjM3vaU,6429
-pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936
-pyparsing/core.py,sha256=zBzGw5vcSd58pB1QkYpY6O_XCcHVKX_nH5xglRx_L-M,213278
-pyparsing/diagram/__init__.py,sha256=oU_UEh6O5voKSFjUdq462_mpmURLOfUIsmWvxi1qgTQ,23003
-pyparsing/diagram/__pycache__/__init__.cpython-38.pyc,,
-pyparsing/diagram/template.jinja2,sha256=SfQ8SLktSBqI5W1DGcUVH1vdflRD6x2sQBApxrcNg7s,589
-pyparsing/exceptions.py,sha256=H4D9gqMavqmAFSsdrU_J6bO-jA-T-A7yvtXWZpooIUA,9030
-pyparsing/helpers.py,sha256=EyjpgDOc3ivwRsU4VXxAWdgIs5gaqMDaLWcwRh5mqxc,39007
-pyparsing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-pyparsing/results.py,sha256=Hd6FAAh5sF8zGXpwsamdVqFUblIwyQf0FH0t7FCb1OY,25353
-pyparsing/testing.py,sha256=szs8AKZREZMhL0y0vsMfaTVAnpqPHetg6VKJBNmc4QY,13388
-pyparsing/unicode.py,sha256=IR-ioeGY29cZ49tG8Ts7ITPWWNP5G2DcZs58oa8zn44,10381
-pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805
+++ /dev/null
-Wheel-Version: 1.0
-Generator: flit 3.6.0
-Root-Is-Purelib: true
-Tag: py3-none-any
-pyparsing-3.0.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-pyparsing-3.0.9.dist-info/LICENSE,sha256=ENUSChaAWAT_2otojCIL-06POXQbVzIGBNRVowngGXI,1023
-pyparsing-3.0.9.dist-info/METADATA,sha256=h_fpm9rwvgZsE8v5YNF4IAo-IpaFWCOfUEm5MMByIiM,4207
-pyparsing-3.0.9.dist-info/RECORD,,
-pyparsing-3.0.9.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-pyparsing-3.0.9.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81
-pyparsing/__init__.py,sha256=52QH3lgPbJhba0estckoGPHRH8JvQSSCGoWiEn2m0bU,9159
-pyparsing/__pycache__/__init__.cpython-38.pyc,,
-pyparsing/__pycache__/actions.cpython-38.pyc,,
-pyparsing/__pycache__/common.cpython-38.pyc,,
-pyparsing/__pycache__/core.cpython-38.pyc,,
-pyparsing/__pycache__/exceptions.cpython-38.pyc,,
-pyparsing/__pycache__/helpers.cpython-38.pyc,,
-pyparsing/__pycache__/results.cpython-38.pyc,,
-pyparsing/__pycache__/testing.cpython-38.pyc,,
-pyparsing/__pycache__/unicode.cpython-38.pyc,,
-pyparsing/__pycache__/util.cpython-38.pyc,,
-pyparsing/actions.py,sha256=wU9i32e0y1ymxKE3OUwSHO-SFIrt1h_wv6Ws0GQjpNU,6426
-pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936
-pyparsing/core.py,sha256=u8GptQE_H6wMkl8OZhxeK1aAPIDXXNgwdShORBwBVS4,213310
-pyparsing/diagram/__init__.py,sha256=f_EfxahqrdkRVahmTwLJXkZ9EEDKNd-O7lBbpJYlE1g,23668
-pyparsing/diagram/__pycache__/__init__.cpython-38.pyc,,
-pyparsing/exceptions.py,sha256=3LbSafD32NYb1Tzt85GHNkhEAU1eZkTtNSk24cPMemo,9023
-pyparsing/helpers.py,sha256=QpUOjW0-psvueMwWb9bQpU2noqKCv98_wnw1VSzSdVo,39129
-pyparsing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sGA30,25341
-pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402
-pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787
-pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805
+pyparsing-3.0.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+pyparsing-3.0.9.dist-info/LICENSE,sha256=ENUSChaAWAT_2otojCIL-06POXQbVzIGBNRVowngGXI,1023\r
+pyparsing-3.0.9.dist-info/METADATA,sha256=h_fpm9rwvgZsE8v5YNF4IAo-IpaFWCOfUEm5MMByIiM,4207\r
+pyparsing-3.0.9.dist-info/RECORD,,\r
+pyparsing-3.0.9.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+pyparsing-3.0.9.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81\r
+pyparsing/__init__.py,sha256=52QH3lgPbJhba0estckoGPHRH8JvQSSCGoWiEn2m0bU,9159\r
+pyparsing/__pycache__/__init__.cpython-311.pyc,,\r
+pyparsing/__pycache__/actions.cpython-311.pyc,,\r
+pyparsing/__pycache__/common.cpython-311.pyc,,\r
+pyparsing/__pycache__/core.cpython-311.pyc,,\r
+pyparsing/__pycache__/exceptions.cpython-311.pyc,,\r
+pyparsing/__pycache__/helpers.cpython-311.pyc,,\r
+pyparsing/__pycache__/results.cpython-311.pyc,,\r
+pyparsing/__pycache__/testing.cpython-311.pyc,,\r
+pyparsing/__pycache__/unicode.cpython-311.pyc,,\r
+pyparsing/__pycache__/util.cpython-311.pyc,,\r
+pyparsing/actions.py,sha256=wU9i32e0y1ymxKE3OUwSHO-SFIrt1h_wv6Ws0GQjpNU,6426\r
+pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936\r
+pyparsing/core.py,sha256=u8GptQE_H6wMkl8OZhxeK1aAPIDXXNgwdShORBwBVS4,213310\r
+pyparsing/diagram/__init__.py,sha256=f_EfxahqrdkRVahmTwLJXkZ9EEDKNd-O7lBbpJYlE1g,23668\r
+pyparsing/diagram/__pycache__/__init__.cpython-311.pyc,,\r
+pyparsing/exceptions.py,sha256=3LbSafD32NYb1Tzt85GHNkhEAU1eZkTtNSk24cPMemo,9023\r
+pyparsing/helpers.py,sha256=QpUOjW0-psvueMwWb9bQpU2noqKCv98_wnw1VSzSdVo,39129\r
+pyparsing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sGA30,25341\r
+pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402\r
+pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787\r
+pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805\r
--- /dev/null
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC. Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team. In October of the same
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation. In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
+
+All Python releases are Open Source (see http://www.opensource.org for
+the Open Source Definition). Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+ Release Derived Year Owner GPL-
+ from compatible? (1)
+
+ 0.9.0 thru 1.2 1991-1995 CWI yes
+ 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
+ 1.6 1.5.2 2000 CNRI no
+ 2.0 1.6 2000 BeOpen.com no
+ 1.6.1 1.6 2001 CNRI yes (2)
+ 2.1 2.0+1.6.1 2001 PSF no
+ 2.0.1 2.0+1.6.1 2001 PSF yes
+ 2.1.1 2.1+2.0.1 2001 PSF yes
+ 2.1.2 2.1.1 2002 PSF yes
+ 2.1.3 2.1.2 2002 PSF yes
+ 2.2 and above 2.1.1 2001-now PSF yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+ the GPL. All Python licenses, unlike the GPL, let you distribute
+ a modified version without making your changes open source. The
+ GPL-compatible licenses make it possible to combine Python with
+ other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+ because its license has a choice of law clause. According to
+ CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+ is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
+All Rights Reserved" are retained in Python alone or in any derivative version
+prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions. Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee. This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party. As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee. Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement. This Agreement together with
+Python 1.6.1 may be located on the internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013. This
+Agreement may also be obtained from a proxy server on the internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee. This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+ ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands. All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null
+Metadata-Version: 2.1
+Name: typing_extensions
+Version: 4.4.0
+Summary: Backported and Experimental Type Hints for Python 3.7+
+Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing
+Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" <levkivskyi@gmail.com>
+Requires-Python: >=3.7
+Description-Content-Type: text/markdown
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Topic :: Software Development
+Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues
+Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md
+Project-URL: Documentation, https://typing.readthedocs.io/
+Project-URL: Home, https://github.com/python/typing_extensions
+Project-URL: Q & A, https://github.com/python/typing/discussions
+Project-URL: Repository, https://github.com/python/typing_extensions
+
+# Typing Extensions
+
+[](https://gitter.im/python/typing)
+
+## Overview
+
+The `typing_extensions` module serves two related purposes:
+
+- Enable use of new type system features on older Python versions. For example,
+ `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows
+ users on previous Python versions to use it too.
+- Enable experimentation with new type system PEPs before they are accepted and
+ added to the `typing` module.
+
+New features may be added to `typing_extensions` as soon as they are specified
+in a PEP that has been added to the [python/peps](https://github.com/python/peps)
+repository. If the PEP is accepted, the feature will then be added to `typing`
+for the next CPython release. No typing PEP has been rejected so far, so we
+haven't yet figured out how to deal with that possibility.
+
+Starting with version 4.0.0, `typing_extensions` uses
+[Semantic Versioning](https://semver.org/). The
+major version is incremented for all backwards-incompatible changes.
+Therefore, it's safe to depend
+on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,
+where `x.y` is the first version that includes all features you need.
+
+`typing_extensions` supports Python versions 3.7 and higher. In the future,
+support for older Python versions will be dropped some time after that version
+reaches end of life.
+
+## Included items
+
+This module currently contains the following:
+
+- Experimental features
+
+ - `override` (see PEP 698)
+ - The `default=` argument to `TypeVar`, `ParamSpec`, and `TypeVarTuple` (see PEP 696)
+ - The `infer_variance=` argument to `TypeVar` (see PEP 695)
+
+- In `typing` since Python 3.11
+
+ - `assert_never`
+ - `assert_type`
+ - `clear_overloads`
+ - `@dataclass_transform()` (see PEP 681)
+ - `get_overloads`
+ - `LiteralString` (see PEP 675)
+ - `Never`
+ - `NotRequired` (see PEP 655)
+ - `reveal_type`
+ - `Required` (see PEP 655)
+ - `Self` (see PEP 673)
+ - `TypeVarTuple` (see PEP 646; the `typing_extensions` version supports the `default=` argument from PEP 696)
+ - `Unpack` (see PEP 646)
+
+- In `typing` since Python 3.10
+
+ - `Concatenate` (see PEP 612)
+ - `ParamSpec` (see PEP 612; the `typing_extensions` version supports the `default=` argument from PEP 696)
+ - `ParamSpecArgs` (see PEP 612)
+ - `ParamSpecKwargs` (see PEP 612)
+ - `TypeAlias` (see PEP 613)
+ - `TypeGuard` (see PEP 647)
+ - `is_typeddict`
+
+- In `typing` since Python 3.9
+
+ - `Annotated` (see PEP 593)
+
+- In `typing` since Python 3.8
+
+ - `final` (see PEP 591)
+ - `Final` (see PEP 591)
+ - `Literal` (see PEP 586)
+ - `Protocol` (see PEP 544)
+ - `runtime_checkable` (see PEP 544)
+ - `TypedDict` (see PEP 589)
+ - `get_origin` (`typing_extensions` provides this function only in Python 3.7+)
+ - `get_args` (`typing_extensions` provides this function only in Python 3.7+)
+
+- In `typing` since Python 3.7
+
+ - `OrderedDict`
+
+- In `typing` since Python 3.5 or 3.6 (see [the typing documentation](https://docs.python.org/3.10/library/typing.html) for details)
+
+ - `AsyncContextManager`
+ - `AsyncGenerator`
+ - `AsyncIterable`
+ - `AsyncIterator`
+ - `Awaitable`
+ - `ChainMap`
+ - `ClassVar` (see PEP 526)
+ - `ContextManager`
+ - `Coroutine`
+ - `Counter`
+ - `DefaultDict`
+ - `Deque`
+ - `NewType`
+ - `NoReturn`
+ - `overload`
+ - `Text`
+ - `Type`
+ - `TYPE_CHECKING`
+ - `get_type_hints`
+
+- The following have always been present in `typing`, but the `typing_extensions` versions provide
+ additional features:
+
+ - `Any` (supports inheritance since Python 3.11)
+ - `NamedTuple` (supports multiple inheritance with `Generic` since Python 3.11)
+ - `TypeVar` (see PEPs 695 and 696)
+
+# Other Notes and Limitations
+
+Certain objects were changed after they were added to `typing`, and
+`typing_extensions` provides a backport even on newer Python versions:
+
+- `TypedDict` does not store runtime information
+ about which (if any) keys are non-required in Python 3.8, and does not
+ honor the `total` keyword with old-style `TypedDict()` in Python
+ 3.9.0 and 3.9.1. `TypedDict` also does not support multiple inheritance
+ with `typing.Generic` on Python <3.11.
+- `get_origin` and `get_args` lack support for `Annotated` in
+ Python 3.8 and lack support for `ParamSpecArgs` and `ParamSpecKwargs`
+ in 3.9.
+- `@final` was changed in Python 3.11 to set the `.__final__` attribute.
+- `@overload` was changed in Python 3.11 to make function overloads
+ introspectable at runtime. In order to access overloads with
+ `typing_extensions.get_overloads()`, you must use
+ `@typing_extensions.overload`.
+- `NamedTuple` was changed in Python 3.11 to allow for multiple inheritance
+ with `typing.Generic`.
+- Since Python 3.11, it has been possible to inherit from `Any` at
+ runtime. `typing_extensions.Any` also provides this capability.
+- `TypeVar` gains two additional parameters, `default=` and `infer_variance=`,
+ in the draft PEPs 695 and 696, which are being considered for inclusion
+ in Python 3.12.
+
+There are a few types whose interface was modified between different
+versions of typing. For example, `typing.Sequence` was modified to
+subclass `typing.Reversible` as of Python 3.5.3.
+
+These changes are _not_ backported to prevent subtle compatibility
+issues when mixing the differing implementations of modified classes.
+
+Certain types have incorrect runtime behavior due to limitations of older
+versions of the typing module:
+
+- `ParamSpec` and `Concatenate` will not work with `get_args` and
+ `get_origin`. Certain PEP 612 special cases in user-defined
+ `Generic`s are also not available.
+
+These types are only guaranteed to work for static type checking.
+
+## Running tests
+
+To run tests, navigate into the appropriate source directory and run
+`test_typing_extensions.py`.
+
--- /dev/null
+__pycache__/typing_extensions.cpython-311.pyc,,\r
+typing_extensions-4.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+typing_extensions-4.4.0.dist-info/LICENSE,sha256=x6-2XnVXB7n7kEhziaF20-09ADHVExr95FwjcV_16JE,12787\r
+typing_extensions-4.4.0.dist-info/METADATA,sha256=1zSh1eMLnLkLMMC6aZSGRKx3eRnivEGDFWGSVD1zqhA,7249\r
+typing_extensions-4.4.0.dist-info/RECORD,,\r
+typing_extensions-4.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+typing_extensions-4.4.0.dist-info/WHEEL,sha256=4TfKIB_xu-04bc2iKz6_zFt-gEFEEDU_31HGhqzOCE8,81\r
+typing_extensions.py,sha256=ipqWiq5AHzrwczt6c26AP05Llh6a5_GaXRpOBqbogHA,80078\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: flit 3.7.1
+Root-Is-Purelib: true
+Tag: py3-none-any
--- /dev/null
+import abc
+import collections
+import collections.abc
+import functools
+import operator
+import sys
+import types as _types
+import typing
+
+
+__all__ = [
+ # Super-special typing primitives.
+ 'Any',
+ 'ClassVar',
+ 'Concatenate',
+ 'Final',
+ 'LiteralString',
+ 'ParamSpec',
+ 'ParamSpecArgs',
+ 'ParamSpecKwargs',
+ 'Self',
+ 'Type',
+ 'TypeVar',
+ 'TypeVarTuple',
+ 'Unpack',
+
+ # ABCs (from collections.abc).
+ 'Awaitable',
+ 'AsyncIterator',
+ 'AsyncIterable',
+ 'Coroutine',
+ 'AsyncGenerator',
+ 'AsyncContextManager',
+ 'ChainMap',
+
+ # Concrete collection types.
+ 'ContextManager',
+ 'Counter',
+ 'Deque',
+ 'DefaultDict',
+ 'NamedTuple',
+ 'OrderedDict',
+ 'TypedDict',
+
+ # Structural checks, a.k.a. protocols.
+ 'SupportsIndex',
+
+ # One-off things.
+ 'Annotated',
+ 'assert_never',
+ 'assert_type',
+ 'clear_overloads',
+ 'dataclass_transform',
+ 'get_overloads',
+ 'final',
+ 'get_args',
+ 'get_origin',
+ 'get_type_hints',
+ 'IntVar',
+ 'is_typeddict',
+ 'Literal',
+ 'NewType',
+ 'overload',
+ 'override',
+ 'Protocol',
+ 'reveal_type',
+ 'runtime',
+ 'runtime_checkable',
+ 'Text',
+ 'TypeAlias',
+ 'TypeGuard',
+ 'TYPE_CHECKING',
+ 'Never',
+ 'NoReturn',
+ 'Required',
+ 'NotRequired',
+]
+
+# for backward compatibility
+PEP_560 = True
+GenericMeta = type
+
+# The functions below are modified copies of typing internal helpers.
+# They are needed by _ProtocolMeta and they provide support for PEP 646.
+
+_marker = object()
+
+
+def _check_generic(cls, parameters, elen=_marker):
+ """Check correct count for parameters of a generic cls (internal helper).
+ This gives a nice error message in case of count mismatch.
+ """
+ if not elen:
+ raise TypeError(f"{cls} is not a generic class")
+ if elen is _marker:
+ if not hasattr(cls, "__parameters__") or not cls.__parameters__:
+ raise TypeError(f"{cls} is not a generic class")
+ elen = len(cls.__parameters__)
+ alen = len(parameters)
+ if alen != elen:
+ if hasattr(cls, "__parameters__"):
+ parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
+ num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
+ if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
+ return
+ raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
+ f" actual {alen}, expected {elen}")
+
+
+if sys.version_info >= (3, 10):
+ def _should_collect_from_parameters(t):
+ return isinstance(
+ t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
+ )
+elif sys.version_info >= (3, 9):
+ def _should_collect_from_parameters(t):
+ return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
+else:
+ def _should_collect_from_parameters(t):
+ return isinstance(t, typing._GenericAlias) and not t._special
+
+
+def _collect_type_vars(types, typevar_types=None):
+ """Collect all type variable contained in types in order of
+ first appearance (lexicographic order). For example::
+
+ _collect_type_vars((T, List[S, T])) == (T, S)
+ """
+ if typevar_types is None:
+ typevar_types = typing.TypeVar
+ tvars = []
+ for t in types:
+ if (
+ isinstance(t, typevar_types) and
+ t not in tvars and
+ not _is_unpack(t)
+ ):
+ tvars.append(t)
+ if _should_collect_from_parameters(t):
+ tvars.extend([t for t in t.__parameters__ if t not in tvars])
+ return tuple(tvars)
+
+
+NoReturn = typing.NoReturn
+
+# Some unconstrained type variables. These are used by the container types.
+# (These are not for export.)
+T = typing.TypeVar('T') # Any type.
+KT = typing.TypeVar('KT') # Key type.
+VT = typing.TypeVar('VT') # Value type.
+T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers.
+T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant.
+
+
+if sys.version_info >= (3, 11):
+ from typing import Any
+else:
+
+ class _AnyMeta(type):
+ def __instancecheck__(self, obj):
+ if self is Any:
+ raise TypeError("typing_extensions.Any cannot be used with isinstance()")
+ return super().__instancecheck__(obj)
+
+ def __repr__(self):
+ if self is Any:
+ return "typing_extensions.Any"
+ return super().__repr__()
+
+ class Any(metaclass=_AnyMeta):
+ """Special type indicating an unconstrained type.
+ - Any is compatible with every type.
+ - Any assumed to have all methods.
+ - All values assumed to be instances of Any.
+ Note that all the above statements are true from the point of view of
+ static type checkers. At runtime, Any should not be used with instance
+ checks.
+ """
+ def __new__(cls, *args, **kwargs):
+ if cls is Any:
+ raise TypeError("Any cannot be instantiated")
+ return super().__new__(cls, *args, **kwargs)
+
+
+ClassVar = typing.ClassVar
+
+# On older versions of typing there is an internal class named "Final".
+# 3.8+
+if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
+ Final = typing.Final
+# 3.7
+else:
+ class _FinalForm(typing._SpecialForm, _root=True):
+
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ def __getitem__(self, parameters):
+ item = typing._type_check(parameters,
+ f'{self._name} accepts only a single type.')
+ return typing._GenericAlias(self, (item,))
+
+ Final = _FinalForm('Final',
+ doc="""A special typing construct to indicate that a name
+ cannot be re-assigned or overridden in a subclass.
+ For example:
+
+ MAX_SIZE: Final = 9000
+ MAX_SIZE += 1 # Error reported by type checker
+
+ class Connection:
+ TIMEOUT: Final[int] = 10
+ class FastConnector(Connection):
+ TIMEOUT = 1 # Error reported by type checker
+
+ There is no runtime checking of these properties.""")
+
+if sys.version_info >= (3, 11):
+ final = typing.final
+else:
+ # @final exists in 3.8+, but we backport it for all versions
+ # before 3.11 to keep support for the __final__ attribute.
+ # See https://bugs.python.org/issue46342
+ def final(f):
+ """This decorator can be used to indicate to type checkers that
+ the decorated method cannot be overridden, and decorated class
+ cannot be subclassed. For example:
+
+ class Base:
+ @final
+ def done(self) -> None:
+ ...
+ class Sub(Base):
+ def done(self) -> None: # Error reported by type checker
+ ...
+ @final
+ class Leaf:
+ ...
+ class Other(Leaf): # Error reported by type checker
+ ...
+
+ There is no runtime checking of these properties. The decorator
+ sets the ``__final__`` attribute to ``True`` on the decorated object
+ to allow runtime introspection.
+ """
+ try:
+ f.__final__ = True
+ except (AttributeError, TypeError):
+ # Skip the attribute silently if it is not writable.
+ # AttributeError happens if the object has __slots__ or a
+ # read-only property, TypeError if it's a builtin class.
+ pass
+ return f
+
+
+def IntVar(name):
+ return typing.TypeVar(name)
+
+
+# 3.8+:
+if hasattr(typing, 'Literal'):
+ Literal = typing.Literal
+# 3.7:
+else:
+ class _LiteralForm(typing._SpecialForm, _root=True):
+
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ def __getitem__(self, parameters):
+ return typing._GenericAlias(self, parameters)
+
+ Literal = _LiteralForm('Literal',
+ doc="""A type that can be used to indicate to type checkers
+ that the corresponding value has a value literally equivalent
+ to the provided parameter. For example:
+
+ var: Literal[4] = 4
+
+ The type checker understands that 'var' is literally equal to
+ the value 4 and no other value.
+
+ Literal[...] cannot be subclassed. There is no runtime
+ checking verifying that the parameter is actually a value
+ instead of a type.""")
+
+
+_overload_dummy = typing._overload_dummy # noqa
+
+
+if hasattr(typing, "get_overloads"): # 3.11+
+ overload = typing.overload
+ get_overloads = typing.get_overloads
+ clear_overloads = typing.clear_overloads
+else:
+ # {module: {qualname: {firstlineno: func}}}
+ _overload_registry = collections.defaultdict(
+ functools.partial(collections.defaultdict, dict)
+ )
+
+ def overload(func):
+ """Decorator for overloaded functions/methods.
+
+ In a stub file, place two or more stub definitions for the same
+ function in a row, each decorated with @overload. For example:
+
+ @overload
+ def utf8(value: None) -> None: ...
+ @overload
+ def utf8(value: bytes) -> bytes: ...
+ @overload
+ def utf8(value: str) -> bytes: ...
+
+ In a non-stub file (i.e. a regular .py file), do the same but
+ follow it with an implementation. The implementation should *not*
+ be decorated with @overload. For example:
+
+ @overload
+ def utf8(value: None) -> None: ...
+ @overload
+ def utf8(value: bytes) -> bytes: ...
+ @overload
+ def utf8(value: str) -> bytes: ...
+ def utf8(value):
+ # implementation goes here
+
+ The overloads for a function can be retrieved at runtime using the
+ get_overloads() function.
+ """
+ # classmethod and staticmethod
+ f = getattr(func, "__func__", func)
+ try:
+ _overload_registry[f.__module__][f.__qualname__][
+ f.__code__.co_firstlineno
+ ] = func
+ except AttributeError:
+ # Not a normal function; ignore.
+ pass
+ return _overload_dummy
+
+ def get_overloads(func):
+ """Return all defined overloads for *func* as a sequence."""
+ # classmethod and staticmethod
+ f = getattr(func, "__func__", func)
+ if f.__module__ not in _overload_registry:
+ return []
+ mod_dict = _overload_registry[f.__module__]
+ if f.__qualname__ not in mod_dict:
+ return []
+ return list(mod_dict[f.__qualname__].values())
+
+ def clear_overloads():
+ """Clear all overloads in the registry."""
+ _overload_registry.clear()
+
+
+# This is not a real generic class. Don't use outside annotations.
+Type = typing.Type
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+
+
+Awaitable = typing.Awaitable
+Coroutine = typing.Coroutine
+AsyncIterable = typing.AsyncIterable
+AsyncIterator = typing.AsyncIterator
+Deque = typing.Deque
+ContextManager = typing.ContextManager
+AsyncContextManager = typing.AsyncContextManager
+DefaultDict = typing.DefaultDict
+
+# 3.7.2+
+if hasattr(typing, 'OrderedDict'):
+ OrderedDict = typing.OrderedDict
+# 3.7.0-3.7.2
+else:
+ OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
+
+Counter = typing.Counter
+ChainMap = typing.ChainMap
+AsyncGenerator = typing.AsyncGenerator
+NewType = typing.NewType
+Text = typing.Text
+TYPE_CHECKING = typing.TYPE_CHECKING
+
+
+_PROTO_WHITELIST = ['Callable', 'Awaitable',
+ 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator',
+ 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
+ 'ContextManager', 'AsyncContextManager']
+
+
+def _get_protocol_attrs(cls):
+ attrs = set()
+ for base in cls.__mro__[:-1]: # without object
+ if base.__name__ in ('Protocol', 'Generic'):
+ continue
+ annotations = getattr(base, '__annotations__', {})
+ for attr in list(base.__dict__.keys()) + list(annotations.keys()):
+ if (not attr.startswith('_abc_') and attr not in (
+ '__abstractmethods__', '__annotations__', '__weakref__',
+ '_is_protocol', '_is_runtime_protocol', '__dict__',
+ '__args__', '__slots__',
+ '__next_in_mro__', '__parameters__', '__origin__',
+ '__orig_bases__', '__extra__', '__tree_hash__',
+ '__doc__', '__subclasshook__', '__init__', '__new__',
+ '__module__', '_MutableMapping__marker', '_gorg')):
+ attrs.add(attr)
+ return attrs
+
+
+def _is_callable_members_only(cls):
+ return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
+
+
+def _maybe_adjust_parameters(cls):
+ """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__.
+
+ The contents of this function are very similar
+ to logic found in typing.Generic.__init_subclass__
+ on the CPython main branch.
+ """
+ tvars = []
+ if '__orig_bases__' in cls.__dict__:
+ tvars = typing._collect_type_vars(cls.__orig_bases__)
+ # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
+ # If found, tvars must be a subset of it.
+ # If not found, tvars is it.
+ # Also check for and reject plain Generic,
+ # and reject multiple Generic[...] and/or Protocol[...].
+ gvars = None
+ for base in cls.__orig_bases__:
+ if (isinstance(base, typing._GenericAlias) and
+ base.__origin__ in (typing.Generic, Protocol)):
+ # for error messages
+ the_base = base.__origin__.__name__
+ if gvars is not None:
+ raise TypeError(
+ "Cannot inherit from Generic[...]"
+ " and/or Protocol[...] multiple types.")
+ gvars = base.__parameters__
+ if gvars is None:
+ gvars = tvars
+ else:
+ tvarset = set(tvars)
+ gvarset = set(gvars)
+ if not tvarset <= gvarset:
+ s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
+ s_args = ', '.join(str(g) for g in gvars)
+ raise TypeError(f"Some type variables ({s_vars}) are"
+ f" not listed in {the_base}[{s_args}]")
+ tvars = gvars
+ cls.__parameters__ = tuple(tvars)
+
+
+# 3.8+
+if hasattr(typing, 'Protocol'):
+ Protocol = typing.Protocol
+# 3.7
+else:
+
+ def _no_init(self, *args, **kwargs):
+ if type(self)._is_protocol:
+ raise TypeError('Protocols cannot be instantiated')
+
+ class _ProtocolMeta(abc.ABCMeta): # noqa: B024
+ # This metaclass is a bit unfortunate and exists only because of the lack
+ # of __instancehook__.
+ def __instancecheck__(cls, instance):
+ # We need this method for situations where attributes are
+ # assigned in __init__.
+ if ((not getattr(cls, '_is_protocol', False) or
+ _is_callable_members_only(cls)) and
+ issubclass(instance.__class__, cls)):
+ return True
+ if cls._is_protocol:
+ if all(hasattr(instance, attr) and
+ (not callable(getattr(cls, attr, None)) or
+ getattr(instance, attr) is not None)
+ for attr in _get_protocol_attrs(cls)):
+ return True
+ return super().__instancecheck__(instance)
+
+ class Protocol(metaclass=_ProtocolMeta):
+ # There is quite a lot of overlapping code with typing.Generic.
+ # Unfortunately it is hard to avoid this while these live in two different
+ # modules. The duplicated code will be removed when Protocol is moved to typing.
+ """Base class for protocol classes. Protocol classes are defined as::
+
+ class Proto(Protocol):
+ def meth(self) -> int:
+ ...
+
+ Such classes are primarily used with static type checkers that recognize
+ structural subtyping (static duck-typing), for example::
+
+ class C:
+ def meth(self) -> int:
+ return 0
+
+ def func(x: Proto) -> int:
+ return x.meth()
+
+ func(C()) # Passes static type check
+
+ See PEP 544 for details. Protocol classes decorated with
+ @typing_extensions.runtime act as simple-minded runtime protocol that checks
+ only the presence of given attributes, ignoring their type signatures.
+
+ Protocol classes can be generic, they are defined as::
+
+ class GenProto(Protocol[T]):
+ def meth(self) -> T:
+ ...
+ """
+ __slots__ = ()
+ _is_protocol = True
+
+ def __new__(cls, *args, **kwds):
+ if cls is Protocol:
+ raise TypeError("Type Protocol cannot be instantiated; "
+ "it can only be used as a base class")
+ return super().__new__(cls)
+
+ @typing._tp_cache
+ def __class_getitem__(cls, params):
+ if not isinstance(params, tuple):
+ params = (params,)
+ if not params and cls is not typing.Tuple:
+ raise TypeError(
+ f"Parameter list to {cls.__qualname__}[...] cannot be empty")
+ msg = "Parameters to generic types must be types."
+ params = tuple(typing._type_check(p, msg) for p in params) # noqa
+ if cls is Protocol:
+ # Generic can only be subscripted with unique type variables.
+ if not all(isinstance(p, typing.TypeVar) for p in params):
+ i = 0
+ while isinstance(params[i], typing.TypeVar):
+ i += 1
+ raise TypeError(
+ "Parameters to Protocol[...] must all be type variables."
+ f" Parameter {i + 1} is {params[i]}")
+ if len(set(params)) != len(params):
+ raise TypeError(
+ "Parameters to Protocol[...] must all be unique")
+ else:
+ # Subscripting a regular Generic subclass.
+ _check_generic(cls, params, len(cls.__parameters__))
+ return typing._GenericAlias(cls, params)
+
+ def __init_subclass__(cls, *args, **kwargs):
+ if '__orig_bases__' in cls.__dict__:
+ error = typing.Generic in cls.__orig_bases__
+ else:
+ error = typing.Generic in cls.__bases__
+ if error:
+ raise TypeError("Cannot inherit from plain Generic")
+ _maybe_adjust_parameters(cls)
+
+ # Determine if this is a protocol or a concrete subclass.
+ if not cls.__dict__.get('_is_protocol', None):
+ cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+ # Set (or override) the protocol subclass hook.
+ def _proto_hook(other):
+ if not cls.__dict__.get('_is_protocol', None):
+ return NotImplemented
+ if not getattr(cls, '_is_runtime_protocol', False):
+ if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+ return NotImplemented
+ raise TypeError("Instance and class checks can only be used with"
+ " @runtime protocols")
+ if not _is_callable_members_only(cls):
+ if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+ return NotImplemented
+ raise TypeError("Protocols with non-method members"
+ " don't support issubclass()")
+ if not isinstance(other, type):
+ # Same error as for issubclass(1, int)
+ raise TypeError('issubclass() arg 1 must be a class')
+ for attr in _get_protocol_attrs(cls):
+ for base in other.__mro__:
+ if attr in base.__dict__:
+ if base.__dict__[attr] is None:
+ return NotImplemented
+ break
+ annotations = getattr(base, '__annotations__', {})
+ if (isinstance(annotations, typing.Mapping) and
+ attr in annotations and
+ isinstance(other, _ProtocolMeta) and
+ other._is_protocol):
+ break
+ else:
+ return NotImplemented
+ return True
+ if '__subclasshook__' not in cls.__dict__:
+ cls.__subclasshook__ = _proto_hook
+
+ # We have nothing more to do for non-protocols.
+ if not cls._is_protocol:
+ return
+
+ # Check consistency of bases.
+ for base in cls.__bases__:
+ if not (base in (object, typing.Generic) or
+ base.__module__ == 'collections.abc' and
+ base.__name__ in _PROTO_WHITELIST or
+ isinstance(base, _ProtocolMeta) and base._is_protocol):
+ raise TypeError('Protocols can only inherit from other'
+ f' protocols, got {repr(base)}')
+ cls.__init__ = _no_init
+
+
+# 3.8+
+if hasattr(typing, 'runtime_checkable'):
+ runtime_checkable = typing.runtime_checkable
+# 3.7
+else:
+ def runtime_checkable(cls):
+ """Mark a protocol class as a runtime protocol, so that it
+ can be used with isinstance() and issubclass(). Raise TypeError
+ if applied to a non-protocol class.
+
+ This allows a simple-minded structural check very similar to the
+ one-offs in collections.abc such as Hashable.
+ """
+ if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
+ raise TypeError('@runtime_checkable can be only applied to protocol classes,'
+ f' got {cls!r}')
+ cls._is_runtime_protocol = True
+ return cls
+
+
+# Exists for backwards compatibility.
+runtime = runtime_checkable
+
+
+# 3.8+
+if hasattr(typing, 'SupportsIndex'):
+ SupportsIndex = typing.SupportsIndex
+# 3.7
+else:
+ @runtime_checkable
+ class SupportsIndex(Protocol):
+ __slots__ = ()
+
+ @abc.abstractmethod
+ def __index__(self) -> int:
+ pass
+
+
+if hasattr(typing, "Required"):
+ # The standard library TypedDict in Python 3.8 does not store runtime information
+ # about which (if any) keys are optional. See https://bugs.python.org/issue38834
+ # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
+ # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059
+ # The standard library TypedDict below Python 3.11 does not store runtime
+ # information about optional and required keys when using Required or NotRequired.
+ # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
+ TypedDict = typing.TypedDict
+ _TypedDictMeta = typing._TypedDictMeta
+ is_typeddict = typing.is_typeddict
+else:
+ def _check_fails(cls, other):
+ try:
+ if sys._getframe(1).f_globals['__name__'] not in ['abc',
+ 'functools',
+ 'typing']:
+ # Typed dicts are only for static structural subtyping.
+ raise TypeError('TypedDict does not support instance and class checks')
+ except (AttributeError, ValueError):
+ pass
+ return False
+
+ def _dict_new(*args, **kwargs):
+ if not args:
+ raise TypeError('TypedDict.__new__(): not enough arguments')
+ _, args = args[0], args[1:] # allow the "cls" keyword be passed
+ return dict(*args, **kwargs)
+
+ _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)'
+
+ def _typeddict_new(*args, total=True, **kwargs):
+ if not args:
+ raise TypeError('TypedDict.__new__(): not enough arguments')
+ _, args = args[0], args[1:] # allow the "cls" keyword be passed
+ if args:
+ typename, args = args[0], args[1:] # allow the "_typename" keyword be passed
+ elif '_typename' in kwargs:
+ typename = kwargs.pop('_typename')
+ import warnings
+ warnings.warn("Passing '_typename' as keyword argument is deprecated",
+ DeprecationWarning, stacklevel=2)
+ else:
+ raise TypeError("TypedDict.__new__() missing 1 required positional "
+ "argument: '_typename'")
+ if args:
+ try:
+ fields, = args # allow the "_fields" keyword be passed
+ except ValueError:
+ raise TypeError('TypedDict.__new__() takes from 2 to 3 '
+ f'positional arguments but {len(args) + 2} '
+ 'were given')
+ elif '_fields' in kwargs and len(kwargs) == 1:
+ fields = kwargs.pop('_fields')
+ import warnings
+ warnings.warn("Passing '_fields' as keyword argument is deprecated",
+ DeprecationWarning, stacklevel=2)
+ else:
+ fields = None
+
+ if fields is None:
+ fields = kwargs
+ elif kwargs:
+ raise TypeError("TypedDict takes either a dict or keyword arguments,"
+ " but not both")
+
+ ns = {'__annotations__': dict(fields)}
+ try:
+ # Setting correct module is necessary to make typed dict classes pickleable.
+ ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ pass
+
+ return _TypedDictMeta(typename, (), ns, total=total)
+
+ _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
+ ' /, *, total=True, **kwargs)')
+
+ class _TypedDictMeta(type):
+ def __init__(cls, name, bases, ns, total=True):
+ super().__init__(name, bases, ns)
+
+ def __new__(cls, name, bases, ns, total=True):
+ # Create new typed dict class object.
+ # This method is called directly when TypedDict is subclassed,
+ # or via _typeddict_new when TypedDict is instantiated. This way
+ # TypedDict supports all three syntaxes described in its docstring.
+ # Subclasses and instances of TypedDict return actual dictionaries
+ # via _dict_new.
+ ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
+ # Don't insert typing.Generic into __bases__ here,
+ # or Generic.__init_subclass__ will raise TypeError
+ # in the super().__new__() call.
+ # Instead, monkey-patch __bases__ onto the class after it's been created.
+ tp_dict = super().__new__(cls, name, (dict,), ns)
+
+ if any(issubclass(base, typing.Generic) for base in bases):
+ tp_dict.__bases__ = (typing.Generic, dict)
+ _maybe_adjust_parameters(tp_dict)
+
+ annotations = {}
+ own_annotations = ns.get('__annotations__', {})
+ msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+ own_annotations = {
+ n: typing._type_check(tp, msg) for n, tp in own_annotations.items()
+ }
+ required_keys = set()
+ optional_keys = set()
+
+ for base in bases:
+ annotations.update(base.__dict__.get('__annotations__', {}))
+ required_keys.update(base.__dict__.get('__required_keys__', ()))
+ optional_keys.update(base.__dict__.get('__optional_keys__', ()))
+
+ annotations.update(own_annotations)
+ for annotation_key, annotation_type in own_annotations.items():
+ annotation_origin = get_origin(annotation_type)
+ if annotation_origin is Annotated:
+ annotation_args = get_args(annotation_type)
+ if annotation_args:
+ annotation_type = annotation_args[0]
+ annotation_origin = get_origin(annotation_type)
+
+ if annotation_origin is Required:
+ required_keys.add(annotation_key)
+ elif annotation_origin is NotRequired:
+ optional_keys.add(annotation_key)
+ elif total:
+ required_keys.add(annotation_key)
+ else:
+ optional_keys.add(annotation_key)
+
+ tp_dict.__annotations__ = annotations
+ tp_dict.__required_keys__ = frozenset(required_keys)
+ tp_dict.__optional_keys__ = frozenset(optional_keys)
+ if not hasattr(tp_dict, '__total__'):
+ tp_dict.__total__ = total
+ return tp_dict
+
+ __instancecheck__ = __subclasscheck__ = _check_fails
+
+ TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
+ TypedDict.__module__ = __name__
+ TypedDict.__doc__ = \
+ """A simple typed name space. At runtime it is equivalent to a plain dict.
+
+ TypedDict creates a dictionary type that expects all of its
+ instances to have a certain set of keys, with each key
+ associated with a value of a consistent type. This expectation
+ is not checked at runtime but is only enforced by type checkers.
+ Usage::
+
+ class Point2D(TypedDict):
+ x: int
+ y: int
+ label: str
+
+ a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
+ b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
+
+ assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+ The type info can be accessed via the Point2D.__annotations__ dict, and
+ the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
+ TypedDict supports two additional equivalent forms::
+
+ Point2D = TypedDict('Point2D', x=int, y=int, label=str)
+ Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+ The class syntax is only supported in Python 3.6+, while two other
+ syntax forms work for Python 2.7 and 3.2+
+ """
+
+ if hasattr(typing, "_TypedDictMeta"):
+ _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
+ else:
+ _TYPEDDICT_TYPES = (_TypedDictMeta,)
+
+ def is_typeddict(tp):
+ """Check if an annotation is a TypedDict class
+
+ For example::
+ class Film(TypedDict):
+ title: str
+ year: int
+
+ is_typeddict(Film) # => True
+ is_typeddict(Union[list, str]) # => False
+ """
+ return isinstance(tp, tuple(_TYPEDDICT_TYPES))
+
+
+if hasattr(typing, "assert_type"):
+ assert_type = typing.assert_type
+
+else:
+ def assert_type(__val, __typ):
+ """Assert (to the type checker) that the value is of the given type.
+
+ When the type checker encounters a call to assert_type(), it
+ emits an error if the value is not of the specified type::
+
+ def greet(name: str) -> None:
+ assert_type(name, str) # ok
+ assert_type(name, int) # type checker error
+
+ At runtime this returns the first argument unchanged and otherwise
+ does nothing.
+ """
+ return __val
+
+
+if hasattr(typing, "Required"):
+ get_type_hints = typing.get_type_hints
+else:
+ import functools
+ import types
+
+ # replaces _strip_annotations()
+ def _strip_extras(t):
+ """Strips Annotated, Required and NotRequired from a given type."""
+ if isinstance(t, _AnnotatedAlias):
+ return _strip_extras(t.__origin__)
+ if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
+ return _strip_extras(t.__args__[0])
+ if isinstance(t, typing._GenericAlias):
+ stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+ if stripped_args == t.__args__:
+ return t
+ return t.copy_with(stripped_args)
+ if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias):
+ stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+ if stripped_args == t.__args__:
+ return t
+ return types.GenericAlias(t.__origin__, stripped_args)
+ if hasattr(types, "UnionType") and isinstance(t, types.UnionType):
+ stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+ if stripped_args == t.__args__:
+ return t
+ return functools.reduce(operator.or_, stripped_args)
+
+ return t
+
+ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
+ """Return type hints for an object.
+
+ This is often the same as obj.__annotations__, but it handles
+ forward references encoded as string literals, adds Optional[t] if a
+ default value equal to None is set and recursively replaces all
+ 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
+ (unless 'include_extras=True').
+
+ The argument may be a module, class, method, or function. The annotations
+ are returned as a dictionary. For classes, annotations include also
+ inherited members.
+
+ TypeError is raised if the argument is not of a type that can contain
+ annotations, and an empty dictionary is returned if no annotations are
+ present.
+
+ BEWARE -- the behavior of globalns and localns is counterintuitive
+ (unless you are familiar with how eval() and exec() work). The
+ search order is locals first, then globals.
+
+ - If no dict arguments are passed, an attempt is made to use the
+ globals from obj (or the respective module's globals for classes),
+ and these are also used as the locals. If the object does not appear
+ to have globals, an empty dictionary is used.
+
+ - If one dict argument is passed, it is used for both globals and
+ locals.
+
+ - If two dict arguments are passed, they specify globals and
+ locals, respectively.
+ """
+ if hasattr(typing, "Annotated"):
+ hint = typing.get_type_hints(
+ obj, globalns=globalns, localns=localns, include_extras=True
+ )
+ else:
+ hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
+ if include_extras:
+ return hint
+ return {k: _strip_extras(t) for k, t in hint.items()}
+
+
+# Python 3.9+ has PEP 593 (Annotated)
+if hasattr(typing, 'Annotated'):
+ Annotated = typing.Annotated
+ # Not exported and not a public API, but needed for get_origin() and get_args()
+ # to work.
+ _AnnotatedAlias = typing._AnnotatedAlias
+# 3.7-3.8
+else:
+ class _AnnotatedAlias(typing._GenericAlias, _root=True):
+ """Runtime representation of an annotated type.
+
+ At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
+ with extra annotations. The alias behaves like a normal typing alias,
+ instantiating is the same as instantiating the underlying type, binding
+ it to types is also the same.
+ """
+ def __init__(self, origin, metadata):
+ if isinstance(origin, _AnnotatedAlias):
+ metadata = origin.__metadata__ + metadata
+ origin = origin.__origin__
+ super().__init__(origin, origin)
+ self.__metadata__ = metadata
+
+ def copy_with(self, params):
+ assert len(params) == 1
+ new_type = params[0]
+ return _AnnotatedAlias(new_type, self.__metadata__)
+
+ def __repr__(self):
+ return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
+ f"{', '.join(repr(a) for a in self.__metadata__)}]")
+
+ def __reduce__(self):
+ return operator.getitem, (
+ Annotated, (self.__origin__,) + self.__metadata__
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, _AnnotatedAlias):
+ return NotImplemented
+ if self.__origin__ != other.__origin__:
+ return False
+ return self.__metadata__ == other.__metadata__
+
+ def __hash__(self):
+ return hash((self.__origin__, self.__metadata__))
+
+ class Annotated:
+ """Add context specific metadata to a type.
+
+ Example: Annotated[int, runtime_check.Unsigned] indicates to the
+ hypothetical runtime_check module that this type is an unsigned int.
+ Every other consumer of this type can ignore this metadata and treat
+ this type as int.
+
+ The first argument to Annotated must be a valid type (and will be in
+ the __origin__ field), the remaining arguments are kept as a tuple in
+ the __extra__ field.
+
+ Details:
+
+ - It's an error to call `Annotated` with less than two arguments.
+ - Nested Annotated are flattened::
+
+ Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+ - Instantiating an annotated type is equivalent to instantiating the
+ underlying type::
+
+ Annotated[C, Ann1](5) == C(5)
+
+ - Annotated can be used as a generic type alias::
+
+ Optimized = Annotated[T, runtime.Optimize()]
+ Optimized[int] == Annotated[int, runtime.Optimize()]
+
+ OptimizedList = Annotated[List[T], runtime.Optimize()]
+ OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+ """
+
+ __slots__ = ()
+
+ def __new__(cls, *args, **kwargs):
+ raise TypeError("Type Annotated cannot be instantiated.")
+
+ @typing._tp_cache
+ def __class_getitem__(cls, params):
+ if not isinstance(params, tuple) or len(params) < 2:
+ raise TypeError("Annotated[...] should be used "
+ "with at least two arguments (a type and an "
+ "annotation).")
+ allowed_special_forms = (ClassVar, Final)
+ if get_origin(params[0]) in allowed_special_forms:
+ origin = params[0]
+ else:
+ msg = "Annotated[t, ...]: t must be a type."
+ origin = typing._type_check(params[0], msg)
+ metadata = tuple(params[1:])
+ return _AnnotatedAlias(origin, metadata)
+
+ def __init_subclass__(cls, *args, **kwargs):
+ raise TypeError(
+ f"Cannot subclass {cls.__module__}.Annotated"
+ )
+
+# Python 3.8 has get_origin() and get_args() but those implementations aren't
+# Annotated-aware, so we can't use those. Python 3.9's versions don't support
+# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
+if sys.version_info[:2] >= (3, 10):
+ get_origin = typing.get_origin
+ get_args = typing.get_args
+# 3.7-3.9
+else:
+ try:
+ # 3.9+
+ from typing import _BaseGenericAlias
+ except ImportError:
+ _BaseGenericAlias = typing._GenericAlias
+ try:
+ # 3.9+
+ from typing import GenericAlias as _typing_GenericAlias
+ except ImportError:
+ _typing_GenericAlias = typing._GenericAlias
+
+ def get_origin(tp):
+ """Get the unsubscripted version of a type.
+
+ This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
+ and Annotated. Return None for unsupported types. Examples::
+
+ get_origin(Literal[42]) is Literal
+ get_origin(int) is None
+ get_origin(ClassVar[int]) is ClassVar
+ get_origin(Generic) is Generic
+ get_origin(Generic[T]) is Generic
+ get_origin(Union[T, int]) is Union
+ get_origin(List[Tuple[T, T]][int]) == list
+ get_origin(P.args) is P
+ """
+ if isinstance(tp, _AnnotatedAlias):
+ return Annotated
+ if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias,
+ ParamSpecArgs, ParamSpecKwargs)):
+ return tp.__origin__
+ if tp is typing.Generic:
+ return typing.Generic
+ return None
+
+ def get_args(tp):
+ """Get type arguments with all substitutions performed.
+
+ For unions, basic simplifications used by Union constructor are performed.
+ Examples::
+ get_args(Dict[str, int]) == (str, int)
+ get_args(int) == ()
+ get_args(Union[int, Union[T, int], str][int]) == (int, str)
+ get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+ get_args(Callable[[], T][int]) == ([], int)
+ """
+ if isinstance(tp, _AnnotatedAlias):
+ return (tp.__origin__,) + tp.__metadata__
+ if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)):
+ if getattr(tp, "_special", False):
+ return ()
+ res = tp.__args__
+ if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+ res = (list(res[:-1]), res[-1])
+ return res
+ return ()
+
+
+# 3.10+
+if hasattr(typing, 'TypeAlias'):
+ TypeAlias = typing.TypeAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+ class _TypeAliasForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ @_TypeAliasForm
+ def TypeAlias(self, parameters):
+ """Special marker indicating that an assignment should
+ be recognized as a proper type alias definition by type
+ checkers.
+
+ For example::
+
+ Predicate: TypeAlias = Callable[..., bool]
+
+ It's invalid when used anywhere except as in the example above.
+ """
+ raise TypeError(f"{self} is not subscriptable")
+# 3.7-3.8
+else:
+ class _TypeAliasForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ TypeAlias = _TypeAliasForm('TypeAlias',
+ doc="""Special marker indicating that an assignment should
+ be recognized as a proper type alias definition by type
+ checkers.
+
+ For example::
+
+ Predicate: TypeAlias = Callable[..., bool]
+
+ It's invalid when used anywhere except as in the example
+ above.""")
+
+
+class _DefaultMixin:
+ """Mixin for TypeVarLike defaults."""
+
+ __slots__ = ()
+
+ def __init__(self, default):
+ if isinstance(default, (tuple, list)):
+ self.__default__ = tuple((typing._type_check(d, "Default must be a type")
+ for d in default))
+ elif default:
+ self.__default__ = typing._type_check(default, "Default must be a type")
+ else:
+ self.__default__ = None
+
+
+# Add default and infer_variance parameters from PEP 696 and 695
+class TypeVar(typing.TypeVar, _DefaultMixin, _root=True):
+ """Type variable."""
+
+ __module__ = 'typing'
+
+ def __init__(self, name, *constraints, bound=None,
+ covariant=False, contravariant=False,
+ default=None, infer_variance=False):
+ super().__init__(name, *constraints, bound=bound, covariant=covariant,
+ contravariant=contravariant)
+ _DefaultMixin.__init__(self, default)
+ self.__infer_variance__ = infer_variance
+
+ # for pickling:
+ try:
+ def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ def_mod = None
+ if def_mod != 'typing_extensions':
+ self.__module__ = def_mod
+
+
+# Python 3.10+ has PEP 612
+if hasattr(typing, 'ParamSpecArgs'):
+ ParamSpecArgs = typing.ParamSpecArgs
+ ParamSpecKwargs = typing.ParamSpecKwargs
+# 3.7-3.9
+else:
+ class _Immutable:
+ """Mixin to indicate that object should not be copied."""
+ __slots__ = ()
+
+ def __copy__(self):
+ return self
+
+ def __deepcopy__(self, memo):
+ return self
+
+ class ParamSpecArgs(_Immutable):
+ """The args for a ParamSpec object.
+
+ Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
+
+ ParamSpecArgs objects have a reference back to their ParamSpec:
+
+ P.args.__origin__ is P
+
+ This type is meant for runtime introspection and has no special meaning to
+ static type checkers.
+ """
+ def __init__(self, origin):
+ self.__origin__ = origin
+
+ def __repr__(self):
+ return f"{self.__origin__.__name__}.args"
+
+ def __eq__(self, other):
+ if not isinstance(other, ParamSpecArgs):
+ return NotImplemented
+ return self.__origin__ == other.__origin__
+
+ class ParamSpecKwargs(_Immutable):
+ """The kwargs for a ParamSpec object.
+
+ Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
+
+ ParamSpecKwargs objects have a reference back to their ParamSpec:
+
+ P.kwargs.__origin__ is P
+
+ This type is meant for runtime introspection and has no special meaning to
+ static type checkers.
+ """
+ def __init__(self, origin):
+ self.__origin__ = origin
+
+ def __repr__(self):
+ return f"{self.__origin__.__name__}.kwargs"
+
+ def __eq__(self, other):
+ if not isinstance(other, ParamSpecKwargs):
+ return NotImplemented
+ return self.__origin__ == other.__origin__
+
+# 3.10+
+if hasattr(typing, 'ParamSpec'):
+
+ # Add default Parameter - PEP 696
+ class ParamSpec(typing.ParamSpec, _DefaultMixin, _root=True):
+ """Parameter specification variable."""
+
+ __module__ = 'typing'
+
+ def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
+ default=None):
+ super().__init__(name, bound=bound, covariant=covariant,
+ contravariant=contravariant)
+ _DefaultMixin.__init__(self, default)
+
+ # for pickling:
+ try:
+ def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ def_mod = None
+ if def_mod != 'typing_extensions':
+ self.__module__ = def_mod
+
+# 3.7-3.9
+else:
+
+ # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+ class ParamSpec(list, _DefaultMixin):
+ """Parameter specification variable.
+
+ Usage::
+
+ P = ParamSpec('P')
+
+ Parameter specification variables exist primarily for the benefit of static
+ type checkers. They are used to forward the parameter types of one
+ callable to another callable, a pattern commonly found in higher order
+ functions and decorators. They are only valid when used in ``Concatenate``,
+ or s the first argument to ``Callable``. In Python 3.10 and higher,
+ they are also supported in user-defined Generics at runtime.
+ See class Generic for more information on generic types. An
+ example for annotating a decorator::
+
+ T = TypeVar('T')
+ P = ParamSpec('P')
+
+ def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+ '''A type-safe decorator to add logging to a function.'''
+ def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+ logging.info(f'{f.__name__} was called')
+ return f(*args, **kwargs)
+ return inner
+
+ @add_logging
+ def add_two(x: float, y: float) -> float:
+ '''Add two numbers together.'''
+ return x + y
+
+ Parameter specification variables defined with covariant=True or
+ contravariant=True can be used to declare covariant or contravariant
+ generic types. These keyword arguments are valid, but their actual semantics
+ are yet to be decided. See PEP 612 for details.
+
+ Parameter specification variables can be introspected. e.g.:
+
+ P.__name__ == 'T'
+ P.__bound__ == None
+ P.__covariant__ == False
+ P.__contravariant__ == False
+
+ Note that only parameter specification variables defined in global scope can
+ be pickled.
+ """
+
+ # Trick Generic __parameters__.
+ __class__ = typing.TypeVar
+
+ @property
+ def args(self):
+ return ParamSpecArgs(self)
+
+ @property
+ def kwargs(self):
+ return ParamSpecKwargs(self)
+
+ def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
+ default=None):
+ super().__init__([self])
+ self.__name__ = name
+ self.__covariant__ = bool(covariant)
+ self.__contravariant__ = bool(contravariant)
+ if bound:
+ self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
+ else:
+ self.__bound__ = None
+ _DefaultMixin.__init__(self, default)
+
+ # for pickling:
+ try:
+ def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ def_mod = None
+ if def_mod != 'typing_extensions':
+ self.__module__ = def_mod
+
+ def __repr__(self):
+ if self.__covariant__:
+ prefix = '+'
+ elif self.__contravariant__:
+ prefix = '-'
+ else:
+ prefix = '~'
+ return prefix + self.__name__
+
+ def __hash__(self):
+ return object.__hash__(self)
+
+ def __eq__(self, other):
+ return self is other
+
+ def __reduce__(self):
+ return self.__name__
+
+ # Hack to get typing._type_check to pass.
+ def __call__(self, *args, **kwargs):
+ pass
+
+
+# 3.7-3.9
+if not hasattr(typing, 'Concatenate'):
+ # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+ class _ConcatenateGenericAlias(list):
+
+ # Trick Generic into looking into this for __parameters__.
+ __class__ = typing._GenericAlias
+
+ # Flag in 3.8.
+ _special = False
+
+ def __init__(self, origin, args):
+ super().__init__(args)
+ self.__origin__ = origin
+ self.__args__ = args
+
+ def __repr__(self):
+ _type_repr = typing._type_repr
+ return (f'{_type_repr(self.__origin__)}'
+ f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
+
+ def __hash__(self):
+ return hash((self.__origin__, self.__args__))
+
+ # Hack to get typing._type_check to pass in Generic.
+ def __call__(self, *args, **kwargs):
+ pass
+
+ @property
+ def __parameters__(self):
+ return tuple(
+ tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
+ )
+
+
+# 3.7-3.9
+@typing._tp_cache
+def _concatenate_getitem(self, parameters):
+ if parameters == ():
+ raise TypeError("Cannot take a Concatenate of no types.")
+ if not isinstance(parameters, tuple):
+ parameters = (parameters,)
+ if not isinstance(parameters[-1], ParamSpec):
+ raise TypeError("The last parameter to Concatenate should be a "
+ "ParamSpec variable.")
+ msg = "Concatenate[arg, ...]: each arg must be a type."
+ parameters = tuple(typing._type_check(p, msg) for p in parameters)
+ return _ConcatenateGenericAlias(self, parameters)
+
+
+# 3.10+
+if hasattr(typing, 'Concatenate'):
+ Concatenate = typing.Concatenate
+ _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+ @_TypeAliasForm
+ def Concatenate(self, parameters):
+ """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+ higher order function which adds, removes or transforms parameters of a
+ callable.
+
+ For example::
+
+ Callable[Concatenate[int, P], int]
+
+ See PEP 612 for detailed information.
+ """
+ return _concatenate_getitem(self, parameters)
+# 3.7-8
+else:
+ class _ConcatenateForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ def __getitem__(self, parameters):
+ return _concatenate_getitem(self, parameters)
+
+ Concatenate = _ConcatenateForm(
+ 'Concatenate',
+ doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+ higher order function which adds, removes or transforms parameters of a
+ callable.
+
+ For example::
+
+ Callable[Concatenate[int, P], int]
+
+ See PEP 612 for detailed information.
+ """)
+
+# 3.10+
+if hasattr(typing, 'TypeGuard'):
+ TypeGuard = typing.TypeGuard
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+ class _TypeGuardForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ @_TypeGuardForm
+ def TypeGuard(self, parameters):
+ """Special typing form used to annotate the return type of a user-defined
+ type guard function. ``TypeGuard`` only accepts a single type argument.
+ At runtime, functions marked this way should return a boolean.
+
+ ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+ type checkers to determine a more precise type of an expression within a
+ program's code flow. Usually type narrowing is done by analyzing
+ conditional code flow and applying the narrowing to a block of code. The
+ conditional expression here is sometimes referred to as a "type guard".
+
+ Sometimes it would be convenient to use a user-defined boolean function
+ as a type guard. Such a function should use ``TypeGuard[...]`` as its
+ return type to alert static type checkers to this intention.
+
+ Using ``-> TypeGuard`` tells the static type checker that for a given
+ function:
+
+ 1. The return value is a boolean.
+ 2. If the return value is ``True``, the type of its argument
+ is the type inside ``TypeGuard``.
+
+ For example::
+
+ def is_str(val: Union[str, float]):
+ # "isinstance" type guard
+ if isinstance(val, str):
+ # Type of ``val`` is narrowed to ``str``
+ ...
+ else:
+ # Else, type of ``val`` is narrowed to ``float``.
+ ...
+
+ Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+ form of ``TypeA`` (it can even be a wider form) and this may lead to
+ type-unsafe results. The main reason is to allow for things like
+ narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+ a subtype of the former, since ``List`` is invariant. The responsibility of
+ writing type-safe type guards is left to the user.
+
+ ``TypeGuard`` also works with type variables. For more information, see
+ PEP 647 (User-Defined Type Guards).
+ """
+ item = typing._type_check(parameters, f'{self} accepts only a single type.')
+ return typing._GenericAlias(self, (item,))
+# 3.7-3.8
+else:
+ class _TypeGuardForm(typing._SpecialForm, _root=True):
+
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ def __getitem__(self, parameters):
+ item = typing._type_check(parameters,
+ f'{self._name} accepts only a single type')
+ return typing._GenericAlias(self, (item,))
+
+ TypeGuard = _TypeGuardForm(
+ 'TypeGuard',
+ doc="""Special typing form used to annotate the return type of a user-defined
+ type guard function. ``TypeGuard`` only accepts a single type argument.
+ At runtime, functions marked this way should return a boolean.
+
+ ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+ type checkers to determine a more precise type of an expression within a
+ program's code flow. Usually type narrowing is done by analyzing
+ conditional code flow and applying the narrowing to a block of code. The
+ conditional expression here is sometimes referred to as a "type guard".
+
+ Sometimes it would be convenient to use a user-defined boolean function
+ as a type guard. Such a function should use ``TypeGuard[...]`` as its
+ return type to alert static type checkers to this intention.
+
+ Using ``-> TypeGuard`` tells the static type checker that for a given
+ function:
+
+ 1. The return value is a boolean.
+ 2. If the return value is ``True``, the type of its argument
+ is the type inside ``TypeGuard``.
+
+ For example::
+
+ def is_str(val: Union[str, float]):
+ # "isinstance" type guard
+ if isinstance(val, str):
+ # Type of ``val`` is narrowed to ``str``
+ ...
+ else:
+ # Else, type of ``val`` is narrowed to ``float``.
+ ...
+
+ Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+ form of ``TypeA`` (it can even be a wider form) and this may lead to
+ type-unsafe results. The main reason is to allow for things like
+ narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+ a subtype of the former, since ``List`` is invariant. The responsibility of
+ writing type-safe type guards is left to the user.
+
+ ``TypeGuard`` also works with type variables. For more information, see
+ PEP 647 (User-Defined Type Guards).
+ """)
+
+
+# Vendored from cpython typing._SpecialFrom
+class _SpecialForm(typing._Final, _root=True):
+ __slots__ = ('_name', '__doc__', '_getitem')
+
+ def __init__(self, getitem):
+ self._getitem = getitem
+ self._name = getitem.__name__
+ self.__doc__ = getitem.__doc__
+
+ def __getattr__(self, item):
+ if item in {'__name__', '__qualname__'}:
+ return self._name
+
+ raise AttributeError(item)
+
+ def __mro_entries__(self, bases):
+ raise TypeError(f"Cannot subclass {self!r}")
+
+ def __repr__(self):
+ return f'typing_extensions.{self._name}'
+
+ def __reduce__(self):
+ return self._name
+
+ def __call__(self, *args, **kwds):
+ raise TypeError(f"Cannot instantiate {self!r}")
+
+ def __or__(self, other):
+ return typing.Union[self, other]
+
+ def __ror__(self, other):
+ return typing.Union[other, self]
+
+ def __instancecheck__(self, obj):
+ raise TypeError(f"{self} cannot be used with isinstance()")
+
+ def __subclasscheck__(self, cls):
+ raise TypeError(f"{self} cannot be used with issubclass()")
+
+ @typing._tp_cache
+ def __getitem__(self, parameters):
+ return self._getitem(self, parameters)
+
+
+if hasattr(typing, "LiteralString"):
+ LiteralString = typing.LiteralString
+else:
+ @_SpecialForm
+ def LiteralString(self, params):
+ """Represents an arbitrary literal string.
+
+ Example::
+
+ from typing_extensions import LiteralString
+
+ def query(sql: LiteralString) -> ...:
+ ...
+
+ query("SELECT * FROM table") # ok
+ query(f"SELECT * FROM {input()}") # not ok
+
+ See PEP 675 for details.
+
+ """
+ raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Self"):
+ Self = typing.Self
+else:
+ @_SpecialForm
+ def Self(self, params):
+ """Used to spell the type of "self" in classes.
+
+ Example::
+
+ from typing import Self
+
+ class ReturnsSelf:
+ def parse(self, data: bytes) -> Self:
+ ...
+ return self
+
+ """
+
+ raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Never"):
+ Never = typing.Never
+else:
+ @_SpecialForm
+ def Never(self, params):
+ """The bottom type, a type that has no members.
+
+ This can be used to define a function that should never be
+ called, or a function that never returns::
+
+ from typing_extensions import Never
+
+ def never_call_me(arg: Never) -> None:
+ pass
+
+ def int_or_str(arg: int | str) -> None:
+ never_call_me(arg) # type checker error
+ match arg:
+ case int():
+ print("It's an int")
+ case str():
+ print("It's a str")
+ case _:
+ never_call_me(arg) # ok, arg is of type Never
+
+ """
+
+ raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, 'Required'):
+ Required = typing.Required
+ NotRequired = typing.NotRequired
+elif sys.version_info[:2] >= (3, 9):
+ class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ @_ExtensionsSpecialForm
+ def Required(self, parameters):
+ """A special typing construct to mark a key of a total=False TypedDict
+ as required. For example:
+
+ class Movie(TypedDict, total=False):
+ title: Required[str]
+ year: int
+
+ m = Movie(
+ title='The Matrix', # typechecker error if key is omitted
+ year=1999,
+ )
+
+ There is no runtime checking that a required key is actually provided
+ when instantiating a related TypedDict.
+ """
+ item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+ return typing._GenericAlias(self, (item,))
+
+ @_ExtensionsSpecialForm
+ def NotRequired(self, parameters):
+ """A special typing construct to mark a key of a TypedDict as
+ potentially missing. For example:
+
+ class Movie(TypedDict):
+ title: str
+ year: NotRequired[int]
+
+ m = Movie(
+ title='The Matrix', # typechecker error if key is omitted
+ year=1999,
+ )
+ """
+ item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+ return typing._GenericAlias(self, (item,))
+
+else:
+ class _RequiredForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ def __getitem__(self, parameters):
+ item = typing._type_check(parameters,
+ f'{self._name} accepts only a single type.')
+ return typing._GenericAlias(self, (item,))
+
+ Required = _RequiredForm(
+ 'Required',
+ doc="""A special typing construct to mark a key of a total=False TypedDict
+ as required. For example:
+
+ class Movie(TypedDict, total=False):
+ title: Required[str]
+ year: int
+
+ m = Movie(
+ title='The Matrix', # typechecker error if key is omitted
+ year=1999,
+ )
+
+ There is no runtime checking that a required key is actually provided
+ when instantiating a related TypedDict.
+ """)
+ NotRequired = _RequiredForm(
+ 'NotRequired',
+ doc="""A special typing construct to mark a key of a TypedDict as
+ potentially missing. For example:
+
+ class Movie(TypedDict):
+ title: str
+ year: NotRequired[int]
+
+ m = Movie(
+ title='The Matrix', # typechecker error if key is omitted
+ year=1999,
+ )
+ """)
+
+
+if hasattr(typing, "Unpack"): # 3.11+
+ Unpack = typing.Unpack
+elif sys.version_info[:2] >= (3, 9):
+ class _UnpackSpecialForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ class _UnpackAlias(typing._GenericAlias, _root=True):
+ __class__ = typing.TypeVar
+
+ @_UnpackSpecialForm
+ def Unpack(self, parameters):
+ """A special typing construct to unpack a variadic type. For example:
+
+ Shape = TypeVarTuple('Shape')
+ Batch = NewType('Batch', int)
+
+ def add_batch_axis(
+ x: Array[Unpack[Shape]]
+ ) -> Array[Batch, Unpack[Shape]]: ...
+
+ """
+ item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+ return _UnpackAlias(self, (item,))
+
+ def _is_unpack(obj):
+ return isinstance(obj, _UnpackAlias)
+
+else:
+ class _UnpackAlias(typing._GenericAlias, _root=True):
+ __class__ = typing.TypeVar
+
+ class _UnpackForm(typing._SpecialForm, _root=True):
+ def __repr__(self):
+ return 'typing_extensions.' + self._name
+
+ def __getitem__(self, parameters):
+ item = typing._type_check(parameters,
+ f'{self._name} accepts only a single type.')
+ return _UnpackAlias(self, (item,))
+
+ Unpack = _UnpackForm(
+ 'Unpack',
+ doc="""A special typing construct to unpack a variadic type. For example:
+
+ Shape = TypeVarTuple('Shape')
+ Batch = NewType('Batch', int)
+
+ def add_batch_axis(
+ x: Array[Unpack[Shape]]
+ ) -> Array[Batch, Unpack[Shape]]: ...
+
+ """)
+
+ def _is_unpack(obj):
+ return isinstance(obj, _UnpackAlias)
+
+
+if hasattr(typing, "TypeVarTuple"): # 3.11+
+
+ # Add default Parameter - PEP 696
+ class TypeVarTuple(typing.TypeVarTuple, _DefaultMixin, _root=True):
+ """Type variable tuple."""
+
+ def __init__(self, name, *, default=None):
+ super().__init__(name)
+ _DefaultMixin.__init__(self, default)
+
+ # for pickling:
+ try:
+ def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ def_mod = None
+ if def_mod != 'typing_extensions':
+ self.__module__ = def_mod
+
+else:
+ class TypeVarTuple(_DefaultMixin):
+ """Type variable tuple.
+
+ Usage::
+
+ Ts = TypeVarTuple('Ts')
+
+ In the same way that a normal type variable is a stand-in for a single
+ type such as ``int``, a type variable *tuple* is a stand-in for a *tuple*
+ type such as ``Tuple[int, str]``.
+
+ Type variable tuples can be used in ``Generic`` declarations.
+ Consider the following example::
+
+ class Array(Generic[*Ts]): ...
+
+ The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
+ where ``T1`` and ``T2`` are type variables. To use these type variables
+ as type parameters of ``Array``, we must *unpack* the type variable tuple using
+ the star operator: ``*Ts``. The signature of ``Array`` then behaves
+ as if we had simply written ``class Array(Generic[T1, T2]): ...``.
+ In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
+ us to parameterise the class with an *arbitrary* number of type parameters.
+
+ Type variable tuples can be used anywhere a normal ``TypeVar`` can.
+ This includes class definitions, as shown above, as well as function
+ signatures and variable annotations::
+
+ class Array(Generic[*Ts]):
+
+ def __init__(self, shape: Tuple[*Ts]):
+ self._shape: Tuple[*Ts] = shape
+
+ def get_shape(self) -> Tuple[*Ts]:
+ return self._shape
+
+ shape = (Height(480), Width(640))
+ x: Array[Height, Width] = Array(shape)
+ y = abs(x) # Inferred type is Array[Height, Width]
+ z = x + x # ... is Array[Height, Width]
+ x.get_shape() # ... is tuple[Height, Width]
+
+ """
+
+ # Trick Generic __parameters__.
+ __class__ = typing.TypeVar
+
+ def __iter__(self):
+ yield self.__unpacked__
+
+ def __init__(self, name, *, default=None):
+ self.__name__ = name
+ _DefaultMixin.__init__(self, default)
+
+ # for pickling:
+ try:
+ def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ def_mod = None
+ if def_mod != 'typing_extensions':
+ self.__module__ = def_mod
+
+ self.__unpacked__ = Unpack[self]
+
+ def __repr__(self):
+ return self.__name__
+
+ def __hash__(self):
+ return object.__hash__(self)
+
+ def __eq__(self, other):
+ return self is other
+
+ def __reduce__(self):
+ return self.__name__
+
+ def __init_subclass__(self, *args, **kwds):
+ if '_root' not in kwds:
+ raise TypeError("Cannot subclass special typing classes")
+
+
+if hasattr(typing, "reveal_type"):
+ reveal_type = typing.reveal_type
+else:
+ def reveal_type(__obj: T) -> T:
+ """Reveal the inferred type of a variable.
+
+ When a static type checker encounters a call to ``reveal_type()``,
+ it will emit the inferred type of the argument::
+
+ x: int = 1
+ reveal_type(x)
+
+ Running a static type checker (e.g., ``mypy``) on this example
+ will produce output similar to 'Revealed type is "builtins.int"'.
+
+ At runtime, the function prints the runtime type of the
+ argument and returns it unchanged.
+
+ """
+ print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr)
+ return __obj
+
+
+if hasattr(typing, "assert_never"):
+ assert_never = typing.assert_never
+else:
+ def assert_never(__arg: Never) -> Never:
+ """Assert to the type checker that a line of code is unreachable.
+
+ Example::
+
+ def int_or_str(arg: int | str) -> None:
+ match arg:
+ case int():
+ print("It's an int")
+ case str():
+ print("It's a str")
+ case _:
+ assert_never(arg)
+
+ If a type checker finds that a call to assert_never() is
+ reachable, it will emit an error.
+
+ At runtime, this throws an exception when called.
+
+ """
+ raise AssertionError("Expected code to be unreachable")
+
+
+if hasattr(typing, 'dataclass_transform'):
+ dataclass_transform = typing.dataclass_transform
+else:
+ def dataclass_transform(
+ *,
+ eq_default: bool = True,
+ order_default: bool = False,
+ kw_only_default: bool = False,
+ field_specifiers: typing.Tuple[
+ typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
+ ...
+ ] = (),
+ **kwargs: typing.Any,
+ ) -> typing.Callable[[T], T]:
+ """Decorator that marks a function, class, or metaclass as providing
+ dataclass-like behavior.
+
+ Example:
+
+ from typing_extensions import dataclass_transform
+
+ _T = TypeVar("_T")
+
+ # Used on a decorator function
+ @dataclass_transform()
+ def create_model(cls: type[_T]) -> type[_T]:
+ ...
+ return cls
+
+ @create_model
+ class CustomerModel:
+ id: int
+ name: str
+
+ # Used on a base class
+ @dataclass_transform()
+ class ModelBase: ...
+
+ class CustomerModel(ModelBase):
+ id: int
+ name: str
+
+ # Used on a metaclass
+ @dataclass_transform()
+ class ModelMeta(type): ...
+
+ class ModelBase(metaclass=ModelMeta): ...
+
+ class CustomerModel(ModelBase):
+ id: int
+ name: str
+
+ Each of the ``CustomerModel`` classes defined in this example will now
+ behave similarly to a dataclass created with the ``@dataclasses.dataclass``
+ decorator. For example, the type checker will synthesize an ``__init__``
+ method.
+
+ The arguments to this decorator can be used to customize this behavior:
+ - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
+ True or False if it is omitted by the caller.
+ - ``order_default`` indicates whether the ``order`` parameter is
+ assumed to be True or False if it is omitted by the caller.
+ - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
+ assumed to be True or False if it is omitted by the caller.
+ - ``field_specifiers`` specifies a static list of supported classes
+ or functions that describe fields, similar to ``dataclasses.field()``.
+
+ At runtime, this decorator records its arguments in the
+ ``__dataclass_transform__`` attribute on the decorated object.
+
+ See PEP 681 for details.
+
+ """
+ def decorator(cls_or_fn):
+ cls_or_fn.__dataclass_transform__ = {
+ "eq_default": eq_default,
+ "order_default": order_default,
+ "kw_only_default": kw_only_default,
+ "field_specifiers": field_specifiers,
+ "kwargs": kwargs,
+ }
+ return cls_or_fn
+ return decorator
+
+
+if hasattr(typing, "override"):
+ override = typing.override
+else:
+ _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
+
+ def override(__arg: _F) -> _F:
+ """Indicate that a method is intended to override a method in a base class.
+
+ Usage:
+
+ class Base:
+ def method(self) -> None: ...
+ pass
+
+ class Child(Base):
+ @override
+ def method(self) -> None:
+ super().method()
+
+ When this decorator is applied to a method, the type checker will
+ validate that it overrides a method with the same name on a base class.
+ This helps prevent bugs that may occur when a base class is changed
+ without an equivalent change to a child class.
+
+ See PEP 698 for details.
+
+ """
+ return __arg
+
+
+# We have to do some monkey patching to deal with the dual nature of
+# Unpack/TypeVarTuple:
+# - We want Unpack to be a kind of TypeVar so it gets accepted in
+# Generic[Unpack[Ts]]
+# - We want it to *not* be treated as a TypeVar for the purposes of
+# counting generic parameters, so that when we subscript a generic,
+# the runtime doesn't try to substitute the Unpack with the subscripted type.
+if not hasattr(typing, "TypeVarTuple"):
+ typing._collect_type_vars = _collect_type_vars
+ typing._check_generic = _check_generic
+
+
+# Backport typing.NamedTuple as it exists in Python 3.11.
+# In 3.11, the ability to define generic `NamedTuple`s was supported.
+# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
+if sys.version_info >= (3, 11):
+ NamedTuple = typing.NamedTuple
+else:
+ def _caller():
+ try:
+ return sys._getframe(2).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError): # For platforms without _getframe()
+ return None
+
+ def _make_nmtuple(name, types, module, defaults=()):
+ fields = [n for n, t in types]
+ annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
+ for n, t in types}
+ nm_tpl = collections.namedtuple(name, fields,
+ defaults=defaults, module=module)
+ nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
+ # The `_field_types` attribute was removed in 3.9;
+ # in earlier versions, it is the same as the `__annotations__` attribute
+ if sys.version_info < (3, 9):
+ nm_tpl._field_types = annotations
+ return nm_tpl
+
+ _prohibited_namedtuple_fields = typing._prohibited
+ _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
+
+ class _NamedTupleMeta(type):
+ def __new__(cls, typename, bases, ns):
+ assert _NamedTuple in bases
+ for base in bases:
+ if base is not _NamedTuple and base is not typing.Generic:
+ raise TypeError(
+ 'can only inherit from a NamedTuple type and Generic')
+ bases = tuple(tuple if base is _NamedTuple else base for base in bases)
+ types = ns.get('__annotations__', {})
+ default_names = []
+ for field_name in types:
+ if field_name in ns:
+ default_names.append(field_name)
+ elif default_names:
+ raise TypeError(f"Non-default namedtuple field {field_name} "
+ f"cannot follow default field"
+ f"{'s' if len(default_names) > 1 else ''} "
+ f"{', '.join(default_names)}")
+ nm_tpl = _make_nmtuple(
+ typename, types.items(),
+ defaults=[ns[n] for n in default_names],
+ module=ns['__module__']
+ )
+ nm_tpl.__bases__ = bases
+ if typing.Generic in bases:
+ class_getitem = typing.Generic.__class_getitem__.__func__
+ nm_tpl.__class_getitem__ = classmethod(class_getitem)
+ # update from user namespace without overriding special namedtuple attributes
+ for key in ns:
+ if key in _prohibited_namedtuple_fields:
+ raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
+ elif key not in _special_namedtuple_fields and key not in nm_tpl._fields:
+ setattr(nm_tpl, key, ns[key])
+ if typing.Generic in bases:
+ nm_tpl.__init_subclass__()
+ return nm_tpl
+
+ def NamedTuple(__typename, __fields=None, **kwargs):
+ if __fields is None:
+ __fields = kwargs.items()
+ elif kwargs:
+ raise TypeError("Either list of fields or keywords"
+ " can be provided to NamedTuple, not both")
+ return _make_nmtuple(__typename, __fields, module=_caller())
+
+ NamedTuple.__doc__ = typing.NamedTuple.__doc__
+ _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
+
+ # On 3.8+, alter the signature so that it matches typing.NamedTuple.
+ # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7,
+ # so just leave the signature as it is on 3.7.
+ if sys.version_info >= (3, 8):
+ NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)'
+
+ def _namedtuple_mro_entries(bases):
+ assert NamedTuple in bases
+ return (_NamedTuple,)
+
+ NamedTuple.__mro_entries__ = _namedtuple_mro_entries
packaging==21.3
pyparsing==3.0.9
-appdirs==1.4.3
+
+platformdirs==2.6.2
+# required for platformdirs on Python < 3.8
+typing_extensions==4.4.0
+
jaraco.text==3.7.0
# required for jaraco.text on older Pythons
importlib_resources==5.4.0
-__pycache__/zipp.cpython-310.pyc,,\r
+__pycache__/zipp.cpython-311.pyc,,\r
zipp-3.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
zipp-3.7.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
zipp-3.7.0.dist-info/METADATA,sha256=ZLzgaXTyZX_MxTU0lcGfhdPY4CjFrT_3vyQ2Fo49pl8,2261\r
"""Return a module spec for vendored names."""
return (
importlib.util.spec_from_loader(fullname, self)
- if self._module_matches_namespace(fullname) else None
+ if self._module_matches_namespace(fullname)
+ else None
)
def install(self):
names = (
- 'packaging', 'pyparsing', 'appdirs', 'jaraco', 'importlib_resources',
+ 'packaging',
+ 'pyparsing',
+ 'platformdirs',
+ 'jaraco',
+ 'importlib_resources',
'more_itertools',
)
VendorImporter(__name__, names).install()
[metadata]
name = setuptools
-version = 65.7.0
+version = 66.0.0
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
importlib_metadata-4.11.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92\r
importlib_metadata-4.11.1.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19\r
importlib_metadata/__init__.py,sha256=Wkh_tb0u0Ds_615ByV9VLLjqgoOWirwMY8EW40oO3nM,30122\r
-importlib_metadata/__pycache__/__init__.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_adapters.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_collections.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_compat.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_functools.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_itertools.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_meta.cpython-310.pyc,,\r
-importlib_metadata/__pycache__/_text.cpython-310.pyc,,\r
+importlib_metadata/__pycache__/__init__.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_adapters.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_collections.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_compat.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_functools.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_itertools.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_meta.cpython-311.pyc,,\r
+importlib_metadata/__pycache__/_text.cpython-311.pyc,,\r
importlib_metadata/_adapters.py,sha256=B6fCi5-8mLVDFUZj3krI5nAo-mKp1dH_qIavyIyFrJs,1862\r
importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743\r
importlib_metadata/_compat.py,sha256=EU2XCFBPFByuI0Of6XkAuBYbzqSyjwwwwqmsK4ccna0,1826\r
importlib_resources-5.4.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
importlib_resources-5.4.0.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20\r
importlib_resources/__init__.py,sha256=zuA0lbRgtVVCcAztM0z5LuBiOCV9L_3qtI6mW2p5xAg,525\r
-importlib_resources/__pycache__/__init__.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_adapters.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_common.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_compat.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_itertools.cpython-310.pyc,,\r
-importlib_resources/__pycache__/_legacy.cpython-310.pyc,,\r
-importlib_resources/__pycache__/abc.cpython-310.pyc,,\r
-importlib_resources/__pycache__/readers.cpython-310.pyc,,\r
-importlib_resources/__pycache__/simple.cpython-310.pyc,,\r
+importlib_resources/__pycache__/__init__.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_adapters.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_common.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_compat.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_itertools.cpython-311.pyc,,\r
+importlib_resources/__pycache__/_legacy.cpython-311.pyc,,\r
+importlib_resources/__pycache__/abc.cpython-311.pyc,,\r
+importlib_resources/__pycache__/readers.cpython-311.pyc,,\r
+importlib_resources/__pycache__/simple.cpython-311.pyc,,\r
importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504\r
importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741\r
importlib_resources/_compat.py,sha256=3LpkIfeN9x4oXjRea5TxZP5VYhPlzuVRhGe-hEv-S0s,2704\r
importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566\r
importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836\r
importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/__pycache__/__init__.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/_compat.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_contents.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_files.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_open.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_path.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_read.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_reader.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/test_resource.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/update-zips.cpython-310.pyc,,\r
-importlib_resources/tests/__pycache__/util.cpython-310.pyc,,\r
+importlib_resources/tests/__pycache__/__init__.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/_compat.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_contents.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_files.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_open.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_path.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_read.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_reader.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/test_resource.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/update-zips.cpython-311.pyc,,\r
+importlib_resources/tests/__pycache__/util.cpython-311.pyc,,\r
importlib_resources/tests/_compat.py,sha256=QGI_4p0DXybypoYvw0kr3jfQqvls3p8u4wy4Wvf0Z_o,435\r
importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data01/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20\r
importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data02/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/one/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data02/one/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13\r
importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/two/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/data02/two/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13\r
importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
importlib_resources/tests/update-zips.py,sha256=x3iJVqWnMM5qp4Oob2Pl3o6Yi03sUjEv_5Wf-UCg3ps,1415\r
importlib_resources/tests/util.py,sha256=X1j-0C96pu3_tmtJuLhzfBfcfMenOphDLkxtCt5j7t4,5309\r
importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876\r
importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-310.pyc,,\r
+importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-311.pyc,,\r
importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698\r
+++ /dev/null
-Copyright Jason R. Coombs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
+++ /dev/null
-Metadata-Version: 2.1
-Name: jaraco.context
-Version: 4.1.1
-Summary: Context managers by jaraco
-Home-page: https://github.com/jaraco/jaraco.context
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
-License: UNKNOWN
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.6
-License-File: LICENSE
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/jaraco.context.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/jaraco.context
-
-.. image:: https://github.com/jaraco/jaraco.context/workflows/tests/badge.svg
- :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
- :alt: Code style: Black
-
-.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest
- :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
- :target: https://blog.jaraco.com/skeleton
-
-
+++ /dev/null
-jaraco.context-4.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-jaraco.context-4.1.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
-jaraco.context-4.1.1.dist-info/METADATA,sha256=bvqDGCk6Z7TkohUqr5XZm19SbF9mVxrtXjN6uF_BAMQ,2031\r
-jaraco.context-4.1.1.dist-info/RECORD,,\r
-jaraco.context-4.1.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-jaraco.context-4.1.1.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
-jaraco/__pycache__/context.cpython-310.pyc,,\r
-jaraco/context.py,sha256=7X1tpCLc5EN45iWGzGcsH0Unx62REIkvtRvglj0SiUA,5420\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
--- /dev/null
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
--- /dev/null
+Metadata-Version: 2.1
+Name: jaraco.context
+Version: 4.2.0
+Summary: Context managers by jaraco
+Home-page: https://github.com/jaraco/jaraco.context
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.context.svg
+ :target: https://pypi.org/project/jaraco.context
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg
+
+.. image:: https://github.com/jaraco/jaraco.context/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest
+ :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.context
+ :target: https://tidelift.com/subscription/pkg/pypi-jaraco.context?utm_source=pypi-jaraco.context&utm_medium=readme
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This 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.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-jaraco.context?utm_source=pypi-jaraco.context&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
--- /dev/null
+jaraco.context-4.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+jaraco.context-4.2.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
+jaraco.context-4.2.0.dist-info/METADATA,sha256=6gWxpGoBWTzl4e8J1HisvNsL79YB4t1DG7ziQj-0k9Y,2883\r
+jaraco.context-4.2.0.dist-info/RECORD,,\r
+jaraco.context-4.2.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92\r
+jaraco.context-4.2.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
+jaraco/__pycache__/context.cpython-311.pyc,,\r
+jaraco/context.py,sha256=NvdB7ArVCDrhtexOnOwSv4ScDuueGbf9LRiOSCqPn6Y,6488\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.38.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
+++ /dev/null
-Copyright Jason R. Coombs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
+++ /dev/null
-Metadata-Version: 2.1
-Name: jaraco.functools
-Version: 3.5.0
-Summary: Functools like those found in stdlib
-Home-page: https://github.com/jaraco/jaraco.functools
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
-License: UNKNOWN
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.7
-License-File: LICENSE
-Requires-Dist: more-itertools
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: jaraco.classes ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg
-
-.. image:: https://img.shields.io/travis/jaraco/jaraco.functools/master.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/jaraco.functools
-
-.. image:: https://github.com/jaraco/jaraco.functools/workflows/tests/badge.svg
- :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/psf/black
- :alt: Code style: Black
-
-.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest
- :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
- :target: https://blog.jaraco.com/skeleton
-
-Additional functools in the spirit of stdlib's functools.
-
-
+++ /dev/null
-jaraco.functools-3.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-jaraco.functools-3.5.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
-jaraco.functools-3.5.0.dist-info/METADATA,sha256=cE9C7u9bo_GjLAuw4nML67a25kUaPDiHn4j03lG4jd0,2276\r
-jaraco.functools-3.5.0.dist-info/RECORD,,\r
-jaraco.functools-3.5.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-jaraco.functools-3.5.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
-jaraco/__pycache__/functools.cpython-310.pyc,,\r
-jaraco/functools.py,sha256=PtEHbXZstgVJrwje4GvJOsz5pEbgslOcgEn2EJNpr2c,13494\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
--- /dev/null
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
--- /dev/null
+Metadata-Version: 2.1
+Name: jaraco.functools
+Version: 3.5.2
+Summary: Functools like those found in stdlib
+Home-page: https://github.com/jaraco/jaraco.functools
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: more-itertools
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: jaraco.classes ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg
+
+.. image:: https://img.shields.io/travis/jaraco/jaraco.functools/master.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.functools
+
+.. image:: https://github.com/jaraco/jaraco.functools/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest
+ :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.functools
+ :target: https://tidelift.com/subscription/pkg/pypi-jaraco.functools?utm_source=pypi-jaraco.functools&utm_medium=readme
+
+Additional functools in the spirit of stdlib's functools.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This 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.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-jaraco.functools?utm_source=pypi-jaraco.functools&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
--- /dev/null
+jaraco.functools-3.5.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+jaraco.functools-3.5.2.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
+jaraco.functools-3.5.2.dist-info/METADATA,sha256=ZIViwS4ZOmaWwA5ArwZ_xXQGR9WDnUSzx-0MO5kGPi8,3154\r
+jaraco.functools-3.5.2.dist-info/RECORD,,\r
+jaraco.functools-3.5.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92\r
+jaraco.functools-3.5.2.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
+jaraco/__pycache__/functools.cpython-311.pyc,,\r
+jaraco/functools.py,sha256=PtEHbXZstgVJrwje4GvJOsz5pEbgslOcgEn2EJNpr2c,13494\r
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
jaraco.text-3.7.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7\r
jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335\r
jaraco/text/__init__.py,sha256=I56MW2ZFwPrYXIxzqxMBe2A1t-T4uZBgEgAKe9-JoqM,15538\r
-jaraco/text/__pycache__/__init__.cpython-310.pyc,,\r
+jaraco/text/__pycache__/__init__.cpython-311.pyc,,\r
... {}['']
>>> key_error()
"""
+
+
+class on_interrupt(contextlib.ContextDecorator):
+ """
+ Replace a KeyboardInterrupt with SystemExit(1)
+
+ >>> def do_interrupt():
+ ... raise KeyboardInterrupt()
+ >>> on_interrupt('error')(do_interrupt)()
+ Traceback (most recent call last):
+ ...
+ SystemExit: 1
+ >>> on_interrupt('error', code=255)(do_interrupt)()
+ Traceback (most recent call last):
+ ...
+ SystemExit: 255
+ >>> on_interrupt('suppress')(do_interrupt)()
+ >>> with __import__('pytest').raises(KeyboardInterrupt):
+ ... on_interrupt('ignore')(do_interrupt)()
+ """
+
+ def __init__(
+ self,
+ action='error',
+ # py3.7 compat
+ # /,
+ code=1,
+ ):
+ self.action = action
+ self.code = code
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exctype, excinst, exctb):
+ if exctype is not KeyboardInterrupt or self.action == 'ignore':
+ return
+ elif self.action == 'error':
+ raise SystemExit(self.code) from excinst
+ return self.action == 'suppress'
more_itertools-8.8.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15\r
more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82\r
more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43\r
-more_itertools/__pycache__/__init__.cpython-310.pyc,,\r
-more_itertools/__pycache__/more.cpython-310.pyc,,\r
-more_itertools/__pycache__/recipes.cpython-310.pyc,,\r
+more_itertools/__pycache__/__init__.cpython-311.pyc,,\r
+more_itertools/__pycache__/more.cpython-311.pyc,,\r
+more_itertools/__pycache__/recipes.cpython-311.pyc,,\r
more_itertools/more.py,sha256=DlZa8v6JihVwfQ5zHidOA-xDE0orcQIUyxVnCaUoDKE,117968\r
more_itertools/more.pyi,sha256=r32pH2raBC1zih3evK4fyvAXvrUamJqc6dgV7QCRL_M,14977\r
more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
OrderedSet is automatically tested on Python 2.7, 3.4, 3.5, 3.6, and 3.7.
We've checked more informally that it works on PyPy and PyPy3.
-
-
-__pycache__/ordered_set.cpython-310.pyc,,\r
+__pycache__/ordered_set.cpython-311.pyc,,\r
ordered_set-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-ordered_set-3.1.1.dist-info/METADATA,sha256=uGvfFaNmhcl69lGdHmyOXc30N3U6Jn8DByfh_VHEPpw,5359\r
+ordered_set-3.1.1.dist-info/METADATA,sha256=qEaJM9CbGNixB_jvfohisKbXTUjcef6nCCcBJju6f4U,5357\r
ordered_set-3.1.1.dist-info/MIT-LICENSE,sha256=TvRE7qUSUBcd0ols7wgNf3zDEEJWW7kv7WDRySrMBBE,1071\r
ordered_set-3.1.1.dist-info/RECORD,,\r
ordered_set-3.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-ordered_set-3.1.1.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110\r
+ordered_set-3.1.1.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110\r
ordered_set-3.1.1.dist-info/top_level.txt,sha256=NTY2_aDi1Do9fl3Z9EmWPxasFkUeW2dzO2D3RDx5CfM,12\r
ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130\r
Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.1)
+Generator: bdist_wheel (0.38.4)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any
packaging-21.3.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10\r
packaging/__about__.py,sha256=ugASIO2w1oUyH8_COqQ2X_s0rDhjbhQC3yJocD03h2c,661\r
packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497\r
-packaging/__pycache__/__about__.cpython-310.pyc,,\r
-packaging/__pycache__/__init__.cpython-310.pyc,,\r
-packaging/__pycache__/_manylinux.cpython-310.pyc,,\r
-packaging/__pycache__/_musllinux.cpython-310.pyc,,\r
-packaging/__pycache__/_structures.cpython-310.pyc,,\r
-packaging/__pycache__/markers.cpython-310.pyc,,\r
-packaging/__pycache__/requirements.cpython-310.pyc,,\r
-packaging/__pycache__/specifiers.cpython-310.pyc,,\r
-packaging/__pycache__/tags.cpython-310.pyc,,\r
-packaging/__pycache__/utils.cpython-310.pyc,,\r
-packaging/__pycache__/version.cpython-310.pyc,,\r
+packaging/__pycache__/__about__.cpython-311.pyc,,\r
+packaging/__pycache__/__init__.cpython-311.pyc,,\r
+packaging/__pycache__/_manylinux.cpython-311.pyc,,\r
+packaging/__pycache__/_musllinux.cpython-311.pyc,,\r
+packaging/__pycache__/_structures.cpython-311.pyc,,\r
+packaging/__pycache__/markers.cpython-311.pyc,,\r
+packaging/__pycache__/requirements.cpython-311.pyc,,\r
+packaging/__pycache__/specifiers.cpython-311.pyc,,\r
+packaging/__pycache__/tags.cpython-311.pyc,,\r
+packaging/__pycache__/utils.cpython-311.pyc,,\r
+packaging/__pycache__/version.cpython-311.pyc,,\r
packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488\r
packaging/_musllinux.py,sha256=_KGgY_qc7vhMGpoqss25n2hiLCNKRtvz9mCrS7gkqyc,4378\r
packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431\r
-pyparsing-3.0.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-pyparsing-3.0.9.dist-info/LICENSE,sha256=ENUSChaAWAT_2otojCIL-06POXQbVzIGBNRVowngGXI,1023
-pyparsing-3.0.9.dist-info/METADATA,sha256=h_fpm9rwvgZsE8v5YNF4IAo-IpaFWCOfUEm5MMByIiM,4207
-pyparsing-3.0.9.dist-info/RECORD,,
-pyparsing-3.0.9.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-pyparsing-3.0.9.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81
-pyparsing/__init__.py,sha256=52QH3lgPbJhba0estckoGPHRH8JvQSSCGoWiEn2m0bU,9159
-pyparsing/__pycache__/__init__.cpython-38.pyc,,
-pyparsing/__pycache__/actions.cpython-38.pyc,,
-pyparsing/__pycache__/common.cpython-38.pyc,,
-pyparsing/__pycache__/core.cpython-38.pyc,,
-pyparsing/__pycache__/exceptions.cpython-38.pyc,,
-pyparsing/__pycache__/helpers.cpython-38.pyc,,
-pyparsing/__pycache__/results.cpython-38.pyc,,
-pyparsing/__pycache__/testing.cpython-38.pyc,,
-pyparsing/__pycache__/unicode.cpython-38.pyc,,
-pyparsing/__pycache__/util.cpython-38.pyc,,
-pyparsing/actions.py,sha256=wU9i32e0y1ymxKE3OUwSHO-SFIrt1h_wv6Ws0GQjpNU,6426
-pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936
-pyparsing/core.py,sha256=u8GptQE_H6wMkl8OZhxeK1aAPIDXXNgwdShORBwBVS4,213310
-pyparsing/diagram/__init__.py,sha256=f_EfxahqrdkRVahmTwLJXkZ9EEDKNd-O7lBbpJYlE1g,23668
-pyparsing/diagram/__pycache__/__init__.cpython-38.pyc,,
-pyparsing/exceptions.py,sha256=3LbSafD32NYb1Tzt85GHNkhEAU1eZkTtNSk24cPMemo,9023
-pyparsing/helpers.py,sha256=QpUOjW0-psvueMwWb9bQpU2noqKCv98_wnw1VSzSdVo,39129
-pyparsing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sGA30,25341
-pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402
-pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787
-pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805
+pyparsing-3.0.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+pyparsing-3.0.9.dist-info/LICENSE,sha256=ENUSChaAWAT_2otojCIL-06POXQbVzIGBNRVowngGXI,1023\r
+pyparsing-3.0.9.dist-info/METADATA,sha256=h_fpm9rwvgZsE8v5YNF4IAo-IpaFWCOfUEm5MMByIiM,4207\r
+pyparsing-3.0.9.dist-info/RECORD,,\r
+pyparsing-3.0.9.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+pyparsing-3.0.9.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81\r
+pyparsing/__init__.py,sha256=52QH3lgPbJhba0estckoGPHRH8JvQSSCGoWiEn2m0bU,9159\r
+pyparsing/__pycache__/__init__.cpython-311.pyc,,\r
+pyparsing/__pycache__/actions.cpython-311.pyc,,\r
+pyparsing/__pycache__/common.cpython-311.pyc,,\r
+pyparsing/__pycache__/core.cpython-311.pyc,,\r
+pyparsing/__pycache__/exceptions.cpython-311.pyc,,\r
+pyparsing/__pycache__/helpers.cpython-311.pyc,,\r
+pyparsing/__pycache__/results.cpython-311.pyc,,\r
+pyparsing/__pycache__/testing.cpython-311.pyc,,\r
+pyparsing/__pycache__/unicode.cpython-311.pyc,,\r
+pyparsing/__pycache__/util.cpython-311.pyc,,\r
+pyparsing/actions.py,sha256=wU9i32e0y1ymxKE3OUwSHO-SFIrt1h_wv6Ws0GQjpNU,6426\r
+pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936\r
+pyparsing/core.py,sha256=u8GptQE_H6wMkl8OZhxeK1aAPIDXXNgwdShORBwBVS4,213310\r
+pyparsing/diagram/__init__.py,sha256=f_EfxahqrdkRVahmTwLJXkZ9EEDKNd-O7lBbpJYlE1g,23668\r
+pyparsing/diagram/__pycache__/__init__.cpython-311.pyc,,\r
+pyparsing/exceptions.py,sha256=3LbSafD32NYb1Tzt85GHNkhEAU1eZkTtNSk24cPMemo,9023\r
+pyparsing/helpers.py,sha256=QpUOjW0-psvueMwWb9bQpU2noqKCv98_wnw1VSzSdVo,39129\r
+pyparsing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sGA30,25341\r
+pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402\r
+pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787\r
+pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805\r
-tomli-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-tomli-2.0.1.dist-info/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
-tomli-2.0.1.dist-info/METADATA,sha256=zPDceKmPwJGLWtZykrHixL7WVXWmJGzZ1jyRT5lCoPI,8875
-tomli-2.0.1.dist-info/RECORD,,
-tomli-2.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-tomli-2.0.1.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81
-tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396
-tomli/__pycache__/__init__.cpython-38.pyc,,
-tomli/__pycache__/_parser.cpython-38.pyc,,
-tomli/__pycache__/_re.cpython-38.pyc,,
-tomli/__pycache__/_types.cpython-38.pyc,,
-tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633
-tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943
-tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
-tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
+tomli-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
+tomli-2.0.1.dist-info/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072\r
+tomli-2.0.1.dist-info/METADATA,sha256=zPDceKmPwJGLWtZykrHixL7WVXWmJGzZ1jyRT5lCoPI,8875\r
+tomli-2.0.1.dist-info/RECORD,,\r
+tomli-2.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+tomli-2.0.1.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81\r
+tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396\r
+tomli/__pycache__/__init__.cpython-311.pyc,,\r
+tomli/__pycache__/_parser.cpython-311.pyc,,\r
+tomli/__pycache__/_re.cpython-311.pyc,,\r
+tomli/__pycache__/_types.cpython-311.pyc,,\r
+tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633\r
+tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943\r
+tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254\r
+tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26\r
-__pycache__/typing_extensions.cpython-310.pyc,,\r
+__pycache__/typing_extensions.cpython-311.pyc,,\r
typing_extensions-4.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
typing_extensions-4.0.1.dist-info/LICENSE,sha256=_xfOlOECAk3raHc-scx0ynbaTmWPNzUx8Kwi1oprsa0,12755\r
typing_extensions-4.0.1.dist-info/METADATA,sha256=iZ_5HONZZBXtF4kroz-IPZYIl9M8IE1B00R82dWcBqE,1736\r
-__pycache__/zipp.cpython-310.pyc,,\r
+__pycache__/zipp.cpython-311.pyc,,\r
zipp-3.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
zipp-3.7.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050\r
zipp-3.7.0.dist-info/METADATA,sha256=ZLzgaXTyZX_MxTU0lcGfhdPY4CjFrT_3vyQ2Fo49pl8,2261\r
if self.tag_build:
version += self.tag_build
if self.tag_date:
- version += time.strftime("-%Y%m%d")
+ version += time.strftime("%Y%m%d")
return version
vtags = property(tags)
def interpret_distro_name(
location, basename, metadata, py_version=None, precedence=SOURCE_DIST, platform=None
):
- """Generate alternative interpretations of a source distro name
+ """Generate the interpretation of a source distro name
Note: if `location` is a filesystem filename, you should call
``pkg_resources.normalize_path()`` on it before passing it to this
routine!
"""
- # Generate alternative interpretations of a source distro name
- # Because some packages are ambiguous as to name/versions split
- # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc.
- # So, we generate each possible interpretation (e.g. "adns, python-1.1.0"
- # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice,
- # the spurious interpretations should be ignored, because in the event
- # there's also an "adns" package, the spurious "python-1.1.0" version will
- # compare lower than any numeric version number, and is therefore unlikely
- # to match a request for it. It's still a potential problem, though, and
- # in the long run PyPI and the distutils should go for "safe" names and
- # versions in distribution archive names (sdist and bdist).
parts = basename.split('-')
if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]):
# it is a bdist_dumb, not an sdist -- bail out
return
- for p in range(1, len(parts) + 1):
- yield Distribution(
- location,
- metadata,
- '-'.join(parts[:p]),
- '-'.join(parts[p:]),
- py_version=py_version,
- precedence=precedence,
- platform=platform,
- )
+ # find the pivot (p) that splits the name from the version.
+ # infer the version as the first item that has a digit.
+ for p in range(len(parts)):
+ if parts[p][:1].isdigit():
+ break
+ else:
+ p = len(parts)
+
+ yield Distribution(
+ location,
+ metadata,
+ '-'.join(parts[:p]),
+ '-'.join(parts[p:]),
+ py_version=py_version,
+ precedence=precedence,
+ platform=platform
+ )
def unique_values(func):
class TestDistInfo:
- metadata_base = DALS("""
+ metadata_base = DALS(
+ """
Metadata-Version: 1.2
Requires-Dist: splort (==4)
Provides-Extra: baz
Requires-Dist: quux (>=1.1); extra == 'baz'
- """)
+ """
+ )
@classmethod
def build_metadata(cls, **kwargs):
- lines = (
- '{key}: {value}\n'.format(**locals())
- for key, value in kwargs.items()
- )
+ lines = ('{key}: {value}\n'.format(**locals()) for key, value in kwargs.items())
return cls.metadata_base + ''.join(lines)
@pytest.fixture
def test_distinfo(self, metadata):
dists = dict(
- (d.project_name, d)
- for d in pkg_resources.find_distributions(metadata)
+ (d.project_name, d) for d in pkg_resources.find_distributions(metadata)
)
assert len(dists) == 2, dists
assert d.extras == ['baz']
def test_invalid_version(self, tmp_path):
+ """
+ Supplying an invalid version crashes dist_info.
+ """
config = "[metadata]\nname=proj\nversion=42\n[egg_info]\ntag_build=invalid!!!\n"
(tmp_path / "setup.cfg").write_text(config, encoding="utf-8")
msg = re.compile("invalid version", re.M | re.I)
- output = run_command("dist_info", cwd=tmp_path)
- assert msg.search(output)
- dist_info = next(tmp_path.glob("*.dist-info"))
- assert dist_info.name.startswith("proj-42")
+ proc = run_command_inner("dist_info", cwd=tmp_path, check=False)
+ assert proc.returncode
+ assert msg.search(proc.stdout)
+ assert not list(tmp_path.glob("*.dist-info"))
def test_tag_arguments(self, tmp_path):
config = """
def test_output_dir(self, tmp_path, keep_egg_info):
config = "[metadata]\nname=proj\nversion=42\n"
(tmp_path / "setup.cfg").write_text(config, encoding="utf-8")
- out = (tmp_path / "__out")
+ out = tmp_path / "__out"
out.mkdir()
opts = ["--keep-egg-info"] if keep_egg_info else []
run_command("dist_info", "--output-dir", out, *opts, cwd=tmp_path)
"""Make sure the .dist-info directory produced with the ``dist_info`` command
is the same as the one produced by ``bdist_wheel``.
"""
- SETUPCFG = DALS("""
+
+ SETUPCFG = DALS(
+ """
[metadata]
name = {name}
version = {version}
executable-name = my_package.module:function
discover =
myproj = my_package.other_module:function
- """)
+ """
+ )
EGG_INFO_OPTS = [
# Related: #3088 #2872
assert read(dist_info / file) == read(wheel_dist_info / file)
-def run_command(*cmd, **kwargs):
- opts = {"stderr": subprocess.STDOUT, "text": True, **kwargs}
+def run_command_inner(*cmd, **kwargs):
+ opts = {
+ "stderr": subprocess.STDOUT,
+ "stdout": subprocess.PIPE,
+ "text": True,
+ 'check': True,
+ **kwargs,
+ }
cmd = [sys.executable, "-c", "__import__('setuptools').setup()", *map(str, cmd)]
- return subprocess.check_output(cmd, **opts)
+ return subprocess.run(cmd, **opts)
+
+
+def run_command(*args, **kwargs):
+ return run_command_inner(*args, **kwargs).stdout
for v, vc in versions:
dists = list(
setuptools.package_index.distros_for_url(
- 'http://example.com/example.zip#egg=example-' + v
+ 'http://example.com/example-foo.zip#egg=example-foo-' + v
)
)
assert dists[0].version == ''
more_file.write_text(text)
+def rewrite_platformdirs(pkg_files: Path):
+ """
+ Replace some absolute imports with relative ones.
+ """
+ init = pkg_files.joinpath('__init__.py')
+ text = init.read_text()
+ text = text.replace('from platformdirs.', 'from .')
+ text = text.replace('from typing_extensions', 'from ..typing_extensions')
+ init.write_text(text)
+
+
def clean(vendor):
"""
Remove all files out of the vendor directory except the meta
data (as pip uninstall doesn't support -t).
"""
- remove_all(
- path
- for path in vendor.glob('*')
- if path.basename() != 'vendored.txt'
- )
+ remove_all(path for path in vendor.glob('*') if path.basename() != 'vendored.txt')
def install(vendor):
clean(vendor)
install_args = [
sys.executable,
- '-m', 'pip',
+ '-m',
+ 'pip',
'install',
- '-r', str(vendor / 'vendored.txt'),
- '-t', str(vendor),
+ '-r',
+ str(vendor / 'vendored.txt'),
+ '-t',
+ str(vendor),
]
subprocess.check_call(install_args)
(vendor / '__init__.py').write_text('')
rewrite_jaraco(vendor / 'jaraco', 'pkg_resources.extern')
rewrite_importlib_resources(vendor / 'importlib_resources', 'pkg_resources.extern')
rewrite_more_itertools(vendor / "more_itertools")
+ rewrite_platformdirs(vendor / "platformdirs")
def update_setuptools():