mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
vendor in Pip==23.1
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
pip==23.0.1
|
||||
pip==23.1
|
||||
safety==2.3.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import List, Optional
|
||||
|
||||
__version__ = "23.0.1"
|
||||
__version__ = "23.1"
|
||||
|
||||
|
||||
def main(args: Optional[List[str]] = None) -> int:
|
||||
|
||||
@@ -6,14 +6,13 @@ import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pipenv.patched.pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
|
||||
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
|
||||
|
||||
from pipenv.patched.pip._internal.exceptions import InvalidWheelFilename
|
||||
from pipenv.patched.pip._internal.models.direct_url import DirectUrl
|
||||
from pipenv.patched.pip._internal.models.format_control import FormatControl
|
||||
from pipenv.patched.pip._internal.models.link import Link
|
||||
from pipenv.patched.pip._internal.models.wheel import Wheel
|
||||
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
|
||||
@@ -33,25 +32,13 @@ def _hash_dict(d: Dict[str, str]) -> str:
|
||||
class Cache:
|
||||
"""An abstract class - provides cache directories for data from links
|
||||
|
||||
|
||||
:param cache_dir: The root of the cache.
|
||||
:param format_control: An object of FormatControl class to limit
|
||||
binaries being read from the cache.
|
||||
:param allowed_formats: which formats of files the cache should store.
|
||||
('binary' and 'source' are the only allowed values)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, cache_dir: str, format_control: FormatControl, allowed_formats: Set[str]
|
||||
) -> None:
|
||||
def __init__(self, cache_dir: str) -> None:
|
||||
super().__init__()
|
||||
assert not cache_dir or os.path.isabs(cache_dir)
|
||||
self.cache_dir = cache_dir or None
|
||||
self.format_control = format_control
|
||||
self.allowed_formats = allowed_formats
|
||||
|
||||
_valid_formats = {"source", "binary"}
|
||||
assert self.allowed_formats.union(_valid_formats) == _valid_formats
|
||||
|
||||
def _get_cache_path_parts(self, link: Link) -> List[str]:
|
||||
"""Get parts of part that must be os.path.joined with cache_dir"""
|
||||
@@ -91,10 +78,6 @@ class Cache:
|
||||
if can_not_cache:
|
||||
return []
|
||||
|
||||
formats = self.format_control.get_allowed_formats(canonical_package_name)
|
||||
if not self.allowed_formats.intersection(formats):
|
||||
return []
|
||||
|
||||
candidates = []
|
||||
path = self.get_path_for_link(link)
|
||||
if os.path.isdir(path):
|
||||
@@ -121,8 +104,8 @@ class Cache:
|
||||
class SimpleWheelCache(Cache):
|
||||
"""A cache of wheels for future installs."""
|
||||
|
||||
def __init__(self, cache_dir: str, format_control: FormatControl) -> None:
|
||||
super().__init__(cache_dir, format_control, {"binary"})
|
||||
def __init__(self, cache_dir: str) -> None:
|
||||
super().__init__(cache_dir)
|
||||
|
||||
def get_path_for_link(self, link: Link) -> str:
|
||||
"""Return a directory to store cached wheels for link
|
||||
@@ -191,13 +174,13 @@ class SimpleWheelCache(Cache):
|
||||
class EphemWheelCache(SimpleWheelCache):
|
||||
"""A SimpleWheelCache that creates it's own temporary cache directory"""
|
||||
|
||||
def __init__(self, format_control: FormatControl) -> None:
|
||||
def __init__(self) -> None:
|
||||
self._temp_dir = TempDirectory(
|
||||
kind=tempdir_kinds.EPHEM_WHEEL_CACHE,
|
||||
globally_managed=True,
|
||||
)
|
||||
|
||||
super().__init__(self._temp_dir.path, format_control)
|
||||
super().__init__(self._temp_dir.path)
|
||||
|
||||
|
||||
class CacheEntry:
|
||||
@@ -221,14 +204,10 @@ class WheelCache(Cache):
|
||||
when a certain link is not found in the simple wheel cache first.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, cache_dir: str, format_control: Optional[FormatControl] = None
|
||||
) -> None:
|
||||
if format_control is None:
|
||||
format_control = FormatControl()
|
||||
super().__init__(cache_dir, format_control, {"binary"})
|
||||
self._wheel_cache = SimpleWheelCache(cache_dir, format_control)
|
||||
self._ephem_cache = EphemWheelCache(format_control)
|
||||
def __init__(self, cache_dir: str) -> None:
|
||||
super().__init__(cache_dir)
|
||||
self._wheel_cache = SimpleWheelCache(cache_dir)
|
||||
self._ephem_cache = EphemWheelCache()
|
||||
|
||||
def get_path_for_link(self, link: Link) -> str:
|
||||
return self._wheel_cache.get_path_for_link(link)
|
||||
|
||||
@@ -122,6 +122,15 @@ class Command(CommandContextMixIn):
|
||||
user_log_file=options.log,
|
||||
)
|
||||
|
||||
always_enabled_features = set(options.features_enabled) & set(
|
||||
cmdoptions.ALWAYS_ENABLED_FEATURES
|
||||
)
|
||||
if always_enabled_features:
|
||||
logger.warning(
|
||||
"The following features are always enabled: %s. ",
|
||||
", ".join(sorted(always_enabled_features)),
|
||||
)
|
||||
|
||||
# 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.
|
||||
|
||||
@@ -252,6 +252,19 @@ no_input: Callable[..., Option] = partial(
|
||||
help="Disable prompting for input.",
|
||||
)
|
||||
|
||||
keyring_provider: Callable[..., Option] = partial(
|
||||
Option,
|
||||
"--keyring-provider",
|
||||
dest="keyring_provider",
|
||||
choices=["auto", "disabled", "import", "subprocess"],
|
||||
default="auto",
|
||||
help=(
|
||||
"Enable the credential lookup via the keyring library if user input is allowed."
|
||||
" Specify which mechanism to use [disabled, import, subprocess]."
|
||||
" (default: disabled)"
|
||||
),
|
||||
)
|
||||
|
||||
proxy: Callable[..., Option] = partial(
|
||||
Option,
|
||||
"--proxy",
|
||||
@@ -770,10 +783,14 @@ def _handle_no_use_pep517(
|
||||
"""
|
||||
raise_option_error(parser, option=option, msg=msg)
|
||||
|
||||
# If user doesn't wish to use pep517, we check if setuptools is installed
|
||||
# If user doesn't wish to use pep517, we check if setuptools and wheel are installed
|
||||
# and raise error if it is not.
|
||||
if not importlib.util.find_spec("setuptools"):
|
||||
msg = "It is not possible to use --no-use-pep517 without setuptools installed."
|
||||
packages = ("setuptools", "wheel")
|
||||
if not all(importlib.util.find_spec(package) for package in packages):
|
||||
msg = (
|
||||
f"It is not possible to use --no-use-pep517 "
|
||||
f"without {' and '.join(packages)} installed."
|
||||
)
|
||||
raise_option_error(parser, option=option, msg=msg)
|
||||
|
||||
# Otherwise, --no-use-pep517 was passed via the command-line.
|
||||
@@ -811,11 +828,18 @@ def _handle_config_settings(
|
||||
if dest is None:
|
||||
dest = {}
|
||||
setattr(parser.values, option.dest, dest)
|
||||
dest[key] = val
|
||||
if key in dest:
|
||||
if isinstance(dest[key], list):
|
||||
dest[key].append(val)
|
||||
else:
|
||||
dest[key] = [dest[key], val]
|
||||
else:
|
||||
dest[key] = val
|
||||
|
||||
|
||||
config_settings: Callable[..., Option] = partial(
|
||||
Option,
|
||||
"-C",
|
||||
"--config-settings",
|
||||
dest="config_settings",
|
||||
type=str,
|
||||
@@ -827,17 +851,6 @@ config_settings: Callable[..., Option] = partial(
|
||||
"to pass multiple keys to the backend.",
|
||||
)
|
||||
|
||||
install_options: Callable[..., Option] = partial(
|
||||
Option,
|
||||
"--install-option",
|
||||
dest="install_options",
|
||||
action="append",
|
||||
metavar="options",
|
||||
help="This option is deprecated. Using this option with location-changing "
|
||||
"options may cause unexpected behavior. "
|
||||
"Use pip-level options like --user, --prefix, --root, and --target.",
|
||||
)
|
||||
|
||||
build_options: Callable[..., Option] = partial(
|
||||
Option,
|
||||
"--build-option",
|
||||
@@ -981,6 +994,11 @@ no_python_version_warning: Callable[..., Option] = partial(
|
||||
)
|
||||
|
||||
|
||||
# Features that are now always on. A warning is printed if they are used.
|
||||
ALWAYS_ENABLED_FEATURES = [
|
||||
"no-binary-enable-wheel-cache", # always on since 23.1
|
||||
]
|
||||
|
||||
use_new_feature: Callable[..., Option] = partial(
|
||||
Option,
|
||||
"--use-feature",
|
||||
@@ -991,8 +1009,8 @@ use_new_feature: Callable[..., Option] = partial(
|
||||
choices=[
|
||||
"fast-deps",
|
||||
"truststore",
|
||||
"no-binary-enable-wheel-cache",
|
||||
],
|
||||
]
|
||||
+ ALWAYS_ENABLED_FEATURES,
|
||||
help="Enable new functionality, that may be backward incompatible.",
|
||||
)
|
||||
|
||||
@@ -1027,6 +1045,7 @@ general_group: Dict[str, Any] = {
|
||||
quiet,
|
||||
log,
|
||||
no_input,
|
||||
keyring_provider,
|
||||
proxy,
|
||||
retries,
|
||||
timeout,
|
||||
|
||||
@@ -151,6 +151,7 @@ class SessionCommandMixin(CommandContextMixIn):
|
||||
|
||||
# Determine if we can prompt the user for authentication or not
|
||||
session.auth.prompting = not options.no_input
|
||||
session.auth.keyring_provider = options.keyring_provider
|
||||
|
||||
return session
|
||||
|
||||
@@ -343,7 +344,6 @@ class RequirementCommand(IndexGroupCommand):
|
||||
install_req_from_req_string,
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=use_pep517,
|
||||
config_settings=getattr(options, "config_settings", None),
|
||||
)
|
||||
resolver_variant = cls.determine_resolver_variant(options)
|
||||
# The long import name and duplicated invocation is needed to convince
|
||||
@@ -410,7 +410,7 @@ class RequirementCommand(IndexGroupCommand):
|
||||
for req in args:
|
||||
req_to_add = install_req_from_line(
|
||||
req,
|
||||
None,
|
||||
comes_from=None,
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=options.use_pep517,
|
||||
user_supplied=True,
|
||||
@@ -438,6 +438,9 @@ class RequirementCommand(IndexGroupCommand):
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=options.use_pep517,
|
||||
user_supplied=True,
|
||||
config_settings=parsed_req.options.get("config_settings")
|
||||
if parsed_req.options
|
||||
else None,
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ class CacheCommand(Command):
|
||||
"""
|
||||
|
||||
def add_options(self) -> None:
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
"--format",
|
||||
action="store",
|
||||
|
||||
@@ -20,7 +20,6 @@ class CheckCommand(Command):
|
||||
%prog [options]"""
|
||||
|
||||
def run(self, options: Values, args: List[str]) -> int:
|
||||
|
||||
package_set, parsing_probs = create_package_set_from_installed()
|
||||
missing, conflicting = check_package_set(package_set)
|
||||
|
||||
|
||||
@@ -8,10 +8,7 @@ from pipenv.patched.pip._internal.cli.cmdoptions import make_target_python
|
||||
from pipenv.patched.pip._internal.cli.req_command import RequirementCommand, with_cleanup
|
||||
from pipenv.patched.pip._internal.cli.status_codes import SUCCESS
|
||||
from pipenv.patched.pip._internal.operations.build.build_tracker import get_build_tracker
|
||||
from pipenv.patched.pip._internal.req.req_install import (
|
||||
LegacySetupPyOptionsCheckMode,
|
||||
check_legacy_setup_py_options,
|
||||
)
|
||||
from pipenv.patched.pip._internal.req.req_install import check_legacy_setup_py_options
|
||||
from pipenv.patched.pip._internal.utils.misc import ensure_dir, normalize_path, write_output
|
||||
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory
|
||||
|
||||
@@ -79,7 +76,6 @@ class DownloadCommand(RequirementCommand):
|
||||
|
||||
@with_cleanup
|
||||
def run(self, options: Values, args: List[str]) -> int:
|
||||
|
||||
options.ignore_installed = True
|
||||
# editable doesn't really make sense for `pip download`, but the bowels
|
||||
# of the RequirementSet code require that property.
|
||||
@@ -109,9 +105,7 @@ class DownloadCommand(RequirementCommand):
|
||||
)
|
||||
|
||||
reqs = self.get_requirements(args, options, finder, session)
|
||||
check_legacy_setup_py_options(
|
||||
options, reqs, LegacySetupPyOptionsCheckMode.DOWNLOAD
|
||||
)
|
||||
check_legacy_setup_py_options(options, reqs)
|
||||
|
||||
preparer = self.make_requirement_preparer(
|
||||
temp_build_dir=directory,
|
||||
|
||||
@@ -5,9 +5,8 @@ import os
|
||||
import shutil
|
||||
import site
|
||||
from optparse import SUPPRESS_HELP, Values
|
||||
from typing import Iterable, List, Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
|
||||
from pipenv.patched.pip._vendor.rich import print_json
|
||||
|
||||
from pipenv.patched.pip._internal.cache import WheelCache
|
||||
@@ -22,22 +21,15 @@ from pipenv.patched.pip._internal.cli.status_codes import ERROR, SUCCESS
|
||||
from pipenv.patched.pip._internal.exceptions import CommandError, InstallationError
|
||||
from pipenv.patched.pip._internal.locations import get_scheme
|
||||
from pipenv.patched.pip._internal.metadata import get_environment
|
||||
from pipenv.patched.pip._internal.models.format_control import FormatControl
|
||||
from pipenv.patched.pip._internal.models.installation_report import InstallationReport
|
||||
from pipenv.patched.pip._internal.operations.build.build_tracker import get_build_tracker
|
||||
from pipenv.patched.pip._internal.operations.check import ConflictDetails, check_install_conflicts
|
||||
from pipenv.patched.pip._internal.req import install_given_reqs
|
||||
from pipenv.patched.pip._internal.req.req_install import (
|
||||
InstallRequirement,
|
||||
LegacySetupPyOptionsCheckMode,
|
||||
check_legacy_setup_py_options,
|
||||
)
|
||||
from pipenv.patched.pip._internal.utils.compat import WINDOWS
|
||||
from pipenv.patched.pip._internal.utils.deprecation import (
|
||||
LegacyInstallReasonFailedBdistWheel,
|
||||
deprecated,
|
||||
)
|
||||
from pipenv.patched.pip._internal.utils.distutils_args import parse_distutils_args
|
||||
from pipenv.patched.pip._internal.utils.filesystem import test_writable_dir
|
||||
from pipenv.patched.pip._internal.utils.logging import getLogger
|
||||
from pipenv.patched.pip._internal.utils.misc import (
|
||||
@@ -52,26 +44,11 @@ from pipenv.patched.pip._internal.utils.virtualenv import (
|
||||
running_under_virtualenv,
|
||||
virtualenv_no_global,
|
||||
)
|
||||
from pipenv.patched.pip._internal.wheel_builder import (
|
||||
BdistWheelAllowedPredicate,
|
||||
build,
|
||||
should_build_for_install_command,
|
||||
)
|
||||
from pipenv.patched.pip._internal.wheel_builder import build, should_build_for_install_command
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
def get_check_bdist_wheel_allowed(
|
||||
format_control: FormatControl,
|
||||
) -> BdistWheelAllowedPredicate:
|
||||
def check_binary_allowed(req: InstallRequirement) -> bool:
|
||||
canonical_name = canonicalize_name(req.name or "")
|
||||
allowed_formats = format_control.get_allowed_formats(canonical_name)
|
||||
return "binary" in allowed_formats
|
||||
|
||||
return check_binary_allowed
|
||||
|
||||
|
||||
class InstallCommand(RequirementCommand):
|
||||
"""
|
||||
Install packages from:
|
||||
@@ -156,7 +133,12 @@ class InstallCommand(RequirementCommand):
|
||||
default=None,
|
||||
help=(
|
||||
"Installation prefix where lib, bin and other top-level "
|
||||
"folders are placed"
|
||||
"folders are placed. Note that the resulting installation may "
|
||||
"contain scripts and other resources which reference the "
|
||||
"Python interpreter of pip, and not that of ``--prefix``. "
|
||||
"See also the ``--python`` option if the intention is to "
|
||||
"install packages into another (possibly pip-free) "
|
||||
"environment."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -218,7 +200,6 @@ class InstallCommand(RequirementCommand):
|
||||
self.cmd_opts.add_option(cmdoptions.override_externally_managed())
|
||||
|
||||
self.cmd_opts.add_option(cmdoptions.config_settings())
|
||||
self.cmd_opts.add_option(cmdoptions.install_options())
|
||||
self.cmd_opts.add_option(cmdoptions.global_options())
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
@@ -309,8 +290,6 @@ class InstallCommand(RequirementCommand):
|
||||
|
||||
cmdoptions.check_dist_restriction(options, check_target=True)
|
||||
|
||||
install_options = options.install_options or []
|
||||
|
||||
logger.verbose("Using %s", get_pip_version())
|
||||
options.use_user_site = decide_user_install(
|
||||
options.use_user_site,
|
||||
@@ -361,28 +340,9 @@ class InstallCommand(RequirementCommand):
|
||||
|
||||
try:
|
||||
reqs = self.get_requirements(args, options, finder, session)
|
||||
check_legacy_setup_py_options(
|
||||
options, reqs, LegacySetupPyOptionsCheckMode.INSTALL
|
||||
)
|
||||
check_legacy_setup_py_options(options, reqs)
|
||||
|
||||
if "no-binary-enable-wheel-cache" in options.features_enabled:
|
||||
# TODO: remove format_control from WheelCache when the deprecation cycle
|
||||
# is over
|
||||
wheel_cache = WheelCache(options.cache_dir)
|
||||
else:
|
||||
if options.format_control.no_binary:
|
||||
deprecated(
|
||||
reason=(
|
||||
"--no-binary currently disables reading from "
|
||||
"the cache of locally built wheels. In the future "
|
||||
"--no-binary will not influence the wheel cache."
|
||||
),
|
||||
replacement="to use the --no-cache-dir option",
|
||||
feature_flag="no-binary-enable-wheel-cache",
|
||||
issue=11453,
|
||||
gone_in="23.1",
|
||||
)
|
||||
wheel_cache = WheelCache(options.cache_dir, options.format_control)
|
||||
wheel_cache = WheelCache(options.cache_dir)
|
||||
|
||||
# Only when installing is it permitted to use PEP 660.
|
||||
# In other circumstances (pip wheel, pip download) we generate
|
||||
@@ -390,8 +350,6 @@ class InstallCommand(RequirementCommand):
|
||||
for req in reqs:
|
||||
req.permit_editable_wheels = True
|
||||
|
||||
reject_location_related_install_options(reqs, options.install_options)
|
||||
|
||||
preparer = self.make_requirement_preparer(
|
||||
temp_build_dir=directory,
|
||||
options=options,
|
||||
@@ -450,14 +408,10 @@ class InstallCommand(RequirementCommand):
|
||||
modifying_pip = pip_req.satisfied_by is None
|
||||
protect_pip_from_modification_on_windows(modifying_pip=modifying_pip)
|
||||
|
||||
check_bdist_wheel_allowed = get_check_bdist_wheel_allowed(
|
||||
finder.format_control
|
||||
)
|
||||
|
||||
reqs_to_build = [
|
||||
r
|
||||
for r in requirement_set.requirements.values()
|
||||
if should_build_for_install_command(r, check_bdist_wheel_allowed)
|
||||
if should_build_for_install_command(r)
|
||||
]
|
||||
|
||||
_, build_failures = build(
|
||||
@@ -468,26 +422,14 @@ class InstallCommand(RequirementCommand):
|
||||
global_options=global_options,
|
||||
)
|
||||
|
||||
# If we're using PEP 517, we cannot do a legacy setup.py install
|
||||
# so we fail here.
|
||||
pep517_build_failure_names: List[str] = [
|
||||
r.name for r in build_failures if r.use_pep517 # type: ignore
|
||||
]
|
||||
if pep517_build_failure_names:
|
||||
if build_failures:
|
||||
raise InstallationError(
|
||||
"Could not build wheels for {}, which is required to "
|
||||
"install pyproject.toml-based projects".format(
|
||||
", ".join(pep517_build_failure_names)
|
||||
", ".join(r.name for r in build_failures) # type: ignore
|
||||
)
|
||||
)
|
||||
|
||||
# For now, we just warn about failures building legacy
|
||||
# requirements, as we'll fall through to a setup.py install for
|
||||
# those.
|
||||
for r in build_failures:
|
||||
if not r.use_pep517:
|
||||
r.legacy_install_reason = LegacyInstallReasonFailedBdistWheel
|
||||
|
||||
to_install = resolver.get_installation_order(requirement_set)
|
||||
|
||||
# Check for conflicts in the package set we're installing.
|
||||
@@ -506,7 +448,6 @@ class InstallCommand(RequirementCommand):
|
||||
|
||||
installed = install_given_reqs(
|
||||
to_install,
|
||||
install_options,
|
||||
global_options,
|
||||
root=options.root_path,
|
||||
home=target_temp_dir_path,
|
||||
@@ -777,45 +718,6 @@ def decide_user_install(
|
||||
return True
|
||||
|
||||
|
||||
def reject_location_related_install_options(
|
||||
requirements: List[InstallRequirement], options: Optional[List[str]]
|
||||
) -> None:
|
||||
"""If any location-changing --install-option arguments were passed for
|
||||
requirements or on the command-line, then show a deprecation warning.
|
||||
"""
|
||||
|
||||
def format_options(option_names: Iterable[str]) -> List[str]:
|
||||
return ["--{}".format(name.replace("_", "-")) for name in option_names]
|
||||
|
||||
offenders = []
|
||||
|
||||
for requirement in requirements:
|
||||
install_options = requirement.install_options
|
||||
location_options = parse_distutils_args(install_options)
|
||||
if location_options:
|
||||
offenders.append(
|
||||
"{!r} from {}".format(
|
||||
format_options(location_options.keys()), requirement
|
||||
)
|
||||
)
|
||||
|
||||
if options:
|
||||
location_options = parse_distutils_args(options)
|
||||
if location_options:
|
||||
offenders.append(
|
||||
"{!r} from command line".format(format_options(location_options.keys()))
|
||||
)
|
||||
|
||||
if not offenders:
|
||||
return
|
||||
|
||||
raise CommandError(
|
||||
"Location-changing options found in --install-option: {}."
|
||||
" This is unsupported, use pip-level options like --user,"
|
||||
" --prefix, --root, and --target instead.".format("; ".join(offenders))
|
||||
)
|
||||
|
||||
|
||||
def create_os_error_message(
|
||||
error: OSError, show_traceback: bool, using_user_site: bool
|
||||
) -> str:
|
||||
|
||||
@@ -12,10 +12,8 @@ from pipenv.patched.pip._internal.exceptions import CommandError
|
||||
from pipenv.patched.pip._internal.operations.build.build_tracker import get_build_tracker
|
||||
from pipenv.patched.pip._internal.req.req_install import (
|
||||
InstallRequirement,
|
||||
LegacySetupPyOptionsCheckMode,
|
||||
check_legacy_setup_py_options,
|
||||
)
|
||||
from pipenv.patched.pip._internal.utils.deprecation import deprecated
|
||||
from pipenv.patched.pip._internal.utils.misc import ensure_dir, normalize_path
|
||||
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.pip._internal.wheel_builder import build, should_build_for_wheel_command
|
||||
@@ -44,7 +42,6 @@ class WheelCommand(RequirementCommand):
|
||||
%prog [options] <archive url/path> ..."""
|
||||
|
||||
def add_options(self) -> None:
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
"-w",
|
||||
"--wheel-dir",
|
||||
@@ -108,7 +105,6 @@ class WheelCommand(RequirementCommand):
|
||||
session = self.get_default_session(options)
|
||||
|
||||
finder = self._build_package_finder(options, session)
|
||||
wheel_cache = WheelCache(options.cache_dir, options.format_control)
|
||||
|
||||
options.wheel_dir = normalize_path(options.wheel_dir)
|
||||
ensure_dir(options.wheel_dir)
|
||||
@@ -122,28 +118,9 @@ class WheelCommand(RequirementCommand):
|
||||
)
|
||||
|
||||
reqs = self.get_requirements(args, options, finder, session)
|
||||
check_legacy_setup_py_options(
|
||||
options, reqs, LegacySetupPyOptionsCheckMode.WHEEL
|
||||
)
|
||||
check_legacy_setup_py_options(options, reqs)
|
||||
|
||||
if "no-binary-enable-wheel-cache" in options.features_enabled:
|
||||
# TODO: remove format_control from WheelCache when the deprecation cycle
|
||||
# is over
|
||||
wheel_cache = WheelCache(options.cache_dir)
|
||||
else:
|
||||
if options.format_control.no_binary:
|
||||
deprecated(
|
||||
reason=(
|
||||
"--no-binary currently disables reading from "
|
||||
"the cache of locally built wheels. In the future "
|
||||
"--no-binary will not influence the wheel cache."
|
||||
),
|
||||
replacement="to use the --no-cache-dir option",
|
||||
feature_flag="no-binary-enable-wheel-cache",
|
||||
issue=11453,
|
||||
gone_in="23.1",
|
||||
)
|
||||
wheel_cache = WheelCache(options.cache_dir, options.format_control)
|
||||
wheel_cache = WheelCache(options.cache_dir)
|
||||
|
||||
preparer = self.make_requirement_preparer(
|
||||
temp_build_dir=directory,
|
||||
|
||||
@@ -36,12 +36,20 @@ ENV_NAMES_IGNORED = "version", "help"
|
||||
kinds = enum(
|
||||
USER="user", # User Specific
|
||||
GLOBAL="global", # System Wide
|
||||
SITE="site", # [Virtual] Environment Specific
|
||||
BASE="base", # Base environment specific (e.g. for all venvs with the same base)
|
||||
SITE="site", # Environment Specific (e.g. per venv)
|
||||
ENV="env", # from PIP_CONFIG_FILE
|
||||
ENV_VAR="env-var", # from Environment Variables
|
||||
)
|
||||
OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR
|
||||
VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE
|
||||
OVERRIDE_ORDER = (
|
||||
kinds.GLOBAL,
|
||||
kinds.USER,
|
||||
kinds.BASE,
|
||||
kinds.SITE,
|
||||
kinds.ENV,
|
||||
kinds.ENV_VAR,
|
||||
)
|
||||
VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.BASE, kinds.SITE
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
@@ -70,6 +78,7 @@ def get_configuration_files() -> Dict[Kind, List[str]]:
|
||||
os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs("pip")
|
||||
]
|
||||
|
||||
base_config_file = os.path.join(sys.base_prefix, CONFIG_BASENAME)
|
||||
site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME)
|
||||
legacy_config_file = os.path.join(
|
||||
os.path.expanduser("~"),
|
||||
@@ -78,6 +87,7 @@ def get_configuration_files() -> Dict[Kind, List[str]]:
|
||||
)
|
||||
new_config_file = os.path.join(appdirs.user_config_dir("pip"), CONFIG_BASENAME)
|
||||
return {
|
||||
kinds.BASE: [base_config_file],
|
||||
kinds.GLOBAL: global_config_files,
|
||||
kinds.SITE: [site_config_file],
|
||||
kinds.USER: [legacy_config_file, new_config_file],
|
||||
@@ -344,6 +354,8 @@ class Configuration:
|
||||
# The legacy config file is overridden by the new config file
|
||||
yield kinds.USER, config_files[kinds.USER]
|
||||
|
||||
yield kinds.BASE, config_files[kinds.BASE]
|
||||
|
||||
# finally virtualenv configuration first trumping others
|
||||
yield kinds.SITE, config_files[kinds.SITE]
|
||||
|
||||
|
||||
@@ -361,20 +361,6 @@ class MetadataInconsistent(InstallationError):
|
||||
)
|
||||
|
||||
|
||||
class LegacyInstallFailure(DiagnosticPipError):
|
||||
"""Error occurred while executing `setup.py install`"""
|
||||
|
||||
reference = "legacy-install-failure"
|
||||
|
||||
def __init__(self, package_details: str) -> None:
|
||||
super().__init__(
|
||||
message="Encountered error while trying to install package.",
|
||||
context=package_details,
|
||||
hint_stmt="See above for output from the failure.",
|
||||
note_stmt="This is an issue with the package mentioned above, not pip.",
|
||||
)
|
||||
|
||||
|
||||
class InstallationSubprocessError(DiagnosticPipError, InstallationError):
|
||||
"""A subprocess call failed."""
|
||||
|
||||
|
||||
@@ -171,7 +171,6 @@ def build_source(
|
||||
expand_dir: bool,
|
||||
cache_link_parsing: bool,
|
||||
) -> Tuple[Optional[str], Optional[LinkSource]]:
|
||||
|
||||
path: Optional[str] = None
|
||||
url: Optional[str] = None
|
||||
if os.path.exists(location): # Is a local path.
|
||||
|
||||
@@ -105,22 +105,31 @@ class ArchiveInfo:
|
||||
hash: Optional[str] = None,
|
||||
hashes: Optional[Dict[str, str]] = None,
|
||||
) -> None:
|
||||
if hash is not None:
|
||||
# set hashes before hash, since the hash setter will further populate hashes
|
||||
self.hashes = hashes
|
||||
self.hash = hash
|
||||
|
||||
@property
|
||||
def hash(self) -> Optional[str]:
|
||||
return self._hash
|
||||
|
||||
@hash.setter
|
||||
def hash(self, value: Optional[str]) -> None:
|
||||
if value is not None:
|
||||
# Auto-populate the hashes key to upgrade to the new format automatically.
|
||||
# We don't back-populate the legacy hash key.
|
||||
# We don't back-populate the legacy hash key from hashes.
|
||||
try:
|
||||
hash_name, hash_value = hash.split("=", 1)
|
||||
hash_name, hash_value = value.split("=", 1)
|
||||
except ValueError:
|
||||
raise DirectUrlValidationError(
|
||||
f"invalid archive_info.hash format: {hash!r}"
|
||||
f"invalid archive_info.hash format: {value!r}"
|
||||
)
|
||||
if hashes is None:
|
||||
hashes = {hash_name: hash_value}
|
||||
elif hash_name not in hash:
|
||||
hashes = hashes.copy()
|
||||
hashes[hash_name] = hash_value
|
||||
self.hash = hash
|
||||
self.hashes = hashes
|
||||
if self.hashes is None:
|
||||
self.hashes = {hash_name: hash_value}
|
||||
elif hash_name not in self.hashes:
|
||||
self.hashes = self.hashes.copy()
|
||||
self.hashes[hash_name] = hash_value
|
||||
self._hash = value
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["ArchiveInfo"]:
|
||||
|
||||
@@ -14,7 +14,7 @@ class InstallationReport:
|
||||
def _install_req_to_dict(cls, ireq: InstallRequirement) -> Dict[str, Any]:
|
||||
assert ireq.download_info, f"No download_info for {ireq}"
|
||||
res = {
|
||||
# PEP 610 json for the download URL. download_info.archive_info.hash may
|
||||
# PEP 610 json for the download URL. download_info.archive_info.hashes may
|
||||
# be absent when the requirement was installed from the wheel cache
|
||||
# and the cache entry was populated by an older pip version that did not
|
||||
# record origin.json.
|
||||
|
||||
@@ -55,25 +55,37 @@ class LinkHash:
|
||||
name: str
|
||||
value: str
|
||||
|
||||
_hash_re = re.compile(
|
||||
_hash_url_fragment_re = re.compile(
|
||||
# NB: we do not validate that the second group (.*) is a valid hex
|
||||
# digest. Instead, we simply keep that string in this class, and then check it
|
||||
# against Hashes when hash-checking is needed. This is easier to debug than
|
||||
# proactively discarding an invalid hex digest, as we handle incorrect hashes
|
||||
# and malformed hashes in the same place.
|
||||
r"({choices})=(.*)".format(
|
||||
r"[#&]({choices})=([^&]*)".format(
|
||||
choices="|".join(re.escape(hash_name) for hash_name in _SUPPORTED_HASHES)
|
||||
),
|
||||
)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self._hash_re.match(f"{self.name}={self.value}")
|
||||
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 split_hash_name_and_value(cls, url: str) -> Optional["LinkHash"]:
|
||||
def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]:
|
||||
"""Search a string for a checksum algorithm name and encoded output value."""
|
||||
match = cls._hash_re.search(url)
|
||||
match = cls._hash_url_fragment_re.search(url)
|
||||
if match is None:
|
||||
return None
|
||||
name, value = match.groups()
|
||||
@@ -217,7 +229,7 @@ class Link(KeyBasedCompareMixin):
|
||||
# trying to set a new value.
|
||||
self._url = url
|
||||
|
||||
link_hash = LinkHash.split_hash_name_and_value(url)
|
||||
link_hash = LinkHash.find_hash_url_fragment(url)
|
||||
hashes_from_link = {} if link_hash is None else link_hash.as_dict()
|
||||
if hashes is None:
|
||||
self._hashes = hashes_from_link
|
||||
@@ -402,15 +414,10 @@ class Link(KeyBasedCompareMixin):
|
||||
if self.dist_info_metadata is None:
|
||||
return None
|
||||
metadata_url = f"{self.url_without_fragment}.metadata"
|
||||
# If data-dist-info-metadata="true" is set, then the metadata file exists,
|
||||
# but there is no information about its checksum or anything else.
|
||||
if self.dist_info_metadata != "true":
|
||||
link_hash = LinkHash.split_hash_name_and_value(self.dist_info_metadata)
|
||||
else:
|
||||
link_hash = None
|
||||
if link_hash is None:
|
||||
metadata_link_hash = LinkHash.parse_pep658_hash(self.dist_info_metadata)
|
||||
if metadata_link_hash is None:
|
||||
return Link(metadata_url)
|
||||
return Link(metadata_url, hashes=link_hash.as_dict())
|
||||
return Link(metadata_url, hashes=metadata_link_hash.as_dict())
|
||||
|
||||
def as_hashes(self) -> Hashes:
|
||||
return Hashes({k: [v] for k, v in self._hashes.items()})
|
||||
|
||||
@@ -83,7 +83,6 @@ class SearchScope:
|
||||
redacted_index_urls = []
|
||||
if self.index_urls and self.index_urls != [PyPI.simple_url]:
|
||||
for url in self.index_urls:
|
||||
|
||||
redacted_index_url = redact_auth_from_url(url)
|
||||
|
||||
# Parse the URL
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
Contains interface (MultiDomainBasicAuth) and associated glue code for
|
||||
providing credentials in the context of network requests.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sysconfig
|
||||
import typing
|
||||
import urllib.parse
|
||||
from abc import ABC, abstractmethod
|
||||
from functools import lru_cache
|
||||
from os.path import commonprefix
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, NamedTuple, Optional, Tuple
|
||||
|
||||
from pipenv.patched.pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
|
||||
@@ -39,6 +44,8 @@ class Credentials(NamedTuple):
|
||||
class KeyRingBaseProvider(ABC):
|
||||
"""Keyring base provider interface"""
|
||||
|
||||
has_keyring: bool
|
||||
|
||||
@abstractmethod
|
||||
def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]:
|
||||
...
|
||||
@@ -51,6 +58,8 @@ class KeyRingBaseProvider(ABC):
|
||||
class KeyRingNullProvider(KeyRingBaseProvider):
|
||||
"""Keyring null provider"""
|
||||
|
||||
has_keyring = False
|
||||
|
||||
def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]:
|
||||
return None
|
||||
|
||||
@@ -61,6 +70,8 @@ class KeyRingNullProvider(KeyRingBaseProvider):
|
||||
class KeyRingPythonProvider(KeyRingBaseProvider):
|
||||
"""Keyring interface which uses locally imported `keyring`"""
|
||||
|
||||
has_keyring = True
|
||||
|
||||
def __init__(self) -> None:
|
||||
import keyring
|
||||
|
||||
@@ -97,6 +108,8 @@ class KeyRingCliProvider(KeyRingBaseProvider):
|
||||
PATH.
|
||||
"""
|
||||
|
||||
has_keyring = True
|
||||
|
||||
def __init__(self, cmd: str) -> None:
|
||||
self.keyring = cmd
|
||||
|
||||
@@ -123,7 +136,7 @@ class KeyRingCliProvider(KeyRingBaseProvider):
|
||||
res = subprocess.run(
|
||||
cmd,
|
||||
stdin=subprocess.DEVNULL,
|
||||
capture_output=True,
|
||||
stdout=subprocess.PIPE,
|
||||
env=env,
|
||||
)
|
||||
if res.returncode:
|
||||
@@ -134,66 +147,89 @@ class KeyRingCliProvider(KeyRingBaseProvider):
|
||||
"""Mirror the implementation of keyring.set_password using cli"""
|
||||
if self.keyring is None:
|
||||
return None
|
||||
|
||||
cmd = [self.keyring, "set", service_name, username]
|
||||
input_ = (password + os.linesep).encode("utf-8")
|
||||
env = os.environ.copy()
|
||||
env["PYTHONIOENCODING"] = "utf-8"
|
||||
res = subprocess.run(cmd, input=input_, env=env)
|
||||
res.check_returncode()
|
||||
subprocess.run(
|
||||
[self.keyring, "set", service_name, username],
|
||||
input=f"{password}{os.linesep}".encode("utf-8"),
|
||||
env=env,
|
||||
check=True,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_keyring_provider() -> KeyRingBaseProvider:
|
||||
@lru_cache(maxsize=None)
|
||||
def get_keyring_provider(provider: str) -> KeyRingBaseProvider:
|
||||
logger.verbose("Keyring provider requested: %s", provider)
|
||||
|
||||
# keyring has previously failed and been disabled
|
||||
if not KEYRING_DISABLED:
|
||||
# Default to trying to use Python provider
|
||||
if KEYRING_DISABLED:
|
||||
provider = "disabled"
|
||||
if provider in ["import", "auto"]:
|
||||
try:
|
||||
return KeyRingPythonProvider()
|
||||
impl = KeyRingPythonProvider()
|
||||
logger.verbose("Keyring provider set: import")
|
||||
return impl
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception as exc:
|
||||
# In the event of an unexpected exception
|
||||
# we should warn the user
|
||||
logger.warning(
|
||||
"Installed copy of keyring fails with exception %s, "
|
||||
"trying to find a keyring executable as a fallback",
|
||||
str(exc),
|
||||
)
|
||||
|
||||
# Fallback to Cli Provider if `keyring` isn't installed
|
||||
msg = "Installed copy of keyring fails with exception %s"
|
||||
if provider == "auto":
|
||||
msg = msg + ", trying to find a keyring executable as a fallback"
|
||||
logger.warning(msg, exc, exc_info=logger.isEnabledFor(logging.DEBUG))
|
||||
if provider in ["subprocess", "auto"]:
|
||||
cli = shutil.which("keyring")
|
||||
if cli and cli.startswith(sysconfig.get_path("scripts")):
|
||||
# all code within this function is stolen from shutil.which implementation
|
||||
@typing.no_type_check
|
||||
def PATH_as_shutil_which_determines_it() -> str:
|
||||
path = os.environ.get("PATH", None)
|
||||
if path is None:
|
||||
try:
|
||||
path = os.confstr("CS_PATH")
|
||||
except (AttributeError, ValueError):
|
||||
# os.confstr() or CS_PATH is not available
|
||||
path = os.defpath
|
||||
# bpo-35755: Don't use os.defpath if the PATH environment variable is
|
||||
# set to an empty string
|
||||
|
||||
return path
|
||||
|
||||
scripts = Path(sysconfig.get_path("scripts"))
|
||||
|
||||
paths = []
|
||||
for path in PATH_as_shutil_which_determines_it().split(os.pathsep):
|
||||
p = Path(path)
|
||||
try:
|
||||
if not p.samefile(scripts):
|
||||
paths.append(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
path = os.pathsep.join(paths)
|
||||
|
||||
cli = shutil.which("keyring", path=path)
|
||||
|
||||
if cli:
|
||||
logger.verbose("Keyring provider set: subprocess with executable %s", cli)
|
||||
return KeyRingCliProvider(cli)
|
||||
|
||||
logger.verbose("Keyring provider set: disabled")
|
||||
return KeyRingNullProvider()
|
||||
|
||||
|
||||
def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]:
|
||||
"""Return the tuple auth for a given url from keyring."""
|
||||
# Do nothing if no url was provided
|
||||
if not url:
|
||||
return None
|
||||
|
||||
keyring = get_keyring_provider()
|
||||
try:
|
||||
return keyring.get_auth_info(url, username)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"Keyring is skipped due to an exception: %s",
|
||||
str(exc),
|
||||
)
|
||||
global KEYRING_DISABLED
|
||||
KEYRING_DISABLED = True
|
||||
return None
|
||||
|
||||
|
||||
class MultiDomainBasicAuth(AuthBase):
|
||||
def __init__(
|
||||
self, prompting: bool = True, index_urls: Optional[List[str]] = None
|
||||
self,
|
||||
prompting: bool = True,
|
||||
index_urls: Optional[List[str]] = None,
|
||||
keyring_provider: str = "auto",
|
||||
) -> None:
|
||||
self.prompting = prompting
|
||||
self.index_urls = index_urls
|
||||
self.keyring_provider = keyring_provider # type: ignore[assignment]
|
||||
self.passwords: Dict[str, AuthInfo] = {}
|
||||
# When the user is prompted to enter credentials and keyring is
|
||||
# available, we will offer to save them. If the user accepts,
|
||||
@@ -202,6 +238,47 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
# ``save_credentials`` to save these.
|
||||
self._credentials_to_save: Optional[Credentials] = None
|
||||
|
||||
@property
|
||||
def keyring_provider(self) -> KeyRingBaseProvider:
|
||||
return get_keyring_provider(self._keyring_provider)
|
||||
|
||||
@keyring_provider.setter
|
||||
def keyring_provider(self, provider: str) -> None:
|
||||
# The free function get_keyring_provider has been decorated with
|
||||
# functools.cache. If an exception occurs in get_keyring_auth that
|
||||
# cache will be cleared and keyring disabled, take that into account
|
||||
# if you want to remove this indirection.
|
||||
self._keyring_provider = provider
|
||||
|
||||
@property
|
||||
def use_keyring(self) -> bool:
|
||||
# We won't use keyring when --no-input is passed unless
|
||||
# a specific provider is requested because it might require
|
||||
# user interaction
|
||||
return self.prompting or self._keyring_provider not in ["auto", "disabled"]
|
||||
|
||||
def _get_keyring_auth(
|
||||
self,
|
||||
url: Optional[str],
|
||||
username: Optional[str],
|
||||
) -> Optional[AuthInfo]:
|
||||
"""Return the tuple auth for a given url from keyring."""
|
||||
# Do nothing if no url was provided
|
||||
if not url:
|
||||
return None
|
||||
|
||||
try:
|
||||
return self.keyring_provider.get_auth_info(url, username)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"Keyring is skipped due to an exception: %s",
|
||||
str(exc),
|
||||
)
|
||||
global KEYRING_DISABLED
|
||||
KEYRING_DISABLED = True
|
||||
get_keyring_provider.cache_clear()
|
||||
return None
|
||||
|
||||
def _get_index_url(self, url: str) -> Optional[str]:
|
||||
"""Return the original index URL matching the requested URL.
|
||||
|
||||
@@ -218,15 +295,42 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
if not url or not self.index_urls:
|
||||
return None
|
||||
|
||||
for u in self.index_urls:
|
||||
prefix = remove_auth_from_url(u).rstrip("/") + "/"
|
||||
if url.startswith(prefix):
|
||||
return u
|
||||
return None
|
||||
url = remove_auth_from_url(url).rstrip("/") + "/"
|
||||
parsed_url = urllib.parse.urlsplit(url)
|
||||
|
||||
candidates = []
|
||||
|
||||
for index in self.index_urls:
|
||||
index = index.rstrip("/") + "/"
|
||||
parsed_index = urllib.parse.urlsplit(remove_auth_from_url(index))
|
||||
if parsed_url == parsed_index:
|
||||
return index
|
||||
|
||||
if parsed_url.netloc != parsed_index.netloc:
|
||||
continue
|
||||
|
||||
candidate = urllib.parse.urlsplit(index)
|
||||
candidates.append(candidate)
|
||||
|
||||
if not candidates:
|
||||
return None
|
||||
|
||||
candidates.sort(
|
||||
reverse=True,
|
||||
key=lambda candidate: commonprefix(
|
||||
[
|
||||
parsed_url.path,
|
||||
candidate.path,
|
||||
]
|
||||
).rfind("/"),
|
||||
)
|
||||
|
||||
return urllib.parse.urlunsplit(candidates[0])
|
||||
|
||||
def _get_new_credentials(
|
||||
self,
|
||||
original_url: str,
|
||||
*,
|
||||
allow_netrc: bool = True,
|
||||
allow_keyring: bool = False,
|
||||
) -> AuthInfo:
|
||||
@@ -270,8 +374,8 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
# The index url is more specific than the netloc, so try it first
|
||||
# fmt: off
|
||||
kr_auth = (
|
||||
get_keyring_auth(index_url, username) or
|
||||
get_keyring_auth(netloc, username)
|
||||
self._get_keyring_auth(index_url, username) or
|
||||
self._get_keyring_auth(netloc, username)
|
||||
)
|
||||
# fmt: on
|
||||
if kr_auth:
|
||||
@@ -348,18 +452,23 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
def _prompt_for_password(
|
||||
self, netloc: str
|
||||
) -> Tuple[Optional[str], Optional[str], bool]:
|
||||
username = ask_input(f"User for {netloc}: ")
|
||||
username = ask_input(f"User for {netloc}: ") if self.prompting else None
|
||||
if not username:
|
||||
return None, None, False
|
||||
auth = get_keyring_auth(netloc, username)
|
||||
if auth and auth[0] is not None and auth[1] is not None:
|
||||
return auth[0], auth[1], False
|
||||
if self.use_keyring:
|
||||
auth = self._get_keyring_auth(netloc, username)
|
||||
if auth and auth[0] is not None and auth[1] is not None:
|
||||
return auth[0], auth[1], False
|
||||
password = ask_password("Password: ")
|
||||
return username, password, True
|
||||
|
||||
# Factored out to allow for easy patching in tests
|
||||
def _should_save_password_to_keyring(self) -> bool:
|
||||
if get_keyring_provider() is None:
|
||||
if (
|
||||
not self.prompting
|
||||
or not self.use_keyring
|
||||
or not self.keyring_provider.has_keyring
|
||||
):
|
||||
return False
|
||||
return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y"
|
||||
|
||||
@@ -369,19 +478,22 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
if resp.status_code != 401:
|
||||
return resp
|
||||
|
||||
username, password = None, None
|
||||
|
||||
# Query the keyring for credentials:
|
||||
if self.use_keyring:
|
||||
username, password = self._get_new_credentials(
|
||||
resp.url,
|
||||
allow_netrc=False,
|
||||
allow_keyring=True,
|
||||
)
|
||||
|
||||
# We are not able to prompt the user so simply return the response
|
||||
if not self.prompting:
|
||||
if not self.prompting and not username and not password:
|
||||
return resp
|
||||
|
||||
parsed = urllib.parse.urlparse(resp.url)
|
||||
|
||||
# Query the keyring for credentials:
|
||||
username, password = self._get_new_credentials(
|
||||
resp.url,
|
||||
allow_netrc=False,
|
||||
allow_keyring=True,
|
||||
)
|
||||
|
||||
# Prompt the user for a new username and password
|
||||
save = False
|
||||
if not username and not password:
|
||||
@@ -431,9 +543,8 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
|
||||
def save_credentials(self, resp: Response, **kwargs: Any) -> None:
|
||||
"""Response callback to save credentials on success."""
|
||||
keyring = get_keyring_provider()
|
||||
assert not isinstance(
|
||||
keyring, KeyRingNullProvider
|
||||
assert (
|
||||
self.keyring_provider.has_keyring
|
||||
), "should never reach here without keyring"
|
||||
|
||||
creds = self._credentials_to_save
|
||||
@@ -441,6 +552,8 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
if creds and resp.status_code < 400:
|
||||
try:
|
||||
logger.info("Saving credentials to keyring")
|
||||
keyring.save_auth_info(creds.url, creds.username, creds.password)
|
||||
self.keyring_provider.save_auth_info(
|
||||
creds.url, creds.username, creds.password
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("Failed to save credentials")
|
||||
|
||||
@@ -316,7 +316,6 @@ class InsecureCacheControlAdapter(CacheControlAdapter):
|
||||
|
||||
|
||||
class PipSession(requests.Session):
|
||||
|
||||
timeout: Optional[int] = None
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -145,9 +145,10 @@ def freeze(
|
||||
|
||||
|
||||
def _format_as_name_version(dist: BaseDistribution) -> str:
|
||||
if isinstance(dist.version, Version):
|
||||
return f"{dist.raw_name}=={dist.version}"
|
||||
return f"{dist.raw_name}==={dist.version}"
|
||||
dist_version = dist.version
|
||||
if isinstance(dist_version, Version):
|
||||
return f"{dist.raw_name}=={dist_version}"
|
||||
return f"{dist.raw_name}==={dist_version}"
|
||||
|
||||
|
||||
def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Legacy editable installation process, i.e. `setup.py develop`.
|
||||
"""
|
||||
import logging
|
||||
from typing import List, Optional, Sequence
|
||||
from typing import Optional, Sequence
|
||||
|
||||
from pipenv.patched.pip._internal.build_env import BuildEnvironment
|
||||
from pipenv.patched.pip._internal.utils.logging import indent_log
|
||||
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def install_editable(
|
||||
install_options: List[str],
|
||||
*,
|
||||
global_options: Sequence[str],
|
||||
prefix: Optional[str],
|
||||
home: Optional[str],
|
||||
@@ -31,7 +31,6 @@ def install_editable(
|
||||
args = make_setuptools_develop_args(
|
||||
setup_py_path,
|
||||
global_options=global_options,
|
||||
install_options=install_options,
|
||||
no_user_config=isolated,
|
||||
prefix=prefix,
|
||||
home=home,
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
"""Legacy installation process, i.e. `setup.py install`.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import List, Optional, Sequence
|
||||
|
||||
from pipenv.patched.pip._internal.build_env import BuildEnvironment
|
||||
from pipenv.patched.pip._internal.exceptions import InstallationError, LegacyInstallFailure
|
||||
from pipenv.patched.pip._internal.locations.base import change_root
|
||||
from pipenv.patched.pip._internal.models.scheme import Scheme
|
||||
from pipenv.patched.pip._internal.utils.misc import ensure_dir
|
||||
from pipenv.patched.pip._internal.utils.setuptools_build import make_setuptools_install_args
|
||||
from pipenv.patched.pip._internal.utils.subprocess import runner_with_spinner_message
|
||||
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def write_installed_files_from_setuptools_record(
|
||||
record_lines: List[str],
|
||||
root: Optional[str],
|
||||
req_description: str,
|
||||
) -> None:
|
||||
def prepend_root(path: str) -> str:
|
||||
if root is None or not os.path.isabs(path):
|
||||
return path
|
||||
else:
|
||||
return change_root(root, path)
|
||||
|
||||
for line in record_lines:
|
||||
directory = os.path.dirname(line)
|
||||
if directory.endswith(".egg-info"):
|
||||
egg_info_dir = prepend_root(directory)
|
||||
break
|
||||
else:
|
||||
message = (
|
||||
"{} did not indicate that it installed an "
|
||||
".egg-info directory. Only setup.py projects "
|
||||
"generating .egg-info directories are supported."
|
||||
).format(req_description)
|
||||
raise InstallationError(message)
|
||||
|
||||
new_lines = []
|
||||
for line in record_lines:
|
||||
filename = line.strip()
|
||||
if os.path.isdir(filename):
|
||||
filename += os.path.sep
|
||||
new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir))
|
||||
new_lines.sort()
|
||||
ensure_dir(egg_info_dir)
|
||||
inst_files_path = os.path.join(egg_info_dir, "installed-files.txt")
|
||||
with open(inst_files_path, "w") as f:
|
||||
f.write("\n".join(new_lines) + "\n")
|
||||
|
||||
|
||||
def install(
|
||||
install_options: List[str],
|
||||
global_options: Sequence[str],
|
||||
root: Optional[str],
|
||||
home: Optional[str],
|
||||
prefix: Optional[str],
|
||||
use_user_site: bool,
|
||||
pycompile: bool,
|
||||
scheme: Scheme,
|
||||
setup_py_path: str,
|
||||
isolated: bool,
|
||||
req_name: str,
|
||||
build_env: BuildEnvironment,
|
||||
unpacked_source_directory: str,
|
||||
req_description: str,
|
||||
) -> bool:
|
||||
|
||||
header_dir = scheme.headers
|
||||
|
||||
with TempDirectory(kind="record") as temp_dir:
|
||||
try:
|
||||
record_filename = os.path.join(temp_dir.path, "install-record.txt")
|
||||
install_args = make_setuptools_install_args(
|
||||
setup_py_path,
|
||||
global_options=global_options,
|
||||
install_options=install_options,
|
||||
record_filename=record_filename,
|
||||
root=root,
|
||||
prefix=prefix,
|
||||
header_dir=header_dir,
|
||||
home=home,
|
||||
use_user_site=use_user_site,
|
||||
no_user_config=isolated,
|
||||
pycompile=pycompile,
|
||||
)
|
||||
|
||||
runner = runner_with_spinner_message(
|
||||
f"Running setup.py install for {req_name}"
|
||||
)
|
||||
with build_env:
|
||||
runner(
|
||||
cmd=install_args,
|
||||
cwd=unpacked_source_directory,
|
||||
)
|
||||
|
||||
if not os.path.exists(record_filename):
|
||||
logger.debug("Record file %s not found", record_filename)
|
||||
# Signal to the caller that we didn't install the new package
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
# Signal to the caller that we didn't install the new package
|
||||
raise LegacyInstallFailure(package_details=req_name) from e
|
||||
|
||||
# At this point, we have successfully installed the requirement.
|
||||
|
||||
# We intentionally do not use any encoding to read the file because
|
||||
# setuptools writes the file using distutils.file_util.write_file,
|
||||
# which does not specify an encoding.
|
||||
with open(record_filename) as f:
|
||||
record_lines = f.read().splitlines()
|
||||
|
||||
write_installed_files_from_setuptools_record(record_lines, root, req_description)
|
||||
return True
|
||||
@@ -143,16 +143,18 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
|
||||
|
||||
# We don't want to warn for directories that are on PATH.
|
||||
not_warn_dirs = [
|
||||
os.path.normcase(i).rstrip(os.sep)
|
||||
os.path.normcase(os.path.normpath(i)).rstrip(os.sep)
|
||||
for i in os.environ.get("PATH", "").split(os.pathsep)
|
||||
]
|
||||
# If an executable sits with sys.executable, we don't warn for it.
|
||||
# This covers the case of venv invocations without activating the venv.
|
||||
not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable)))
|
||||
not_warn_dirs.append(
|
||||
os.path.normcase(os.path.normpath(os.path.dirname(sys.executable)))
|
||||
)
|
||||
warn_for: Dict[str, Set[str]] = {
|
||||
parent_dir: scripts
|
||||
for parent_dir, scripts in grouped_by_dir.items()
|
||||
if os.path.normcase(parent_dir) not in not_warn_dirs
|
||||
if os.path.normcase(os.path.normpath(parent_dir)) not in not_warn_dirs
|
||||
}
|
||||
if not warn_for:
|
||||
return None
|
||||
|
||||
@@ -179,7 +179,10 @@ def unpack_url(
|
||||
|
||||
|
||||
def _check_download_dir(
|
||||
link: Link, download_dir: str, hashes: Optional[Hashes]
|
||||
link: Link,
|
||||
download_dir: str,
|
||||
hashes: Optional[Hashes],
|
||||
warn_on_hash_mismatch: bool = True,
|
||||
) -> Optional[str]:
|
||||
"""Check download_dir for previously downloaded file with correct hash
|
||||
If a correct file is found return its path else None
|
||||
@@ -195,10 +198,11 @@ def _check_download_dir(
|
||||
try:
|
||||
hashes.check_against_path(download_path)
|
||||
except HashMismatch:
|
||||
logger.warning(
|
||||
"Previously-downloaded file %s has bad hash. Re-downloading.",
|
||||
download_path,
|
||||
)
|
||||
if warn_on_hash_mismatch:
|
||||
logger.warning(
|
||||
"Previously-downloaded file %s has bad hash. Re-downloading.",
|
||||
download_path,
|
||||
)
|
||||
os.unlink(download_path)
|
||||
return None
|
||||
return download_path
|
||||
@@ -263,18 +267,28 @@ class RequirementPreparer:
|
||||
|
||||
def _log_preparing_link(self, req: InstallRequirement) -> None:
|
||||
"""Provide context for the requirement being prepared."""
|
||||
if req.link.is_file and not req.original_link_is_in_wheel_cache:
|
||||
if req.link.is_file and not req.is_wheel_from_cache:
|
||||
message = "Processing %s"
|
||||
information = str(display_path(req.link.file_path))
|
||||
else:
|
||||
message = "Collecting %s"
|
||||
information = str(req.req or req)
|
||||
|
||||
# If we used req.req, inject requirement source if available (this
|
||||
# would already be included if we used req directly)
|
||||
if req.req and req.comes_from:
|
||||
if isinstance(req.comes_from, str):
|
||||
comes_from: Optional[str] = req.comes_from
|
||||
else:
|
||||
comes_from = req.comes_from.from_path()
|
||||
if comes_from:
|
||||
information += f" (from {comes_from})"
|
||||
|
||||
if (message, information) != self._previous_requirement_header:
|
||||
self._previous_requirement_header = (message, information)
|
||||
logger.info(message, information)
|
||||
|
||||
if req.original_link_is_in_wheel_cache:
|
||||
if req.is_wheel_from_cache:
|
||||
with indent_log():
|
||||
logger.info("Using cached %s", req.link.filename)
|
||||
|
||||
@@ -475,7 +489,18 @@ class RequirementPreparer:
|
||||
file_path = None
|
||||
if self.download_dir is not None and req.link.is_wheel:
|
||||
hashes = self._get_linked_req_hashes(req)
|
||||
file_path = _check_download_dir(req.link, self.download_dir, hashes)
|
||||
file_path = _check_download_dir(
|
||||
req.link,
|
||||
self.download_dir,
|
||||
hashes,
|
||||
# When a locally built wheel has been found in cache, we don't warn
|
||||
# about re-downloading when the already downloaded wheel hash does
|
||||
# not match. This is because the hash must be checked against the
|
||||
# original link, not the cached link. It that case the already
|
||||
# downloaded file will be removed and re-fetched from cache (which
|
||||
# implies a hash check against the cache entry's origin.json).
|
||||
warn_on_hash_mismatch=not req.is_wheel_from_cache,
|
||||
)
|
||||
|
||||
if file_path is not None:
|
||||
# The file is already available, so mark it as downloaded
|
||||
@@ -526,9 +551,35 @@ class RequirementPreparer:
|
||||
assert req.link
|
||||
link = req.link
|
||||
|
||||
self._ensure_link_req_src_dir(req, parallel_builds)
|
||||
hashes = self._get_linked_req_hashes(req)
|
||||
|
||||
if hashes and req.is_wheel_from_cache:
|
||||
assert req.download_info is not None
|
||||
assert link.is_wheel
|
||||
assert link.is_file
|
||||
# We need to verify hashes, and we have found the requirement in the cache
|
||||
# of locally built wheels.
|
||||
if (
|
||||
isinstance(req.download_info.info, ArchiveInfo)
|
||||
and req.download_info.info.hashes
|
||||
and hashes.has_one_of(req.download_info.info.hashes)
|
||||
):
|
||||
# At this point we know the requirement was built from a hashable source
|
||||
# artifact, and we verified that the cache entry's hash of the original
|
||||
# artifact matches one of the hashes we expect. We don't verify hashes
|
||||
# against the cached wheel, because the wheel is not the original.
|
||||
hashes = None
|
||||
else:
|
||||
logger.warning(
|
||||
"The hashes of the source archive found in cache entry "
|
||||
"don't match, ignoring cached built wheel "
|
||||
"and re-downloading source."
|
||||
)
|
||||
req.link = req.cached_wheel_source_link
|
||||
link = req.link
|
||||
|
||||
self._ensure_link_req_src_dir(req, parallel_builds)
|
||||
|
||||
if link.is_existing_dir():
|
||||
local_file = None
|
||||
elif link.url not in self._downloaded:
|
||||
@@ -561,12 +612,15 @@ class RequirementPreparer:
|
||||
# Make sure we have a hash in download_info. If we got it as part of the
|
||||
# URL, it will have been verified and we can rely on it. Otherwise we
|
||||
# compute it from the downloaded file.
|
||||
# FIXME: https://github.com/pypa/pip/issues/11943
|
||||
if (
|
||||
isinstance(req.download_info.info, ArchiveInfo)
|
||||
and not req.download_info.info.hash
|
||||
and not req.download_info.info.hashes
|
||||
and local_file
|
||||
):
|
||||
hash = hash_file(local_file.path)[0].hexdigest()
|
||||
# We populate info.hash for backward compatibility.
|
||||
# This will automatically populate info.hashes.
|
||||
req.download_info.info.hash = f"sha256={hash}"
|
||||
|
||||
# For use in later processing,
|
||||
|
||||
@@ -91,14 +91,19 @@ def load_pyproject_toml(
|
||||
# If we haven't worked out whether to use PEP 517 yet,
|
||||
# and the user hasn't explicitly stated a preference,
|
||||
# we do so if the project has a pyproject.toml file
|
||||
# or if we cannot import setuptools.
|
||||
# or if we cannot import setuptools or wheels.
|
||||
|
||||
# We fallback to PEP 517 when without setuptools,
|
||||
# We fallback to PEP 517 when without setuptools or without the wheel package,
|
||||
# so setuptools can be installed as a default build backend.
|
||||
# For more info see:
|
||||
# https://discuss.python.org/t/pip-without-setuptools-could-the-experience-be-improved/11810/9
|
||||
# https://github.com/pypa/pip/issues/8559
|
||||
elif use_pep517 is None:
|
||||
use_pep517 = has_pyproject or not importlib.util.find_spec("setuptools")
|
||||
use_pep517 = (
|
||||
has_pyproject
|
||||
or not importlib.util.find_spec("setuptools")
|
||||
or not importlib.util.find_spec("wheel")
|
||||
)
|
||||
|
||||
# At this point, we know whether we're going to use PEP 517.
|
||||
assert use_pep517 is not None
|
||||
|
||||
@@ -36,7 +36,6 @@ def _validate_requirements(
|
||||
|
||||
def install_given_reqs(
|
||||
requirements: List[InstallRequirement],
|
||||
install_options: List[str],
|
||||
global_options: Sequence[str],
|
||||
root: Optional[str],
|
||||
home: Optional[str],
|
||||
@@ -71,7 +70,6 @@ def install_given_reqs(
|
||||
|
||||
try:
|
||||
requirement.install(
|
||||
install_options,
|
||||
global_options,
|
||||
root=root,
|
||||
home=home,
|
||||
|
||||
@@ -11,7 +11,7 @@ InstallRequirement.
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from typing import Any, Dict, Optional, Set, Tuple, Union
|
||||
from typing import Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
from pipenv.patched.pip._vendor.packaging.markers import Marker
|
||||
from pipenv.patched.pip._vendor.packaging.requirements import InvalidRequirement, Requirement
|
||||
@@ -201,15 +201,16 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
|
||||
def install_req_from_editable(
|
||||
editable_req: str,
|
||||
comes_from: Optional[Union[InstallRequirement, str]] = None,
|
||||
*,
|
||||
use_pep517: Optional[bool] = None,
|
||||
isolated: bool = False,
|
||||
options: Optional[Dict[str, Any]] = None,
|
||||
global_options: Optional[List[str]] = None,
|
||||
hash_options: Optional[Dict[str, List[str]]] = None,
|
||||
constraint: bool = False,
|
||||
user_supplied: bool = False,
|
||||
permit_editable_wheels: bool = False,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
) -> InstallRequirement:
|
||||
|
||||
parts = parse_req_from_editable(editable_req)
|
||||
|
||||
return InstallRequirement(
|
||||
@@ -222,9 +223,8 @@ def install_req_from_editable(
|
||||
constraint=constraint,
|
||||
use_pep517=use_pep517,
|
||||
isolated=isolated,
|
||||
install_options=options.get("install_options", []) if options else [],
|
||||
global_options=options.get("global_options", []) if options else [],
|
||||
hash_options=options.get("hashes", {}) if options else {},
|
||||
global_options=global_options,
|
||||
hash_options=hash_options,
|
||||
config_settings=config_settings,
|
||||
extras=parts.extras,
|
||||
)
|
||||
@@ -376,13 +376,15 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar
|
||||
def install_req_from_line(
|
||||
name: str,
|
||||
comes_from: Optional[Union[str, InstallRequirement]] = None,
|
||||
*,
|
||||
use_pep517: Optional[bool] = None,
|
||||
isolated: bool = False,
|
||||
options: Optional[Dict[str, Any]] = None,
|
||||
global_options: Optional[List[str]] = None,
|
||||
hash_options: Optional[Dict[str, List[str]]] = None,
|
||||
constraint: bool = False,
|
||||
line_source: Optional[str] = None,
|
||||
user_supplied: bool = False,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
) -> InstallRequirement:
|
||||
"""Creates an InstallRequirement from a name, which might be a
|
||||
requirement, directory containing 'setup.py', filename, or URL.
|
||||
@@ -399,9 +401,8 @@ def install_req_from_line(
|
||||
markers=parts.markers,
|
||||
use_pep517=use_pep517,
|
||||
isolated=isolated,
|
||||
install_options=options.get("install_options", []) if options else [],
|
||||
global_options=options.get("global_options", []) if options else [],
|
||||
hash_options=options.get("hashes", {}) if options else {},
|
||||
global_options=global_options,
|
||||
hash_options=hash_options,
|
||||
config_settings=config_settings,
|
||||
constraint=constraint,
|
||||
extras=parts.extras,
|
||||
@@ -415,7 +416,6 @@ def install_req_from_req_string(
|
||||
isolated: bool = False,
|
||||
use_pep517: Optional[bool] = None,
|
||||
user_supplied: bool = False,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
) -> InstallRequirement:
|
||||
try:
|
||||
req = get_requirement(req_string)
|
||||
@@ -445,7 +445,6 @@ def install_req_from_req_string(
|
||||
isolated=isolated,
|
||||
use_pep517=use_pep517,
|
||||
user_supplied=user_supplied,
|
||||
config_settings=config_settings,
|
||||
)
|
||||
|
||||
|
||||
@@ -454,7 +453,7 @@ def install_req_from_parsed_requirement(
|
||||
isolated: bool = False,
|
||||
use_pep517: Optional[bool] = None,
|
||||
user_supplied: bool = False,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
) -> InstallRequirement:
|
||||
if parsed_req.is_editable:
|
||||
req = install_req_from_editable(
|
||||
@@ -473,7 +472,14 @@ def install_req_from_parsed_requirement(
|
||||
comes_from=parsed_req.comes_from,
|
||||
use_pep517=use_pep517,
|
||||
isolated=isolated,
|
||||
options=parsed_req.options,
|
||||
global_options=(
|
||||
parsed_req.options.get("global_options", [])
|
||||
if parsed_req.options
|
||||
else []
|
||||
),
|
||||
hash_options=(
|
||||
parsed_req.options.get("hashes", {}) if parsed_req.options else {}
|
||||
),
|
||||
constraint=parsed_req.constraint,
|
||||
line_source=parsed_req.line_source,
|
||||
user_supplied=user_supplied,
|
||||
@@ -493,7 +499,6 @@ def install_req_from_link_and_ireq(
|
||||
markers=ireq.markers,
|
||||
use_pep517=ireq.use_pep517,
|
||||
isolated=ireq.isolated,
|
||||
install_options=ireq.install_options,
|
||||
global_options=ireq.global_options,
|
||||
hash_options=ireq.hash_options,
|
||||
config_settings=ireq.config_settings,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Requirements file parsing
|
||||
"""
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
@@ -69,14 +70,16 @@ SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [
|
||||
|
||||
# options to be passed to requirements
|
||||
SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [
|
||||
cmdoptions.install_options,
|
||||
cmdoptions.global_options,
|
||||
cmdoptions.hash,
|
||||
cmdoptions.config_settings,
|
||||
]
|
||||
|
||||
# the 'dest' string values
|
||||
SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ParsedRequirement:
|
||||
def __init__(
|
||||
@@ -166,7 +169,6 @@ def handle_requirement_line(
|
||||
line: ParsedLine,
|
||||
options: Optional[optparse.Values] = None,
|
||||
) -> ParsedRequirement:
|
||||
|
||||
# preserve for the nested code path
|
||||
line_comes_from = "{} {} (line {})".format(
|
||||
"-c" if line.constraint else "-r",
|
||||
@@ -211,6 +213,12 @@ def handle_option_line(
|
||||
options: Optional[optparse.Values] = None,
|
||||
session: Optional[PipSession] = None,
|
||||
) -> None:
|
||||
if opts.hashes:
|
||||
logger.warning(
|
||||
"%s line %s has --hash but no requirement, and will be ignored.",
|
||||
filename,
|
||||
lineno,
|
||||
)
|
||||
|
||||
if options:
|
||||
# percolate options upward
|
||||
|
||||
@@ -8,7 +8,6 @@ import shutil
|
||||
import sys
|
||||
import uuid
|
||||
import zipfile
|
||||
from enum import Enum
|
||||
from optparse import Values
|
||||
from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union
|
||||
|
||||
@@ -21,7 +20,7 @@ from pipenv.patched.pip._vendor.packaging.version import parse as parse_version
|
||||
from pipenv.patched.pip._vendor.pyproject_hooks import BuildBackendHookCaller
|
||||
|
||||
from pipenv.patched.pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment
|
||||
from pipenv.patched.pip._internal.exceptions import InstallationError, LegacyInstallFailure
|
||||
from pipenv.patched.pip._internal.exceptions import InstallationError
|
||||
from pipenv.patched.pip._internal.locations import get_scheme
|
||||
from pipenv.patched.pip._internal.metadata import (
|
||||
BaseDistribution,
|
||||
@@ -40,15 +39,10 @@ from pipenv.patched.pip._internal.operations.build.metadata_legacy import (
|
||||
from pipenv.patched.pip._internal.operations.install.editable_legacy import (
|
||||
install_editable as install_editable_legacy,
|
||||
)
|
||||
from pipenv.patched.pip._internal.operations.install.legacy import install as install_legacy
|
||||
from pipenv.patched.pip._internal.operations.install.wheel import install_wheel
|
||||
from pipenv.patched.pip._internal.pyproject import load_pyproject_toml, make_pyproject_path
|
||||
from pipenv.patched.pip._internal.req.req_uninstall import UninstallPathSet
|
||||
from pipenv.patched.pip._internal.utils.deprecation import LegacyInstallReason, deprecated
|
||||
from pipenv.patched.pip._internal.utils.direct_url_helpers import (
|
||||
direct_url_for_editable,
|
||||
direct_url_from_link,
|
||||
)
|
||||
from pipenv.patched.pip._internal.utils.deprecation import deprecated
|
||||
from pipenv.patched.pip._internal.utils.hashes import Hashes
|
||||
from pipenv.patched.pip._internal.utils.misc import (
|
||||
ConfiguredBuildBackendHookCaller,
|
||||
@@ -83,10 +77,10 @@ class InstallRequirement:
|
||||
markers: Optional[Marker] = None,
|
||||
use_pep517: Optional[bool] = None,
|
||||
isolated: bool = False,
|
||||
install_options: Optional[List[str]] = None,
|
||||
*,
|
||||
global_options: Optional[List[str]] = None,
|
||||
hash_options: Optional[Dict[str, List[str]]] = None,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
constraint: bool = False,
|
||||
extras: Collection[str] = (),
|
||||
user_supplied: bool = False,
|
||||
@@ -98,7 +92,6 @@ class InstallRequirement:
|
||||
self.constraint = constraint
|
||||
self.editable = editable
|
||||
self.permit_editable_wheels = permit_editable_wheels
|
||||
self.legacy_install_reason: Optional[LegacyInstallReason] = None
|
||||
|
||||
# source_dir is the local directory where the linked requirement is
|
||||
# located, or unpacked. In case unpacking is needed, creating and
|
||||
@@ -115,7 +108,11 @@ class InstallRequirement:
|
||||
# PEP 508 URL requirement
|
||||
link = Link(req.url)
|
||||
self.link = self.original_link = link
|
||||
self.original_link_is_in_wheel_cache = False
|
||||
|
||||
# When this InstallRequirement is a wheel obtained from the cache of locally
|
||||
# built wheels, this is the source link corresponding to the cache entry, which
|
||||
# was used to download and build the cached wheel.
|
||||
self.cached_wheel_source_link: Optional[Link] = None
|
||||
|
||||
# Information about the location of the artifact that was downloaded . This
|
||||
# property is guaranteed to be set in resolver results.
|
||||
@@ -146,7 +143,6 @@ class InstallRequirement:
|
||||
# Set to True after successful installation
|
||||
self.install_succeeded: Optional[bool] = None
|
||||
# Supplied options
|
||||
self.install_options = install_options if install_options else []
|
||||
self.global_options = global_options if global_options else []
|
||||
self.hash_options = hash_options if hash_options else {}
|
||||
self.config_settings = config_settings
|
||||
@@ -295,7 +291,12 @@ class InstallRequirement:
|
||||
|
||||
"""
|
||||
good_hashes = self.hash_options.copy()
|
||||
link = self.link if trust_internet else self.original_link
|
||||
if trust_internet:
|
||||
link = self.link
|
||||
elif self.original_link and self.user_supplied:
|
||||
link = self.original_link
|
||||
else:
|
||||
link = None
|
||||
if link and link.hash:
|
||||
good_hashes.setdefault(link.hash_name, []).append(link.hash)
|
||||
return Hashes(good_hashes)
|
||||
@@ -440,6 +441,12 @@ class InstallRequirement:
|
||||
return False
|
||||
return self.link.is_wheel
|
||||
|
||||
@property
|
||||
def is_wheel_from_cache(self) -> bool:
|
||||
# When True, it means that this InstallRequirement is a local wheel file in the
|
||||
# cache of locally built wheels.
|
||||
return self.cached_wheel_source_link is not None
|
||||
|
||||
# Things valid for sdists
|
||||
@property
|
||||
def unpacked_source_directory(self) -> str:
|
||||
@@ -479,6 +486,15 @@ class InstallRequirement:
|
||||
)
|
||||
|
||||
if pyproject_toml_data is None:
|
||||
if self.config_settings:
|
||||
deprecated(
|
||||
reason=f"Config settings are ignored for project {self}.",
|
||||
replacement=(
|
||||
"to use --use-pep517 or add a "
|
||||
"pyproject.toml file to the project"
|
||||
),
|
||||
gone_in="23.3",
|
||||
)
|
||||
self.use_pep517 = False
|
||||
return
|
||||
|
||||
@@ -747,7 +763,6 @@ class InstallRequirement:
|
||||
|
||||
def install(
|
||||
self,
|
||||
install_options: List[str],
|
||||
global_options: Optional[Sequence[str]] = None,
|
||||
root: Optional[str] = None,
|
||||
home: Optional[str] = None,
|
||||
@@ -765,11 +780,9 @@ class InstallRequirement:
|
||||
prefix=prefix,
|
||||
)
|
||||
|
||||
global_options = global_options if global_options is not None else []
|
||||
if self.editable and not self.is_wheel:
|
||||
install_editable_legacy(
|
||||
install_options,
|
||||
global_options,
|
||||
global_options=global_options if global_options is not None else [],
|
||||
prefix=prefix,
|
||||
home=home,
|
||||
use_user_site=use_user_site,
|
||||
@@ -782,82 +795,23 @@ class InstallRequirement:
|
||||
self.install_succeeded = True
|
||||
return
|
||||
|
||||
if self.is_wheel:
|
||||
assert self.local_file_path
|
||||
direct_url = None
|
||||
# TODO this can be refactored to direct_url = self.download_info
|
||||
if self.editable:
|
||||
direct_url = direct_url_for_editable(self.unpacked_source_directory)
|
||||
elif self.original_link:
|
||||
direct_url = direct_url_from_link(
|
||||
self.original_link,
|
||||
self.source_dir,
|
||||
self.original_link_is_in_wheel_cache,
|
||||
)
|
||||
install_wheel(
|
||||
self.name,
|
||||
self.local_file_path,
|
||||
scheme=scheme,
|
||||
req_description=str(self.req),
|
||||
pycompile=pycompile,
|
||||
warn_script_location=warn_script_location,
|
||||
direct_url=direct_url,
|
||||
requested=self.user_supplied,
|
||||
)
|
||||
self.install_succeeded = True
|
||||
return
|
||||
assert self.is_wheel
|
||||
assert self.local_file_path
|
||||
|
||||
# TODO: Why don't we do this for editable installs?
|
||||
|
||||
# Extend the list of global and install options passed on to
|
||||
# the setup.py call with the ones from the requirements file.
|
||||
# Options specified in requirements file override those
|
||||
# specified on the command line, since the last option given
|
||||
# to setup.py is the one that is used.
|
||||
global_options = list(global_options) + self.global_options
|
||||
install_options = list(install_options) + self.install_options
|
||||
|
||||
try:
|
||||
if (
|
||||
self.legacy_install_reason is not None
|
||||
and self.legacy_install_reason.emit_before_install
|
||||
):
|
||||
self.legacy_install_reason.emit_deprecation(self.name)
|
||||
success = install_legacy(
|
||||
install_options=install_options,
|
||||
global_options=global_options,
|
||||
root=root,
|
||||
home=home,
|
||||
prefix=prefix,
|
||||
use_user_site=use_user_site,
|
||||
pycompile=pycompile,
|
||||
scheme=scheme,
|
||||
setup_py_path=self.setup_py_path,
|
||||
isolated=self.isolated,
|
||||
req_name=self.name,
|
||||
build_env=self.build_env,
|
||||
unpacked_source_directory=self.unpacked_source_directory,
|
||||
req_description=str(self.req),
|
||||
)
|
||||
except LegacyInstallFailure as exc:
|
||||
self.install_succeeded = False
|
||||
raise exc
|
||||
except Exception:
|
||||
self.install_succeeded = True
|
||||
raise
|
||||
|
||||
self.install_succeeded = success
|
||||
|
||||
if (
|
||||
success
|
||||
and self.legacy_install_reason is not None
|
||||
and self.legacy_install_reason.emit_after_success
|
||||
):
|
||||
self.legacy_install_reason.emit_deprecation(self.name)
|
||||
install_wheel(
|
||||
self.name,
|
||||
self.local_file_path,
|
||||
scheme=scheme,
|
||||
req_description=str(self.req),
|
||||
pycompile=pycompile,
|
||||
warn_script_location=warn_script_location,
|
||||
direct_url=self.download_info if self.original_link else None,
|
||||
requested=self.user_supplied,
|
||||
)
|
||||
self.install_succeeded = True
|
||||
|
||||
|
||||
def check_invalid_constraint_type(req: InstallRequirement) -> str:
|
||||
|
||||
# Check for unsupported forms
|
||||
problem = ""
|
||||
if not req.name:
|
||||
@@ -894,54 +848,21 @@ def _has_option(options: Values, reqs: List[InstallRequirement], option: str) ->
|
||||
return False
|
||||
|
||||
|
||||
def _install_option_ignored(
|
||||
install_options: List[str], reqs: List[InstallRequirement]
|
||||
) -> bool:
|
||||
for req in reqs:
|
||||
if (install_options or req.install_options) and not req.use_pep517:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class LegacySetupPyOptionsCheckMode(Enum):
|
||||
INSTALL = 1
|
||||
WHEEL = 2
|
||||
DOWNLOAD = 3
|
||||
|
||||
|
||||
def check_legacy_setup_py_options(
|
||||
options: Values,
|
||||
reqs: List[InstallRequirement],
|
||||
mode: LegacySetupPyOptionsCheckMode,
|
||||
) -> None:
|
||||
has_install_options = _has_option(options, reqs, "install_options")
|
||||
has_build_options = _has_option(options, reqs, "build_options")
|
||||
has_global_options = _has_option(options, reqs, "global_options")
|
||||
legacy_setup_py_options_present = (
|
||||
has_install_options or has_build_options or has_global_options
|
||||
)
|
||||
if not legacy_setup_py_options_present:
|
||||
return
|
||||
|
||||
options.format_control.disallow_binaries()
|
||||
logger.warning(
|
||||
"Implying --no-binary=:all: due to the presence of "
|
||||
"--build-option / --global-option / --install-option. "
|
||||
"Consider using --config-settings for more flexibility.",
|
||||
)
|
||||
if mode == LegacySetupPyOptionsCheckMode.INSTALL and has_install_options:
|
||||
if _install_option_ignored(options.install_options, reqs):
|
||||
logger.warning(
|
||||
"Ignoring --install-option when building using PEP 517",
|
||||
)
|
||||
else:
|
||||
deprecated(
|
||||
reason=(
|
||||
"--install-option is deprecated because "
|
||||
"it forces pip to use the 'setup.py install' "
|
||||
"command which is itself deprecated."
|
||||
),
|
||||
issue=11358,
|
||||
replacement="to use --config-settings",
|
||||
gone_in="23.1",
|
||||
)
|
||||
if has_build_options or has_global_options:
|
||||
deprecated(
|
||||
reason="--build-option and --global-option are deprecated.",
|
||||
issue=11859,
|
||||
replacement="to use --config-settings",
|
||||
gone_in="23.3",
|
||||
)
|
||||
logger.warning(
|
||||
"Implying --no-binary=:all: due to the presence of "
|
||||
"--build-option / --global-option. "
|
||||
)
|
||||
options.format_control.disallow_binaries()
|
||||
|
||||
@@ -11,8 +11,9 @@ from pipenv.patched.pip._internal.metadata import BaseDistribution
|
||||
from pipenv.patched.pip._internal.utils.compat import WINDOWS
|
||||
from pipenv.patched.pip._internal.utils.egg_link import egg_link_path_from_location
|
||||
from pipenv.patched.pip._internal.utils.logging import getLogger, indent_log
|
||||
from pipenv.patched.pip._internal.utils.misc import ask, is_local, normalize_path, renames, rmtree
|
||||
from pipenv.patched.pip._internal.utils.misc import ask, normalize_path, renames, rmtree
|
||||
from pipenv.patched.pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory
|
||||
from pipenv.patched.pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
@@ -312,6 +313,10 @@ class UninstallPathSet:
|
||||
self._pth: Dict[str, UninstallPthEntries] = {}
|
||||
self._dist = dist
|
||||
self._moved_paths = StashedUninstallPathSet()
|
||||
# Create local cache of normalize_path results. Creating an UninstallPathSet
|
||||
# can result in hundreds/thousands of redundant calls to normalize_path with
|
||||
# the same args, which hurts performance.
|
||||
self._normalize_path_cached = functools.lru_cache()(normalize_path)
|
||||
|
||||
def _permitted(self, path: str) -> bool:
|
||||
"""
|
||||
@@ -319,14 +324,17 @@ class UninstallPathSet:
|
||||
remove/modify, False otherwise.
|
||||
|
||||
"""
|
||||
return is_local(path)
|
||||
# aka is_local, but caching normalized sys.prefix
|
||||
if not running_under_virtualenv():
|
||||
return True
|
||||
return path.startswith(self._normalize_path_cached(sys.prefix))
|
||||
|
||||
def add(self, path: str) -> None:
|
||||
head, tail = os.path.split(path)
|
||||
|
||||
# we normalize the head to resolve parent directory symlinks, but not
|
||||
# the tail, since we only want to uninstall symlinks, not their targets
|
||||
path = os.path.join(normalize_path(head), os.path.normcase(tail))
|
||||
path = os.path.join(self._normalize_path_cached(head), os.path.normcase(tail))
|
||||
|
||||
if not os.path.exists(path):
|
||||
return
|
||||
@@ -341,7 +349,7 @@ class UninstallPathSet:
|
||||
self.add(cache_from_source(path))
|
||||
|
||||
def add_pth(self, pth_file: str, entry: str) -> None:
|
||||
pth_file = normalize_path(pth_file)
|
||||
pth_file = self._normalize_path_cached(pth_file)
|
||||
if self._permitted(pth_file):
|
||||
if pth_file not in self._pth:
|
||||
self._pth[pth_file] = UninstallPthEntries(pth_file)
|
||||
@@ -531,12 +539,14 @@ class UninstallPathSet:
|
||||
# above, so this only covers the setuptools-style editable.
|
||||
with open(develop_egg_link) as fh:
|
||||
link_pointer = os.path.normcase(fh.readline().strip())
|
||||
normalized_link_pointer = normalize_path(link_pointer)
|
||||
normalized_link_pointer = paths_to_remove._normalize_path_cached(
|
||||
link_pointer
|
||||
)
|
||||
assert os.path.samefile(
|
||||
normalized_link_pointer, normalized_dist_location
|
||||
), (
|
||||
f"Egg-link {link_pointer} does not match installed location of "
|
||||
f"{dist.raw_name} (at {dist_location})"
|
||||
f"Egg-link {develop_egg_link} (to {link_pointer}) does not match "
|
||||
f"installed location of {dist.raw_name} (at {dist_location})"
|
||||
)
|
||||
paths_to_remove.add(develop_egg_link)
|
||||
easy_install_pth = os.path.join(
|
||||
|
||||
@@ -431,12 +431,12 @@ class Resolver(BaseResolver):
|
||||
if cache_entry is not None:
|
||||
logger.debug("Using cached wheel link: %s", cache_entry.link)
|
||||
if req.link is req.original_link and cache_entry.persistent:
|
||||
req.original_link_is_in_wheel_cache = True
|
||||
req.cached_wheel_source_link = req.link
|
||||
if cache_entry.origin is not None:
|
||||
req.download_info = cache_entry.origin
|
||||
else:
|
||||
# Legacy cache entry that does not have origin.json.
|
||||
# download_info may miss the archive_info.hash field.
|
||||
# download_info may miss the archive_info.hashes field.
|
||||
req.download_info = direct_url_from_link(
|
||||
req.link, link_is_in_wheel_cache=cache_entry.persistent
|
||||
)
|
||||
|
||||
@@ -66,15 +66,13 @@ def make_install_req_from_link(
|
||||
use_pep517=template.use_pep517,
|
||||
isolated=template.isolated,
|
||||
constraint=template.constraint,
|
||||
options=dict(
|
||||
install_options=template.install_options,
|
||||
global_options=template.global_options,
|
||||
hashes=template.hash_options,
|
||||
),
|
||||
global_options=template.global_options,
|
||||
hash_options=template.hash_options,
|
||||
config_settings=template.config_settings,
|
||||
)
|
||||
ireq.original_link = template.original_link
|
||||
ireq.link = link
|
||||
ireq.extras = template.extras
|
||||
return ireq
|
||||
|
||||
|
||||
@@ -82,7 +80,7 @@ def make_install_req_from_editable(
|
||||
link: Link, template: InstallRequirement
|
||||
) -> InstallRequirement:
|
||||
assert template.editable, "template not editable"
|
||||
return install_req_from_editable(
|
||||
ireq = install_req_from_editable(
|
||||
link.url,
|
||||
user_supplied=template.user_supplied,
|
||||
comes_from=template.comes_from,
|
||||
@@ -90,13 +88,12 @@ def make_install_req_from_editable(
|
||||
isolated=template.isolated,
|
||||
constraint=template.constraint,
|
||||
permit_editable_wheels=template.permit_editable_wheels,
|
||||
options=dict(
|
||||
install_options=template.install_options,
|
||||
global_options=template.global_options,
|
||||
hashes=template.hash_options,
|
||||
),
|
||||
global_options=template.global_options,
|
||||
hash_options=template.hash_options,
|
||||
config_settings=template.config_settings,
|
||||
)
|
||||
ireq.extras = template.extras
|
||||
return ireq
|
||||
|
||||
|
||||
def _make_install_req_from_dist(
|
||||
@@ -115,11 +112,8 @@ def _make_install_req_from_dist(
|
||||
use_pep517=template.use_pep517,
|
||||
isolated=template.isolated,
|
||||
constraint=template.constraint,
|
||||
options=dict(
|
||||
install_options=template.install_options,
|
||||
global_options=template.global_options,
|
||||
hashes=template.hash_options,
|
||||
),
|
||||
global_options=template.global_options,
|
||||
hash_options=template.hash_options,
|
||||
config_settings=template.config_settings,
|
||||
)
|
||||
ireq.satisfied_by = dist
|
||||
@@ -269,7 +263,7 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
|
||||
version: Optional[CandidateVersion] = None,
|
||||
) -> None:
|
||||
source_link = link
|
||||
cache_entry = factory.get_wheel_cache_entry(link, name)
|
||||
cache_entry = factory.get_wheel_cache_entry(source_link, name)
|
||||
if cache_entry is not None:
|
||||
logger.debug("Using cached wheel link: %s", cache_entry.link)
|
||||
link = cache_entry.link
|
||||
@@ -287,13 +281,15 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
|
||||
)
|
||||
|
||||
if cache_entry is not None:
|
||||
assert ireq.link.is_wheel
|
||||
assert ireq.link.is_file
|
||||
if cache_entry.persistent and template.link is template.original_link:
|
||||
ireq.original_link_is_in_wheel_cache = True
|
||||
ireq.cached_wheel_source_link = source_link
|
||||
if cache_entry.origin is not None:
|
||||
ireq.download_info = cache_entry.origin
|
||||
else:
|
||||
# Legacy cache entry that does not have origin.json.
|
||||
# download_info may miss the archive_info.hash field.
|
||||
# download_info may miss the archive_info.hashes field.
|
||||
ireq.download_info = direct_url_from_link(
|
||||
source_link, link_is_in_wheel_cache=cache_entry.persistent
|
||||
)
|
||||
|
||||
@@ -535,7 +535,7 @@ class Factory:
|
||||
hash mismatches. Furthermore, cached wheels at present have
|
||||
nondeterministic contents due to file modification times.
|
||||
"""
|
||||
if self._wheel_cache is None or self.preparer.require_hashes:
|
||||
if self._wheel_cache is None:
|
||||
return None
|
||||
return self._wheel_cache.get_cache_entry(
|
||||
link=link,
|
||||
@@ -632,7 +632,6 @@ class Factory:
|
||||
e: "ResolutionImpossible[Requirement, Candidate]",
|
||||
constraints: Dict[str, Constraint],
|
||||
) -> InstallationError:
|
||||
|
||||
assert e.causes, "Installation error reported with no cause"
|
||||
|
||||
# If one of the things we can't solve is "we need Python X.Y",
|
||||
|
||||
@@ -104,7 +104,7 @@ class PipProvider(_ProviderBase):
|
||||
def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:
|
||||
return requirement_or_candidate.name
|
||||
|
||||
def get_preference( # type: ignore
|
||||
def get_preference(
|
||||
self,
|
||||
identifier: str,
|
||||
resolutions: Mapping[str, Candidate],
|
||||
@@ -124,14 +124,29 @@ class PipProvider(_ProviderBase):
|
||||
* If equal, prefer if any requirement is "pinned", i.e. contains
|
||||
operator ``===`` or ``==``.
|
||||
* If equal, calculate an approximate "depth" and resolve requirements
|
||||
closer to the user-specified requirements first.
|
||||
closer to the user-specified requirements first. If the depth cannot
|
||||
by determined (eg: due to no matching parents), it is considered
|
||||
infinite.
|
||||
* Order user-specified requirements by the order they are specified.
|
||||
* If equal, prefers "non-free" requirements, i.e. contains at least one
|
||||
operator, such as ``>=`` or ``<``.
|
||||
* If equal, order alphabetically for consistency (helps debuggability).
|
||||
"""
|
||||
lookups = (r.get_candidate_lookup() for r, _ in information[identifier])
|
||||
candidate, ireqs = zip(*lookups)
|
||||
try:
|
||||
next(iter(information[identifier]))
|
||||
except StopIteration:
|
||||
# There is no information for this identifier, so there's no known
|
||||
# candidates.
|
||||
has_information = False
|
||||
else:
|
||||
has_information = True
|
||||
|
||||
if has_information:
|
||||
lookups = (r.get_candidate_lookup() for r, _ in information[identifier])
|
||||
candidate, ireqs = zip(*lookups)
|
||||
else:
|
||||
candidate, ireqs = None, ()
|
||||
|
||||
operators = [
|
||||
specifier.operator
|
||||
for specifier_set in (ireq.specifier for ireq in ireqs if ireq)
|
||||
@@ -146,11 +161,14 @@ class PipProvider(_ProviderBase):
|
||||
requested_order: Union[int, float] = self._user_requested[identifier]
|
||||
except KeyError:
|
||||
requested_order = math.inf
|
||||
parent_depths = (
|
||||
self._known_depths[parent.name] if parent is not None else 0.0
|
||||
for _, parent in information[identifier]
|
||||
)
|
||||
inferred_depth = min(d for d in parent_depths) + 1.0
|
||||
if has_information:
|
||||
parent_depths = (
|
||||
self._known_depths[parent.name] if parent is not None else 0.0
|
||||
for _, parent in information[identifier]
|
||||
)
|
||||
inferred_depth = min(d for d in parent_depths) + 1.0
|
||||
else:
|
||||
inferred_depth = math.inf
|
||||
else:
|
||||
inferred_depth = 1.0
|
||||
self._known_depths[identifier] = inferred_depth
|
||||
@@ -161,16 +179,6 @@ class PipProvider(_ProviderBase):
|
||||
# free, so we always do it first to avoid needless work if it fails.
|
||||
requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER
|
||||
|
||||
# HACK: Setuptools have a very long and solid backward compatibility
|
||||
# track record, and extremely few projects would request a narrow,
|
||||
# non-recent version range of it since that would break a lot things.
|
||||
# (Most projects specify it only to request for an installer feature,
|
||||
# which does not work, but that's another topic.) Intentionally
|
||||
# delaying Setuptools helps reduce branches the resolver has to check.
|
||||
# This serves as a temporary fix for issues like "apache-airflow[all]"
|
||||
# while we work on "proper" branch pruning techniques.
|
||||
delay_this = identifier == "setuptools"
|
||||
|
||||
# Prefer the causes of backtracking on the assumption that the problem
|
||||
# resolving the dependency tree is related to the failures that caused
|
||||
# the backtracking
|
||||
@@ -178,7 +186,6 @@ class PipProvider(_ProviderBase):
|
||||
|
||||
return (
|
||||
not requires_python,
|
||||
delay_this,
|
||||
not direct,
|
||||
not pinned,
|
||||
not backtrack_cause,
|
||||
|
||||
@@ -11,9 +11,9 @@ logger = getLogger(__name__)
|
||||
|
||||
class PipReporter(BaseReporter):
|
||||
def __init__(self) -> None:
|
||||
self.backtracks_by_package: DefaultDict[str, int] = defaultdict(int)
|
||||
self.reject_count_by_package: DefaultDict[str, int] = defaultdict(int)
|
||||
|
||||
self._messages_at_backtrack = {
|
||||
self._messages_at_reject_count = {
|
||||
1: (
|
||||
"pip is looking at multiple versions of {package_name} to "
|
||||
"determine which version is compatible with other "
|
||||
@@ -32,16 +32,28 @@ class PipReporter(BaseReporter):
|
||||
),
|
||||
}
|
||||
|
||||
def backtracking(self, candidate: Candidate) -> None:
|
||||
self.backtracks_by_package[candidate.name] += 1
|
||||
def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None:
|
||||
self.reject_count_by_package[candidate.name] += 1
|
||||
|
||||
count = self.backtracks_by_package[candidate.name]
|
||||
if count not in self._messages_at_backtrack:
|
||||
count = self.reject_count_by_package[candidate.name]
|
||||
if count not in self._messages_at_reject_count:
|
||||
return
|
||||
|
||||
message = self._messages_at_backtrack[count]
|
||||
message = self._messages_at_reject_count[count]
|
||||
logger.info("INFO: %s", message.format(package_name=candidate.name))
|
||||
|
||||
msg = "Will try a different candidate, due to conflict:"
|
||||
for req_info in criterion.information:
|
||||
req, parent = req_info.requirement, req_info.parent
|
||||
# Inspired by Factory.get_installation_error
|
||||
msg += "\n "
|
||||
if parent:
|
||||
msg += f"{parent.name} {parent.version} depends on "
|
||||
else:
|
||||
msg += "The user requested "
|
||||
msg += req.format_for_error()
|
||||
logger.debug(msg)
|
||||
|
||||
|
||||
class PipDebuggingReporter(BaseReporter):
|
||||
"""A reporter that does an info log for every event it sees."""
|
||||
@@ -61,8 +73,8 @@ class PipDebuggingReporter(BaseReporter):
|
||||
def adding_requirement(self, requirement: Requirement, parent: Candidate) -> None:
|
||||
logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent)
|
||||
|
||||
def backtracking(self, candidate: Candidate) -> None:
|
||||
logger.info("Reporter.backtracking(%r)", candidate)
|
||||
def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None:
|
||||
logger.info("Reporter.rejecting_candidate(%r, %r)", criterion, candidate)
|
||||
|
||||
def pinning(self, candidate: Candidate) -> None:
|
||||
logger.info("Reporter.pinning(%r)", candidate)
|
||||
|
||||
@@ -64,7 +64,6 @@ class SpecifierRequirement(Requirement):
|
||||
return format_name(self.project_name, self._extras)
|
||||
|
||||
def format_for_error(self) -> str:
|
||||
|
||||
# Convert comma-separated specifiers into "A, B, ..., F and G"
|
||||
# This makes the specifier a bit more "human readable", without
|
||||
# risking a change in meaning. (Hopefully! Not all edge cases have
|
||||
|
||||
@@ -88,9 +88,9 @@ class Resolver(BaseResolver):
|
||||
)
|
||||
|
||||
try:
|
||||
try_to_avoid_resolution_too_deep = 2000000
|
||||
limit_how_complex_resolution_can_be = 200000
|
||||
result = self._result = resolver.resolve(
|
||||
collected.requirements, max_rounds=try_to_avoid_resolution_too_deep
|
||||
collected.requirements, max_rounds=limit_how_complex_resolution_can_be
|
||||
)
|
||||
|
||||
except ResolutionImpossible as e:
|
||||
|
||||
@@ -118,71 +118,3 @@ def deprecated(
|
||||
raise PipDeprecationWarning(message)
|
||||
|
||||
warnings.warn(message, category=PipDeprecationWarning, stacklevel=2)
|
||||
|
||||
|
||||
class LegacyInstallReason:
|
||||
def __init__(
|
||||
self,
|
||||
reason: str,
|
||||
replacement: Optional[str] = None,
|
||||
gone_in: Optional[str] = None,
|
||||
feature_flag: Optional[str] = None,
|
||||
issue: Optional[int] = None,
|
||||
emit_after_success: bool = False,
|
||||
emit_before_install: bool = False,
|
||||
):
|
||||
self._reason = reason
|
||||
self._replacement = replacement
|
||||
self._gone_in = gone_in
|
||||
self._feature_flag = feature_flag
|
||||
self._issue = issue
|
||||
self.emit_after_success = emit_after_success
|
||||
self.emit_before_install = emit_before_install
|
||||
|
||||
def emit_deprecation(self, name: str) -> None:
|
||||
deprecated(
|
||||
reason=self._reason.format(name=name),
|
||||
replacement=self._replacement,
|
||||
gone_in=self._gone_in,
|
||||
feature_flag=self._feature_flag,
|
||||
issue=self._issue,
|
||||
)
|
||||
|
||||
|
||||
LegacyInstallReasonFailedBdistWheel = LegacyInstallReason(
|
||||
reason=(
|
||||
"{name} was installed using the legacy 'setup.py install' "
|
||||
"method, because a wheel could not be built for it."
|
||||
),
|
||||
replacement="to fix the wheel build issue reported above",
|
||||
gone_in="23.1",
|
||||
issue=8368,
|
||||
emit_after_success=True,
|
||||
)
|
||||
|
||||
|
||||
LegacyInstallReasonMissingWheelPackage = LegacyInstallReason(
|
||||
reason=(
|
||||
"{name} is being installed using the legacy "
|
||||
"'setup.py install' method, because it does not have a "
|
||||
"'pyproject.toml' and the 'wheel' package "
|
||||
"is not installed."
|
||||
),
|
||||
replacement="to enable the '--use-pep517' option",
|
||||
gone_in="23.1",
|
||||
issue=8559,
|
||||
emit_before_install=True,
|
||||
)
|
||||
|
||||
LegacyInstallReasonNoBinaryForcesSetuptoolsInstall = LegacyInstallReason(
|
||||
reason=(
|
||||
"{name} is being installed using the legacy "
|
||||
"'setup.py install' method, because the '--no-binary' option was enabled "
|
||||
"for it and this currently disables local wheel building for projects that "
|
||||
"don't have a 'pyproject.toml' file."
|
||||
),
|
||||
replacement="to enable the '--use-pep517' option",
|
||||
gone_in="23.1",
|
||||
issue=11451,
|
||||
emit_before_install=True,
|
||||
)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
from getopt import GetoptError, getopt
|
||||
from typing import Dict, List
|
||||
|
||||
_options = [
|
||||
"exec-prefix=",
|
||||
"home=",
|
||||
"install-base=",
|
||||
"install-data=",
|
||||
"install-headers=",
|
||||
"install-lib=",
|
||||
"install-platlib=",
|
||||
"install-purelib=",
|
||||
"install-scripts=",
|
||||
"prefix=",
|
||||
"root=",
|
||||
"user",
|
||||
]
|
||||
|
||||
|
||||
def parse_distutils_args(args: List[str]) -> Dict[str, str]:
|
||||
"""Parse provided arguments, returning an object that has the matched arguments.
|
||||
|
||||
Any unknown arguments are ignored.
|
||||
"""
|
||||
result = {}
|
||||
for arg in args:
|
||||
try:
|
||||
parsed_opt, _ = getopt(args=[arg], shortopts="", longopts=_options)
|
||||
except GetoptError:
|
||||
# We don't care about any other options, which here may be
|
||||
# considered unrecognized since our option list is not
|
||||
# exhaustive.
|
||||
continue
|
||||
|
||||
if not parsed_opt:
|
||||
continue
|
||||
|
||||
option = parsed_opt[0]
|
||||
name_from_parsed = option[0][2:].replace("-", "_")
|
||||
value_from_parsed = option[1] or "true"
|
||||
result[name_from_parsed] = value_from_parsed
|
||||
|
||||
return result
|
||||
@@ -105,6 +105,13 @@ class Hashes:
|
||||
with open(path, "rb") as file:
|
||||
return self.check_against_file(file)
|
||||
|
||||
def has_one_of(self, hashes: Dict[str, str]) -> bool:
|
||||
"""Return whether any of the given hashes are allowed."""
|
||||
for hash_name, hex_digest in hashes.items():
|
||||
if self.is_hash_allowed(hash_name, hex_digest):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""Return whether I know any known-good hashes."""
|
||||
return bool(self._allowed)
|
||||
|
||||
@@ -32,6 +32,7 @@ from typing import (
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
@@ -614,18 +615,6 @@ def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]:
|
||||
return h, length
|
||||
|
||||
|
||||
def is_wheel_installed() -> bool:
|
||||
"""
|
||||
Return whether the wheel package is installed.
|
||||
"""
|
||||
try:
|
||||
import wheel # noqa: F401
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]:
|
||||
"""
|
||||
Return paired elements.
|
||||
@@ -669,7 +658,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
def build_wheel(
|
||||
self,
|
||||
wheel_directory: str,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
metadata_directory: Optional[str] = None,
|
||||
) -> str:
|
||||
cs = self.config_holder.config_settings
|
||||
@@ -678,7 +667,9 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
)
|
||||
|
||||
def build_sdist(
|
||||
self, sdist_directory: str, config_settings: Optional[Dict[str, str]] = None
|
||||
self,
|
||||
sdist_directory: str,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
) -> str:
|
||||
cs = self.config_holder.config_settings
|
||||
return super().build_sdist(sdist_directory, config_settings=cs)
|
||||
@@ -686,7 +677,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
def build_editable(
|
||||
self,
|
||||
wheel_directory: str,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
metadata_directory: Optional[str] = None,
|
||||
) -> str:
|
||||
cs = self.config_holder.config_settings
|
||||
@@ -695,19 +686,19 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
)
|
||||
|
||||
def get_requires_for_build_wheel(
|
||||
self, config_settings: Optional[Dict[str, str]] = None
|
||||
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
|
||||
) -> List[str]:
|
||||
cs = self.config_holder.config_settings
|
||||
return super().get_requires_for_build_wheel(config_settings=cs)
|
||||
|
||||
def get_requires_for_build_sdist(
|
||||
self, config_settings: Optional[Dict[str, str]] = None
|
||||
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
|
||||
) -> List[str]:
|
||||
cs = self.config_holder.config_settings
|
||||
return super().get_requires_for_build_sdist(config_settings=cs)
|
||||
|
||||
def get_requires_for_build_editable(
|
||||
self, config_settings: Optional[Dict[str, str]] = None
|
||||
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
|
||||
) -> List[str]:
|
||||
cs = self.config_holder.config_settings
|
||||
return super().get_requires_for_build_editable(config_settings=cs)
|
||||
@@ -715,7 +706,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
def prepare_metadata_for_build_wheel(
|
||||
self,
|
||||
metadata_directory: str,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
_allow_fallback: bool = True,
|
||||
) -> str:
|
||||
cs = self.config_holder.config_settings
|
||||
@@ -728,7 +719,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
def prepare_metadata_for_build_editable(
|
||||
self,
|
||||
metadata_directory: str,
|
||||
config_settings: Optional[Dict[str, str]] = None,
|
||||
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
|
||||
_allow_fallback: bool = True,
|
||||
) -> str:
|
||||
cs = self.config_holder.config_settings
|
||||
|
||||
@@ -103,8 +103,8 @@ def make_setuptools_clean_args(
|
||||
|
||||
def make_setuptools_develop_args(
|
||||
setup_py_path: str,
|
||||
*,
|
||||
global_options: Sequence[str],
|
||||
install_options: Sequence[str],
|
||||
no_user_config: bool,
|
||||
prefix: Optional[str],
|
||||
home: Optional[str],
|
||||
@@ -120,8 +120,6 @@ def make_setuptools_develop_args(
|
||||
|
||||
args += ["develop", "--no-deps"]
|
||||
|
||||
args += install_options
|
||||
|
||||
if prefix:
|
||||
args += ["--prefix", prefix]
|
||||
if home is not None:
|
||||
@@ -146,50 +144,3 @@ def make_setuptools_egg_info_args(
|
||||
args += ["--egg-base", egg_info_dir]
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def make_setuptools_install_args(
|
||||
setup_py_path: str,
|
||||
global_options: Sequence[str],
|
||||
install_options: Sequence[str],
|
||||
record_filename: str,
|
||||
root: Optional[str],
|
||||
prefix: Optional[str],
|
||||
header_dir: Optional[str],
|
||||
home: Optional[str],
|
||||
use_user_site: bool,
|
||||
no_user_config: bool,
|
||||
pycompile: bool,
|
||||
) -> List[str]:
|
||||
assert not (use_user_site and prefix)
|
||||
assert not (use_user_site and root)
|
||||
|
||||
args = make_setuptools_shim_args(
|
||||
setup_py_path,
|
||||
global_options=global_options,
|
||||
no_user_config=no_user_config,
|
||||
unbuffered_output=True,
|
||||
)
|
||||
args += ["install", "--record", record_filename]
|
||||
args += ["--single-version-externally-managed"]
|
||||
|
||||
if root is not None:
|
||||
args += ["--root", root]
|
||||
if prefix is not None:
|
||||
args += ["--prefix", prefix]
|
||||
if home is not None:
|
||||
args += ["--home", home]
|
||||
if use_user_site:
|
||||
args += ["--user", "--prefix="]
|
||||
|
||||
if pycompile:
|
||||
args += ["--compile"]
|
||||
else:
|
||||
args += ["--no-compile"]
|
||||
|
||||
if header_dir:
|
||||
args += ["--install-headers", header_dir]
|
||||
|
||||
args += install_options
|
||||
|
||||
return args
|
||||
|
||||
@@ -5,7 +5,7 @@ import logging
|
||||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
from typing import Callable, Iterable, List, Optional, Tuple
|
||||
from typing import Iterable, List, Optional, Tuple
|
||||
|
||||
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name, canonicalize_version
|
||||
from pipenv.patched.pip._vendor.packaging.version import InvalidVersion, Version
|
||||
@@ -19,12 +19,8 @@ from pipenv.patched.pip._internal.operations.build.wheel import build_wheel_pep5
|
||||
from pipenv.patched.pip._internal.operations.build.wheel_editable import build_wheel_editable
|
||||
from pipenv.patched.pip._internal.operations.build.wheel_legacy import build_wheel_legacy
|
||||
from pipenv.patched.pip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.pip._internal.utils.deprecation import (
|
||||
LegacyInstallReasonMissingWheelPackage,
|
||||
LegacyInstallReasonNoBinaryForcesSetuptoolsInstall,
|
||||
)
|
||||
from pipenv.patched.pip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed
|
||||
from pipenv.patched.pip._internal.utils.misc import ensure_dir, hash_file
|
||||
from pipenv.patched.pip._internal.utils.setuptools_build import make_setuptools_clean_args
|
||||
from pipenv.patched.pip._internal.utils.subprocess import call_subprocess
|
||||
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory
|
||||
@@ -35,7 +31,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE)
|
||||
|
||||
BdistWheelAllowedPredicate = Callable[[InstallRequirement], bool]
|
||||
BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]]
|
||||
|
||||
|
||||
@@ -50,7 +45,6 @@ def _contains_egg_info(s: str) -> bool:
|
||||
def _should_build(
|
||||
req: InstallRequirement,
|
||||
need_wheel: bool,
|
||||
check_bdist_wheel: Optional[BdistWheelAllowedPredicate] = None,
|
||||
) -> bool:
|
||||
"""Return whether an InstallRequirement should be built into a wheel."""
|
||||
if req.constraint:
|
||||
@@ -78,24 +72,6 @@ def _should_build(
|
||||
# we only build PEP 660 editable requirements
|
||||
return req.supports_pyproject_editable()
|
||||
|
||||
if req.use_pep517:
|
||||
return True
|
||||
|
||||
assert check_bdist_wheel is not None
|
||||
if not check_bdist_wheel(req):
|
||||
# /!\ When we change this to unconditionally return True, we must also remove
|
||||
# support for `--install-option`. Indeed, `--install-option` implies
|
||||
# `--no-binary` so we can return False here and run `setup.py install`.
|
||||
# `--global-option` and `--build-option` can remain until we drop support for
|
||||
# building with `setup.py bdist_wheel`.
|
||||
req.legacy_install_reason = LegacyInstallReasonNoBinaryForcesSetuptoolsInstall
|
||||
return False
|
||||
|
||||
if not is_wheel_installed():
|
||||
# we don't build legacy requirements if wheel is not installed
|
||||
req.legacy_install_reason = LegacyInstallReasonMissingWheelPackage
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -107,11 +83,8 @@ def should_build_for_wheel_command(
|
||||
|
||||
def should_build_for_install_command(
|
||||
req: InstallRequirement,
|
||||
check_bdist_wheel_allowed: BdistWheelAllowedPredicate,
|
||||
) -> bool:
|
||||
return _should_build(
|
||||
req, need_wheel=False, check_bdist_wheel=check_bdist_wheel_allowed
|
||||
)
|
||||
return _should_build(req, need_wheel=False)
|
||||
|
||||
|
||||
def _should_cache(
|
||||
|
||||
@@ -6,8 +6,8 @@ import os
|
||||
import sys
|
||||
|
||||
|
||||
version = (1, 0, 4)
|
||||
__version__ = "1.0.4"
|
||||
version = (1, 0, 5)
|
||||
__version__ = "1.0.5"
|
||||
|
||||
|
||||
if os.environ.get("MSGPACK_PUREPYTHON") or sys.version_info[0] == 2:
|
||||
|
||||
@@ -56,7 +56,7 @@ class Timestamp(object):
|
||||
Note: Negative times (before the UNIX epoch) are represented as negative seconds + positive ns.
|
||||
"""
|
||||
if not isinstance(seconds, int_types):
|
||||
raise TypeError("seconds must be an interger")
|
||||
raise TypeError("seconds must be an integer")
|
||||
if not isinstance(nanoseconds, int_types):
|
||||
raise TypeError("nanoseconds must be an integer")
|
||||
if not (0 <= nanoseconds < 10**9):
|
||||
|
||||
@@ -814,7 +814,7 @@ class Packer(object):
|
||||
self._pack_raw_header(n)
|
||||
return self._buffer.write(obj)
|
||||
if check(obj, memoryview):
|
||||
n = len(obj) * obj.itemsize
|
||||
n = obj.nbytes
|
||||
if n >= 2**32:
|
||||
raise ValueError("Memoryview is too large")
|
||||
self._pack_bin_header(n)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
import os
|
||||
import errno
|
||||
import sys
|
||||
|
||||
from pipenv.patched.pip._vendor import six
|
||||
|
||||
|
||||
def _makedirs_31(path, exist_ok=False):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as exc:
|
||||
if not exist_ok or exc.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
|
||||
# rely on compatibility behavior until mode considerations
|
||||
# and exists_ok considerations are disentangled.
|
||||
# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663
|
||||
needs_makedirs = (
|
||||
six.PY2 or
|
||||
(3, 4) <= sys.version_info < (3, 4, 1)
|
||||
)
|
||||
makedirs = _makedirs_31 if needs_makedirs else os.makedirs
|
||||
@@ -27,7 +27,6 @@ def _set_platform_dir_class() -> type[PlatformDirsABC]:
|
||||
from pipenv.patched.pip._vendor.platformdirs.unix import Unix as Result
|
||||
|
||||
if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
|
||||
|
||||
if os.getenv("SHELL") or os.getenv("PREFIX"):
|
||||
return Result
|
||||
|
||||
@@ -50,15 +49,23 @@ def user_data_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: data directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
roaming=roaming,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_data_dir
|
||||
|
||||
|
||||
def site_data_dir(
|
||||
@@ -66,15 +73,23 @@ def site_data_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: data directory shared by users
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
multipath=multipath,
|
||||
ensure_exists=ensure_exists,
|
||||
).site_data_dir
|
||||
|
||||
|
||||
def user_config_dir(
|
||||
@@ -82,15 +97,23 @@ def user_config_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: config directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
roaming=roaming,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_config_dir
|
||||
|
||||
|
||||
def site_config_dir(
|
||||
@@ -98,15 +121,23 @@ def site_config_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: config directory shared by the users
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
multipath=multipath,
|
||||
ensure_exists=ensure_exists,
|
||||
).site_config_dir
|
||||
|
||||
|
||||
def user_cache_dir(
|
||||
@@ -114,15 +145,47 @@ def user_cache_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: cache directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_cache_dir
|
||||
|
||||
|
||||
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,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: cache directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).site_cache_dir
|
||||
|
||||
|
||||
def user_state_dir(
|
||||
@@ -130,15 +193,23 @@ def user_state_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: state directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
roaming=roaming,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_state_dir
|
||||
|
||||
|
||||
def user_log_dir(
|
||||
@@ -146,15 +217,23 @@ def user_log_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: log directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_log_dir
|
||||
|
||||
|
||||
def user_documents_dir() -> str:
|
||||
@@ -169,15 +248,23 @@ def user_runtime_dir(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: runtime directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_dir
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_runtime_dir
|
||||
|
||||
|
||||
def user_data_path(
|
||||
@@ -185,15 +272,23 @@ def user_data_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: data path tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
roaming=roaming,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_data_path
|
||||
|
||||
|
||||
def site_data_path(
|
||||
@@ -201,15 +296,23 @@ def site_data_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: data path shared by users
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
multipath=multipath,
|
||||
ensure_exists=ensure_exists,
|
||||
).site_data_path
|
||||
|
||||
|
||||
def user_config_path(
|
||||
@@ -217,15 +320,23 @@ def user_config_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: config path tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
roaming=roaming,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_config_path
|
||||
|
||||
|
||||
def site_config_path(
|
||||
@@ -233,15 +344,47 @@ def site_config_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
multipath: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param multipath: See `roaming <platformdirs.api.PlatformDirsABC.multipath>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: config path shared by the users
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
multipath=multipath,
|
||||
ensure_exists=ensure_exists,
|
||||
).site_config_path
|
||||
|
||||
|
||||
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,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: cache directory tied to the user
|
||||
"""
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).site_cache_path
|
||||
|
||||
|
||||
def user_cache_path(
|
||||
@@ -249,15 +392,23 @@ def user_cache_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: cache path tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_cache_path
|
||||
|
||||
|
||||
def user_state_path(
|
||||
@@ -265,15 +416,23 @@ def user_state_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
roaming: bool = False,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: state path tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
roaming=roaming,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_state_path
|
||||
|
||||
|
||||
def user_log_path(
|
||||
@@ -281,15 +440,23 @@ def user_log_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `roaming <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: log path tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_log_path
|
||||
|
||||
|
||||
def user_documents_path() -> Path:
|
||||
@@ -304,15 +471,23 @@ def user_runtime_path(
|
||||
appauthor: str | None | Literal[False] = None,
|
||||
version: str | None = None,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
) -> Path:
|
||||
"""
|
||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||
:param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
|
||||
:param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
:param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
:param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
:returns: runtime path tied to the user
|
||||
"""
|
||||
return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_path
|
||||
return PlatformDirs(
|
||||
appname=appname,
|
||||
appauthor=appauthor,
|
||||
version=version,
|
||||
opinion=opinion,
|
||||
ensure_exists=ensure_exists,
|
||||
).user_runtime_path
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -330,6 +505,7 @@ __all__ = [
|
||||
"user_runtime_dir",
|
||||
"site_data_dir",
|
||||
"site_config_dir",
|
||||
"site_cache_dir",
|
||||
"user_data_path",
|
||||
"user_config_path",
|
||||
"user_cache_path",
|
||||
@@ -339,4 +515,5 @@ __all__ = [
|
||||
"user_runtime_path",
|
||||
"site_data_path",
|
||||
"site_config_path",
|
||||
"site_cache_path",
|
||||
]
|
||||
|
||||
@@ -12,6 +12,7 @@ PROPS = (
|
||||
"user_runtime_dir",
|
||||
"site_data_dir",
|
||||
"site_config_dir",
|
||||
"site_cache_dir",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@ from .api import PlatformDirsABC
|
||||
class Android(PlatformDirsABC):
|
||||
"""
|
||||
Follows the guidance `from here <https://android.stackexchange.com/a/216132>`_. Makes use of the
|
||||
`appname <platformdirs.api.PlatformDirsABC.appname>` and
|
||||
`version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
`appname <platformdirs.api.PlatformDirsABC.appname>`,
|
||||
`version <platformdirs.api.PlatformDirsABC.version>`,
|
||||
`ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
"""
|
||||
|
||||
@property
|
||||
@@ -43,6 +44,11 @@ class Android(PlatformDirsABC):
|
||||
""":return: cache directory tied to the user, e.g. e.g. ``/data/user/<userid>/<packagename>/cache/<AppName>``"""
|
||||
return self._append_app_name_and_version(cast(str, _android_folder()), "cache")
|
||||
|
||||
@property
|
||||
def site_cache_dir(self) -> str:
|
||||
""":return: cache directory shared by users, same as `user_cache_dir`"""
|
||||
return self.user_cache_dir
|
||||
|
||||
@property
|
||||
def user_state_dir(self) -> str:
|
||||
""":return: state directory tied to the user, same as `user_data_dir`"""
|
||||
|
||||
@@ -22,6 +22,7 @@ class PlatformDirsABC(ABC):
|
||||
roaming: bool = False,
|
||||
multipath: bool = False,
|
||||
opinion: bool = True,
|
||||
ensure_exists: bool = False,
|
||||
):
|
||||
"""
|
||||
Create a new platform directory.
|
||||
@@ -32,6 +33,7 @@ class PlatformDirsABC(ABC):
|
||||
:param roaming: See `roaming`.
|
||||
:param multipath: See `multipath`.
|
||||
:param opinion: See `opinion`.
|
||||
:param ensure_exists: See `ensure_exists`.
|
||||
"""
|
||||
self.appname = appname #: The name of application.
|
||||
self.appauthor = appauthor
|
||||
@@ -56,6 +58,11 @@ class PlatformDirsABC(ABC):
|
||||
returned. By default, the first item would only be returned.
|
||||
"""
|
||||
self.opinion = opinion #: A flag to indicating to use opinionated values.
|
||||
self.ensure_exists = ensure_exists
|
||||
"""
|
||||
Optionally create the directory (and any missing parents) upon access if it does not exist.
|
||||
By default, no directories are created.
|
||||
"""
|
||||
|
||||
def _append_app_name_and_version(self, *base: str) -> str:
|
||||
params = list(base[1:])
|
||||
@@ -63,7 +70,13 @@ class PlatformDirsABC(ABC):
|
||||
params.append(self.appname)
|
||||
if self.version:
|
||||
params.append(self.version)
|
||||
return os.path.join(base[0], *params)
|
||||
path = os.path.join(base[0], *params)
|
||||
self._optionally_create_directory(path)
|
||||
return path
|
||||
|
||||
def _optionally_create_directory(self, path: str) -> None:
|
||||
if self.ensure_exists:
|
||||
Path(path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
@@ -90,6 +103,11 @@ class PlatformDirsABC(ABC):
|
||||
def user_cache_dir(self) -> str:
|
||||
""":return: cache directory tied to the user"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def site_cache_dir(self) -> str:
|
||||
""":return: cache directory shared by users"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def user_state_dir(self) -> str:
|
||||
@@ -135,6 +153,11 @@ class PlatformDirsABC(ABC):
|
||||
""":return: cache path tied to the user"""
|
||||
return Path(self.user_cache_dir)
|
||||
|
||||
@property
|
||||
def site_cache_path(self) -> Path:
|
||||
""":return: cache path shared by users"""
|
||||
return Path(self.site_cache_dir)
|
||||
|
||||
@property
|
||||
def user_state_path(self) -> Path:
|
||||
""":return: state path tied to the user"""
|
||||
|
||||
@@ -9,14 +9,15 @@ class MacOS(PlatformDirsABC):
|
||||
"""
|
||||
Platform directories for the macOS operating system. Follows the guidance from `Apple documentation
|
||||
<https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
|
||||
Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>` and
|
||||
`version <platformdirs.api.PlatformDirsABC.version>`.
|
||||
Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`,
|
||||
`version <platformdirs.api.PlatformDirsABC.version>`,
|
||||
`ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
"""
|
||||
|
||||
@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"))
|
||||
|
||||
@property
|
||||
def site_data_dir(self) -> str:
|
||||
@@ -25,19 +26,24 @@ class MacOS(PlatformDirsABC):
|
||||
|
||||
@property
|
||||
def user_config_dir(self) -> str:
|
||||
""":return: config directory tied to the user, e.g. ``~/Library/Preferences/$appname/$version``"""
|
||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences/"))
|
||||
""":return: config directory tied to the user, same as `user_data_dir`"""
|
||||
return self.user_data_dir
|
||||
|
||||
@property
|
||||
def site_config_dir(self) -> str:
|
||||
""":return: config directory shared by the users, e.g. ``/Library/Preferences/$appname``"""
|
||||
return self._append_app_name_and_version("/Library/Preferences")
|
||||
""":return: config directory shared by the users, same as `site_data_dir`"""
|
||||
return self.site_data_dir
|
||||
|
||||
@property
|
||||
def user_cache_dir(self) -> str:
|
||||
""":return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
|
||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))
|
||||
|
||||
@property
|
||||
def site_cache_dir(self) -> str:
|
||||
""":return: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``"""
|
||||
return self._append_app_name_and_version("/Library/Caches")
|
||||
|
||||
@property
|
||||
def user_state_dir(self) -> str:
|
||||
""":return: state directory tied to the user, same as `user_data_dir`"""
|
||||
|
||||
@@ -24,7 +24,8 @@ class Unix(PlatformDirsABC):
|
||||
`appname <platformdirs.api.PlatformDirsABC.appname>`,
|
||||
`version <platformdirs.api.PlatformDirsABC.version>`,
|
||||
`multipath <platformdirs.api.PlatformDirsABC.multipath>`,
|
||||
`opinion <platformdirs.api.PlatformDirsABC.opinion>`.
|
||||
`opinion <platformdirs.api.PlatformDirsABC.opinion>`,
|
||||
`ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
"""
|
||||
|
||||
@property
|
||||
@@ -93,6 +94,13 @@ class Unix(PlatformDirsABC):
|
||||
path = os.path.expanduser("~/.cache")
|
||||
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")
|
||||
|
||||
@property
|
||||
def user_state_dir(self) -> str:
|
||||
"""
|
||||
@@ -148,6 +156,11 @@ class Unix(PlatformDirsABC):
|
||||
""":return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``"""
|
||||
return self._first_item_as_path_if_multipath(self.site_config_dir)
|
||||
|
||||
@property
|
||||
def site_cache_path(self) -> Path:
|
||||
""":return: cache path shared by users. Only return first item, even if ``multipath`` is set to ``True``"""
|
||||
return self._first_item_as_path_if_multipath(self.site_cache_dir)
|
||||
|
||||
def _first_item_as_path_if_multipath(self, directory: str) -> Path:
|
||||
if self.multipath:
|
||||
# If multipath is True, the first path is returned.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# file generated by setuptools_scm
|
||||
# don't change, don't track in version control
|
||||
__version__ = version = '2.6.2'
|
||||
__version_tuple__ = version_tuple = (2, 6, 2)
|
||||
__version__ = version = '3.2.0'
|
||||
__version_tuple__ = version_tuple = (3, 2, 0)
|
||||
|
||||
@@ -17,7 +17,9 @@ class Windows(PlatformDirsABC):
|
||||
`appauthor <platformdirs.api.PlatformDirsABC.appauthor>`,
|
||||
`version <platformdirs.api.PlatformDirsABC.version>`,
|
||||
`roaming <platformdirs.api.PlatformDirsABC.roaming>`,
|
||||
`opinion <platformdirs.api.PlatformDirsABC.opinion>`."""
|
||||
`opinion <platformdirs.api.PlatformDirsABC.opinion>`,
|
||||
`ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
|
||||
"""
|
||||
|
||||
@property
|
||||
def user_data_dir(self) -> str:
|
||||
@@ -41,7 +43,9 @@ class Windows(PlatformDirsABC):
|
||||
params.append(opinion_value)
|
||||
if self.version:
|
||||
params.append(self.version)
|
||||
return os.path.join(path, *params)
|
||||
path = os.path.join(path, *params)
|
||||
self._optionally_create_directory(path)
|
||||
return path
|
||||
|
||||
@property
|
||||
def site_data_dir(self) -> str:
|
||||
@@ -68,6 +72,12 @@ class Windows(PlatformDirsABC):
|
||||
path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA"))
|
||||
return self._append_parts(path, opinion_value="Cache")
|
||||
|
||||
@property
|
||||
def site_cache_dir(self) -> str:
|
||||
""":return: cache directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname\\Cache\\$version``"""
|
||||
path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
|
||||
return self._append_parts(path, opinion_value="Cache")
|
||||
|
||||
@property
|
||||
def user_state_dir(self) -> str:
|
||||
""":return: state directory tied to the user, same as `user_data_dir`"""
|
||||
@@ -81,6 +91,7 @@ class Windows(PlatformDirsABC):
|
||||
path = self.user_data_dir
|
||||
if self.opinion:
|
||||
path = os.path.join(path, "Logs")
|
||||
self._optionally_create_directory(path)
|
||||
return path
|
||||
|
||||
@property
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"""
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
__version__ = '2.13.0'
|
||||
__version__ = '2.14.0'
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
__all__ = ['lex', 'format', 'highlight']
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
from fnmatch import fnmatch
|
||||
|
||||
@@ -878,10 +878,12 @@ class HtmlFormatter(Formatter):
|
||||
# for all but the last line
|
||||
for part in parts[:-1]:
|
||||
if line:
|
||||
if lspan != cspan:
|
||||
# Also check for part being non-empty, so we avoid creating
|
||||
# empty <span> tags
|
||||
if lspan != cspan and part:
|
||||
line.extend(((lspan and '</span>'), cspan, part,
|
||||
(cspan and '</span>'), lsep))
|
||||
else: # both are the same
|
||||
else: # both are the same, or the current part was empty
|
||||
line.extend((part, (lspan and '</span>'), lsep))
|
||||
yield 1, ''.join(line)
|
||||
line = []
|
||||
|
||||
@@ -128,38 +128,12 @@ class IRCFormatter(Formatter):
|
||||
self._lineno = 0
|
||||
|
||||
def _write_lineno(self, outfile):
|
||||
self._lineno += 1
|
||||
outfile.write("\n%04d: " % self._lineno)
|
||||
|
||||
def _format_unencoded_with_lineno(self, tokensource, outfile):
|
||||
self._write_lineno(outfile)
|
||||
|
||||
for ttype, value in tokensource:
|
||||
if value.endswith("\n"):
|
||||
self._write_lineno(outfile)
|
||||
value = value[:-1]
|
||||
color = self.colorscheme.get(ttype)
|
||||
while color is None:
|
||||
ttype = ttype.parent
|
||||
color = self.colorscheme.get(ttype)
|
||||
if color:
|
||||
color = color[self.darkbg]
|
||||
spl = value.split('\n')
|
||||
for line in spl[:-1]:
|
||||
self._write_lineno(outfile)
|
||||
if line:
|
||||
outfile.write(ircformat(color, line[:-1]))
|
||||
if spl[-1]:
|
||||
outfile.write(ircformat(color, spl[-1]))
|
||||
else:
|
||||
outfile.write(value)
|
||||
|
||||
outfile.write("\n")
|
||||
if self.linenos:
|
||||
self._lineno += 1
|
||||
outfile.write("%04d: " % self._lineno)
|
||||
|
||||
def format_unencoded(self, tokensource, outfile):
|
||||
if self.linenos:
|
||||
self._format_unencoded_with_lineno(tokensource, outfile)
|
||||
return
|
||||
self._write_lineno(outfile)
|
||||
|
||||
for ttype, value in tokensource:
|
||||
color = self.colorscheme.get(ttype)
|
||||
@@ -173,6 +147,7 @@ class IRCFormatter(Formatter):
|
||||
if line:
|
||||
outfile.write(ircformat(color, line))
|
||||
outfile.write('\n')
|
||||
self._write_lineno(outfile)
|
||||
if spl[-1]:
|
||||
outfile.write(ircformat(color, spl[-1]))
|
||||
else:
|
||||
|
||||
@@ -14,15 +14,16 @@ import time
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.filter import apply_filters, Filter
|
||||
from pipenv.patched.pip._vendor.pygments.filters import get_filter_by_name
|
||||
from pipenv.patched.pip._vendor.pygments.token import Error, Text, Other, _TokenType
|
||||
from pipenv.patched.pip._vendor.pygments.token import Error, Text, Other, Whitespace, _TokenType
|
||||
from pipenv.patched.pip._vendor.pygments.util import get_bool_opt, get_int_opt, get_list_opt, \
|
||||
make_analysator, Future, guess_decode
|
||||
from pipenv.patched.pip._vendor.pygments.regexopt import regex_opt
|
||||
|
||||
__all__ = ['Lexer', 'RegexLexer', 'ExtendedRegexLexer', 'DelegatingLexer',
|
||||
'LexerContext', 'include', 'inherit', 'bygroups', 'using', 'this',
|
||||
'default', 'words']
|
||||
'default', 'words', 'line_re']
|
||||
|
||||
line_re = re.compile('.*?\n')
|
||||
|
||||
_encoding_map = [(b'\xef\xbb\xbf', 'utf-8'),
|
||||
(b'\xff\xfe\0\0', 'utf-32'),
|
||||
@@ -670,7 +671,7 @@ class RegexLexer(Lexer, metaclass=RegexLexerMeta):
|
||||
# at EOL, reset state to "root"
|
||||
statestack = ['root']
|
||||
statetokens = tokendefs['root']
|
||||
yield pos, Text, '\n'
|
||||
yield pos, Whitespace, '\n'
|
||||
pos += 1
|
||||
continue
|
||||
yield pos, Error, text[pos]
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
from fnmatch import fnmatch
|
||||
|
||||
@@ -30,6 +30,7 @@ LEXERS = {
|
||||
'AppleScriptLexer': ('pipenv.patched.pip._vendor.pygments.lexers.scripting', 'AppleScript', ('applescript',), ('*.applescript',), ()),
|
||||
'ArduinoLexer': ('pipenv.patched.pip._vendor.pygments.lexers.c_like', 'Arduino', ('arduino',), ('*.ino',), ('text/x-arduino',)),
|
||||
'ArrowLexer': ('pipenv.patched.pip._vendor.pygments.lexers.arrow', 'Arrow', ('arrow',), ('*.arw',), ()),
|
||||
'ArturoLexer': ('pipenv.patched.pip._vendor.pygments.lexers.arturo', 'Arturo', ('arturo', 'art'), ('*.art',), ()),
|
||||
'AscLexer': ('pipenv.patched.pip._vendor.pygments.lexers.asc', 'ASCII armored', ('asc', 'pem'), ('*.asc', '*.pem', 'id_dsa', 'id_ecdsa', 'id_ecdsa_sk', 'id_ed25519', 'id_ed25519_sk', 'id_rsa'), ('application/pgp-keys', 'application/pgp-encrypted', 'application/pgp-signature')),
|
||||
'AspectJLexer': ('pipenv.patched.pip._vendor.pygments.lexers.jvm', 'AspectJ', ('aspectj',), ('*.aj',), ('text/x-aspectj',)),
|
||||
'AsymptoteLexer': ('pipenv.patched.pip._vendor.pygments.lexers.graphics', 'Asymptote', ('asymptote', 'asy'), ('*.asy',), ('text/x-asymptote',)),
|
||||
@@ -152,13 +153,14 @@ LEXERS = {
|
||||
'EvoqueXmlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'XML+Evoque', ('xml+evoque',), ('*.xml',), ('application/xml+evoque',)),
|
||||
'ExeclineLexer': ('pipenv.patched.pip._vendor.pygments.lexers.shell', 'execline', ('execline',), ('*.exec',), ()),
|
||||
'EzhilLexer': ('pipenv.patched.pip._vendor.pygments.lexers.ezhil', 'Ezhil', ('ezhil',), ('*.n',), ('text/x-ezhil',)),
|
||||
'FSharpLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dotnet', 'F#', ('fsharp', 'f#'), ('*.fs', '*.fsi'), ('text/x-fsharp',)),
|
||||
'FSharpLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dotnet', 'F#', ('fsharp', 'f#'), ('*.fs', '*.fsi', '*.fsx'), ('text/x-fsharp',)),
|
||||
'FStarLexer': ('pipenv.patched.pip._vendor.pygments.lexers.ml', 'FStar', ('fstar',), ('*.fst', '*.fsti'), ('text/x-fstar',)),
|
||||
'FactorLexer': ('pipenv.patched.pip._vendor.pygments.lexers.factor', 'Factor', ('factor',), ('*.factor',), ('text/x-factor',)),
|
||||
'FancyLexer': ('pipenv.patched.pip._vendor.pygments.lexers.ruby', 'Fancy', ('fancy', 'fy'), ('*.fy', '*.fancypack'), ('text/x-fancysrc',)),
|
||||
'FantomLexer': ('pipenv.patched.pip._vendor.pygments.lexers.fantom', 'Fantom', ('fan',), ('*.fan',), ('application/x-fantom',)),
|
||||
'FelixLexer': ('pipenv.patched.pip._vendor.pygments.lexers.felix', 'Felix', ('felix', 'flx'), ('*.flx', '*.flxh'), ('text/x-felix',)),
|
||||
'FennelLexer': ('pipenv.patched.pip._vendor.pygments.lexers.lisp', 'Fennel', ('fennel', 'fnl'), ('*.fnl',), ()),
|
||||
'FiftLexer': ('pipenv.patched.pip._vendor.pygments.lexers.fift', 'Fift', ('fift', 'fif'), ('*.fif',), ()),
|
||||
'FishShellLexer': ('pipenv.patched.pip._vendor.pygments.lexers.shell', 'Fish', ('fish', 'fishshell'), ('*.fish', '*.load'), ('application/x-fish',)),
|
||||
'FlatlineLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dsls', 'Flatline', ('flatline',), (), ('text/x-flatline',)),
|
||||
'FloScriptLexer': ('pipenv.patched.pip._vendor.pygments.lexers.floscript', 'FloScript', ('floscript', 'flo'), ('*.flo',), ()),
|
||||
@@ -167,7 +169,9 @@ LEXERS = {
|
||||
'FortranLexer': ('pipenv.patched.pip._vendor.pygments.lexers.fortran', 'Fortran', ('fortran', 'f90'), ('*.f03', '*.f90', '*.F03', '*.F90'), ('text/x-fortran',)),
|
||||
'FoxProLexer': ('pipenv.patched.pip._vendor.pygments.lexers.foxpro', 'FoxPro', ('foxpro', 'vfp', 'clipper', 'xbase'), ('*.PRG', '*.prg'), ()),
|
||||
'FreeFemLexer': ('pipenv.patched.pip._vendor.pygments.lexers.freefem', 'Freefem', ('freefem',), ('*.edp',), ('text/x-freefem',)),
|
||||
'FuncLexer': ('pipenv.patched.pip._vendor.pygments.lexers.func', 'FunC', ('func', 'fc'), ('*.fc', '*.func'), ()),
|
||||
'FutharkLexer': ('pipenv.patched.pip._vendor.pygments.lexers.futhark', 'Futhark', ('futhark',), ('*.fut',), ('text/x-futhark',)),
|
||||
'GAPConsoleLexer': ('pipenv.patched.pip._vendor.pygments.lexers.algebra', 'GAP session', ('gap-console', 'gap-repl'), ('*.tst',), ()),
|
||||
'GAPLexer': ('pipenv.patched.pip._vendor.pygments.lexers.algebra', 'GAP', ('gap',), ('*.g', '*.gd', '*.gi', '*.gap'), ()),
|
||||
'GDScriptLexer': ('pipenv.patched.pip._vendor.pygments.lexers.gdscript', 'GDScript', ('gdscript', 'gd'), ('*.gd',), ('text/x-gdscript', 'application/x-gdscript')),
|
||||
'GLShaderLexer': ('pipenv.patched.pip._vendor.pygments.lexers.graphics', 'GLSL', ('glsl',), ('*.vert', '*.frag', '*.geo'), ('text/x-glslsrc',)),
|
||||
@@ -196,7 +200,7 @@ LEXERS = {
|
||||
'HaxeLexer': ('pipenv.patched.pip._vendor.pygments.lexers.haxe', 'Haxe', ('haxe', 'hxsl', 'hx'), ('*.hx', '*.hxsl'), ('text/haxe', 'text/x-haxe', 'text/x-hx')),
|
||||
'HexdumpLexer': ('pipenv.patched.pip._vendor.pygments.lexers.hexdump', 'Hexdump', ('hexdump',), (), ()),
|
||||
'HsailLexer': ('pipenv.patched.pip._vendor.pygments.lexers.asm', 'HSAIL', ('hsail', 'hsa'), ('*.hsail',), ('text/x-hsail',)),
|
||||
'HspecLexer': ('pipenv.patched.pip._vendor.pygments.lexers.haskell', 'Hspec', ('hspec',), (), ()),
|
||||
'HspecLexer': ('pipenv.patched.pip._vendor.pygments.lexers.haskell', 'Hspec', ('hspec',), ('*Spec.hs',), ()),
|
||||
'HtmlDjangoLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'HTML+Django/Jinja', ('html+django', 'html+jinja', 'htmldjango'), ('*.html.j2', '*.htm.j2', '*.xhtml.j2', '*.html.jinja2', '*.htm.jinja2', '*.xhtml.jinja2'), ('text/html+django', 'text/html+jinja')),
|
||||
'HtmlGenshiLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'HTML+Genshi', ('html+genshi', 'html+kid'), (), ('text/html+genshi',)),
|
||||
'HtmlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.html', 'HTML', ('html',), ('*.html', '*.htm', '*.xhtml', '*.xslt'), ('text/html', 'application/xhtml+xml')),
|
||||
@@ -236,6 +240,7 @@ LEXERS = {
|
||||
'JsonBareObjectLexer': ('pipenv.patched.pip._vendor.pygments.lexers.data', 'JSONBareObject', (), (), ()),
|
||||
'JsonLdLexer': ('pipenv.patched.pip._vendor.pygments.lexers.data', 'JSON-LD', ('jsonld', 'json-ld'), ('*.jsonld',), ('application/ld+json',)),
|
||||
'JsonLexer': ('pipenv.patched.pip._vendor.pygments.lexers.data', 'JSON', ('json', 'json-object'), ('*.json', 'Pipfile.lock'), ('application/json', 'application/json-object')),
|
||||
'JsonnetLexer': ('pipenv.patched.pip._vendor.pygments.lexers.jsonnet', 'Jsonnet', ('jsonnet',), ('*.jsonnet', '*.libsonnet'), ()),
|
||||
'JspLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'Java Server Page', ('jsp',), ('*.jsp',), ('application/x-jsp',)),
|
||||
'JuliaConsoleLexer': ('pipenv.patched.pip._vendor.pygments.lexers.julia', 'Julia console', ('jlcon', 'julia-repl'), (), ()),
|
||||
'JuliaLexer': ('pipenv.patched.pip._vendor.pygments.lexers.julia', 'Julia', ('julia', 'jl'), ('*.jl',), ('text/x-julia', 'application/x-julia')),
|
||||
@@ -270,8 +275,10 @@ LEXERS = {
|
||||
'LogosLexer': ('pipenv.patched.pip._vendor.pygments.lexers.objective', 'Logos', ('logos',), ('*.x', '*.xi', '*.xm', '*.xmi'), ('text/x-logos',)),
|
||||
'LogtalkLexer': ('pipenv.patched.pip._vendor.pygments.lexers.prolog', 'Logtalk', ('logtalk',), ('*.lgt', '*.logtalk'), ('text/x-logtalk',)),
|
||||
'LuaLexer': ('pipenv.patched.pip._vendor.pygments.lexers.scripting', 'Lua', ('lua',), ('*.lua', '*.wlua'), ('text/x-lua', 'application/x-lua')),
|
||||
'MCFunctionLexer': ('pipenv.patched.pip._vendor.pygments.lexers.mcfunction', 'MCFunction', ('mcfunction', 'mcf'), ('*.mcfunction',), ('text/mcfunction',)),
|
||||
'MCFunctionLexer': ('pipenv.patched.pip._vendor.pygments.lexers.minecraft', 'MCFunction', ('mcfunction', 'mcf'), ('*.mcfunction',), ('text/mcfunction',)),
|
||||
'MCSchemaLexer': ('pipenv.patched.pip._vendor.pygments.lexers.minecraft', 'MCSchema', ('mcschema',), ('*.mcschema',), ('text/mcschema',)),
|
||||
'MIMELexer': ('pipenv.patched.pip._vendor.pygments.lexers.mime', 'MIME', ('mime',), (), ('multipart/mixed', 'multipart/related', 'multipart/alternative')),
|
||||
'MIPSLexer': ('pipenv.patched.pip._vendor.pygments.lexers.mips', 'MIPS', ('mips',), ('*.mips', '*.MIPS'), ()),
|
||||
'MOOCodeLexer': ('pipenv.patched.pip._vendor.pygments.lexers.scripting', 'MOOCode', ('moocode', 'moo'), ('*.moo',), ('text/x-moocode',)),
|
||||
'MSDOSSessionLexer': ('pipenv.patched.pip._vendor.pygments.lexers.shell', 'MSDOS Session', ('doscon',), (), ()),
|
||||
'Macaulay2Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.macaulay2', 'Macaulay2', ('macaulay2',), ('*.m2',), ()),
|
||||
@@ -316,7 +323,7 @@ LEXERS = {
|
||||
'MyghtyXmlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.templates', 'XML+Myghty', ('xml+myghty',), (), ('application/xml+myghty',)),
|
||||
'NCLLexer': ('pipenv.patched.pip._vendor.pygments.lexers.ncl', 'NCL', ('ncl',), ('*.ncl',), ('text/ncl',)),
|
||||
'NSISLexer': ('pipenv.patched.pip._vendor.pygments.lexers.installers', 'NSIS', ('nsis', 'nsi', 'nsh'), ('*.nsi', '*.nsh'), ('text/x-nsis',)),
|
||||
'NasmLexer': ('pipenv.patched.pip._vendor.pygments.lexers.asm', 'NASM', ('nasm',), ('*.asm', '*.ASM'), ('text/x-nasm',)),
|
||||
'NasmLexer': ('pipenv.patched.pip._vendor.pygments.lexers.asm', 'NASM', ('nasm',), ('*.asm', '*.ASM', '*.nasm'), ('text/x-nasm',)),
|
||||
'NasmObjdumpLexer': ('pipenv.patched.pip._vendor.pygments.lexers.asm', 'objdump-nasm', ('objdump-nasm',), ('*.objdump-intel',), ('text/x-nasm-objdump',)),
|
||||
'NemerleLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dotnet', 'Nemerle', ('nemerle',), ('*.n',), ('text/x-nemerle',)),
|
||||
'NesCLexer': ('pipenv.patched.pip._vendor.pygments.lexers.c_like', 'nesC', ('nesc',), ('*.nc',), ('text/x-nescsrc',)),
|
||||
@@ -350,6 +357,7 @@ LEXERS = {
|
||||
'PegLexer': ('pipenv.patched.pip._vendor.pygments.lexers.grammar_notation', 'PEG', ('peg',), ('*.peg',), ('text/x-peg',)),
|
||||
'Perl6Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.perl', 'Perl6', ('perl6', 'pl6', 'raku'), ('*.pl', '*.pm', '*.nqp', '*.p6', '*.6pl', '*.p6l', '*.pl6', '*.6pm', '*.p6m', '*.pm6', '*.t', '*.raku', '*.rakumod', '*.rakutest', '*.rakudoc'), ('text/x-perl6', 'application/x-perl6')),
|
||||
'PerlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.perl', 'Perl', ('perl', 'pl'), ('*.pl', '*.pm', '*.t', '*.perl'), ('text/x-perl', 'application/x-perl')),
|
||||
'PhixLexer': ('pipenv.patched.pip._vendor.pygments.lexers.phix', 'Phix', ('phix',), ('*.exw',), ('text/x-phix',)),
|
||||
'PhpLexer': ('pipenv.patched.pip._vendor.pygments.lexers.php', 'PHP', ('php', 'php3', 'php4', 'php5'), ('*.php', '*.php[345]', '*.inc'), ('text/x-php',)),
|
||||
'PigLexer': ('pipenv.patched.pip._vendor.pygments.lexers.jvm', 'Pig', ('pig',), ('*.pig',), ('text/x-pig',)),
|
||||
'PikeLexer': ('pipenv.patched.pip._vendor.pygments.lexers.c_like', 'Pike', ('pike',), ('*.pike', '*.pmod'), ('text/x-pike',)),
|
||||
@@ -357,6 +365,7 @@ LEXERS = {
|
||||
'PlPgsqlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sql', 'PL/pgSQL', ('plpgsql',), (), ('text/x-plpgsql',)),
|
||||
'PointlessLexer': ('pipenv.patched.pip._vendor.pygments.lexers.pointless', 'Pointless', ('pointless',), ('*.ptls',), ()),
|
||||
'PonyLexer': ('pipenv.patched.pip._vendor.pygments.lexers.pony', 'Pony', ('pony',), ('*.pony',), ()),
|
||||
'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',)),
|
||||
'PostgresLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sql', 'PostgreSQL SQL dialect', ('postgresql', 'postgres'), (), ('text/x-postgresql',)),
|
||||
@@ -376,7 +385,7 @@ LEXERS = {
|
||||
'Python2Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.python', 'Python 2.x', ('python2', 'py2'), (), ('text/x-python2', 'application/x-python2')),
|
||||
'Python2TracebackLexer': ('pipenv.patched.pip._vendor.pygments.lexers.python', 'Python 2.x Traceback', ('py2tb',), ('*.py2tb',), ('text/x-python2-traceback',)),
|
||||
'PythonConsoleLexer': ('pipenv.patched.pip._vendor.pygments.lexers.python', 'Python console session', ('pycon',), (), ('text/x-python-doctest',)),
|
||||
'PythonLexer': ('pipenv.patched.pip._vendor.pygments.lexers.python', 'Python', ('python', 'py', 'sage', 'python3', 'py3'), ('*.py', '*.pyw', '*.jy', '*.sage', '*.sc', 'SConstruct', 'SConscript', '*.bzl', 'BUCK', 'BUILD', 'BUILD.bazel', 'WORKSPACE', '*.tac'), ('text/x-python', 'application/x-python', 'text/x-python3', 'application/x-python3')),
|
||||
'PythonLexer': ('pipenv.patched.pip._vendor.pygments.lexers.python', 'Python', ('python', 'py', 'sage', 'python3', 'py3'), ('*.py', '*.pyw', '*.pyi', '*.jy', '*.sage', '*.sc', 'SConstruct', 'SConscript', '*.bzl', 'BUCK', 'BUILD', 'BUILD.bazel', 'WORKSPACE', '*.tac'), ('text/x-python', 'application/x-python', 'text/x-python3', 'application/x-python3')),
|
||||
'PythonTracebackLexer': ('pipenv.patched.pip._vendor.pygments.lexers.python', 'Python Traceback', ('pytb', 'py3tb'), ('*.pytb', '*.py3tb'), ('text/x-python-traceback', 'text/x-python3-traceback')),
|
||||
'PythonUL4Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.ul4', 'Python+UL4', ('py+ul4',), ('*.pyul4',), ()),
|
||||
'QBasicLexer': ('pipenv.patched.pip._vendor.pygments.lexers.basic', 'QBasic', ('qbasic', 'basic'), ('*.BAS', '*.bas'), ('text/basic',)),
|
||||
@@ -421,7 +430,7 @@ LEXERS = {
|
||||
'SASLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sas', 'SAS', ('sas',), ('*.SAS', '*.sas'), ('text/x-sas', 'text/sas', 'application/x-sas')),
|
||||
'SLexer': ('pipenv.patched.pip._vendor.pygments.lexers.r', 'S', ('splus', 's', 'r'), ('*.S', '*.R', '.Rhistory', '.Rprofile', '.Renviron'), ('text/S-plus', 'text/S', 'text/x-r-source', 'text/x-r', 'text/x-R', 'text/x-r-history', 'text/x-r-profile')),
|
||||
'SMLLexer': ('pipenv.patched.pip._vendor.pygments.lexers.ml', 'Standard ML', ('sml',), ('*.sml', '*.sig', '*.fun'), ('text/x-standardml', 'application/x-standardml')),
|
||||
'SNBTLexer': ('pipenv.patched.pip._vendor.pygments.lexers.mcfunction', 'SNBT', ('snbt',), ('*.snbt',), ('text/snbt',)),
|
||||
'SNBTLexer': ('pipenv.patched.pip._vendor.pygments.lexers.minecraft', 'SNBT', ('snbt',), ('*.snbt',), ('text/snbt',)),
|
||||
'SarlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.jvm', 'SARL', ('sarl',), ('*.sarl',), ('text/x-sarl',)),
|
||||
'SassLexer': ('pipenv.patched.pip._vendor.pygments.lexers.css', 'Sass', ('sass',), ('*.sass',), ('text/x-sass',)),
|
||||
'SaviLexer': ('pipenv.patched.pip._vendor.pygments.lexers.savi', 'Savi', ('savi',), ('*.savi',), ()),
|
||||
@@ -485,6 +494,7 @@ LEXERS = {
|
||||
'ThingsDBLexer': ('pipenv.patched.pip._vendor.pygments.lexers.thingsdb', 'ThingsDB', ('ti', 'thingsdb'), ('*.ti',), ()),
|
||||
'ThriftLexer': ('pipenv.patched.pip._vendor.pygments.lexers.dsls', 'Thrift', ('thrift',), ('*.thrift',), ('application/x-thrift',)),
|
||||
'TiddlyWiki5Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.markup', 'tiddler', ('tid',), ('*.tid',), ('text/vnd.tiddlywiki',)),
|
||||
'TlbLexer': ('pipenv.patched.pip._vendor.pygments.lexers.tlb', 'Tl-b', ('tlb',), ('*.tlb',), ()),
|
||||
'TodotxtLexer': ('pipenv.patched.pip._vendor.pygments.lexers.textfmts', 'Todotxt', ('todotxt',), ('todo.txt', '*.todotxt'), ('text/x-todo',)),
|
||||
'TransactSqlLexer': ('pipenv.patched.pip._vendor.pygments.lexers.sql', 'Transact-SQL', ('tsql', 't-sql'), ('*.sql',), ('text/x-tsql',)),
|
||||
'TreetopLexer': ('pipenv.patched.pip._vendor.pygments.lexers.parsers', 'Treetop', ('treetop',), ('*.treetop', '*.tt'), ()),
|
||||
@@ -519,6 +529,8 @@ LEXERS = {
|
||||
'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',), ()),
|
||||
'WhileyLexer': ('pipenv.patched.pip._vendor.pygments.lexers.whiley', 'Whiley', ('whiley',), ('*.whiley',), ('text/x-whiley',)),
|
||||
'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',)),
|
||||
'XMLUL4Lexer': ('pipenv.patched.pip._vendor.pygments.lexers.ul4', 'XML+UL4', ('xml+ul4',), ('*.xmlul4',), ()),
|
||||
'XQueryLexer': ('pipenv.patched.pip._vendor.pygments.lexers.webmisc', 'XQuery', ('xquery', 'xqy', 'xq', 'xql', 'xqm'), ('*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'), ('text/xquery', 'application/xquery')),
|
||||
|
||||
@@ -12,18 +12,16 @@ import re
|
||||
import keyword
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.lexer import Lexer, RegexLexer, include, bygroups, using, \
|
||||
default, words, combined, do_insertions, this
|
||||
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
|
||||
Number, Punctuation, Generic, Other, Error, Whitespace
|
||||
from pipenv.patched.pip._vendor.pygments import unistring as uni
|
||||
|
||||
__all__ = ['PythonLexer', 'PythonConsoleLexer', 'PythonTracebackLexer',
|
||||
'Python2Lexer', 'Python2TracebackLexer',
|
||||
'CythonLexer', 'DgLexer', 'NumPyLexer']
|
||||
|
||||
line_re = re.compile('.*?\n')
|
||||
|
||||
|
||||
class PythonLexer(RegexLexer):
|
||||
"""
|
||||
@@ -42,6 +40,8 @@ class PythonLexer(RegexLexer):
|
||||
filenames = [
|
||||
'*.py',
|
||||
'*.pyw',
|
||||
# Type stubs
|
||||
'*.pyi',
|
||||
# Jython
|
||||
'*.jy',
|
||||
# Sage
|
||||
@@ -100,11 +100,11 @@ class PythonLexer(RegexLexer):
|
||||
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\n', Text),
|
||||
(r'\n', Whitespace),
|
||||
(r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
|
||||
bygroups(Text, String.Affix, String.Doc)),
|
||||
bygroups(Whitespace, String.Affix, String.Doc)),
|
||||
(r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
|
||||
bygroups(Text, String.Affix, String.Doc)),
|
||||
bygroups(Whitespace, String.Affix, String.Doc)),
|
||||
(r'\A#!.+$', Comment.Hashbang),
|
||||
(r'#.*$', Comment.Single),
|
||||
(r'\\\n', Text),
|
||||
@@ -169,7 +169,7 @@ class PythonLexer(RegexLexer):
|
||||
combined('bytesescape', 'dqs')),
|
||||
("([bB])(')", bygroups(String.Affix, String.Single),
|
||||
combined('bytesescape', 'sqs')),
|
||||
|
||||
|
||||
(r'[^\S\n]+', Text),
|
||||
include('numbers'),
|
||||
(r'!=|==|<<|>>|:=|[-~+/*%=<>&^|.]', Operator),
|
||||
@@ -192,13 +192,13 @@ class PythonLexer(RegexLexer):
|
||||
(r'(=\s*)?' # debug (https://bugs.python.org/issue36817)
|
||||
r'(\![sraf])?' # conversion
|
||||
r':', String.Interpol, '#pop'),
|
||||
(r'\s+', Text), # allow new lines
|
||||
(r'\s+', Whitespace), # allow new lines
|
||||
include('expr'),
|
||||
],
|
||||
'expr-inside-fstring-inner': [
|
||||
(r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
|
||||
(r'[])}]', Punctuation, '#pop'),
|
||||
(r'\s+', Text), # allow new lines
|
||||
(r'\s+', Whitespace), # allow new lines
|
||||
include('expr'),
|
||||
],
|
||||
'expr-keywords': [
|
||||
@@ -229,7 +229,7 @@ class PythonLexer(RegexLexer):
|
||||
],
|
||||
'soft-keywords-inner': [
|
||||
# optional `_` keyword
|
||||
(r'(\s+)([^\n_]*)(_\b)', bygroups(Text, using(this), Keyword)),
|
||||
(r'(\s+)([^\n_]*)(_\b)', bygroups(Whitespace, using(this), Keyword)),
|
||||
default('#pop')
|
||||
],
|
||||
'builtins': [
|
||||
@@ -445,11 +445,11 @@ class Python2Lexer(RegexLexer):
|
||||
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\n', Text),
|
||||
(r'\n', Whitespace),
|
||||
(r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
|
||||
bygroups(Text, String.Affix, String.Doc)),
|
||||
bygroups(Whitespace, String.Affix, String.Doc)),
|
||||
(r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
|
||||
bygroups(Text, String.Affix, String.Doc)),
|
||||
bygroups(Whitespace, String.Affix, String.Doc)),
|
||||
(r'[^\S\n]+', Text),
|
||||
(r'\A#!.+$', Comment.Hashbang),
|
||||
(r'#.*$', Comment.Single),
|
||||
@@ -742,7 +742,7 @@ class PythonTracebackLexer(RegexLexer):
|
||||
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\n', Text),
|
||||
(r'\n', Whitespace),
|
||||
(r'^Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'),
|
||||
(r'^During handling of the above exception, another '
|
||||
r'exception occurred:\n\n', Generic.Traceback),
|
||||
@@ -753,24 +753,24 @@ class PythonTracebackLexer(RegexLexer):
|
||||
],
|
||||
'intb': [
|
||||
(r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)',
|
||||
bygroups(Text, Name.Builtin, Text, Number, Text, Name, Text)),
|
||||
bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)),
|
||||
(r'^( File )("[^"]+")(, line )(\d+)(\n)',
|
||||
bygroups(Text, Name.Builtin, Text, Number, Text)),
|
||||
bygroups(Text, Name.Builtin, Text, Number, Whitespace)),
|
||||
(r'^( )(.+)(\n)',
|
||||
bygroups(Text, using(PythonLexer), Text), 'markers'),
|
||||
bygroups(Whitespace, using(PythonLexer), Whitespace), 'markers'),
|
||||
(r'^([ \t]*)(\.\.\.)(\n)',
|
||||
bygroups(Text, Comment, Text)), # for doctests...
|
||||
bygroups(Whitespace, Comment, Whitespace)), # for doctests...
|
||||
(r'^([^:]+)(: )(.+)(\n)',
|
||||
bygroups(Generic.Error, Text, Name, Text), '#pop'),
|
||||
bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
|
||||
(r'^([a-zA-Z_][\w.]*)(:?\n)',
|
||||
bygroups(Generic.Error, Text), '#pop')
|
||||
bygroups(Generic.Error, Whitespace), '#pop')
|
||||
],
|
||||
'markers': [
|
||||
# Either `PEP 657 <https://www.python.org/dev/peps/pep-0657/>`
|
||||
# error locations in Python 3.11+, or single-caret markers
|
||||
# for syntax errors before that.
|
||||
(r'^( {4,})([~^]+)(\n)',
|
||||
bygroups(Text, Punctuation.Marker, Text),
|
||||
bygroups(Whitespace, Punctuation.Marker, Whitespace),
|
||||
'#pop'),
|
||||
default('#pop'),
|
||||
],
|
||||
@@ -808,17 +808,17 @@ class Python2TracebackLexer(RegexLexer):
|
||||
],
|
||||
'intb': [
|
||||
(r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)',
|
||||
bygroups(Text, Name.Builtin, Text, Number, Text, Name, Text)),
|
||||
bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)),
|
||||
(r'^( File )("[^"]+")(, line )(\d+)(\n)',
|
||||
bygroups(Text, Name.Builtin, Text, Number, Text)),
|
||||
bygroups(Text, Name.Builtin, Text, Number, Whitespace)),
|
||||
(r'^( )(.+)(\n)',
|
||||
bygroups(Text, using(Python2Lexer), Text), 'marker'),
|
||||
bygroups(Text, using(Python2Lexer), Whitespace), 'marker'),
|
||||
(r'^([ \t]*)(\.\.\.)(\n)',
|
||||
bygroups(Text, Comment, Text)), # for doctests...
|
||||
bygroups(Text, Comment, Whitespace)), # for doctests...
|
||||
(r'^([^:]+)(: )(.+)(\n)',
|
||||
bygroups(Generic.Error, Text, Name, Text), '#pop'),
|
||||
bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
|
||||
(r'^([a-zA-Z_]\w*)(:?\n)',
|
||||
bygroups(Generic.Error, Text), '#pop')
|
||||
bygroups(Generic.Error, Whitespace), '#pop')
|
||||
],
|
||||
'marker': [
|
||||
# For syntax errors.
|
||||
@@ -843,13 +843,13 @@ class CythonLexer(RegexLexer):
|
||||
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\n', Text),
|
||||
(r'^(\s*)("""(?:.|\n)*?""")', bygroups(Text, String.Doc)),
|
||||
(r"^(\s*)('''(?:.|\n)*?''')", bygroups(Text, String.Doc)),
|
||||
(r'\n', Whitespace),
|
||||
(r'^(\s*)("""(?:.|\n)*?""")', bygroups(Whitespace, String.Doc)),
|
||||
(r"^(\s*)('''(?:.|\n)*?''')", bygroups(Whitespace, String.Doc)),
|
||||
(r'[^\S\n]+', Text),
|
||||
(r'#.*$', Comment),
|
||||
(r'[]{}:(),;[]', Punctuation),
|
||||
(r'\\\n', Text),
|
||||
(r'\\\n', Whitespace),
|
||||
(r'\\', Text),
|
||||
(r'(in|is|and|or|not)\b', Operator.Word),
|
||||
(r'(<)([a-zA-Z0-9.?]+)(>)',
|
||||
|
||||
@@ -74,6 +74,8 @@ class PygmentsDoc(Directive):
|
||||
out = self.document_formatters()
|
||||
elif self.arguments[0] == 'filters':
|
||||
out = self.document_filters()
|
||||
elif self.arguments[0] == 'lexers_overview':
|
||||
out = self.document_lexers_overview()
|
||||
else:
|
||||
raise Exception('invalid argument for "pygmentsdoc" directive')
|
||||
node = nodes.compound()
|
||||
@@ -83,6 +85,66 @@ class PygmentsDoc(Directive):
|
||||
self.state.document.settings.record_dependencies.add(fn)
|
||||
return node.children
|
||||
|
||||
def document_lexers_overview(self):
|
||||
"""Generate a tabular overview of all lexers.
|
||||
|
||||
The columns are the lexer name, the extensions handled by this lexer
|
||||
(or "None"), the aliases and a link to the lexer class."""
|
||||
from pipenv.patched.pip._vendor.pygments.lexers._mapping import LEXERS
|
||||
from pipenv.patched.pip._vendor.pygments.lexers import find_lexer_class
|
||||
out = []
|
||||
|
||||
table = []
|
||||
|
||||
def format_link(name, url):
|
||||
if url:
|
||||
return f'`{name} <{url}>`_'
|
||||
return name
|
||||
|
||||
for classname, data in sorted(LEXERS.items(), key=lambda x: x[1][1].lower()):
|
||||
lexer_cls = find_lexer_class(data[1])
|
||||
extensions = lexer_cls.filenames + lexer_cls.alias_filenames
|
||||
|
||||
table.append({
|
||||
'name': format_link(data[1], lexer_cls.url),
|
||||
'extensions': ', '.join(extensions).replace('*', '\\*').replace('_', '\\') or 'None',
|
||||
'aliases': ', '.join(data[2]),
|
||||
'class': f'{data[0]}.{classname}'
|
||||
})
|
||||
|
||||
column_names = ['name', 'extensions', 'aliases', 'class']
|
||||
column_lengths = [max([len(row[column]) for row in table if row[column]])
|
||||
for column in column_names]
|
||||
|
||||
def write_row(*columns):
|
||||
"""Format a table row"""
|
||||
out = []
|
||||
for l, c in zip(column_lengths, columns):
|
||||
if c:
|
||||
out.append(c.ljust(l))
|
||||
else:
|
||||
out.append(' '*l)
|
||||
|
||||
return ' '.join(out)
|
||||
|
||||
def write_seperator():
|
||||
"""Write a table separator row"""
|
||||
sep = ['='*c for c in column_lengths]
|
||||
return write_row(*sep)
|
||||
|
||||
out.append(write_seperator())
|
||||
out.append(write_row('Name', 'Extension(s)', 'Short name(s)', 'Lexer class'))
|
||||
out.append(write_seperator())
|
||||
for row in table:
|
||||
out.append(write_row(
|
||||
row['name'],
|
||||
row['extensions'],
|
||||
row['aliases'],
|
||||
f':class:`~{row["class"]}`'))
|
||||
out.append(write_seperator())
|
||||
|
||||
return '\n'.join(out)
|
||||
|
||||
def document_lexers(self):
|
||||
from pipenv.patched.pip._vendor.pygments.lexers._mapping import LEXERS
|
||||
out = []
|
||||
|
||||
@@ -11,7 +11,7 @@ __all__ = [
|
||||
"ResolutionTooDeep",
|
||||
]
|
||||
|
||||
__version__ = "0.8.1"
|
||||
__version__ = "1.0.1"
|
||||
|
||||
|
||||
from .providers import AbstractProvider, AbstractResolver
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class AbstractProvider(object):
|
||||
"""Delegate class to provide requirement interface for the resolver."""
|
||||
"""Delegate class to provide the required interface for the resolver."""
|
||||
|
||||
def identify(self, requirement_or_candidate):
|
||||
"""Given a requirement, return an identifier for it.
|
||||
@@ -24,9 +24,9 @@ class AbstractProvider(object):
|
||||
this group of arguments is.
|
||||
|
||||
:param identifier: An identifier as returned by ``identify()``. This
|
||||
identifies the dependency matches of which should be returned.
|
||||
identifies the dependency matches which should be returned.
|
||||
:param resolutions: Mapping of candidates currently pinned by the
|
||||
resolver. Each key is an identifier, and the value a candidate.
|
||||
resolver. Each key is an identifier, and the value is a candidate.
|
||||
The candidate may conflict with requirements from ``information``.
|
||||
:param candidates: Mapping of each dependency's possible candidates.
|
||||
Each value is an iterator of candidates.
|
||||
@@ -39,10 +39,10 @@ class AbstractProvider(object):
|
||||
|
||||
* ``requirement`` specifies a requirement contributing to the current
|
||||
list of candidates.
|
||||
* ``parent`` specifies the candidate that provides (dependend on) the
|
||||
* ``parent`` specifies the candidate that provides (depended on) the
|
||||
requirement, or ``None`` to indicate a root requirement.
|
||||
|
||||
The preference could depend on a various of issues, including (not
|
||||
The preference could depend on various issues, including (not
|
||||
necessarily in this order):
|
||||
|
||||
* Is this package pinned in the current resolution result?
|
||||
@@ -61,7 +61,7 @@ class AbstractProvider(object):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_matches(self, identifier, requirements, incompatibilities):
|
||||
"""Find all possible candidates that satisfy given constraints.
|
||||
"""Find all possible candidates that satisfy the given constraints.
|
||||
|
||||
:param identifier: An identifier as returned by ``identify()``. This
|
||||
identifies the dependency matches of which should be returned.
|
||||
@@ -92,7 +92,7 @@ class AbstractProvider(object):
|
||||
def is_satisfied_by(self, requirement, candidate):
|
||||
"""Whether the given requirement can be satisfied by a candidate.
|
||||
|
||||
The candidate is guarenteed to have been generated from the
|
||||
The candidate is guaranteed to have been generated from the
|
||||
requirement.
|
||||
|
||||
A boolean should be returned to indicate whether ``candidate`` is a
|
||||
|
||||
@@ -36,7 +36,7 @@ class BaseReporter(object):
|
||||
:param causes: The information on the collision that caused the backtracking.
|
||||
"""
|
||||
|
||||
def backtracking(self, candidate):
|
||||
def rejecting_candidate(self, criterion, candidate):
|
||||
"""Called when rejecting a candidate during backtracking."""
|
||||
|
||||
def pinning(self, candidate):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import collections
|
||||
import itertools
|
||||
import operator
|
||||
|
||||
from .providers import AbstractResolver
|
||||
@@ -173,6 +174,31 @@ class Resolution(object):
|
||||
raise RequirementsConflicted(criterion)
|
||||
criteria[identifier] = criterion
|
||||
|
||||
def _remove_information_from_criteria(self, criteria, parents):
|
||||
"""Remove information from parents of criteria.
|
||||
|
||||
Concretely, removes all values from each criterion's ``information``
|
||||
field that have one of ``parents`` as provider of the requirement.
|
||||
|
||||
:param criteria: The criteria to update.
|
||||
:param parents: Identifiers for which to remove information from all criteria.
|
||||
"""
|
||||
if not parents:
|
||||
return
|
||||
for key, criterion in criteria.items():
|
||||
criteria[key] = Criterion(
|
||||
criterion.candidates,
|
||||
[
|
||||
information
|
||||
for information in criterion.information
|
||||
if (
|
||||
information.parent is None
|
||||
or self._p.identify(information.parent) not in parents
|
||||
)
|
||||
],
|
||||
criterion.incompatibilities,
|
||||
)
|
||||
|
||||
def _get_preference(self, name):
|
||||
return self._p.get_preference(
|
||||
identifier=name,
|
||||
@@ -212,6 +238,7 @@ class Resolution(object):
|
||||
try:
|
||||
criteria = self._get_updated_criteria(candidate)
|
||||
except RequirementsConflicted as e:
|
||||
self._r.rejecting_candidate(e.criterion, candidate)
|
||||
causes.append(e.criterion)
|
||||
continue
|
||||
|
||||
@@ -240,8 +267,8 @@ class Resolution(object):
|
||||
# end, signal for backtracking.
|
||||
return causes
|
||||
|
||||
def _backtrack(self):
|
||||
"""Perform backtracking.
|
||||
def _backjump(self, causes):
|
||||
"""Perform backjumping.
|
||||
|
||||
When we enter here, the stack is like this::
|
||||
|
||||
@@ -257,22 +284,46 @@ class Resolution(object):
|
||||
|
||||
Each iteration of the loop will:
|
||||
|
||||
1. Discard Z.
|
||||
2. Discard Y but remember its incompatibility information gathered
|
||||
1. Identify Z. The incompatibility is not always caused by the latest
|
||||
state. For example, given three requirements A, B and C, with
|
||||
dependencies A1, B1 and C1, where A1 and B1 are incompatible: the
|
||||
last state might be related to C, so we want to discard the
|
||||
previous state.
|
||||
2. Discard Z.
|
||||
3. Discard Y but remember its incompatibility information gathered
|
||||
previously, and the failure we're dealing with right now.
|
||||
3. Push a new state Y' based on X, and apply the incompatibility
|
||||
4. Push a new state Y' based on X, and apply the incompatibility
|
||||
information from Y to Y'.
|
||||
4a. If this causes Y' to conflict, we need to backtrack again. Make Y'
|
||||
5a. If this causes Y' to conflict, we need to backtrack again. Make Y'
|
||||
the new Z and go back to step 2.
|
||||
4b. If the incompatibilities apply cleanly, end backtracking.
|
||||
5b. If the incompatibilities apply cleanly, end backtracking.
|
||||
"""
|
||||
incompatible_reqs = itertools.chain(
|
||||
(c.parent for c in causes if c.parent is not None),
|
||||
(c.requirement for c in causes),
|
||||
)
|
||||
incompatible_deps = {self._p.identify(r) for r in incompatible_reqs}
|
||||
while len(self._states) >= 3:
|
||||
# Remove the state that triggered backtracking.
|
||||
del self._states[-1]
|
||||
|
||||
# Retrieve the last candidate pin and known incompatibilities.
|
||||
broken_state = self._states.pop()
|
||||
name, candidate = broken_state.mapping.popitem()
|
||||
# Ensure to backtrack to a state that caused the incompatibility
|
||||
incompatible_state = False
|
||||
while not incompatible_state:
|
||||
# Retrieve the last candidate pin and known incompatibilities.
|
||||
try:
|
||||
broken_state = self._states.pop()
|
||||
name, candidate = broken_state.mapping.popitem()
|
||||
except (IndexError, KeyError):
|
||||
raise ResolutionImpossible(causes)
|
||||
current_dependencies = {
|
||||
self._p.identify(d)
|
||||
for d in self._p.get_dependencies(candidate)
|
||||
}
|
||||
incompatible_state = not current_dependencies.isdisjoint(
|
||||
incompatible_deps
|
||||
)
|
||||
|
||||
incompatibilities_from_broken = [
|
||||
(k, list(v.incompatibilities))
|
||||
for k, v in broken_state.criteria.items()
|
||||
@@ -281,8 +332,6 @@ class Resolution(object):
|
||||
# Also mark the newly known incompatibility.
|
||||
incompatibilities_from_broken.append((name, [candidate]))
|
||||
|
||||
self._r.backtracking(candidate=candidate)
|
||||
|
||||
# Create a new state from the last known-to-work one, and apply
|
||||
# the previously gathered incompatibility information.
|
||||
def _patch_criteria():
|
||||
@@ -368,22 +417,38 @@ class Resolution(object):
|
||||
self._r.ending(state=self.state)
|
||||
return self.state
|
||||
|
||||
# keep track of satisfied names to calculate diff after pinning
|
||||
satisfied_names = set(self.state.criteria.keys()) - set(
|
||||
unsatisfied_names
|
||||
)
|
||||
|
||||
# Choose the most preferred unpinned criterion to try.
|
||||
name = min(unsatisfied_names, key=self._get_preference)
|
||||
failure_causes = self._attempt_to_pin_criterion(name)
|
||||
|
||||
if failure_causes:
|
||||
causes = [i for c in failure_causes for i in c.information]
|
||||
# Backtrack if pinning fails. The backtrack process puts us in
|
||||
# Backjump if pinning fails. The backjump process puts us in
|
||||
# an unpinned state, so we can work on it in the next round.
|
||||
self._r.resolving_conflicts(causes=causes)
|
||||
success = self._backtrack()
|
||||
success = self._backjump(causes)
|
||||
self.state.backtrack_causes[:] = causes
|
||||
|
||||
# Dead ends everywhere. Give up.
|
||||
if not success:
|
||||
raise ResolutionImpossible(self.state.backtrack_causes)
|
||||
else:
|
||||
# discard as information sources any invalidated names
|
||||
# (unsatisfied names that were previously satisfied)
|
||||
newly_unsatisfied_names = {
|
||||
key
|
||||
for key, criterion in self.state.criteria.items()
|
||||
if key in satisfied_names
|
||||
and not self._is_current_pin_satisfying(key, criterion)
|
||||
}
|
||||
self._remove_information_from_criteria(
|
||||
self.state.criteria, newly_unsatisfied_names
|
||||
)
|
||||
# Pinning was successful. Push a new state to do another pin.
|
||||
self._push_new_state()
|
||||
|
||||
|
||||
@@ -117,13 +117,14 @@ class _FactoryIterableView(object):
|
||||
|
||||
def __init__(self, factory):
|
||||
self._factory = factory
|
||||
self._iterable = None
|
||||
|
||||
def __repr__(self):
|
||||
return "{}({})".format(type(self).__name__, list(self._factory()))
|
||||
return "{}({})".format(type(self).__name__, list(self))
|
||||
|
||||
def __bool__(self):
|
||||
try:
|
||||
next(self._factory())
|
||||
next(iter(self))
|
||||
except StopIteration:
|
||||
return False
|
||||
return True
|
||||
@@ -131,7 +132,11 @@ class _FactoryIterableView(object):
|
||||
__nonzero__ = __bool__ # XXX: Python 2.
|
||||
|
||||
def __iter__(self):
|
||||
return self._factory()
|
||||
iterable = (
|
||||
self._factory() if self._iterable is None else self._iterable
|
||||
)
|
||||
self._iterable, current = itertools.tee(iterable)
|
||||
return current
|
||||
|
||||
|
||||
class _SequenceIterableView(object):
|
||||
|
||||
@@ -12,9 +12,7 @@ body {{
|
||||
</head>
|
||||
<html>
|
||||
<body>
|
||||
<code>
|
||||
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
|
||||
</code>
|
||||
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><code>{code}</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
@@ -3,20 +3,6 @@ from typing import IO, Iterable, Iterator, List, Optional, Type
|
||||
|
||||
|
||||
class NullFile(IO[str]):
|
||||
|
||||
# TODO: "mode", "name" and "closed" are only required for Python 3.6.
|
||||
|
||||
@property
|
||||
def mode(self) -> str:
|
||||
return ""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "NullFile"
|
||||
|
||||
def closed(self) -> bool:
|
||||
return False
|
||||
|
||||
def close(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ if __name__ == "__main__": # pragma: no cover
|
||||
),
|
||||
width=60,
|
||||
style="on dark_blue",
|
||||
title="Algin",
|
||||
title="Align",
|
||||
)
|
||||
|
||||
console.print(
|
||||
|
||||
@@ -43,6 +43,9 @@ def _ansi_tokenize(ansi_text: str) -> Iterable[_AnsiToken]:
|
||||
if start > position:
|
||||
yield _AnsiToken(ansi_text[position:start])
|
||||
if sgr:
|
||||
if sgr == "(":
|
||||
position = end + 1
|
||||
continue
|
||||
if sgr.endswith("m"):
|
||||
yield _AnsiToken("", sgr[1:-1], osc)
|
||||
else:
|
||||
|
||||
@@ -60,7 +60,7 @@ def _get_codepoint_cell_size(codepoint: int) -> int:
|
||||
"""Get the cell size of a character.
|
||||
|
||||
Args:
|
||||
character (str): A single character.
|
||||
codepoint (int): Codepoint of a character.
|
||||
|
||||
Returns:
|
||||
int: Number of cells (0, 1 or 2) occupied by that character.
|
||||
|
||||
@@ -513,15 +513,14 @@ class Color(NamedTuple):
|
||||
def downgrade(self, system: ColorSystem) -> "Color":
|
||||
"""Downgrade a color system to a system with fewer colors."""
|
||||
|
||||
if self.type in [ColorType.DEFAULT, system]:
|
||||
if self.type in (ColorType.DEFAULT, system):
|
||||
return self
|
||||
# Convert to 8-bit color from truecolor color
|
||||
if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR:
|
||||
assert self.triplet is not None
|
||||
red, green, blue = self.triplet.normalized
|
||||
_h, l, s = rgb_to_hls(red, green, blue)
|
||||
# If saturation is under 10% assume it is grayscale
|
||||
if s < 0.1:
|
||||
_h, l, s = rgb_to_hls(*self.triplet.normalized)
|
||||
# If saturation is under 15% assume it is grayscale
|
||||
if s < 0.15:
|
||||
gray = round(l * 25.0)
|
||||
if gray == 0:
|
||||
color_number = 16
|
||||
@@ -531,8 +530,13 @@ class Color(NamedTuple):
|
||||
color_number = 231 + gray
|
||||
return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
|
||||
|
||||
red, green, blue = self.triplet
|
||||
six_red = red / 95 if red < 95 else 1 + (red - 95) / 40
|
||||
six_green = green / 95 if green < 95 else 1 + (green - 95) / 40
|
||||
six_blue = blue / 95 if blue < 95 else 1 + (blue - 95) / 40
|
||||
|
||||
color_number = (
|
||||
16 + 36 * round(red * 5.0) + 6 * round(green * 5.0) + round(blue * 5.0)
|
||||
16 + 36 * round(six_red) + 6 * round(six_green) + round(six_blue)
|
||||
)
|
||||
return Color(self.name, ColorType.EIGHT_BIT, number=color_number)
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import inspect
|
||||
import io
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
@@ -48,6 +47,7 @@ else:
|
||||
from . import errors, themes
|
||||
from ._emoji_replace import _emoji_replace
|
||||
from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT
|
||||
from ._fileno import get_fileno
|
||||
from ._log_render import FormatTimeCallable, LogRender
|
||||
from .align import Align, AlignMethod
|
||||
from .color import ColorSystem, blend_rgb
|
||||
@@ -711,11 +711,6 @@ class Console:
|
||||
self._force_terminal = None
|
||||
if force_terminal is not None:
|
||||
self._force_terminal = force_terminal
|
||||
else:
|
||||
# If FORCE_COLOR env var has any value at all, we force terminal.
|
||||
force_color = self._environ.get("FORCE_COLOR")
|
||||
if force_color is not None:
|
||||
self._force_terminal = True
|
||||
|
||||
self._file = file
|
||||
self.quiet = quiet
|
||||
@@ -758,7 +753,7 @@ class Console:
|
||||
self._is_alt_screen = False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<console width={self.width} {str(self._color_system)}>"
|
||||
return f"<console width={self.width} {self._color_system!s}>"
|
||||
|
||||
@property
|
||||
def file(self) -> IO[str]:
|
||||
@@ -949,6 +944,15 @@ class Console:
|
||||
# Return False for Idle which claims to be a tty but can't handle ansi codes
|
||||
return False
|
||||
|
||||
if self.is_jupyter:
|
||||
# return False for Jupyter, which may have FORCE_COLOR set
|
||||
return False
|
||||
|
||||
# If FORCE_COLOR env var has any value at all, we assume a terminal.
|
||||
force_color = self._environ.get("FORCE_COLOR")
|
||||
if force_color is not None:
|
||||
self._force_terminal = True
|
||||
|
||||
isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
|
||||
try:
|
||||
return False if isatty is None else isatty()
|
||||
@@ -1146,7 +1150,7 @@ class Console:
|
||||
status: RenderableType,
|
||||
*,
|
||||
spinner: str = "dots",
|
||||
spinner_style: str = "status.spinner",
|
||||
spinner_style: StyleType = "status.spinner",
|
||||
speed: float = 1.0,
|
||||
refresh_per_second: float = 12.5,
|
||||
) -> "Status":
|
||||
@@ -1523,7 +1527,7 @@ class Console:
|
||||
if text:
|
||||
sep_text = Text(sep, justify=justify, end=end)
|
||||
append(sep_text.join(text))
|
||||
del text[:]
|
||||
text.clear()
|
||||
|
||||
for renderable in objects:
|
||||
renderable = rich_cast(renderable)
|
||||
@@ -2006,12 +2010,11 @@ class Console:
|
||||
if WINDOWS:
|
||||
use_legacy_windows_render = False
|
||||
if self.legacy_windows:
|
||||
try:
|
||||
fileno = get_fileno(self.file)
|
||||
if fileno is not None:
|
||||
use_legacy_windows_render = (
|
||||
self.file.fileno() in _STD_STREAMS_OUTPUT
|
||||
fileno in _STD_STREAMS_OUTPUT
|
||||
)
|
||||
except (ValueError, io.UnsupportedOperation):
|
||||
pass
|
||||
|
||||
if use_legacy_windows_render:
|
||||
from pipenv.patched.pip._vendor.rich._win32_console import LegacyWindowsTerm
|
||||
@@ -2026,13 +2029,31 @@ class Console:
|
||||
# Either a non-std stream on legacy Windows, or modern Windows.
|
||||
text = self._render_buffer(self._buffer[:])
|
||||
# https://bugs.python.org/issue37871
|
||||
# https://github.com/python/cpython/issues/82052
|
||||
# We need to avoid writing more than 32Kb in a single write, due to the above bug
|
||||
write = self.file.write
|
||||
for line in text.splitlines(True):
|
||||
try:
|
||||
write(line)
|
||||
except UnicodeEncodeError as error:
|
||||
error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
|
||||
raise
|
||||
# Worse case scenario, every character is 4 bytes of utf-8
|
||||
MAX_WRITE = 32 * 1024 // 4
|
||||
try:
|
||||
if len(text) <= MAX_WRITE:
|
||||
write(text)
|
||||
else:
|
||||
batch: List[str] = []
|
||||
batch_append = batch.append
|
||||
size = 0
|
||||
for line in text.splitlines(True):
|
||||
if size + len(line) > MAX_WRITE and batch:
|
||||
write("".join(batch))
|
||||
batch.clear()
|
||||
size = 0
|
||||
batch_append(line)
|
||||
size += len(line)
|
||||
if batch:
|
||||
write("".join(batch))
|
||||
batch.clear()
|
||||
except UnicodeEncodeError as error:
|
||||
error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
|
||||
raise
|
||||
else:
|
||||
text = self._render_buffer(self._buffer[:])
|
||||
try:
|
||||
|
||||
@@ -138,10 +138,11 @@ DEFAULT_STYLES: Dict[str, Style] = {
|
||||
"tree.line": Style(),
|
||||
"markdown.paragraph": Style(),
|
||||
"markdown.text": Style(),
|
||||
"markdown.emph": Style(italic=True),
|
||||
"markdown.em": Style(italic=True),
|
||||
"markdown.emph": Style(italic=True), # For commonmark backwards compatibility
|
||||
"markdown.strong": Style(bold=True),
|
||||
"markdown.code": Style(bgcolor="black", color="bright_white"),
|
||||
"markdown.code_block": Style(dim=True, color="cyan", bgcolor="black"),
|
||||
"markdown.code": Style(bold=True, color="cyan", bgcolor="black"),
|
||||
"markdown.code_block": Style(color="cyan", bgcolor="black"),
|
||||
"markdown.block_quote": Style(color="magenta"),
|
||||
"markdown.list": Style(color="cyan"),
|
||||
"markdown.item": Style(),
|
||||
@@ -157,7 +158,8 @@ DEFAULT_STYLES: Dict[str, Style] = {
|
||||
"markdown.h6": Style(italic=True),
|
||||
"markdown.h7": Style(italic=True, dim=True),
|
||||
"markdown.link": Style(color="bright_blue"),
|
||||
"markdown.link_url": Style(color="blue"),
|
||||
"markdown.link_url": Style(color="blue", underline=True),
|
||||
"markdown.s": Style(strike=True),
|
||||
"iso8601.date": Style(color="blue"),
|
||||
"iso8601.time": Style(color="magenta"),
|
||||
"iso8601.timezone": Style(color="yellow"),
|
||||
|
||||
@@ -34,7 +34,7 @@ class FileProxy(io.TextIOBase):
|
||||
line, new_line, text = text.partition("\n")
|
||||
if new_line:
|
||||
lines.append("".join(buffer) + line)
|
||||
del buffer[:]
|
||||
buffer.clear()
|
||||
else:
|
||||
buffer.append(line)
|
||||
break
|
||||
@@ -52,3 +52,6 @@ class FileProxy(io.TextIOBase):
|
||||
if output:
|
||||
self.__console.print(output)
|
||||
del self.__buffer[:]
|
||||
|
||||
def fileno(self) -> int:
|
||||
return self.__file.fileno()
|
||||
|
||||
@@ -82,7 +82,7 @@ class ReprHighlighter(RegexHighlighter):
|
||||
|
||||
base_style = "repr."
|
||||
highlights = [
|
||||
r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*?)(?P<tag_end>>)",
|
||||
r"(?P<tag_start><)(?P<tag_name>[-\w.:|]*)(?P<tag_contents>[\w\W]*)(?P<tag_end>>)",
|
||||
r'(?P<attrib_name>[\w_]{1,50})=(?P<attrib_value>"?[\w_]+"?)?',
|
||||
r"(?P<brace>[][{}()])",
|
||||
_combine_regex(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from pathlib import Path
|
||||
from json import loads, dumps
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
@@ -131,8 +132,7 @@ if __name__ == "__main__":
|
||||
if args.path == "-":
|
||||
json_data = sys.stdin.read()
|
||||
else:
|
||||
with open(args.path, "rt") as json_file:
|
||||
json_data = json_file.read()
|
||||
json_data = Path(args.path).read_text()
|
||||
except Exception as error:
|
||||
error_console.print(f"Unable to read {args.path!r}; {error}")
|
||||
sys.exit(-1)
|
||||
|
||||
@@ -210,6 +210,8 @@ class Live(JupyterMixin, RenderHook):
|
||||
renderable (RenderableType): New renderable to use.
|
||||
refresh (bool, optional): Refresh the display. Defaults to False.
|
||||
"""
|
||||
if isinstance(renderable, str):
|
||||
renderable = self.console.render_str(renderable)
|
||||
with self._lock:
|
||||
self._renderable = renderable
|
||||
if refresh:
|
||||
|
||||
@@ -30,7 +30,7 @@ from pipenv.patched.pip._vendor.rich.repr import RichReprResult
|
||||
try:
|
||||
import pipenv.vendor.attr as _attr_module
|
||||
|
||||
_has_attrs = True
|
||||
_has_attrs = hasattr(_attr_module, "ib")
|
||||
except ImportError: # pragma: no cover
|
||||
_has_attrs = False
|
||||
|
||||
@@ -55,13 +55,6 @@ if TYPE_CHECKING:
|
||||
)
|
||||
|
||||
|
||||
JUPYTER_CLASSES_TO_NOT_RENDER = {
|
||||
# Matplotlib "Artists" manage their own rendering in a Jupyter notebook, and we should not try to render them too.
|
||||
# "Typically, all [Matplotlib] visible elements in a figure are subclasses of Artist."
|
||||
"matplotlib.artist.Artist",
|
||||
}
|
||||
|
||||
|
||||
def _is_attr_object(obj: Any) -> bool:
|
||||
"""Check if an object was created with attrs module."""
|
||||
return _has_attrs and _attr_module.has(type(obj))
|
||||
@@ -122,69 +115,40 @@ def _ipy_display_hook(
|
||||
max_string: Optional[int] = None,
|
||||
max_depth: Optional[int] = None,
|
||||
expand_all: bool = False,
|
||||
) -> None:
|
||||
) -> Union[str, None]:
|
||||
# needed here to prevent circular import:
|
||||
from ._inspect import is_object_one_of_types
|
||||
from .console import ConsoleRenderable
|
||||
|
||||
# always skip rich generated jupyter renderables or None values
|
||||
if _safe_isinstance(value, JupyterRenderable) or value is None:
|
||||
return
|
||||
return None
|
||||
|
||||
console = console or get_console()
|
||||
if console.is_jupyter:
|
||||
# Delegate rendering to IPython if the object (and IPython) supports it
|
||||
# https://ipython.readthedocs.io/en/stable/config/integrating.html#rich-display
|
||||
ipython_repr_methods = [
|
||||
"_repr_html_",
|
||||
"_repr_markdown_",
|
||||
"_repr_json_",
|
||||
"_repr_latex_",
|
||||
"_repr_jpeg_",
|
||||
"_repr_png_",
|
||||
"_repr_svg_",
|
||||
"_repr_mimebundle_",
|
||||
]
|
||||
for repr_method in ipython_repr_methods:
|
||||
method = getattr(value, repr_method, None)
|
||||
if inspect.ismethod(method):
|
||||
# Calling the method ourselves isn't ideal. The interface for the `_repr_*_` methods
|
||||
# specifies that if they return None, then they should not be rendered
|
||||
# by the notebook.
|
||||
try:
|
||||
repr_result = method()
|
||||
except Exception:
|
||||
continue # If the method raises, treat it as if it doesn't exist, try any others
|
||||
if repr_result is not None:
|
||||
return # Delegate rendering to IPython
|
||||
|
||||
# When in a Jupyter notebook let's avoid the display of some specific classes,
|
||||
# as they result in the rendering of useless and noisy lines such as `<Figure size 432x288 with 1 Axes>`.
|
||||
# What does this do?
|
||||
# --> if the class has "matplotlib.artist.Artist" in its hierarchy for example, we don't render it.
|
||||
if is_object_one_of_types(value, JUPYTER_CLASSES_TO_NOT_RENDER):
|
||||
return
|
||||
|
||||
# certain renderables should start on a new line
|
||||
if _safe_isinstance(value, ConsoleRenderable):
|
||||
console.line()
|
||||
|
||||
console.print(
|
||||
value
|
||||
if _safe_isinstance(value, RichRenderable)
|
||||
else Pretty(
|
||||
value,
|
||||
overflow=overflow,
|
||||
indent_guides=indent_guides,
|
||||
max_length=max_length,
|
||||
max_string=max_string,
|
||||
max_depth=max_depth,
|
||||
expand_all=expand_all,
|
||||
margin=12,
|
||||
),
|
||||
crop=crop,
|
||||
new_line_start=True,
|
||||
)
|
||||
with console.capture() as capture:
|
||||
# certain renderables should start on a new line
|
||||
if _safe_isinstance(value, ConsoleRenderable):
|
||||
console.line()
|
||||
console.print(
|
||||
value
|
||||
if _safe_isinstance(value, RichRenderable)
|
||||
else Pretty(
|
||||
value,
|
||||
overflow=overflow,
|
||||
indent_guides=indent_guides,
|
||||
max_length=max_length,
|
||||
max_string=max_string,
|
||||
max_depth=max_depth,
|
||||
expand_all=expand_all,
|
||||
margin=12,
|
||||
),
|
||||
crop=crop,
|
||||
new_line_start=True,
|
||||
end="",
|
||||
)
|
||||
# strip trailing newline, not usually part of a text repr
|
||||
# I'm not sure if this should be prevented at a lower level
|
||||
return capture.get().rstrip("\n")
|
||||
|
||||
|
||||
def _safe_isinstance(
|
||||
@@ -247,7 +211,7 @@ def install(
|
||||
)
|
||||
builtins._ = value # type: ignore[attr-defined]
|
||||
|
||||
try: # pragma: no cover
|
||||
if "get_ipython" in globals():
|
||||
ip = get_ipython() # type: ignore[name-defined]
|
||||
from IPython.core.formatters import BaseFormatter
|
||||
|
||||
@@ -272,7 +236,7 @@ def install(
|
||||
# replace plain text formatter with rich formatter
|
||||
rich_formatter = RichFormatter()
|
||||
ip.display_formatter.formatters["text/plain"] = rich_formatter
|
||||
except Exception:
|
||||
else:
|
||||
sys.displayhook = display_hook
|
||||
|
||||
|
||||
@@ -371,6 +335,7 @@ class Pretty(JupyterMixin):
|
||||
indent_size=self.indent_size,
|
||||
max_length=self.max_length,
|
||||
max_string=self.max_string,
|
||||
max_depth=self.max_depth,
|
||||
expand_all=self.expand_all,
|
||||
)
|
||||
text_width = (
|
||||
@@ -433,7 +398,7 @@ class Node:
|
||||
is_tuple: bool = False
|
||||
is_namedtuple: bool = False
|
||||
children: Optional[List["Node"]] = None
|
||||
key_separator = ": "
|
||||
key_separator: str = ": "
|
||||
separator: str = ", "
|
||||
|
||||
def iter_tokens(self) -> Iterable[str]:
|
||||
@@ -642,7 +607,6 @@ def traverse(
|
||||
return Node(value_repr="...")
|
||||
|
||||
obj_type = type(obj)
|
||||
py_version = (sys.version_info.major, sys.version_info.minor)
|
||||
children: List[Node]
|
||||
reached_max_depth = max_depth is not None and depth >= max_depth
|
||||
|
||||
@@ -780,7 +744,7 @@ def traverse(
|
||||
is_dataclass(obj)
|
||||
and not _safe_isinstance(obj, type)
|
||||
and not fake_attributes
|
||||
and (_is_dataclass_repr(obj) or py_version == (3, 6))
|
||||
and _is_dataclass_repr(obj)
|
||||
):
|
||||
push_visited(obj_id)
|
||||
children = []
|
||||
@@ -793,6 +757,7 @@ def traverse(
|
||||
close_brace=")",
|
||||
children=children,
|
||||
last=root,
|
||||
empty=f"{obj.__class__.__name__}()",
|
||||
)
|
||||
|
||||
for last, field in loop_last(
|
||||
|
||||
@@ -4,12 +4,12 @@ import typing
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import deque
|
||||
from collections.abc import Sized
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
from io import RawIOBase, UnsupportedOperation
|
||||
from math import ceil
|
||||
from mmap import mmap
|
||||
from operator import length_hint
|
||||
from os import PathLike, stat
|
||||
from threading import Event, RLock, Thread
|
||||
from types import TracebackType
|
||||
@@ -151,7 +151,7 @@ def track(
|
||||
pulse_style=pulse_style,
|
||||
),
|
||||
TaskProgressColumn(show_speed=show_speed),
|
||||
TimeRemainingColumn(),
|
||||
TimeRemainingColumn(elapsed_when_finished=True),
|
||||
)
|
||||
)
|
||||
progress = Progress(
|
||||
@@ -677,7 +677,7 @@ class TimeElapsedColumn(ProgressColumn):
|
||||
"""Renders time elapsed."""
|
||||
|
||||
def render(self, task: "Task") -> Text:
|
||||
"""Show time remaining."""
|
||||
"""Show time elapsed."""
|
||||
elapsed = task.finished_time if task.finished else task.elapsed
|
||||
if elapsed is None:
|
||||
return Text("-:--:--", style="progress.elapsed")
|
||||
@@ -1197,18 +1197,13 @@ class Progress(JupyterMixin):
|
||||
Returns:
|
||||
Iterable[ProgressType]: An iterable of values taken from the provided sequence.
|
||||
"""
|
||||
|
||||
task_total: Optional[float] = None
|
||||
if total is None:
|
||||
if isinstance(sequence, Sized):
|
||||
task_total = float(len(sequence))
|
||||
else:
|
||||
task_total = total
|
||||
total = float(length_hint(sequence)) or None
|
||||
|
||||
if task_id is None:
|
||||
task_id = self.add_task(description, total=task_total)
|
||||
task_id = self.add_task(description, total=total)
|
||||
else:
|
||||
self.update(task_id, total=task_total)
|
||||
self.update(task_id, total=total)
|
||||
|
||||
if self.live.auto_refresh:
|
||||
with _TrackThread(self, task_id, update_period) as track_thread:
|
||||
@@ -1342,7 +1337,7 @@ class Progress(JupyterMixin):
|
||||
RuntimeWarning,
|
||||
)
|
||||
buffering = -1
|
||||
elif _mode == "rt" or _mode == "r":
|
||||
elif _mode in ("rt", "r"):
|
||||
if buffering == 0:
|
||||
raise ValueError("can't have unbuffered text I/O")
|
||||
elif buffering == 1:
|
||||
@@ -1363,7 +1358,7 @@ class Progress(JupyterMixin):
|
||||
reader = _Reader(handle, self, task_id, close_handle=True)
|
||||
|
||||
# wrap the reader in a `TextIOWrapper` if text mode
|
||||
if mode == "r" or mode == "rt":
|
||||
if mode in ("r", "rt"):
|
||||
return io.TextIOWrapper(
|
||||
reader,
|
||||
encoding=encoding,
|
||||
|
||||
@@ -55,7 +55,7 @@ def auto(
|
||||
if key is None:
|
||||
append(repr(value))
|
||||
else:
|
||||
if len(default) and default[0] == value:
|
||||
if default and default[0] == value:
|
||||
continue
|
||||
append(f"{key}={value!r}")
|
||||
else:
|
||||
|
||||
@@ -51,13 +51,9 @@ class Rule(JupyterMixin):
|
||||
) -> RenderResult:
|
||||
width = options.max_width
|
||||
|
||||
# Python3.6 doesn't have an isascii method on str
|
||||
isascii = getattr(str, "isascii", None) or (
|
||||
lambda s: all(ord(c) < 128 for c in s)
|
||||
)
|
||||
characters = (
|
||||
"-"
|
||||
if (options.ascii_only and not isascii(self.characters))
|
||||
if (options.ascii_only and not self.characters.isascii())
|
||||
else self.characters
|
||||
)
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ class Segment(NamedTuple):
|
||||
|
||||
cell_size = get_character_cell_size
|
||||
|
||||
pos = int((cut / cell_length) * len(text))
|
||||
pos = int((cut / cell_length) * (len(text) - 1))
|
||||
|
||||
before = text[:pos]
|
||||
cell_pos = cell_len(before)
|
||||
@@ -303,7 +303,7 @@ class Segment(NamedTuple):
|
||||
if include_new_lines:
|
||||
cropped_line.append(new_line_segment)
|
||||
yield cropped_line
|
||||
del line[:]
|
||||
line.clear()
|
||||
else:
|
||||
append(segment)
|
||||
if line:
|
||||
@@ -365,7 +365,7 @@ class Segment(NamedTuple):
|
||||
int: The length of the line.
|
||||
"""
|
||||
_cell_len = cell_len
|
||||
return sum(_cell_len(segment.text) for segment in line)
|
||||
return sum(_cell_len(text) for text, style, control in line if not control)
|
||||
|
||||
@classmethod
|
||||
def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
|
||||
@@ -727,7 +727,7 @@ console.print(text)"""
|
||||
console.print(Syntax(code, "python", line_numbers=True))
|
||||
console.print()
|
||||
console.print(
|
||||
"When you call [b]print()[/b], Rich [i]renders[/i] the object in to the the following:\n"
|
||||
"When you call [b]print()[/b], Rich [i]renders[/i] the object in to the following:\n"
|
||||
)
|
||||
fragments = list(console.render(text))
|
||||
console.print(fragments)
|
||||
|
||||
@@ -11,6 +11,18 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class Spinner:
|
||||
"""A spinner animation.
|
||||
|
||||
Args:
|
||||
name (str): Name of spinner (run python -m rich.spinner).
|
||||
text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
|
||||
style (StyleType, optional): Style for spinner animation. Defaults to None.
|
||||
speed (float, optional): Speed factor for animation. Defaults to 1.0.
|
||||
|
||||
Raises:
|
||||
KeyError: If name isn't one of the supported spinner animations.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
@@ -19,17 +31,6 @@ class Spinner:
|
||||
style: Optional["StyleType"] = None,
|
||||
speed: float = 1.0,
|
||||
) -> None:
|
||||
"""A spinner animation.
|
||||
|
||||
Args:
|
||||
name (str): Name of spinner (run python -m rich.spinner).
|
||||
text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
|
||||
style (StyleType, optional): Style for spinner animation. Defaults to None.
|
||||
speed (float, optional): Speed factor for animation. Defaults to 1.0.
|
||||
|
||||
Raises:
|
||||
KeyError: If name isn't one of the supported spinner animations.
|
||||
"""
|
||||
try:
|
||||
spinner = SPINNERS[name]
|
||||
except KeyError:
|
||||
|
||||
@@ -645,6 +645,29 @@ class Style:
|
||||
style._meta = self._meta
|
||||
return style
|
||||
|
||||
@lru_cache(maxsize=128)
|
||||
def clear_meta_and_links(self) -> "Style":
|
||||
"""Get a copy of this style with link and meta information removed.
|
||||
|
||||
Returns:
|
||||
Style: New style object.
|
||||
"""
|
||||
if self._null:
|
||||
return NULL_STYLE
|
||||
style: Style = self.__new__(Style)
|
||||
style._ansi = self._ansi
|
||||
style._style_definition = self._style_definition
|
||||
style._color = self._color
|
||||
style._bgcolor = self._bgcolor
|
||||
style._attributes = self._attributes
|
||||
style._set_attributes = self._set_attributes
|
||||
style._link = None
|
||||
style._link_id = ""
|
||||
style._hash = self._hash
|
||||
style._null = False
|
||||
style._meta = None
|
||||
return style
|
||||
|
||||
def update_link(self, link: Optional[str] = None) -> "Style":
|
||||
"""Get a copy with a different value for link.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
import sys
|
||||
import textwrap
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
@@ -338,8 +339,7 @@ class Syntax(JupyterMixin):
|
||||
Returns:
|
||||
[Syntax]: A Syntax object that may be printed to the console
|
||||
"""
|
||||
with open(path, "rt", encoding=encoding) as code_file:
|
||||
code = code_file.read()
|
||||
code = Path(path).read_text(encoding=encoding)
|
||||
|
||||
if not lexer:
|
||||
lexer = cls.guess_lexer(path, code=code)
|
||||
@@ -494,7 +494,10 @@ class Syntax(JupyterMixin):
|
||||
|
||||
# Skip over tokens until line start
|
||||
while line_no < _line_start:
|
||||
_token_type, token = next(tokens)
|
||||
try:
|
||||
_token_type, token = next(tokens)
|
||||
except StopIteration:
|
||||
break
|
||||
yield (token, None)
|
||||
if token.endswith("\n"):
|
||||
line_no += 1
|
||||
@@ -671,6 +674,8 @@ class Syntax(JupyterMixin):
|
||||
line_offset = max(0, start_line - 1)
|
||||
lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl)
|
||||
if self.line_range:
|
||||
if line_offset > len(lines):
|
||||
return
|
||||
lines = lines[line_offset:end_line]
|
||||
|
||||
if self.indent_guides and not options.ascii_only:
|
||||
|
||||
@@ -53,11 +53,7 @@ class Span(NamedTuple):
|
||||
"""Style associated with the span."""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"Span({self.start}, {self.end}, {self.style!r})"
|
||||
if (isinstance(self.style, Style) and self.style._meta)
|
||||
else f"Span({self.start}, {self.end}, {repr(self.style)})"
|
||||
)
|
||||
return f"Span({self.start}, {self.end}, {self.style!r})"
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return self.end > self.start
|
||||
@@ -1204,7 +1200,7 @@ class Text(JupyterMixin):
|
||||
width (int): Maximum characters in a line.
|
||||
|
||||
Returns:
|
||||
Lines: List of lines.
|
||||
Lines: Lines container.
|
||||
"""
|
||||
lines: Lines = Lines()
|
||||
append = lines.append
|
||||
|
||||
@@ -56,17 +56,20 @@ class Theme:
|
||||
return theme
|
||||
|
||||
@classmethod
|
||||
def read(cls, path: str, inherit: bool = True) -> "Theme":
|
||||
def read(
|
||||
cls, path: str, inherit: bool = True, encoding: Optional[str] = None
|
||||
) -> "Theme":
|
||||
"""Read a theme from a path.
|
||||
|
||||
Args:
|
||||
path (str): Path to a config file readable by Python configparser module.
|
||||
inherit (bool, optional): Inherit default styles. Defaults to True.
|
||||
encoding (str, optional): Encoding of the config file. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Theme: A new theme instance.
|
||||
"""
|
||||
with open(path, "rt") as config_file:
|
||||
with open(path, "rt", encoding=encoding) as config_file:
|
||||
return cls.from_file(config_file, source=path, inherit=inherit)
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import linecache
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from traceback import walk_tb
|
||||
from types import ModuleType, TracebackType
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Type, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
from pipenv.patched.pip._vendor.pygments.lexers import guess_lexer_for_filename
|
||||
from pipenv.patched.pip._vendor.pygments.token import Comment, Keyword, Name, Number, Operator, String
|
||||
@@ -41,6 +53,10 @@ def install(
|
||||
theme: Optional[str] = None,
|
||||
word_wrap: bool = False,
|
||||
show_locals: bool = False,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: Optional[bool] = None,
|
||||
indent_guides: bool = True,
|
||||
suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
max_frames: int = 100,
|
||||
@@ -58,6 +74,11 @@ def install(
|
||||
a theme appropriate for the platform.
|
||||
word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
|
||||
show_locals (bool, optional): Enable display of local variables. Defaults to False.
|
||||
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
|
||||
Defaults to 10.
|
||||
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
|
||||
suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
|
||||
@@ -65,7 +86,13 @@ def install(
|
||||
Callable: The previous exception handler that was replaced.
|
||||
|
||||
"""
|
||||
traceback_console = Console(file=sys.stderr) if console is None else console
|
||||
traceback_console = Console(stderr=True) if console is None else console
|
||||
|
||||
locals_hide_sunder = (
|
||||
True
|
||||
if (traceback_console.is_jupyter and locals_hide_sunder is None)
|
||||
else locals_hide_sunder
|
||||
)
|
||||
|
||||
def excepthook(
|
||||
type_: Type[BaseException],
|
||||
@@ -82,6 +109,10 @@ def install(
|
||||
theme=theme,
|
||||
word_wrap=word_wrap,
|
||||
show_locals=show_locals,
|
||||
locals_max_length=locals_max_length,
|
||||
locals_max_string=locals_max_string,
|
||||
locals_hide_dunder=locals_hide_dunder,
|
||||
locals_hide_sunder=bool(locals_hide_sunder),
|
||||
indent_guides=indent_guides,
|
||||
suppress=suppress,
|
||||
max_frames=max_frames,
|
||||
@@ -192,6 +223,8 @@ class Traceback:
|
||||
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
|
||||
Defaults to 10.
|
||||
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
|
||||
|
||||
@@ -208,14 +241,17 @@ class Traceback:
|
||||
def __init__(
|
||||
self,
|
||||
trace: Optional[Trace] = None,
|
||||
*,
|
||||
width: Optional[int] = 100,
|
||||
extra_lines: int = 3,
|
||||
theme: Optional[str] = None,
|
||||
word_wrap: bool = False,
|
||||
show_locals: bool = False,
|
||||
indent_guides: bool = True,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: bool = False,
|
||||
indent_guides: bool = True,
|
||||
suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
max_frames: int = 100,
|
||||
):
|
||||
@@ -237,6 +273,8 @@ class Traceback:
|
||||
self.indent_guides = indent_guides
|
||||
self.locals_max_length = locals_max_length
|
||||
self.locals_max_string = locals_max_string
|
||||
self.locals_hide_dunder = locals_hide_dunder
|
||||
self.locals_hide_sunder = locals_hide_sunder
|
||||
|
||||
self.suppress: Sequence[str] = []
|
||||
for suppress_entity in suppress:
|
||||
@@ -257,14 +295,17 @@ class Traceback:
|
||||
exc_type: Type[Any],
|
||||
exc_value: BaseException,
|
||||
traceback: Optional[TracebackType],
|
||||
*,
|
||||
width: Optional[int] = 100,
|
||||
extra_lines: int = 3,
|
||||
theme: Optional[str] = None,
|
||||
word_wrap: bool = False,
|
||||
show_locals: bool = False,
|
||||
indent_guides: bool = True,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: bool = False,
|
||||
indent_guides: bool = True,
|
||||
suppress: Iterable[Union[str, ModuleType]] = (),
|
||||
max_frames: int = 100,
|
||||
) -> "Traceback":
|
||||
@@ -283,6 +324,8 @@ class Traceback:
|
||||
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
|
||||
Defaults to 10.
|
||||
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
|
||||
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
|
||||
|
||||
@@ -290,8 +333,16 @@ class Traceback:
|
||||
Traceback: A Traceback instance that may be printed.
|
||||
"""
|
||||
rich_traceback = cls.extract(
|
||||
exc_type, exc_value, traceback, show_locals=show_locals
|
||||
exc_type,
|
||||
exc_value,
|
||||
traceback,
|
||||
show_locals=show_locals,
|
||||
locals_max_length=locals_max_length,
|
||||
locals_max_string=locals_max_string,
|
||||
locals_hide_dunder=locals_hide_dunder,
|
||||
locals_hide_sunder=locals_hide_sunder,
|
||||
)
|
||||
|
||||
return cls(
|
||||
rich_traceback,
|
||||
width=width,
|
||||
@@ -302,6 +353,8 @@ class Traceback:
|
||||
indent_guides=indent_guides,
|
||||
locals_max_length=locals_max_length,
|
||||
locals_max_string=locals_max_string,
|
||||
locals_hide_dunder=locals_hide_dunder,
|
||||
locals_hide_sunder=locals_hide_sunder,
|
||||
suppress=suppress,
|
||||
max_frames=max_frames,
|
||||
)
|
||||
@@ -312,9 +365,12 @@ class Traceback:
|
||||
exc_type: Type[BaseException],
|
||||
exc_value: BaseException,
|
||||
traceback: Optional[TracebackType],
|
||||
*,
|
||||
show_locals: bool = False,
|
||||
locals_max_length: int = LOCALS_MAX_LENGTH,
|
||||
locals_max_string: int = LOCALS_MAX_STRING,
|
||||
locals_hide_dunder: bool = True,
|
||||
locals_hide_sunder: bool = False,
|
||||
) -> Trace:
|
||||
"""Extract traceback information.
|
||||
|
||||
@@ -326,6 +382,8 @@ class Traceback:
|
||||
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
|
||||
Defaults to 10.
|
||||
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
|
||||
locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
|
||||
locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
|
||||
|
||||
Returns:
|
||||
Trace: A Trace instance which you can use to construct a `Traceback`.
|
||||
@@ -362,6 +420,20 @@ class Traceback:
|
||||
stacks.append(stack)
|
||||
append = stack.frames.append
|
||||
|
||||
def get_locals(
|
||||
iter_locals: Iterable[Tuple[str, object]]
|
||||
) -> Iterable[Tuple[str, object]]:
|
||||
"""Extract locals from an iterator of key pairs."""
|
||||
if not (locals_hide_dunder or locals_hide_sunder):
|
||||
yield from iter_locals
|
||||
return
|
||||
for key, value in iter_locals:
|
||||
if locals_hide_dunder and key.startswith("__"):
|
||||
continue
|
||||
if locals_hide_sunder and key.startswith("_"):
|
||||
continue
|
||||
yield key, value
|
||||
|
||||
for frame_summary, line_no in walk_tb(traceback):
|
||||
filename = frame_summary.f_code.co_filename
|
||||
if filename and not filename.startswith("<"):
|
||||
@@ -369,6 +441,7 @@ class Traceback:
|
||||
filename = os.path.join(_IMPORT_CWD, filename)
|
||||
if frame_summary.f_locals.get("_rich_traceback_omit", False):
|
||||
continue
|
||||
|
||||
frame = Frame(
|
||||
filename=filename or "?",
|
||||
lineno=line_no,
|
||||
@@ -379,7 +452,7 @@ class Traceback:
|
||||
max_length=locals_max_length,
|
||||
max_string=locals_max_string,
|
||||
)
|
||||
for key, value in frame_summary.f_locals.items()
|
||||
for key, value in get_locals(frame_summary.f_locals.items())
|
||||
}
|
||||
if show_locals
|
||||
else None,
|
||||
@@ -494,13 +567,14 @@ class Traceback:
|
||||
highlighter = ReprHighlighter()
|
||||
path_highlighter = PathHighlighter()
|
||||
if syntax_error.filename != "<stdin>":
|
||||
text = Text.assemble(
|
||||
(f" {syntax_error.filename}", "pygments.string"),
|
||||
(":", "pygments.text"),
|
||||
(str(syntax_error.lineno), "pygments.number"),
|
||||
style="pygments.text",
|
||||
)
|
||||
yield path_highlighter(text)
|
||||
if os.path.exists(syntax_error.filename):
|
||||
text = Text.assemble(
|
||||
(f" {syntax_error.filename}", "pygments.string"),
|
||||
(":", "pygments.text"),
|
||||
(str(syntax_error.lineno), "pygments.number"),
|
||||
style="pygments.text",
|
||||
)
|
||||
yield path_highlighter(text)
|
||||
syntax_error_text = highlighter(syntax_error.line.rstrip())
|
||||
syntax_error_text.no_wrap = True
|
||||
offset = min(syntax_error.offset - 1, len(syntax_error_text))
|
||||
@@ -531,7 +605,6 @@ class Traceback:
|
||||
def _render_stack(self, stack: Stack) -> RenderResult:
|
||||
path_highlighter = PathHighlighter()
|
||||
theme = self.theme
|
||||
code_cache: Dict[str, str] = {}
|
||||
|
||||
def read_code(filename: str) -> str:
|
||||
"""Read files, and cache results on filename.
|
||||
@@ -542,14 +615,7 @@ class Traceback:
|
||||
Returns:
|
||||
str: Contents of file
|
||||
"""
|
||||
code = code_cache.get(filename)
|
||||
if code is None:
|
||||
with open(
|
||||
filename, "rt", encoding="utf-8", errors="replace"
|
||||
) as code_file:
|
||||
code = code_file.read()
|
||||
code_cache[filename] = code
|
||||
return code
|
||||
return "".join(linecache.getlines(filename))
|
||||
|
||||
def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
|
||||
if frame.locals:
|
||||
@@ -588,14 +654,23 @@ class Traceback:
|
||||
frame_filename = frame.filename
|
||||
suppressed = any(frame_filename.startswith(path) for path in self.suppress)
|
||||
|
||||
text = Text.assemble(
|
||||
path_highlighter(Text(frame.filename, style="pygments.string")),
|
||||
(":", "pygments.text"),
|
||||
(str(frame.lineno), "pygments.number"),
|
||||
" in ",
|
||||
(frame.name, "pygments.function"),
|
||||
style="pygments.text",
|
||||
)
|
||||
if os.path.exists(frame.filename):
|
||||
text = Text.assemble(
|
||||
path_highlighter(Text(frame.filename, style="pygments.string")),
|
||||
(":", "pygments.text"),
|
||||
(str(frame.lineno), "pygments.number"),
|
||||
" in ",
|
||||
(frame.name, "pygments.function"),
|
||||
style="pygments.text",
|
||||
)
|
||||
else:
|
||||
text = Text.assemble(
|
||||
"in ",
|
||||
(frame.name, "pygments.function"),
|
||||
(":", "pygments.text"),
|
||||
(str(frame.lineno), "pygments.number"),
|
||||
style="pygments.text",
|
||||
)
|
||||
if not frame.filename.startswith("<") and not first:
|
||||
yield ""
|
||||
yield text
|
||||
@@ -605,6 +680,10 @@ class Traceback:
|
||||
if not suppressed:
|
||||
try:
|
||||
code = read_code(frame.filename)
|
||||
if not code:
|
||||
# code may be an empty string if the file doesn't exist, OR
|
||||
# if the traceback filename is generated dynamically
|
||||
continue
|
||||
lexer_name = self._guess_lexer(frame.filename, code)
|
||||
syntax = Syntax(
|
||||
code,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import functools
|
||||
import sys
|
||||
import threading
|
||||
@@ -88,51 +89,13 @@ tornado = None # type: ignore
|
||||
if t.TYPE_CHECKING:
|
||||
import types
|
||||
|
||||
from .wait import wait_base
|
||||
from .stop import stop_base
|
||||
from .retry import RetryBaseT
|
||||
from .stop import StopBaseT
|
||||
from .wait import WaitBaseT
|
||||
|
||||
|
||||
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable)
|
||||
_RetValT = t.TypeVar("_RetValT")
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(fn: WrappedFn) -> WrappedFn:
|
||||
pass
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(*dargs: t.Any, **dkw: t.Any) -> t.Callable[[WrappedFn], WrappedFn]: # noqa
|
||||
pass
|
||||
|
||||
|
||||
def retry(*dargs: t.Any, **dkw: t.Any) -> t.Union[WrappedFn, t.Callable[[WrappedFn], WrappedFn]]: # noqa
|
||||
"""Wrap a function with a new `Retrying` object.
|
||||
|
||||
:param dargs: positional arguments passed to Retrying object
|
||||
:param dkw: keyword arguments passed to the Retrying object
|
||||
"""
|
||||
# support both @retry and @retry() as valid syntax
|
||||
if len(dargs) == 1 and callable(dargs[0]):
|
||||
return retry()(dargs[0])
|
||||
else:
|
||||
|
||||
def wrap(f: WrappedFn) -> WrappedFn:
|
||||
if isinstance(f, retry_base):
|
||||
warnings.warn(
|
||||
f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
|
||||
f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
|
||||
)
|
||||
if iscoroutinefunction(f):
|
||||
r: "BaseRetrying" = AsyncRetrying(*dargs, **dkw)
|
||||
elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f):
|
||||
r = TornadoRetrying(*dargs, **dkw)
|
||||
else:
|
||||
r = Retrying(*dargs, **dkw)
|
||||
|
||||
return r.wraps(f)
|
||||
|
||||
return wrap
|
||||
WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
|
||||
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
|
||||
|
||||
|
||||
class TryAgain(Exception):
|
||||
@@ -216,7 +179,7 @@ class AttemptManager:
|
||||
exc_value: t.Optional[BaseException],
|
||||
traceback: t.Optional["types.TracebackType"],
|
||||
) -> t.Optional[bool]:
|
||||
if isinstance(exc_value, BaseException):
|
||||
if exc_type is not None and exc_value is not None:
|
||||
self.retry_state.set_exception((exc_type, exc_value, traceback))
|
||||
return True # Swallow exception.
|
||||
else:
|
||||
@@ -229,9 +192,9 @@ class BaseRetrying(ABC):
|
||||
def __init__(
|
||||
self,
|
||||
sleep: t.Callable[[t.Union[int, float]], None] = sleep,
|
||||
stop: "stop_base" = stop_never,
|
||||
wait: "wait_base" = wait_none(),
|
||||
retry: retry_base = retry_if_exception_type(),
|
||||
stop: "StopBaseT" = stop_never,
|
||||
wait: "WaitBaseT" = wait_none(),
|
||||
retry: "RetryBaseT" = retry_if_exception_type(),
|
||||
before: t.Callable[["RetryCallState"], None] = before_nothing,
|
||||
after: t.Callable[["RetryCallState"], None] = after_nothing,
|
||||
before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
|
||||
@@ -254,8 +217,8 @@ class BaseRetrying(ABC):
|
||||
def copy(
|
||||
self,
|
||||
sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset,
|
||||
stop: t.Union["stop_base", object] = _unset,
|
||||
wait: t.Union["wait_base", object] = _unset,
|
||||
stop: t.Union["StopBaseT", object] = _unset,
|
||||
wait: t.Union["WaitBaseT", object] = _unset,
|
||||
retry: t.Union[retry_base, object] = _unset,
|
||||
before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
|
||||
after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
|
||||
@@ -312,9 +275,9 @@ class BaseRetrying(ABC):
|
||||
statistics from each thread).
|
||||
"""
|
||||
try:
|
||||
return self._local.statistics
|
||||
return self._local.statistics # type: ignore[no-any-return]
|
||||
except AttributeError:
|
||||
self._local.statistics = {}
|
||||
self._local.statistics = t.cast(t.Dict[str, t.Any], {})
|
||||
return self._local.statistics
|
||||
|
||||
def wraps(self, f: WrappedFn) -> WrappedFn:
|
||||
@@ -330,10 +293,10 @@ class BaseRetrying(ABC):
|
||||
def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
|
||||
return self.copy(*args, **kwargs).wraps(f)
|
||||
|
||||
wrapped_f.retry = self
|
||||
wrapped_f.retry_with = retry_with
|
||||
wrapped_f.retry = self # type: ignore[attr-defined]
|
||||
wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
|
||||
|
||||
return wrapped_f
|
||||
return wrapped_f # type: ignore[return-value]
|
||||
|
||||
def begin(self) -> None:
|
||||
self.statistics.clear()
|
||||
@@ -348,15 +311,15 @@ class BaseRetrying(ABC):
|
||||
self.before(retry_state)
|
||||
return DoAttempt()
|
||||
|
||||
is_explicit_retry = retry_state.outcome.failed and isinstance(retry_state.outcome.exception(), TryAgain)
|
||||
if not (is_explicit_retry or self.retry(retry_state=retry_state)):
|
||||
is_explicit_retry = fut.failed and isinstance(fut.exception(), TryAgain)
|
||||
if not (is_explicit_retry or self.retry(retry_state)):
|
||||
return fut.result()
|
||||
|
||||
if self.after is not None:
|
||||
self.after(retry_state)
|
||||
|
||||
self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
|
||||
if self.stop(retry_state=retry_state):
|
||||
if self.stop(retry_state):
|
||||
if self.retry_error_callback:
|
||||
return self.retry_error_callback(retry_state)
|
||||
retry_exc = self.retry_error_cls(fut)
|
||||
@@ -365,7 +328,7 @@ class BaseRetrying(ABC):
|
||||
raise retry_exc from fut.exception()
|
||||
|
||||
if self.wait:
|
||||
sleep = self.wait(retry_state=retry_state)
|
||||
sleep = self.wait(retry_state)
|
||||
else:
|
||||
sleep = 0.0
|
||||
retry_state.next_action = RetryAction(sleep)
|
||||
@@ -393,14 +356,24 @@ class BaseRetrying(ABC):
|
||||
break
|
||||
|
||||
@abstractmethod
|
||||
def __call__(self, fn: t.Callable[..., _RetValT], *args: t.Any, **kwargs: t.Any) -> _RetValT:
|
||||
def __call__(
|
||||
self,
|
||||
fn: t.Callable[..., WrappedFnReturnT],
|
||||
*args: t.Any,
|
||||
**kwargs: t.Any,
|
||||
) -> WrappedFnReturnT:
|
||||
pass
|
||||
|
||||
|
||||
class Retrying(BaseRetrying):
|
||||
"""Retrying controller."""
|
||||
|
||||
def __call__(self, fn: t.Callable[..., _RetValT], *args: t.Any, **kwargs: t.Any) -> _RetValT:
|
||||
def __call__(
|
||||
self,
|
||||
fn: t.Callable[..., WrappedFnReturnT],
|
||||
*args: t.Any,
|
||||
**kwargs: t.Any,
|
||||
) -> WrappedFnReturnT:
|
||||
self.begin()
|
||||
|
||||
retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
|
||||
@@ -410,17 +383,23 @@ class Retrying(BaseRetrying):
|
||||
try:
|
||||
result = fn(*args, **kwargs)
|
||||
except BaseException: # noqa: B902
|
||||
retry_state.set_exception(sys.exc_info())
|
||||
retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
|
||||
else:
|
||||
retry_state.set_result(result)
|
||||
elif isinstance(do, DoSleep):
|
||||
retry_state.prepare_for_next_attempt()
|
||||
self.sleep(do)
|
||||
else:
|
||||
return do
|
||||
return do # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class Future(futures.Future):
|
||||
if sys.version_info[1] >= 9:
|
||||
FutureGenericT = futures.Future[t.Any]
|
||||
else:
|
||||
FutureGenericT = futures.Future
|
||||
|
||||
|
||||
class Future(FutureGenericT):
|
||||
"""Encapsulates a (future or past) attempted call to a target function."""
|
||||
|
||||
def __init__(self, attempt_number: int) -> None:
|
||||
@@ -493,13 +472,15 @@ class RetryCallState:
|
||||
fut.set_result(val)
|
||||
self.outcome, self.outcome_timestamp = fut, ts
|
||||
|
||||
def set_exception(self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType"]) -> None:
|
||||
def set_exception(
|
||||
self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType| None"]
|
||||
) -> None:
|
||||
ts = time.monotonic()
|
||||
fut = Future(self.attempt_number)
|
||||
fut.set_exception(exc_info[1])
|
||||
self.outcome, self.outcome_timestamp = fut, ts
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
if self.outcome is None:
|
||||
result = "none yet"
|
||||
elif self.outcome.failed:
|
||||
@@ -513,7 +494,115 @@ class RetryCallState:
|
||||
return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(func: WrappedFn) -> WrappedFn:
|
||||
...
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(
|
||||
sleep: t.Callable[[t.Union[int, float]], None] = sleep,
|
||||
stop: "StopBaseT" = stop_never,
|
||||
wait: "WaitBaseT" = wait_none(),
|
||||
retry: "RetryBaseT" = retry_if_exception_type(),
|
||||
before: t.Callable[["RetryCallState"], None] = before_nothing,
|
||||
after: t.Callable[["RetryCallState"], None] = after_nothing,
|
||||
before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
|
||||
reraise: bool = False,
|
||||
retry_error_cls: t.Type["RetryError"] = RetryError,
|
||||
retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
|
||||
) -> t.Callable[[WrappedFn], WrappedFn]:
|
||||
...
|
||||
|
||||
|
||||
def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
|
||||
"""Wrap a function with a new `Retrying` object.
|
||||
|
||||
:param dargs: positional arguments passed to Retrying object
|
||||
:param dkw: keyword arguments passed to the Retrying object
|
||||
"""
|
||||
# support both @retry and @retry() as valid syntax
|
||||
if len(dargs) == 1 and callable(dargs[0]):
|
||||
return retry()(dargs[0])
|
||||
else:
|
||||
|
||||
def wrap(f: WrappedFn) -> WrappedFn:
|
||||
if isinstance(f, retry_base):
|
||||
warnings.warn(
|
||||
f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
|
||||
f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
|
||||
)
|
||||
r: "BaseRetrying"
|
||||
if iscoroutinefunction(f):
|
||||
r = AsyncRetrying(*dargs, **dkw)
|
||||
elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f):
|
||||
r = TornadoRetrying(*dargs, **dkw)
|
||||
else:
|
||||
r = Retrying(*dargs, **dkw)
|
||||
|
||||
return r.wraps(f)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
from pipenv.patched.pip._vendor.tenacity._asyncio import AsyncRetrying # noqa:E402,I100
|
||||
|
||||
if tornado:
|
||||
from pipenv.patched.pip._vendor.tenacity.tornadoweb import TornadoRetrying
|
||||
|
||||
|
||||
__all__ = [
|
||||
"retry_base",
|
||||
"retry_all",
|
||||
"retry_always",
|
||||
"retry_any",
|
||||
"retry_if_exception",
|
||||
"retry_if_exception_type",
|
||||
"retry_if_exception_cause_type",
|
||||
"retry_if_not_exception_type",
|
||||
"retry_if_not_result",
|
||||
"retry_if_result",
|
||||
"retry_never",
|
||||
"retry_unless_exception_type",
|
||||
"retry_if_exception_message",
|
||||
"retry_if_not_exception_message",
|
||||
"sleep",
|
||||
"sleep_using_event",
|
||||
"stop_after_attempt",
|
||||
"stop_after_delay",
|
||||
"stop_all",
|
||||
"stop_any",
|
||||
"stop_never",
|
||||
"stop_when_event_set",
|
||||
"wait_chain",
|
||||
"wait_combine",
|
||||
"wait_exponential",
|
||||
"wait_fixed",
|
||||
"wait_incrementing",
|
||||
"wait_none",
|
||||
"wait_random",
|
||||
"wait_random_exponential",
|
||||
"wait_full_jitter",
|
||||
"wait_exponential_jitter",
|
||||
"before_log",
|
||||
"before_nothing",
|
||||
"after_log",
|
||||
"after_nothing",
|
||||
"before_sleep_log",
|
||||
"before_sleep_nothing",
|
||||
"retry",
|
||||
"WrappedFn",
|
||||
"TryAgain",
|
||||
"NO_RESULT",
|
||||
"DoAttempt",
|
||||
"DoSleep",
|
||||
"BaseAction",
|
||||
"RetryAction",
|
||||
"RetryError",
|
||||
"AttemptManager",
|
||||
"BaseRetrying",
|
||||
"Retrying",
|
||||
"Future",
|
||||
"RetryCallState",
|
||||
"AsyncRetrying",
|
||||
]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import functools
|
||||
import sys
|
||||
import typing
|
||||
import typing as t
|
||||
from asyncio import sleep
|
||||
|
||||
from pipenv.patched.pip._vendor.tenacity import AttemptManager
|
||||
@@ -26,21 +26,20 @@ from pipenv.patched.pip._vendor.tenacity import DoAttempt
|
||||
from pipenv.patched.pip._vendor.tenacity import DoSleep
|
||||
from pipenv.patched.pip._vendor.tenacity import RetryCallState
|
||||
|
||||
WrappedFn = typing.TypeVar("WrappedFn", bound=typing.Callable)
|
||||
_RetValT = typing.TypeVar("_RetValT")
|
||||
WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
|
||||
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]])
|
||||
|
||||
|
||||
class AsyncRetrying(BaseRetrying):
|
||||
def __init__(self, sleep: typing.Callable[[float], typing.Awaitable] = sleep, **kwargs: typing.Any) -> None:
|
||||
sleep: t.Callable[[float], t.Awaitable[t.Any]]
|
||||
|
||||
def __init__(self, sleep: t.Callable[[float], t.Awaitable[t.Any]] = sleep, **kwargs: t.Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.sleep = sleep
|
||||
|
||||
async def __call__( # type: ignore # Change signature from supertype
|
||||
self,
|
||||
fn: typing.Callable[..., typing.Awaitable[_RetValT]],
|
||||
*args: typing.Any,
|
||||
**kwargs: typing.Any,
|
||||
) -> _RetValT:
|
||||
async def __call__( # type: ignore[override]
|
||||
self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any
|
||||
) -> WrappedFnReturnT:
|
||||
self.begin()
|
||||
|
||||
retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
|
||||
@@ -50,21 +49,24 @@ class AsyncRetrying(BaseRetrying):
|
||||
try:
|
||||
result = await fn(*args, **kwargs)
|
||||
except BaseException: # noqa: B902
|
||||
retry_state.set_exception(sys.exc_info())
|
||||
retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
|
||||
else:
|
||||
retry_state.set_result(result)
|
||||
elif isinstance(do, DoSleep):
|
||||
retry_state.prepare_for_next_attempt()
|
||||
await self.sleep(do)
|
||||
else:
|
||||
return do
|
||||
return do # type: ignore[no-any-return]
|
||||
|
||||
def __iter__(self) -> t.Generator[AttemptManager, None, None]:
|
||||
raise TypeError("AsyncRetrying object is not iterable")
|
||||
|
||||
def __aiter__(self) -> "AsyncRetrying":
|
||||
self.begin()
|
||||
self._retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> typing.Union[AttemptManager, typing.Any]:
|
||||
async def __anext__(self) -> AttemptManager:
|
||||
while True:
|
||||
do = self.iter(retry_state=self._retry_state)
|
||||
if do is None:
|
||||
@@ -75,18 +77,18 @@ class AsyncRetrying(BaseRetrying):
|
||||
self._retry_state.prepare_for_next_attempt()
|
||||
await self.sleep(do)
|
||||
else:
|
||||
return do
|
||||
raise StopAsyncIteration
|
||||
|
||||
def wraps(self, fn: WrappedFn) -> WrappedFn:
|
||||
fn = super().wraps(fn)
|
||||
# Ensure wrapper is recognized as a coroutine function.
|
||||
|
||||
@functools.wraps(fn)
|
||||
async def async_wrapped(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
||||
async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
return await fn(*args, **kwargs)
|
||||
|
||||
# Preserve attributes
|
||||
async_wrapped.retry = fn.retry
|
||||
async_wrapped.retry_with = fn.retry_with
|
||||
async_wrapped.retry = fn.retry # type: ignore[attr-defined]
|
||||
async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined]
|
||||
|
||||
return async_wrapped
|
||||
return async_wrapped # type: ignore[return-value]
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import sys
|
||||
import typing
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
# sys.maxsize:
|
||||
@@ -66,3 +67,10 @@ def get_callback_name(cb: typing.Callable[..., typing.Any]) -> str:
|
||||
except AttributeError:
|
||||
pass
|
||||
return ".".join(segments)
|
||||
|
||||
|
||||
time_unit_type = typing.Union[int, float, timedelta]
|
||||
|
||||
|
||||
def to_seconds(time_unit: time_unit_type) -> float:
|
||||
return float(time_unit.total_seconds() if isinstance(time_unit, timedelta) else time_unit)
|
||||
|
||||
@@ -36,9 +36,14 @@ def after_log(
|
||||
"""After call strategy that logs to some logger the finished attempt."""
|
||||
|
||||
def log_it(retry_state: "RetryCallState") -> None:
|
||||
if retry_state.fn is None:
|
||||
# NOTE(sileht): can't really happen, but we must please mypy
|
||||
fn_name = "<unknown>"
|
||||
else:
|
||||
fn_name = _utils.get_callback_name(retry_state.fn)
|
||||
logger.log(
|
||||
log_level,
|
||||
f"Finished call to '{_utils.get_callback_name(retry_state.fn)}' "
|
||||
f"Finished call to '{fn_name}' "
|
||||
f"after {sec_format % retry_state.seconds_since_start}(s), "
|
||||
f"this was the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.",
|
||||
)
|
||||
|
||||
@@ -32,9 +32,14 @@ def before_log(logger: "logging.Logger", log_level: int) -> typing.Callable[["Re
|
||||
"""Before call strategy that logs to some logger the attempt."""
|
||||
|
||||
def log_it(retry_state: "RetryCallState") -> None:
|
||||
if retry_state.fn is None:
|
||||
# NOTE(sileht): can't really happen, but we must please mypy
|
||||
fn_name = "<unknown>"
|
||||
else:
|
||||
fn_name = _utils.get_callback_name(retry_state.fn)
|
||||
logger.log(
|
||||
log_level,
|
||||
f"Starting call to '{_utils.get_callback_name(retry_state.fn)}', "
|
||||
f"Starting call to '{fn_name}', "
|
||||
f"this is the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.",
|
||||
)
|
||||
|
||||
|
||||
@@ -36,6 +36,14 @@ def before_sleep_log(
|
||||
"""Before call strategy that logs to some logger the attempt."""
|
||||
|
||||
def log_it(retry_state: "RetryCallState") -> None:
|
||||
local_exc_info: BaseException | bool | None
|
||||
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("log_it() called before outcome was set")
|
||||
|
||||
if retry_state.next_action is None:
|
||||
raise RuntimeError("log_it() called before next_action was set")
|
||||
|
||||
if retry_state.outcome.failed:
|
||||
ex = retry_state.outcome.exception()
|
||||
verb, value = "raised", f"{ex.__class__.__name__}: {ex}"
|
||||
@@ -48,10 +56,15 @@ def before_sleep_log(
|
||||
verb, value = "returned", retry_state.outcome.result()
|
||||
local_exc_info = False # exc_info does not apply when no exception
|
||||
|
||||
if retry_state.fn is None:
|
||||
# NOTE(sileht): can't really happen, but we must please mypy
|
||||
fn_name = "<unknown>"
|
||||
else:
|
||||
fn_name = _utils.get_callback_name(retry_state.fn)
|
||||
|
||||
logger.log(
|
||||
log_level,
|
||||
f"Retrying {_utils.get_callback_name(retry_state.fn)} "
|
||||
f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.",
|
||||
f"Retrying {fn_name} " f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.",
|
||||
exc_info=local_exc_info,
|
||||
)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user