mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Vendor in pip 23.2
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from typing import List, Optional
|
||||
|
||||
__version__ = "23.1.2"
|
||||
__version__ = "23.2"
|
||||
|
||||
|
||||
def main(args: Optional[List[str]] = None) -> int:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
# Remove '' and current working directory from the first entry
|
||||
# of sys.path, if present to avoid using current directory
|
||||
@@ -20,12 +19,6 @@ if __package__ == "":
|
||||
sys.path.insert(0, path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Work around the error reported in #9540, pending a proper fix.
|
||||
# Note: It is essential the warning filter is set *before* importing
|
||||
# pip, as the deprecation happens at import time, not runtime.
|
||||
warnings.filterwarnings(
|
||||
"ignore", category=DeprecationWarning, module=".*packaging\\.version"
|
||||
)
|
||||
import importlib.util
|
||||
import sys
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
|
||||
@@ -194,7 +194,17 @@ class CacheEntry:
|
||||
self.origin: Optional[DirectUrl] = None
|
||||
origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
|
||||
if origin_direct_url_path.exists():
|
||||
self.origin = DirectUrl.from_json(origin_direct_url_path.read_text())
|
||||
try:
|
||||
self.origin = DirectUrl.from_json(
|
||||
origin_direct_url_path.read_text(encoding="utf-8")
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Ignoring invalid cache entry origin file %s for %s (%s)",
|
||||
origin_direct_url_path,
|
||||
link.filename,
|
||||
e,
|
||||
)
|
||||
|
||||
|
||||
class WheelCache(Cache):
|
||||
@@ -257,16 +267,26 @@ class WheelCache(Cache):
|
||||
@staticmethod
|
||||
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
|
||||
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
|
||||
if origin_path.is_file():
|
||||
origin = DirectUrl.from_json(origin_path.read_text())
|
||||
# TODO: use DirectUrl.equivalent when https://github.com/pypa/pip/pull/10564
|
||||
# is merged.
|
||||
if origin.url != download_info.url:
|
||||
if origin_path.exists():
|
||||
try:
|
||||
origin = DirectUrl.from_json(origin_path.read_text(encoding="utf-8"))
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Origin URL %s in cache entry %s does not match download URL %s. "
|
||||
"This is likely a pip bug or a cache corruption issue.",
|
||||
origin.url,
|
||||
cache_dir,
|
||||
download_info.url,
|
||||
"Could not read origin file %s in cache entry (%s). "
|
||||
"Will attempt to overwrite it.",
|
||||
origin_path,
|
||||
e,
|
||||
)
|
||||
else:
|
||||
# TODO: use DirectUrl.equivalent when
|
||||
# https://github.com/pypa/pip/pull/10564 is merged.
|
||||
if origin.url != download_info.url:
|
||||
logger.warning(
|
||||
"Origin URL %s in cache entry %s does not match download URL "
|
||||
"%s. This is likely a pip bug or a cache corruption issue. "
|
||||
"Will overwrite it with the new value.",
|
||||
origin.url,
|
||||
cache_dir,
|
||||
download_info.url,
|
||||
)
|
||||
origin_path.write_text(download_info.to_json(), encoding="utf-8")
|
||||
|
||||
@@ -131,6 +131,17 @@ class Command(CommandContextMixIn):
|
||||
", ".join(sorted(always_enabled_features)),
|
||||
)
|
||||
|
||||
# Make sure that the --python argument isn't specified after the
|
||||
# subcommand. We can tell, because if --python was specified,
|
||||
# we should only reach this point if we're running in the created
|
||||
# subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment
|
||||
# variable set.
|
||||
if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
|
||||
logger.critical(
|
||||
"The --python option must be placed before the pip subcommand name"
|
||||
)
|
||||
sys.exit(ERROR)
|
||||
|
||||
# TODO: Try to get these passing down from the command?
|
||||
# without resorting to os.environ to hold these.
|
||||
# This also affects isolated builds and it should.
|
||||
|
||||
@@ -7,6 +7,7 @@ from pipenv.patched.pip._internal.cli.status_codes import ERROR, SUCCESS
|
||||
from pipenv.patched.pip._internal.operations.check import (
|
||||
check_package_set,
|
||||
create_package_set_from_installed,
|
||||
warn_legacy_versions_and_specifiers,
|
||||
)
|
||||
from pipenv.patched.pip._internal.utils.misc import write_output
|
||||
|
||||
@@ -21,6 +22,7 @@ class CheckCommand(Command):
|
||||
|
||||
def run(self, options: Values, args: List[str]) -> int:
|
||||
package_set, parsing_probs = create_package_set_from_installed()
|
||||
warn_legacy_versions_and_specifiers(package_set)
|
||||
missing, conflicting = check_package_set(package_set)
|
||||
|
||||
for project_name in missing:
|
||||
|
||||
@@ -22,15 +22,10 @@ COMPLETION_SCRIPTS = {
|
||||
complete -o default -F _pip_completion {prog}
|
||||
""",
|
||||
"zsh": """
|
||||
function _pip_completion {{
|
||||
local words cword
|
||||
read -Ac words
|
||||
read -cn cword
|
||||
reply=( $( COMP_WORDS="$words[*]" \\
|
||||
COMP_CWORD=$(( cword-1 )) \\
|
||||
PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null ))
|
||||
}}
|
||||
compctl -K _pip_completion {prog}
|
||||
#compdef -P pip[0-9.]#
|
||||
compadd $( COMP_WORDS="$words[*]" \\
|
||||
COMP_CWORD=$((CURRENT-1)) \\
|
||||
PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
|
||||
""",
|
||||
"fish": """
|
||||
function __fish_complete_pip
|
||||
|
||||
@@ -137,6 +137,10 @@ class DownloadCommand(RequirementCommand):
|
||||
assert req.name is not None
|
||||
preparer.save_linked_requirement(req)
|
||||
downloaded.append(req.name)
|
||||
|
||||
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
|
||||
requirement_set.warn_legacy_versions_and_specifiers()
|
||||
|
||||
if downloaded:
|
||||
write_output("Successfully downloaded %s", " ".join(downloaded))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sys
|
||||
from optparse import Values
|
||||
from typing import List
|
||||
from typing import AbstractSet, List
|
||||
|
||||
from pipenv.patched.pip._internal.cli import cmdoptions
|
||||
from pipenv.patched.pip._internal.cli.base_command import Command
|
||||
@@ -8,7 +8,18 @@ from pipenv.patched.pip._internal.cli.status_codes import SUCCESS
|
||||
from pipenv.patched.pip._internal.operations.freeze import freeze
|
||||
from pipenv.patched.pip._internal.utils.compat import stdlib_pkgs
|
||||
|
||||
DEV_PKGS = {"pip", "setuptools", "distribute", "wheel"}
|
||||
|
||||
def _should_suppress_build_backends() -> bool:
|
||||
return sys.version_info < (3, 12)
|
||||
|
||||
|
||||
def _dev_pkgs() -> AbstractSet[str]:
|
||||
pkgs = {"pip"}
|
||||
|
||||
if _should_suppress_build_backends():
|
||||
pkgs |= {"setuptools", "distribute", "wheel"}
|
||||
|
||||
return pkgs
|
||||
|
||||
|
||||
class FreezeCommand(Command):
|
||||
@@ -61,7 +72,7 @@ class FreezeCommand(Command):
|
||||
action="store_true",
|
||||
help=(
|
||||
"Do not skip these packages in the output:"
|
||||
" {}".format(", ".join(DEV_PKGS))
|
||||
" {}".format(", ".join(_dev_pkgs()))
|
||||
),
|
||||
)
|
||||
self.cmd_opts.add_option(
|
||||
@@ -77,7 +88,7 @@ class FreezeCommand(Command):
|
||||
def run(self, options: Values, args: List[str]) -> int:
|
||||
skip = set(stdlib_pkgs)
|
||||
if not options.freeze_all:
|
||||
skip.update(DEV_PKGS)
|
||||
skip.update(_dev_pkgs())
|
||||
|
||||
if options.excludes:
|
||||
skip.update(options.excludes)
|
||||
|
||||
@@ -387,6 +387,9 @@ class InstallCommand(RequirementCommand):
|
||||
json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
|
||||
|
||||
if options.dry_run:
|
||||
# In non dry-run mode, the legacy versions and specifiers check
|
||||
# will be done as part of conflict detection.
|
||||
requirement_set.warn_legacy_versions_and_specifiers()
|
||||
would_install_items = sorted(
|
||||
(r.metadata["name"], r.metadata["version"])
|
||||
for r in requirement_set.requirements_to_install
|
||||
|
||||
@@ -103,7 +103,10 @@ class ListCommand(IndexGroupCommand):
|
||||
dest="list_format",
|
||||
default="columns",
|
||||
choices=("columns", "freeze", "json"),
|
||||
help="Select the output format among: columns (default), freeze, or json",
|
||||
help=(
|
||||
"Select the output format among: columns (default), freeze, or json. "
|
||||
"The 'freeze' format cannot be used with the --outdated option."
|
||||
),
|
||||
)
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
@@ -157,7 +160,7 @@ class ListCommand(IndexGroupCommand):
|
||||
|
||||
if options.outdated and options.list_format == "freeze":
|
||||
raise CommandError(
|
||||
"List format 'freeze' can not be used with the --outdated option."
|
||||
"List format 'freeze' cannot be used with the --outdated option."
|
||||
)
|
||||
|
||||
cmdoptions.check_list_path_option(options)
|
||||
|
||||
@@ -153,6 +153,9 @@ class WheelCommand(RequirementCommand):
|
||||
elif should_build_for_wheel_command(req):
|
||||
reqs_to_build.append(req)
|
||||
|
||||
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
|
||||
requirement_set.warn_legacy_versions_and_specifiers()
|
||||
|
||||
# build wheels
|
||||
build_successes, build_failures = build(
|
||||
reqs_to_build,
|
||||
|
||||
@@ -210,8 +210,15 @@ class Configuration:
|
||||
# Ensure directory exists.
|
||||
ensure_dir(os.path.dirname(fname))
|
||||
|
||||
with open(fname, "w") as f:
|
||||
parser.write(f)
|
||||
# Ensure directory's permission(need to be writeable)
|
||||
try:
|
||||
with open(fname, "w") as f:
|
||||
parser.write(f)
|
||||
except OSError as error:
|
||||
raise ConfigurationError(
|
||||
f"An error occurred while writing to the configuration file "
|
||||
f"{fname}: {error}"
|
||||
)
|
||||
|
||||
#
|
||||
# Private routines
|
||||
|
||||
@@ -544,7 +544,7 @@ class HashMissing(HashError):
|
||||
# so the output can be directly copied into the requirements file.
|
||||
package = (
|
||||
self.req.original_link
|
||||
if self.req.original_link
|
||||
if self.req.is_direct
|
||||
# In case someone feeds something downright stupid
|
||||
# to InstallRequirement's constructor.
|
||||
else getattr(self.req, "req", None)
|
||||
|
||||
@@ -125,7 +125,6 @@ class LinkEvaluator:
|
||||
target_python: TargetPython,
|
||||
allow_yanked: bool,
|
||||
ignore_requires_python: Optional[bool] = None,
|
||||
ignore_compatibility: Optional[bool] = None,
|
||||
) -> None:
|
||||
"""
|
||||
:param project_name: The user supplied package name.
|
||||
@@ -143,8 +142,6 @@ class LinkEvaluator:
|
||||
:param ignore_requires_python: Whether to ignore incompatible
|
||||
PEP 503 "data-requires-python" values in HTML links. Defaults
|
||||
to False.
|
||||
:param ignore_compatibility: Whether to ignore
|
||||
compatibility of python versions and allow all versions of packages.
|
||||
"""
|
||||
if ignore_requires_python is None:
|
||||
ignore_requires_python = False
|
||||
@@ -154,7 +151,6 @@ class LinkEvaluator:
|
||||
self._ignore_requires_python = ignore_requires_python
|
||||
self._formats = formats
|
||||
self._target_python = target_python
|
||||
self._ignore_compatibility = ignore_compatibility
|
||||
|
||||
self.project_name = project_name
|
||||
|
||||
@@ -185,10 +181,10 @@ class LinkEvaluator:
|
||||
LinkType.format_unsupported,
|
||||
f"unsupported archive format: {ext}",
|
||||
)
|
||||
if "binary" not in self._formats and ext == WHEEL_EXTENSION and not self._ignore_compatibility:
|
||||
if "binary" not in self._formats and ext == WHEEL_EXTENSION :
|
||||
reason = f"No binaries permitted for {self.project_name}"
|
||||
return (LinkType.format_unsupported, reason)
|
||||
if "macosx10" in link.path and ext == ".zip" and not self._ignore_compatibility:
|
||||
if "macosx10" in link.path and ext == ".zip":
|
||||
return (LinkType.format_unsupported, "macosx10 one")
|
||||
if ext == WHEEL_EXTENSION:
|
||||
try:
|
||||
@@ -203,7 +199,7 @@ class LinkEvaluator:
|
||||
return (LinkType.different_project, reason)
|
||||
|
||||
supported_tags = self._target_python.get_tags()
|
||||
if not wheel.supported(supported_tags) and not self._ignore_compatibility:
|
||||
if not wheel.supported(supported_tags):
|
||||
# Include the wheel's tags in the reason string to
|
||||
# simplify troubleshooting compatibility issues.
|
||||
file_tags = ", ".join(wheel.get_formatted_file_tags())
|
||||
@@ -244,7 +240,7 @@ class LinkEvaluator:
|
||||
version_info=self._target_python.py_version_info,
|
||||
ignore_requires_python=self._ignore_requires_python,
|
||||
)
|
||||
if not supports_python and not self._ignore_compatibility:
|
||||
if not supports_python:
|
||||
reason = f"{version} Requires-Python {link.requires_python}"
|
||||
return (LinkType.requires_python_mismatch, reason)
|
||||
|
||||
@@ -493,7 +489,6 @@ class CandidateEvaluator:
|
||||
|
||||
def _sort_key(
|
||||
self, candidate: InstallationCandidate,
|
||||
ignore_compatibility: bool = True
|
||||
) -> CandidateSortingKey:
|
||||
"""
|
||||
Function to pass as the `key` argument to a call to sorted() to sort
|
||||
@@ -539,11 +534,10 @@ class CandidateEvaluator:
|
||||
)
|
||||
)
|
||||
except ValueError:
|
||||
if not ignore_compatibility:
|
||||
raise UnsupportedWheel(
|
||||
"{} is not a supported wheel for this platform. It "
|
||||
"can't be sorted.".format(wheel.filename)
|
||||
)
|
||||
raise UnsupportedWheel(
|
||||
"{} is not a supported wheel for this platform. It "
|
||||
"can't be sorted.".format(wheel.filename)
|
||||
)
|
||||
pri = -(support_num)
|
||||
if self._prefer_binary:
|
||||
binary_preference = 1
|
||||
@@ -611,7 +605,6 @@ class PackageFinder:
|
||||
format_control: Optional[FormatControl] = None,
|
||||
candidate_prefs: Optional[CandidatePreferences] = None,
|
||||
ignore_requires_python: Optional[bool] = None,
|
||||
ignore_compatibility: Optional[bool] = False
|
||||
) -> None:
|
||||
"""
|
||||
This constructor is primarily meant to be used by the create() class
|
||||
@@ -633,7 +626,6 @@ class PackageFinder:
|
||||
self._ignore_requires_python = ignore_requires_python
|
||||
self._link_collector = link_collector
|
||||
self._target_python = target_python
|
||||
self._ignore_compatibility = ignore_compatibility
|
||||
|
||||
self.format_control = format_control
|
||||
|
||||
@@ -734,7 +726,6 @@ class PackageFinder:
|
||||
target_python=self._target_python,
|
||||
allow_yanked=self._allow_yanked,
|
||||
ignore_requires_python=self._ignore_requires_python,
|
||||
ignore_compatibility=self._ignore_compatibility
|
||||
)
|
||||
|
||||
def _sort_links(self, links: Iterable[Link]) -> List[Link]:
|
||||
|
||||
@@ -151,7 +151,7 @@ def _emit_egg_deprecation(location: Optional[str]) -> None:
|
||||
deprecated(
|
||||
reason=f"Loading egg at {location} is deprecated.",
|
||||
replacement="to use pip for package installation.",
|
||||
gone_in=None,
|
||||
gone_in="23.3",
|
||||
)
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ class Environment(BaseEnvironment):
|
||||
for location in self._paths:
|
||||
yield from finder.find(location)
|
||||
for dist in finder.find_eggs(location):
|
||||
# _emit_egg_deprecation(dist.location) # TODO: Enable this.
|
||||
_emit_egg_deprecation(dist.location)
|
||||
yield dist
|
||||
# This must go last because that's how pkg_resources tie-breaks.
|
||||
yield from finder.find_linked(location)
|
||||
|
||||
@@ -22,7 +22,7 @@ class InstallationReport:
|
||||
# is_direct is true if the requirement was a direct URL reference (which
|
||||
# includes editable requirements), and false if the requirement was
|
||||
# downloaded from a PEP 503 index or --find-links.
|
||||
"is_direct": bool(ireq.original_link),
|
||||
"is_direct": ireq.is_direct,
|
||||
# requested is true if the requirement was specified by the user (aka
|
||||
# top level requirement), and false if it was installed as a dependency of a
|
||||
# requirement. https://peps.python.org/pep-0376/#requested
|
||||
|
||||
@@ -69,18 +69,6 @@ class LinkHash:
|
||||
def __post_init__(self) -> None:
|
||||
assert self.name in _SUPPORTED_HASHES
|
||||
|
||||
@classmethod
|
||||
def parse_pep658_hash(cls, dist_info_metadata: str) -> Optional["LinkHash"]:
|
||||
"""Parse a PEP 658 data-dist-info-metadata hash."""
|
||||
if dist_info_metadata == "true":
|
||||
return None
|
||||
name, sep, value = dist_info_metadata.partition("=")
|
||||
if not sep:
|
||||
return None
|
||||
if name not in _SUPPORTED_HASHES:
|
||||
return None
|
||||
return cls(name=name, value=value)
|
||||
|
||||
@classmethod
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]:
|
||||
@@ -107,6 +95,28 @@ class LinkHash:
|
||||
return hashes.is_hash_allowed(self.name, hex_digest=self.value)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MetadataFile:
|
||||
"""Information about a core metadata file associated with a distribution."""
|
||||
|
||||
hashes: Optional[Dict[str, str]]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.hashes is not None:
|
||||
assert all(name in _SUPPORTED_HASHES for name in self.hashes)
|
||||
|
||||
|
||||
def supported_hashes(hashes: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]:
|
||||
# Remove any unsupported hash types from the mapping. If this leaves no
|
||||
# supported hashes, return None
|
||||
if hashes is None:
|
||||
return None
|
||||
hashes = {n: v for n, v in hashes.items() if n in _SUPPORTED_HASHES}
|
||||
if not hashes:
|
||||
return None
|
||||
return hashes
|
||||
|
||||
|
||||
def _clean_url_path_part(part: str) -> str:
|
||||
"""
|
||||
Clean a "part" of a URL path (i.e. after splitting on "@" characters).
|
||||
@@ -179,7 +189,7 @@ class Link(KeyBasedCompareMixin):
|
||||
"comes_from",
|
||||
"requires_python",
|
||||
"yanked_reason",
|
||||
"dist_info_metadata",
|
||||
"metadata_file_data",
|
||||
"cache_link_parsing",
|
||||
"egg_fragment",
|
||||
]
|
||||
@@ -190,7 +200,7 @@ class Link(KeyBasedCompareMixin):
|
||||
comes_from: Optional[Union[str, "IndexContent"]] = None,
|
||||
requires_python: Optional[str] = None,
|
||||
yanked_reason: Optional[str] = None,
|
||||
dist_info_metadata: Optional[str] = None,
|
||||
metadata_file_data: Optional[MetadataFile] = None,
|
||||
cache_link_parsing: bool = True,
|
||||
hashes: Optional[Mapping[str, str]] = None,
|
||||
) -> None:
|
||||
@@ -208,11 +218,10 @@ class Link(KeyBasedCompareMixin):
|
||||
a simple repository HTML link. If the file has been yanked but
|
||||
no reason was provided, this should be the empty string. See
|
||||
PEP 592 for more information and the specification.
|
||||
:param dist_info_metadata: the metadata attached to the file, or None if no such
|
||||
metadata is provided. This is the value of the "data-dist-info-metadata"
|
||||
attribute, if present, in a simple repository HTML link. This may be parsed
|
||||
into its own `Link` by `self.metadata_link()`. See PEP 658 for more
|
||||
information and the specification.
|
||||
:param metadata_file_data: the metadata attached to the file, or None if
|
||||
no such metadata is provided. This argument, if not None, indicates
|
||||
that a separate metadata file exists, and also optionally supplies
|
||||
hashes for that file.
|
||||
:param cache_link_parsing: A flag that is used elsewhere to determine
|
||||
whether resources retrieved from this link should be cached. PyPI
|
||||
URLs should generally have this set to False, for example.
|
||||
@@ -220,6 +229,10 @@ class Link(KeyBasedCompareMixin):
|
||||
determine the validity of a download.
|
||||
"""
|
||||
|
||||
# The comes_from, requires_python, and metadata_file_data arguments are
|
||||
# only used by classmethods of this class, and are not used in client
|
||||
# code directly.
|
||||
|
||||
# url can be a UNC windows share
|
||||
if url.startswith("\\\\"):
|
||||
url = path_to_url(url)
|
||||
@@ -239,7 +252,7 @@ class Link(KeyBasedCompareMixin):
|
||||
self.comes_from = comes_from
|
||||
self.requires_python = requires_python if requires_python else None
|
||||
self.yanked_reason = yanked_reason
|
||||
self.dist_info_metadata = dist_info_metadata
|
||||
self.metadata_file_data = metadata_file_data
|
||||
|
||||
super().__init__(key=url, defining_class=Link)
|
||||
|
||||
@@ -262,9 +275,25 @@ class Link(KeyBasedCompareMixin):
|
||||
url = _ensure_quoted_url(urllib.parse.urljoin(page_url, file_url))
|
||||
pyrequire = file_data.get("requires-python")
|
||||
yanked_reason = file_data.get("yanked")
|
||||
dist_info_metadata = file_data.get("dist-info-metadata")
|
||||
hashes = file_data.get("hashes", {})
|
||||
|
||||
# PEP 714: Indexes must use the name core-metadata, but
|
||||
# clients should support the old name as a fallback for compatibility.
|
||||
metadata_info = file_data.get("core-metadata")
|
||||
if metadata_info is None:
|
||||
metadata_info = file_data.get("dist-info-metadata")
|
||||
|
||||
# The metadata info value may be a boolean, or a dict of hashes.
|
||||
if isinstance(metadata_info, dict):
|
||||
# The file exists, and hashes have been supplied
|
||||
metadata_file_data = MetadataFile(supported_hashes(metadata_info))
|
||||
elif metadata_info:
|
||||
# The file exists, but there are no hashes
|
||||
metadata_file_data = MetadataFile(None)
|
||||
else:
|
||||
# False or not present: the file does not exist
|
||||
metadata_file_data = None
|
||||
|
||||
# The Link.yanked_reason expects an empty string instead of a boolean.
|
||||
if yanked_reason and not isinstance(yanked_reason, str):
|
||||
yanked_reason = ""
|
||||
@@ -278,7 +307,7 @@ class Link(KeyBasedCompareMixin):
|
||||
requires_python=pyrequire,
|
||||
yanked_reason=yanked_reason,
|
||||
hashes=hashes,
|
||||
dist_info_metadata=dist_info_metadata,
|
||||
metadata_file_data=metadata_file_data,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -298,14 +327,39 @@ class Link(KeyBasedCompareMixin):
|
||||
url = _ensure_quoted_url(urllib.parse.urljoin(base_url, href))
|
||||
pyrequire = anchor_attribs.get("data-requires-python")
|
||||
yanked_reason = anchor_attribs.get("data-yanked")
|
||||
dist_info_metadata = anchor_attribs.get("data-dist-info-metadata")
|
||||
|
||||
# PEP 714: Indexes must use the name data-core-metadata, but
|
||||
# clients should support the old name as a fallback for compatibility.
|
||||
metadata_info = anchor_attribs.get("data-core-metadata")
|
||||
if metadata_info is None:
|
||||
metadata_info = anchor_attribs.get("data-dist-info-metadata")
|
||||
# The metadata info value may be the string "true", or a string of
|
||||
# the form "hashname=hashval"
|
||||
if metadata_info == "true":
|
||||
# The file exists, but there are no hashes
|
||||
metadata_file_data = MetadataFile(None)
|
||||
elif metadata_info is None:
|
||||
# The file does not exist
|
||||
metadata_file_data = None
|
||||
else:
|
||||
# The file exists, and hashes have been supplied
|
||||
hashname, sep, hashval = metadata_info.partition("=")
|
||||
if sep == "=":
|
||||
metadata_file_data = MetadataFile(supported_hashes({hashname: hashval}))
|
||||
else:
|
||||
# Error - data is wrong. Treat as no hashes supplied.
|
||||
logger.debug(
|
||||
"Index returned invalid data-dist-info-metadata value: %s",
|
||||
metadata_info,
|
||||
)
|
||||
metadata_file_data = MetadataFile(None)
|
||||
|
||||
return cls(
|
||||
url,
|
||||
comes_from=page_url,
|
||||
requires_python=pyrequire,
|
||||
yanked_reason=yanked_reason,
|
||||
dist_info_metadata=dist_info_metadata,
|
||||
metadata_file_data=metadata_file_data,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
@@ -407,17 +461,13 @@ class Link(KeyBasedCompareMixin):
|
||||
return match.group(1)
|
||||
|
||||
def metadata_link(self) -> Optional["Link"]:
|
||||
"""Implementation of PEP 658 parsing."""
|
||||
# Note that Link.from_element() parsing the "data-dist-info-metadata" attribute
|
||||
# from an HTML anchor tag is typically how the Link.dist_info_metadata attribute
|
||||
# gets set.
|
||||
if self.dist_info_metadata is None:
|
||||
"""Return a link to the associated core metadata file (if any)."""
|
||||
if self.metadata_file_data is None:
|
||||
return None
|
||||
metadata_url = f"{self.url_without_fragment}.metadata"
|
||||
metadata_link_hash = LinkHash.parse_pep658_hash(self.dist_info_metadata)
|
||||
if metadata_link_hash is None:
|
||||
if self.metadata_file_data.hashes is None:
|
||||
return Link(metadata_url)
|
||||
return Link(metadata_url, hashes=metadata_link_hash.as_dict())
|
||||
return Link(metadata_url, hashes=self.metadata_file_data.hashes)
|
||||
|
||||
def as_hashes(self) -> Hashes:
|
||||
return Hashes({k: [v] for k, v in self._hashes.items()})
|
||||
|
||||
@@ -136,6 +136,6 @@ class SearchScope:
|
||||
index_urls = self.index_urls
|
||||
if project_name in self.index_lookup:
|
||||
index_urls = [self.index_lookup[project_name]]
|
||||
else:
|
||||
elif self.index_urls:
|
||||
index_urls = [self.index_urls[0]]
|
||||
return [mkurl_pypi_url(url) for url in index_urls]
|
||||
|
||||
@@ -514,7 +514,9 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
|
||||
# Consume content and release the original connection to allow our new
|
||||
# request to reuse the same one.
|
||||
resp.content
|
||||
# The result of the assignment isn't used, it's just needed to consume
|
||||
# the content.
|
||||
_ = resp.content
|
||||
resp.raw.release_conn()
|
||||
|
||||
# Add our new username and password to the request
|
||||
|
||||
@@ -5,12 +5,15 @@ import logging
|
||||
from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple
|
||||
|
||||
from pipenv.patched.pip._vendor.packaging.requirements import Requirement
|
||||
from pipenv.patched.pip._vendor.packaging.specifiers import LegacySpecifier
|
||||
from pipenv.patched.pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
||||
from pipenv.patched.pip._vendor.packaging.version import LegacyVersion
|
||||
|
||||
from pipenv.patched.pip._internal.distributions import make_distribution_for_install_requirement
|
||||
from pipenv.patched.pip._internal.metadata import get_default_environment
|
||||
from pipenv.patched.pip._internal.metadata.base import DistributionVersion
|
||||
from pipenv.patched.pip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.pip._internal.utils.deprecation import deprecated
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -57,6 +60,8 @@ def check_package_set(
|
||||
package name and returns a boolean.
|
||||
"""
|
||||
|
||||
warn_legacy_versions_and_specifiers(package_set)
|
||||
|
||||
missing = {}
|
||||
conflicting = {}
|
||||
|
||||
@@ -147,3 +152,36 @@ def _create_whitelist(
|
||||
break
|
||||
|
||||
return packages_affected
|
||||
|
||||
|
||||
def warn_legacy_versions_and_specifiers(package_set: PackageSet) -> None:
|
||||
for project_name, package_details in package_set.items():
|
||||
if isinstance(package_details.version, LegacyVersion):
|
||||
deprecated(
|
||||
reason=(
|
||||
f"{project_name} {package_details.version} "
|
||||
f"has a non-standard version number."
|
||||
),
|
||||
replacement=(
|
||||
f"to upgrade to a newer version of {project_name} "
|
||||
f"or contact the author to suggest that they "
|
||||
f"release a version with a conforming version number"
|
||||
),
|
||||
issue=12063,
|
||||
gone_in="23.3",
|
||||
)
|
||||
for dep in package_details.dependencies:
|
||||
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
|
||||
deprecated(
|
||||
reason=(
|
||||
f"{project_name} {package_details.version} "
|
||||
f"has a non-standard dependency specifier {dep}."
|
||||
),
|
||||
replacement=(
|
||||
f"to upgrade to a newer version of {project_name} "
|
||||
f"or contact the author to suggest that they "
|
||||
f"release a version with a conforming dependency specifiers"
|
||||
),
|
||||
issue=12063,
|
||||
gone_in="23.3",
|
||||
)
|
||||
|
||||
@@ -352,7 +352,7 @@ class RequirementPreparer:
|
||||
# a surprising hash mismatch in the future.
|
||||
# file:/// URLs aren't pinnable, so don't complain about them
|
||||
# not being pinned.
|
||||
if req.original_link is None and not req.is_pinned:
|
||||
if not req.is_direct and not req.is_pinned:
|
||||
raise HashUnpinned()
|
||||
|
||||
# If known-good hashes are missing for this requirement,
|
||||
@@ -410,7 +410,7 @@ class RequirementPreparer:
|
||||
# NB: raw_name will fall back to the name from the install requirement if
|
||||
# the Name: field is not present, but it's noted in the raw_name docstring
|
||||
# that that should NEVER happen anyway.
|
||||
if metadata_dist.raw_name != req.req.name:
|
||||
if canonicalize_name(metadata_dist.raw_name) != canonicalize_name(req.req.name):
|
||||
raise MetadataInconsistent(
|
||||
req, "Name", req.req.name, metadata_dist.raw_name
|
||||
)
|
||||
@@ -471,6 +471,19 @@ class RequirementPreparer:
|
||||
logger.debug("Downloading link %s to %s", link, filepath)
|
||||
req = links_to_fully_download[link]
|
||||
req.local_file_path = filepath
|
||||
# TODO: This needs fixing for sdists
|
||||
# This is an emergency fix for #11847, which reports that
|
||||
# distributions get downloaded twice when metadata is loaded
|
||||
# from a PEP 658 standalone metadata file. Setting _downloaded
|
||||
# fixes this for wheels, but breaks the sdist case (tests
|
||||
# test_download_metadata). As PyPI is currently only serving
|
||||
# metadata for wheels, this is not an immediate issue.
|
||||
# Fixing the problem properly looks like it will require a
|
||||
# complete refactoring of the `prepare_linked_requirements_more`
|
||||
# logic, and I haven't a clue where to start on that, so for now
|
||||
# I have fixed the issue *just* for wheels.
|
||||
if req.is_wheel:
|
||||
self._downloaded[req.link.url] = filepath
|
||||
|
||||
# This step is necessary to ensure all lazy wheels are processed
|
||||
# successfully by the 'download', 'wheel', and 'install' commands.
|
||||
|
||||
@@ -104,6 +104,8 @@ class InstallRequirement:
|
||||
if link.is_file:
|
||||
self.source_dir = os.path.normpath(os.path.abspath(link.file_path))
|
||||
|
||||
# original_link is the direct URL that was provided by the user for the
|
||||
# requirement, either directly or via a constraints file.
|
||||
if link is None and req and req.url:
|
||||
# PEP 508 URL requirement
|
||||
link = Link(req.url)
|
||||
@@ -244,6 +246,11 @@ class InstallRequirement:
|
||||
def specifier(self) -> SpecifierSet:
|
||||
return self.req.specifier
|
||||
|
||||
@property
|
||||
def is_direct(self) -> bool:
|
||||
"""Whether this requirement was specified as a direct URL."""
|
||||
return self.original_link is not None
|
||||
|
||||
@property
|
||||
def is_pinned(self) -> bool:
|
||||
"""Return whether I am pinned to an exact version.
|
||||
@@ -293,7 +300,7 @@ class InstallRequirement:
|
||||
good_hashes = self.hash_options.copy()
|
||||
if trust_internet:
|
||||
link = self.link
|
||||
elif self.original_link and self.user_supplied:
|
||||
elif self.is_direct and self.user_supplied:
|
||||
link = self.original_link
|
||||
else:
|
||||
link = None
|
||||
@@ -507,7 +514,7 @@ class InstallRequirement:
|
||||
self.unpacked_source_directory,
|
||||
backend,
|
||||
backend_path=backend_path,
|
||||
python_executable=os.getenv('PIP_PYTHON_PATH', sys.executable)
|
||||
python_executable=os.getenv('PIPENV_PYTHON_PATH', sys.executable)
|
||||
)
|
||||
|
||||
def isolated_editable_sanity_check(self) -> None:
|
||||
@@ -805,7 +812,7 @@ class InstallRequirement:
|
||||
req_description=str(self.req),
|
||||
pycompile=pycompile,
|
||||
warn_script_location=warn_script_location,
|
||||
direct_url=self.download_info if self.original_link else None,
|
||||
direct_url=self.download_info if self.is_direct else None,
|
||||
requested=self.user_supplied,
|
||||
)
|
||||
self.install_succeeded = True
|
||||
|
||||
@@ -2,9 +2,12 @@ import logging
|
||||
from collections import OrderedDict
|
||||
from typing import Dict, List
|
||||
|
||||
from pipenv.patched.pip._vendor.packaging.specifiers import LegacySpecifier
|
||||
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
|
||||
from pipenv.patched.pip._vendor.packaging.version import LegacyVersion
|
||||
|
||||
from pipenv.patched.pip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.pip._internal.utils.deprecation import deprecated
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -80,3 +83,37 @@ class RequirementSet:
|
||||
for install_req in self.all_requirements
|
||||
if not install_req.constraint and not install_req.satisfied_by
|
||||
]
|
||||
|
||||
def warn_legacy_versions_and_specifiers(self) -> None:
|
||||
for req in self.requirements_to_install:
|
||||
version = req.get_dist().version
|
||||
if isinstance(version, LegacyVersion):
|
||||
deprecated(
|
||||
reason=(
|
||||
f"pip has selected the non standard version {version} "
|
||||
f"of {req}. In the future this version will be "
|
||||
f"ignored as it isn't standard compliant."
|
||||
),
|
||||
replacement=(
|
||||
"set or update constraints to select another version "
|
||||
"or contact the package author to fix the version number"
|
||||
),
|
||||
issue=12063,
|
||||
gone_in="23.3",
|
||||
)
|
||||
for dep in req.get_dist().iter_dependencies():
|
||||
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
|
||||
deprecated(
|
||||
reason=(
|
||||
f"pip has selected {req} {version} which has non "
|
||||
f"standard dependency specifier {dep}. "
|
||||
f"In the future this version of {req} will be "
|
||||
f"ignored as it isn't standard compliant."
|
||||
),
|
||||
replacement=(
|
||||
"set or update constraints to select another version "
|
||||
"or contact the package author to fix the version number"
|
||||
),
|
||||
issue=12063,
|
||||
gone_in="23.3",
|
||||
)
|
||||
|
||||
@@ -345,6 +345,7 @@ class AlreadyInstalledCandidate(Candidate):
|
||||
self.dist = dist
|
||||
self._ireq = _make_install_req_from_dist(dist, template)
|
||||
self._factory = factory
|
||||
self._version = None
|
||||
|
||||
# This is just logging some messages, so we can do it eagerly.
|
||||
# The returned dist would be exactly the same as self.dist because we
|
||||
@@ -380,7 +381,9 @@ class AlreadyInstalledCandidate(Candidate):
|
||||
|
||||
@property
|
||||
def version(self) -> CandidateVersion:
|
||||
return self.dist.version
|
||||
if self._version is None:
|
||||
self._version = self.dist.version
|
||||
return self._version
|
||||
|
||||
@property
|
||||
def is_editable(self) -> bool:
|
||||
|
||||
@@ -20,7 +20,7 @@ class PipReporter(BaseReporter):
|
||||
"requirements. This could take a while."
|
||||
),
|
||||
8: (
|
||||
"pip is looking at multiple versions of {package_name} to "
|
||||
"pip is still looking at multiple versions of {package_name} to "
|
||||
"determine which version is compatible with other "
|
||||
"requirements. This could take a while."
|
||||
),
|
||||
|
||||
@@ -159,6 +159,9 @@ class Resolver(BaseResolver):
|
||||
|
||||
reqs = req_set.all_requirements
|
||||
self.factory.preparer.prepare_linked_requirements_more(reqs)
|
||||
for req in reqs:
|
||||
req.prepared = True
|
||||
req.needs_more_preparation = False
|
||||
return req_set
|
||||
|
||||
def get_installation_order(
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# The following comment should be removed at some point in the future.
|
||||
# mypy: strict-optional=False
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, Tuple
|
||||
@@ -20,8 +17,11 @@ def glibc_version_string_confstr() -> Optional[str]:
|
||||
if sys.platform == "win32":
|
||||
return None
|
||||
try:
|
||||
gnu_libc_version = os.confstr("CS_GNU_LIBC_VERSION")
|
||||
if gnu_libc_version is None:
|
||||
return None
|
||||
# os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17":
|
||||
_, version = os.confstr("CS_GNU_LIBC_VERSION").split()
|
||||
_, version = gnu_libc_version.split()
|
||||
except (AttributeError, OSError, ValueError):
|
||||
# os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
|
||||
return None
|
||||
|
||||
@@ -127,10 +127,15 @@ def get_prog() -> str:
|
||||
# Tenacity raises RetryError by default, explicitly raise the original exception
|
||||
@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
|
||||
def rmtree(dir: str, ignore_errors: bool = False) -> None:
|
||||
shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler)
|
||||
if sys.version_info >= (3, 12):
|
||||
shutil.rmtree(dir, ignore_errors=ignore_errors, onexc=rmtree_errorhandler)
|
||||
else:
|
||||
shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler)
|
||||
|
||||
|
||||
def rmtree_errorhandler(func: Callable[..., Any], path: str, exc_info: ExcInfo) -> None:
|
||||
def rmtree_errorhandler(
|
||||
func: Callable[..., Any], path: str, exc_info: Union[ExcInfo, BaseException]
|
||||
) -> None:
|
||||
"""On Windows, the files in .svn are read-only, so when rmtree() tries to
|
||||
remove them, an exception is thrown. We catch that here, remove the
|
||||
read-only attribute, and hopefully continue without problems."""
|
||||
|
||||
@@ -31,7 +31,7 @@ class Mercurial(VersionControl):
|
||||
|
||||
@staticmethod
|
||||
def get_base_rev_args(rev: str) -> List[str]:
|
||||
return [rev]
|
||||
return ["-r", rev]
|
||||
|
||||
def fetch_new(
|
||||
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .core import contents, where
|
||||
|
||||
__all__ = ["contents", "where"]
|
||||
__version__ = "2022.12.07"
|
||||
__version__ = "2023.05.07"
|
||||
|
||||
@@ -4525,3 +4525,65 @@ BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu
|
||||
9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O
|
||||
be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
|
||||
# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
|
||||
# Label: "BJCA Global Root CA1"
|
||||
# Serial: 113562791157148395269083148143378328608
|
||||
# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90
|
||||
# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a
|
||||
# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU
|
||||
MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI
|
||||
T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz
|
||||
MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF
|
||||
SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh
|
||||
bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z
|
||||
xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ
|
||||
spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5
|
||||
58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR
|
||||
at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll
|
||||
5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq
|
||||
nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK
|
||||
V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/
|
||||
pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO
|
||||
z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn
|
||||
jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+
|
||||
WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF
|
||||
7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
|
||||
AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4
|
||||
YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli
|
||||
awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u
|
||||
+2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88
|
||||
X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN
|
||||
SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo
|
||||
P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI
|
||||
+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz
|
||||
znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9
|
||||
eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2
|
||||
YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy
|
||||
r/6zcCwupvI=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
|
||||
# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
|
||||
# Label: "BJCA Global Root CA2"
|
||||
# Serial: 58605626836079930195615843123109055211
|
||||
# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c
|
||||
# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6
|
||||
# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw
|
||||
CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ
|
||||
VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy
|
||||
MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ
|
||||
TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS
|
||||
b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B
|
||||
IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+
|
||||
+kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK
|
||||
sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
|
||||
AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA
|
||||
94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B
|
||||
43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
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
|
||||
|
||||
@@ -13,11 +13,8 @@ The package resource API is designed to work with normal filesystem packages,
|
||||
.zip files and with custom PEP 302 loaders that support the ``get_data()``
|
||||
method.
|
||||
|
||||
This module is deprecated. Users are directed to
|
||||
`importlib.resources <https://docs.python.org/3/library/importlib.resources.html>`_
|
||||
and
|
||||
`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
|
||||
instead.
|
||||
This module is deprecated. Users are directed to :mod:`importlib.resources`,
|
||||
:mod:`importlib.metadata` and :pypi:`packaging` instead.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -118,7 +115,12 @@ _namespace_handlers = None
|
||||
_namespace_packages = None
|
||||
|
||||
|
||||
warnings.warn("pkg_resources is deprecated as an API", DeprecationWarning)
|
||||
warnings.warn(
|
||||
"pkg_resources is deprecated as an API. "
|
||||
"See https://setuptools.pypa.io/en/latest/pkg_resources.html",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
|
||||
_PEP440_FALLBACK = re.compile(r"^v?(?P<safe>(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I)
|
||||
@@ -1659,10 +1661,9 @@ is not allowed.
|
||||
|
||||
# for compatibility, warn; in future
|
||||
# raise ValueError(msg)
|
||||
warnings.warn(
|
||||
issue_warning(
|
||||
msg[:-1] + " and will raise exceptions in a future release.",
|
||||
DeprecationWarning,
|
||||
stacklevel=4,
|
||||
)
|
||||
|
||||
def _get(self, path):
|
||||
|
||||
@@ -6,17 +6,20 @@ 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 pipenv.patched.pip._vendor.typing_extensions import Literal
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .api import PlatformDirsABC
|
||||
from .version import __version__
|
||||
from .version import __version_tuple__ as __version_info__
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
if sys.version_info >= (3, 8): # pragma: no cover (py38+)
|
||||
from typing import Literal
|
||||
else: # pragma: no cover (py38+)
|
||||
from pipenv.patched.pip._vendor.typing_extensions import Literal
|
||||
|
||||
|
||||
def _set_platform_dir_class() -> type[PlatformDirsABC]:
|
||||
if sys.platform == "win32":
|
||||
@@ -48,8 +51,8 @@ def user_data_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -72,8 +75,8 @@ def site_data_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
multipath: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -96,8 +99,8 @@ def user_config_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -120,8 +123,8 @@ def site_config_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
multipath: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -144,8 +147,8 @@ def user_cache_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -168,8 +171,8 @@ def site_cache_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -192,8 +195,8 @@ def user_state_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -216,8 +219,8 @@ def user_log_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -237,18 +240,36 @@ def user_log_dir(
|
||||
|
||||
|
||||
def user_documents_dir() -> str:
|
||||
"""
|
||||
:returns: documents directory tied to the user
|
||||
"""
|
||||
""":returns: documents directory tied to the user"""
|
||||
return PlatformDirs().user_documents_dir
|
||||
|
||||
|
||||
def user_downloads_dir() -> str:
|
||||
""":returns: downloads directory tied to the user"""
|
||||
return PlatformDirs().user_downloads_dir
|
||||
|
||||
|
||||
def user_pictures_dir() -> str:
|
||||
""":returns: pictures directory tied to the user"""
|
||||
return PlatformDirs().user_pictures_dir
|
||||
|
||||
|
||||
def user_videos_dir() -> str:
|
||||
""":returns: videos directory tied to the user"""
|
||||
return PlatformDirs().user_videos_dir
|
||||
|
||||
|
||||
def user_music_dir() -> str:
|
||||
""":returns: music directory tied to the user"""
|
||||
return PlatformDirs().user_music_dir
|
||||
|
||||
|
||||
def user_runtime_dir(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -271,8 +292,8 @@ def user_data_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -295,8 +316,8 @@ def site_data_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
multipath: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -319,8 +340,8 @@ def user_config_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -343,8 +364,8 @@ def site_config_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
multipath: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -367,8 +388,8 @@ def site_cache_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -391,8 +412,8 @@ def user_cache_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -415,8 +436,8 @@ def user_state_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -439,8 +460,8 @@ def user_log_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -460,18 +481,36 @@ def user_log_path(
|
||||
|
||||
|
||||
def user_documents_path() -> Path:
|
||||
"""
|
||||
:returns: documents path tied to the user
|
||||
"""
|
||||
""":returns: documents path tied to the user"""
|
||||
return PlatformDirs().user_documents_path
|
||||
|
||||
|
||||
def user_downloads_path() -> Path:
|
||||
""":returns: downloads path tied to the user"""
|
||||
return PlatformDirs().user_downloads_path
|
||||
|
||||
|
||||
def user_pictures_path() -> Path:
|
||||
""":returns: pictures path tied to the user"""
|
||||
return PlatformDirs().user_pictures_path
|
||||
|
||||
|
||||
def user_videos_path() -> Path:
|
||||
""":returns: videos path tied to the user"""
|
||||
return PlatformDirs().user_videos_path
|
||||
|
||||
|
||||
def user_music_path() -> Path:
|
||||
""":returns: music path tied to the user"""
|
||||
return PlatformDirs().user_music_path
|
||||
|
||||
|
||||
def user_runtime_path(
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
@@ -502,6 +541,10 @@ __all__ = [
|
||||
"user_state_dir",
|
||||
"user_log_dir",
|
||||
"user_documents_dir",
|
||||
"user_downloads_dir",
|
||||
"user_pictures_dir",
|
||||
"user_videos_dir",
|
||||
"user_music_dir",
|
||||
"user_runtime_dir",
|
||||
"site_data_dir",
|
||||
"site_config_dir",
|
||||
@@ -512,6 +555,10 @@ __all__ = [
|
||||
"user_state_path",
|
||||
"user_log_path",
|
||||
"user_documents_path",
|
||||
"user_downloads_path",
|
||||
"user_pictures_path",
|
||||
"user_videos_path",
|
||||
"user_music_path",
|
||||
"user_runtime_path",
|
||||
"site_data_path",
|
||||
"site_config_path",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"""Main entry point."""
|
||||
from __future__ import annotations
|
||||
|
||||
from pipenv.patched.pip._vendor.platformdirs import PlatformDirs, __version__
|
||||
@@ -9,6 +10,10 @@ PROPS = (
|
||||
"user_state_dir",
|
||||
"user_log_dir",
|
||||
"user_documents_dir",
|
||||
"user_downloads_dir",
|
||||
"user_pictures_dir",
|
||||
"user_videos_dir",
|
||||
"user_music_dir",
|
||||
"user_runtime_dir",
|
||||
"site_data_dir",
|
||||
"site_config_dir",
|
||||
@@ -17,30 +22,31 @@ PROPS = (
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run main entry point."""
|
||||
app_name = "MyApp"
|
||||
app_author = "MyCompany"
|
||||
|
||||
print(f"-- platformdirs {__version__} --")
|
||||
print(f"-- platformdirs {__version__} --") # noqa: T201
|
||||
|
||||
print("-- app dirs (with optional 'version')")
|
||||
print("-- app dirs (with optional 'version')") # noqa: T201
|
||||
dirs = PlatformDirs(app_name, app_author, version="1.0")
|
||||
for prop in PROPS:
|
||||
print(f"{prop}: {getattr(dirs, prop)}")
|
||||
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||
|
||||
print("\n-- app dirs (without optional 'version')")
|
||||
print("\n-- app dirs (without optional 'version')") # noqa: T201
|
||||
dirs = PlatformDirs(app_name, app_author)
|
||||
for prop in PROPS:
|
||||
print(f"{prop}: {getattr(dirs, prop)}")
|
||||
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||
|
||||
print("\n-- app dirs (without optional 'appauthor')")
|
||||
print("\n-- app dirs (without optional 'appauthor')") # noqa: T201
|
||||
dirs = PlatformDirs(app_name)
|
||||
for prop in PROPS:
|
||||
print(f"{prop}: {getattr(dirs, prop)}")
|
||||
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||
|
||||
print("\n-- app dirs (with disabled 'appauthor')")
|
||||
print("\n-- app dirs (with disabled 'appauthor')") # noqa: T201
|
||||
dirs = PlatformDirs(app_name, appauthor=False)
|
||||
for prop in PROPS:
|
||||
print(f"{prop}: {getattr(dirs, prop)}")
|
||||
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"""Android."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@@ -30,7 +31,8 @@ class Android(PlatformDirsABC):
|
||||
@property
|
||||
def user_config_dir(self) -> str:
|
||||
"""
|
||||
:return: config directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/shared_prefs/<AppName>``
|
||||
: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")
|
||||
|
||||
@@ -62,16 +64,34 @@ class Android(PlatformDirsABC):
|
||||
"""
|
||||
path = self.user_cache_dir
|
||||
if self.opinion:
|
||||
path = os.path.join(path, "log")
|
||||
path = os.path.join(path, "log") # noqa: PTH118
|
||||
return path
|
||||
|
||||
@property
|
||||
def user_documents_dir(self) -> str:
|
||||
"""
|
||||
:return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``
|
||||
"""
|
||||
""":return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``"""
|
||||
return _android_documents_folder()
|
||||
|
||||
@property
|
||||
def user_downloads_dir(self) -> str:
|
||||
""":return: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``"""
|
||||
return _android_downloads_folder()
|
||||
|
||||
@property
|
||||
def user_pictures_dir(self) -> str:
|
||||
""":return: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``"""
|
||||
return _android_pictures_folder()
|
||||
|
||||
@property
|
||||
def user_videos_dir(self) -> str:
|
||||
""":return: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``"""
|
||||
return _android_videos_folder()
|
||||
|
||||
@property
|
||||
def user_music_dir(self) -> str:
|
||||
""":return: music directory tied to the user e.g. ``/storage/emulated/0/Music``"""
|
||||
return _android_music_folder()
|
||||
|
||||
@property
|
||||
def user_runtime_dir(self) -> str:
|
||||
"""
|
||||
@@ -80,20 +100,20 @@ class Android(PlatformDirsABC):
|
||||
"""
|
||||
path = self.user_cache_dir
|
||||
if self.opinion:
|
||||
path = os.path.join(path, "tmp")
|
||||
path = os.path.join(path, "tmp") # noqa: PTH118
|
||||
return path
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _android_folder() -> str | None:
|
||||
""":return: base folder for the Android OS or None if cannot be found"""
|
||||
""":return: base folder for the Android OS or None if it 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:
|
||||
context = autoclass("android.content.Context")
|
||||
result: str | None = context.getFilesDir().getParentFile().getAbsolutePath()
|
||||
except Exception: # noqa: BLE001
|
||||
# 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:
|
||||
@@ -112,15 +132,79 @@ def _android_documents_folder() -> str:
|
||||
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:
|
||||
context = autoclass("android.content.Context")
|
||||
environment = autoclass("android.os.Environment")
|
||||
documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
|
||||
except Exception: # noqa: BLE001
|
||||
documents_dir = "/storage/emulated/0/Documents"
|
||||
|
||||
return documents_dir
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _android_downloads_folder() -> str:
|
||||
""":return: downloads folder for the Android OS"""
|
||||
# Get directories with pyjnius
|
||||
try:
|
||||
from jnius import autoclass
|
||||
|
||||
context = autoclass("android.content.Context")
|
||||
environment = autoclass("android.os.Environment")
|
||||
downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
|
||||
except Exception: # noqa: BLE001
|
||||
downloads_dir = "/storage/emulated/0/Downloads"
|
||||
|
||||
return downloads_dir
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _android_pictures_folder() -> str:
|
||||
""":return: pictures folder for the Android OS"""
|
||||
# Get directories with pyjnius
|
||||
try:
|
||||
from jnius import autoclass
|
||||
|
||||
context = autoclass("android.content.Context")
|
||||
environment = autoclass("android.os.Environment")
|
||||
pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath()
|
||||
except Exception: # noqa: BLE001
|
||||
pictures_dir = "/storage/emulated/0/Pictures"
|
||||
|
||||
return pictures_dir
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _android_videos_folder() -> str:
|
||||
""":return: videos folder for the Android OS"""
|
||||
# Get directories with pyjnius
|
||||
try:
|
||||
from jnius import autoclass
|
||||
|
||||
context = autoclass("android.content.Context")
|
||||
environment = autoclass("android.os.Environment")
|
||||
videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath()
|
||||
except Exception: # noqa: BLE001
|
||||
videos_dir = "/storage/emulated/0/DCIM/Camera"
|
||||
|
||||
return videos_dir
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _android_music_folder() -> str:
|
||||
""":return: music folder for the Android OS"""
|
||||
# Get directories with pyjnius
|
||||
try:
|
||||
from jnius import autoclass
|
||||
|
||||
context = autoclass("android.content.Context")
|
||||
environment = autoclass("android.os.Environment")
|
||||
music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath()
|
||||
except Exception: # noqa: BLE001
|
||||
music_dir = "/storage/emulated/0/Music"
|
||||
|
||||
return music_dir
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Android",
|
||||
]
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
"""Base API."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if sys.version_info >= (3, 8): # pragma: no branch
|
||||
from typing import Literal # pragma: no cover
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3, 8): # pragma: no cover (py38+)
|
||||
from typing import Literal
|
||||
else: # pragma: no cover (py38+)
|
||||
from pipenv.patched.pip._vendor.typing_extensions import Literal
|
||||
|
||||
|
||||
class PlatformDirsABC(ABC):
|
||||
"""
|
||||
Abstract base class for platform directories.
|
||||
"""
|
||||
"""Abstract base class for platform directories."""
|
||||
|
||||
def __init__(
|
||||
def __init__( # noqa: PLR0913
|
||||
self,
|
||||
appname: str | None = None,
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
multipath: bool = False,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
):
|
||||
roaming: bool = False, # noqa: FBT001, FBT002
|
||||
multipath: bool = False, # noqa: FBT001, FBT002
|
||||
opinion: bool = True, # noqa: FBT001, FBT002
|
||||
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||
) -> None:
|
||||
"""
|
||||
Create a new platform directory.
|
||||
|
||||
@@ -70,7 +74,7 @@ class PlatformDirsABC(ABC):
|
||||
params.append(self.appname)
|
||||
if self.version:
|
||||
params.append(self.version)
|
||||
path = os.path.join(base[0], *params)
|
||||
path = os.path.join(base[0], *params) # noqa: PTH118
|
||||
self._optionally_create_directory(path)
|
||||
return path
|
||||
|
||||
@@ -123,6 +127,26 @@ class PlatformDirsABC(ABC):
|
||||
def user_documents_dir(self) -> str:
|
||||
""":return: documents directory tied to the user"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def user_downloads_dir(self) -> str:
|
||||
""":return: downloads directory tied to the user"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def user_pictures_dir(self) -> str:
|
||||
""":return: pictures directory tied to the user"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def user_videos_dir(self) -> str:
|
||||
""":return: videos directory tied to the user"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def user_music_dir(self) -> str:
|
||||
""":return: music directory tied to the user"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def user_runtime_dir(self) -> str:
|
||||
@@ -173,6 +197,26 @@ class PlatformDirsABC(ABC):
|
||||
""":return: documents path tied to the user"""
|
||||
return Path(self.user_documents_dir)
|
||||
|
||||
@property
|
||||
def user_downloads_path(self) -> Path:
|
||||
""":return: downloads path tied to the user"""
|
||||
return Path(self.user_downloads_dir)
|
||||
|
||||
@property
|
||||
def user_pictures_path(self) -> Path:
|
||||
""":return: pictures path tied to the user"""
|
||||
return Path(self.user_pictures_dir)
|
||||
|
||||
@property
|
||||
def user_videos_path(self) -> Path:
|
||||
""":return: videos path tied to the user"""
|
||||
return Path(self.user_videos_dir)
|
||||
|
||||
@property
|
||||
def user_music_path(self) -> Path:
|
||||
""":return: music path tied to the user"""
|
||||
return Path(self.user_music_dir)
|
||||
|
||||
@property
|
||||
def user_runtime_path(self) -> Path:
|
||||
""":return: runtime path tied to the user"""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""macOS."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from .api import PlatformDirsABC
|
||||
|
||||
@@ -17,7 +18,7 @@ class MacOS(PlatformDirsABC):
|
||||
@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"))
|
||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111
|
||||
|
||||
@property
|
||||
def site_data_dir(self) -> str:
|
||||
@@ -37,7 +38,7 @@ class MacOS(PlatformDirsABC):
|
||||
@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"))
|
||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches")) # noqa: PTH111
|
||||
|
||||
@property
|
||||
def site_cache_dir(self) -> str:
|
||||
@@ -52,17 +53,37 @@ class MacOS(PlatformDirsABC):
|
||||
@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"))
|
||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) # noqa: PTH111
|
||||
|
||||
@property
|
||||
def user_documents_dir(self) -> str:
|
||||
""":return: documents directory tied to the user, e.g. ``~/Documents``"""
|
||||
return os.path.expanduser("~/Documents")
|
||||
return os.path.expanduser("~/Documents") # noqa: PTH111
|
||||
|
||||
@property
|
||||
def user_downloads_dir(self) -> str:
|
||||
""":return: downloads directory tied to the user, e.g. ``~/Downloads``"""
|
||||
return os.path.expanduser("~/Downloads") # noqa: PTH111
|
||||
|
||||
@property
|
||||
def user_pictures_dir(self) -> str:
|
||||
""":return: pictures directory tied to the user, e.g. ``~/Pictures``"""
|
||||
return os.path.expanduser("~/Pictures") # noqa: PTH111
|
||||
|
||||
@property
|
||||
def user_videos_dir(self) -> str:
|
||||
""":return: videos directory tied to the user, e.g. ``~/Movies``"""
|
||||
return os.path.expanduser("~/Movies") # noqa: PTH111
|
||||
|
||||
@property
|
||||
def user_music_dir(self) -> str:
|
||||
""":return: music directory tied to the user, e.g. ``~/Music``"""
|
||||
return os.path.expanduser("~/Music") # noqa: PTH111
|
||||
|
||||
@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"))
|
||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) # noqa: PTH111
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"""Unix."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@@ -7,12 +8,14 @@ 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:
|
||||
if sys.platform == "win32":
|
||||
|
||||
def getuid() -> int:
|
||||
raise RuntimeError("should only be used on Linux")
|
||||
msg = "should only be used on Unix"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
else:
|
||||
from os import getuid
|
||||
|
||||
|
||||
class Unix(PlatformDirsABC):
|
||||
@@ -36,7 +39,7 @@ class Unix(PlatformDirsABC):
|
||||
"""
|
||||
path = os.environ.get("XDG_DATA_HOME", "")
|
||||
if not path.strip():
|
||||
path = os.path.expanduser("~/.local/share")
|
||||
path = os.path.expanduser("~/.local/share") # noqa: PTH111
|
||||
return self._append_app_name_and_version(path)
|
||||
|
||||
@property
|
||||
@@ -56,7 +59,7 @@ class Unix(PlatformDirsABC):
|
||||
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]
|
||||
path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list] # noqa: PTH111
|
||||
return os.pathsep.join(path_list)
|
||||
|
||||
@property
|
||||
@@ -67,7 +70,7 @@ class Unix(PlatformDirsABC):
|
||||
"""
|
||||
path = os.environ.get("XDG_CONFIG_HOME", "")
|
||||
if not path.strip():
|
||||
path = os.path.expanduser("~/.config")
|
||||
path = os.path.expanduser("~/.config") # noqa: PTH111
|
||||
return self._append_app_name_and_version(path)
|
||||
|
||||
@property
|
||||
@@ -91,15 +94,13 @@ class Unix(PlatformDirsABC):
|
||||
"""
|
||||
path = os.environ.get("XDG_CACHE_HOME", "")
|
||||
if not path.strip():
|
||||
path = os.path.expanduser("~/.cache")
|
||||
path = os.path.expanduser("~/.cache") # noqa: PTH111
|
||||
return self._append_app_name_and_version(path)
|
||||
|
||||
@property
|
||||
def site_cache_dir(self) -> str:
|
||||
"""
|
||||
:return: cache directory shared by users, e.g. ``/var/tmp/$appname/$version``
|
||||
"""
|
||||
return self._append_app_name_and_version("/var/tmp")
|
||||
""":return: cache directory shared by users, e.g. ``/var/tmp/$appname/$version``"""
|
||||
return self._append_app_name_and_version("/var/tmp") # noqa: S108
|
||||
|
||||
@property
|
||||
def user_state_dir(self) -> str:
|
||||
@@ -109,41 +110,60 @@ class Unix(PlatformDirsABC):
|
||||
"""
|
||||
path = os.environ.get("XDG_STATE_HOME", "")
|
||||
if not path.strip():
|
||||
path = os.path.expanduser("~/.local/state")
|
||||
path = os.path.expanduser("~/.local/state") # noqa: PTH111
|
||||
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
|
||||
"""
|
||||
""":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")
|
||||
path = os.path.join(path, "log") # noqa: PTH118
|
||||
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 directory tied to the user, e.g. ``~/Documents``"""
|
||||
return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents")
|
||||
|
||||
return documents_dir
|
||||
@property
|
||||
def user_downloads_dir(self) -> str:
|
||||
""":return: downloads directory tied to the user, e.g. ``~/Downloads``"""
|
||||
return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads")
|
||||
|
||||
@property
|
||||
def user_pictures_dir(self) -> str:
|
||||
""":return: pictures directory tied to the user, e.g. ``~/Pictures``"""
|
||||
return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures")
|
||||
|
||||
@property
|
||||
def user_videos_dir(self) -> str:
|
||||
""":return: videos directory tied to the user, e.g. ``~/Videos``"""
|
||||
return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos")
|
||||
|
||||
@property
|
||||
def user_music_dir(self) -> str:
|
||||
""":return: music directory tied to the user, e.g. ``~/Music``"""
|
||||
return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music")
|
||||
|
||||
@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``
|
||||
``$XDG_RUNTIME_DIR/$appname/$version``.
|
||||
|
||||
For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if
|
||||
exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR``
|
||||
is not set.
|
||||
"""
|
||||
path = os.environ.get("XDG_RUNTIME_DIR", "")
|
||||
if not path.strip():
|
||||
path = f"/run/user/{getuid()}"
|
||||
if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
|
||||
path = f"/var/run/user/{getuid()}"
|
||||
if not Path(path).exists():
|
||||
path = f"/tmp/runtime-{getuid()}" # noqa: S108
|
||||
else:
|
||||
path = f"/run/user/{getuid()}"
|
||||
return self._append_app_name_and_version(path)
|
||||
|
||||
@property
|
||||
@@ -168,13 +188,23 @@ class Unix(PlatformDirsABC):
|
||||
return Path(directory)
|
||||
|
||||
|
||||
def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
|
||||
media_dir = _get_user_dirs_folder(env_var)
|
||||
if media_dir is None:
|
||||
media_dir = os.environ.get(env_var, "").strip()
|
||||
if not media_dir:
|
||||
media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111
|
||||
|
||||
return media_dir
|
||||
|
||||
|
||||
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):
|
||||
"""Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/."""
|
||||
user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs"
|
||||
if user_dirs_config_path.exists():
|
||||
parser = ConfigParser()
|
||||
|
||||
with open(user_dirs_config_path) as stream:
|
||||
with user_dirs_config_path.open() as stream:
|
||||
# Add fake section header, so ConfigParser doesn't complain
|
||||
parser.read_string(f"[top]\n{stream.read()}")
|
||||
|
||||
@@ -183,8 +213,7 @@ def _get_user_dirs_folder(key: str) -> str | None:
|
||||
|
||||
path = parser["top"][key].strip('"')
|
||||
# Handle relative home paths
|
||||
path = path.replace("$HOME", os.path.expanduser("~"))
|
||||
return path
|
||||
return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# file generated by setuptools_scm
|
||||
# don't change, don't track in version control
|
||||
__version__ = version = '3.2.0'
|
||||
__version_tuple__ = version_tuple = (3, 2, 0)
|
||||
__version__ = version = '3.8.1'
|
||||
__version_tuple__ = version_tuple = (3, 8, 1)
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
"""Windows."""
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from typing import Callable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .api import PlatformDirsABC
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
|
||||
class Windows(PlatformDirsABC):
|
||||
"""`MSDN on where to store app data files
|
||||
"""
|
||||
`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>`,
|
||||
@@ -43,7 +48,7 @@ class Windows(PlatformDirsABC):
|
||||
params.append(opinion_value)
|
||||
if self.version:
|
||||
params.append(self.version)
|
||||
path = os.path.join(path, *params)
|
||||
path = os.path.join(path, *params) # noqa: PTH118
|
||||
self._optionally_create_directory(path)
|
||||
return path
|
||||
|
||||
@@ -85,36 +90,53 @@ class Windows(PlatformDirsABC):
|
||||
|
||||
@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
|
||||
"""
|
||||
""":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")
|
||||
path = os.path.join(path, "Logs") # noqa: PTH118
|
||||
self._optionally_create_directory(path)
|
||||
return path
|
||||
|
||||
@property
|
||||
def user_documents_dir(self) -> str:
|
||||
"""
|
||||
:return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``
|
||||
"""
|
||||
""":return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``"""
|
||||
return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
|
||||
|
||||
@property
|
||||
def user_downloads_dir(self) -> str:
|
||||
""":return: downloads directory tied to the user e.g. ``%USERPROFILE%\\Downloads``"""
|
||||
return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS"))
|
||||
|
||||
@property
|
||||
def user_pictures_dir(self) -> str:
|
||||
""":return: pictures directory tied to the user e.g. ``%USERPROFILE%\\Pictures``"""
|
||||
return os.path.normpath(get_win_folder("CSIDL_MYPICTURES"))
|
||||
|
||||
@property
|
||||
def user_videos_dir(self) -> str:
|
||||
""":return: videos directory tied to the user e.g. ``%USERPROFILE%\\Videos``"""
|
||||
return os.path.normpath(get_win_folder("CSIDL_MYVIDEO"))
|
||||
|
||||
@property
|
||||
def user_music_dir(self) -> str:
|
||||
""":return: music directory tied to the user e.g. ``%USERPROFILE%\\Music``"""
|
||||
return os.path.normpath(get_win_folder("CSIDL_MYMUSIC"))
|
||||
|
||||
@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"))
|
||||
path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) # noqa: PTH118
|
||||
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")
|
||||
result = get_win_folder_if_csidl_name_not_env_var(csidl_name)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
env_var_name = {
|
||||
"CSIDL_APPDATA": "APPDATA",
|
||||
@@ -122,28 +144,54 @@ def get_win_folder_from_env_vars(csidl_name: str) -> str:
|
||||
"CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
|
||||
}.get(csidl_name)
|
||||
if env_var_name is None:
|
||||
raise ValueError(f"Unknown CSIDL name: {csidl_name}")
|
||||
msg = f"Unknown CSIDL name: {csidl_name}"
|
||||
raise ValueError(msg)
|
||||
result = os.environ.get(env_var_name)
|
||||
if result is None:
|
||||
raise ValueError(f"Unset environment variable: {env_var_name}")
|
||||
msg = f"Unset environment variable: {env_var_name}"
|
||||
raise ValueError(msg)
|
||||
return result
|
||||
|
||||
|
||||
def get_win_folder_from_registry(csidl_name: str) -> str:
|
||||
"""Get folder from the registry.
|
||||
def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None:
|
||||
"""Get folder for a CSIDL name that does not exist as an environment variable."""
|
||||
if csidl_name == "CSIDL_PERSONAL":
|
||||
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") # noqa: PTH118
|
||||
|
||||
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 csidl_name == "CSIDL_DOWNLOADS":
|
||||
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads") # noqa: PTH118
|
||||
|
||||
if csidl_name == "CSIDL_MYPICTURES":
|
||||
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures") # noqa: PTH118
|
||||
|
||||
if csidl_name == "CSIDL_MYVIDEO":
|
||||
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos") # noqa: PTH118
|
||||
|
||||
if csidl_name == "CSIDL_MYMUSIC":
|
||||
return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music") # noqa: PTH118
|
||||
return None
|
||||
|
||||
|
||||
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 these 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",
|
||||
"CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
|
||||
"CSIDL_MYPICTURES": "My Pictures",
|
||||
"CSIDL_MYVIDEO": "My Video",
|
||||
"CSIDL_MYMUSIC": "My Music",
|
||||
}.get(csidl_name)
|
||||
if shell_folder_name is None:
|
||||
raise ValueError(f"Unknown CSIDL name: {csidl_name}")
|
||||
msg = f"Unknown CSIDL name: {csidl_name}"
|
||||
raise ValueError(msg)
|
||||
if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows
|
||||
raise NotImplementedError
|
||||
import winreg
|
||||
@@ -155,25 +203,37 @@ def get_win_folder_from_registry(csidl_name: str) -> str:
|
||||
|
||||
def get_win_folder_via_ctypes(csidl_name: str) -> str:
|
||||
"""Get folder with ctypes."""
|
||||
# There is no 'CSIDL_DOWNLOADS'.
|
||||
# Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead.
|
||||
# https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
|
||||
|
||||
csidl_const = {
|
||||
"CSIDL_APPDATA": 26,
|
||||
"CSIDL_COMMON_APPDATA": 35,
|
||||
"CSIDL_LOCAL_APPDATA": 28,
|
||||
"CSIDL_PERSONAL": 5,
|
||||
"CSIDL_MYPICTURES": 39,
|
||||
"CSIDL_MYVIDEO": 14,
|
||||
"CSIDL_MYMUSIC": 13,
|
||||
"CSIDL_DOWNLOADS": 40,
|
||||
}.get(csidl_name)
|
||||
if csidl_const is None:
|
||||
raise ValueError(f"Unknown CSIDL name: {csidl_name}")
|
||||
msg = f"Unknown CSIDL name: {csidl_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
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):
|
||||
if any(ord(c) > 255 for c in buf): # noqa: PLR2004
|
||||
buf2 = ctypes.create_unicode_buffer(1024)
|
||||
if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
|
||||
buf = buf2
|
||||
|
||||
if csidl_name == "CSIDL_DOWNLOADS":
|
||||
return os.path.join(buf.value, "Downloads") # noqa: PTH118
|
||||
|
||||
return buf.value
|
||||
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
.. _Pygments master branch:
|
||||
https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
__version__ = '2.14.0'
|
||||
__version__ = '2.15.1'
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
__all__ = ['lex', 'format', 'highlight']
|
||||
@@ -34,7 +34,9 @@ __all__ = ['lex', 'format', 'highlight']
|
||||
|
||||
def lex(code, lexer):
|
||||
"""
|
||||
Lex ``code`` with ``lexer`` and return an iterable of tokens.
|
||||
Lex `code` with the `lexer` (must be a `Lexer` instance)
|
||||
and return an iterable of tokens. Currently, this only calls
|
||||
`lexer.get_tokens()`.
|
||||
"""
|
||||
try:
|
||||
return lexer.get_tokens(code)
|
||||
@@ -49,11 +51,12 @@ def lex(code, lexer):
|
||||
|
||||
def format(tokens, formatter, outfile=None): # pylint: disable=redefined-builtin
|
||||
"""
|
||||
Format a tokenlist ``tokens`` with the formatter ``formatter``.
|
||||
Format ``tokens`` (an iterable of tokens) with the formatter ``formatter``
|
||||
(a `Formatter` instance).
|
||||
|
||||
If ``outfile`` is given and a valid file object (an object
|
||||
with a ``write`` method), the result will be written to it, otherwise
|
||||
it is returned as a string.
|
||||
If ``outfile`` is given and a valid file object (an object with a
|
||||
``write`` method), the result will be written to it, otherwise it
|
||||
is returned as a string.
|
||||
"""
|
||||
try:
|
||||
if not outfile:
|
||||
@@ -73,10 +76,7 @@ def format(tokens, formatter, outfile=None): # pylint: disable=redefined-builti
|
||||
|
||||
def highlight(code, lexer, formatter, outfile=None):
|
||||
"""
|
||||
Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``.
|
||||
|
||||
If ``outfile`` is given and a valid file object (an object
|
||||
with a ``write`` method), the result will be written to it, otherwise
|
||||
it is returned as a string.
|
||||
This is the most high-level highlighting function. It combines `lex` and
|
||||
`format` in one function.
|
||||
"""
|
||||
return format(lex(code, lexer), formatter, outfile)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Main entry point for ``python -m pygments``.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Command line interface.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -185,7 +185,7 @@ def main_inner(parser, argns):
|
||||
return 0
|
||||
|
||||
if argns.V:
|
||||
print('Pygments version %s, (c) 2006-2022 by Georg Brandl, Matthäus '
|
||||
print('Pygments version %s, (c) 2006-2023 by Georg Brandl, Matthäus '
|
||||
'Chajdas and contributors.' % __version__)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Format colored console output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Module that implements the default filter.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Module containing filter lookup functions and default
|
||||
filters.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Base formatter class.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -26,7 +26,21 @@ class Formatter:
|
||||
"""
|
||||
Converts a token stream to text.
|
||||
|
||||
Options accepted:
|
||||
Formatters should have attributes to help selecting them. These
|
||||
are similar to the corresponding :class:`~pygments.lexer.Lexer`
|
||||
attributes.
|
||||
|
||||
.. autoattribute:: name
|
||||
:no-value:
|
||||
|
||||
.. autoattribute:: aliases
|
||||
:no-value:
|
||||
|
||||
.. autoattribute:: filenames
|
||||
:no-value:
|
||||
|
||||
You can pass options as keyword arguments to the constructor.
|
||||
All formatters accept these basic options:
|
||||
|
||||
``style``
|
||||
The style to use, can be a string or a Style subclass
|
||||
@@ -47,15 +61,19 @@ class Formatter:
|
||||
support (default: None).
|
||||
``outencoding``
|
||||
Overrides ``encoding`` if given.
|
||||
|
||||
"""
|
||||
|
||||
#: Name of the formatter
|
||||
#: Full name for the formatter, in human-readable form.
|
||||
name = None
|
||||
|
||||
#: Shortcuts for the formatter
|
||||
#: A list of short, unique identifiers that can be used to lookup
|
||||
#: the formatter from a list, e.g. using :func:`.get_formatter_by_name()`.
|
||||
aliases = []
|
||||
|
||||
#: fn match rules
|
||||
#: A list of fnmatch patterns that match filenames for which this
|
||||
#: formatter can produce output. The patterns in this list should be unique
|
||||
#: among all formatters.
|
||||
filenames = []
|
||||
|
||||
#: If True, this formatter outputs Unicode strings when no encoding
|
||||
@@ -63,6 +81,11 @@ class Formatter:
|
||||
unicodeoutput = True
|
||||
|
||||
def __init__(self, **options):
|
||||
"""
|
||||
As with lexers, this constructor takes arbitrary optional arguments,
|
||||
and if you override it, you should first process your own options, then
|
||||
call the base class implementation.
|
||||
"""
|
||||
self.style = _lookup_style(options.get('style', 'default'))
|
||||
self.full = get_bool_opt(options, 'full', False)
|
||||
self.title = options.get('title', '')
|
||||
@@ -75,18 +98,25 @@ class Formatter:
|
||||
|
||||
def get_style_defs(self, arg=''):
|
||||
"""
|
||||
Return the style definitions for the current style as a string.
|
||||
This method must return statements or declarations suitable to define
|
||||
the current style for subsequent highlighted text (e.g. CSS classes
|
||||
in the `HTMLFormatter`).
|
||||
|
||||
``arg`` is an additional argument whose meaning depends on the
|
||||
formatter used. Note that ``arg`` can also be a list or tuple
|
||||
for some formatters like the html formatter.
|
||||
The optional argument `arg` can be used to modify the generation and
|
||||
is formatter dependent (it is standardized because it can be given on
|
||||
the command line).
|
||||
|
||||
This method is called by the ``-S`` :doc:`command-line option <cmdline>`,
|
||||
the `arg` is then given by the ``-a`` option.
|
||||
"""
|
||||
return ''
|
||||
|
||||
def format(self, tokensource, outfile):
|
||||
"""
|
||||
Format ``tokensource``, an iterable of ``(tokentype, tokenstring)``
|
||||
tuples and write it into ``outfile``.
|
||||
This method must format the tokens from the `tokensource` iterable and
|
||||
write the formatted version to the file object `outfile`.
|
||||
|
||||
Formatter options can control how exactly the tokens are converted.
|
||||
"""
|
||||
if self.encoding:
|
||||
# wrap the outfile in a StreamWriter
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
|
||||
Pygments formatters.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
from fnmatch import fnmatch
|
||||
import fnmatch
|
||||
from os.path import basename
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.formatters._mapping import FORMATTERS
|
||||
@@ -21,6 +22,16 @@ __all__ = ['get_formatter_by_name', 'get_formatter_for_filename',
|
||||
'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS)
|
||||
|
||||
_formatter_cache = {} # classes by name
|
||||
_pattern_cache = {}
|
||||
|
||||
|
||||
def _fn_matches(fn, glob):
|
||||
"""Return whether the supplied file name fn matches pattern filename."""
|
||||
if glob not in _pattern_cache:
|
||||
pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob))
|
||||
return pattern.match(fn)
|
||||
return _pattern_cache[glob].match(fn)
|
||||
|
||||
|
||||
def _load_formatters(module_name):
|
||||
"""Load a formatter (and all others in the module too)."""
|
||||
@@ -57,9 +68,12 @@ def find_formatter_class(alias):
|
||||
|
||||
|
||||
def get_formatter_by_name(_alias, **options):
|
||||
"""Lookup and instantiate a formatter by alias.
|
||||
"""
|
||||
Return an instance of a :class:`.Formatter` subclass that has `alias` in its
|
||||
aliases list. The formatter is given the `options` at its instantiation.
|
||||
|
||||
Raises ClassNotFound if not found.
|
||||
Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that
|
||||
alias is found.
|
||||
"""
|
||||
cls = find_formatter_class(_alias)
|
||||
if cls is None:
|
||||
@@ -67,19 +81,18 @@ def get_formatter_by_name(_alias, **options):
|
||||
return cls(**options)
|
||||
|
||||
|
||||
def load_formatter_from_file(filename, formattername="CustomFormatter",
|
||||
**options):
|
||||
"""Load a formatter from a file.
|
||||
def load_formatter_from_file(filename, formattername="CustomFormatter", **options):
|
||||
"""
|
||||
Return a `Formatter` subclass instance loaded from the provided file, relative
|
||||
to the current directory.
|
||||
|
||||
This method expects a file located relative to the current working
|
||||
directory, which contains a class named CustomFormatter. By default,
|
||||
it expects the Formatter to be named CustomFormatter; you can specify
|
||||
your own class name as the second argument to this function.
|
||||
The file is expected to contain a Formatter class named ``formattername``
|
||||
(by default, CustomFormatter). Users should be very careful with the input, because
|
||||
this method is equivalent to running ``eval()`` on the input file. The formatter is
|
||||
given the `options` at its instantiation.
|
||||
|
||||
Users should be very careful with the input, because this method
|
||||
is equivalent to running eval on the input file.
|
||||
|
||||
Raises ClassNotFound if there are any problems importing the Formatter.
|
||||
:exc:`pygments.util.ClassNotFound` is raised if there are any errors loading
|
||||
the formatter.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
"""
|
||||
@@ -104,20 +117,23 @@ def load_formatter_from_file(filename, formattername="CustomFormatter",
|
||||
|
||||
|
||||
def get_formatter_for_filename(fn, **options):
|
||||
"""Lookup and instantiate a formatter by filename pattern.
|
||||
"""
|
||||
Return a :class:`.Formatter` subclass instance that has a filename pattern
|
||||
matching `fn`. The formatter is given the `options` at its instantiation.
|
||||
|
||||
Raises ClassNotFound if not found.
|
||||
Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename
|
||||
is found.
|
||||
"""
|
||||
fn = basename(fn)
|
||||
for modname, name, _, filenames, _ in FORMATTERS.values():
|
||||
for filename in filenames:
|
||||
if fnmatch(fn, filename):
|
||||
if _fn_matches(fn, filename):
|
||||
if name not in _formatter_cache:
|
||||
_load_formatters(modname)
|
||||
return _formatter_cache[name](**options)
|
||||
for cls in find_plugin_formatters():
|
||||
for filename in cls.filenames:
|
||||
if fnmatch(fn, filename):
|
||||
if _fn_matches(fn, filename):
|
||||
return cls(**options)
|
||||
raise ClassNotFound("no formatter found for file name %r" % fn)
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Automatically generated by scripts/gen_mapfiles.py.
|
||||
# DO NOT EDIT BY HAND; run `make mapfiles` instead.
|
||||
# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead.
|
||||
|
||||
FORMATTERS = {
|
||||
'BBCodeFormatter': ('pygments.formatters.bbcode', 'BBCode', ('bbcode', 'bb'), (), 'Format tokens with BBcodes. These formatting codes are used by many bulletin boards, so you can highlight your sourcecode with pygments before posting it there.'),
|
||||
'BmpImageFormatter': ('pygments.formatters.img', 'img_bmp', ('bmp', 'bitmap'), ('*.bmp',), 'Create a bitmap image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
|
||||
'GifImageFormatter': ('pygments.formatters.img', 'img_gif', ('gif',), ('*.gif',), 'Create a GIF image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
|
||||
'GroffFormatter': ('pygments.formatters.groff', 'groff', ('groff', 'troff', 'roff'), (), 'Format tokens with groff escapes to change their color and font style.'),
|
||||
'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "Format tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass` option."),
|
||||
'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "Format tokens as HTML 4 ``<span>`` tags. By default, the content is enclosed in a ``<pre>`` tag, itself wrapped in a ``<div>`` tag (but see the `nowrap` option). The ``<div>``'s CSS class can be set by the `cssclass` option."),
|
||||
'IRCFormatter': ('pygments.formatters.irc', 'IRC', ('irc', 'IRC'), (), 'Format tokens with IRC color sequences'),
|
||||
'ImageFormatter': ('pygments.formatters.img', 'img', ('img', 'IMG', 'png'), ('*.png',), 'Create a PNG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
|
||||
'JpgImageFormatter': ('pygments.formatters.img', 'img_jpg', ('jpg', 'jpeg'), ('*.jpg',), 'Create a JPEG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
BBcode formatter.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for groff output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -84,7 +84,7 @@ class GroffFormatter(Formatter):
|
||||
if ndef['color'] is not None:
|
||||
colors.add(ndef['color'])
|
||||
|
||||
for color in colors:
|
||||
for color in sorted(colors):
|
||||
outfile.write('.defcolor ' + color + ' rgb #' + color + '\n')
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for HTML output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -62,7 +62,7 @@ def _get_ttype_class(ttype):
|
||||
CSSFILE_TEMPLATE = '''\
|
||||
/*
|
||||
generated by Pygments <https://pygments.org/>
|
||||
Copyright 2006-2022 by the Pygments team.
|
||||
Copyright 2006-2023 by the Pygments team.
|
||||
Licensed under the BSD license, see LICENSE for details.
|
||||
*/
|
||||
%(styledefs)s
|
||||
@@ -73,7 +73,7 @@ DOC_HEADER = '''\
|
||||
"http://www.w3.org/TR/html4/strict.dtd">
|
||||
<!--
|
||||
generated by Pygments <https://pygments.org/>
|
||||
Copyright 2006-2022 by the Pygments team.
|
||||
Copyright 2006-2023 by the Pygments team.
|
||||
Licensed under the BSD license, see LICENSE for details.
|
||||
-->
|
||||
<html>
|
||||
@@ -112,9 +112,9 @@ DOC_FOOTER = '''\
|
||||
|
||||
class HtmlFormatter(Formatter):
|
||||
r"""
|
||||
Format tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped
|
||||
in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass`
|
||||
option.
|
||||
Format tokens as HTML 4 ``<span>`` tags. By default, the content is enclosed
|
||||
in a ``<pre>`` tag, itself wrapped in a ``<div>`` tag (but see the `nowrap` option).
|
||||
The ``<div>``'s CSS class can be set by the `cssclass` option.
|
||||
|
||||
If the `linenos` option is set to ``"table"``, the ``<pre>`` is
|
||||
additionally wrapped inside a ``<table>`` which has one row and two
|
||||
@@ -140,8 +140,6 @@ class HtmlFormatter(Formatter):
|
||||
|
||||
(whitespace added to improve clarity).
|
||||
|
||||
Wrapping can be disabled using the `nowrap` option.
|
||||
|
||||
A list of lines can be specified using the `hl_lines` option to make these
|
||||
lines highlighted (as of Pygments 0.11).
|
||||
|
||||
@@ -187,8 +185,8 @@ class HtmlFormatter(Formatter):
|
||||
Additional options accepted:
|
||||
|
||||
`nowrap`
|
||||
If set to ``True``, don't wrap the tokens at all, not even inside a ``<pre>``
|
||||
tag. This disables most other options (default: ``False``).
|
||||
If set to ``True``, don't add a ``<pre>`` and a ``<div>`` tag
|
||||
around the tokens. This disables most other options (default: ``False``).
|
||||
|
||||
`full`
|
||||
Tells the formatter to output a "full" document, i.e. a complete
|
||||
@@ -635,7 +633,7 @@ class HtmlFormatter(Formatter):
|
||||
# write CSS file only if noclobber_cssfile isn't given as an option.
|
||||
try:
|
||||
if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
|
||||
with open(cssfilename, "w") as cf:
|
||||
with open(cssfilename, "w", encoding="utf-8") as cf:
|
||||
cf.write(CSSFILE_TEMPLATE %
|
||||
{'styledefs': self.get_style_defs('body')})
|
||||
except OSError as err:
|
||||
@@ -721,7 +719,7 @@ class HtmlFormatter(Formatter):
|
||||
yield 0, dummyoutfile.getvalue()
|
||||
yield 0, '</div>'
|
||||
yield 0, '</td></tr></table>'
|
||||
|
||||
|
||||
|
||||
def _wrap_inlinelinenos(self, inner):
|
||||
# need a list of lines since we need the width of a single number :(
|
||||
@@ -946,9 +944,9 @@ class HtmlFormatter(Formatter):
|
||||
output = source
|
||||
if self.wrapcode:
|
||||
output = self._wrap_code(output)
|
||||
|
||||
|
||||
output = self._wrap_pre(output)
|
||||
|
||||
|
||||
return output
|
||||
|
||||
def format_unencoded(self, tokensource, outfile):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for Pixmap output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for IRC output
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for LaTeX fancyvrb output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Other formatters: NullFormatter, RawTokenFormatter.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for Pango markup output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
A formatter that generates RTF files.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for SVG output.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Formatter for terminal output with ANSI sequences.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
Formatter version 1.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Base lexer classes.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -50,7 +50,31 @@ class Lexer(metaclass=LexerMeta):
|
||||
"""
|
||||
Lexer for a specific language.
|
||||
|
||||
Basic options recognized:
|
||||
See also :doc:`lexerdevelopment`, a high-level guide to writing
|
||||
lexers.
|
||||
|
||||
Lexer classes have attributes used for choosing the most appropriate
|
||||
lexer based on various criteria.
|
||||
|
||||
.. autoattribute:: name
|
||||
:no-value:
|
||||
.. autoattribute:: aliases
|
||||
:no-value:
|
||||
.. autoattribute:: filenames
|
||||
:no-value:
|
||||
.. autoattribute:: alias_filenames
|
||||
.. autoattribute:: mimetypes
|
||||
:no-value:
|
||||
.. autoattribute:: priority
|
||||
|
||||
Lexers included in Pygments should have an additional attribute:
|
||||
|
||||
.. autoattribute:: url
|
||||
:no-value:
|
||||
|
||||
You can pass options to the constructor. The basic options recognized
|
||||
by all lexers and processed by the base `Lexer` class are:
|
||||
|
||||
``stripnl``
|
||||
Strip leading and trailing newlines from the input (default: True).
|
||||
``stripall``
|
||||
@@ -74,28 +98,55 @@ class Lexer(metaclass=LexerMeta):
|
||||
Overrides the ``encoding`` if given.
|
||||
"""
|
||||
|
||||
#: Name of the lexer
|
||||
#: Full name of the lexer, in human-readable form
|
||||
name = None
|
||||
|
||||
#: URL of the language specification/definition
|
||||
url = None
|
||||
|
||||
#: Shortcuts for the lexer
|
||||
#: A list of short, unique identifiers that can be used to look
|
||||
#: up the lexer from a list, e.g., using `get_lexer_by_name()`.
|
||||
aliases = []
|
||||
|
||||
#: File name globs
|
||||
#: A list of `fnmatch` patterns that match filenames which contain
|
||||
#: content for this lexer. The patterns in this list should be unique among
|
||||
#: all lexers.
|
||||
filenames = []
|
||||
|
||||
#: Secondary file name globs
|
||||
#: A list of `fnmatch` patterns that match filenames which may or may not
|
||||
#: contain content for this lexer. This list is used by the
|
||||
#: :func:`.guess_lexer_for_filename()` function, to determine which lexers
|
||||
#: are then included in guessing the correct one. That means that
|
||||
#: e.g. every lexer for HTML and a template language should include
|
||||
#: ``\*.html`` in this list.
|
||||
alias_filenames = []
|
||||
|
||||
#: MIME types
|
||||
#: A list of MIME types for content that can be lexed with this lexer.
|
||||
mimetypes = []
|
||||
|
||||
#: Priority, should multiple lexers match and no content is provided
|
||||
priority = 0
|
||||
|
||||
#: URL of the language specification/definition. Used in the Pygments
|
||||
#: documentation.
|
||||
url = None
|
||||
|
||||
def __init__(self, **options):
|
||||
"""
|
||||
This constructor takes arbitrary options as keyword arguments.
|
||||
Every subclass must first process its own options and then call
|
||||
the `Lexer` constructor, since it processes the basic
|
||||
options like `stripnl`.
|
||||
|
||||
An example looks like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def __init__(self, **options):
|
||||
self.compress = options.get('compress', '')
|
||||
Lexer.__init__(self, **options)
|
||||
|
||||
As these options must all be specifiable as strings (due to the
|
||||
command line usage), there are various utility functions
|
||||
available to help with that, see `Utilities`_.
|
||||
"""
|
||||
self.options = options
|
||||
self.stripnl = get_bool_opt(options, 'stripnl', True)
|
||||
self.stripall = get_bool_opt(options, 'stripall', False)
|
||||
@@ -124,10 +175,13 @@ class Lexer(metaclass=LexerMeta):
|
||||
|
||||
def analyse_text(text):
|
||||
"""
|
||||
Has to return a float between ``0`` and ``1`` that indicates
|
||||
if a lexer wants to highlight this text. Used by ``guess_lexer``.
|
||||
If this method returns ``0`` it won't highlight it in any case, if
|
||||
it returns ``1`` highlighting with this lexer is guaranteed.
|
||||
A static method which is called for lexer guessing.
|
||||
|
||||
It should analyse the text and return a float in the range
|
||||
from ``0.0`` to ``1.0``. If it returns ``0.0``, the lexer
|
||||
will not be selected as the most probable one, if it returns
|
||||
``1.0``, it will be selected immediately. This is used by
|
||||
`guess_lexer`.
|
||||
|
||||
The `LexerMeta` metaclass automatically wraps this function so
|
||||
that it works like a static method (no ``self`` or ``cls``
|
||||
@@ -138,12 +192,17 @@ class Lexer(metaclass=LexerMeta):
|
||||
|
||||
def get_tokens(self, text, unfiltered=False):
|
||||
"""
|
||||
Return an iterable of (tokentype, value) pairs generated from
|
||||
`text`. If `unfiltered` is set to `True`, the filtering mechanism
|
||||
is bypassed even if filters are defined.
|
||||
This method is the basic interface of a lexer. It is called by
|
||||
the `highlight()` function. It must process the text and return an
|
||||
iterable of ``(tokentype, value)`` pairs from `text`.
|
||||
|
||||
Also preprocess the text, i.e. expand tabs and strip it if
|
||||
wanted and applies registered filters.
|
||||
Normally, you don't need to override this method. The default
|
||||
implementation processes the options recognized by all lexers
|
||||
(`stripnl`, `stripall` and so on), and then yields all tokens
|
||||
from `get_tokens_unprocessed()`, with the ``index`` dropped.
|
||||
|
||||
If `unfiltered` is set to `True`, the filtering mechanism is
|
||||
bypassed even if filters are defined.
|
||||
"""
|
||||
if not isinstance(text, str):
|
||||
if self.encoding == 'guess':
|
||||
@@ -197,11 +256,12 @@ class Lexer(metaclass=LexerMeta):
|
||||
|
||||
def get_tokens_unprocessed(self, text):
|
||||
"""
|
||||
Return an iterable of (index, tokentype, value) pairs where "index"
|
||||
is the starting position of the token within the input text.
|
||||
This method should process the text and return an iterable of
|
||||
``(index, tokentype, value)`` tuples where ``index`` is the starting
|
||||
position of the token within the input text.
|
||||
|
||||
In subclasses, implement this method as a generator to
|
||||
maximize effectiveness.
|
||||
It must be overridden by subclasses. It is recommended to
|
||||
implement it as a generator to maximize effectiveness.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
|
||||
Pygments lexers.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
from fnmatch import fnmatch
|
||||
import fnmatch
|
||||
from os.path import basename
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.lexers._mapping import LEXERS
|
||||
@@ -27,6 +28,16 @@ __all__ = ['get_lexer_by_name', 'get_lexer_for_filename', 'find_lexer_class',
|
||||
'guess_lexer', 'load_lexer_from_file'] + list(LEXERS) + list(COMPAT)
|
||||
|
||||
_lexer_cache = {}
|
||||
_pattern_cache = {}
|
||||
|
||||
|
||||
def _fn_matches(fn, glob):
|
||||
"""Return whether the supplied file name fn matches pattern filename."""
|
||||
if glob not in _pattern_cache:
|
||||
pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob))
|
||||
return pattern.match(fn)
|
||||
return _pattern_cache[glob].match(fn)
|
||||
|
||||
|
||||
def _load_lexers(module_name):
|
||||
"""Load a lexer (and all others in the module too)."""
|
||||
@@ -51,9 +62,9 @@ def get_all_lexers(plugins=True):
|
||||
|
||||
|
||||
def find_lexer_class(name):
|
||||
"""Lookup a lexer class by name.
|
||||
|
||||
Return None if not found.
|
||||
"""
|
||||
Return the `Lexer` subclass that with the *name* attribute as given by
|
||||
the *name* argument.
|
||||
"""
|
||||
if name in _lexer_cache:
|
||||
return _lexer_cache[name]
|
||||
@@ -69,10 +80,15 @@ def find_lexer_class(name):
|
||||
|
||||
|
||||
def find_lexer_class_by_name(_alias):
|
||||
"""Lookup a lexer class by alias.
|
||||
"""
|
||||
Return the `Lexer` subclass that has `alias` in its aliases list, without
|
||||
instantiating it.
|
||||
|
||||
Like `get_lexer_by_name`, but does not instantiate the class.
|
||||
|
||||
Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is
|
||||
found.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
"""
|
||||
if not _alias:
|
||||
@@ -91,9 +107,13 @@ def find_lexer_class_by_name(_alias):
|
||||
|
||||
|
||||
def get_lexer_by_name(_alias, **options):
|
||||
"""Get a lexer by an alias.
|
||||
"""
|
||||
Return an instance of a `Lexer` subclass that has `alias` in its
|
||||
aliases list. The lexer is given the `options` at its
|
||||
instantiation.
|
||||
|
||||
Raises ClassNotFound if not found.
|
||||
Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is
|
||||
found.
|
||||
"""
|
||||
if not _alias:
|
||||
raise ClassNotFound('no lexer for alias %r found' % _alias)
|
||||
@@ -158,13 +178,13 @@ def find_lexer_class_for_filename(_fn, code=None):
|
||||
fn = basename(_fn)
|
||||
for modname, name, _, filenames, _ in LEXERS.values():
|
||||
for filename in filenames:
|
||||
if fnmatch(fn, filename):
|
||||
if _fn_matches(fn, filename):
|
||||
if name not in _lexer_cache:
|
||||
_load_lexers(modname)
|
||||
matches.append((_lexer_cache[name], filename))
|
||||
for cls in find_plugin_lexers():
|
||||
for filename in cls.filenames:
|
||||
if fnmatch(fn, filename):
|
||||
if _fn_matches(fn, filename):
|
||||
matches.append((cls, filename))
|
||||
|
||||
if isinstance(code, bytes):
|
||||
@@ -192,10 +212,15 @@ def find_lexer_class_for_filename(_fn, code=None):
|
||||
def get_lexer_for_filename(_fn, code=None, **options):
|
||||
"""Get a lexer for a filename.
|
||||
|
||||
If multiple lexers match the filename pattern, use ``analyse_text()`` to
|
||||
figure out which one is more appropriate.
|
||||
Return a `Lexer` subclass instance that has a filename pattern
|
||||
matching `fn`. The lexer is given the `options` at its
|
||||
instantiation.
|
||||
|
||||
Raises ClassNotFound if not found.
|
||||
Raise :exc:`pygments.util.ClassNotFound` if no lexer for that filename
|
||||
is found.
|
||||
|
||||
If multiple lexers match the filename pattern, use their ``analyse_text()``
|
||||
methods to figure out which one is more appropriate.
|
||||
"""
|
||||
res = find_lexer_class_for_filename(_fn, code)
|
||||
if not res:
|
||||
@@ -204,9 +229,12 @@ def get_lexer_for_filename(_fn, code=None, **options):
|
||||
|
||||
|
||||
def get_lexer_for_mimetype(_mime, **options):
|
||||
"""Get a lexer for a mimetype.
|
||||
"""
|
||||
Return a `Lexer` subclass instance that has `mime` in its mimetype
|
||||
list. The lexer is given the `options` at its instantiation.
|
||||
|
||||
Raises ClassNotFound if not found.
|
||||
Will raise :exc:`pygments.util.ClassNotFound` if not lexer for that mimetype
|
||||
is found.
|
||||
"""
|
||||
for modname, name, _, _, mimetypes in LEXERS.values():
|
||||
if _mime in mimetypes:
|
||||
@@ -232,30 +260,22 @@ def _iter_lexerclasses(plugins=True):
|
||||
|
||||
def guess_lexer_for_filename(_fn, _text, **options):
|
||||
"""
|
||||
Lookup all lexers that handle those filenames primary (``filenames``)
|
||||
or secondary (``alias_filenames``). Then run a text analysis for those
|
||||
lexers and choose the best result.
|
||||
As :func:`guess_lexer()`, but only lexers which have a pattern in `filenames`
|
||||
or `alias_filenames` that matches `filename` are taken into consideration.
|
||||
|
||||
usage::
|
||||
|
||||
>>> from pygments.lexers import guess_lexer_for_filename
|
||||
>>> guess_lexer_for_filename('hello.html', '<%= @foo %>')
|
||||
<pygments.lexers.templates.RhtmlLexer object at 0xb7d2f32c>
|
||||
>>> guess_lexer_for_filename('hello.html', '<h1>{{ title|e }}</h1>')
|
||||
<pygments.lexers.templates.HtmlDjangoLexer object at 0xb7d2f2ac>
|
||||
>>> guess_lexer_for_filename('style.css', 'a { color: <?= $link ?> }')
|
||||
<pygments.lexers.templates.CssPhpLexer object at 0xb7ba518c>
|
||||
:exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can
|
||||
handle the content.
|
||||
"""
|
||||
fn = basename(_fn)
|
||||
primary = {}
|
||||
matching_lexers = set()
|
||||
for lexer in _iter_lexerclasses():
|
||||
for filename in lexer.filenames:
|
||||
if fnmatch(fn, filename):
|
||||
if _fn_matches(fn, filename):
|
||||
matching_lexers.add(lexer)
|
||||
primary[lexer] = True
|
||||
for filename in lexer.alias_filenames:
|
||||
if fnmatch(fn, filename):
|
||||
if _fn_matches(fn, filename):
|
||||
matching_lexers.add(lexer)
|
||||
primary[lexer] = False
|
||||
if not matching_lexers:
|
||||
@@ -282,7 +302,15 @@ def guess_lexer_for_filename(_fn, _text, **options):
|
||||
|
||||
|
||||
def guess_lexer(_text, **options):
|
||||
"""Guess a lexer by strong distinctions in the text (eg, shebang)."""
|
||||
"""
|
||||
Return a `Lexer` subclass instance that's guessed from the text in
|
||||
`text`. For that, the :meth:`.analyse_text()` method of every known lexer
|
||||
class is called with the text as argument, and the lexer which returned the
|
||||
highest value will be instantiated and returned.
|
||||
|
||||
:exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can
|
||||
handle the content.
|
||||
"""
|
||||
|
||||
if not isinstance(_text, str):
|
||||
inencoding = options.get('inencoding', options.get('encoding'))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Automatically generated by scripts/gen_mapfiles.py.
|
||||
# DO NOT EDIT BY HAND; run `make mapfiles` instead.
|
||||
# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead.
|
||||
|
||||
LEXERS = {
|
||||
'ABAPLexer': ('pipenv.patched.pip._vendor.pygments.lexers.business', 'ABAP', ('abap',), ('*.abap', '*.ABAP'), ('text/x-abap',)),
|
||||
@@ -71,6 +71,7 @@ LEXERS = {
|
||||
'CadlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.archetype', 'cADL', ('cadl',), ('*.cadl',), ()),
|
||||
'CapDLLexer': ('pipenv.patched.pip._vendor.pygments.lexers.esoteric', 'CapDL', ('capdl',), ('*.cdl',), ()),
|
||||
'CapnProtoLexer': ('pipenv.patched.pip._vendor.pygments.lexers.capnproto', "Cap'n Proto", ('capnp',), ('*.capnp',), ()),
|
||||
'CarbonLexer': ('pipenv.patched.pip._vendor.pygments.lexers.carbon', 'Carbon', ('carbon',), ('*.carbon',), ('text/x-carbon',)),
|
||||
'CbmBasicV2Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.basic', 'CBM BASIC V2', ('cbmbas',), ('*.bas',), ()),
|
||||
'CddlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.cddl', 'CDDL', ('cddl',), ('*.cddl',), ('text/x-cddl',)),
|
||||
'CeylonLexer': ('pipenv.patched.pip._vendor.pygments.lexers.jvm', 'Ceylon', ('ceylon',), ('*.ceylon',), ('text/x-ceylon',)),
|
||||
@@ -121,6 +122,7 @@ LEXERS = {
|
||||
'DarcsPatchLexer': ('pipenv.patched.pip._vendor.pygments.lexers.diff', 'Darcs Patch', ('dpatch',), ('*.dpatch', '*.darcspatch'), ()),
|
||||
'DartLexer': ('pipenv.patched.pip._vendor.pygments.lexers.javascript', 'Dart', ('dart',), ('*.dart',), ('text/x-dart',)),
|
||||
'Dasm16Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.asm', 'DASM16', ('dasm16',), ('*.dasm16', '*.dasm'), ('text/x-dasm16',)),
|
||||
'DaxLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dax', 'Dax', ('dax',), ('*.dax',), ()),
|
||||
'DebianControlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.installers', 'Debian Control file', ('debcontrol', 'control'), ('control',), ()),
|
||||
'DelphiLexer': ('pipenv.patched.pip._vendor.pygments.lexers.pascal', 'Delphi', ('delphi', 'pas', 'pascal', 'objectpascal'), ('*.pas', '*.dpr'), ('text/x-pascal',)),
|
||||
'DevicetreeLexer': ('pipenv.patched.pip._vendor.pygments.lexers.devicetree', 'Devicetree', ('devicetree', 'dts'), ('*.dts', '*.dtsi'), ('text/x-c',)),
|
||||
@@ -368,6 +370,7 @@ LEXERS = {
|
||||
'PortugolLexer': ('pipenv.patched.pip._vendor.pygments.lexers.pascal', 'Portugol', ('portugol',), ('*.alg', '*.portugol'), ()),
|
||||
'PostScriptLexer': ('pipenv.patched.pip._vendor.pygments.lexers.graphics', 'PostScript', ('postscript', 'postscr'), ('*.ps', '*.eps'), ('application/postscript',)),
|
||||
'PostgresConsoleLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sql', 'PostgreSQL console (psql)', ('psql', 'postgresql-console', 'postgres-console'), (), ('text/x-postgresql-psql',)),
|
||||
'PostgresExplainLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sql', 'PostgreSQL EXPLAIN dialect', ('postgres-explain',), ('*.explain',), ('text/x-postgresql-explain',)),
|
||||
'PostgresLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sql', 'PostgreSQL SQL dialect', ('postgresql', 'postgres'), (), ('text/x-postgresql',)),
|
||||
'PovrayLexer': ('pipenv.patched.pip._vendor.pygments.lexers.graphics', 'POVRay', ('pov',), ('*.pov', '*.inc'), ('text/x-povray',)),
|
||||
'PowerShellLexer': ('pipenv.patched.pip._vendor.pygments.lexers.shell', 'PowerShell', ('powershell', 'pwsh', 'posh', 'ps1', 'psm1'), ('*.ps1', '*.psm1'), ('text/x-powershell',)),
|
||||
@@ -488,7 +491,7 @@ LEXERS = {
|
||||
'TeraTermLexer': ('pipenv.patched.pip._vendor.pygments.lexers.teraterm', 'Tera Term macro', ('teratermmacro', 'teraterm', 'ttl'), ('*.ttl',), ('text/x-teratermmacro',)),
|
||||
'TermcapLexer': ('pipenv.patched.pip._vendor.pygments.lexers.configs', 'Termcap', ('termcap',), ('termcap', 'termcap.src'), ()),
|
||||
'TerminfoLexer': ('pipenv.patched.pip._vendor.pygments.lexers.configs', 'Terminfo', ('terminfo',), ('terminfo', 'terminfo.src'), ()),
|
||||
'TerraformLexer': ('pipenv.patched.pip._vendor.pygments.lexers.configs', 'Terraform', ('terraform', 'tf'), ('*.tf',), ('application/x-tf', 'application/x-terraform')),
|
||||
'TerraformLexer': ('pipenv.patched.pip._vendor.pygments.lexers.configs', 'Terraform', ('terraform', 'tf', 'hcl'), ('*.tf', '*.hcl'), ('application/x-tf', 'application/x-terraform')),
|
||||
'TexLexer': ('pipenv.patched.pip._vendor.pygments.lexers.markup', 'TeX', ('tex', 'latex'), ('*.tex', '*.aux', '*.toc'), ('text/x-tex', 'text/x-latex')),
|
||||
'TextLexer': ('pipenv.patched.pip._vendor.pygments.lexers.special', 'Text only', ('text',), ('*.txt',), ('text/plain',)),
|
||||
'ThingsDBLexer': ('pipenv.patched.pip._vendor.pygments.lexers.thingsdb', 'ThingsDB', ('ti', 'thingsdb'), ('*.ti',), ()),
|
||||
@@ -528,7 +531,9 @@ LEXERS = {
|
||||
'WDiffLexer': ('pipenv.patched.pip._vendor.pygments.lexers.diff', 'WDiff', ('wdiff',), ('*.wdiff',), ()),
|
||||
'WatLexer': ('pipenv.patched.pip._vendor.pygments.lexers.webassembly', 'WebAssembly', ('wast', 'wat'), ('*.wat', '*.wast'), ()),
|
||||
'WebIDLLexer': ('pipenv.patched.pip._vendor.pygments.lexers.webidl', 'Web IDL', ('webidl',), ('*.webidl',), ()),
|
||||
'WgslLexer': ('pipenv.patched.pip._vendor.pygments.lexers.wgsl', 'WebGPU Shading Language', ('wgsl',), ('*.wgsl',), ('text/wgsl',)),
|
||||
'WhileyLexer': ('pipenv.patched.pip._vendor.pygments.lexers.whiley', 'Whiley', ('whiley',), ('*.whiley',), ('text/x-whiley',)),
|
||||
'WikitextLexer': ('pipenv.patched.pip._vendor.pygments.lexers.markup', 'Wikitext', ('wikitext', 'mediawiki'), (), ('text/x-wiki',)),
|
||||
'WoWTocLexer': ('pipenv.patched.pip._vendor.pygments.lexers.wowtoc', 'World of Warcraft TOC', ('wowtoc',), ('*.toc',), ()),
|
||||
'WrenLexer': ('pipenv.patched.pip._vendor.pygments.lexers.wren', 'Wren', ('wren',), ('*.wren',), ()),
|
||||
'X10Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.x10', 'X10', ('x10', 'xten'), ('*.x10',), ('text/x-x10',)),
|
||||
@@ -540,6 +545,7 @@ LEXERS = {
|
||||
'XmlPhpLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'XML+PHP', ('xml+php',), (), ('application/xml+php',)),
|
||||
'XmlSmartyLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'XML+Smarty', ('xml+smarty',), (), ('application/xml+smarty',)),
|
||||
'XorgLexer': ('pipenv.patched.pip._vendor.pygments.lexers.xorg', 'Xorg', ('xorg.conf',), ('xorg.conf',), ()),
|
||||
'XppLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dotnet', 'X++', ('xpp', 'x++'), ('*.xpp',), ()),
|
||||
'XsltLexer': ('pipenv.patched.pip._vendor.pygments.lexers.html', 'XSLT', ('xslt',), ('*.xsl', '*.xslt', '*.xpl'), ('application/xsl+xml', 'application/xslt+xml')),
|
||||
'XtendLexer': ('pipenv.patched.pip._vendor.pygments.lexers.jvm', 'Xtend', ('xtend',), ('*.xtend',), ('text/x-xtend',)),
|
||||
'XtlangLexer': ('pipenv.patched.pip._vendor.pygments.lexers.lisp', 'xtlang', ('extempore',), ('*.xtm',), ()),
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
Lexers for Python and related languages.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import keyword
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.lexer import Lexer, RegexLexer, include, bygroups, using, \
|
||||
default, words, combined, do_insertions, this, line_re
|
||||
from pipenv.patched.pip._vendor.pygments.lexer import DelegatingLexer, Lexer, RegexLexer, include, \
|
||||
bygroups, using, default, words, combined, do_insertions, this, line_re
|
||||
from pipenv.patched.pip._vendor.pygments.util import get_bool_opt, shebang_matches
|
||||
from pipenv.patched.pip._vendor.pygments.token import Text, Comment, Operator, Keyword, Name, String, \
|
||||
Number, Punctuation, Generic, Other, Error, Whitespace
|
||||
@@ -234,16 +234,16 @@ class PythonLexer(RegexLexer):
|
||||
],
|
||||
'builtins': [
|
||||
(words((
|
||||
'__import__', 'abs', 'all', 'any', 'bin', 'bool', 'bytearray',
|
||||
'breakpoint', 'bytes', 'chr', 'classmethod', 'compile', 'complex',
|
||||
'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'filter',
|
||||
'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
|
||||
'hash', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
|
||||
'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview',
|
||||
'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print',
|
||||
'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr',
|
||||
'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple',
|
||||
'type', 'vars', 'zip'), prefix=r'(?<!\.)', suffix=r'\b'),
|
||||
'__import__', 'abs', 'aiter', 'all', 'any', 'bin', 'bool', 'bytearray',
|
||||
'breakpoint', 'bytes', 'callable', 'chr', 'classmethod', 'compile',
|
||||
'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval',
|
||||
'filter', 'float', 'format', 'frozenset', 'getattr', 'globals',
|
||||
'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'isinstance',
|
||||
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max',
|
||||
'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow',
|
||||
'print', 'property', 'range', 'repr', 'reversed', 'round', 'set',
|
||||
'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
|
||||
'tuple', 'type', 'vars', 'zip'), prefix=r'(?<!\.)', suffix=r'\b'),
|
||||
Name.Builtin),
|
||||
(r'(?<!\.)(self|Ellipsis|NotImplemented|cls)\b', Name.Builtin.Pseudo),
|
||||
(words((
|
||||
@@ -341,7 +341,7 @@ class PythonLexer(RegexLexer):
|
||||
(r'\.', Name.Namespace),
|
||||
# if None occurs here, it's "raise x from None", since None can
|
||||
# never be a module name
|
||||
(r'None\b', Name.Builtin.Pseudo, '#pop'),
|
||||
(r'None\b', Keyword.Constant, '#pop'),
|
||||
(uni_name, Name.Namespace),
|
||||
default('#pop'),
|
||||
],
|
||||
@@ -635,15 +635,50 @@ class Python2Lexer(RegexLexer):
|
||||
def analyse_text(text):
|
||||
return shebang_matches(text, r'pythonw?2(\.\d)?')
|
||||
|
||||
class _PythonConsoleLexerBase(RegexLexer):
|
||||
name = 'Python console session'
|
||||
aliases = ['pycon']
|
||||
mimetypes = ['text/x-python-doctest']
|
||||
|
||||
class PythonConsoleLexer(Lexer):
|
||||
"""Auxiliary lexer for `PythonConsoleLexer`.
|
||||
|
||||
Code tokens are output as ``Token.Other.Code``, traceback tokens as
|
||||
``Token.Other.Traceback``.
|
||||
"""
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'(>>> )(.*\n)', bygroups(Generic.Prompt, Other.Code), 'continuations'),
|
||||
# This happens, e.g., when tracebacks are embedded in documentation;
|
||||
# trailing whitespaces are often stripped in such contexts.
|
||||
(r'(>>>)(\n)', bygroups(Generic.Prompt, Whitespace)),
|
||||
(r'(\^C)?Traceback \(most recent call last\):\n', Other.Traceback, 'traceback'),
|
||||
# SyntaxError starts with this
|
||||
(r' File "[^"]+", line \d+', Other.Traceback, 'traceback'),
|
||||
(r'.*\n', Generic.Output),
|
||||
],
|
||||
'continuations': [
|
||||
(r'(\.\.\. )(.*\n)', bygroups(Generic.Prompt, Other.Code)),
|
||||
# See above.
|
||||
(r'(\.\.\.)(\n)', bygroups(Generic.Prompt, Whitespace)),
|
||||
default('#pop'),
|
||||
],
|
||||
'traceback': [
|
||||
# As soon as we see a traceback, consume everything until the next
|
||||
# >>> prompt.
|
||||
(r'(?=>>>( |$))', Text, '#pop'),
|
||||
(r'(KeyboardInterrupt)(\n)', bygroups(Name.Class, Whitespace)),
|
||||
(r'.*\n', Other.Traceback),
|
||||
],
|
||||
}
|
||||
|
||||
class PythonConsoleLexer(DelegatingLexer):
|
||||
"""
|
||||
For Python console output or doctests, such as:
|
||||
|
||||
.. sourcecode:: pycon
|
||||
|
||||
>>> a = 'foo'
|
||||
>>> print a
|
||||
>>> print(a)
|
||||
foo
|
||||
>>> 1 / 0
|
||||
Traceback (most recent call last):
|
||||
@@ -659,70 +694,28 @@ class PythonConsoleLexer(Lexer):
|
||||
.. versionchanged:: 2.5
|
||||
Now defaults to ``True``.
|
||||
"""
|
||||
|
||||
name = 'Python console session'
|
||||
aliases = ['pycon']
|
||||
mimetypes = ['text/x-python-doctest']
|
||||
|
||||
def __init__(self, **options):
|
||||
self.python3 = get_bool_opt(options, 'python3', True)
|
||||
Lexer.__init__(self, **options)
|
||||
|
||||
def get_tokens_unprocessed(self, text):
|
||||
if self.python3:
|
||||
pylexer = PythonLexer(**self.options)
|
||||
tblexer = PythonTracebackLexer(**self.options)
|
||||
python3 = get_bool_opt(options, 'python3', True)
|
||||
if python3:
|
||||
pylexer = PythonLexer
|
||||
tblexer = PythonTracebackLexer
|
||||
else:
|
||||
pylexer = Python2Lexer(**self.options)
|
||||
tblexer = Python2TracebackLexer(**self.options)
|
||||
|
||||
curcode = ''
|
||||
insertions = []
|
||||
curtb = ''
|
||||
tbindex = 0
|
||||
tb = 0
|
||||
for match in line_re.finditer(text):
|
||||
line = match.group()
|
||||
if line.startswith('>>> ') or line.startswith('... '):
|
||||
tb = 0
|
||||
insertions.append((len(curcode),
|
||||
[(0, Generic.Prompt, line[:4])]))
|
||||
curcode += line[4:]
|
||||
elif line.rstrip() == '...' and not tb:
|
||||
# only a new >>> prompt can end an exception block
|
||||
# otherwise an ellipsis in place of the traceback frames
|
||||
# will be mishandled
|
||||
insertions.append((len(curcode),
|
||||
[(0, Generic.Prompt, '...')]))
|
||||
curcode += line[3:]
|
||||
else:
|
||||
if curcode:
|
||||
yield from do_insertions(
|
||||
insertions, pylexer.get_tokens_unprocessed(curcode))
|
||||
curcode = ''
|
||||
insertions = []
|
||||
if (line.startswith('Traceback (most recent call last):') or
|
||||
re.match(' File "[^"]+", line \\d+\\n$', line)):
|
||||
tb = 1
|
||||
curtb = line
|
||||
tbindex = match.start()
|
||||
elif line == 'KeyboardInterrupt\n':
|
||||
yield match.start(), Name.Class, line
|
||||
elif tb:
|
||||
curtb += line
|
||||
if not (line.startswith(' ') or line.strip() == '...'):
|
||||
tb = 0
|
||||
for i, t, v in tblexer.get_tokens_unprocessed(curtb):
|
||||
yield tbindex+i, t, v
|
||||
curtb = ''
|
||||
else:
|
||||
yield match.start(), Generic.Output, line
|
||||
if curcode:
|
||||
yield from do_insertions(insertions,
|
||||
pylexer.get_tokens_unprocessed(curcode))
|
||||
if curtb:
|
||||
for i, t, v in tblexer.get_tokens_unprocessed(curtb):
|
||||
yield tbindex+i, t, v
|
||||
|
||||
pylexer = Python2Lexer
|
||||
tblexer = Python2TracebackLexer
|
||||
# We have two auxiliary lexers. Use DelegatingLexer twice with
|
||||
# different tokens. TODO: DelegatingLexer should support this
|
||||
# directly, by accepting a tuplet of auxiliary lexers and a tuple of
|
||||
# distinguishing tokens. Then we wouldn't need this intermediary
|
||||
# class.
|
||||
class _ReplaceInnerCode(DelegatingLexer):
|
||||
def __init__(self, **options):
|
||||
super().__init__(pylexer, _PythonConsoleLexerBase, Other.Code, **options)
|
||||
super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options)
|
||||
|
||||
class PythonTracebackLexer(RegexLexer):
|
||||
"""
|
||||
@@ -743,7 +736,7 @@ class PythonTracebackLexer(RegexLexer):
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\n', Whitespace),
|
||||
(r'^Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'),
|
||||
(r'^(\^C)?Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'),
|
||||
(r'^During handling of the above exception, another '
|
||||
r'exception occurred:\n\n', Generic.Traceback),
|
||||
(r'^The above exception was the direct cause of the '
|
||||
@@ -763,7 +756,8 @@ class PythonTracebackLexer(RegexLexer):
|
||||
(r'^([^:]+)(: )(.+)(\n)',
|
||||
bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
|
||||
(r'^([a-zA-Z_][\w.]*)(:?\n)',
|
||||
bygroups(Generic.Error, Whitespace), '#pop')
|
||||
bygroups(Generic.Error, Whitespace), '#pop'),
|
||||
default('#pop'),
|
||||
],
|
||||
'markers': [
|
||||
# Either `PEP 657 <https://www.python.org/dev/peps/pep-0657/>`
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
A simple modeline parser (based on pymodeline).
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
yourfilter = yourfilter:YourFilter
|
||||
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
An algorithm that generates optimized regexes for matching long lists of
|
||||
literal strings.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Have a look at the `DelphiLexer` to get an idea of how to use
|
||||
this scanner.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
import re
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Sphinx extension to generate automatic documentation of lexers,
|
||||
formatters and filters.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Basic style object.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
Contains built-in styles.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.plugin import find_plugin_styles
|
||||
from pipenv.patched.pip._vendor.pygments.util import ClassNotFound
|
||||
|
||||
|
||||
#: Maps style names to 'submodule::classname'.
|
||||
#: A dictionary of built-in styles, mapping style names to
|
||||
#: ``'submodule::classname'`` strings.
|
||||
STYLE_MAP = {
|
||||
'default': 'default::DefaultStyle',
|
||||
'emacs': 'emacs::EmacsStyle',
|
||||
@@ -66,6 +66,13 @@ STYLE_MAP = {
|
||||
|
||||
|
||||
def get_style_by_name(name):
|
||||
"""
|
||||
Return a style class by its short name. The names of the builtin styles
|
||||
are listed in :data:`pygments.styles.STYLE_MAP`.
|
||||
|
||||
Will raise :exc:`pygments.util.ClassNotFound` if no style of that name is
|
||||
found.
|
||||
"""
|
||||
if name in STYLE_MAP:
|
||||
mod, cls = STYLE_MAP[name].split('::')
|
||||
builtin = "yes"
|
||||
@@ -90,8 +97,7 @@ def get_style_by_name(name):
|
||||
|
||||
|
||||
def get_all_styles():
|
||||
"""Return a generator for all styles by name,
|
||||
both builtin and plugin."""
|
||||
"""Return a generator for all styles by name, both builtin and plugin."""
|
||||
yield from STYLE_MAP
|
||||
for name, _ in find_plugin_styles():
|
||||
yield name
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Basic token types and the standard tokens.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
Inspired by chartypes_create.py from the MoinMoin project.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -112,7 +112,7 @@ if __name__ == '__main__': # pragma: no cover
|
||||
|
||||
categories = {'xid_start': [], 'xid_continue': []}
|
||||
|
||||
with open(__file__) as fp:
|
||||
with open(__file__, encoding='utf-8') as fp:
|
||||
content = fp.read()
|
||||
|
||||
header = content[:content.find('Cc =')]
|
||||
@@ -136,7 +136,7 @@ if __name__ == '__main__': # pragma: no cover
|
||||
if ('a' + c).isidentifier():
|
||||
categories['xid_continue'].append(c)
|
||||
|
||||
with open(__file__, 'w') as fp:
|
||||
with open(__file__, 'w', encoding='utf-8') as fp:
|
||||
fp.write(header)
|
||||
|
||||
for cat in sorted(categories):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Utility functions.
|
||||
|
||||
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||||
:copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
@@ -32,10 +32,16 @@ class ClassNotFound(ValueError):
|
||||
|
||||
|
||||
class OptionError(Exception):
|
||||
pass
|
||||
|
||||
"""
|
||||
This exception will be raised by all option processing functions if
|
||||
the type or value of the argument is not correct.
|
||||
"""
|
||||
|
||||
def get_choice_opt(options, optname, allowed, default=None, normcase=False):
|
||||
"""
|
||||
If the key `optname` from the dictionary is not in the sequence
|
||||
`allowed`, raise an error, otherwise return it.
|
||||
"""
|
||||
string = options.get(optname, default)
|
||||
if normcase:
|
||||
string = string.lower()
|
||||
@@ -46,6 +52,17 @@ def get_choice_opt(options, optname, allowed, default=None, normcase=False):
|
||||
|
||||
|
||||
def get_bool_opt(options, optname, default=None):
|
||||
"""
|
||||
Intuitively, this is `options.get(optname, default)`, but restricted to
|
||||
Boolean value. The Booleans can be represented as string, in order to accept
|
||||
Boolean value from the command line arguments. If the key `optname` is
|
||||
present in the dictionary `options` and is not associated with a Boolean,
|
||||
raise an `OptionError`. If it is absent, `default` is returned instead.
|
||||
|
||||
The valid string values for ``True`` are ``1``, ``yes``, ``true`` and
|
||||
``on``, the ones for ``False`` are ``0``, ``no``, ``false`` and ``off``
|
||||
(matched case-insensitively).
|
||||
"""
|
||||
string = options.get(optname, default)
|
||||
if isinstance(string, bool):
|
||||
return string
|
||||
@@ -66,6 +83,7 @@ def get_bool_opt(options, optname, default=None):
|
||||
|
||||
|
||||
def get_int_opt(options, optname, default=None):
|
||||
"""As :func:`get_bool_opt`, but interpret the value as an integer."""
|
||||
string = options.get(optname, default)
|
||||
try:
|
||||
return int(string)
|
||||
@@ -78,8 +96,12 @@ def get_int_opt(options, optname, default=None):
|
||||
'must give an integer value' % (
|
||||
string, optname))
|
||||
|
||||
|
||||
def get_list_opt(options, optname, default=None):
|
||||
"""
|
||||
If the key `optname` from the dictionary `options` is a string,
|
||||
split it at whitespace and return it. If it is already a list
|
||||
or a tuple, it is returned as a list.
|
||||
"""
|
||||
val = options.get(optname, default)
|
||||
if isinstance(val, str):
|
||||
return val.split()
|
||||
|
||||
@@ -56,7 +56,7 @@ self-explanatory class names, and the use of :class:`'+'<And>`,
|
||||
:class:`'|'<MatchFirst>`, :class:`'^'<Or>` and :class:`'&'<Each>` operators.
|
||||
|
||||
The :class:`ParseResults` object returned from
|
||||
:class:`ParserElement.parseString` can be
|
||||
:class:`ParserElement.parse_string` can be
|
||||
accessed as a nested list, a dictionary, or an object with named
|
||||
attributes.
|
||||
|
||||
@@ -85,11 +85,11 @@ classes inherit from. Use the docstrings for examples of how to:
|
||||
and :class:`'&'<Each>` operators to combine simple expressions into
|
||||
more complex ones
|
||||
- associate names with your parsed results using
|
||||
:class:`ParserElement.setResultsName`
|
||||
:class:`ParserElement.set_results_name`
|
||||
- access the parsed data, which is returned as a :class:`ParseResults`
|
||||
object
|
||||
- find some helpful expression short-cuts like :class:`delimitedList`
|
||||
and :class:`oneOf`
|
||||
- find some helpful expression short-cuts like :class:`DelimitedList`
|
||||
and :class:`one_of`
|
||||
- find more useful common expressions in the :class:`pyparsing_common`
|
||||
namespace class
|
||||
"""
|
||||
@@ -106,30 +106,22 @@ class version_info(NamedTuple):
|
||||
@property
|
||||
def __version__(self):
|
||||
return (
|
||||
"{}.{}.{}".format(self.major, self.minor, self.micro)
|
||||
f"{self.major}.{self.minor}.{self.micro}"
|
||||
+ (
|
||||
"{}{}{}".format(
|
||||
"r" if self.releaselevel[0] == "c" else "",
|
||||
self.releaselevel[0],
|
||||
self.serial,
|
||||
),
|
||||
f"{'r' if self.releaselevel[0] == 'c' else ''}{self.releaselevel[0]}{self.serial}",
|
||||
"",
|
||||
)[self.releaselevel == "final"]
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "{} {} / {}".format(__name__, self.__version__, __version_time__)
|
||||
return f"{__name__} {self.__version__} / {__version_time__}"
|
||||
|
||||
def __repr__(self):
|
||||
return "{}.{}({})".format(
|
||||
__name__,
|
||||
type(self).__name__,
|
||||
", ".join("{}={!r}".format(*nv) for nv in zip(self._fields, self)),
|
||||
)
|
||||
return f"{__name__}.{type(self).__name__}({', '.join('{}={!r}'.format(*nv) for nv in zip(self._fields, self))})"
|
||||
|
||||
|
||||
__version_info__ = version_info(3, 0, 9, "final", 0)
|
||||
__version_time__ = "05 May 2022 07:02 UTC"
|
||||
__version_info__ = version_info(3, 1, 0, "final", 1)
|
||||
__version_time__ = "18 Jun 2023 14:05 UTC"
|
||||
__version__ = __version_info__.__version__
|
||||
__versionTime__ = __version_time__
|
||||
__author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>"
|
||||
@@ -139,9 +131,9 @@ from .exceptions import *
|
||||
from .actions import *
|
||||
from .core import __diag__, __compat__
|
||||
from .results import *
|
||||
from .core import *
|
||||
from .core import * # type: ignore[misc, assignment]
|
||||
from .core import _builtin_exprs as core_builtin_exprs
|
||||
from .helpers import *
|
||||
from .helpers import * # type: ignore[misc, assignment]
|
||||
from .helpers import _builtin_exprs as helper_builtin_exprs
|
||||
|
||||
from .unicode import unicode_set, UnicodeRangeList, pyparsing_unicode as unicode
|
||||
@@ -153,11 +145,11 @@ from .common import (
|
||||
|
||||
# define backward compat synonyms
|
||||
if "pyparsing_unicode" not in globals():
|
||||
pyparsing_unicode = unicode
|
||||
pyparsing_unicode = unicode # type: ignore[misc]
|
||||
if "pyparsing_common" not in globals():
|
||||
pyparsing_common = common
|
||||
pyparsing_common = common # type: ignore[misc]
|
||||
if "pyparsing_test" not in globals():
|
||||
pyparsing_test = testing
|
||||
pyparsing_test = testing # type: ignore[misc]
|
||||
|
||||
core_builtin_exprs += common_builtin_exprs + helper_builtin_exprs
|
||||
|
||||
@@ -174,7 +166,9 @@ __all__ = [
|
||||
"CaselessKeyword",
|
||||
"CaselessLiteral",
|
||||
"CharsNotIn",
|
||||
"CloseMatch",
|
||||
"Combine",
|
||||
"DelimitedList",
|
||||
"Dict",
|
||||
"Each",
|
||||
"Empty",
|
||||
@@ -227,9 +221,11 @@ __all__ = [
|
||||
"alphas8bit",
|
||||
"any_close_tag",
|
||||
"any_open_tag",
|
||||
"autoname_elements",
|
||||
"c_style_comment",
|
||||
"col",
|
||||
"common_html_entity",
|
||||
"condition_as_parse_action",
|
||||
"counted_array",
|
||||
"cpp_style_comment",
|
||||
"dbl_quoted_string",
|
||||
@@ -241,6 +237,7 @@ __all__ = [
|
||||
"html_comment",
|
||||
"identchars",
|
||||
"identbodychars",
|
||||
"infix_notation",
|
||||
"java_style_comment",
|
||||
"line",
|
||||
"line_end",
|
||||
@@ -255,8 +252,12 @@ __all__ = [
|
||||
"null_debug_action",
|
||||
"nums",
|
||||
"one_of",
|
||||
"original_text_for",
|
||||
"printables",
|
||||
"punc8bit",
|
||||
"pyparsing_common",
|
||||
"pyparsing_test",
|
||||
"pyparsing_unicode",
|
||||
"python_style_comment",
|
||||
"quoted_string",
|
||||
"remove_quotes",
|
||||
@@ -267,28 +268,20 @@ __all__ = [
|
||||
"srange",
|
||||
"string_end",
|
||||
"string_start",
|
||||
"token_map",
|
||||
"trace_parse_action",
|
||||
"ungroup",
|
||||
"unicode_set",
|
||||
"unicode_string",
|
||||
"with_attribute",
|
||||
"indentedBlock",
|
||||
"original_text_for",
|
||||
"ungroup",
|
||||
"infix_notation",
|
||||
"locatedExpr",
|
||||
"with_class",
|
||||
"CloseMatch",
|
||||
"token_map",
|
||||
"pyparsing_common",
|
||||
"pyparsing_unicode",
|
||||
"unicode_set",
|
||||
"condition_as_parse_action",
|
||||
"pyparsing_test",
|
||||
# pre-PEP8 compatibility names
|
||||
"__versionTime__",
|
||||
"anyCloseTag",
|
||||
"anyOpenTag",
|
||||
"cStyleComment",
|
||||
"commonHTMLEntity",
|
||||
"conditionAsParseAction",
|
||||
"countedArray",
|
||||
"cppStyleComment",
|
||||
"dblQuotedString",
|
||||
@@ -296,9 +289,12 @@ __all__ = [
|
||||
"delimitedList",
|
||||
"dictOf",
|
||||
"htmlComment",
|
||||
"indentedBlock",
|
||||
"infixNotation",
|
||||
"javaStyleComment",
|
||||
"lineEnd",
|
||||
"lineStart",
|
||||
"locatedExpr",
|
||||
"makeHTMLTags",
|
||||
"makeXMLTags",
|
||||
"matchOnlyAtCol",
|
||||
@@ -308,6 +304,7 @@ __all__ = [
|
||||
"nullDebugAction",
|
||||
"oneOf",
|
||||
"opAssoc",
|
||||
"originalTextFor",
|
||||
"pythonStyleComment",
|
||||
"quotedString",
|
||||
"removeQuotes",
|
||||
@@ -317,15 +314,9 @@ __all__ = [
|
||||
"sglQuotedString",
|
||||
"stringEnd",
|
||||
"stringStart",
|
||||
"tokenMap",
|
||||
"traceParseAction",
|
||||
"unicodeString",
|
||||
"withAttribute",
|
||||
"indentedBlock",
|
||||
"originalTextFor",
|
||||
"infixNotation",
|
||||
"locatedExpr",
|
||||
"withClass",
|
||||
"tokenMap",
|
||||
"conditionAsParseAction",
|
||||
"autoname_elements",
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# actions.py
|
||||
|
||||
from .exceptions import ParseException
|
||||
from .util import col
|
||||
from .util import col, replaced_by_pep8
|
||||
|
||||
|
||||
class OnlyOnce:
|
||||
@@ -38,7 +38,7 @@ def match_only_at_col(n):
|
||||
|
||||
def verify_col(strg, locn, toks):
|
||||
if col(locn, strg) != n:
|
||||
raise ParseException(strg, locn, "matched token not at column {}".format(n))
|
||||
raise ParseException(strg, locn, f"matched token not at column {n}")
|
||||
|
||||
return verify_col
|
||||
|
||||
@@ -148,15 +148,13 @@ def with_attribute(*args, **attr_dict):
|
||||
raise ParseException(
|
||||
s,
|
||||
l,
|
||||
"attribute {!r} has value {!r}, must be {!r}".format(
|
||||
attrName, tokens[attrName], attrValue
|
||||
),
|
||||
f"attribute {attrName!r} has value {tokens[attrName]!r}, must be {attrValue!r}",
|
||||
)
|
||||
|
||||
return pa
|
||||
|
||||
|
||||
with_attribute.ANY_VALUE = object()
|
||||
with_attribute.ANY_VALUE = object() # type: ignore [attr-defined]
|
||||
|
||||
|
||||
def with_class(classname, namespace=""):
|
||||
@@ -195,13 +193,25 @@ def with_class(classname, namespace=""):
|
||||
1 4 0 1 0
|
||||
1,3 2,3 1,1
|
||||
"""
|
||||
classattr = "{}:class".format(namespace) if namespace else "class"
|
||||
classattr = f"{namespace}:class" if namespace else "class"
|
||||
return with_attribute(**{classattr: classname})
|
||||
|
||||
|
||||
# pre-PEP8 compatibility symbols
|
||||
replaceWith = replace_with
|
||||
removeQuotes = remove_quotes
|
||||
withAttribute = with_attribute
|
||||
withClass = with_class
|
||||
matchOnlyAtCol = match_only_at_col
|
||||
# fmt: off
|
||||
@replaced_by_pep8(replace_with)
|
||||
def replaceWith(): ...
|
||||
|
||||
@replaced_by_pep8(remove_quotes)
|
||||
def removeQuotes(): ...
|
||||
|
||||
@replaced_by_pep8(with_attribute)
|
||||
def withAttribute(): ...
|
||||
|
||||
@replaced_by_pep8(with_class)
|
||||
def withClass(): ...
|
||||
|
||||
@replaced_by_pep8(match_only_at_col)
|
||||
def matchOnlyAtCol(): ...
|
||||
|
||||
# fmt: on
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# common.py
|
||||
from .core import *
|
||||
from .helpers import delimited_list, any_open_tag, any_close_tag
|
||||
from .helpers import DelimitedList, any_open_tag, any_close_tag
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@@ -22,17 +22,17 @@ class pyparsing_common:
|
||||
|
||||
Parse actions:
|
||||
|
||||
- :class:`convertToInteger`
|
||||
- :class:`convertToFloat`
|
||||
- :class:`convertToDate`
|
||||
- :class:`convertToDatetime`
|
||||
- :class:`stripHTMLTags`
|
||||
- :class:`upcaseTokens`
|
||||
- :class:`downcaseTokens`
|
||||
- :class:`convert_to_integer`
|
||||
- :class:`convert_to_float`
|
||||
- :class:`convert_to_date`
|
||||
- :class:`convert_to_datetime`
|
||||
- :class:`strip_html_tags`
|
||||
- :class:`upcase_tokens`
|
||||
- :class:`downcase_tokens`
|
||||
|
||||
Example::
|
||||
|
||||
pyparsing_common.number.runTests('''
|
||||
pyparsing_common.number.run_tests('''
|
||||
# any int or real number, returned as the appropriate type
|
||||
100
|
||||
-100
|
||||
@@ -42,7 +42,7 @@ class pyparsing_common:
|
||||
1e-12
|
||||
''')
|
||||
|
||||
pyparsing_common.fnumber.runTests('''
|
||||
pyparsing_common.fnumber.run_tests('''
|
||||
# any int or real number, returned as float
|
||||
100
|
||||
-100
|
||||
@@ -52,19 +52,19 @@ class pyparsing_common:
|
||||
1e-12
|
||||
''')
|
||||
|
||||
pyparsing_common.hex_integer.runTests('''
|
||||
pyparsing_common.hex_integer.run_tests('''
|
||||
# hex numbers
|
||||
100
|
||||
FF
|
||||
''')
|
||||
|
||||
pyparsing_common.fraction.runTests('''
|
||||
pyparsing_common.fraction.run_tests('''
|
||||
# fractions
|
||||
1/2
|
||||
-3/4
|
||||
''')
|
||||
|
||||
pyparsing_common.mixed_integer.runTests('''
|
||||
pyparsing_common.mixed_integer.run_tests('''
|
||||
# mixed fractions
|
||||
1
|
||||
1/2
|
||||
@@ -73,8 +73,8 @@ class pyparsing_common:
|
||||
''')
|
||||
|
||||
import uuid
|
||||
pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID))
|
||||
pyparsing_common.uuid.runTests('''
|
||||
pyparsing_common.uuid.set_parse_action(token_map(uuid.UUID))
|
||||
pyparsing_common.uuid.run_tests('''
|
||||
# uuid
|
||||
12345678-1234-5678-1234-567812345678
|
||||
''')
|
||||
@@ -260,8 +260,8 @@ class pyparsing_common:
|
||||
Example::
|
||||
|
||||
date_expr = pyparsing_common.iso8601_date.copy()
|
||||
date_expr.setParseAction(pyparsing_common.convertToDate())
|
||||
print(date_expr.parseString("1999-12-31"))
|
||||
date_expr.set_parse_action(pyparsing_common.convert_to_date())
|
||||
print(date_expr.parse_string("1999-12-31"))
|
||||
|
||||
prints::
|
||||
|
||||
@@ -287,8 +287,8 @@ class pyparsing_common:
|
||||
Example::
|
||||
|
||||
dt_expr = pyparsing_common.iso8601_datetime.copy()
|
||||
dt_expr.setParseAction(pyparsing_common.convertToDatetime())
|
||||
print(dt_expr.parseString("1999-12-31T23:59:59.999"))
|
||||
dt_expr.set_parse_action(pyparsing_common.convert_to_datetime())
|
||||
print(dt_expr.parse_string("1999-12-31T23:59:59.999"))
|
||||
|
||||
prints::
|
||||
|
||||
@@ -326,9 +326,9 @@ class pyparsing_common:
|
||||
|
||||
# strip HTML links from normal text
|
||||
text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>'
|
||||
td, td_end = makeHTMLTags("TD")
|
||||
table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end
|
||||
print(table_text.parseString(text).body)
|
||||
td, td_end = make_html_tags("TD")
|
||||
table_text = td + SkipTo(td_end).set_parse_action(pyparsing_common.strip_html_tags)("body") + td_end
|
||||
print(table_text.parse_string(text).body)
|
||||
|
||||
Prints::
|
||||
|
||||
@@ -348,7 +348,7 @@ class pyparsing_common:
|
||||
.streamline()
|
||||
.set_name("commaItem")
|
||||
)
|
||||
comma_separated_list = delimited_list(
|
||||
comma_separated_list = DelimitedList(
|
||||
Opt(quoted_string.copy() | _commasepitem, default="")
|
||||
).set_name("comma separated list")
|
||||
"""Predefined expression of 1 or more printable words or quoted strings, separated by commas."""
|
||||
@@ -363,7 +363,7 @@ class pyparsing_common:
|
||||
url = Regex(
|
||||
# https://mathiasbynens.be/demo/url-regex
|
||||
# https://gist.github.com/dperini/729294
|
||||
r"^" +
|
||||
r"(?P<url>" +
|
||||
# protocol identifier (optional)
|
||||
# short syntax // still required
|
||||
r"(?:(?:(?P<scheme>https?|ftp):)?\/\/)" +
|
||||
@@ -405,18 +405,26 @@ class pyparsing_common:
|
||||
r"(\?(?P<query>[^#]*))?" +
|
||||
# fragment (optional)
|
||||
r"(#(?P<fragment>\S*))?" +
|
||||
r"$"
|
||||
r")"
|
||||
).set_name("url")
|
||||
"""URL (http/https/ftp scheme)"""
|
||||
# fmt: on
|
||||
|
||||
# pre-PEP8 compatibility names
|
||||
convertToInteger = convert_to_integer
|
||||
"""Deprecated - use :class:`convert_to_integer`"""
|
||||
convertToFloat = convert_to_float
|
||||
"""Deprecated - use :class:`convert_to_float`"""
|
||||
convertToDate = convert_to_date
|
||||
"""Deprecated - use :class:`convert_to_date`"""
|
||||
convertToDatetime = convert_to_datetime
|
||||
"""Deprecated - use :class:`convert_to_datetime`"""
|
||||
stripHTMLTags = strip_html_tags
|
||||
"""Deprecated - use :class:`strip_html_tags`"""
|
||||
upcaseTokens = upcase_tokens
|
||||
"""Deprecated - use :class:`upcase_tokens`"""
|
||||
downcaseTokens = downcase_tokens
|
||||
"""Deprecated - use :class:`downcase_tokens`"""
|
||||
|
||||
|
||||
_builtin_exprs = [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
# mypy: ignore-errors
|
||||
import railroad
|
||||
from pipenv.patched.pip._vendor import pyparsing
|
||||
import typing
|
||||
@@ -17,11 +18,13 @@ import inspect
|
||||
|
||||
|
||||
jinja2_template_source = """\
|
||||
{% if not embed %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
{% endif %}
|
||||
{% if not head %}
|
||||
<style type="text/css">
|
||||
<style>
|
||||
.railroad-heading {
|
||||
font-family: monospace;
|
||||
}
|
||||
@@ -29,8 +32,10 @@ jinja2_template_source = """\
|
||||
{% else %}
|
||||
{{ head | safe }}
|
||||
{% endif %}
|
||||
{% if not embed %}
|
||||
</head>
|
||||
<body>
|
||||
{% endif %}
|
||||
{{ body | safe }}
|
||||
{% for diagram in diagrams %}
|
||||
<div class="railroad-group">
|
||||
@@ -41,8 +46,10 @@ jinja2_template_source = """\
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not embed %}
|
||||
</body>
|
||||
</html>
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
template = Template(jinja2_template_source)
|
||||
@@ -127,7 +134,7 @@ class EditablePartial(Generic[T]):
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
|
||||
def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str:
|
||||
def railroad_to_html(diagrams: List[NamedDiagram], embed=False, **kwargs) -> str:
|
||||
"""
|
||||
Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams
|
||||
:params kwargs: kwargs to be passed in to the template
|
||||
@@ -137,13 +144,17 @@ def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str:
|
||||
if diagram.diagram is None:
|
||||
continue
|
||||
io = StringIO()
|
||||
diagram.diagram.writeSvg(io.write)
|
||||
try:
|
||||
css = kwargs.get('css')
|
||||
diagram.diagram.writeStandalone(io.write, css=css)
|
||||
except AttributeError:
|
||||
diagram.diagram.writeSvg(io.write)
|
||||
title = diagram.name
|
||||
if diagram.index == 0:
|
||||
title += " (root)"
|
||||
data.append({"title": title, "text": "", "svg": io.getvalue()})
|
||||
|
||||
return template.render(diagrams=data, **kwargs)
|
||||
return template.render(diagrams=data, embed=embed, **kwargs)
|
||||
|
||||
|
||||
def resolve_partial(partial: "EditablePartial[T]") -> T:
|
||||
@@ -398,7 +409,6 @@ def _apply_diagram_item_enhancements(fn):
|
||||
show_results_names: bool = False,
|
||||
show_groups: bool = False,
|
||||
) -> typing.Optional[EditablePartial]:
|
||||
|
||||
ret = fn(
|
||||
element,
|
||||
parent,
|
||||
@@ -555,9 +565,11 @@ def _to_diagram_element(
|
||||
else:
|
||||
ret = EditablePartial.from_call(railroad.Group, label="", item="")
|
||||
elif isinstance(element, pyparsing.TokenConverter):
|
||||
ret = EditablePartial.from_call(
|
||||
AnnotatedItem, label=type(element).__name__.lower(), item=""
|
||||
)
|
||||
label = type(element).__name__.lower()
|
||||
if label == "tokenconverter":
|
||||
ret = EditablePartial.from_call(railroad.Sequence, items=[])
|
||||
else:
|
||||
ret = EditablePartial.from_call(AnnotatedItem, label=label, item="")
|
||||
elif isinstance(element, pyparsing.Opt):
|
||||
ret = EditablePartial.from_call(railroad.Optional, item="")
|
||||
elif isinstance(element, pyparsing.OneOrMore):
|
||||
@@ -571,10 +583,12 @@ def _to_diagram_element(
|
||||
elif isinstance(element, pyparsing.Empty) and not element.customName:
|
||||
# Skip unnamed "Empty" elements
|
||||
ret = None
|
||||
elif len(exprs) > 1:
|
||||
elif isinstance(element, pyparsing.ParseElementEnhance):
|
||||
ret = EditablePartial.from_call(railroad.Sequence, items=[])
|
||||
elif len(exprs) > 0 and not element_results_name:
|
||||
ret = EditablePartial.from_call(railroad.Group, item="", label=name)
|
||||
elif len(exprs) > 0:
|
||||
ret = EditablePartial.from_call(railroad.Sequence, items=[])
|
||||
else:
|
||||
terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName)
|
||||
ret = terminal
|
||||
|
||||
@@ -4,7 +4,13 @@ import re
|
||||
import sys
|
||||
import typing
|
||||
|
||||
from .util import col, line, lineno, _collapse_string_to_ranges
|
||||
from .util import (
|
||||
col,
|
||||
line,
|
||||
lineno,
|
||||
_collapse_string_to_ranges,
|
||||
replaced_by_pep8,
|
||||
)
|
||||
from .unicode import pyparsing_unicode as ppu
|
||||
|
||||
|
||||
@@ -19,6 +25,20 @@ _exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.")
|
||||
class ParseBaseException(Exception):
|
||||
"""base exception class for all parsing runtime exceptions"""
|
||||
|
||||
loc: int
|
||||
msg: str
|
||||
pstr: str
|
||||
parser_element: typing.Any # "ParserElement"
|
||||
args: typing.Tuple[str, int, typing.Optional[str]]
|
||||
|
||||
__slots__ = (
|
||||
"loc",
|
||||
"msg",
|
||||
"pstr",
|
||||
"parser_element",
|
||||
"args",
|
||||
)
|
||||
|
||||
# Performance tuning: we construct a *lot* of these, so keep this
|
||||
# constructor as small and fast as possible
|
||||
def __init__(
|
||||
@@ -35,7 +55,7 @@ class ParseBaseException(Exception):
|
||||
else:
|
||||
self.msg = msg
|
||||
self.pstr = pstr
|
||||
self.parser_element = self.parserElement = elem
|
||||
self.parser_element = elem
|
||||
self.args = (pstr, loc, msg)
|
||||
|
||||
@staticmethod
|
||||
@@ -64,7 +84,7 @@ class ParseBaseException(Exception):
|
||||
if isinstance(exc, ParseBaseException):
|
||||
ret.append(exc.line)
|
||||
ret.append(" " * (exc.column - 1) + "^")
|
||||
ret.append("{}: {}".format(type(exc).__name__, exc))
|
||||
ret.append(f"{type(exc).__name__}: {exc}")
|
||||
|
||||
if depth > 0:
|
||||
callers = inspect.getinnerframes(exc.__traceback__, context=depth)
|
||||
@@ -74,7 +94,9 @@ class ParseBaseException(Exception):
|
||||
|
||||
f_self = frm.f_locals.get("self", None)
|
||||
if isinstance(f_self, ParserElement):
|
||||
if frm.f_code.co_name not in ("parseImpl", "_parseNoCache"):
|
||||
if not frm.f_code.co_name.startswith(
|
||||
("parseImpl", "_parseNoCache")
|
||||
):
|
||||
continue
|
||||
if id(f_self) in seen:
|
||||
continue
|
||||
@@ -82,21 +104,19 @@ class ParseBaseException(Exception):
|
||||
|
||||
self_type = type(f_self)
|
||||
ret.append(
|
||||
"{}.{} - {}".format(
|
||||
self_type.__module__, self_type.__name__, f_self
|
||||
)
|
||||
f"{self_type.__module__}.{self_type.__name__} - {f_self}"
|
||||
)
|
||||
|
||||
elif f_self is not None:
|
||||
self_type = type(f_self)
|
||||
ret.append("{}.{}".format(self_type.__module__, self_type.__name__))
|
||||
ret.append(f"{self_type.__module__}.{self_type.__name__}")
|
||||
|
||||
else:
|
||||
code = frm.f_code
|
||||
if code.co_name in ("wrapper", "<module>"):
|
||||
continue
|
||||
|
||||
ret.append("{}".format(code.co_name))
|
||||
ret.append(code.co_name)
|
||||
|
||||
depth -= 1
|
||||
if not depth:
|
||||
@@ -110,7 +130,7 @@ class ParseBaseException(Exception):
|
||||
internal factory method to simplify creating one type of ParseException
|
||||
from another - avoids having __init__ signature conflicts among subclasses
|
||||
"""
|
||||
return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
|
||||
return cls(pe.pstr, pe.loc, pe.msg, pe.parser_element)
|
||||
|
||||
@property
|
||||
def line(self) -> str:
|
||||
@@ -140,6 +160,15 @@ class ParseBaseException(Exception):
|
||||
"""
|
||||
return col(self.loc, self.pstr)
|
||||
|
||||
# pre-PEP8 compatibility
|
||||
@property
|
||||
def parserElement(self):
|
||||
return self.parser_element
|
||||
|
||||
@parserElement.setter
|
||||
def parserElement(self, elem):
|
||||
self.parser_element = elem
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.pstr:
|
||||
if self.loc >= len(self.pstr):
|
||||
@@ -154,14 +183,14 @@ class ParseBaseException(Exception):
|
||||
foundstr = (", found %r" % found).replace(r"\\", "\\")
|
||||
else:
|
||||
foundstr = ""
|
||||
return "{}{} (at char {}), (line:{}, col:{})".format(
|
||||
self.msg, foundstr, self.loc, self.lineno, self.column
|
||||
)
|
||||
return f"{self.msg}{foundstr} (at char {self.loc}), (line:{self.lineno}, col:{self.column})"
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str:
|
||||
def mark_input_line(
|
||||
self, marker_string: typing.Optional[str] = None, *, markerString: str = ">!<"
|
||||
) -> str:
|
||||
"""
|
||||
Extracts the exception line from the input string, and marks
|
||||
the location of the exception with a special symbol.
|
||||
@@ -214,7 +243,10 @@ class ParseBaseException(Exception):
|
||||
"""
|
||||
return self.explain_exception(self, depth)
|
||||
|
||||
markInputline = mark_input_line
|
||||
# fmt: off
|
||||
@replaced_by_pep8(mark_input_line)
|
||||
def markInputline(self): ...
|
||||
# fmt: on
|
||||
|
||||
|
||||
class ParseException(ParseBaseException):
|
||||
@@ -264,4 +296,4 @@ class RecursiveGrammarException(Exception):
|
||||
self.parseElementTrace = parseElementList
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "RecursiveGrammarException: {}".format(self.parseElementTrace)
|
||||
return f"RecursiveGrammarException: {self.parseElementTrace}"
|
||||
|
||||
@@ -1,73 +1,22 @@
|
||||
# helpers.py
|
||||
import html.entities
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
|
||||
from . import __diag__
|
||||
from .core import *
|
||||
from .util import _bslash, _flatten, _escape_regex_range_chars
|
||||
from .util import (
|
||||
_bslash,
|
||||
_flatten,
|
||||
_escape_regex_range_chars,
|
||||
replaced_by_pep8,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# global helpers
|
||||
#
|
||||
def delimited_list(
|
||||
expr: Union[str, ParserElement],
|
||||
delim: Union[str, ParserElement] = ",",
|
||||
combine: bool = False,
|
||||
min: typing.Optional[int] = None,
|
||||
max: typing.Optional[int] = None,
|
||||
*,
|
||||
allow_trailing_delim: bool = False,
|
||||
) -> ParserElement:
|
||||
"""Helper to define a delimited list of expressions - the delimiter
|
||||
defaults to ','. By default, the list elements and delimiters can
|
||||
have intervening whitespace, and comments, but this can be
|
||||
overridden by passing ``combine=True`` in the constructor. If
|
||||
``combine`` is set to ``True``, the matching tokens are
|
||||
returned as a single token string, with the delimiters included;
|
||||
otherwise, the matching tokens are returned as a list of tokens,
|
||||
with the delimiters suppressed.
|
||||
|
||||
If ``allow_trailing_delim`` is set to True, then the list may end with
|
||||
a delimiter.
|
||||
|
||||
Example::
|
||||
|
||||
delimited_list(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc']
|
||||
delimited_list(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
|
||||
"""
|
||||
if isinstance(expr, str_type):
|
||||
expr = ParserElement._literalStringClass(expr)
|
||||
|
||||
dlName = "{expr} [{delim} {expr}]...{end}".format(
|
||||
expr=str(expr.copy().streamline()),
|
||||
delim=str(delim),
|
||||
end=" [{}]".format(str(delim)) if allow_trailing_delim else "",
|
||||
)
|
||||
|
||||
if not combine:
|
||||
delim = Suppress(delim)
|
||||
|
||||
if min is not None:
|
||||
if min < 1:
|
||||
raise ValueError("min must be greater than 0")
|
||||
min -= 1
|
||||
if max is not None:
|
||||
if min is not None and max <= min:
|
||||
raise ValueError("max must be greater than, or equal to min")
|
||||
max -= 1
|
||||
delimited_list_expr = expr + (delim + expr)[min, max]
|
||||
|
||||
if allow_trailing_delim:
|
||||
delimited_list_expr += Opt(delim)
|
||||
|
||||
if combine:
|
||||
return Combine(delimited_list_expr).set_name(dlName)
|
||||
else:
|
||||
return delimited_list_expr.set_name(dlName)
|
||||
|
||||
|
||||
def counted_array(
|
||||
expr: ParserElement,
|
||||
int_expr: typing.Optional[ParserElement] = None,
|
||||
@@ -187,7 +136,7 @@ def match_previous_expr(expr: ParserElement) -> ParserElement:
|
||||
theseTokens = _flatten(t.as_list())
|
||||
if theseTokens != matchTokens:
|
||||
raise ParseException(
|
||||
s, l, "Expected {}, found{}".format(matchTokens, theseTokens)
|
||||
s, l, f"Expected {matchTokens}, found{theseTokens}"
|
||||
)
|
||||
|
||||
rep.set_parse_action(must_match_these_tokens, callDuringTry=True)
|
||||
@@ -218,7 +167,7 @@ def one_of(
|
||||
- ``caseless`` - treat all literals as caseless - (default= ``False``)
|
||||
- ``use_regex`` - as an optimization, will
|
||||
generate a :class:`Regex` object; otherwise, will generate
|
||||
a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if
|
||||
a :class:`MatchFirst` object (if ``caseless=True`` or ``as_keyword=True``, or if
|
||||
creating a :class:`Regex` raises an exception) - (default= ``True``)
|
||||
- ``as_keyword`` - enforce :class:`Keyword`-style matching on the
|
||||
generated expressions - (default= ``False``)
|
||||
@@ -262,6 +211,7 @@ def one_of(
|
||||
|
||||
symbols: List[str] = []
|
||||
if isinstance(strs, str_type):
|
||||
strs = typing.cast(str, strs)
|
||||
symbols = strs.split()
|
||||
elif isinstance(strs, Iterable):
|
||||
symbols = list(strs)
|
||||
@@ -293,15 +243,13 @@ def one_of(
|
||||
try:
|
||||
if all(len(sym) == 1 for sym in symbols):
|
||||
# symbols are just single characters, create range regex pattern
|
||||
patt = "[{}]".format(
|
||||
"".join(_escape_regex_range_chars(sym) for sym in symbols)
|
||||
)
|
||||
patt = f"[{''.join(_escape_regex_range_chars(sym) for sym in symbols)}]"
|
||||
else:
|
||||
patt = "|".join(re.escape(sym) for sym in symbols)
|
||||
|
||||
# wrap with \b word break markers if defining as keywords
|
||||
if asKeyword:
|
||||
patt = r"\b(?:{})\b".format(patt)
|
||||
patt = rf"\b(?:{patt})\b"
|
||||
|
||||
ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols))
|
||||
|
||||
@@ -371,7 +319,7 @@ def original_text_for(
|
||||
expression. Useful to restore the parsed fields of an HTML start
|
||||
tag into the raw tag text itself, or to revert separate tokens with
|
||||
intervening whitespace back to the original matching input text. By
|
||||
default, returns astring containing the original parsed text.
|
||||
default, returns a string containing the original parsed text.
|
||||
|
||||
If the optional ``as_string`` argument is passed as
|
||||
``False``, then the return value is
|
||||
@@ -390,7 +338,7 @@ def original_text_for(
|
||||
src = "this is test <b> bold <i>text</i> </b> normal text "
|
||||
for tag in ("b", "i"):
|
||||
opener, closer = make_html_tags(tag)
|
||||
patt = original_text_for(opener + SkipTo(closer) + closer)
|
||||
patt = original_text_for(opener + ... + closer)
|
||||
print(patt.search_string(src)[0])
|
||||
|
||||
prints::
|
||||
@@ -426,7 +374,7 @@ def ungroup(expr: ParserElement) -> ParserElement:
|
||||
|
||||
def locatedExpr(expr: ParserElement) -> ParserElement:
|
||||
"""
|
||||
(DEPRECATED - future code should use the Located class)
|
||||
(DEPRECATED - future code should use the :class:`Located` class)
|
||||
Helper to decorate a returned token with its starting and ending
|
||||
locations in the input string.
|
||||
|
||||
@@ -437,12 +385,12 @@ def locatedExpr(expr: ParserElement) -> ParserElement:
|
||||
- ``value`` - the actual parsed results
|
||||
|
||||
Be careful if the input text contains ``<TAB>`` characters, you
|
||||
may want to call :class:`ParserElement.parseWithTabs`
|
||||
may want to call :class:`ParserElement.parse_with_tabs`
|
||||
|
||||
Example::
|
||||
|
||||
wd = Word(alphas)
|
||||
for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
|
||||
for match in locatedExpr(wd).search_string("ljsdf123lksdjjf123lkkjj1222"):
|
||||
print(match)
|
||||
|
||||
prints::
|
||||
@@ -471,6 +419,7 @@ def nested_expr(
|
||||
closing delimiters (``"("`` and ``")"`` are the default).
|
||||
|
||||
Parameters:
|
||||
|
||||
- ``opener`` - opening character for a nested list
|
||||
(default= ``"("``); can also be a pyparsing expression
|
||||
- ``closer`` - closing character for a nested list
|
||||
@@ -507,7 +456,7 @@ def nested_expr(
|
||||
|
||||
c_function = (decl_data_type("type")
|
||||
+ ident("name")
|
||||
+ LPAR + Opt(delimited_list(arg), [])("args") + RPAR
|
||||
+ LPAR + Opt(DelimitedList(arg), [])("args") + RPAR
|
||||
+ code_body("body"))
|
||||
c_function.ignore(c_style_comment)
|
||||
|
||||
@@ -539,6 +488,8 @@ def nested_expr(
|
||||
raise ValueError("opening and closing strings cannot be the same")
|
||||
if content is None:
|
||||
if isinstance(opener, str_type) and isinstance(closer, str_type):
|
||||
opener = typing.cast(str, opener)
|
||||
closer = typing.cast(str, closer)
|
||||
if len(opener) == 1 and len(closer) == 1:
|
||||
if ignoreExpr is not None:
|
||||
content = Combine(
|
||||
@@ -695,12 +646,15 @@ common_html_entity = Regex("&(?P<entity>" + "|".join(_htmlEntityMap) + ");").set
|
||||
)
|
||||
|
||||
|
||||
def replace_html_entity(t):
|
||||
def replace_html_entity(s, l, t):
|
||||
"""Helper parser action to replace common HTML entities with their special characters"""
|
||||
return _htmlEntityMap.get(t.entity)
|
||||
|
||||
|
||||
class OpAssoc(Enum):
|
||||
"""Enumeration of operator associativity
|
||||
- used in constructing InfixNotationOperatorSpec for :class:`infix_notation`"""
|
||||
|
||||
LEFT = 1
|
||||
RIGHT = 2
|
||||
|
||||
@@ -742,6 +696,7 @@ def infix_notation(
|
||||
improve your parser performance.
|
||||
|
||||
Parameters:
|
||||
|
||||
- ``base_expr`` - expression representing the most basic operand to
|
||||
be used in the expression
|
||||
- ``op_list`` - list of tuples, one for each operator precedence level
|
||||
@@ -764,11 +719,11 @@ def infix_notation(
|
||||
``set_parse_action(*fn)``
|
||||
(:class:`ParserElement.set_parse_action`)
|
||||
- ``lpar`` - expression for matching left-parentheses; if passed as a
|
||||
str, then will be parsed as Suppress(lpar). If lpar is passed as
|
||||
str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as
|
||||
an expression (such as ``Literal('(')``), then it will be kept in
|
||||
the parsed results, and grouped with them. (default= ``Suppress('(')``)
|
||||
- ``rpar`` - expression for matching right-parentheses; if passed as a
|
||||
str, then will be parsed as Suppress(rpar). If rpar is passed as
|
||||
str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as
|
||||
an expression (such as ``Literal(')')``), then it will be kept in
|
||||
the parsed results, and grouped with them. (default= ``Suppress(')')``)
|
||||
|
||||
@@ -800,9 +755,13 @@ def infix_notation(
|
||||
(5+3)*6
|
||||
[[[5, '+', 3], '*', 6]]
|
||||
|
||||
(5+x)*y
|
||||
[[[5, '+', 'x'], '*', 'y']]
|
||||
|
||||
-2--11
|
||||
[[['-', 2], '-', ['-', 11]]]
|
||||
"""
|
||||
|
||||
# captive version of FollowedBy that does not do parse actions or capture results names
|
||||
class _FB(FollowedBy):
|
||||
def parseImpl(self, instring, loc, doActions=True):
|
||||
@@ -823,19 +782,25 @@ def infix_notation(
|
||||
else:
|
||||
lastExpr = base_expr | (lpar + ret + rpar)
|
||||
|
||||
arity: int
|
||||
rightLeftAssoc: opAssoc
|
||||
pa: typing.Optional[ParseAction]
|
||||
opExpr1: ParserElement
|
||||
opExpr2: ParserElement
|
||||
for i, operDef in enumerate(op_list):
|
||||
opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4]
|
||||
opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] # type: ignore[assignment]
|
||||
if isinstance(opExpr, str_type):
|
||||
opExpr = ParserElement._literalStringClass(opExpr)
|
||||
opExpr = typing.cast(ParserElement, opExpr)
|
||||
if arity == 3:
|
||||
if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2:
|
||||
raise ValueError(
|
||||
"if numterms=3, opExpr must be a tuple or list of two expressions"
|
||||
)
|
||||
opExpr1, opExpr2 = opExpr
|
||||
term_name = "{}{} term".format(opExpr1, opExpr2)
|
||||
term_name = f"{opExpr1}{opExpr2} term"
|
||||
else:
|
||||
term_name = "{} term".format(opExpr)
|
||||
term_name = f"{opExpr} term"
|
||||
|
||||
if not 1 <= arity <= 3:
|
||||
raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
|
||||
@@ -843,7 +808,8 @@ def infix_notation(
|
||||
if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT):
|
||||
raise ValueError("operator must indicate right or left associativity")
|
||||
|
||||
thisExpr: Forward = Forward().set_name(term_name)
|
||||
thisExpr: ParserElement = Forward().set_name(term_name)
|
||||
thisExpr = typing.cast(Forward, thisExpr)
|
||||
if rightLeftAssoc is OpAssoc.LEFT:
|
||||
if arity == 1:
|
||||
matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...])
|
||||
@@ -890,7 +856,7 @@ def infix_notation(
|
||||
|
||||
def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]):
|
||||
"""
|
||||
(DEPRECATED - use IndentedBlock class instead)
|
||||
(DEPRECATED - use :class:`IndentedBlock` class instead)
|
||||
Helper method for defining space-delimited indentation blocks,
|
||||
such as those used to define block statements in Python source code.
|
||||
|
||||
@@ -1063,22 +1029,28 @@ _builtin_exprs: List[ParserElement] = [
|
||||
]
|
||||
|
||||
|
||||
# compatibility function, superseded by DelimitedList class
|
||||
def delimited_list(
|
||||
expr: Union[str, ParserElement],
|
||||
delim: Union[str, ParserElement] = ",",
|
||||
combine: bool = False,
|
||||
min: typing.Optional[int] = None,
|
||||
max: typing.Optional[int] = None,
|
||||
*,
|
||||
allow_trailing_delim: bool = False,
|
||||
) -> ParserElement:
|
||||
"""(DEPRECATED - use :class:`DelimitedList` class)"""
|
||||
return DelimitedList(
|
||||
expr, delim, combine, min, max, allow_trailing_delim=allow_trailing_delim
|
||||
)
|
||||
|
||||
|
||||
# pre-PEP8 compatible names
|
||||
delimitedList = delimited_list
|
||||
countedArray = counted_array
|
||||
matchPreviousLiteral = match_previous_literal
|
||||
matchPreviousExpr = match_previous_expr
|
||||
oneOf = one_of
|
||||
dictOf = dict_of
|
||||
originalTextFor = original_text_for
|
||||
nestedExpr = nested_expr
|
||||
makeHTMLTags = make_html_tags
|
||||
makeXMLTags = make_xml_tags
|
||||
anyOpenTag, anyCloseTag = any_open_tag, any_close_tag
|
||||
commonHTMLEntity = common_html_entity
|
||||
replaceHTMLEntity = replace_html_entity
|
||||
# fmt: off
|
||||
opAssoc = OpAssoc
|
||||
infixNotation = infix_notation
|
||||
anyOpenTag = any_open_tag
|
||||
anyCloseTag = any_close_tag
|
||||
commonHTMLEntity = common_html_entity
|
||||
cStyleComment = c_style_comment
|
||||
htmlComment = html_comment
|
||||
restOfLine = rest_of_line
|
||||
@@ -1086,3 +1058,43 @@ dblSlashComment = dbl_slash_comment
|
||||
cppStyleComment = cpp_style_comment
|
||||
javaStyleComment = java_style_comment
|
||||
pythonStyleComment = python_style_comment
|
||||
|
||||
@replaced_by_pep8(DelimitedList)
|
||||
def delimitedList(): ...
|
||||
|
||||
@replaced_by_pep8(DelimitedList)
|
||||
def delimited_list(): ...
|
||||
|
||||
@replaced_by_pep8(counted_array)
|
||||
def countedArray(): ...
|
||||
|
||||
@replaced_by_pep8(match_previous_literal)
|
||||
def matchPreviousLiteral(): ...
|
||||
|
||||
@replaced_by_pep8(match_previous_expr)
|
||||
def matchPreviousExpr(): ...
|
||||
|
||||
@replaced_by_pep8(one_of)
|
||||
def oneOf(): ...
|
||||
|
||||
@replaced_by_pep8(dict_of)
|
||||
def dictOf(): ...
|
||||
|
||||
@replaced_by_pep8(original_text_for)
|
||||
def originalTextFor(): ...
|
||||
|
||||
@replaced_by_pep8(nested_expr)
|
||||
def nestedExpr(): ...
|
||||
|
||||
@replaced_by_pep8(make_html_tags)
|
||||
def makeHTMLTags(): ...
|
||||
|
||||
@replaced_by_pep8(make_xml_tags)
|
||||
def makeXMLTags(): ...
|
||||
|
||||
@replaced_by_pep8(replace_html_entity)
|
||||
def replaceHTMLEntity(): ...
|
||||
|
||||
@replaced_by_pep8(infix_notation)
|
||||
def infixNotation(): ...
|
||||
# fmt: on
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
# results.py
|
||||
from collections.abc import MutableMapping, Mapping, MutableSequence, Iterator
|
||||
from collections.abc import (
|
||||
MutableMapping,
|
||||
Mapping,
|
||||
MutableSequence,
|
||||
Iterator,
|
||||
Sequence,
|
||||
Container,
|
||||
)
|
||||
import pprint
|
||||
from weakref import ref as wkref
|
||||
from typing import Tuple, Any
|
||||
from typing import Tuple, Any, Dict, Set, List
|
||||
|
||||
str_type: Tuple[type, ...] = (str, bytes)
|
||||
_generator_type = type((_ for _ in ()))
|
||||
|
||||
|
||||
class _ParseResultsWithOffset:
|
||||
tup: Tuple["ParseResults", int]
|
||||
__slots__ = ["tup"]
|
||||
|
||||
def __init__(self, p1, p2):
|
||||
self.tup = (p1, p2)
|
||||
def __init__(self, p1: "ParseResults", p2: int):
|
||||
self.tup: Tuple[ParseResults, int] = (p1, p2)
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.tup[i]
|
||||
@@ -47,7 +54,7 @@ class ParseResults:
|
||||
result = date_str.parse_string("1999/12/31")
|
||||
|
||||
def test(s, fn=repr):
|
||||
print("{} -> {}".format(s, fn(eval(s))))
|
||||
print(f"{s} -> {fn(eval(s))}")
|
||||
test("list(result)")
|
||||
test("result[0]")
|
||||
test("result['month']")
|
||||
@@ -70,27 +77,33 @@ class ParseResults:
|
||||
- year: '1999'
|
||||
"""
|
||||
|
||||
_null_values: Tuple[Any, ...] = (None, [], "", ())
|
||||
_null_values: Tuple[Any, ...] = (None, [], ())
|
||||
|
||||
__slots__ = [
|
||||
_name: str
|
||||
_parent: "ParseResults"
|
||||
_all_names: Set[str]
|
||||
_modal: bool
|
||||
_toklist: List[Any]
|
||||
_tokdict: Dict[str, Any]
|
||||
|
||||
__slots__ = (
|
||||
"_name",
|
||||
"_parent",
|
||||
"_all_names",
|
||||
"_modal",
|
||||
"_toklist",
|
||||
"_tokdict",
|
||||
"__weakref__",
|
||||
]
|
||||
)
|
||||
|
||||
class List(list):
|
||||
"""
|
||||
Simple wrapper class to distinguish parsed list results that should be preserved
|
||||
as actual Python lists, instead of being converted to :class:`ParseResults`:
|
||||
as actual Python lists, instead of being converted to :class:`ParseResults`::
|
||||
|
||||
LBRACK, RBRACK = map(pp.Suppress, "[]")
|
||||
element = pp.Forward()
|
||||
item = ppc.integer
|
||||
element_list = LBRACK + pp.delimited_list(element) + RBRACK
|
||||
element_list = LBRACK + pp.DelimitedList(element) + RBRACK
|
||||
|
||||
# add parse actions to convert from ParseResults to actual Python collection types
|
||||
def as_python_list(t):
|
||||
@@ -107,7 +120,7 @@ class ParseResults:
|
||||
(2,3,4)
|
||||
''', post_parse=lambda s, r: (r[0], type(r[0])))
|
||||
|
||||
prints:
|
||||
prints::
|
||||
|
||||
100
|
||||
(100, <class 'int'>)
|
||||
@@ -127,8 +140,7 @@ class ParseResults:
|
||||
|
||||
if not isinstance(contained, list):
|
||||
raise TypeError(
|
||||
"{} may only be constructed with a list,"
|
||||
" not {}".format(cls.__name__, type(contained).__name__)
|
||||
f"{cls.__name__} may only be constructed with a list, not {type(contained).__name__}"
|
||||
)
|
||||
|
||||
return list.__new__(cls)
|
||||
@@ -159,6 +171,7 @@ class ParseResults:
|
||||
def __init__(
|
||||
self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance
|
||||
):
|
||||
self._tokdict: Dict[str, _ParseResultsWithOffset]
|
||||
self._modal = modal
|
||||
if name is not None and name != "":
|
||||
if isinstance(name, int):
|
||||
@@ -210,7 +223,7 @@ class ParseResults:
|
||||
]
|
||||
sub = v
|
||||
if isinstance(sub, ParseResults):
|
||||
sub._parent = wkref(self)
|
||||
sub._parent = self
|
||||
|
||||
def __delitem__(self, i):
|
||||
if isinstance(i, (int, slice)):
|
||||
@@ -263,7 +276,7 @@ class ParseResults:
|
||||
"""
|
||||
Since ``keys()`` returns an iterator, this method is helpful in bypassing
|
||||
code that looks for the existence of any defined results names."""
|
||||
return bool(self._tokdict)
|
||||
return not not self._tokdict
|
||||
|
||||
def pop(self, *args, **kwargs):
|
||||
"""
|
||||
@@ -311,9 +324,7 @@ class ParseResults:
|
||||
if k == "default":
|
||||
args = (args[0], v)
|
||||
else:
|
||||
raise TypeError(
|
||||
"pop() got an unexpected keyword argument {!r}".format(k)
|
||||
)
|
||||
raise TypeError(f"pop() got an unexpected keyword argument {k!r}")
|
||||
if isinstance(args[0], int) or len(args) == 1 or args[0] in self:
|
||||
index = args[0]
|
||||
ret = self[index]
|
||||
@@ -423,12 +434,15 @@ class ParseResults:
|
||||
raise AttributeError(name)
|
||||
return ""
|
||||
|
||||
def __add__(self, other) -> "ParseResults":
|
||||
def __add__(self, other: "ParseResults") -> "ParseResults":
|
||||
ret = self.copy()
|
||||
ret += other
|
||||
return ret
|
||||
|
||||
def __iadd__(self, other) -> "ParseResults":
|
||||
def __iadd__(self, other: "ParseResults") -> "ParseResults":
|
||||
if not other:
|
||||
return self
|
||||
|
||||
if other._tokdict:
|
||||
offset = len(self._toklist)
|
||||
addoffset = lambda a: offset if a < 0 else a + offset
|
||||
@@ -441,7 +455,7 @@ class ParseResults:
|
||||
for k, v in otherdictitems:
|
||||
self[k] = v
|
||||
if isinstance(v[0], ParseResults):
|
||||
v[0]._parent = wkref(self)
|
||||
v[0]._parent = self
|
||||
|
||||
self._toklist += other._toklist
|
||||
self._all_names |= other._all_names
|
||||
@@ -456,7 +470,7 @@ class ParseResults:
|
||||
return other + self
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "{}({!r}, {})".format(type(self).__name__, self._toklist, self.as_dict())
|
||||
return f"{type(self).__name__}({self._toklist!r}, {self.as_dict()})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return (
|
||||
@@ -532,7 +546,10 @@ class ParseResults:
|
||||
|
||||
def copy(self) -> "ParseResults":
|
||||
"""
|
||||
Returns a new copy of a :class:`ParseResults` object.
|
||||
Returns a new shallow copy of a :class:`ParseResults` object. `ParseResults`
|
||||
items contained within the source are shared with the copy. Use
|
||||
:class:`ParseResults.deepcopy()` to create a copy with its own separate
|
||||
content values.
|
||||
"""
|
||||
ret = ParseResults(self._toklist)
|
||||
ret._tokdict = self._tokdict.copy()
|
||||
@@ -541,6 +558,27 @@ class ParseResults:
|
||||
ret._name = self._name
|
||||
return ret
|
||||
|
||||
def deepcopy(self) -> "ParseResults":
|
||||
"""
|
||||
Returns a new deep copy of a :class:`ParseResults` object.
|
||||
"""
|
||||
ret = self.copy()
|
||||
# replace values with copies if they are of known mutable types
|
||||
for i, obj in enumerate(self._toklist):
|
||||
if isinstance(obj, ParseResults):
|
||||
self._toklist[i] = obj.deepcopy()
|
||||
elif isinstance(obj, (str, bytes)):
|
||||
pass
|
||||
elif isinstance(obj, MutableMapping):
|
||||
self._toklist[i] = dest = type(obj)()
|
||||
for k, v in obj.items():
|
||||
dest[k] = v.deepcopy() if isinstance(v, ParseResults) else v
|
||||
elif isinstance(obj, Container):
|
||||
self._toklist[i] = type(obj)(
|
||||
v.deepcopy() if isinstance(v, ParseResults) else v for v in obj
|
||||
)
|
||||
return ret
|
||||
|
||||
def get_name(self):
|
||||
r"""
|
||||
Returns the results name for this token expression. Useful when several
|
||||
@@ -569,20 +607,17 @@ class ParseResults:
|
||||
if self._name:
|
||||
return self._name
|
||||
elif self._parent:
|
||||
par = self._parent()
|
||||
|
||||
def find_in_parent(sub):
|
||||
return next(
|
||||
(
|
||||
k
|
||||
for k, vlist in par._tokdict.items()
|
||||
for v, loc in vlist
|
||||
if sub is v
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
return find_in_parent(self) if par else None
|
||||
par: "ParseResults" = self._parent
|
||||
parent_tokdict_items = par._tokdict.items()
|
||||
return next(
|
||||
(
|
||||
k
|
||||
for k, vlist in parent_tokdict_items
|
||||
for v, loc in vlist
|
||||
if v is self
|
||||
),
|
||||
None,
|
||||
)
|
||||
elif (
|
||||
len(self) == 1
|
||||
and len(self._tokdict) == 1
|
||||
@@ -623,7 +658,7 @@ class ParseResults:
|
||||
for k, v in items:
|
||||
if out:
|
||||
out.append(NL)
|
||||
out.append("{}{}- {}: ".format(indent, (" " * _depth), k))
|
||||
out.append(f"{indent}{(' ' * _depth)}- {k}: ")
|
||||
if isinstance(v, ParseResults):
|
||||
if v:
|
||||
out.append(
|
||||
@@ -685,7 +720,7 @@ class ParseResults:
|
||||
num = Word(nums)
|
||||
func = Forward()
|
||||
term = ident | num | Group('(' + func + ')')
|
||||
func <<= ident + Group(Optional(delimited_list(term)))
|
||||
func <<= ident + Group(Optional(DelimitedList(term)))
|
||||
result = func.parse_string("fna a,b,(fnb c,d,200),100")
|
||||
result.pprint(width=40)
|
||||
|
||||
@@ -705,7 +740,7 @@ class ParseResults:
|
||||
self._toklist,
|
||||
(
|
||||
self._tokdict.copy(),
|
||||
self._parent is not None and self._parent() or None,
|
||||
None,
|
||||
self._all_names,
|
||||
self._name,
|
||||
),
|
||||
@@ -714,10 +749,7 @@ class ParseResults:
|
||||
def __setstate__(self, state):
|
||||
self._toklist, (self._tokdict, par, inAccumNames, self._name) = state
|
||||
self._all_names = set(inAccumNames)
|
||||
if par is not None:
|
||||
self._parent = wkref(par)
|
||||
else:
|
||||
self._parent = None
|
||||
self._parent = None
|
||||
|
||||
def __getnewargs__(self):
|
||||
return self._toklist, self._name
|
||||
@@ -738,6 +770,7 @@ class ParseResults:
|
||||
iter(obj)
|
||||
except Exception:
|
||||
return False
|
||||
# str's are iterable, but in pyparsing, we don't want to iterate over them
|
||||
else:
|
||||
return not isinstance(obj, str_type)
|
||||
|
||||
@@ -752,8 +785,11 @@ class ParseResults:
|
||||
return ret
|
||||
|
||||
asList = as_list
|
||||
"""Deprecated - use :class:`as_list`"""
|
||||
asDict = as_dict
|
||||
"""Deprecated - use :class:`as_dict`"""
|
||||
getName = get_name
|
||||
"""Deprecated - use :class:`get_name`"""
|
||||
|
||||
|
||||
MutableMapping.register(ParseResults)
|
||||
|
||||
@@ -222,7 +222,7 @@ class pyparsing_test:
|
||||
)
|
||||
else:
|
||||
# warning here maybe?
|
||||
print("no validation for {!r}".format(test_string))
|
||||
print(f"no validation for {test_string!r}")
|
||||
|
||||
# do this last, in case some specific test results can be reported instead
|
||||
self.assertTrue(
|
||||
@@ -265,15 +265,18 @@ class pyparsing_test:
|
||||
if expand_tabs:
|
||||
s = s.expandtabs()
|
||||
if mark_control is not None:
|
||||
mark_control = typing.cast(str, mark_control)
|
||||
if mark_control == "unicode":
|
||||
tbl = str.maketrans(
|
||||
{c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433))}
|
||||
| {127: 0x2421}
|
||||
)
|
||||
transtable_map = {
|
||||
c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433))
|
||||
}
|
||||
transtable_map[127] = 0x2421
|
||||
tbl = str.maketrans(transtable_map)
|
||||
eol_mark = ""
|
||||
else:
|
||||
ord_mark_control = ord(mark_control)
|
||||
tbl = str.maketrans(
|
||||
{c: mark_control for c in list(range(0, 32)) + [127]}
|
||||
{c: ord_mark_control for c in list(range(0, 32)) + [127]}
|
||||
)
|
||||
s = s.translate(tbl)
|
||||
if mark_spaces is not None and mark_spaces != " ":
|
||||
@@ -303,7 +306,7 @@ class pyparsing_test:
|
||||
header0 = (
|
||||
lead
|
||||
+ "".join(
|
||||
"{}{}".format(" " * 99, (i + 1) % 100)
|
||||
f"{' ' * 99}{(i + 1) % 100}"
|
||||
for i in range(max(max_line_len // 100, 1))
|
||||
)
|
||||
+ "\n"
|
||||
@@ -313,10 +316,7 @@ class pyparsing_test:
|
||||
header1 = (
|
||||
header0
|
||||
+ lead
|
||||
+ "".join(
|
||||
" {}".format((i + 1) % 10)
|
||||
for i in range(-(-max_line_len // 10))
|
||||
)
|
||||
+ "".join(f" {(i + 1) % 10}" for i in range(-(-max_line_len // 10)))
|
||||
+ "\n"
|
||||
)
|
||||
header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n"
|
||||
@@ -324,7 +324,7 @@ class pyparsing_test:
|
||||
header1
|
||||
+ header2
|
||||
+ "\n".join(
|
||||
"{:{}d}:{}{}".format(i, lineno_width, line, eol_mark)
|
||||
f"{i:{lineno_width}d}:{line}{eol_mark}"
|
||||
for i, line in enumerate(s_lines, start=start_line)
|
||||
)
|
||||
+ "\n"
|
||||
|
||||
@@ -64,27 +64,27 @@ class unicode_set:
|
||||
|
||||
@_lazyclassproperty
|
||||
def printables(cls):
|
||||
"all non-whitespace characters in this range"
|
||||
"""all non-whitespace characters in this range"""
|
||||
return "".join(filterfalse(str.isspace, cls._chars_for_ranges))
|
||||
|
||||
@_lazyclassproperty
|
||||
def alphas(cls):
|
||||
"all alphabetic characters in this range"
|
||||
"""all alphabetic characters in this range"""
|
||||
return "".join(filter(str.isalpha, cls._chars_for_ranges))
|
||||
|
||||
@_lazyclassproperty
|
||||
def nums(cls):
|
||||
"all numeric digit characters in this range"
|
||||
"""all numeric digit characters in this range"""
|
||||
return "".join(filter(str.isdigit, cls._chars_for_ranges))
|
||||
|
||||
@_lazyclassproperty
|
||||
def alphanums(cls):
|
||||
"all alphanumeric characters in this range"
|
||||
"""all alphanumeric characters in this range"""
|
||||
return cls.alphas + cls.nums
|
||||
|
||||
@_lazyclassproperty
|
||||
def identchars(cls):
|
||||
"all characters in this range that are valid identifier characters, plus underscore '_'"
|
||||
"""all characters in this range that are valid identifier characters, plus underscore '_'"""
|
||||
return "".join(
|
||||
sorted(
|
||||
set(
|
||||
@@ -100,13 +100,13 @@ class unicode_set:
|
||||
def identbodychars(cls):
|
||||
"""
|
||||
all characters in this range that are valid identifier body characters,
|
||||
plus the digits 0-9
|
||||
plus the digits 0-9, and · (Unicode MIDDLE DOT)
|
||||
"""
|
||||
return "".join(
|
||||
sorted(
|
||||
set(
|
||||
cls.identchars
|
||||
+ "0123456789"
|
||||
+ "0123456789·"
|
||||
+ "".join(
|
||||
[c for c in cls._chars_for_ranges if ("_" + c).isidentifier()]
|
||||
)
|
||||
@@ -114,6 +114,16 @@ class unicode_set:
|
||||
)
|
||||
)
|
||||
|
||||
@_lazyclassproperty
|
||||
def identifier(cls):
|
||||
"""
|
||||
a pyparsing Word expression for an identifier using this range's definitions for
|
||||
identchars and identbodychars
|
||||
"""
|
||||
from pipenv.patched.pip._vendor.pyparsing import Word
|
||||
|
||||
return Word(cls.identchars, cls.identbodychars)
|
||||
|
||||
|
||||
class pyparsing_unicode(unicode_set):
|
||||
"""
|
||||
@@ -128,32 +138,32 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class BasicMultilingualPlane(unicode_set):
|
||||
"Unicode set for the Basic Multilingual Plane"
|
||||
"""Unicode set for the Basic Multilingual Plane"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0020, 0xFFFF),
|
||||
]
|
||||
|
||||
class Latin1(unicode_set):
|
||||
"Unicode set for Latin-1 Unicode Character Range"
|
||||
"""Unicode set for Latin-1 Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0020, 0x007E),
|
||||
(0x00A0, 0x00FF),
|
||||
]
|
||||
|
||||
class LatinA(unicode_set):
|
||||
"Unicode set for Latin-A Unicode Character Range"
|
||||
"""Unicode set for Latin-A Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0100, 0x017F),
|
||||
]
|
||||
|
||||
class LatinB(unicode_set):
|
||||
"Unicode set for Latin-B Unicode Character Range"
|
||||
"""Unicode set for Latin-B Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0180, 0x024F),
|
||||
]
|
||||
|
||||
class Greek(unicode_set):
|
||||
"Unicode set for Greek Unicode Character Ranges"
|
||||
"""Unicode set for Greek Unicode Character Ranges"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0342, 0x0345),
|
||||
(0x0370, 0x0377),
|
||||
@@ -193,7 +203,7 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Cyrillic(unicode_set):
|
||||
"Unicode set for Cyrillic Unicode Character Range"
|
||||
"""Unicode set for Cyrillic Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0400, 0x052F),
|
||||
(0x1C80, 0x1C88),
|
||||
@@ -206,7 +216,7 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Chinese(unicode_set):
|
||||
"Unicode set for Chinese Unicode Character Range"
|
||||
"""Unicode set for Chinese Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x2E80, 0x2E99),
|
||||
(0x2E9B, 0x2EF3),
|
||||
@@ -229,8 +239,7 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Japanese(unicode_set):
|
||||
"Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges"
|
||||
_ranges: UnicodeRangeList = []
|
||||
"""Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges"""
|
||||
|
||||
class Kanji(unicode_set):
|
||||
"Unicode set for Kanji Unicode Character Range"
|
||||
@@ -240,7 +249,7 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Hiragana(unicode_set):
|
||||
"Unicode set for Hiragana Unicode Character Range"
|
||||
"""Unicode set for Hiragana Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x3041, 0x3096),
|
||||
(0x3099, 0x30A0),
|
||||
@@ -252,7 +261,7 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Katakana(unicode_set):
|
||||
"Unicode set for Katakana Unicode Character Range"
|
||||
"""Unicode set for Katakana Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x3099, 0x309C),
|
||||
(0x30A0, 0x30FF),
|
||||
@@ -265,8 +274,18 @@ class pyparsing_unicode(unicode_set):
|
||||
(0x1F213,),
|
||||
]
|
||||
|
||||
漢字 = Kanji
|
||||
カタカナ = Katakana
|
||||
ひらがな = Hiragana
|
||||
|
||||
_ranges = (
|
||||
Kanji._ranges
|
||||
+ Hiragana._ranges
|
||||
+ Katakana._ranges
|
||||
)
|
||||
|
||||
class Hangul(unicode_set):
|
||||
"Unicode set for Hangul (Korean) Unicode Character Range"
|
||||
"""Unicode set for Hangul (Korean) Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x1100, 0x11FF),
|
||||
(0x302E, 0x302F),
|
||||
@@ -288,17 +307,17 @@ class pyparsing_unicode(unicode_set):
|
||||
Korean = Hangul
|
||||
|
||||
class CJK(Chinese, Japanese, Hangul):
|
||||
"Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range"
|
||||
"""Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range"""
|
||||
|
||||
class Thai(unicode_set):
|
||||
"Unicode set for Thai Unicode Character Range"
|
||||
"""Unicode set for Thai Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0E01, 0x0E3A),
|
||||
(0x0E3F, 0x0E5B)
|
||||
]
|
||||
|
||||
class Arabic(unicode_set):
|
||||
"Unicode set for Arabic Unicode Character Range"
|
||||
"""Unicode set for Arabic Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0600, 0x061B),
|
||||
(0x061E, 0x06FF),
|
||||
@@ -306,7 +325,7 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Hebrew(unicode_set):
|
||||
"Unicode set for Hebrew Unicode Character Range"
|
||||
"""Unicode set for Hebrew Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0591, 0x05C7),
|
||||
(0x05D0, 0x05EA),
|
||||
@@ -320,33 +339,23 @@ class pyparsing_unicode(unicode_set):
|
||||
]
|
||||
|
||||
class Devanagari(unicode_set):
|
||||
"Unicode set for Devanagari Unicode Character Range"
|
||||
"""Unicode set for Devanagari Unicode Character Range"""
|
||||
_ranges: UnicodeRangeList = [
|
||||
(0x0900, 0x097F),
|
||||
(0xA8E0, 0xA8FF)
|
||||
]
|
||||
|
||||
BMP = BasicMultilingualPlane
|
||||
|
||||
# add language identifiers using language Unicode
|
||||
العربية = Arabic
|
||||
中文 = Chinese
|
||||
кириллица = Cyrillic
|
||||
Ελληνικά = Greek
|
||||
עִברִית = Hebrew
|
||||
日本語 = Japanese
|
||||
한국어 = Korean
|
||||
ไทย = Thai
|
||||
देवनागरी = Devanagari
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
pyparsing_unicode.Japanese._ranges = (
|
||||
pyparsing_unicode.Japanese.Kanji._ranges
|
||||
+ pyparsing_unicode.Japanese.Hiragana._ranges
|
||||
+ pyparsing_unicode.Japanese.Katakana._ranges
|
||||
)
|
||||
|
||||
pyparsing_unicode.BMP = pyparsing_unicode.BasicMultilingualPlane
|
||||
|
||||
# add language identifiers using language Unicode
|
||||
pyparsing_unicode.العربية = pyparsing_unicode.Arabic
|
||||
pyparsing_unicode.中文 = pyparsing_unicode.Chinese
|
||||
pyparsing_unicode.кириллица = pyparsing_unicode.Cyrillic
|
||||
pyparsing_unicode.Ελληνικά = pyparsing_unicode.Greek
|
||||
pyparsing_unicode.עִברִית = pyparsing_unicode.Hebrew
|
||||
pyparsing_unicode.日本語 = pyparsing_unicode.Japanese
|
||||
pyparsing_unicode.Japanese.漢字 = pyparsing_unicode.Japanese.Kanji
|
||||
pyparsing_unicode.Japanese.カタカナ = pyparsing_unicode.Japanese.Katakana
|
||||
pyparsing_unicode.Japanese.ひらがな = pyparsing_unicode.Japanese.Hiragana
|
||||
pyparsing_unicode.한국어 = pyparsing_unicode.Korean
|
||||
pyparsing_unicode.ไทย = pyparsing_unicode.Thai
|
||||
pyparsing_unicode.देवनागरी = pyparsing_unicode.Devanagari
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# util.py
|
||||
import inspect
|
||||
import warnings
|
||||
import types
|
||||
import collections
|
||||
import itertools
|
||||
from functools import lru_cache
|
||||
from typing import List, Union, Iterable
|
||||
from functools import lru_cache, wraps
|
||||
from typing import Callable, List, Union, Iterable, TypeVar, cast
|
||||
|
||||
_bslash = chr(92)
|
||||
C = TypeVar("C", bound=Callable)
|
||||
|
||||
|
||||
class __config_flags:
|
||||
@@ -20,18 +22,15 @@ class __config_flags:
|
||||
def _set(cls, dname, value):
|
||||
if dname in cls._fixed_names:
|
||||
warnings.warn(
|
||||
"{}.{} {} is {} and cannot be overridden".format(
|
||||
cls.__name__,
|
||||
dname,
|
||||
cls._type_desc,
|
||||
str(getattr(cls, dname)).upper(),
|
||||
)
|
||||
f"{cls.__name__}.{dname} {cls._type_desc} is {str(getattr(cls, dname)).upper()}"
|
||||
f" and cannot be overridden",
|
||||
stacklevel=3,
|
||||
)
|
||||
return
|
||||
if dname in cls._all_names:
|
||||
setattr(cls, dname, value)
|
||||
else:
|
||||
raise ValueError("no such {} {!r}".format(cls._type_desc, dname))
|
||||
raise ValueError(f"no such {cls._type_desc} {dname!r}")
|
||||
|
||||
enable = classmethod(lambda cls, name: cls._set(name, True))
|
||||
disable = classmethod(lambda cls, name: cls._set(name, False))
|
||||
@@ -45,7 +44,7 @@ def col(loc: int, strg: str) -> int:
|
||||
|
||||
Note: the default parsing behavior is to expand tabs in the input string
|
||||
before starting the parsing process. See
|
||||
:class:`ParserElement.parseString` for more
|
||||
:class:`ParserElement.parse_string` for more
|
||||
information on parsing strings containing ``<TAB>`` s, and suggested
|
||||
methods to maintain a consistent view of the parsed string, the parse
|
||||
location, and line and column positions within the parsed string.
|
||||
@@ -60,7 +59,7 @@ def lineno(loc: int, strg: str) -> int:
|
||||
The first line is number 1.
|
||||
|
||||
Note - the default parsing behavior is to expand tabs in the input string
|
||||
before starting the parsing process. See :class:`ParserElement.parseString`
|
||||
before starting the parsing process. See :class:`ParserElement.parse_string`
|
||||
for more information on parsing strings containing ``<TAB>`` s, and
|
||||
suggested methods to maintain a consistent view of the parsed string, the
|
||||
parse location, and line and column positions within the parsed string.
|
||||
@@ -102,19 +101,24 @@ class _UnboundedCache:
|
||||
class _FifoCache:
|
||||
def __init__(self, size):
|
||||
self.not_in_cache = not_in_cache = object()
|
||||
cache = collections.OrderedDict()
|
||||
cache = {}
|
||||
keyring = [object()] * size
|
||||
cache_get = cache.get
|
||||
cache_pop = cache.pop
|
||||
keyiter = itertools.cycle(range(size))
|
||||
|
||||
def get(_, key):
|
||||
return cache_get(key, not_in_cache)
|
||||
|
||||
def set_(_, key, value):
|
||||
cache[key] = value
|
||||
while len(cache) > size:
|
||||
cache.popitem(last=False)
|
||||
i = next(keyiter)
|
||||
cache_pop(keyring[i], None)
|
||||
keyring[i] = key
|
||||
|
||||
def clear(_):
|
||||
cache.clear()
|
||||
keyring[:] = [object()] * size
|
||||
|
||||
self.size = size
|
||||
self.get = types.MethodType(get, self)
|
||||
@@ -189,9 +193,9 @@ def _collapse_string_to_ranges(
|
||||
is_consecutive.value = next(is_consecutive.counter)
|
||||
return is_consecutive.value
|
||||
|
||||
is_consecutive.prev = 0
|
||||
is_consecutive.counter = itertools.count()
|
||||
is_consecutive.value = -1
|
||||
is_consecutive.prev = 0 # type: ignore [attr-defined]
|
||||
is_consecutive.counter = itertools.count() # type: ignore [attr-defined]
|
||||
is_consecutive.value = -1 # type: ignore [attr-defined]
|
||||
|
||||
def escape_re_range_char(c):
|
||||
return "\\" + c if c in r"\^-][" else c
|
||||
@@ -215,9 +219,7 @@ def _collapse_string_to_ranges(
|
||||
else:
|
||||
sep = "" if ord(last) == ord(first) + 1 else "-"
|
||||
ret.append(
|
||||
"{}{}{}".format(
|
||||
escape_re_range_char(first), sep, escape_re_range_char(last)
|
||||
)
|
||||
f"{escape_re_range_char(first)}{sep}{escape_re_range_char(last)}"
|
||||
)
|
||||
else:
|
||||
ret = [escape_re_range_char(c) for c in s]
|
||||
@@ -233,3 +235,50 @@ def _flatten(ll: list) -> list:
|
||||
else:
|
||||
ret.append(i)
|
||||
return ret
|
||||
|
||||
|
||||
def _make_synonym_function(compat_name: str, fn: C) -> C:
|
||||
# In a future version, uncomment the code in the internal _inner() functions
|
||||
# to begin emitting DeprecationWarnings.
|
||||
|
||||
# Unwrap staticmethod/classmethod
|
||||
fn = getattr(fn, "__func__", fn)
|
||||
|
||||
# (Presence of 'self' arg in signature is used by explain_exception() methods, so we take
|
||||
# some extra steps to add it if present in decorated function.)
|
||||
if "self" == list(inspect.signature(fn).parameters)[0]:
|
||||
|
||||
@wraps(fn)
|
||||
def _inner(self, *args, **kwargs):
|
||||
# warnings.warn(
|
||||
# f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3
|
||||
# )
|
||||
return fn(self, *args, **kwargs)
|
||||
|
||||
else:
|
||||
|
||||
@wraps(fn)
|
||||
def _inner(*args, **kwargs):
|
||||
# warnings.warn(
|
||||
# f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3
|
||||
# )
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
_inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`"""
|
||||
_inner.__name__ = compat_name
|
||||
_inner.__annotations__ = fn.__annotations__
|
||||
if isinstance(fn, types.FunctionType):
|
||||
_inner.__kwdefaults__ = fn.__kwdefaults__
|
||||
elif isinstance(fn, type) and hasattr(fn, "__init__"):
|
||||
_inner.__kwdefaults__ = fn.__init__.__kwdefaults__
|
||||
else:
|
||||
_inner.__kwdefaults__ = None
|
||||
_inner.__qualname__ = fn.__qualname__
|
||||
return cast(C, _inner)
|
||||
|
||||
|
||||
def replaced_by_pep8(fn: C) -> Callable[[Callable], C]:
|
||||
"""
|
||||
Decorator for pre-PEP8 compatibility synonyms, to link them to the new function.
|
||||
"""
|
||||
return lambda other: _make_synonym_function(other.__name__, fn)
|
||||
|
||||
@@ -63,10 +63,10 @@ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_ver
|
||||
# Check urllib3 for compatibility.
|
||||
major, minor, patch = urllib3_version # noqa: F811
|
||||
major, minor, patch = int(major), int(minor), int(patch)
|
||||
# urllib3 >= 1.21.1, <= 1.26
|
||||
assert major == 1
|
||||
assert minor >= 21
|
||||
assert minor <= 26
|
||||
# urllib3 >= 1.21.1
|
||||
assert major >= 1
|
||||
if major == 1:
|
||||
assert minor >= 21
|
||||
|
||||
# Check charset_normalizer for compatibility.
|
||||
if chardet_version:
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
__title__ = "requests"
|
||||
__description__ = "Python HTTP for Humans."
|
||||
__url__ = "https://requests.readthedocs.io"
|
||||
__version__ = "2.28.2"
|
||||
__build__ = 0x022802
|
||||
__version__ = "2.31.0"
|
||||
__build__ = 0x023100
|
||||
__author__ = "Kenneth Reitz"
|
||||
__author_email__ = "me@kennethreitz.org"
|
||||
__license__ = "Apache 2.0"
|
||||
|
||||
@@ -14,9 +14,11 @@ _VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$")
|
||||
_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$")
|
||||
_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$")
|
||||
|
||||
_HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR)
|
||||
_HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE)
|
||||
HEADER_VALIDATORS = {
|
||||
bytes: (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE),
|
||||
str: (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR),
|
||||
bytes: _HEADER_VALIDATORS_BYTE,
|
||||
str: _HEADER_VALIDATORS_STR,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ from pipenv.patched.pip._vendor.urllib3.exceptions import ProxyError as _ProxyEr
|
||||
from pipenv.patched.pip._vendor.urllib3.exceptions import ReadTimeoutError, ResponseError
|
||||
from pipenv.patched.pip._vendor.urllib3.exceptions import SSLError as _SSLError
|
||||
from pipenv.patched.pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url
|
||||
from pipenv.patched.pip._vendor.urllib3.response import HTTPResponse
|
||||
from pipenv.patched.pip._vendor.urllib3.util import Timeout as TimeoutSauce
|
||||
from pipenv.patched.pip._vendor.urllib3.util import parse_url
|
||||
from pipenv.patched.pip._vendor.urllib3.util.retry import Retry
|
||||
@@ -194,7 +193,6 @@ class HTTPAdapter(BaseAdapter):
|
||||
num_pools=connections,
|
||||
maxsize=maxsize,
|
||||
block=block,
|
||||
strict=True,
|
||||
**pool_kwargs,
|
||||
)
|
||||
|
||||
@@ -485,63 +483,19 @@ class HTTPAdapter(BaseAdapter):
|
||||
timeout = TimeoutSauce(connect=timeout, read=timeout)
|
||||
|
||||
try:
|
||||
if not chunked:
|
||||
resp = conn.urlopen(
|
||||
method=request.method,
|
||||
url=url,
|
||||
body=request.body,
|
||||
headers=request.headers,
|
||||
redirect=False,
|
||||
assert_same_host=False,
|
||||
preload_content=False,
|
||||
decode_content=False,
|
||||
retries=self.max_retries,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
# Send the request.
|
||||
else:
|
||||
if hasattr(conn, "proxy_pool"):
|
||||
conn = conn.proxy_pool
|
||||
|
||||
low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT)
|
||||
|
||||
try:
|
||||
skip_host = "Host" in request.headers
|
||||
low_conn.putrequest(
|
||||
request.method,
|
||||
url,
|
||||
skip_accept_encoding=True,
|
||||
skip_host=skip_host,
|
||||
)
|
||||
|
||||
for header, value in request.headers.items():
|
||||
low_conn.putheader(header, value)
|
||||
|
||||
low_conn.endheaders()
|
||||
|
||||
for i in request.body:
|
||||
low_conn.send(hex(len(i))[2:].encode("utf-8"))
|
||||
low_conn.send(b"\r\n")
|
||||
low_conn.send(i)
|
||||
low_conn.send(b"\r\n")
|
||||
low_conn.send(b"0\r\n\r\n")
|
||||
|
||||
# Receive the response from the server
|
||||
r = low_conn.getresponse()
|
||||
|
||||
resp = HTTPResponse.from_httplib(
|
||||
r,
|
||||
pool=conn,
|
||||
connection=low_conn,
|
||||
preload_content=False,
|
||||
decode_content=False,
|
||||
)
|
||||
except Exception:
|
||||
# If we hit any problems here, clean up the connection.
|
||||
# Then, raise so that we can handle the actual exception.
|
||||
low_conn.close()
|
||||
raise
|
||||
resp = conn.urlopen(
|
||||
method=request.method,
|
||||
url=url,
|
||||
body=request.body,
|
||||
headers=request.headers,
|
||||
redirect=False,
|
||||
assert_same_host=False,
|
||||
preload_content=False,
|
||||
decode_content=False,
|
||||
retries=self.max_retries,
|
||||
timeout=timeout,
|
||||
chunked=chunked,
|
||||
)
|
||||
|
||||
except (ProtocolError, OSError) as err:
|
||||
raise ConnectionError(err, request=request)
|
||||
|
||||
@@ -106,7 +106,7 @@ def post(url, data=None, json=None, **kwargs):
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
||||
object to send in the body of the :class:`Request`.
|
||||
:param json: (optional) json data to send in the body of the :class:`Request`.
|
||||
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
:rtype: requests.Response
|
||||
@@ -121,7 +121,7 @@ def put(url, data=None, **kwargs):
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
||||
object to send in the body of the :class:`Request`.
|
||||
:param json: (optional) json data to send in the body of the :class:`Request`.
|
||||
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
:rtype: requests.Response
|
||||
@@ -136,7 +136,7 @@ def patch(url, data=None, **kwargs):
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
||||
object to send in the body of the :class:`Request`.
|
||||
:param json: (optional) json data to send in the body of the :class:`Request`.
|
||||
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
:rtype: requests.Response
|
||||
|
||||
@@ -324,7 +324,9 @@ class SessionRedirectMixin:
|
||||
except KeyError:
|
||||
username, password = None, None
|
||||
|
||||
if username and password:
|
||||
# urllib3 handles proxy authorization for us in the standard adapter.
|
||||
# Avoid appending this to TLS tunneled requests where it may be leaked.
|
||||
if not scheme.startswith('https') and username and password:
|
||||
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
|
||||
|
||||
return new_proxies
|
||||
|
||||
@@ -25,7 +25,12 @@ from . import certs
|
||||
from .__version__ import __version__
|
||||
|
||||
# to_native_string is unused here, but imported here for backwards compatibility
|
||||
from ._internal_utils import HEADER_VALIDATORS, to_native_string # noqa: F401
|
||||
from ._internal_utils import ( # noqa: F401
|
||||
_HEADER_VALIDATORS_BYTE,
|
||||
_HEADER_VALIDATORS_STR,
|
||||
HEADER_VALIDATORS,
|
||||
to_native_string,
|
||||
)
|
||||
from .compat import (
|
||||
Mapping,
|
||||
basestring,
|
||||
@@ -1031,20 +1036,23 @@ def check_header_validity(header):
|
||||
:param header: tuple, in the format (name, value).
|
||||
"""
|
||||
name, value = header
|
||||
|
||||
for part in header:
|
||||
if type(part) not in HEADER_VALIDATORS:
|
||||
raise InvalidHeader(
|
||||
f"Header part ({part!r}) from {{{name!r}: {value!r}}} must be "
|
||||
f"of type str or bytes, not {type(part)}"
|
||||
)
|
||||
|
||||
_validate_header_part(name, "name", HEADER_VALIDATORS[type(name)][0])
|
||||
_validate_header_part(value, "value", HEADER_VALIDATORS[type(value)][1])
|
||||
_validate_header_part(header, name, 0)
|
||||
_validate_header_part(header, value, 1)
|
||||
|
||||
|
||||
def _validate_header_part(header_part, header_kind, validator):
|
||||
def _validate_header_part(header, header_part, header_validator_index):
|
||||
if isinstance(header_part, str):
|
||||
validator = _HEADER_VALIDATORS_STR[header_validator_index]
|
||||
elif isinstance(header_part, bytes):
|
||||
validator = _HEADER_VALIDATORS_BYTE[header_validator_index]
|
||||
else:
|
||||
raise InvalidHeader(
|
||||
f"Header part ({header_part!r}) from {header} "
|
||||
f"must be of type str or bytes, not {type(header_part)}"
|
||||
)
|
||||
|
||||
if not validator.match(header_part):
|
||||
header_kind = "name" if header_validator_index == 0 else "value"
|
||||
raise InvalidHeader(
|
||||
f"Invalid leading whitespace, reserved character(s), or return"
|
||||
f"character(s) in header {header_kind}: {header_part!r}"
|
||||
|
||||
@@ -952,6 +952,7 @@ class Console:
|
||||
force_color = self._environ.get("FORCE_COLOR")
|
||||
if force_color is not None:
|
||||
self._force_terminal = True
|
||||
return True
|
||||
|
||||
isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
|
||||
try:
|
||||
@@ -2000,7 +2001,6 @@ class Console:
|
||||
self._record_buffer.extend(self._buffer[:])
|
||||
|
||||
if self._buffer_index == 0:
|
||||
|
||||
if self.is_jupyter: # pragma: no cover
|
||||
from .jupyter import display
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from typing import (
|
||||
from pipenv.patched.pip._vendor.rich.repr import RichReprResult
|
||||
|
||||
try:
|
||||
import pipenv.vendor.attr as _attr_module
|
||||
import attr as _attr_module
|
||||
|
||||
_has_attrs = hasattr(_attr_module, "ib")
|
||||
except ImportError: # pragma: no cover
|
||||
|
||||
@@ -590,7 +590,6 @@ class Syntax(JupyterMixin):
|
||||
def __rich_measure__(
|
||||
self, console: "Console", options: "ConsoleOptions"
|
||||
) -> "Measurement":
|
||||
|
||||
_, right, _, left = Padding.unpack(self.padding)
|
||||
padding = left + right
|
||||
if self.code_width is not None:
|
||||
@@ -688,7 +687,7 @@ class Syntax(JupyterMixin):
|
||||
lines = (
|
||||
Text("\n")
|
||||
.join(lines)
|
||||
.with_indent_guides(self.tab_size, style=style)
|
||||
.with_indent_guides(self.tab_size, style=style + Style(italic=False))
|
||||
.split("\n", allow_blank=True)
|
||||
)
|
||||
|
||||
@@ -830,7 +829,6 @@ def _get_code_index_for_syntax_position(
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ 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
|
||||
Mathematisch Centrum (CWI, see https://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)
|
||||
National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
@@ -19,7 +19,7 @@ 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
|
||||
All Python releases are Open Source (see https://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.
|
||||
@@ -59,6 +59,17 @@ direction to make these releases possible.
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
Python software and documentation are licensed under the
|
||||
Python Software Foundation License Version 2.
|
||||
|
||||
Starting with Python 3.8.6, examples, recipes, and other code in
|
||||
the documentation are dual licensed under the PSF License Version 2
|
||||
and the Zero-Clause BSD license.
|
||||
|
||||
Some software incorporated into Python is under different licenses.
|
||||
The licenses are listed with code falling under that license.
|
||||
|
||||
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
@@ -73,7 +84,7 @@ 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;
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
@@ -252,3 +263,17 @@ 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.
|
||||
|
||||
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
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.
|
||||
|
||||
@@ -65,8 +65,10 @@ __all__ = [
|
||||
'get_args',
|
||||
'get_origin',
|
||||
'get_original_bases',
|
||||
'get_protocol_members',
|
||||
'get_type_hints',
|
||||
'IntVar',
|
||||
'is_protocol',
|
||||
'is_typeddict',
|
||||
'Literal',
|
||||
'NewType',
|
||||
@@ -85,6 +87,45 @@ __all__ = [
|
||||
'NoReturn',
|
||||
'Required',
|
||||
'NotRequired',
|
||||
|
||||
# Pure aliases, have always been in typing
|
||||
'AbstractSet',
|
||||
'AnyStr',
|
||||
'BinaryIO',
|
||||
'Callable',
|
||||
'Collection',
|
||||
'Container',
|
||||
'Dict',
|
||||
'ForwardRef',
|
||||
'FrozenSet',
|
||||
'Generator',
|
||||
'Generic',
|
||||
'Hashable',
|
||||
'IO',
|
||||
'ItemsView',
|
||||
'Iterable',
|
||||
'Iterator',
|
||||
'KeysView',
|
||||
'List',
|
||||
'Mapping',
|
||||
'MappingView',
|
||||
'Match',
|
||||
'MutableMapping',
|
||||
'MutableSequence',
|
||||
'MutableSet',
|
||||
'Optional',
|
||||
'Pattern',
|
||||
'Reversible',
|
||||
'Sequence',
|
||||
'Set',
|
||||
'Sized',
|
||||
'TextIO',
|
||||
'Tuple',
|
||||
'Union',
|
||||
'ValuesView',
|
||||
'cast',
|
||||
'no_type_check',
|
||||
'no_type_check_decorator',
|
||||
]
|
||||
|
||||
# for backward compatibility
|
||||
@@ -201,17 +242,19 @@ else:
|
||||
|
||||
ClassVar = typing.ClassVar
|
||||
|
||||
|
||||
class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
class _FinalForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __getitem__(self, parameters):
|
||||
item = typing._type_check(parameters,
|
||||
f'{self._name} accepts only a single type.')
|
||||
@@ -303,14 +346,11 @@ else:
|
||||
def __hash__(self):
|
||||
return hash(frozenset(_value_and_type_iter(self.__args__)))
|
||||
|
||||
class _LiteralForm(typing._SpecialForm, _root=True):
|
||||
class _LiteralForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __init__(self, doc: str):
|
||||
self._name = 'Literal'
|
||||
self._doc = self.__doc__ = doc
|
||||
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
def __getitem__(self, parameters):
|
||||
if not isinstance(parameters, tuple):
|
||||
parameters = (parameters,)
|
||||
@@ -453,9 +493,10 @@ TYPE_CHECKING = typing.TYPE_CHECKING
|
||||
_PROTO_ALLOWLIST = {
|
||||
'collections.abc': [
|
||||
'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable',
|
||||
'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
|
||||
'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer',
|
||||
],
|
||||
'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'],
|
||||
'typing_extensions': ['Buffer'],
|
||||
}
|
||||
|
||||
|
||||
@@ -545,9 +586,8 @@ def _caller(depth=2):
|
||||
# so we backport the 3.12 version of Protocol to Python <=3.11
|
||||
if sys.version_info >= (3, 12):
|
||||
Protocol = typing.Protocol
|
||||
runtime_checkable = typing.runtime_checkable
|
||||
else:
|
||||
def _allow_reckless_class_checks(depth=4):
|
||||
def _allow_reckless_class_checks(depth=3):
|
||||
"""Allow instance and class checks for special stdlib modules.
|
||||
The abc and functools modules indiscriminately call isinstance() and
|
||||
issubclass() on the whole MRO of a user class, which may contain protocols.
|
||||
@@ -558,11 +598,41 @@ else:
|
||||
if type(self)._is_protocol:
|
||||
raise TypeError('Protocols cannot be instantiated')
|
||||
|
||||
class _ProtocolMeta(abc.ABCMeta):
|
||||
if sys.version_info >= (3, 8):
|
||||
# Inheriting from typing._ProtocolMeta isn't actually desirable,
|
||||
# but is necessary to allow typing.Protocol and typing_extensions.Protocol
|
||||
# to mix without getting TypeErrors about "metaclass conflict"
|
||||
_typing_Protocol = typing.Protocol
|
||||
_ProtocolMetaBase = type(_typing_Protocol)
|
||||
else:
|
||||
_typing_Protocol = _marker
|
||||
_ProtocolMetaBase = abc.ABCMeta
|
||||
|
||||
class _ProtocolMeta(_ProtocolMetaBase):
|
||||
# This metaclass is somewhat unfortunate,
|
||||
# but is necessary for several reasons...
|
||||
#
|
||||
# NOTE: DO NOT call super() in any methods in this class
|
||||
# That would call the methods on typing._ProtocolMeta on Python 3.8-3.11
|
||||
# and those are slow
|
||||
def __new__(mcls, name, bases, namespace, **kwargs):
|
||||
if name == "Protocol" and len(bases) < 2:
|
||||
pass
|
||||
elif {Protocol, _typing_Protocol} & set(bases):
|
||||
for base in bases:
|
||||
if not (
|
||||
base in {object, typing.Generic, Protocol, _typing_Protocol}
|
||||
or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, [])
|
||||
or is_protocol(base)
|
||||
):
|
||||
raise TypeError(
|
||||
f"Protocols can only inherit from other protocols, "
|
||||
f"got {base!r}"
|
||||
)
|
||||
return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs)
|
||||
|
||||
def __init__(cls, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
abc.ABCMeta.__init__(cls, *args, **kwargs)
|
||||
if getattr(cls, "_is_protocol", False):
|
||||
cls.__protocol_attrs__ = _get_protocol_attrs(cls)
|
||||
# PEP 544 prohibits using issubclass()
|
||||
@@ -572,31 +642,46 @@ else:
|
||||
)
|
||||
|
||||
def __subclasscheck__(cls, other):
|
||||
if cls is Protocol:
|
||||
return type.__subclasscheck__(cls, other)
|
||||
if (
|
||||
getattr(cls, '_is_protocol', False)
|
||||
and not cls.__callable_proto_members_only__
|
||||
and not _allow_reckless_class_checks(depth=3)
|
||||
and not _allow_reckless_class_checks()
|
||||
):
|
||||
raise TypeError(
|
||||
"Protocols with non-method members don't support issubclass()"
|
||||
)
|
||||
return super().__subclasscheck__(other)
|
||||
if not isinstance(other, type):
|
||||
# Same error message as for issubclass(1, int).
|
||||
raise TypeError('issubclass() arg 1 must be a class')
|
||||
if (
|
||||
not cls.__callable_proto_members_only__
|
||||
and cls.__dict__.get("__subclasshook__") is _proto_hook
|
||||
):
|
||||
raise TypeError(
|
||||
"Protocols with non-method members don't support issubclass()"
|
||||
)
|
||||
if not getattr(cls, '_is_runtime_protocol', False):
|
||||
raise TypeError(
|
||||
"Instance and class checks can only be used with "
|
||||
"@runtime_checkable protocols"
|
||||
)
|
||||
return abc.ABCMeta.__subclasscheck__(cls, other)
|
||||
|
||||
def __instancecheck__(cls, instance):
|
||||
# We need this method for situations where attributes are
|
||||
# assigned in __init__.
|
||||
if cls is Protocol:
|
||||
return type.__instancecheck__(cls, instance)
|
||||
if not getattr(cls, "_is_protocol", False):
|
||||
# i.e., it's a concrete subclass of a protocol
|
||||
return super().__instancecheck__(instance)
|
||||
return abc.ABCMeta.__instancecheck__(cls, instance)
|
||||
|
||||
if (
|
||||
not getattr(cls, '_is_runtime_protocol', False) and
|
||||
not _allow_reckless_class_checks(depth=2)
|
||||
not _allow_reckless_class_checks()
|
||||
):
|
||||
raise TypeError("Instance and class checks can only be used with"
|
||||
" @runtime_checkable protocols")
|
||||
|
||||
if super().__instancecheck__(instance):
|
||||
if abc.ABCMeta.__instancecheck__(cls, instance):
|
||||
return True
|
||||
|
||||
for attr in cls.__protocol_attrs__:
|
||||
@@ -615,7 +700,7 @@ else:
|
||||
# Hack so that typing.Generic.__class_getitem__
|
||||
# treats typing_extensions.Protocol
|
||||
# as equivalent to typing.Protocol on Python 3.8+
|
||||
if super().__eq__(other) is True:
|
||||
if abc.ABCMeta.__eq__(cls, other) is True:
|
||||
return True
|
||||
return (
|
||||
cls is Protocol and other is getattr(typing, "Protocol", object())
|
||||
@@ -632,18 +717,6 @@ else:
|
||||
if not cls.__dict__.get('_is_protocol', False):
|
||||
return NotImplemented
|
||||
|
||||
# First, perform various sanity checks.
|
||||
if not getattr(cls, '_is_runtime_protocol', False):
|
||||
if _allow_reckless_class_checks():
|
||||
return NotImplemented
|
||||
raise TypeError("Instance and class checks can only be used with"
|
||||
" @runtime_checkable protocols")
|
||||
|
||||
if not isinstance(other, type):
|
||||
# Same error message as for issubclass(1, int).
|
||||
raise TypeError('issubclass() arg 1 must be a class')
|
||||
|
||||
# Second, perform the actual structural compatibility check.
|
||||
for attr in cls.__protocol_attrs__:
|
||||
for base in other.__mro__:
|
||||
# Check if the members appears in the class dictionary...
|
||||
@@ -657,25 +730,13 @@ else:
|
||||
if (
|
||||
isinstance(annotations, collections.abc.Mapping)
|
||||
and attr in annotations
|
||||
and issubclass(other, (typing.Generic, _ProtocolMeta))
|
||||
# All subclasses of Generic have an _is_proto attribute on 3.8+
|
||||
# But not on 3.7
|
||||
and getattr(other, "_is_protocol", False)
|
||||
and is_protocol(other)
|
||||
):
|
||||
break
|
||||
else:
|
||||
return NotImplemented
|
||||
return True
|
||||
|
||||
def _check_proto_bases(cls):
|
||||
for base in cls.__bases__:
|
||||
if not (base in (object, typing.Generic) or
|
||||
base.__module__ in _PROTO_ALLOWLIST and
|
||||
base.__name__ in _PROTO_ALLOWLIST[base.__module__] or
|
||||
isinstance(base, _ProtocolMeta) and base._is_protocol):
|
||||
raise TypeError('Protocols can only inherit from other'
|
||||
f' protocols, got {repr(base)}')
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
class Protocol(typing.Generic, metaclass=_ProtocolMeta):
|
||||
__doc__ = typing.Protocol.__doc__
|
||||
@@ -694,13 +755,8 @@ else:
|
||||
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
|
||||
|
||||
# ... otherwise check consistency of bases, and prohibit instantiation.
|
||||
_check_proto_bases(cls)
|
||||
if cls.__init__ is Protocol.__init__:
|
||||
# Prohibit instantiation for protocol classes
|
||||
if cls._is_protocol and cls.__init__ is Protocol.__init__:
|
||||
cls.__init__ = _no_init
|
||||
|
||||
else:
|
||||
@@ -790,15 +846,14 @@ else:
|
||||
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.
|
||||
_check_proto_bases(cls)
|
||||
if cls.__init__ is Protocol.__init__:
|
||||
# Prohibit instantiation for protocol classes
|
||||
if cls._is_protocol and cls.__init__ is Protocol.__init__:
|
||||
cls.__init__ = _no_init
|
||||
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
runtime_checkable = typing.runtime_checkable
|
||||
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
|
||||
@@ -898,7 +953,22 @@ else:
|
||||
pass
|
||||
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
def _ensure_subclassable(mro_entries):
|
||||
def inner(func):
|
||||
if sys.implementation.name == "pypy" and sys.version_info < (3, 9):
|
||||
cls_dict = {
|
||||
"__call__": staticmethod(func),
|
||||
"__mro_entries__": staticmethod(mro_entries)
|
||||
}
|
||||
t = type(func.__name__, (), cls_dict)
|
||||
return functools.update_wrapper(t(), func)
|
||||
else:
|
||||
func.__mro_entries__ = mro_entries
|
||||
return func
|
||||
return inner
|
||||
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
# 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"
|
||||
@@ -908,117 +978,61 @@ if sys.version_info >= (3, 12):
|
||||
# Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
|
||||
# Aaaand on 3.12 we add __orig_bases__ to TypedDict
|
||||
# to enable better runtime introspection.
|
||||
# On 3.13 we deprecate some odd ways of creating TypedDicts.
|
||||
TypedDict = typing.TypedDict
|
||||
_TypedDictMeta = typing._TypedDictMeta
|
||||
is_typeddict = typing.is_typeddict
|
||||
else:
|
||||
def _check_fails(cls, other):
|
||||
try:
|
||||
if _caller() 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')
|
||||
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')
|
||||
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")
|
||||
|
||||
if kwargs:
|
||||
warnings.warn(
|
||||
"The kwargs-based syntax for TypedDict definitions is deprecated, "
|
||||
"may be removed in a future version, and may not be "
|
||||
"understood by third-party type checkers.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
ns = {'__annotations__': dict(fields)}
|
||||
module = _caller()
|
||||
if module is not None:
|
||||
# Setting correct module is necessary to make typed dict classes pickleable.
|
||||
ns['__module__'] = module
|
||||
|
||||
return _TypedDictMeta(typename, (), ns, total=total)
|
||||
|
||||
_typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
|
||||
' /, *, total=True, **kwargs)')
|
||||
|
||||
# 3.10.0 and later
|
||||
_TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
_fake_name = "Protocol"
|
||||
else:
|
||||
_fake_name = "_Protocol"
|
||||
|
||||
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)
|
||||
"""Create new typed dict class object.
|
||||
|
||||
is_generic = any(issubclass(base, typing.Generic) for base in bases)
|
||||
This method is called when TypedDict is subclassed,
|
||||
or when TypedDict is instantiated. This way
|
||||
TypedDict supports all three syntax forms described in its docstring.
|
||||
Subclasses and instances of TypedDict return actual dictionaries.
|
||||
"""
|
||||
for base in bases:
|
||||
if type(base) is not _TypedDictMeta and base is not typing.Generic:
|
||||
raise TypeError('cannot inherit from both a TypedDict type '
|
||||
'and a non-TypedDict base class')
|
||||
|
||||
if is_generic:
|
||||
tp_dict.__bases__ = (typing.Generic, dict)
|
||||
_maybe_adjust_parameters(tp_dict)
|
||||
if any(issubclass(b, typing.Generic) for b in bases):
|
||||
generic_base = (typing.Generic,)
|
||||
else:
|
||||
# generic TypedDicts get __orig_bases__ from Generic
|
||||
tp_dict.__orig_bases__ = bases or (TypedDict,)
|
||||
generic_base = ()
|
||||
|
||||
# typing.py generally doesn't let you inherit from plain Generic, unless
|
||||
# the name of the class happens to be "Protocol" (or "_Protocol" on 3.7).
|
||||
tp_dict = type.__new__(_TypedDictMeta, _fake_name, (*generic_base, dict), ns)
|
||||
tp_dict.__name__ = name
|
||||
if tp_dict.__qualname__ == _fake_name:
|
||||
tp_dict.__qualname__ = name
|
||||
|
||||
if not hasattr(tp_dict, '__orig_bases__'):
|
||||
tp_dict.__orig_bases__ = bases
|
||||
|
||||
annotations = {}
|
||||
own_annotations = ns.get('__annotations__', {})
|
||||
msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
|
||||
kwds = {"module": tp_dict.__module__} if _TAKES_MODULE else {}
|
||||
own_annotations = {
|
||||
n: typing._type_check(tp, msg, **kwds)
|
||||
for n, tp in own_annotations.items()
|
||||
}
|
||||
if _TAKES_MODULE:
|
||||
own_annotations = {
|
||||
n: typing._type_check(tp, msg, module=tp_dict.__module__)
|
||||
for n, tp in own_annotations.items()
|
||||
}
|
||||
else:
|
||||
own_annotations = {
|
||||
n: typing._type_check(tp, msg)
|
||||
for n, tp in own_annotations.items()
|
||||
}
|
||||
required_keys = set()
|
||||
optional_keys = set()
|
||||
|
||||
@@ -1052,17 +1066,25 @@ else:
|
||||
tp_dict.__total__ = total
|
||||
return tp_dict
|
||||
|
||||
__instancecheck__ = __subclasscheck__ = _check_fails
|
||||
__call__ = dict # static method
|
||||
|
||||
TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
|
||||
TypedDict.__module__ = __name__
|
||||
TypedDict.__doc__ = \
|
||||
"""A simple typed name space. At runtime it is equivalent to a plain dict.
|
||||
def __subclasscheck__(cls, other):
|
||||
# Typed dicts are only for static structural subtyping.
|
||||
raise TypeError('TypedDict does not support instance and class checks')
|
||||
|
||||
TypedDict creates a dictionary type that expects all of its
|
||||
instances to have a certain set of keys, with each key
|
||||
__instancecheck__ = __subclasscheck__
|
||||
|
||||
_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
|
||||
|
||||
@_ensure_subclassable(lambda bases: (_TypedDict,))
|
||||
def TypedDict(__typename, __fields=_marker, *, total=True, **kwargs):
|
||||
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
|
||||
|
||||
TypedDict creates a dictionary type such that a type checker will expect all
|
||||
instances to have a certain set of keys, where each key is
|
||||
associated with a value of a consistent type. This expectation
|
||||
is not checked at runtime but is only enforced by type checkers.
|
||||
is not checked at runtime.
|
||||
|
||||
Usage::
|
||||
|
||||
class Point2D(TypedDict):
|
||||
@@ -1077,14 +1099,66 @@ else:
|
||||
|
||||
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::
|
||||
TypedDict supports an additional equivalent form::
|
||||
|
||||
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+
|
||||
By default, all keys must be present in a TypedDict. It is possible
|
||||
to override this by specifying totality::
|
||||
|
||||
class Point2D(TypedDict, total=False):
|
||||
x: int
|
||||
y: int
|
||||
|
||||
This means that a Point2D TypedDict can have any of the keys omitted. A type
|
||||
checker is only expected to support a literal False or True as the value of
|
||||
the total argument. True is the default, and makes all items defined in the
|
||||
class body be required.
|
||||
|
||||
The Required and NotRequired special forms can also be used to mark
|
||||
individual keys as being required or not required::
|
||||
|
||||
class Point2D(TypedDict):
|
||||
x: int # the "x" key must always be present (Required is the default)
|
||||
y: NotRequired[int] # the "y" key can be omitted
|
||||
|
||||
See PEP 655 for more details on Required and NotRequired.
|
||||
"""
|
||||
if __fields is _marker or __fields is None:
|
||||
if __fields is _marker:
|
||||
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
|
||||
else:
|
||||
deprecated_thing = "Passing `None` as the 'fields' parameter"
|
||||
|
||||
example = f"`{__typename} = TypedDict({__typename!r}, {{}})`"
|
||||
deprecation_msg = (
|
||||
f"{deprecated_thing} is deprecated and will be disallowed in "
|
||||
"Python 3.15. To create a TypedDict class with 0 fields "
|
||||
"using the functional syntax, pass an empty dictionary, e.g. "
|
||||
) + example + "."
|
||||
warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
|
||||
__fields = kwargs
|
||||
elif kwargs:
|
||||
raise TypeError("TypedDict takes either a dict or keyword arguments,"
|
||||
" but not both")
|
||||
if kwargs:
|
||||
warnings.warn(
|
||||
"The kwargs-based syntax for TypedDict definitions is deprecated "
|
||||
"in Python 3.11, will be removed in Python 3.13, and may not be "
|
||||
"understood by third-party type checkers.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
ns = {'__annotations__': dict(__fields)}
|
||||
module = _caller()
|
||||
if module is not None:
|
||||
# Setting correct module is necessary to make typed dict classes pickleable.
|
||||
ns['__module__'] = module
|
||||
|
||||
td = _TypedDictMeta(__typename, (), ns, total=total)
|
||||
td.__orig_bases__ = (TypedDict,)
|
||||
return td
|
||||
|
||||
if hasattr(typing, "_TypedDictMeta"):
|
||||
_TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
|
||||
@@ -1102,7 +1176,10 @@ else:
|
||||
is_typeddict(Film) # => True
|
||||
is_typeddict(Union[list, str]) # => False
|
||||
"""
|
||||
return isinstance(tp, tuple(_TYPEDDICT_TYPES))
|
||||
# On 3.8, this would otherwise return True
|
||||
if hasattr(typing, "TypedDict") and tp is typing.TypedDict:
|
||||
return False
|
||||
return isinstance(tp, _TYPEDDICT_TYPES)
|
||||
|
||||
|
||||
if hasattr(typing, "assert_type"):
|
||||
@@ -1372,11 +1449,7 @@ 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
|
||||
@_ExtensionsSpecialForm
|
||||
def TypeAlias(self, parameters):
|
||||
"""Special marker indicating that an assignment should
|
||||
be recognized as a proper type alias definition by type
|
||||
@@ -1391,21 +1464,19 @@ elif sys.version_info[:2] >= (3, 9):
|
||||
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 = _ExtensionsSpecialForm(
|
||||
'TypeAlias',
|
||||
doc="""Special marker indicating that an assignment should
|
||||
be recognized as a proper type alias definition by type
|
||||
checkers.
|
||||
|
||||
TypeAlias = _TypeAliasForm('TypeAlias',
|
||||
doc="""Special marker indicating that an assignment should
|
||||
be recognized as a proper type alias definition by type
|
||||
checkers.
|
||||
For example::
|
||||
|
||||
For example::
|
||||
Predicate: TypeAlias = Callable[..., bool]
|
||||
|
||||
Predicate: TypeAlias = Callable[..., bool]
|
||||
|
||||
It's invalid when used anywhere except as in the example
|
||||
above.""")
|
||||
It's invalid when used anywhere except as in the example
|
||||
above."""
|
||||
)
|
||||
|
||||
|
||||
def _set_default(type_param, default):
|
||||
@@ -1720,7 +1791,7 @@ if hasattr(typing, 'Concatenate'):
|
||||
_ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa: F811
|
||||
# 3.9
|
||||
elif sys.version_info[:2] >= (3, 9):
|
||||
@_TypeAliasForm
|
||||
@_ExtensionsSpecialForm
|
||||
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
|
||||
@@ -1735,10 +1806,7 @@ elif sys.version_info[:2] >= (3, 9):
|
||||
return _concatenate_getitem(self, parameters)
|
||||
# 3.7-8
|
||||
else:
|
||||
class _ConcatenateForm(typing._SpecialForm, _root=True):
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
class _ConcatenateForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __getitem__(self, parameters):
|
||||
return _concatenate_getitem(self, parameters)
|
||||
|
||||
@@ -1760,11 +1828,7 @@ 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
|
||||
@_ExtensionsSpecialForm
|
||||
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.
|
||||
@@ -1812,11 +1876,7 @@ elif sys.version_info[:2] >= (3, 9):
|
||||
return typing._GenericAlias(self, (item,))
|
||||
# 3.7-3.8
|
||||
else:
|
||||
class _TypeGuardForm(typing._SpecialForm, _root=True):
|
||||
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
class _TypeGuardForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __getitem__(self, parameters):
|
||||
item = typing._type_check(parameters,
|
||||
f'{self._name} accepts only a single type')
|
||||
@@ -1921,7 +1981,7 @@ else:
|
||||
|
||||
Example::
|
||||
|
||||
from typing_extensions import LiteralString
|
||||
from pipenv.patched.pip._vendor.typing_extensions import LiteralString
|
||||
|
||||
def query(sql: LiteralString) -> ...:
|
||||
...
|
||||
@@ -1966,7 +2026,7 @@ else:
|
||||
This can be used to define a function that should never be
|
||||
called, or a function that never returns::
|
||||
|
||||
from typing_extensions import Never
|
||||
from pipenv.patched.pip._vendor.typing_extensions import Never
|
||||
|
||||
def never_call_me(arg: Never) -> None:
|
||||
pass
|
||||
@@ -1990,10 +2050,6 @@ 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
|
||||
@@ -2032,10 +2088,7 @@ elif sys.version_info[:2] >= (3, 9):
|
||||
return typing._GenericAlias(self, (item,))
|
||||
|
||||
else:
|
||||
class _RequiredForm(typing._SpecialForm, _root=True):
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
class _RequiredForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __getitem__(self, parameters):
|
||||
item = typing._type_check(parameters,
|
||||
f'{self._name} accepts only a single type.')
|
||||
@@ -2123,14 +2176,11 @@ if sys.version_info >= (3, 12): # PEP 692 changed the repr of Unpack[]
|
||||
return get_origin(obj) is Unpack
|
||||
|
||||
elif sys.version_info[:2] >= (3, 9):
|
||||
class _UnpackSpecialForm(typing._SpecialForm, _root=True):
|
||||
class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __init__(self, getitem):
|
||||
super().__init__(getitem)
|
||||
self.__doc__ = _UNPACK_DOC
|
||||
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
class _UnpackAlias(typing._GenericAlias, _root=True):
|
||||
__class__ = typing.TypeVar
|
||||
|
||||
@@ -2146,10 +2196,7 @@ else:
|
||||
class _UnpackAlias(typing._GenericAlias, _root=True):
|
||||
__class__ = typing.TypeVar
|
||||
|
||||
class _UnpackForm(typing._SpecialForm, _root=True):
|
||||
def __repr__(self):
|
||||
return 'typing_extensions.' + self._name
|
||||
|
||||
class _UnpackForm(_ExtensionsSpecialForm, _root=True):
|
||||
def __getitem__(self, parameters):
|
||||
item = typing._type_check(parameters,
|
||||
f'{self._name} accepts only a single type.')
|
||||
@@ -2327,7 +2374,7 @@ else:
|
||||
|
||||
Example:
|
||||
|
||||
from typing_extensions import dataclass_transform
|
||||
from pipenv.patched.pip._vendor.typing_extensions import dataclass_transform
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
@@ -2541,7 +2588,8 @@ if not hasattr(typing, "TypeVarTuple"):
|
||||
# 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.
|
||||
# On 3.12, we added __orig_bases__ to call-based NamedTuples
|
||||
if sys.version_info >= (3, 12):
|
||||
# On 3.13, we deprecated kwargs-based NamedTuples
|
||||
if sys.version_info >= (3, 13):
|
||||
NamedTuple = typing.NamedTuple
|
||||
else:
|
||||
def _make_nmtuple(name, types, module, defaults=()):
|
||||
@@ -2585,8 +2633,11 @@ else:
|
||||
)
|
||||
nm_tpl.__bases__ = bases
|
||||
if typing.Generic in bases:
|
||||
class_getitem = typing.Generic.__class_getitem__.__func__
|
||||
nm_tpl.__class_getitem__ = classmethod(class_getitem)
|
||||
if hasattr(typing, '_generic_class_getitem'): # 3.12+
|
||||
nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem)
|
||||
else:
|
||||
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:
|
||||
@@ -2597,30 +2648,87 @@ else:
|
||||
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")
|
||||
nt = _make_nmtuple(__typename, __fields, module=_caller())
|
||||
nt.__orig_bases__ = (NamedTuple,)
|
||||
return nt
|
||||
|
||||
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
|
||||
@_ensure_subclassable(_namedtuple_mro_entries)
|
||||
def NamedTuple(__typename, __fields=_marker, **kwargs):
|
||||
"""Typed version of namedtuple.
|
||||
|
||||
Usage::
|
||||
|
||||
class Employee(NamedTuple):
|
||||
name: str
|
||||
id: int
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||
|
||||
The resulting class has an extra __annotations__ attribute, giving a
|
||||
dict that maps field names to types. (The field names are also in
|
||||
the _fields attribute, which is part of the namedtuple API.)
|
||||
An alternative equivalent functional syntax is also accepted::
|
||||
|
||||
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
||||
"""
|
||||
if __fields is _marker:
|
||||
if kwargs:
|
||||
deprecated_thing = "Creating NamedTuple classes using keyword arguments"
|
||||
deprecation_msg = (
|
||||
"{name} is deprecated and will be disallowed in Python {remove}. "
|
||||
"Use the class-based or functional syntax instead."
|
||||
)
|
||||
else:
|
||||
deprecated_thing = "Failing to pass a value for the 'fields' parameter"
|
||||
example = f"`{__typename} = NamedTuple({__typename!r}, [])`"
|
||||
deprecation_msg = (
|
||||
"{name} is deprecated and will be disallowed in Python {remove}. "
|
||||
"To create a NamedTuple class with 0 fields "
|
||||
"using the functional syntax, "
|
||||
"pass an empty list, e.g. "
|
||||
) + example + "."
|
||||
elif __fields is None:
|
||||
if kwargs:
|
||||
raise TypeError(
|
||||
"Cannot pass `None` as the 'fields' parameter "
|
||||
"and also specify fields using keyword arguments"
|
||||
)
|
||||
else:
|
||||
deprecated_thing = "Passing `None` as the 'fields' parameter"
|
||||
example = f"`{__typename} = NamedTuple({__typename!r}, [])`"
|
||||
deprecation_msg = (
|
||||
"{name} is deprecated and will be disallowed in Python {remove}. "
|
||||
"To create a NamedTuple class with 0 fields "
|
||||
"using the functional syntax, "
|
||||
"pass an empty list, e.g. "
|
||||
) + example + "."
|
||||
elif kwargs:
|
||||
raise TypeError("Either list of fields or keywords"
|
||||
" can be provided to NamedTuple, not both")
|
||||
if __fields is _marker or __fields is None:
|
||||
warnings.warn(
|
||||
deprecation_msg.format(name=deprecated_thing, remove="3.15"),
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
__fields = kwargs.items()
|
||||
nt = _make_nmtuple(__typename, __fields, module=_caller())
|
||||
nt.__orig_bases__ = (NamedTuple,)
|
||||
return nt
|
||||
|
||||
# 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):
|
||||
_new_signature = '(typename, fields=None, /, **kwargs)'
|
||||
if isinstance(NamedTuple, _types.FunctionType):
|
||||
NamedTuple.__text_signature__ = _new_signature
|
||||
else:
|
||||
NamedTuple.__call__.__text_signature__ = _new_signature
|
||||
|
||||
|
||||
if hasattr(collections.abc, "Buffer"):
|
||||
@@ -2662,7 +2770,7 @@ else:
|
||||
Examples::
|
||||
|
||||
from typing import TypeVar, Generic
|
||||
from typing_extensions import NamedTuple, TypedDict
|
||||
from pipenv.patched.pip._vendor.typing_extensions import NamedTuple, TypedDict
|
||||
|
||||
T = TypeVar("T")
|
||||
class Foo(Generic[T]): ...
|
||||
@@ -2873,3 +2981,92 @@ else:
|
||||
if not _is_unionable(left):
|
||||
return NotImplemented
|
||||
return typing.Union[left, self]
|
||||
|
||||
|
||||
if hasattr(typing, "is_protocol"):
|
||||
is_protocol = typing.is_protocol
|
||||
get_protocol_members = typing.get_protocol_members
|
||||
else:
|
||||
def is_protocol(__tp: type) -> bool:
|
||||
"""Return True if the given type is a Protocol.
|
||||
|
||||
Example::
|
||||
|
||||
>>> from pipenv.patched.pip._vendor.typing_extensions import Protocol, is_protocol
|
||||
>>> class P(Protocol):
|
||||
... def a(self) -> str: ...
|
||||
... b: int
|
||||
>>> is_protocol(P)
|
||||
True
|
||||
>>> is_protocol(int)
|
||||
False
|
||||
"""
|
||||
return (
|
||||
isinstance(__tp, type)
|
||||
and getattr(__tp, '_is_protocol', False)
|
||||
and __tp is not Protocol
|
||||
and __tp is not getattr(typing, "Protocol", object())
|
||||
)
|
||||
|
||||
def get_protocol_members(__tp: type) -> typing.FrozenSet[str]:
|
||||
"""Return the set of members defined in a Protocol.
|
||||
|
||||
Example::
|
||||
|
||||
>>> from pipenv.patched.pip._vendor.typing_extensions import Protocol, get_protocol_members
|
||||
>>> class P(Protocol):
|
||||
... def a(self) -> str: ...
|
||||
... b: int
|
||||
>>> get_protocol_members(P)
|
||||
frozenset({'a', 'b'})
|
||||
|
||||
Raise a TypeError for arguments that are not Protocols.
|
||||
"""
|
||||
if not is_protocol(__tp):
|
||||
raise TypeError(f'{__tp!r} is not a Protocol')
|
||||
if hasattr(__tp, '__protocol_attrs__'):
|
||||
return frozenset(__tp.__protocol_attrs__)
|
||||
return frozenset(_get_protocol_attrs(__tp))
|
||||
|
||||
|
||||
# Aliases for items that have always been in typing.
|
||||
# Explicitly assign these (rather than using `from typing import *` at the top),
|
||||
# so that we get a CI error if one of these is deleted from typing.py
|
||||
# in a future version of Python
|
||||
AbstractSet = typing.AbstractSet
|
||||
AnyStr = typing.AnyStr
|
||||
BinaryIO = typing.BinaryIO
|
||||
Callable = typing.Callable
|
||||
Collection = typing.Collection
|
||||
Container = typing.Container
|
||||
Dict = typing.Dict
|
||||
ForwardRef = typing.ForwardRef
|
||||
FrozenSet = typing.FrozenSet
|
||||
Generator = typing.Generator
|
||||
Generic = typing.Generic
|
||||
Hashable = typing.Hashable
|
||||
IO = typing.IO
|
||||
ItemsView = typing.ItemsView
|
||||
Iterable = typing.Iterable
|
||||
Iterator = typing.Iterator
|
||||
KeysView = typing.KeysView
|
||||
List = typing.List
|
||||
Mapping = typing.Mapping
|
||||
MappingView = typing.MappingView
|
||||
Match = typing.Match
|
||||
MutableMapping = typing.MutableMapping
|
||||
MutableSequence = typing.MutableSequence
|
||||
MutableSet = typing.MutableSet
|
||||
Optional = typing.Optional
|
||||
Pattern = typing.Pattern
|
||||
Reversible = typing.Reversible
|
||||
Sequence = typing.Sequence
|
||||
Set = typing.Set
|
||||
Sized = typing.Sized
|
||||
TextIO = typing.TextIO
|
||||
Tuple = typing.Tuple
|
||||
Union = typing.Union
|
||||
ValuesView = typing.ValuesView
|
||||
cast = typing.cast
|
||||
no_type_check = typing.no_type_check
|
||||
no_type_check_decorator = typing.no_type_check_decorator
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# This file is protected via CODEOWNERS
|
||||
__version__ = "1.26.15"
|
||||
__version__ = "1.26.16"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user