mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Vendor in latest packages available (#5657)
* Update to the latest vendoring package versions available. * add news fragment * fix vendoring CI
This commit is contained in:
@@ -76,7 +76,7 @@ jobs:
|
||||
with:
|
||||
api_key: ${{ secrets.FORESIGHT_API_KEY }}
|
||||
- run: |
|
||||
python -m pip install --upgrade wheel invoke parver beautifulsoup4 vistir towncrier requests parse
|
||||
python -m pip install --upgrade wheel invoke parver beautifulsoup4 vistir towncrier requests parse hatch-fancy-pypi-readme
|
||||
python -m invoke vendoring.update
|
||||
tests:
|
||||
name: ${{matrix.os}} / ${{ matrix.python-version }}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Vendor in latest available dependencies: ``attrs==23.1.0`` ``click-didyoumean==0.3.0`` ``click==8.1.3`` ``markupsafe==2.1.2`` ``pipdeptree==2.7.0`` ``shellingham==1.5.0.post1`` ``tomlkit==0.11.7``
|
||||
Vendored
+74
-21
@@ -1,9 +1,11 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
import sys
|
||||
"""
|
||||
Classes Without Boilerplate
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
from typing import Callable
|
||||
|
||||
from . import converters, exceptions, filters, setters, validators
|
||||
from ._cmp import cmp_using
|
||||
@@ -20,31 +22,22 @@ from ._make import (
|
||||
make_class,
|
||||
validate,
|
||||
)
|
||||
from ._next_gen import define, field, frozen, mutable
|
||||
from ._version_info import VersionInfo
|
||||
|
||||
|
||||
__version__ = "22.1.0"
|
||||
__version_info__ = VersionInfo._from_version_string(__version__)
|
||||
|
||||
__title__ = "attrs"
|
||||
__description__ = "Classes Without Boilerplate"
|
||||
__url__ = "https://www.attrs.org/"
|
||||
__uri__ = __url__
|
||||
__doc__ = __description__ + " <" + __uri__ + ">"
|
||||
|
||||
__author__ = "Hynek Schlawack"
|
||||
__email__ = "hs@ox.cx"
|
||||
|
||||
__license__ = "MIT"
|
||||
__copyright__ = "Copyright (c) 2015 Hynek Schlawack"
|
||||
|
||||
|
||||
s = attributes = attrs
|
||||
ib = attr = attrib
|
||||
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
|
||||
|
||||
|
||||
class AttrsInstance:
|
||||
pass
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Attribute",
|
||||
"AttrsInstance",
|
||||
"Factory",
|
||||
"NOTHING",
|
||||
"asdict",
|
||||
@@ -56,15 +49,19 @@ __all__ = [
|
||||
"attrs",
|
||||
"cmp_using",
|
||||
"converters",
|
||||
"define",
|
||||
"evolve",
|
||||
"exceptions",
|
||||
"field",
|
||||
"fields",
|
||||
"fields_dict",
|
||||
"filters",
|
||||
"frozen",
|
||||
"get_run_validators",
|
||||
"has",
|
||||
"ib",
|
||||
"make_class",
|
||||
"mutable",
|
||||
"resolve_types",
|
||||
"s",
|
||||
"set_run_validators",
|
||||
@@ -73,7 +70,63 @@ __all__ = [
|
||||
"validators",
|
||||
]
|
||||
|
||||
if sys.version_info[:2] >= (3, 6):
|
||||
from ._next_gen import define, field, frozen, mutable # noqa: F401
|
||||
|
||||
__all__.extend(("define", "field", "frozen", "mutable"))
|
||||
def _make_getattr(mod_name: str) -> Callable:
|
||||
"""
|
||||
Create a metadata proxy for packaging information that uses *mod_name* in
|
||||
its warnings and errors.
|
||||
"""
|
||||
|
||||
def __getattr__(name: str) -> str:
|
||||
dunder_to_metadata = {
|
||||
"__title__": "Name",
|
||||
"__copyright__": "",
|
||||
"__version__": "version",
|
||||
"__version_info__": "version",
|
||||
"__description__": "summary",
|
||||
"__uri__": "",
|
||||
"__url__": "",
|
||||
"__author__": "",
|
||||
"__email__": "",
|
||||
"__license__": "license",
|
||||
}
|
||||
if name not in dunder_to_metadata.keys():
|
||||
raise AttributeError(f"module {mod_name} has no attribute {name}")
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
from importlib_metadata import metadata
|
||||
else:
|
||||
from importlib.metadata import metadata
|
||||
|
||||
if name != "__version_info__":
|
||||
warnings.warn(
|
||||
f"Accessing {mod_name}.{name} is deprecated and will be "
|
||||
"removed in a future release. Use importlib.metadata directly "
|
||||
"to query for attrs's packaging metadata.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
meta = metadata("attrs")
|
||||
if name == "__license__":
|
||||
return "MIT"
|
||||
elif name == "__copyright__":
|
||||
return "Copyright (c) 2015 Hynek Schlawack"
|
||||
elif name in ("__uri__", "__url__"):
|
||||
return meta["Project-URL"].split(" ", 1)[-1]
|
||||
elif name == "__version_info__":
|
||||
return VersionInfo._from_version_string(meta["version"])
|
||||
elif name == "__author__":
|
||||
return meta["Author-email"].rsplit(" ", 1)[0]
|
||||
elif name == "__email__":
|
||||
return meta["Author-email"].rsplit("<", 1)[1][:-1]
|
||||
|
||||
return meta[dunder_to_metadata[name]]
|
||||
|
||||
return __getattr__
|
||||
|
||||
|
||||
__getattr__ = _make_getattr(__name__)
|
||||
|
||||
Vendored
+98
-13
@@ -1,9 +1,9 @@
|
||||
import enum
|
||||
import sys
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
ClassVar,
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
@@ -25,8 +25,14 @@ from . import filters as filters
|
||||
from . import setters as setters
|
||||
from . import validators as validators
|
||||
from ._cmp import cmp_using as cmp_using
|
||||
from ._typing_compat import AttrsInstance_
|
||||
from ._version_info import VersionInfo
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeGuard
|
||||
else:
|
||||
from typing_extensions import TypeGuard
|
||||
|
||||
__version__: str
|
||||
__version_info__: VersionInfo
|
||||
__title__: str
|
||||
@@ -42,30 +48,34 @@ _T = TypeVar("_T")
|
||||
_C = TypeVar("_C", bound=type)
|
||||
|
||||
_EqOrderType = Union[bool, Callable[[Any], Any]]
|
||||
_ValidatorType = Callable[[Any, Attribute[_T], _T], Any]
|
||||
_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any]
|
||||
_ConverterType = Callable[[Any], Any]
|
||||
_FilterType = Callable[[Attribute[_T], _T], bool]
|
||||
_FilterType = Callable[["Attribute[_T]", _T], bool]
|
||||
_ReprType = Callable[[Any], str]
|
||||
_ReprArgType = Union[bool, _ReprType]
|
||||
_OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any]
|
||||
_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any]
|
||||
_OnSetAttrArgType = Union[
|
||||
_OnSetAttrType, List[_OnSetAttrType], setters._NoOpType
|
||||
]
|
||||
_FieldTransformer = Callable[
|
||||
[type, List[Attribute[Any]]], List[Attribute[Any]]
|
||||
[type, List["Attribute[Any]"]], List["Attribute[Any]"]
|
||||
]
|
||||
# FIXME: in reality, if multiple validators are passed they must be in a list
|
||||
# or tuple, but those are invariant and so would prevent subtypes of
|
||||
# _ValidatorType from working when passed in a list or tuple.
|
||||
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
|
||||
|
||||
# A protocol to be able to statically accept an attrs class.
|
||||
class AttrsInstance(Protocol):
|
||||
__attrs_attrs__: ClassVar[Any]
|
||||
# We subclass this here to keep the protocol's qualified name clean.
|
||||
class AttrsInstance(AttrsInstance_, Protocol):
|
||||
pass
|
||||
|
||||
_A = TypeVar("_A", bound=AttrsInstance)
|
||||
# _make --
|
||||
|
||||
NOTHING: object
|
||||
class _Nothing(enum.Enum):
|
||||
NOTHING = enum.auto()
|
||||
|
||||
NOTHING = _Nothing.NOTHING
|
||||
|
||||
# NOTE: Factory lies about its return type to make this possible:
|
||||
# `x: List[int] # = Factory(list)`
|
||||
@@ -107,6 +117,7 @@ def __dataclass_transform__(
|
||||
eq_default: bool = True,
|
||||
order_default: bool = False,
|
||||
kw_only_default: bool = False,
|
||||
frozen_default: bool = False,
|
||||
field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()),
|
||||
) -> Callable[[_T], _T]: ...
|
||||
|
||||
@@ -125,6 +136,8 @@ class Attribute(Generic[_T]):
|
||||
type: Optional[Type[_T]]
|
||||
kw_only: bool
|
||||
on_setattr: _OnSetAttrType
|
||||
alias: Optional[str]
|
||||
|
||||
def evolve(self, **changes: Any) -> "Attribute[Any]": ...
|
||||
|
||||
# NOTE: We had several choices for the annotation to use for type arg:
|
||||
@@ -167,6 +180,7 @@ def attrib(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the
|
||||
@@ -187,6 +201,7 @@ def attrib(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
@@ -206,6 +221,7 @@ def attrib(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
@@ -225,6 +241,7 @@ def attrib(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
def field(
|
||||
@@ -241,6 +258,8 @@ def field(
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the
|
||||
@@ -260,6 +279,8 @@ def field(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
@@ -278,6 +299,8 @@ def field(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
@@ -296,6 +319,8 @@ def field(
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
|
||||
@@ -323,6 +348,7 @@ def attrs(
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
|
||||
@@ -350,6 +376,7 @@ def attrs(
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
@overload
|
||||
@__dataclass_transform__(field_descriptors=(attrib, field))
|
||||
@@ -358,6 +385,7 @@ def define(
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
@@ -383,6 +411,7 @@ def define(
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
@@ -403,17 +432,73 @@ def define(
|
||||
) -> Callable[[_C], _C]: ...
|
||||
|
||||
mutable = define
|
||||
frozen = define # they differ only in their defaults
|
||||
|
||||
@overload
|
||||
@__dataclass_transform__(
|
||||
frozen_default=True, field_descriptors=(attrib, field)
|
||||
)
|
||||
def frozen(
|
||||
maybe_cls: _C,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
frozen: bool = ...,
|
||||
weakref_slot: bool = ...,
|
||||
str: bool = ...,
|
||||
auto_attribs: bool = ...,
|
||||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
auto_detect: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
@__dataclass_transform__(
|
||||
frozen_default=True, field_descriptors=(attrib, field)
|
||||
)
|
||||
def frozen(
|
||||
maybe_cls: None = ...,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
frozen: bool = ...,
|
||||
weakref_slot: bool = ...,
|
||||
str: bool = ...,
|
||||
auto_attribs: bool = ...,
|
||||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
auto_detect: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
def fields(cls: Type[AttrsInstance]) -> Any: ...
|
||||
def fields_dict(cls: Type[AttrsInstance]) -> Dict[str, Attribute[Any]]: ...
|
||||
def validate(inst: AttrsInstance) -> None: ...
|
||||
def resolve_types(
|
||||
cls: _C,
|
||||
cls: _A,
|
||||
globalns: Optional[Dict[str, Any]] = ...,
|
||||
localns: Optional[Dict[str, Any]] = ...,
|
||||
attribs: Optional[List[Attribute[Any]]] = ...,
|
||||
) -> _C: ...
|
||||
include_extras: bool = ...,
|
||||
) -> _A: ...
|
||||
|
||||
# TODO: add support for returning a proper attrs class from the mypy plugin
|
||||
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
|
||||
@@ -470,7 +555,7 @@ def astuple(
|
||||
tuple_factory: Type[Sequence[Any]] = ...,
|
||||
retain_collection_types: bool = ...,
|
||||
) -> Tuple[Any, ...]: ...
|
||||
def has(cls: type) -> bool: ...
|
||||
def has(cls: type) -> TypeGuard[Type[AttrsInstance]]: ...
|
||||
def assoc(inst: _T, **changes: Any) -> _T: ...
|
||||
def evolve(inst: _T, **changes: Any) -> _T: ...
|
||||
|
||||
|
||||
Vendored
+17
-17
@@ -20,22 +20,22 @@ def cmp_using(
|
||||
class_name="Comparable",
|
||||
):
|
||||
"""
|
||||
Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and
|
||||
``cmp`` arguments to customize field comparison.
|
||||
Create a class that can be passed into `attrs.field`'s ``eq``, ``order``,
|
||||
and ``cmp`` arguments to customize field comparison.
|
||||
|
||||
The resulting class will have a full set of ordering methods if
|
||||
at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
|
||||
The resulting class will have a full set of ordering methods if at least
|
||||
one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
|
||||
|
||||
:param Optional[callable] eq: `callable` used to evaluate equality
|
||||
of two objects.
|
||||
:param Optional[callable] lt: `callable` used to evaluate whether
|
||||
one object is less than another object.
|
||||
:param Optional[callable] le: `callable` used to evaluate whether
|
||||
one object is less than or equal to another object.
|
||||
:param Optional[callable] gt: `callable` used to evaluate whether
|
||||
one object is greater than another object.
|
||||
:param Optional[callable] ge: `callable` used to evaluate whether
|
||||
one object is greater than or equal to another object.
|
||||
:param Optional[callable] eq: `callable` used to evaluate equality of two
|
||||
objects.
|
||||
:param Optional[callable] lt: `callable` used to evaluate whether one
|
||||
object is less than another object.
|
||||
:param Optional[callable] le: `callable` used to evaluate whether one
|
||||
object is less than or equal to another object.
|
||||
:param Optional[callable] gt: `callable` used to evaluate whether one
|
||||
object is greater than another object.
|
||||
:param Optional[callable] ge: `callable` used to evaluate whether one
|
||||
object is greater than or equal to another object.
|
||||
|
||||
:param bool require_same_type: When `True`, equality and ordering methods
|
||||
will return `NotImplemented` if objects are not of the same type.
|
||||
@@ -130,9 +130,9 @@ def _make_operator(name, func):
|
||||
|
||||
return result
|
||||
|
||||
method.__name__ = "__%s__" % (name,)
|
||||
method.__doc__ = "Return a %s b. Computed by attrs." % (
|
||||
_operation_names[name],
|
||||
method.__name__ = f"__{name}__"
|
||||
method.__doc__ = (
|
||||
f"Return a {_operation_names[name]} b. Computed by attrs."
|
||||
)
|
||||
|
||||
return method
|
||||
|
||||
Vendored
+7
-7
@@ -3,11 +3,11 @@ from typing import Any, Callable, Optional, Type
|
||||
_CompareWithType = Callable[[Any, Any], bool]
|
||||
|
||||
def cmp_using(
|
||||
eq: Optional[_CompareWithType],
|
||||
lt: Optional[_CompareWithType],
|
||||
le: Optional[_CompareWithType],
|
||||
gt: Optional[_CompareWithType],
|
||||
ge: Optional[_CompareWithType],
|
||||
require_same_type: bool,
|
||||
class_name: str,
|
||||
eq: Optional[_CompareWithType] = ...,
|
||||
lt: Optional[_CompareWithType] = ...,
|
||||
le: Optional[_CompareWithType] = ...,
|
||||
gt: Optional[_CompareWithType] = ...,
|
||||
ge: Optional[_CompareWithType] = ...,
|
||||
require_same_type: bool = ...,
|
||||
class_name: str = ...,
|
||||
) -> Type: ...
|
||||
|
||||
Vendored
+29
-29
@@ -9,20 +9,13 @@ import types
|
||||
import warnings
|
||||
|
||||
from collections.abc import Mapping, Sequence # noqa
|
||||
from typing import _GenericAlias
|
||||
|
||||
|
||||
PYPY = platform.python_implementation() == "PyPy"
|
||||
PY36 = sys.version_info[:2] >= (3, 6)
|
||||
HAS_F_STRINGS = PY36
|
||||
PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
|
||||
PY310 = sys.version_info[:2] >= (3, 10)
|
||||
|
||||
|
||||
if PYPY or PY36:
|
||||
ordered_dict = dict
|
||||
else:
|
||||
from collections import OrderedDict
|
||||
|
||||
ordered_dict = OrderedDict
|
||||
PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
|
||||
|
||||
|
||||
def just_warn(*args, **kw):
|
||||
@@ -90,32 +83,32 @@ def make_set_closure_cell():
|
||||
|
||||
# Otherwise gotta do it the hard way.
|
||||
|
||||
# Create a function that will set its first cellvar to `value`.
|
||||
def set_first_cellvar_to(value):
|
||||
x = value
|
||||
return
|
||||
|
||||
# This function will be eliminated as dead code, but
|
||||
# not before its reference to `x` forces `x` to be
|
||||
# represented as a closure cell rather than a local.
|
||||
def force_x_to_be_a_cell(): # pragma: no cover
|
||||
return x
|
||||
|
||||
try:
|
||||
# Extract the code object and make sure our assumptions about
|
||||
# the closure behavior are correct.
|
||||
co = set_first_cellvar_to.__code__
|
||||
if co.co_cellvars != ("x",) or co.co_freevars != ():
|
||||
raise AssertionError # pragma: no cover
|
||||
|
||||
# Convert this code object to a code object that sets the
|
||||
# function's first _freevar_ (not cellvar) to the argument.
|
||||
if sys.version_info >= (3, 8):
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
cell.cell_contents = value
|
||||
|
||||
else:
|
||||
# Create a function that will set its first cellvar to `value`.
|
||||
def set_first_cellvar_to(value):
|
||||
x = value
|
||||
return
|
||||
|
||||
# This function will be eliminated as dead code, but
|
||||
# not before its reference to `x` forces `x` to be
|
||||
# represented as a closure cell rather than a local.
|
||||
def force_x_to_be_a_cell(): # pragma: no cover
|
||||
return x
|
||||
|
||||
# Extract the code object and make sure our assumptions about
|
||||
# the closure behavior are correct.
|
||||
co = set_first_cellvar_to.__code__
|
||||
if co.co_cellvars != ("x",) or co.co_freevars != ():
|
||||
raise AssertionError # pragma: no cover
|
||||
|
||||
# Convert this code object to a code object that sets the
|
||||
# function's first _freevar_ (not cellvar) to the argument.
|
||||
args = [co.co_argcount]
|
||||
args.append(co.co_kwonlyargcount)
|
||||
args.extend(
|
||||
@@ -183,3 +176,10 @@ set_closure_cell = make_set_closure_cell()
|
||||
# don't have a direct reference to the thread-local in their globals dict.
|
||||
# If they have such a reference, it breaks cloudpickle.
|
||||
repr_context = threading.local()
|
||||
|
||||
|
||||
def get_generic_base(cl):
|
||||
"""If this is a generic class (A[str]), return the generic base for it."""
|
||||
if cl.__class__ is _GenericAlias:
|
||||
return cl.__origin__
|
||||
return None
|
||||
|
||||
Vendored
+93
-36
@@ -3,6 +3,7 @@
|
||||
|
||||
import copy
|
||||
|
||||
from ._compat import PY_3_9_PLUS, get_generic_base
|
||||
from ._make import NOTHING, _obj_setattr, fields
|
||||
from .exceptions import AttrsAttributeNotFoundError
|
||||
|
||||
@@ -16,13 +17,13 @@ def asdict(
|
||||
value_serializer=None,
|
||||
):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a dict.
|
||||
Return the *attrs* attribute values of *inst* as a dict.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
Optionally recurse into other *attrs*-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
:param inst: Instance of an *attrs*-decorated class.
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
*attrs*-decorated.
|
||||
:param callable filter: A callable whose return code determines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the `attrs.Attribute` as the first argument and the
|
||||
@@ -40,7 +41,7 @@ def asdict(
|
||||
|
||||
:rtype: return type of *dict_factory*
|
||||
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. versionadded:: 16.0.0 *dict_factory*
|
||||
@@ -195,13 +196,13 @@ def astuple(
|
||||
retain_collection_types=False,
|
||||
):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a tuple.
|
||||
Return the *attrs* attribute values of *inst* as a tuple.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
Optionally recurse into other *attrs*-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
:param inst: Instance of an *attrs*-decorated class.
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
*attrs*-decorated.
|
||||
:param callable filter: A callable whose return code determines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the `attrs.Attribute` as the first argument and the
|
||||
@@ -215,7 +216,7 @@ def astuple(
|
||||
|
||||
:rtype: return type of *tuple_factory*
|
||||
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
@@ -289,28 +290,48 @@ def astuple(
|
||||
|
||||
def has(cls):
|
||||
"""
|
||||
Check whether *cls* is a class with ``attrs`` attributes.
|
||||
Check whether *cls* is a class with *attrs* attributes.
|
||||
|
||||
:param type cls: Class to introspect.
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return getattr(cls, "__attrs_attrs__", None) is not None
|
||||
attrs = getattr(cls, "__attrs_attrs__", None)
|
||||
if attrs is not None:
|
||||
return True
|
||||
|
||||
# No attrs, maybe it's a specialized generic (A[str])?
|
||||
generic_base = get_generic_base(cls)
|
||||
if generic_base is not None:
|
||||
generic_attrs = getattr(generic_base, "__attrs_attrs__", None)
|
||||
if generic_attrs is not None:
|
||||
# Stick it on here for speed next time.
|
||||
cls.__attrs_attrs__ = generic_attrs
|
||||
return generic_attrs is not None
|
||||
return False
|
||||
|
||||
|
||||
def assoc(inst, **changes):
|
||||
"""
|
||||
Copy *inst* and apply *changes*.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
This is different from `evolve` that applies the changes to the arguments
|
||||
that create the new instance.
|
||||
|
||||
`evolve`'s behavior is preferable, but there are `edge cases`_ where it
|
||||
doesn't work. Therefore `assoc` is deprecated, but will not be removed.
|
||||
|
||||
.. _`edge cases`: https://github.com/python-attrs/attrs/issues/251
|
||||
|
||||
:param inst: Instance of a class with *attrs* attributes.
|
||||
:param changes: Keyword changes in the new copy.
|
||||
|
||||
:return: A copy of inst with *changes* incorporated.
|
||||
|
||||
:raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
|
||||
be found on *cls*.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.AttrsAttributeNotFoundError: If *attr_name*
|
||||
couldn't be found on *cls*.
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. deprecated:: 17.1.0
|
||||
@@ -318,57 +339,83 @@ def assoc(inst, **changes):
|
||||
This function will not be removed du to the slightly different approach
|
||||
compared to `attrs.evolve`.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"assoc is deprecated and will be removed after 2018/01.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
new = copy.copy(inst)
|
||||
attrs = fields(inst.__class__)
|
||||
for k, v in changes.items():
|
||||
a = getattr(attrs, k, NOTHING)
|
||||
if a is NOTHING:
|
||||
raise AttrsAttributeNotFoundError(
|
||||
"{k} is not an attrs attribute on {cl}.".format(
|
||||
k=k, cl=new.__class__
|
||||
)
|
||||
f"{k} is not an attrs attribute on {new.__class__}."
|
||||
)
|
||||
_obj_setattr(new, k, v)
|
||||
return new
|
||||
|
||||
|
||||
def evolve(inst, **changes):
|
||||
def evolve(*args, **changes):
|
||||
"""
|
||||
Create a new instance, based on *inst* with *changes* applied.
|
||||
Create a new instance, based on the first positional argument with
|
||||
*changes* applied.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
:param inst: Instance of a class with *attrs* attributes.
|
||||
:param changes: Keyword changes in the new copy.
|
||||
|
||||
:return: A copy of inst with *changes* incorporated.
|
||||
|
||||
:raise TypeError: If *attr_name* couldn't be found in the class
|
||||
``__init__``.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
.. versionadded:: 17.1.0
|
||||
.. deprecated:: 23.1.0
|
||||
It is now deprecated to pass the instance using the keyword argument
|
||||
*inst*. It will raise a warning until at least April 2024, after which
|
||||
it will become an error. Always pass the instance as a positional
|
||||
argument.
|
||||
"""
|
||||
# Try to get instance by positional argument first.
|
||||
# Use changes otherwise and warn it'll break.
|
||||
if args:
|
||||
try:
|
||||
(inst,) = args
|
||||
except ValueError:
|
||||
raise TypeError(
|
||||
f"evolve() takes 1 positional argument, but {len(args)} "
|
||||
"were given"
|
||||
) from None
|
||||
else:
|
||||
try:
|
||||
inst = changes.pop("inst")
|
||||
except KeyError:
|
||||
raise TypeError(
|
||||
"evolve() missing 1 required positional argument: 'inst'"
|
||||
) from None
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"Passing the instance per keyword argument is deprecated and "
|
||||
"will stop working in, or after, April 2024.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
cls = inst.__class__
|
||||
attrs = fields(cls)
|
||||
for a in attrs:
|
||||
if not a.init:
|
||||
continue
|
||||
attr_name = a.name # To deal with private attributes.
|
||||
init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
|
||||
init_name = a.alias
|
||||
if init_name not in changes:
|
||||
changes[init_name] = getattr(inst, attr_name)
|
||||
|
||||
return cls(**changes)
|
||||
|
||||
|
||||
def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
||||
def resolve_types(
|
||||
cls, globalns=None, localns=None, attribs=None, include_extras=True
|
||||
):
|
||||
"""
|
||||
Resolve any strings and forward annotations in type annotations.
|
||||
|
||||
@@ -387,10 +434,14 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
||||
:param Optional[dict] localns: Dictionary containing local variables.
|
||||
:param Optional[list] attribs: List of attribs for the given class.
|
||||
This is necessary when calling from inside a ``field_transformer``
|
||||
since *cls* is not an ``attrs`` class yet.
|
||||
since *cls* is not an *attrs* class yet.
|
||||
:param bool include_extras: Resolve more accurately, if possible.
|
||||
Pass ``include_extras`` to ``typing.get_hints``, if supported by the
|
||||
typing module. On supported Python versions (3.9+), this resolves the
|
||||
types more accurately.
|
||||
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class and you didn't pass any attribs.
|
||||
:raise NameError: If types cannot be resolved because of missing variables.
|
||||
|
||||
@@ -400,6 +451,7 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
.. versionadded:: 21.1.0 *attribs*
|
||||
.. versionadded:: 23.1.0 *include_extras*
|
||||
|
||||
"""
|
||||
# Since calling get_type_hints is expensive we cache whether we've
|
||||
@@ -407,7 +459,12 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
||||
if getattr(cls, "__attrs_types_resolved__", None) != cls:
|
||||
import typing
|
||||
|
||||
hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
|
||||
kwargs = {"globalns": globalns, "localns": localns}
|
||||
|
||||
if PY_3_9_PLUS:
|
||||
kwargs["include_extras"] = include_extras
|
||||
|
||||
hints = typing.get_type_hints(cls, **kwargs)
|
||||
for field in fields(cls) if attribs is None else attribs:
|
||||
if field.name in hints:
|
||||
# Since fields have been frozen we must work around it.
|
||||
|
||||
Vendored
+311
-330
File diff suppressed because it is too large
Load Diff
Vendored
+15
-3
@@ -1,8 +1,8 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
These are Python 3.6+-only and keyword-only APIs that call `attr.s` and
|
||||
`attr.ib` with different default values.
|
||||
These are keyword-only APIs that call `attr.s` and `attr.ib` with different
|
||||
default values.
|
||||
"""
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ def define(
|
||||
*,
|
||||
these=None,
|
||||
repr=None,
|
||||
unsafe_hash=None,
|
||||
hash=None,
|
||||
init=None,
|
||||
slots=True,
|
||||
@@ -45,7 +46,7 @@ def define(
|
||||
match_args=True,
|
||||
):
|
||||
r"""
|
||||
Define an ``attrs`` class.
|
||||
Define an *attrs* class.
|
||||
|
||||
Differences to the classic `attr.s` that it uses underneath:
|
||||
|
||||
@@ -81,6 +82,8 @@ def define(
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
.. versionchanged:: 21.3.0 Converters are also run ``on_setattr``.
|
||||
.. versionadded:: 22.2.0
|
||||
*unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
|
||||
"""
|
||||
|
||||
def do_it(cls, auto_attribs):
|
||||
@@ -89,6 +92,7 @@ def define(
|
||||
these=these,
|
||||
repr=repr,
|
||||
hash=hash,
|
||||
unsafe_hash=unsafe_hash,
|
||||
init=init,
|
||||
slots=slots,
|
||||
frozen=frozen,
|
||||
@@ -163,17 +167,23 @@ def field(
|
||||
hash=None,
|
||||
init=True,
|
||||
metadata=None,
|
||||
type=None,
|
||||
converter=None,
|
||||
factory=None,
|
||||
kw_only=False,
|
||||
eq=None,
|
||||
order=None,
|
||||
on_setattr=None,
|
||||
alias=None,
|
||||
):
|
||||
"""
|
||||
Identical to `attr.ib`, except keyword-only and with some arguments
|
||||
removed.
|
||||
|
||||
.. versionadded:: 23.1.0
|
||||
The *type* parameter has been re-added; mostly for
|
||||
{func}`attrs.make_class`. Please note that type checkers ignore this
|
||||
metadata.
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
return attrib(
|
||||
@@ -183,12 +193,14 @@ def field(
|
||||
hash=hash,
|
||||
init=init,
|
||||
metadata=metadata,
|
||||
type=type,
|
||||
converter=converter,
|
||||
factory=factory,
|
||||
kw_only=kw_only,
|
||||
eq=eq,
|
||||
order=order,
|
||||
on_setattr=on_setattr,
|
||||
alias=alias,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
from typing import Any, ClassVar, Protocol
|
||||
|
||||
# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`.
|
||||
MYPY = False
|
||||
|
||||
if MYPY:
|
||||
# A protocol to be able to statically accept an attrs class.
|
||||
class AttrsInstance_(Protocol):
|
||||
__attrs_attrs__: ClassVar[Any]
|
||||
|
||||
else:
|
||||
# For type checkers without plug-in support use an empty protocol that
|
||||
# will (hopefully) be combined into a union.
|
||||
class AttrsInstance_(Protocol):
|
||||
pass
|
||||
Vendored
+1
-1
@@ -141,4 +141,4 @@ def to_bool(val):
|
||||
except TypeError:
|
||||
# Raised when "val" is not hashable (e.g., lists)
|
||||
pass
|
||||
raise ValueError("Cannot convert value to bool: {}".format(val))
|
||||
raise ValueError(f"Cannot convert value to bool: {val}")
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
from typing import Callable, Optional, TypeVar, overload
|
||||
from typing import Callable, TypeVar, overload
|
||||
|
||||
from . import _ConverterType
|
||||
|
||||
|
||||
Vendored
+7
-8
@@ -34,7 +34,7 @@ class FrozenAttributeError(FrozenError):
|
||||
|
||||
class AttrsAttributeNotFoundError(ValueError):
|
||||
"""
|
||||
An ``attrs`` function couldn't find an attribute that the user asked for.
|
||||
An *attrs* function couldn't find an attribute that the user asked for.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
@@ -42,7 +42,7 @@ class AttrsAttributeNotFoundError(ValueError):
|
||||
|
||||
class NotAnAttrsClassError(ValueError):
|
||||
"""
|
||||
A non-``attrs`` class has been passed into an ``attrs`` function.
|
||||
A non-*attrs* class has been passed into an *attrs* function.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
@@ -50,7 +50,7 @@ class NotAnAttrsClassError(ValueError):
|
||||
|
||||
class DefaultAlreadySetError(RuntimeError):
|
||||
"""
|
||||
A default has been set using ``attr.ib()`` and is attempted to be reset
|
||||
A default has been set when defining the field and is attempted to be reset
|
||||
using the decorator.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
@@ -59,8 +59,7 @@ class DefaultAlreadySetError(RuntimeError):
|
||||
|
||||
class UnannotatedAttributeError(RuntimeError):
|
||||
"""
|
||||
A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type
|
||||
annotation.
|
||||
A class with ``auto_attribs=True`` has a field without a type annotation.
|
||||
|
||||
.. versionadded:: 17.3.0
|
||||
"""
|
||||
@@ -68,7 +67,7 @@ class UnannotatedAttributeError(RuntimeError):
|
||||
|
||||
class PythonTooOldError(RuntimeError):
|
||||
"""
|
||||
It was attempted to use an ``attrs`` feature that requires a newer Python
|
||||
It was attempted to use an *attrs* feature that requires a newer Python
|
||||
version.
|
||||
|
||||
.. versionadded:: 18.2.0
|
||||
@@ -77,8 +76,8 @@ class PythonTooOldError(RuntimeError):
|
||||
|
||||
class NotCallableError(TypeError):
|
||||
"""
|
||||
A ``attr.ib()`` requiring a callable has been set with a value
|
||||
that is not callable.
|
||||
A field requiring a callable has been set with a value that is not
|
||||
callable.
|
||||
|
||||
.. versionadded:: 19.2.0
|
||||
"""
|
||||
|
||||
Vendored
+21
-6
@@ -13,6 +13,7 @@ def _split_what(what):
|
||||
"""
|
||||
return (
|
||||
frozenset(cls for cls in what if isinstance(cls, type)),
|
||||
frozenset(cls for cls in what if isinstance(cls, str)),
|
||||
frozenset(cls for cls in what if isinstance(cls, Attribute)),
|
||||
)
|
||||
|
||||
@@ -22,14 +23,21 @@ def include(*what):
|
||||
Include *what*.
|
||||
|
||||
:param what: What to include.
|
||||
:type what: `list` of `type` or `attrs.Attribute`\\ s
|
||||
:type what: `list` of classes `type`, field names `str` or
|
||||
`attrs.Attribute`\\ s
|
||||
|
||||
:rtype: `callable`
|
||||
|
||||
.. versionchanged:: 23.1.0 Accept strings with field names.
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
cls, names, attrs = _split_what(what)
|
||||
|
||||
def include_(attribute, value):
|
||||
return value.__class__ in cls or attribute in attrs
|
||||
return (
|
||||
value.__class__ in cls
|
||||
or attribute.name in names
|
||||
or attribute in attrs
|
||||
)
|
||||
|
||||
return include_
|
||||
|
||||
@@ -39,13 +47,20 @@ def exclude(*what):
|
||||
Exclude *what*.
|
||||
|
||||
:param what: What to exclude.
|
||||
:type what: `list` of classes or `attrs.Attribute`\\ s.
|
||||
:type what: `list` of classes `type`, field names `str` or
|
||||
`attrs.Attribute`\\ s.
|
||||
|
||||
:rtype: `callable`
|
||||
|
||||
.. versionchanged:: 23.3.0 Accept field name string as input argument
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
cls, names, attrs = _split_what(what)
|
||||
|
||||
def exclude_(attribute, value):
|
||||
return value.__class__ not in cls and attribute not in attrs
|
||||
return not (
|
||||
value.__class__ in cls
|
||||
or attribute.name in names
|
||||
or attribute in attrs
|
||||
)
|
||||
|
||||
return exclude_
|
||||
|
||||
Vendored
+2
-2
@@ -2,5 +2,5 @@ from typing import Any, Union
|
||||
|
||||
from . import Attribute, _FilterType
|
||||
|
||||
def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
def include(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
def exclude(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
from typing import Any, NewType, NoReturn, TypeVar, cast
|
||||
from typing import Any, NewType, NoReturn, TypeVar
|
||||
|
||||
from . import Attribute, _OnSetAttrType
|
||||
|
||||
|
||||
Vendored
+142
-16
@@ -9,18 +9,14 @@ import operator
|
||||
import re
|
||||
|
||||
from contextlib import contextmanager
|
||||
from re import Pattern
|
||||
|
||||
from ._config import get_run_validators, set_run_validators
|
||||
from ._make import _AndValidator, and_, attrib, attrs
|
||||
from .converters import default_if_none
|
||||
from .exceptions import NotCallableError
|
||||
|
||||
|
||||
try:
|
||||
Pattern = re.Pattern
|
||||
except AttributeError: # Python <3.7 lacks a Pattern type.
|
||||
Pattern = type(re.compile(""))
|
||||
|
||||
|
||||
__all__ = [
|
||||
"and_",
|
||||
"deep_iterable",
|
||||
@@ -37,6 +33,7 @@ __all__ = [
|
||||
"matches_re",
|
||||
"max_len",
|
||||
"min_len",
|
||||
"not_",
|
||||
"optional",
|
||||
"provides",
|
||||
"set_disabled",
|
||||
@@ -126,7 +123,7 @@ def instance_of(type):
|
||||
`isinstance` therefore it's also valid to pass a tuple of types).
|
||||
|
||||
:param type: The type to check for.
|
||||
:type type: type or tuple of types
|
||||
:type type: type or tuple of type
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attrs.Attribute`), the expected type, and the value it
|
||||
@@ -247,7 +244,17 @@ def provides(interface):
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attrs.Attribute`), the expected interface, and the
|
||||
value it got.
|
||||
|
||||
.. deprecated:: 23.1.0
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"attrs's zope-interface support is deprecated and will be removed in, "
|
||||
"or after, April 2024.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return _ProvidesValidator(interface)
|
||||
|
||||
|
||||
@@ -273,15 +280,16 @@ def optional(validator):
|
||||
which can be set to ``None`` in addition to satisfying the requirements of
|
||||
the sub-validator.
|
||||
|
||||
:param validator: A validator (or a list of validators) that is used for
|
||||
non-``None`` values.
|
||||
:type validator: callable or `list` of callables.
|
||||
:param Callable | tuple[Callable] | list[Callable] validator: A validator
|
||||
(or validators) that is used for non-``None`` values.
|
||||
|
||||
.. versionadded:: 15.1.0
|
||||
.. versionchanged:: 17.1.0 *validator* can be a list of validators.
|
||||
.. versionchanged:: 23.1.0 *validator* can also be a tuple of validators.
|
||||
"""
|
||||
if isinstance(validator, list):
|
||||
if isinstance(validator, (list, tuple)):
|
||||
return _OptionalValidator(_AndValidator(validator))
|
||||
|
||||
return _OptionalValidator(validator)
|
||||
|
||||
|
||||
@@ -357,13 +365,13 @@ class _IsCallableValidator:
|
||||
|
||||
def is_callable():
|
||||
"""
|
||||
A validator that raises a `attr.exceptions.NotCallableError` if the
|
||||
A validator that raises a `attrs.exceptions.NotCallableError` if the
|
||||
initializer is called with a value for this particular attribute
|
||||
that is not callable.
|
||||
|
||||
.. versionadded:: 19.1.0
|
||||
|
||||
:raises `attr.exceptions.NotCallableError`: With a human readable error
|
||||
:raises attrs.exceptions.NotCallableError: With a human readable error
|
||||
message containing the attribute (`attrs.Attribute`) name,
|
||||
and the value it got.
|
||||
"""
|
||||
@@ -391,7 +399,7 @@ class _DeepIterable:
|
||||
iterable_identifier = (
|
||||
""
|
||||
if self.iterable_validator is None
|
||||
else " {iterable!r}".format(iterable=self.iterable_validator)
|
||||
else f" {self.iterable_validator!r}"
|
||||
)
|
||||
return (
|
||||
"<deep_iterable validator for{iterable_identifier}"
|
||||
@@ -548,7 +556,7 @@ class _MaxLengthValidator:
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<max_len validator for {max}>".format(max=self.max_length)
|
||||
return f"<max_len validator for {self.max_length}>"
|
||||
|
||||
|
||||
def max_len(length):
|
||||
@@ -579,7 +587,7 @@ class _MinLengthValidator:
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<min_len validator for {min}>".format(min=self.min_length)
|
||||
return f"<min_len validator for {self.min_length}>"
|
||||
|
||||
|
||||
def min_len(length):
|
||||
@@ -592,3 +600,121 @@ def min_len(length):
|
||||
.. versionadded:: 22.1.0
|
||||
"""
|
||||
return _MinLengthValidator(length)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _SubclassOfValidator:
|
||||
type = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not issubclass(value, self.type):
|
||||
raise TypeError(
|
||||
"'{name}' must be a subclass of {type!r} "
|
||||
"(got {value!r}).".format(
|
||||
name=attr.name,
|
||||
type=self.type,
|
||||
value=value,
|
||||
),
|
||||
attr,
|
||||
self.type,
|
||||
value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<subclass_of validator for type {type!r}>".format(
|
||||
type=self.type
|
||||
)
|
||||
|
||||
|
||||
def _subclass_of(type):
|
||||
"""
|
||||
A validator that raises a `TypeError` if the initializer is called
|
||||
with a wrong type for this particular attribute (checks are performed using
|
||||
`issubclass` therefore it's also valid to pass a tuple of types).
|
||||
|
||||
:param type: The type to check for.
|
||||
:type type: type or tuple of types
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attrs.Attribute`), the expected type, and the value it
|
||||
got.
|
||||
"""
|
||||
return _SubclassOfValidator(type)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _NotValidator:
|
||||
validator = attrib()
|
||||
msg = attrib(
|
||||
converter=default_if_none(
|
||||
"not_ validator child '{validator!r}' "
|
||||
"did not raise a captured error"
|
||||
)
|
||||
)
|
||||
exc_types = attrib(
|
||||
validator=deep_iterable(
|
||||
member_validator=_subclass_of(Exception),
|
||||
iterable_validator=instance_of(tuple),
|
||||
),
|
||||
)
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
try:
|
||||
self.validator(inst, attr, value)
|
||||
except self.exc_types:
|
||||
pass # suppress error to invert validity
|
||||
else:
|
||||
raise ValueError(
|
||||
self.msg.format(
|
||||
validator=self.validator,
|
||||
exc_types=self.exc_types,
|
||||
),
|
||||
attr,
|
||||
self.validator,
|
||||
value,
|
||||
self.exc_types,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<not_ validator wrapping {what!r}, " "capturing {exc_types!r}>"
|
||||
).format(
|
||||
what=self.validator,
|
||||
exc_types=self.exc_types,
|
||||
)
|
||||
|
||||
|
||||
def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)):
|
||||
"""
|
||||
A validator that wraps and logically 'inverts' the validator passed to it.
|
||||
It will raise a `ValueError` if the provided validator *doesn't* raise a
|
||||
`ValueError` or `TypeError` (by default), and will suppress the exception
|
||||
if the provided validator *does*.
|
||||
|
||||
Intended to be used with existing validators to compose logic without
|
||||
needing to create inverted variants, for example, ``not_(in_(...))``.
|
||||
|
||||
:param validator: A validator to be logically inverted.
|
||||
:param msg: Message to raise if validator fails.
|
||||
Formatted with keys ``exc_types`` and ``validator``.
|
||||
:type msg: str
|
||||
:param exc_types: Exception type(s) to capture.
|
||||
Other types raised by child validators will not be intercepted and
|
||||
pass through.
|
||||
|
||||
:raises ValueError: With a human readable error message,
|
||||
the attribute (of type `attrs.Attribute`),
|
||||
the validator that failed to raise an exception,
|
||||
the value it got,
|
||||
and the expected exception types.
|
||||
|
||||
.. versionadded:: 22.2.0
|
||||
"""
|
||||
try:
|
||||
exc_types = tuple(exc_types)
|
||||
except TypeError:
|
||||
exc_types = (exc_types,)
|
||||
return _NotValidator(validator, msg, exc_types)
|
||||
|
||||
Vendored
+9
-1
@@ -51,7 +51,9 @@ def instance_of(
|
||||
def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ...
|
||||
def provides(interface: Any) -> _ValidatorType[Any]: ...
|
||||
def optional(
|
||||
validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
|
||||
validator: Union[
|
||||
_ValidatorType[_T], List[_ValidatorType[_T]], Tuple[_ValidatorType[_T]]
|
||||
]
|
||||
) -> _ValidatorType[Optional[_T]]: ...
|
||||
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
|
||||
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
|
||||
@@ -78,3 +80,9 @@ def ge(val: _T) -> _ValidatorType[_T]: ...
|
||||
def gt(val: _T) -> _ValidatorType[_T]: ...
|
||||
def max_len(length: int) -> _ValidatorType[_T]: ...
|
||||
def min_len(length: int) -> _ValidatorType[_T]: ...
|
||||
def not_(
|
||||
validator: _ValidatorType[_T],
|
||||
*,
|
||||
msg: Optional[str] = None,
|
||||
exc_types: Union[Type[Exception], Iterable[Type[Exception]]] = ...,
|
||||
) -> _ValidatorType[_T]: ...
|
||||
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Hynek Schlawack and the attrs contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+5
-10
@@ -3,17 +3,9 @@
|
||||
from pipenv.vendor.attr import (
|
||||
NOTHING,
|
||||
Attribute,
|
||||
AttrsInstance,
|
||||
Factory,
|
||||
__author__,
|
||||
__copyright__,
|
||||
__description__,
|
||||
__doc__,
|
||||
__email__,
|
||||
__license__,
|
||||
__title__,
|
||||
__url__,
|
||||
__version__,
|
||||
__version_info__,
|
||||
_make_getattr,
|
||||
assoc,
|
||||
cmp_using,
|
||||
define,
|
||||
@@ -48,6 +40,7 @@ __all__ = [
|
||||
"assoc",
|
||||
"astuple",
|
||||
"Attribute",
|
||||
"AttrsInstance",
|
||||
"cmp_using",
|
||||
"converters",
|
||||
"define",
|
||||
@@ -68,3 +61,5 @@ __all__ = [
|
||||
"validate",
|
||||
"validators",
|
||||
]
|
||||
|
||||
__getattr__ = _make_getattr(__name__)
|
||||
|
||||
Vendored
+3
-2
@@ -23,6 +23,7 @@ from attr import __version_info__ as __version_info__
|
||||
from attr import _FilterType
|
||||
from attr import assoc as assoc
|
||||
from attr import Attribute as Attribute
|
||||
from attr import AttrsInstance as AttrsInstance
|
||||
from attr import cmp_using as cmp_using
|
||||
from attr import converters as converters
|
||||
from attr import define as define
|
||||
@@ -45,7 +46,7 @@ from attr import validators as validators
|
||||
|
||||
# TODO: see definition of attr.asdict/astuple
|
||||
def asdict(
|
||||
inst: Any,
|
||||
inst: AttrsInstance,
|
||||
recurse: bool = ...,
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
dict_factory: Type[Mapping[Any, Any]] = ...,
|
||||
@@ -58,7 +59,7 @@ def asdict(
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
def astuple(
|
||||
inst: Any,
|
||||
inst: AttrsInstance,
|
||||
recurse: bool = ...,
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
tuple_factory: Type[Sequence[Any]] = ...,
|
||||
|
||||
Vendored
+1
-3
@@ -41,7 +41,6 @@ from .termui import clear as clear
|
||||
from .termui import confirm as confirm
|
||||
from .termui import echo_via_pager as echo_via_pager
|
||||
from .termui import edit as edit
|
||||
from .termui import get_terminal_size as get_terminal_size
|
||||
from .termui import getchar as getchar
|
||||
from .termui import launch as launch
|
||||
from .termui import pause as pause
|
||||
@@ -68,8 +67,7 @@ from .utils import echo as echo
|
||||
from .utils import format_filename as format_filename
|
||||
from .utils import get_app_dir as get_app_dir
|
||||
from .utils import get_binary_stream as get_binary_stream
|
||||
from .utils import get_os_args as get_os_args
|
||||
from .utils import get_text_stream as get_text_stream
|
||||
from .utils import open_file as open_file
|
||||
|
||||
__version__ = "8.0.3"
|
||||
__version__ = "8.1.3"
|
||||
|
||||
Vendored
+3
-4
@@ -388,9 +388,9 @@ def open_stream(
|
||||
) -> t.Tuple[t.IO, bool]:
|
||||
binary = "b" in mode
|
||||
|
||||
# Standard streams first. These are simple because they don't need
|
||||
# special handling for the atomic flag. It's entirely ignored.
|
||||
if filename == "-":
|
||||
# Standard streams first. These are simple because they ignore the
|
||||
# atomic flag. Use fsdecode to handle Path("-").
|
||||
if os.fsdecode(filename) == "-":
|
||||
if any(m in mode for m in ["w", "a", "x"]):
|
||||
if binary:
|
||||
return get_binary_stdout(), False
|
||||
@@ -561,7 +561,6 @@ if sys.platform.startswith("win") and WIN:
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
else:
|
||||
|
||||
def _get_argv_encoding() -> str:
|
||||
|
||||
Vendored
-1
@@ -675,7 +675,6 @@ if WIN:
|
||||
_translate_ch_to_exc(rv)
|
||||
return rv
|
||||
|
||||
|
||||
else:
|
||||
import tty
|
||||
import termios
|
||||
|
||||
Vendored
-100
@@ -1,100 +0,0 @@
|
||||
import codecs
|
||||
import os
|
||||
from gettext import gettext as _
|
||||
|
||||
|
||||
def _verify_python_env() -> None:
|
||||
"""Ensures that the environment is good for Unicode."""
|
||||
try:
|
||||
from locale import getpreferredencoding
|
||||
|
||||
fs_enc = codecs.lookup(getpreferredencoding()).name
|
||||
except Exception:
|
||||
fs_enc = "ascii"
|
||||
|
||||
if fs_enc != "ascii":
|
||||
return
|
||||
|
||||
extra = [
|
||||
_(
|
||||
"Click will abort further execution because Python was"
|
||||
" configured to use ASCII as encoding for the environment."
|
||||
" Consult https://click.palletsprojects.com/unicode-support/"
|
||||
" for mitigation steps."
|
||||
)
|
||||
]
|
||||
|
||||
if os.name == "posix":
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
rv = subprocess.Popen(
|
||||
["locale", "-a"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding="ascii",
|
||||
errors="replace",
|
||||
).communicate()[0]
|
||||
except OSError:
|
||||
rv = ""
|
||||
|
||||
good_locales = set()
|
||||
has_c_utf8 = False
|
||||
|
||||
for line in rv.splitlines():
|
||||
locale = line.strip()
|
||||
|
||||
if locale.lower().endswith((".utf-8", ".utf8")):
|
||||
good_locales.add(locale)
|
||||
|
||||
if locale.lower() in ("c.utf8", "c.utf-8"):
|
||||
has_c_utf8 = True
|
||||
|
||||
if not good_locales:
|
||||
extra.append(
|
||||
_(
|
||||
"Additional information: on this system no suitable"
|
||||
" UTF-8 locales were discovered. This most likely"
|
||||
" requires resolving by reconfiguring the locale"
|
||||
" system."
|
||||
)
|
||||
)
|
||||
elif has_c_utf8:
|
||||
extra.append(
|
||||
_(
|
||||
"This system supports the C.UTF-8 locale which is"
|
||||
" recommended. You might be able to resolve your"
|
||||
" issue by exporting the following environment"
|
||||
" variables:"
|
||||
)
|
||||
)
|
||||
extra.append(" export LC_ALL=C.UTF-8\n export LANG=C.UTF-8")
|
||||
else:
|
||||
extra.append(
|
||||
_(
|
||||
"This system lists some UTF-8 supporting locales"
|
||||
" that you can pick from. The following suitable"
|
||||
" locales were discovered: {locales}"
|
||||
).format(locales=", ".join(sorted(good_locales)))
|
||||
)
|
||||
|
||||
bad_locale = None
|
||||
|
||||
for env_locale in os.environ.get("LC_ALL"), os.environ.get("LANG"):
|
||||
if env_locale and env_locale.lower().endswith((".utf-8", ".utf8")):
|
||||
bad_locale = env_locale
|
||||
|
||||
if env_locale is not None:
|
||||
break
|
||||
|
||||
if bad_locale is not None:
|
||||
extra.append(
|
||||
_(
|
||||
"Click discovered that you exported a UTF-8 locale"
|
||||
" but the locale system could not pick up from it"
|
||||
" because it does not exist. The exported locale is"
|
||||
" {locale!r} but it is not supported."
|
||||
).format(locale=bad_locale)
|
||||
)
|
||||
|
||||
raise RuntimeError("\n\n".join(extra))
|
||||
Vendored
+157
-112
@@ -1,8 +1,8 @@
|
||||
import enum
|
||||
import errno
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import typing
|
||||
import typing as t
|
||||
from collections import abc
|
||||
from contextlib import contextmanager
|
||||
@@ -14,7 +14,6 @@ from gettext import ngettext
|
||||
from itertools import repeat
|
||||
|
||||
from . import types
|
||||
from ._unicodefun import _verify_python_env
|
||||
from .exceptions import Abort
|
||||
from .exceptions import BadParameter
|
||||
from .exceptions import ClickException
|
||||
@@ -224,9 +223,14 @@ class Context:
|
||||
codes are used in texts that Click prints which is by
|
||||
default not the case. This for instance would affect
|
||||
help output.
|
||||
:param show_default: Show defaults for all options. If not set,
|
||||
defaults to the value from a parent context. Overrides an
|
||||
option's ``show_default`` argument.
|
||||
:param show_default: Show the default value for commands. If this
|
||||
value is not set, it defaults to the value from the parent
|
||||
context. ``Command.show_default`` overrides this default for the
|
||||
specific command.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
The ``show_default`` parameter is overridden by
|
||||
``Command.show_default``, instead of the other way around.
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
The ``show_default`` parameter defaults to the value from the
|
||||
@@ -288,6 +292,8 @@ class Context:
|
||||
#: must be never propagated to another arguments. This is used
|
||||
#: to implement nested parsing.
|
||||
self.protected_args: t.List[str] = []
|
||||
#: the collected prefixes of the command's options.
|
||||
self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set()
|
||||
|
||||
if obj is None and parent is not None:
|
||||
obj = parent.obj
|
||||
@@ -632,13 +638,13 @@ class Context:
|
||||
self.obj = rv = object_type()
|
||||
return rv
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def lookup_default(
|
||||
self, name: str, call: "te.Literal[True]" = True
|
||||
) -> t.Optional[t.Any]:
|
||||
...
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def lookup_default(
|
||||
self, name: str, call: "te.Literal[False]" = ...
|
||||
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
||||
@@ -956,7 +962,7 @@ class BaseCommand:
|
||||
|
||||
return results
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def main(
|
||||
self,
|
||||
args: t.Optional[t.Sequence[str]] = None,
|
||||
@@ -967,7 +973,7 @@ class BaseCommand:
|
||||
) -> "te.NoReturn":
|
||||
...
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def main(
|
||||
self,
|
||||
args: t.Optional[t.Sequence[str]] = None,
|
||||
@@ -1029,10 +1035,6 @@ class BaseCommand:
|
||||
.. versionchanged:: 3.0
|
||||
Added the ``standalone_mode`` parameter.
|
||||
"""
|
||||
# Verify that the environment is configured correctly, or reject
|
||||
# further execution to avoid a broken script.
|
||||
_verify_python_env()
|
||||
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
|
||||
@@ -1133,13 +1135,6 @@ class Command(BaseCommand):
|
||||
Click. A basic command handles command line parsing and might dispatch
|
||||
more parsing to commands nested below it.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Added the `context_settings` parameter.
|
||||
.. versionchanged:: 8.0
|
||||
Added repr showing the command name
|
||||
.. versionchanged:: 7.1
|
||||
Added the `no_args_is_help` parameter.
|
||||
|
||||
:param name: the name of the command to use unless a group overrides it.
|
||||
:param context_settings: an optional dictionary with defaults that are
|
||||
passed to the context object.
|
||||
@@ -1161,6 +1156,20 @@ class Command(BaseCommand):
|
||||
|
||||
:param deprecated: issues a message indicating that
|
||||
the command is deprecated.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
``help``, ``epilog``, and ``short_help`` are stored unprocessed,
|
||||
all formatting is done when outputting help text, not at init,
|
||||
and is done even if not using the ``@command`` decorator.
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
Added a ``repr`` showing the command name.
|
||||
|
||||
.. versionchanged:: 7.1
|
||||
Added the ``no_args_is_help`` parameter.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Added the ``context_settings`` parameter.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -1186,12 +1195,6 @@ class Command(BaseCommand):
|
||||
#: should show up in the help page and execute. Eager parameters
|
||||
#: will automatically be handled before non eager ones.
|
||||
self.params: t.List["Parameter"] = params or []
|
||||
|
||||
# if a form feed (page break) is found in the help text, truncate help
|
||||
# text to the content preceding the first form feed
|
||||
if help and "\f" in help:
|
||||
help = help.split("\f", 1)[0]
|
||||
|
||||
self.help = help
|
||||
self.epilog = epilog
|
||||
self.options_metavar = options_metavar
|
||||
@@ -1299,10 +1302,12 @@ class Command(BaseCommand):
|
||||
"""Gets short help for the command or makes it by shortening the
|
||||
long help string.
|
||||
"""
|
||||
text = self.short_help or ""
|
||||
|
||||
if not text and self.help:
|
||||
if self.short_help:
|
||||
text = inspect.cleandoc(self.short_help)
|
||||
elif self.help:
|
||||
text = make_default_short_help(self.help, limit)
|
||||
else:
|
||||
text = ""
|
||||
|
||||
if self.deprecated:
|
||||
text = _("(Deprecated) {text}").format(text=text)
|
||||
@@ -1328,12 +1333,13 @@ class Command(BaseCommand):
|
||||
|
||||
def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None:
|
||||
"""Writes the help text to the formatter if it exists."""
|
||||
text = self.help or ""
|
||||
text = self.help if self.help is not None else ""
|
||||
|
||||
if self.deprecated:
|
||||
text = _("(Deprecated) {text}").format(text=text)
|
||||
|
||||
if text:
|
||||
text = inspect.cleandoc(text).partition("\f")[0]
|
||||
formatter.write_paragraph()
|
||||
|
||||
with formatter.indentation():
|
||||
@@ -1354,9 +1360,11 @@ class Command(BaseCommand):
|
||||
def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None:
|
||||
"""Writes the epilog into the formatter if it exists."""
|
||||
if self.epilog:
|
||||
epilog = inspect.cleandoc(self.epilog)
|
||||
formatter.write_paragraph()
|
||||
|
||||
with formatter.indentation():
|
||||
formatter.write_text(self.epilog)
|
||||
formatter.write_text(epilog)
|
||||
|
||||
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]:
|
||||
if not args and self.no_args_is_help and not ctx.resilient_parsing:
|
||||
@@ -1379,6 +1387,7 @@ class Command(BaseCommand):
|
||||
)
|
||||
|
||||
ctx.args = args
|
||||
ctx._opt_prefixes.update(parser._opt_prefixes)
|
||||
return args
|
||||
|
||||
def invoke(self, ctx: Context) -> t.Any:
|
||||
@@ -1568,17 +1577,6 @@ class MultiCommand(Command):
|
||||
|
||||
return decorator
|
||||
|
||||
def resultcallback(self, replace: bool = False) -> t.Callable[[F], F]:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'resultcallback' has been renamed to 'result_callback'."
|
||||
" The old name will be removed in Click 8.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.result_callback(replace=replace)
|
||||
|
||||
def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None:
|
||||
"""Extra format methods for multi methods that adds all the commands
|
||||
after the options.
|
||||
@@ -1631,11 +1629,11 @@ class MultiCommand(Command):
|
||||
if not ctx.protected_args:
|
||||
if self.invoke_without_command:
|
||||
# No subcommand was invoked, so the result callback is
|
||||
# invoked with None for regular groups, or an empty list
|
||||
# for chained groups.
|
||||
# invoked with the group return value for regular
|
||||
# groups, or an empty list for chained groups.
|
||||
with ctx:
|
||||
super().invoke(ctx)
|
||||
return _process_result([] if self.chain else None)
|
||||
rv = super().invoke(ctx)
|
||||
return _process_result([] if self.chain else rv)
|
||||
ctx.fail(_("Missing command."))
|
||||
|
||||
# Fetch args back out
|
||||
@@ -1811,9 +1809,19 @@ class Group(MultiCommand):
|
||||
_check_multicommand(self, name, cmd, register=True)
|
||||
self.commands[name] = cmd
|
||||
|
||||
@t.overload
|
||||
def command(self, __func: t.Callable[..., t.Any]) -> Command:
|
||||
...
|
||||
|
||||
@t.overload
|
||||
def command(
|
||||
self, *args: t.Any, **kwargs: t.Any
|
||||
) -> t.Callable[[t.Callable[..., t.Any]], Command]:
|
||||
...
|
||||
|
||||
def command(
|
||||
self, *args: t.Any, **kwargs: t.Any
|
||||
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]:
|
||||
"""A shortcut decorator for declaring and attaching a command to
|
||||
the group. This takes the same arguments as :func:`command` and
|
||||
immediately registers the created command with this group by
|
||||
@@ -1822,24 +1830,49 @@ class Group(MultiCommand):
|
||||
To customize the command class used, set the
|
||||
:attr:`command_class` attribute.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
This decorator can be applied without parentheses.
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
Added the :attr:`command_class` attribute.
|
||||
"""
|
||||
from .decorators import command
|
||||
|
||||
if self.command_class is not None and "cls" not in kwargs:
|
||||
if self.command_class and kwargs.get("cls") is None:
|
||||
kwargs["cls"] = self.command_class
|
||||
|
||||
func: t.Optional[t.Callable] = None
|
||||
|
||||
if args and callable(args[0]):
|
||||
assert (
|
||||
len(args) == 1 and not kwargs
|
||||
), "Use 'command(**kwargs)(callable)' to provide arguments."
|
||||
(func,) = args
|
||||
args = ()
|
||||
|
||||
def decorator(f: t.Callable[..., t.Any]) -> Command:
|
||||
cmd = command(*args, **kwargs)(f)
|
||||
cmd: Command = command(*args, **kwargs)(f)
|
||||
self.add_command(cmd)
|
||||
return cmd
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
|
||||
return decorator
|
||||
|
||||
@t.overload
|
||||
def group(self, __func: t.Callable[..., t.Any]) -> "Group":
|
||||
...
|
||||
|
||||
@t.overload
|
||||
def group(
|
||||
self, *args: t.Any, **kwargs: t.Any
|
||||
) -> t.Callable[[t.Callable[..., t.Any]], "Group"]:
|
||||
...
|
||||
|
||||
def group(
|
||||
self, *args: t.Any, **kwargs: t.Any
|
||||
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]:
|
||||
"""A shortcut decorator for declaring and attaching a group to
|
||||
the group. This takes the same arguments as :func:`group` and
|
||||
immediately registers the created group with this group by
|
||||
@@ -1848,22 +1881,37 @@ class Group(MultiCommand):
|
||||
To customize the group class used, set the :attr:`group_class`
|
||||
attribute.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
This decorator can be applied without parentheses.
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
Added the :attr:`group_class` attribute.
|
||||
"""
|
||||
from .decorators import group
|
||||
|
||||
if self.group_class is not None and "cls" not in kwargs:
|
||||
func: t.Optional[t.Callable] = None
|
||||
|
||||
if args and callable(args[0]):
|
||||
assert (
|
||||
len(args) == 1 and not kwargs
|
||||
), "Use 'group(**kwargs)(callable)' to provide arguments."
|
||||
(func,) = args
|
||||
args = ()
|
||||
|
||||
if self.group_class is not None and kwargs.get("cls") is None:
|
||||
if self.group_class is type:
|
||||
kwargs["cls"] = type(self)
|
||||
else:
|
||||
kwargs["cls"] = self.group_class
|
||||
|
||||
def decorator(f: t.Callable[..., t.Any]) -> "Group":
|
||||
cmd = group(*args, **kwargs)(f)
|
||||
cmd: Group = group(*args, **kwargs)(f)
|
||||
self.add_command(cmd)
|
||||
return cmd
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
|
||||
return decorator
|
||||
|
||||
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]:
|
||||
@@ -2020,11 +2068,6 @@ class Parameter:
|
||||
t.Union[t.List["CompletionItem"], t.List[str]],
|
||||
]
|
||||
] = None,
|
||||
autocompletion: t.Optional[
|
||||
t.Callable[
|
||||
[Context, t.List[str], str], t.List[t.Union[t.Tuple[str, str], str]]
|
||||
]
|
||||
] = None,
|
||||
) -> None:
|
||||
self.name, self.opts, self.secondary_opts = self._parse_decls(
|
||||
param_decls or (), expose_value
|
||||
@@ -2048,36 +2091,6 @@ class Parameter:
|
||||
self.is_eager = is_eager
|
||||
self.metavar = metavar
|
||||
self.envvar = envvar
|
||||
|
||||
if autocompletion is not None:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'autocompletion' is renamed to 'shell_complete'. The old name is"
|
||||
" deprecated and will be removed in Click 8.1. See the docs about"
|
||||
" 'Parameter' for information about new behavior.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
def shell_complete(
|
||||
ctx: Context, param: "Parameter", incomplete: str
|
||||
) -> t.List["CompletionItem"]:
|
||||
from pipenv.vendor.click.shell_completion import CompletionItem
|
||||
|
||||
out = []
|
||||
|
||||
for c in autocompletion(ctx, [], incomplete): # type: ignore
|
||||
if isinstance(c, tuple):
|
||||
c = CompletionItem(c[0], help=c[1])
|
||||
elif isinstance(c, str):
|
||||
c = CompletionItem(c)
|
||||
|
||||
if c.value.startswith(incomplete):
|
||||
out.append(c)
|
||||
|
||||
return out
|
||||
|
||||
self._custom_shell_complete = shell_complete
|
||||
|
||||
if __debug__:
|
||||
@@ -2172,13 +2185,13 @@ class Parameter:
|
||||
|
||||
return metavar
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def get_default(
|
||||
self, ctx: Context, call: "te.Literal[True]" = True
|
||||
) -> t.Optional[t.Any]:
|
||||
...
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def get_default(
|
||||
self, ctx: Context, call: bool = ...
|
||||
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
||||
@@ -2399,25 +2412,27 @@ class Option(Parameter):
|
||||
|
||||
All other parameters are passed onwards to the parameter constructor.
|
||||
|
||||
:param show_default: controls if the default value should be shown on the
|
||||
help page. Normally, defaults are not shown. If this
|
||||
value is a string, it shows the string instead of the
|
||||
value. This is particularly useful for dynamic options.
|
||||
:param show_envvar: controls if an environment variable should be shown on
|
||||
the help page. Normally, environment variables
|
||||
are not shown.
|
||||
:param prompt: if set to `True` or a non empty string then the user will be
|
||||
prompted for input. If set to `True` the prompt will be the
|
||||
option name capitalized.
|
||||
:param show_default: Show the default value for this option in its
|
||||
help text. Values are not shown by default, unless
|
||||
:attr:`Context.show_default` is ``True``. If this value is a
|
||||
string, it shows that string in parentheses instead of the
|
||||
actual value. This is particularly useful for dynamic options.
|
||||
For single option boolean flags, the default remains hidden if
|
||||
its value is ``False``.
|
||||
:param show_envvar: Controls if an environment variable should be
|
||||
shown on the help page. Normally, environment variables are not
|
||||
shown.
|
||||
:param prompt: If set to ``True`` or a non empty string then the
|
||||
user will be prompted for input. If set to ``True`` the prompt
|
||||
will be the option name capitalized.
|
||||
:param confirmation_prompt: Prompt a second time to confirm the
|
||||
value if it was prompted for. Can be set to a string instead of
|
||||
``True`` to customize the message.
|
||||
:param prompt_required: If set to ``False``, the user will be
|
||||
prompted for input only when the option was specified as a flag
|
||||
without a value.
|
||||
:param hide_input: if this is `True` then the input on the prompt will be
|
||||
hidden from the user. This is useful for password
|
||||
input.
|
||||
:param hide_input: If this is ``True`` then the input on the prompt
|
||||
will be hidden from the user. This is useful for password input.
|
||||
:param is_flag: forces this option to act as a flag. The default is
|
||||
auto detection.
|
||||
:param flag_value: which value should be used for this flag if it's
|
||||
@@ -2435,6 +2450,18 @@ class Option(Parameter):
|
||||
:param help: the help string.
|
||||
:param hidden: hide this option from help outputs.
|
||||
|
||||
.. versionchanged:: 8.1.0
|
||||
Help text indentation is cleaned here instead of only in the
|
||||
``@option`` decorator.
|
||||
|
||||
.. versionchanged:: 8.1.0
|
||||
The ``show_default`` parameter overrides
|
||||
``Context.show_default``.
|
||||
|
||||
.. versionchanged:: 8.1.0
|
||||
The default of a single option boolean flag is not shown if the
|
||||
default value is ``False``.
|
||||
|
||||
.. versionchanged:: 8.0.1
|
||||
``type`` is detected from ``flag_value`` if given.
|
||||
"""
|
||||
@@ -2444,7 +2471,7 @@ class Option(Parameter):
|
||||
def __init__(
|
||||
self,
|
||||
param_decls: t.Optional[t.Sequence[str]] = None,
|
||||
show_default: t.Union[bool, str] = False,
|
||||
show_default: t.Union[bool, str, None] = None,
|
||||
prompt: t.Union[bool, str] = False,
|
||||
confirmation_prompt: t.Union[bool, str] = False,
|
||||
prompt_required: bool = True,
|
||||
@@ -2461,6 +2488,9 @@ class Option(Parameter):
|
||||
show_envvar: bool = False,
|
||||
**attrs: t.Any,
|
||||
) -> None:
|
||||
if help:
|
||||
help = inspect.cleandoc(help)
|
||||
|
||||
default_is_missing = "default" not in attrs
|
||||
super().__init__(param_decls, type=type, multiple=multiple, **attrs)
|
||||
|
||||
@@ -2472,7 +2502,7 @@ class Option(Parameter):
|
||||
elif prompt is False:
|
||||
prompt_text = None
|
||||
else:
|
||||
prompt_text = t.cast(str, prompt)
|
||||
prompt_text = prompt
|
||||
|
||||
self.prompt = prompt_text
|
||||
self.confirmation_prompt = confirmation_prompt
|
||||
@@ -2499,7 +2529,7 @@ class Option(Parameter):
|
||||
# flag if flag_value is set.
|
||||
self._flag_needs_value = flag_value is not None
|
||||
|
||||
if is_flag and default_is_missing:
|
||||
if is_flag and default_is_missing and not self.required:
|
||||
self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False
|
||||
|
||||
if flag_value is None:
|
||||
@@ -2550,6 +2580,9 @@ class Option(Parameter):
|
||||
if self.is_flag:
|
||||
raise TypeError("'count' is not valid with 'is_flag'.")
|
||||
|
||||
if self.multiple and self.is_flag:
|
||||
raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.")
|
||||
|
||||
def to_info_dict(self) -> t.Dict[str, t.Any]:
|
||||
info_dict = super().to_info_dict()
|
||||
info_dict.update(
|
||||
@@ -2711,16 +2744,23 @@ class Option(Parameter):
|
||||
finally:
|
||||
ctx.resilient_parsing = resilient
|
||||
|
||||
show_default_is_str = isinstance(self.show_default, str)
|
||||
show_default = False
|
||||
show_default_is_str = False
|
||||
|
||||
if show_default_is_str or (
|
||||
default_value is not None and (self.show_default or ctx.show_default)
|
||||
):
|
||||
if self.show_default is not None:
|
||||
if isinstance(self.show_default, str):
|
||||
show_default_is_str = show_default = True
|
||||
else:
|
||||
show_default = self.show_default
|
||||
elif ctx.show_default is not None:
|
||||
show_default = ctx.show_default
|
||||
|
||||
if show_default_is_str or (show_default and (default_value is not None)):
|
||||
if show_default_is_str:
|
||||
default_string = f"({self.show_default})"
|
||||
elif isinstance(default_value, (list, tuple)):
|
||||
default_string = ", ".join(str(d) for d in default_value)
|
||||
elif callable(default_value):
|
||||
elif inspect.isfunction(default_value):
|
||||
default_string = _("(dynamic)")
|
||||
elif self.is_bool_flag and self.secondary_opts:
|
||||
# For boolean flags that have distinct True/False opts,
|
||||
@@ -2728,6 +2768,8 @@ class Option(Parameter):
|
||||
default_string = split_opt(
|
||||
(self.opts if self.default else self.secondary_opts)[0]
|
||||
)[1]
|
||||
elif self.is_bool_flag and not self.secondary_opts and not default_value:
|
||||
default_string = ""
|
||||
else:
|
||||
default_string = str(default_value)
|
||||
|
||||
@@ -2753,13 +2795,13 @@ class Option(Parameter):
|
||||
|
||||
return ("; " if any_prefix_is_slash else " / ").join(rv), help
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def get_default(
|
||||
self, ctx: Context, call: "te.Literal[True]" = True
|
||||
) -> t.Optional[t.Any]:
|
||||
...
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def get_default(
|
||||
self, ctx: Context, call: bool = ...
|
||||
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
||||
@@ -2770,7 +2812,7 @@ class Option(Parameter):
|
||||
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
||||
# If we're a non boolean flag our default is more complex because
|
||||
# we need to look at all flags in the same group to figure out
|
||||
# if we're the the default one in which case we return the flag
|
||||
# if we're the default one in which case we return the flag
|
||||
# value as default.
|
||||
if self.is_flag and not self.is_bool_flag:
|
||||
for param in ctx.command.params:
|
||||
@@ -2821,7 +2863,10 @@ class Option(Parameter):
|
||||
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"
|
||||
rv = os.environ.get(envvar)
|
||||
|
||||
return rv
|
||||
if rv:
|
||||
return rv
|
||||
|
||||
return None
|
||||
|
||||
def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]:
|
||||
rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx)
|
||||
|
||||
Vendored
+97
-36
@@ -14,7 +14,7 @@ from .globals import get_current_context
|
||||
from .utils import echo
|
||||
|
||||
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
||||
FC = t.TypeVar("FC", t.Callable[..., t.Any], Command)
|
||||
FC = t.TypeVar("FC", bound=t.Union[t.Callable[..., t.Any], Command])
|
||||
|
||||
|
||||
def pass_context(f: F) -> F:
|
||||
@@ -121,43 +121,38 @@ def pass_meta_key(
|
||||
return decorator
|
||||
|
||||
|
||||
def _make_command(
|
||||
f: F,
|
||||
name: t.Optional[str],
|
||||
attrs: t.MutableMapping[str, t.Any],
|
||||
cls: t.Type[Command],
|
||||
CmdType = t.TypeVar("CmdType", bound=Command)
|
||||
|
||||
|
||||
@t.overload
|
||||
def command(
|
||||
__func: t.Callable[..., t.Any],
|
||||
) -> Command:
|
||||
if isinstance(f, Command):
|
||||
raise TypeError("Attempted to convert a callback into a command twice.")
|
||||
...
|
||||
|
||||
try:
|
||||
params = f.__click_params__ # type: ignore
|
||||
params.reverse()
|
||||
del f.__click_params__ # type: ignore
|
||||
except AttributeError:
|
||||
params = []
|
||||
|
||||
help = attrs.get("help")
|
||||
@t.overload
|
||||
def command(
|
||||
name: t.Optional[str] = None,
|
||||
**attrs: t.Any,
|
||||
) -> t.Callable[..., Command]:
|
||||
...
|
||||
|
||||
if help is None:
|
||||
help = inspect.getdoc(f)
|
||||
else:
|
||||
help = inspect.cleandoc(help)
|
||||
|
||||
attrs["help"] = help
|
||||
return cls(
|
||||
name=name or f.__name__.lower().replace("_", "-"),
|
||||
callback=f,
|
||||
params=params,
|
||||
**attrs,
|
||||
)
|
||||
@t.overload
|
||||
def command(
|
||||
name: t.Optional[str] = None,
|
||||
cls: t.Type[CmdType] = ...,
|
||||
**attrs: t.Any,
|
||||
) -> t.Callable[..., CmdType]:
|
||||
...
|
||||
|
||||
|
||||
def command(
|
||||
name: t.Optional[str] = None,
|
||||
name: t.Union[str, t.Callable[..., t.Any], None] = None,
|
||||
cls: t.Optional[t.Type[Command]] = None,
|
||||
**attrs: t.Any,
|
||||
) -> t.Callable[[F], Command]:
|
||||
) -> t.Union[Command, t.Callable[..., Command]]:
|
||||
r"""Creates a new :class:`Command` and uses the decorated function as
|
||||
callback. This will also automatically attach all decorated
|
||||
:func:`option`\s and :func:`argument`\s as parameters to the command.
|
||||
@@ -167,6 +162,8 @@ def command(
|
||||
pass the intended name as the first argument.
|
||||
|
||||
All keyword arguments are forwarded to the underlying command class.
|
||||
For the ``params`` argument, any decorated params are appended to
|
||||
the end of the list.
|
||||
|
||||
Once decorated the function turns into a :class:`Command` instance
|
||||
that can be invoked as a command line utility or be attached to a
|
||||
@@ -176,24 +173,91 @@ def command(
|
||||
name with underscores replaced by dashes.
|
||||
:param cls: the command class to instantiate. This defaults to
|
||||
:class:`Command`.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
This decorator can be applied without parentheses.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
The ``params`` argument can be used. Decorated params are
|
||||
appended to the end of the list.
|
||||
"""
|
||||
|
||||
func: t.Optional[t.Callable[..., t.Any]] = None
|
||||
|
||||
if callable(name):
|
||||
func = name
|
||||
name = None
|
||||
assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
|
||||
assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."
|
||||
|
||||
if cls is None:
|
||||
cls = Command
|
||||
|
||||
def decorator(f: t.Callable[..., t.Any]) -> Command:
|
||||
cmd = _make_command(f, name, attrs, cls) # type: ignore
|
||||
if isinstance(f, Command):
|
||||
raise TypeError("Attempted to convert a callback into a command twice.")
|
||||
|
||||
attr_params = attrs.pop("params", None)
|
||||
params = attr_params if attr_params is not None else []
|
||||
|
||||
try:
|
||||
decorator_params = f.__click_params__ # type: ignore
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
del f.__click_params__ # type: ignore
|
||||
params.extend(reversed(decorator_params))
|
||||
|
||||
if attrs.get("help") is None:
|
||||
attrs["help"] = f.__doc__
|
||||
|
||||
cmd = cls( # type: ignore[misc]
|
||||
name=name or f.__name__.lower().replace("_", "-"), # type: ignore[arg-type]
|
||||
callback=f,
|
||||
params=params,
|
||||
**attrs,
|
||||
)
|
||||
cmd.__doc__ = f.__doc__
|
||||
return cmd
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def group(name: t.Optional[str] = None, **attrs: t.Any) -> t.Callable[[F], Group]:
|
||||
@t.overload
|
||||
def group(
|
||||
__func: t.Callable[..., t.Any],
|
||||
) -> Group:
|
||||
...
|
||||
|
||||
|
||||
@t.overload
|
||||
def group(
|
||||
name: t.Optional[str] = None,
|
||||
**attrs: t.Any,
|
||||
) -> t.Callable[[F], Group]:
|
||||
...
|
||||
|
||||
|
||||
def group(
|
||||
name: t.Union[str, t.Callable[..., t.Any], None] = None, **attrs: t.Any
|
||||
) -> t.Union[Group, t.Callable[[F], Group]]:
|
||||
"""Creates a new :class:`Group` with a function as callback. This
|
||||
works otherwise the same as :func:`command` just that the `cls`
|
||||
parameter is set to :class:`Group`.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
This decorator can be applied without parentheses.
|
||||
"""
|
||||
attrs.setdefault("cls", Group)
|
||||
if attrs.get("cls") is None:
|
||||
attrs["cls"] = Group
|
||||
|
||||
if callable(name):
|
||||
grp: t.Callable[[F], Group] = t.cast(Group, command(**attrs))
|
||||
return grp(name)
|
||||
|
||||
return t.cast(Group, command(name, **attrs))
|
||||
|
||||
|
||||
@@ -219,7 +283,7 @@ def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]:
|
||||
"""
|
||||
|
||||
def decorator(f: FC) -> FC:
|
||||
ArgumentClass = attrs.pop("cls", Argument)
|
||||
ArgumentClass = attrs.pop("cls", None) or Argument
|
||||
_param_memo(f, ArgumentClass(param_decls, **attrs))
|
||||
return f
|
||||
|
||||
@@ -240,10 +304,7 @@ def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]:
|
||||
def decorator(f: FC) -> FC:
|
||||
# Issue 926, copy attrs, so pre-defined options can re-use the same cls=
|
||||
option_attrs = attrs.copy()
|
||||
|
||||
if "help" in option_attrs:
|
||||
option_attrs["help"] = inspect.cleandoc(option_attrs["help"])
|
||||
OptionClass = option_attrs.pop("cls", Option)
|
||||
OptionClass = option_attrs.pop("cls", None) or Option
|
||||
_param_memo(f, OptionClass(param_decls, **option_attrs))
|
||||
return f
|
||||
|
||||
|
||||
Vendored
+2
-3
@@ -1,4 +1,3 @@
|
||||
import typing
|
||||
import typing as t
|
||||
from threading import local
|
||||
|
||||
@@ -9,12 +8,12 @@ if t.TYPE_CHECKING:
|
||||
_local = local()
|
||||
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def get_current_context(silent: "te.Literal[False]" = False) -> "Context":
|
||||
...
|
||||
|
||||
|
||||
@typing.overload
|
||||
@t.overload
|
||||
def get_current_context(silent: bool = ...) -> t.Optional["Context"]:
|
||||
...
|
||||
|
||||
|
||||
+10
-11
@@ -102,10 +102,10 @@ _SOURCE_BASH = """\
|
||||
IFS=',' read type value <<< "$completion"
|
||||
|
||||
if [[ $type == 'dir' ]]; then
|
||||
COMREPLY=()
|
||||
COMPREPLY=()
|
||||
compopt -o dirnames
|
||||
elif [[ $type == 'file' ]]; then
|
||||
COMREPLY=()
|
||||
COMPREPLY=()
|
||||
compopt -o default
|
||||
elif [[ $type == 'plain' ]]; then
|
||||
COMPREPLY+=($value)
|
||||
@@ -448,17 +448,16 @@ def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _start_of_option(value: str) -> bool:
|
||||
def _start_of_option(ctx: Context, value: str) -> bool:
|
||||
"""Check if the value looks like the start of an option."""
|
||||
if not value:
|
||||
return False
|
||||
|
||||
c = value[0]
|
||||
# Allow "/" since that starts a path.
|
||||
return not c.isalnum() and c != "/"
|
||||
return c in ctx._opt_prefixes
|
||||
|
||||
|
||||
def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool:
|
||||
def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool:
|
||||
"""Determine if the given parameter is an option that needs a value.
|
||||
|
||||
:param args: List of complete args before the incomplete value.
|
||||
@@ -467,7 +466,7 @@ def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool:
|
||||
if not isinstance(param, Option):
|
||||
return False
|
||||
|
||||
if param.is_flag:
|
||||
if param.is_flag or param.count:
|
||||
return False
|
||||
|
||||
last_option = None
|
||||
@@ -476,7 +475,7 @@ def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool:
|
||||
if index + 1 > param.nargs:
|
||||
break
|
||||
|
||||
if _start_of_option(arg):
|
||||
if _start_of_option(ctx, arg):
|
||||
last_option = arg
|
||||
|
||||
return last_option is not None and last_option in param.opts
|
||||
@@ -551,7 +550,7 @@ def _resolve_incomplete(
|
||||
# split and discard the "=" to make completion easier.
|
||||
if incomplete == "=":
|
||||
incomplete = ""
|
||||
elif "=" in incomplete and _start_of_option(incomplete):
|
||||
elif "=" in incomplete and _start_of_option(ctx, incomplete):
|
||||
name, _, incomplete = incomplete.partition("=")
|
||||
args.append(name)
|
||||
|
||||
@@ -559,7 +558,7 @@ def _resolve_incomplete(
|
||||
# even if they start with the option character. If it hasn't been
|
||||
# given and the incomplete arg looks like an option, the current
|
||||
# command will provide option name completions.
|
||||
if "--" not in args and _start_of_option(incomplete):
|
||||
if "--" not in args and _start_of_option(ctx, incomplete):
|
||||
return ctx.command, incomplete
|
||||
|
||||
params = ctx.command.get_params(ctx)
|
||||
@@ -567,7 +566,7 @@ def _resolve_incomplete(
|
||||
# If the last complete arg is an option name with an incomplete
|
||||
# value, the option will provide value completions.
|
||||
for param in params:
|
||||
if _is_incomplete_option(args, param):
|
||||
if _is_incomplete_option(ctx, args, param):
|
||||
return param, incomplete
|
||||
|
||||
# It's not an option name or value. The first argument without a
|
||||
|
||||
Vendored
+4
-26
@@ -3,7 +3,6 @@ import io
|
||||
import itertools
|
||||
import os
|
||||
import sys
|
||||
import typing
|
||||
import typing as t
|
||||
from gettext import gettext as _
|
||||
|
||||
@@ -94,7 +93,7 @@ def prompt(
|
||||
"""Prompts a user for input. This is a convenience function that can
|
||||
be used to prompt a user for input later.
|
||||
|
||||
If the user aborts the input by sending a interrupt signal, this
|
||||
If the user aborts the input by sending an interrupt signal, this
|
||||
function will catch it and raise a :exc:`Abort` exception.
|
||||
|
||||
:param text: the text to show for the prompt.
|
||||
@@ -160,7 +159,6 @@ def prompt(
|
||||
if confirmation_prompt is True:
|
||||
confirmation_prompt = _("Repeat for confirmation")
|
||||
|
||||
confirmation_prompt = t.cast(str, confirmation_prompt)
|
||||
confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix)
|
||||
|
||||
while True:
|
||||
@@ -182,9 +180,9 @@ def prompt(
|
||||
if not confirmation_prompt:
|
||||
return result
|
||||
while True:
|
||||
confirmation_prompt = t.cast(str, confirmation_prompt)
|
||||
value2 = prompt_func(confirmation_prompt)
|
||||
if value2:
|
||||
is_empty = not value and not value2
|
||||
if value2 or is_empty:
|
||||
break
|
||||
if value == value2:
|
||||
return result
|
||||
@@ -252,26 +250,6 @@ def confirm(
|
||||
return rv
|
||||
|
||||
|
||||
def get_terminal_size() -> os.terminal_size:
|
||||
"""Returns the current size of the terminal as tuple in the form
|
||||
``(width, height)`` in columns and rows.
|
||||
|
||||
.. deprecated:: 8.0
|
||||
Will be removed in Click 8.1. Use
|
||||
:func:`shutil.get_terminal_size` instead.
|
||||
"""
|
||||
import shutil
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'click.get_terminal_size()' is deprecated and will be removed"
|
||||
" in Click 8.1. Use 'shutil.get_terminal_size()' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return shutil.get_terminal_size()
|
||||
|
||||
|
||||
def echo_via_pager(
|
||||
text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str],
|
||||
color: t.Optional[bool] = None,
|
||||
@@ -627,7 +605,7 @@ def unstyle(text: str) -> str:
|
||||
|
||||
def secho(
|
||||
message: t.Optional[t.Any] = None,
|
||||
file: t.Optional[t.IO] = None,
|
||||
file: t.Optional[t.IO[t.AnyStr]] = None,
|
||||
nl: bool = True,
|
||||
err: bool = False,
|
||||
color: t.Optional[bool] = None,
|
||||
|
||||
Vendored
+4
-4
@@ -464,16 +464,16 @@ class CliRunner:
|
||||
Added the ``temp_dir`` parameter.
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
t = tempfile.mkdtemp(dir=temp_dir)
|
||||
os.chdir(t)
|
||||
dt = tempfile.mkdtemp(dir=temp_dir) # type: ignore[type-var]
|
||||
os.chdir(dt)
|
||||
|
||||
try:
|
||||
yield t
|
||||
yield t.cast(str, dt)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
if temp_dir is None:
|
||||
try:
|
||||
shutil.rmtree(t)
|
||||
shutil.rmtree(dt)
|
||||
except OSError: # noqa: B014
|
||||
pass
|
||||
|
||||
Vendored
+45
-24
@@ -63,7 +63,14 @@ class ParamType:
|
||||
# The class name without the "ParamType" suffix.
|
||||
param_type = type(self).__name__.partition("ParamType")[0]
|
||||
param_type = param_type.partition("ParameterType")[0]
|
||||
return {"param_type": param_type, "name": self.name}
|
||||
|
||||
# Custom subclasses might not remember to set a name.
|
||||
if hasattr(self, "name"):
|
||||
name = self.name
|
||||
else:
|
||||
name = param_type
|
||||
|
||||
return {"param_type": param_type, "name": name}
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
@@ -724,7 +731,7 @@ class File(ParamType):
|
||||
|
||||
return f
|
||||
except OSError as e: # noqa: B014
|
||||
self.fail(f"{os.fsdecode(value)!r}: {e.strerror}", param, ctx)
|
||||
self.fail(f"'{os.fsdecode(value)}': {e.strerror}", param, ctx)
|
||||
|
||||
def shell_complete(
|
||||
self, ctx: "Context", param: "Parameter", incomplete: str
|
||||
@@ -744,30 +751,31 @@ class File(ParamType):
|
||||
|
||||
|
||||
class Path(ParamType):
|
||||
"""The path type is similar to the :class:`File` type but it performs
|
||||
different checks. First of all, instead of returning an open file
|
||||
handle it returns just the filename. Secondly, it can perform various
|
||||
basic checks about what the file or directory should be.
|
||||
"""The ``Path`` type is similar to the :class:`File` type, but
|
||||
returns the filename instead of an open file. Various checks can be
|
||||
enabled to validate the type of file and permissions.
|
||||
|
||||
:param exists: if set to true, the file or directory needs to exist for
|
||||
this value to be valid. If this is not required and a
|
||||
file does indeed not exist, then all further checks are
|
||||
silently skipped.
|
||||
:param file_okay: controls if a file is a possible value.
|
||||
:param dir_okay: controls if a directory is a possible value.
|
||||
:param writable: if true, a writable check is performed.
|
||||
:param exists: The file or directory needs to exist for the value to
|
||||
be valid. If this is not set to ``True``, and the file does not
|
||||
exist, then all further checks are silently skipped.
|
||||
:param file_okay: Allow a file as a value.
|
||||
:param dir_okay: Allow a directory as a value.
|
||||
:param readable: if true, a readable check is performed.
|
||||
:param resolve_path: if this is true, then the path is fully resolved
|
||||
before the value is passed onwards. This means
|
||||
that it's absolute and symlinks are resolved. It
|
||||
will not expand a tilde-prefix, as this is
|
||||
supposed to be done by the shell only.
|
||||
:param allow_dash: If this is set to `True`, a single dash to indicate
|
||||
standard streams is permitted.
|
||||
:param writable: if true, a writable check is performed.
|
||||
:param executable: if true, an executable check is performed.
|
||||
:param resolve_path: Make the value absolute and resolve any
|
||||
symlinks. A ``~`` is not expanded, as this is supposed to be
|
||||
done by the shell only.
|
||||
:param allow_dash: Allow a single dash as a value, which indicates
|
||||
a standard stream (but does not open it). Use
|
||||
:func:`~click.open_file` to handle opening this value.
|
||||
:param path_type: Convert the incoming path value to this type. If
|
||||
``None``, keep Python's default, which is ``str``. Useful to
|
||||
convert to :class:`pathlib.Path`.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
Added the ``executable`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
Allow passing ``type=pathlib.Path``.
|
||||
|
||||
@@ -787,12 +795,14 @@ class Path(ParamType):
|
||||
resolve_path: bool = False,
|
||||
allow_dash: bool = False,
|
||||
path_type: t.Optional[t.Type] = None,
|
||||
executable: bool = False,
|
||||
):
|
||||
self.exists = exists
|
||||
self.file_okay = file_okay
|
||||
self.dir_okay = dir_okay
|
||||
self.writable = writable
|
||||
self.readable = readable
|
||||
self.writable = writable
|
||||
self.executable = executable
|
||||
self.resolve_path = resolve_path
|
||||
self.allow_dash = allow_dash
|
||||
self.type = path_type
|
||||
@@ -865,12 +875,22 @@ class Path(ParamType):
|
||||
)
|
||||
if not self.dir_okay and stat.S_ISDIR(st.st_mode):
|
||||
self.fail(
|
||||
_("{name} {filename!r} is a directory.").format(
|
||||
_("{name} '{filename}' is a directory.").format(
|
||||
name=self.name.title(), filename=os.fsdecode(value)
|
||||
),
|
||||
param,
|
||||
ctx,
|
||||
)
|
||||
|
||||
if self.readable and not os.access(rv, os.R_OK):
|
||||
self.fail(
|
||||
_("{name} {filename!r} is not readable.").format(
|
||||
name=self.name.title(), filename=os.fsdecode(value)
|
||||
),
|
||||
param,
|
||||
ctx,
|
||||
)
|
||||
|
||||
if self.writable and not os.access(rv, os.W_OK):
|
||||
self.fail(
|
||||
_("{name} {filename!r} is not writable.").format(
|
||||
@@ -879,9 +899,10 @@ class Path(ParamType):
|
||||
param,
|
||||
ctx,
|
||||
)
|
||||
if self.readable and not os.access(rv, os.R_OK):
|
||||
|
||||
if self.executable and not os.access(value, os.X_OK):
|
||||
self.fail(
|
||||
_("{name} {filename!r} is not readable.").format(
|
||||
_("{name} {filename!r} is not executable.").format(
|
||||
name=self.name.title(), filename=os.fsdecode(value)
|
||||
),
|
||||
param,
|
||||
|
||||
Vendored
+39
-38
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import typing as t
|
||||
from functools import update_wrapper
|
||||
@@ -203,7 +204,7 @@ class KeepOpenFile:
|
||||
|
||||
def echo(
|
||||
message: t.Optional[t.Any] = None,
|
||||
file: t.Optional[t.IO] = None,
|
||||
file: t.Optional[t.IO[t.Any]] = None,
|
||||
nl: bool = True,
|
||||
err: bool = False,
|
||||
color: t.Optional[bool] = None,
|
||||
@@ -340,55 +341,45 @@ def open_file(
|
||||
lazy: bool = False,
|
||||
atomic: bool = False,
|
||||
) -> t.IO:
|
||||
"""This is similar to how the :class:`File` works but for manual
|
||||
usage. Files are opened non lazy by default. This can open regular
|
||||
files as well as stdin/stdout if ``'-'`` is passed.
|
||||
"""Open a file, with extra behavior to handle ``'-'`` to indicate
|
||||
a standard stream, lazy open on write, and atomic write. Similar to
|
||||
the behavior of the :class:`~click.File` param type.
|
||||
|
||||
If stdin/stdout is returned the stream is wrapped so that the context
|
||||
manager will not close the stream accidentally. This makes it possible
|
||||
to always use the function like this without having to worry to
|
||||
accidentally close a standard stream::
|
||||
If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is
|
||||
wrapped so that using it in a context manager will not close it.
|
||||
This makes it possible to use the function without accidentally
|
||||
closing a standard stream:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with open_file(filename) as f:
|
||||
...
|
||||
|
||||
.. versionadded:: 3.0
|
||||
:param filename: The name of the file to open, or ``'-'`` for
|
||||
``stdin``/``stdout``.
|
||||
:param mode: The mode in which to open the file.
|
||||
:param encoding: The encoding to decode or encode a file opened in
|
||||
text mode.
|
||||
:param errors: The error handling mode.
|
||||
:param lazy: Wait to open the file until it is accessed. For read
|
||||
mode, the file is temporarily opened to raise access errors
|
||||
early, then closed until it is read again.
|
||||
:param atomic: Write to a temporary file and replace the given file
|
||||
on close.
|
||||
|
||||
:param filename: the name of the file to open (or ``'-'`` for stdin/stdout).
|
||||
:param mode: the mode in which to open the file.
|
||||
:param encoding: the encoding to use.
|
||||
:param errors: the error handling for this file.
|
||||
:param lazy: can be flipped to true to open the file lazily.
|
||||
:param atomic: in atomic mode writes go into a temporary file and it's
|
||||
moved on close.
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
if lazy:
|
||||
return t.cast(t.IO, LazyFile(filename, mode, encoding, errors, atomic=atomic))
|
||||
|
||||
f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)
|
||||
|
||||
if not should_close:
|
||||
f = t.cast(t.IO, KeepOpenFile(f))
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def get_os_args() -> t.Sequence[str]:
|
||||
"""Returns the argument part of ``sys.argv``, removing the first
|
||||
value which is the name of the script.
|
||||
|
||||
.. deprecated:: 8.0
|
||||
Will be removed in Click 8.1. Access ``sys.argv[1:]`` directly
|
||||
instead.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'get_os_args' is deprecated and will be removed in Click 8.1."
|
||||
" Access 'sys.argv[1:]' directly instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return sys.argv[1:]
|
||||
|
||||
|
||||
def format_filename(
|
||||
filename: t.Union[str, bytes, os.PathLike], shorten: bool = False
|
||||
) -> str:
|
||||
@@ -484,7 +475,7 @@ class PacifyFlushWrapper:
|
||||
|
||||
|
||||
def _detect_program_name(
|
||||
path: t.Optional[str] = None, _main: ModuleType = sys.modules["__main__"]
|
||||
path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None
|
||||
) -> str:
|
||||
"""Determine the command used to run the program, for use in help
|
||||
text. If a file or entry point was executed, the file name is
|
||||
@@ -506,6 +497,9 @@ def _detect_program_name(
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
if _main is None:
|
||||
_main = sys.modules["__main__"]
|
||||
|
||||
if not path:
|
||||
path = sys.argv[0]
|
||||
|
||||
@@ -546,7 +540,7 @@ def _expand_args(
|
||||
See :func:`glob.glob`, :func:`os.path.expanduser`, and
|
||||
:func:`os.path.expandvars`.
|
||||
|
||||
This intended for use on Windows, where the shell does not do any
|
||||
This is intended for use on Windows, where the shell does not do any
|
||||
expansion. It may not exactly match what a Unix shell would do.
|
||||
|
||||
:param args: List of command line arguments to expand.
|
||||
@@ -554,6 +548,10 @@ def _expand_args(
|
||||
:param env: Expand environment variables.
|
||||
:param glob_recursive: ``**`` matches directories recursively.
|
||||
|
||||
.. versionchanged:: 8.1
|
||||
Invalid glob patterns are treated as empty expansions rather
|
||||
than raising an error.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
:meta private:
|
||||
@@ -569,7 +567,10 @@ def _expand_args(
|
||||
if env:
|
||||
arg = os.path.expandvars(arg)
|
||||
|
||||
matches = glob(arg, recursive=glob_recursive)
|
||||
try:
|
||||
matches = glob(arg, recursive=glob_recursive)
|
||||
except re.error:
|
||||
matches = []
|
||||
|
||||
if not matches:
|
||||
out.append(arg)
|
||||
|
||||
+28
-20
@@ -1,48 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Extension for ``click`` to provide a group
|
||||
with a git-like *did-you-mean* feature.
|
||||
"""
|
||||
|
||||
"""
|
||||
Extension for the python ``click`` module to provide
|
||||
a group with a git-like *did-you-mean* feature.
|
||||
"""
|
||||
import difflib
|
||||
import typing
|
||||
|
||||
import pipenv.vendor.click as click
|
||||
import difflib
|
||||
|
||||
__version__ = "0.0.3"
|
||||
|
||||
|
||||
class DYMMixin(object): # pylint: disable=too-few-public-methods
|
||||
class DYMMixin:
|
||||
"""
|
||||
Mixin class for click MultiCommand inherited classes
|
||||
to provide git-like *did-you-mean* functionality when
|
||||
a certain command is not registered.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
||||
self.max_suggestions = kwargs.pop("max_suggestions", 3)
|
||||
self.cutoff = kwargs.pop("cutoff", 0.5)
|
||||
super(DYMMixin, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs) # type: ignore
|
||||
|
||||
def resolve_command(self, ctx, args):
|
||||
def resolve_command(
|
||||
self, ctx: click.Context, args: typing.List[str]
|
||||
) -> typing.Tuple[
|
||||
typing.Optional[str], typing.Optional[click.Command], typing.List[str]
|
||||
]:
|
||||
"""
|
||||
Overrides clicks ``resolve_command`` method
|
||||
and appends *Did you mean ...* suggestions
|
||||
to the raised exception message.
|
||||
"""
|
||||
original_cmd_name = click.utils.make_str(args[0])
|
||||
|
||||
try:
|
||||
return super(DYMMixin, self).resolve_command(ctx, args)
|
||||
return super(DYMMixin, self).resolve_command(ctx, args) # type: ignore
|
||||
except click.exceptions.UsageError as error:
|
||||
error_msg = str(error)
|
||||
matches = difflib.get_close_matches(original_cmd_name,
|
||||
self.list_commands(ctx), self.max_suggestions, self.cutoff)
|
||||
original_cmd_name = click.utils.make_str(args[0])
|
||||
matches = difflib.get_close_matches(
|
||||
original_cmd_name,
|
||||
self.list_commands(ctx), # type: ignore
|
||||
self.max_suggestions,
|
||||
self.cutoff,
|
||||
)
|
||||
if matches:
|
||||
error_msg += '\n\nDid you mean one of these?\n %s' % '\n '.join(matches) # pylint: disable=line-too-long
|
||||
fmt_matches = "\n ".join(matches)
|
||||
error_msg += "\n\n"
|
||||
error_msg += f"Did you mean one of these?\n {fmt_matches}"
|
||||
|
||||
raise click.exceptions.UsageError(error_msg, error.ctx)
|
||||
|
||||
|
||||
class DYMGroup(DYMMixin, click.Group): # pylint: disable=too-many-public-methods
|
||||
class DYMGroup(DYMMixin, click.Group):
|
||||
"""
|
||||
click Group to provide git-like
|
||||
*did-you-mean* functionality when a certain
|
||||
@@ -50,7 +58,7 @@ class DYMGroup(DYMMixin, click.Group): # pylint: disable=too-many-public-method
|
||||
"""
|
||||
|
||||
|
||||
class DYMCommandCollection(DYMMixin, click.CommandCollection): # pylint: disable=too-many-public-methods
|
||||
class DYMCommandCollection(DYMMixin, click.CommandCollection):
|
||||
"""
|
||||
click CommandCollection to provide git-like
|
||||
*did-you-mean* functionality when a certain
|
||||
|
||||
Vendored
+16
-9
@@ -11,9 +11,10 @@ if t.TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
__version__ = "2.0.1"
|
||||
__version__ = "2.1.2"
|
||||
|
||||
_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
|
||||
_strip_comments_re = re.compile(r"<!--.*?-->", re.DOTALL)
|
||||
_strip_tags_re = re.compile(r"<.*?>", re.DOTALL)
|
||||
|
||||
|
||||
def _simple_escaping_wrapper(name: str) -> t.Callable[..., "Markup"]:
|
||||
@@ -92,19 +93,24 @@ class Markup(str):
|
||||
|
||||
return NotImplemented
|
||||
|
||||
def __mul__(self, num: int) -> "Markup":
|
||||
def __mul__(self, num: "te.SupportsIndex") -> "Markup":
|
||||
if isinstance(num, int):
|
||||
return self.__class__(super().__mul__(num))
|
||||
|
||||
return NotImplemented # type: ignore
|
||||
return NotImplemented
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __mod__(self, arg: t.Any) -> "Markup":
|
||||
if isinstance(arg, tuple):
|
||||
# a tuple of arguments, each wrapped
|
||||
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
||||
else:
|
||||
elif hasattr(type(arg), "__getitem__") and not isinstance(arg, str):
|
||||
# a mapping of arguments, wrapped
|
||||
arg = _MarkupEscapeHelper(arg, self.escape)
|
||||
else:
|
||||
# a single argument, wrapped with the helper and a tuple
|
||||
arg = (_MarkupEscapeHelper(arg, self.escape),)
|
||||
|
||||
return self.__class__(super().__mod__(arg))
|
||||
|
||||
@@ -153,8 +159,11 @@ class Markup(str):
|
||||
>>> Markup("Main »\t<em>About</em>").striptags()
|
||||
'Main » About'
|
||||
"""
|
||||
stripped = " ".join(_striptags_re.sub("", self).split())
|
||||
return Markup(stripped).unescape()
|
||||
# Use two regexes to avoid ambiguous matches.
|
||||
value = _strip_comments_re.sub("", self)
|
||||
value = _strip_tags_re.sub("", value)
|
||||
value = " ".join(value.split())
|
||||
return Markup(value).unescape()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s: t.Any) -> "Markup":
|
||||
@@ -280,9 +289,7 @@ try:
|
||||
from ._speedups import escape as escape
|
||||
from ._speedups import escape_silent as escape_silent
|
||||
from ._speedups import soft_str as soft_str
|
||||
from ._speedups import soft_unicode
|
||||
except ImportError:
|
||||
from ._native import escape as escape
|
||||
from ._native import escape_silent as escape_silent # noqa: F401
|
||||
from ._native import soft_str as soft_str # noqa: F401
|
||||
from ._native import soft_unicode # noqa: F401
|
||||
|
||||
Vendored
-12
@@ -61,15 +61,3 @@ def soft_str(s: t.Any) -> str:
|
||||
return str(s)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def soft_unicode(s: t.Any) -> str:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'soft_unicode' has been renamed to 'soft_str'. The old name"
|
||||
" will be removed in MarkupSafe 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return soft_str(s)
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2015 Vineet Naik (naikvin@gmail.com)
|
||||
Copyright (c) The pipdeptree developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
Vendored
+125
-6
@@ -1,4 +1,5 @@
|
||||
import argparse
|
||||
import fnmatch
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
@@ -10,6 +11,7 @@ from collections import defaultdict, deque
|
||||
from collections.abc import Mapping
|
||||
from importlib import import_module
|
||||
from itertools import chain
|
||||
from textwrap import dedent
|
||||
|
||||
from pipenv.patched.pip._vendor import pkg_resources
|
||||
|
||||
@@ -349,14 +351,14 @@ class PackageDAG(Mapping):
|
||||
m = {}
|
||||
seen = set()
|
||||
for node in self._obj.keys():
|
||||
if node.key in exclude:
|
||||
if any(fnmatch.fnmatch(node.key, e) for e in exclude):
|
||||
continue
|
||||
if include is None or node.key in include:
|
||||
if include is None or any(fnmatch.fnmatch(node.key, i) for i in include):
|
||||
stack.append(node)
|
||||
while True:
|
||||
if len(stack) > 0:
|
||||
n = stack.pop()
|
||||
cldn = [c for c in self._obj[n] if c.key not in exclude]
|
||||
cldn = [c for c in self._obj[n] if not any(fnmatch.fnmatch(c.key, e) for e in exclude)]
|
||||
m[n] = cldn
|
||||
seen.add(n.key)
|
||||
for c in cldn:
|
||||
@@ -550,6 +552,103 @@ def render_json_tree(tree, indent):
|
||||
return json.dumps([aux(p) for p in nodes], indent=indent)
|
||||
|
||||
|
||||
def render_mermaid(tree) -> str:
|
||||
"""Produce a Mermaid flowchart from the dependency graph.
|
||||
|
||||
:param dict tree: dependency graph
|
||||
"""
|
||||
# List of reserved keywords in Mermaid that cannot be used as node names.
|
||||
# See: https://github.com/mermaid-js/mermaid/issues/4182#issuecomment-1454787806
|
||||
reserved_ids: set[str] = {
|
||||
"C4Component",
|
||||
"C4Container",
|
||||
"C4Deployment",
|
||||
"C4Dynamic",
|
||||
"_blank",
|
||||
"_parent",
|
||||
"_self",
|
||||
"_top",
|
||||
"call",
|
||||
"class",
|
||||
"classDef",
|
||||
"click",
|
||||
"end",
|
||||
"flowchart",
|
||||
"flowchart-v2",
|
||||
"graph",
|
||||
"interpolate",
|
||||
"linkStyle",
|
||||
"style",
|
||||
"subgraph",
|
||||
}
|
||||
node_ids_map: dict[str:str] = {}
|
||||
|
||||
def mermaid_id(key: str) -> str:
|
||||
"""Returns a valid Mermaid node ID from a string."""
|
||||
# If we have already seen this key, return the canonical ID.
|
||||
canonical_id = node_ids_map.get(key)
|
||||
if canonical_id is not None:
|
||||
return canonical_id
|
||||
# If the key is not a reserved keyword, return it as is, and update the map.
|
||||
if key not in reserved_ids:
|
||||
node_ids_map[key] = key
|
||||
return key
|
||||
# If the key is a reserved keyword, append a number to it.
|
||||
number = 0
|
||||
while True:
|
||||
new_id = f"{key}_{number}"
|
||||
if new_id not in node_ids_map:
|
||||
node_ids_map[key] = new_id
|
||||
return new_id
|
||||
number += 1
|
||||
|
||||
# Use a sets to avoid duplicate entries.
|
||||
nodes: set[str] = set()
|
||||
edges: set[str] = set()
|
||||
|
||||
if isinstance(tree, ReversedPackageDAG):
|
||||
for package, reverse_dependencies in tree.items():
|
||||
package_label = "\\n".join(
|
||||
(package.project_name, "(missing)" if package.is_missing else package.installed_version)
|
||||
)
|
||||
package_key = mermaid_id(package.key)
|
||||
nodes.add(f'{package_key}["{package_label}"]')
|
||||
for reverse_dependency in reverse_dependencies:
|
||||
edge_label = reverse_dependency.req.version_spec or "any"
|
||||
reverse_dependency_key = mermaid_id(reverse_dependency.key)
|
||||
edges.add(f'{package_key} -- "{edge_label}" --> {reverse_dependency_key}')
|
||||
else:
|
||||
for package, dependencies in tree.items():
|
||||
package_label = "\\n".join((package.project_name, package.version))
|
||||
package_key = mermaid_id(package.key)
|
||||
nodes.add(f'{package_key}["{package_label}"]')
|
||||
for dependency in dependencies:
|
||||
edge_label = dependency.version_spec or "any"
|
||||
dependency_key = mermaid_id(dependency.key)
|
||||
if dependency.is_missing:
|
||||
dependency_label = f"{dependency.project_name}\\n(missing)"
|
||||
nodes.add(f'{dependency_key}["{dependency_label}"]:::missing')
|
||||
edges.add(f"{package_key} -.-> {dependency_key}")
|
||||
else:
|
||||
edges.add(f'{package_key} -- "{edge_label}" --> {dependency_key}')
|
||||
|
||||
# Produce the Mermaid Markdown.
|
||||
indent = " " * 4
|
||||
output = dedent(
|
||||
f"""\
|
||||
flowchart TD
|
||||
{indent}classDef missing stroke-dasharray: 5
|
||||
"""
|
||||
)
|
||||
# Sort the nodes and edges to make the output deterministic.
|
||||
output += indent
|
||||
output += f"\n{indent}".join(node for node in sorted(nodes))
|
||||
output += "\n" + indent
|
||||
output += f"\n{indent}".join(edge for edge in sorted(edges))
|
||||
output += "\n"
|
||||
return output
|
||||
|
||||
|
||||
def dump_graphviz(tree, output_format="dot", is_reverse=False):
|
||||
"""Output dependency graph as one of the supported GraphViz output formats.
|
||||
|
||||
@@ -612,7 +711,11 @@ def dump_graphviz(tree, output_format="dot", is_reverse=False):
|
||||
|
||||
# Allow output of dot format, even if GraphViz isn't installed.
|
||||
if output_format == "dot":
|
||||
return graph.source
|
||||
# Emulates graphviz.dot.Dot.__iter__() to force the sorting of graph.body.
|
||||
# Fixes https://github.com/tox-dev/pipdeptree/issues/188
|
||||
# That way we can guarantee the output of the dot format is deterministic
|
||||
# and stable.
|
||||
return "".join([tuple(graph)[0]] + sorted(graph.body) + [graph._tail])
|
||||
|
||||
# As it's unknown if the selected output format is binary or not, try to
|
||||
# decode it as UTF8 and only print it out in binary if that's not possible.
|
||||
@@ -742,12 +845,20 @@ def get_parser():
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--packages",
|
||||
help="Comma separated list of select packages to show " "in the output. If set, --all will be ignored.",
|
||||
help=(
|
||||
"Comma separated list of select packages to show in the output. "
|
||||
"Wildcards are supported, like 'somepackage.*'. "
|
||||
"If set, --all will be ignored."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--exclude",
|
||||
help="Comma separated list of select packages to exclude " "from the output. If set, --all will be ignored.",
|
||||
help=(
|
||||
"Comma separated list of select packages to exclude from the output. "
|
||||
"Wildcards are supported, like 'somepackage.*'. "
|
||||
"If set, --all will be ignored."
|
||||
),
|
||||
metavar="PACKAGES",
|
||||
)
|
||||
parser.add_argument(
|
||||
@@ -771,6 +882,12 @@ def get_parser():
|
||||
"This option overrides all other options (except --json)."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--mermaid",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=("Display dependency tree as a Maermaid graph. " "This option overrides all other options."),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--graph-output",
|
||||
dest="output_format",
|
||||
@@ -880,6 +997,8 @@ def main():
|
||||
print(render_json(tree, indent=4))
|
||||
elif args.json_tree:
|
||||
print(render_json_tree(tree, indent=4))
|
||||
elif args.mermaid:
|
||||
print(render_mermaid(tree))
|
||||
elif args.output_format:
|
||||
output = dump_graphviz(tree, output_format=args.output_format, is_reverse=args.reverse)
|
||||
print_graphviz(output)
|
||||
|
||||
Vendored
+2
-3
@@ -1,5 +1,4 @@
|
||||
# coding: utf-8
|
||||
# file generated by setuptools_scm
|
||||
# don't change, don't track in version control
|
||||
__version__ = version = '2.3.1'
|
||||
__version_tuple__ = version_tuple = (2, 3, 1)
|
||||
__version__ = version = '2.7.0'
|
||||
__version_tuple__ = version_tuple = (2, 7, 0)
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import os
|
||||
from ._core import ShellDetectionFailure
|
||||
|
||||
|
||||
__version__ = "1.5.0"
|
||||
__version__ = "1.5.0.post1"
|
||||
|
||||
|
||||
def detect_shell(pid=None, max_depth=10):
|
||||
|
||||
Vendored
+27
-26
@@ -1,31 +1,31 @@
|
||||
from .api import TOMLDocument
|
||||
from .api import aot
|
||||
from .api import array
|
||||
from .api import boolean
|
||||
from .api import comment
|
||||
from .api import date
|
||||
from .api import datetime
|
||||
from .api import document
|
||||
from .api import dump
|
||||
from .api import dumps
|
||||
from .api import float_
|
||||
from .api import inline_table
|
||||
from .api import integer
|
||||
from .api import item
|
||||
from .api import key
|
||||
from .api import key_value
|
||||
from .api import load
|
||||
from .api import loads
|
||||
from .api import nl
|
||||
from .api import parse
|
||||
from .api import string
|
||||
from .api import table
|
||||
from .api import time
|
||||
from .api import value
|
||||
from .api import ws
|
||||
from pipenv.vendor.tomlkit.api import TOMLDocument
|
||||
from pipenv.vendor.tomlkit.api import aot
|
||||
from pipenv.vendor.tomlkit.api import array
|
||||
from pipenv.vendor.tomlkit.api import boolean
|
||||
from pipenv.vendor.tomlkit.api import comment
|
||||
from pipenv.vendor.tomlkit.api import date
|
||||
from pipenv.vendor.tomlkit.api import datetime
|
||||
from pipenv.vendor.tomlkit.api import document
|
||||
from pipenv.vendor.tomlkit.api import dump
|
||||
from pipenv.vendor.tomlkit.api import dumps
|
||||
from pipenv.vendor.tomlkit.api import float_
|
||||
from pipenv.vendor.tomlkit.api import inline_table
|
||||
from pipenv.vendor.tomlkit.api import integer
|
||||
from pipenv.vendor.tomlkit.api import item
|
||||
from pipenv.vendor.tomlkit.api import key
|
||||
from pipenv.vendor.tomlkit.api import key_value
|
||||
from pipenv.vendor.tomlkit.api import load
|
||||
from pipenv.vendor.tomlkit.api import loads
|
||||
from pipenv.vendor.tomlkit.api import nl
|
||||
from pipenv.vendor.tomlkit.api import parse
|
||||
from pipenv.vendor.tomlkit.api import string
|
||||
from pipenv.vendor.tomlkit.api import table
|
||||
from pipenv.vendor.tomlkit.api import time
|
||||
from pipenv.vendor.tomlkit.api import value
|
||||
from pipenv.vendor.tomlkit.api import ws
|
||||
|
||||
|
||||
__version__ = "0.9.2"
|
||||
__version__ = "0.11.7"
|
||||
__all__ = [
|
||||
"aot",
|
||||
"array",
|
||||
@@ -49,6 +49,7 @@ __all__ = [
|
||||
"string",
|
||||
"table",
|
||||
"time",
|
||||
"TOMLDocument",
|
||||
"value",
|
||||
"ws",
|
||||
]
|
||||
|
||||
Vendored
+2
-3
@@ -1,3 +1,4 @@
|
||||
import contextlib
|
||||
import sys
|
||||
|
||||
from typing import Any
|
||||
@@ -15,9 +16,7 @@ def decode(string: Any, encodings: Optional[List[str]] = None):
|
||||
encodings = encodings or ["utf-8", "latin1", "ascii"]
|
||||
|
||||
for encoding in encodings:
|
||||
try:
|
||||
with contextlib.suppress(UnicodeEncodeError, UnicodeDecodeError):
|
||||
return string.decode(encoding)
|
||||
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||
pass
|
||||
|
||||
return string.decode(encodings[0], errors="ignore")
|
||||
|
||||
Vendored
+31
-13
@@ -6,9 +6,10 @@ from datetime import datetime
|
||||
from datetime import time
|
||||
from datetime import timedelta
|
||||
from datetime import timezone
|
||||
from typing import Collection
|
||||
from typing import Union
|
||||
|
||||
from ._compat import decode
|
||||
from pipenv.vendor.tomlkit._compat import decode
|
||||
|
||||
|
||||
RFC_3339_LOOSE = re.compile(
|
||||
@@ -97,31 +98,48 @@ def parse_rfc3339(string: str) -> Union[datetime, date, time]:
|
||||
raise ValueError("Invalid RFC 339 string")
|
||||
|
||||
|
||||
_escaped = {"b": "\b", "t": "\t", "n": "\n", "f": "\f", "r": "\r", '"': '"', "\\": "\\"}
|
||||
_escapes = {v: k for k, v in _escaped.items()}
|
||||
# https://toml.io/en/v1.0.0#string
|
||||
CONTROL_CHARS = frozenset(chr(c) for c in range(0x20)) | {chr(0x7F)}
|
||||
_escaped = {
|
||||
"b": "\b",
|
||||
"t": "\t",
|
||||
"n": "\n",
|
||||
"f": "\f",
|
||||
"r": "\r",
|
||||
'"': '"',
|
||||
"\\": "\\",
|
||||
}
|
||||
_compact_escapes = {
|
||||
**{v: f"\\{k}" for k, v in _escaped.items()},
|
||||
'"""': '""\\"',
|
||||
}
|
||||
_basic_escapes = CONTROL_CHARS | {'"', "\\"}
|
||||
|
||||
|
||||
def escape_string(s: str) -> str:
|
||||
def _unicode_escape(seq: str) -> str:
|
||||
return "".join(f"\\u{ord(c):04x}" for c in seq)
|
||||
|
||||
|
||||
def escape_string(s: str, escape_sequences: Collection[str] = _basic_escapes) -> str:
|
||||
s = decode(s)
|
||||
|
||||
res = []
|
||||
start = 0
|
||||
|
||||
def flush():
|
||||
def flush(inc=1):
|
||||
if start != i:
|
||||
res.append(s[start:i])
|
||||
|
||||
return i + 1
|
||||
return i + inc
|
||||
|
||||
i = 0
|
||||
while i < len(s):
|
||||
c = s[i]
|
||||
if c in '"\\\n\r\t\b\f':
|
||||
start = flush()
|
||||
res.append("\\" + _escapes[c])
|
||||
elif ord(c) < 0x20:
|
||||
start = flush()
|
||||
res.append("\\u%04x" % ord(c))
|
||||
for seq in escape_sequences:
|
||||
seq_len = len(seq)
|
||||
if s[i:].startswith(seq):
|
||||
start = flush(seq_len)
|
||||
res.append(_compact_escapes.get(seq) or _unicode_escape(seq))
|
||||
i += seq_len - 1 # fast-forward escape sequence
|
||||
i += 1
|
||||
|
||||
flush()
|
||||
|
||||
Vendored
+50
-29
@@ -3,33 +3,35 @@ import datetime as _datetime
|
||||
from collections.abc import Mapping
|
||||
from typing import IO
|
||||
from typing import Iterable
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from ._utils import parse_rfc3339
|
||||
from .container import Container
|
||||
from .exceptions import UnexpectedCharError
|
||||
from .items import AoT
|
||||
from .items import Array
|
||||
from .items import Bool
|
||||
from .items import Comment
|
||||
from .items import Date
|
||||
from .items import DateTime
|
||||
from .items import DottedKey
|
||||
from .items import Float
|
||||
from .items import InlineTable
|
||||
from .items import Integer
|
||||
from .items import Item as _Item
|
||||
from .items import Key
|
||||
from .items import SingleKey
|
||||
from .items import String
|
||||
from .items import Table
|
||||
from .items import Time
|
||||
from .items import Trivia
|
||||
from .items import Whitespace
|
||||
from .items import item
|
||||
from .parser import Parser
|
||||
from .toml_document import TOMLDocument
|
||||
from pipenv.vendor.tomlkit._utils import parse_rfc3339
|
||||
from pipenv.vendor.tomlkit.container import Container
|
||||
from pipenv.vendor.tomlkit.exceptions import UnexpectedCharError
|
||||
from pipenv.vendor.tomlkit.items import AoT
|
||||
from pipenv.vendor.tomlkit.items import Array
|
||||
from pipenv.vendor.tomlkit.items import Bool
|
||||
from pipenv.vendor.tomlkit.items import Comment
|
||||
from pipenv.vendor.tomlkit.items import Date
|
||||
from pipenv.vendor.tomlkit.items import DateTime
|
||||
from pipenv.vendor.tomlkit.items import DottedKey
|
||||
from pipenv.vendor.tomlkit.items import Float
|
||||
from pipenv.vendor.tomlkit.items import InlineTable
|
||||
from pipenv.vendor.tomlkit.items import Integer
|
||||
from pipenv.vendor.tomlkit.items import Item as _Item
|
||||
from pipenv.vendor.tomlkit.items import Key
|
||||
from pipenv.vendor.tomlkit.items import SingleKey
|
||||
from pipenv.vendor.tomlkit.items import String
|
||||
from pipenv.vendor.tomlkit.items import StringType as _StringType
|
||||
from pipenv.vendor.tomlkit.items import Table
|
||||
from pipenv.vendor.tomlkit.items import Time
|
||||
from pipenv.vendor.tomlkit.items import Trivia
|
||||
from pipenv.vendor.tomlkit.items import Whitespace
|
||||
from pipenv.vendor.tomlkit.items import item
|
||||
from pipenv.vendor.tomlkit.parser import Parser
|
||||
from pipenv.vendor.tomlkit.toml_document import TOMLDocument
|
||||
|
||||
|
||||
def loads(string: Union[str, bytes]) -> TOMLDocument:
|
||||
@@ -57,7 +59,7 @@ def dumps(data: Mapping, sort_keys: bool = False) -> str:
|
||||
raise TypeError(msg) from ex
|
||||
|
||||
|
||||
def load(fp: IO) -> TOMLDocument:
|
||||
def load(fp: Union[IO[str], IO[bytes]]) -> TOMLDocument:
|
||||
"""
|
||||
Load toml document from a file-like object.
|
||||
"""
|
||||
@@ -104,9 +106,28 @@ def boolean(raw: str) -> Bool:
|
||||
return item(raw == "true")
|
||||
|
||||
|
||||
def string(raw: str) -> String:
|
||||
"""Create a string item."""
|
||||
return item(raw)
|
||||
def string(
|
||||
raw: str,
|
||||
*,
|
||||
literal: bool = False,
|
||||
multiline: bool = False,
|
||||
escape: bool = True,
|
||||
) -> String:
|
||||
"""Create a string item.
|
||||
|
||||
By default, this function will create *single line basic* strings, but
|
||||
boolean flags (e.g. ``literal=True`` and/or ``multiline=True``)
|
||||
can be used for personalization.
|
||||
|
||||
For more information, please check the spec: `<https://toml.io/en/v1.0.0#string>`__.
|
||||
|
||||
Common escaping rules will be applied for basic strings.
|
||||
This can be controlled by explicitly setting ``escape=False``.
|
||||
Please note that, if you disable escaping, you will have to make sure that
|
||||
the given strings don't contain any forbidden character or sequence.
|
||||
"""
|
||||
type_ = _StringType.select(literal, multiline)
|
||||
return String.from_raw(raw, type_, escape)
|
||||
|
||||
|
||||
def date(raw: str) -> Date:
|
||||
@@ -154,7 +175,7 @@ def array(raw: str = None) -> Array:
|
||||
return value(raw)
|
||||
|
||||
|
||||
def table(is_super_table: bool = False) -> Table:
|
||||
def table(is_super_table: Optional[bool] = None) -> Table:
|
||||
"""Create an empty table.
|
||||
|
||||
:param is_super_table: if true, the table is a super table
|
||||
|
||||
Vendored
+107
-53
@@ -8,22 +8,22 @@ from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from ._compat import decode
|
||||
from ._utils import merge_dicts
|
||||
from .exceptions import KeyAlreadyPresent
|
||||
from .exceptions import NonExistentKey
|
||||
from .exceptions import TOMLKitError
|
||||
from .items import AoT
|
||||
from .items import Comment
|
||||
from .items import Item
|
||||
from .items import Key
|
||||
from .items import Null
|
||||
from .items import SingleKey
|
||||
from .items import Table
|
||||
from .items import Trivia
|
||||
from .items import Whitespace
|
||||
from .items import _CustomDict
|
||||
from .items import item as _item
|
||||
from pipenv.vendor.tomlkit._compat import decode
|
||||
from pipenv.vendor.tomlkit._utils import merge_dicts
|
||||
from pipenv.vendor.tomlkit.exceptions import KeyAlreadyPresent
|
||||
from pipenv.vendor.tomlkit.exceptions import NonExistentKey
|
||||
from pipenv.vendor.tomlkit.exceptions import TOMLKitError
|
||||
from pipenv.vendor.tomlkit.items import AoT
|
||||
from pipenv.vendor.tomlkit.items import Comment
|
||||
from pipenv.vendor.tomlkit.items import Item
|
||||
from pipenv.vendor.tomlkit.items import Key
|
||||
from pipenv.vendor.tomlkit.items import Null
|
||||
from pipenv.vendor.tomlkit.items import SingleKey
|
||||
from pipenv.vendor.tomlkit.items import Table
|
||||
from pipenv.vendor.tomlkit.items import Trivia
|
||||
from pipenv.vendor.tomlkit.items import Whitespace
|
||||
from pipenv.vendor.tomlkit.items import _CustomDict
|
||||
from pipenv.vendor.tomlkit.items import item as _item
|
||||
|
||||
|
||||
_NOT_SET = object()
|
||||
@@ -46,8 +46,27 @@ class Container(_CustomDict):
|
||||
def body(self) -> List[Tuple[Optional[Key], Item]]:
|
||||
return self._body
|
||||
|
||||
def unwrap(self) -> Dict[str, Any]:
|
||||
unwrapped = {}
|
||||
for k, v in self.items():
|
||||
if k is None:
|
||||
continue
|
||||
|
||||
if isinstance(k, Key):
|
||||
k = k.key
|
||||
|
||||
if hasattr(v, "unwrap"):
|
||||
v = v.unwrap()
|
||||
|
||||
if k in unwrapped:
|
||||
merge_dicts(unwrapped[k], v)
|
||||
else:
|
||||
unwrapped[k] = v
|
||||
|
||||
return unwrapped
|
||||
|
||||
@property
|
||||
def value(self) -> Dict[Any, Any]:
|
||||
def value(self) -> Dict[str, Any]:
|
||||
d = {}
|
||||
for k, v in self._body:
|
||||
if k is None:
|
||||
@@ -173,9 +192,9 @@ class Container(_CustomDict):
|
||||
item.name = key.key
|
||||
|
||||
prev = self._previous_item()
|
||||
prev_ws = isinstance(prev, Whitespace) or ends_with_withespace(prev)
|
||||
prev_ws = isinstance(prev, Whitespace) or ends_with_whitespace(prev)
|
||||
if isinstance(item, Table):
|
||||
if item.name != key.key:
|
||||
if not self._parsed:
|
||||
item.invalidate_display_name()
|
||||
if self._body and not (self._parsed or item.trivia.indent or prev_ws):
|
||||
item.trivia.indent = "\n"
|
||||
@@ -291,7 +310,7 @@ class Container(_CustomDict):
|
||||
previous_item = self._body[-1][1]
|
||||
if not (
|
||||
isinstance(previous_item, Whitespace)
|
||||
or ends_with_withespace(previous_item)
|
||||
or ends_with_whitespace(previous_item)
|
||||
or is_table
|
||||
or "\n" in previous_item.trivia.trail
|
||||
):
|
||||
@@ -327,6 +346,25 @@ class Container(_CustomDict):
|
||||
|
||||
return self
|
||||
|
||||
def _remove_at(self, idx: int) -> None:
|
||||
key = self._body[idx][0]
|
||||
index = self._map.get(key)
|
||||
if index is None:
|
||||
raise NonExistentKey(key)
|
||||
self._body[idx] = (None, Null())
|
||||
|
||||
if isinstance(index, tuple):
|
||||
index = list(index)
|
||||
index.remove(idx)
|
||||
if len(index) == 1:
|
||||
index = index.pop()
|
||||
else:
|
||||
index = tuple(index)
|
||||
self._map[key] = index
|
||||
else:
|
||||
dict.__delitem__(self, key.key)
|
||||
self._map.pop(key)
|
||||
|
||||
def remove(self, key: Union[Key, str]) -> "Container":
|
||||
"""Remove a key from the container."""
|
||||
if not isinstance(key, Key):
|
||||
@@ -406,7 +444,7 @@ class Container(_CustomDict):
|
||||
previous_item = self._body[idx - 1][1]
|
||||
if not (
|
||||
isinstance(previous_item, Whitespace)
|
||||
or ends_with_withespace(previous_item)
|
||||
or ends_with_whitespace(previous_item)
|
||||
or isinstance(item, (AoT, Table))
|
||||
or "\n" in previous_item.trivia.trail
|
||||
):
|
||||
@@ -487,7 +525,8 @@ class Container(_CustomDict):
|
||||
|
||||
if not table.is_super_table() or (
|
||||
any(
|
||||
not isinstance(v, (Table, AoT, Whitespace)) for _, v in table.value.body
|
||||
not isinstance(v, (Table, AoT, Whitespace, Null))
|
||||
for _, v in table.value.body
|
||||
)
|
||||
and not key.is_dotted()
|
||||
):
|
||||
@@ -495,16 +534,21 @@ class Container(_CustomDict):
|
||||
if table.is_aot_element():
|
||||
open_, close = "[[", "]]"
|
||||
|
||||
cur += "{}{}{}{}{}{}{}{}".format(
|
||||
table.trivia.indent,
|
||||
open_,
|
||||
decode(_key),
|
||||
close,
|
||||
table.trivia.comment_ws,
|
||||
decode(table.trivia.comment),
|
||||
table.trivia.trail,
|
||||
"\n" if "\n" not in table.trivia.trail and len(table.value) > 0 else "",
|
||||
newline_in_table_trivia = (
|
||||
"\n" if "\n" not in table.trivia.trail and len(table.value) > 0 else ""
|
||||
)
|
||||
cur += (
|
||||
f"{table.trivia.indent}"
|
||||
f"{open_}"
|
||||
f"{decode(_key)}"
|
||||
f"{close}"
|
||||
f"{table.trivia.comment_ws}"
|
||||
f"{decode(table.trivia.comment)}"
|
||||
f"{table.trivia.trail}"
|
||||
f"{newline_in_table_trivia}"
|
||||
)
|
||||
elif table.trivia.indent == "\n":
|
||||
cur += table.trivia.indent
|
||||
|
||||
for k, v in table.value.body:
|
||||
if isinstance(v, Table):
|
||||
@@ -545,14 +589,14 @@ class Container(_CustomDict):
|
||||
if not table.is_super_table():
|
||||
open_, close = "[[", "]]"
|
||||
|
||||
cur += "{}{}{}{}{}{}{}".format(
|
||||
table.trivia.indent,
|
||||
open_,
|
||||
decode(_key),
|
||||
close,
|
||||
table.trivia.comment_ws,
|
||||
decode(table.trivia.comment),
|
||||
table.trivia.trail,
|
||||
cur += (
|
||||
f"{table.trivia.indent}"
|
||||
f"{open_}"
|
||||
f"{decode(_key)}"
|
||||
f"{close}"
|
||||
f"{table.trivia.comment_ws}"
|
||||
f"{decode(table.trivia.comment)}"
|
||||
f"{table.trivia.trail}"
|
||||
)
|
||||
|
||||
for k, v in table.value.body:
|
||||
@@ -580,14 +624,14 @@ class Container(_CustomDict):
|
||||
if prefix is not None:
|
||||
_key = prefix + "." + _key
|
||||
|
||||
return "{}{}{}{}{}{}{}".format(
|
||||
item.trivia.indent,
|
||||
decode(_key),
|
||||
key.sep,
|
||||
decode(item.as_string()),
|
||||
item.trivia.comment_ws,
|
||||
decode(item.trivia.comment),
|
||||
item.trivia.trail,
|
||||
return (
|
||||
f"{item.trivia.indent}"
|
||||
f"{decode(_key)}"
|
||||
f"{key.sep}"
|
||||
f"{decode(item.as_string())}"
|
||||
f"{item.trivia.comment_ws}"
|
||||
f"{decode(item.trivia.comment)}"
|
||||
f"{item.trivia.trail}"
|
||||
)
|
||||
|
||||
def __len__(self) -> int:
|
||||
@@ -698,7 +742,7 @@ class Container(_CustomDict):
|
||||
# - it is not the last item
|
||||
last, _ = self._previous_item_with_index()
|
||||
idx = last if idx < 0 else idx
|
||||
has_ws = ends_with_withespace(value)
|
||||
has_ws = ends_with_whitespace(value)
|
||||
next_ws = idx < last and isinstance(self._body[idx + 1][1], Whitespace)
|
||||
if idx < last and not (next_ws or has_ws):
|
||||
value.append(None, Whitespace("\n"))
|
||||
@@ -783,7 +827,7 @@ class OutOfOrderTableProxy(_CustomDict):
|
||||
self._tables_map = {}
|
||||
|
||||
for i in indices:
|
||||
key, item = self._container._body[i]
|
||||
_, item = self._container._body[i]
|
||||
|
||||
if isinstance(item, Table):
|
||||
self._tables.append(item)
|
||||
@@ -794,6 +838,9 @@ class OutOfOrderTableProxy(_CustomDict):
|
||||
if k is not None:
|
||||
dict.__setitem__(self, k.key, v)
|
||||
|
||||
def unwrap(self) -> str:
|
||||
return self._internal_container.unwrap()
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._internal_container.value
|
||||
@@ -818,10 +865,20 @@ class OutOfOrderTableProxy(_CustomDict):
|
||||
if key is not None:
|
||||
dict.__setitem__(self, key, item)
|
||||
|
||||
def _remove_table(self, table: Table) -> None:
|
||||
"""Remove table from the parent container"""
|
||||
self._tables.remove(table)
|
||||
for idx, item in enumerate(self._container._body):
|
||||
if item[1] is table:
|
||||
self._container._remove_at(idx)
|
||||
break
|
||||
|
||||
def __delitem__(self, key: Union[Key, str]) -> None:
|
||||
if key in self._tables_map:
|
||||
table = self._tables[self._tables_map[key]]
|
||||
del table[key]
|
||||
if not table and len(self._tables) > 1:
|
||||
self._remove_table(table)
|
||||
del self._tables_map[key]
|
||||
else:
|
||||
raise NonExistentKey(key)
|
||||
@@ -836,15 +893,12 @@ class OutOfOrderTableProxy(_CustomDict):
|
||||
def __len__(self) -> int:
|
||||
return dict.__len__(self)
|
||||
|
||||
def __getattr__(self, attribute):
|
||||
return getattr(self._internal_container, attribute)
|
||||
|
||||
def setdefault(self, key: Union[Key, str], default: Any) -> Any:
|
||||
super().setdefault(key, default=default)
|
||||
return self[key]
|
||||
|
||||
|
||||
def ends_with_withespace(it: Any) -> bool:
|
||||
def ends_with_whitespace(it: Any) -> bool:
|
||||
"""Returns ``True`` if the given item ``it`` is a ``Table`` or ``AoT`` object
|
||||
ending with a ``Whitespace``.
|
||||
"""
|
||||
|
||||
Vendored
+14
-2
@@ -1,3 +1,4 @@
|
||||
from typing import Collection
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@@ -193,6 +194,7 @@ class KeyAlreadyPresent(TOMLKitError):
|
||||
"""
|
||||
|
||||
def __init__(self, key):
|
||||
key = getattr(key, "key", key)
|
||||
message = f'Key "{key}" already exists.'
|
||||
|
||||
super().__init__(message)
|
||||
@@ -208,8 +210,18 @@ class InvalidControlChar(ParseError):
|
||||
display_code += hex(char)[2:]
|
||||
|
||||
message = (
|
||||
"Control characters (codes less than 0x1f and 0x7f) are not allowed in {}, "
|
||||
"use {} instead".format(type, display_code)
|
||||
"Control characters (codes less than 0x1f and 0x7f)"
|
||||
f" are not allowed in {type}, "
|
||||
f"use {display_code} instead"
|
||||
)
|
||||
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidStringError(ValueError, TOMLKitError):
|
||||
def __init__(self, value: str, invalid_sequences: Collection[str], delimiter: str):
|
||||
repr_ = repr(value)[1:-1]
|
||||
super().__init__(
|
||||
f"Invalid string: {delimiter}{repr_}{delimiter}. "
|
||||
f"The character sequences {invalid_sequences} are invalid."
|
||||
)
|
||||
|
||||
Vendored
+505
-187
File diff suppressed because it is too large
Load Diff
Vendored
+74
-65
@@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
import re
|
||||
import string
|
||||
|
||||
@@ -7,48 +8,48 @@ from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import Union
|
||||
|
||||
from ._compat import decode
|
||||
from ._utils import RFC_3339_LOOSE
|
||||
from ._utils import _escaped
|
||||
from ._utils import parse_rfc3339
|
||||
from .container import Container
|
||||
from .exceptions import EmptyKeyError
|
||||
from .exceptions import EmptyTableNameError
|
||||
from .exceptions import InternalParserError
|
||||
from .exceptions import InvalidCharInStringError
|
||||
from .exceptions import InvalidControlChar
|
||||
from .exceptions import InvalidDateError
|
||||
from .exceptions import InvalidDateTimeError
|
||||
from .exceptions import InvalidNumberError
|
||||
from .exceptions import InvalidTimeError
|
||||
from .exceptions import InvalidUnicodeValueError
|
||||
from .exceptions import ParseError
|
||||
from .exceptions import UnexpectedCharError
|
||||
from .exceptions import UnexpectedEofError
|
||||
from .items import AoT
|
||||
from .items import Array
|
||||
from .items import Bool
|
||||
from .items import BoolType
|
||||
from .items import Comment
|
||||
from .items import Date
|
||||
from .items import DateTime
|
||||
from .items import Float
|
||||
from .items import InlineTable
|
||||
from .items import Integer
|
||||
from .items import Item
|
||||
from .items import Key
|
||||
from .items import KeyType
|
||||
from .items import Null
|
||||
from .items import SingleKey
|
||||
from .items import String
|
||||
from .items import StringType
|
||||
from .items import Table
|
||||
from .items import Time
|
||||
from .items import Trivia
|
||||
from .items import Whitespace
|
||||
from .source import Source
|
||||
from .toml_char import TOMLChar
|
||||
from .toml_document import TOMLDocument
|
||||
from pipenv.vendor.tomlkit._compat import decode
|
||||
from pipenv.vendor.tomlkit._utils import RFC_3339_LOOSE
|
||||
from pipenv.vendor.tomlkit._utils import _escaped
|
||||
from pipenv.vendor.tomlkit._utils import parse_rfc3339
|
||||
from pipenv.vendor.tomlkit.container import Container
|
||||
from pipenv.vendor.tomlkit.exceptions import EmptyKeyError
|
||||
from pipenv.vendor.tomlkit.exceptions import EmptyTableNameError
|
||||
from pipenv.vendor.tomlkit.exceptions import InternalParserError
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidCharInStringError
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidControlChar
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidDateError
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidDateTimeError
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidNumberError
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidTimeError
|
||||
from pipenv.vendor.tomlkit.exceptions import InvalidUnicodeValueError
|
||||
from pipenv.vendor.tomlkit.exceptions import ParseError
|
||||
from pipenv.vendor.tomlkit.exceptions import UnexpectedCharError
|
||||
from pipenv.vendor.tomlkit.exceptions import UnexpectedEofError
|
||||
from pipenv.vendor.tomlkit.items import AoT
|
||||
from pipenv.vendor.tomlkit.items import Array
|
||||
from pipenv.vendor.tomlkit.items import Bool
|
||||
from pipenv.vendor.tomlkit.items import BoolType
|
||||
from pipenv.vendor.tomlkit.items import Comment
|
||||
from pipenv.vendor.tomlkit.items import Date
|
||||
from pipenv.vendor.tomlkit.items import DateTime
|
||||
from pipenv.vendor.tomlkit.items import Float
|
||||
from pipenv.vendor.tomlkit.items import InlineTable
|
||||
from pipenv.vendor.tomlkit.items import Integer
|
||||
from pipenv.vendor.tomlkit.items import Item
|
||||
from pipenv.vendor.tomlkit.items import Key
|
||||
from pipenv.vendor.tomlkit.items import KeyType
|
||||
from pipenv.vendor.tomlkit.items import Null
|
||||
from pipenv.vendor.tomlkit.items import SingleKey
|
||||
from pipenv.vendor.tomlkit.items import String
|
||||
from pipenv.vendor.tomlkit.items import StringType
|
||||
from pipenv.vendor.tomlkit.items import Table
|
||||
from pipenv.vendor.tomlkit.items import Time
|
||||
from pipenv.vendor.tomlkit.items import Trivia
|
||||
from pipenv.vendor.tomlkit.items import Whitespace
|
||||
from pipenv.vendor.tomlkit.source import Source
|
||||
from pipenv.vendor.tomlkit.toml_char import TOMLChar
|
||||
from pipenv.vendor.tomlkit.toml_document import TOMLDocument
|
||||
|
||||
|
||||
CTRL_I = 0x09 # Tab
|
||||
@@ -146,7 +147,10 @@ class Parser:
|
||||
key, value = item
|
||||
if (key is not None and key.is_multi()) or not self._merge_ws(value, body):
|
||||
# We actually have a table
|
||||
body.append(key, value)
|
||||
try:
|
||||
body.append(key, value)
|
||||
except Exception as e:
|
||||
raise self.parse_error(ParseError, str(e)) from e
|
||||
|
||||
self.mark()
|
||||
|
||||
@@ -157,7 +161,10 @@ class Parser:
|
||||
# along with it.
|
||||
value = self._parse_aot(value, key)
|
||||
|
||||
body.append(key, value)
|
||||
try:
|
||||
body.append(key, value)
|
||||
except Exception as e:
|
||||
raise self.parse_error(ParseError, str(e)) from e
|
||||
|
||||
body.parsing(False)
|
||||
|
||||
@@ -226,7 +233,7 @@ class Parser:
|
||||
# Found a table, delegate to the calling function.
|
||||
return
|
||||
else:
|
||||
# Begining of a KV pair.
|
||||
# Beginning of a KV pair.
|
||||
# Return to beginning of whitespace so it gets included
|
||||
# as indentation for the KV about to be parsed.
|
||||
state.restore = True
|
||||
@@ -464,6 +471,7 @@ class Parser:
|
||||
# datetime
|
||||
try:
|
||||
dt = parse_rfc3339(raw)
|
||||
assert isinstance(dt, datetime.datetime)
|
||||
return DateTime(
|
||||
dt.year,
|
||||
dt.month,
|
||||
@@ -482,17 +490,20 @@ class Parser:
|
||||
if m.group(1):
|
||||
try:
|
||||
dt = parse_rfc3339(raw)
|
||||
assert isinstance(dt, datetime.date)
|
||||
date = Date(dt.year, dt.month, dt.day, trivia, raw)
|
||||
self.mark()
|
||||
while self._current not in "\t\n\r#,]}" and self.inc():
|
||||
pass
|
||||
|
||||
time_raw = self.extract()
|
||||
if not time_raw.strip():
|
||||
trivia.comment_ws = time_raw
|
||||
time_part = time_raw.rstrip()
|
||||
trivia.comment_ws = time_raw[len(time_part) :]
|
||||
if not time_part:
|
||||
return date
|
||||
|
||||
dt = parse_rfc3339(raw + time_raw)
|
||||
dt = parse_rfc3339(raw + time_part)
|
||||
assert isinstance(dt, datetime.datetime)
|
||||
return DateTime(
|
||||
dt.year,
|
||||
dt.month,
|
||||
@@ -503,7 +514,7 @@ class Parser:
|
||||
dt.microsecond,
|
||||
dt.tzinfo,
|
||||
trivia,
|
||||
raw + time_raw,
|
||||
raw + time_part,
|
||||
)
|
||||
except ValueError:
|
||||
raise self.parse_error(InvalidDateError)
|
||||
@@ -511,6 +522,7 @@ class Parser:
|
||||
if m.group(5):
|
||||
try:
|
||||
t = parse_rfc3339(raw)
|
||||
assert isinstance(t, datetime.time)
|
||||
return Time(
|
||||
t.hour,
|
||||
t.minute,
|
||||
@@ -672,10 +684,10 @@ class Parser:
|
||||
or sign
|
||||
and raw.startswith(".")
|
||||
):
|
||||
return
|
||||
return None
|
||||
|
||||
if raw.startswith(("0o", "0x", "0b")) and sign:
|
||||
return
|
||||
return None
|
||||
|
||||
digits = "[0-9]"
|
||||
base = 10
|
||||
@@ -693,14 +705,14 @@ class Parser:
|
||||
clean = re.sub(f"(?i)(?<={digits})_(?={digits})", "", raw).lower()
|
||||
|
||||
if "_" in clean:
|
||||
return
|
||||
return None
|
||||
|
||||
if (
|
||||
clean.endswith(".")
|
||||
or not clean.startswith("0x")
|
||||
and clean.split("e", 1)[0].endswith(".")
|
||||
):
|
||||
return
|
||||
return None
|
||||
|
||||
try:
|
||||
return Integer(int(sign + clean, base), trivia, sign + raw)
|
||||
@@ -708,7 +720,7 @@ class Parser:
|
||||
try:
|
||||
return Float(float(sign + clean), trivia, sign + raw)
|
||||
except ValueError:
|
||||
return
|
||||
return None
|
||||
|
||||
def _parse_literal_string(self) -> String:
|
||||
with self._state:
|
||||
@@ -802,9 +814,7 @@ class Parser:
|
||||
delim.is_singleline()
|
||||
and not escaped
|
||||
and (code == CHR_DEL or code <= CTRL_CHAR_LIMIT and code != CTRL_I)
|
||||
):
|
||||
raise self.parse_error(InvalidControlChar, code, "strings")
|
||||
elif (
|
||||
) or (
|
||||
delim.is_multiline()
|
||||
and not escaped
|
||||
and (
|
||||
@@ -901,8 +911,6 @@ class Parser:
|
||||
raise self.parse_error(UnexpectedEofError)
|
||||
elif self._current != "]":
|
||||
raise self.parse_error(UnexpectedCharError, self._current)
|
||||
elif not key.key.strip():
|
||||
raise self.parse_error(EmptyTableNameError)
|
||||
|
||||
key.sep = ""
|
||||
full_key = key
|
||||
@@ -916,7 +924,7 @@ class Parser:
|
||||
if parent_name:
|
||||
parent_name_parts = tuple(parent_name)
|
||||
else:
|
||||
parent_name_parts = tuple()
|
||||
parent_name_parts = ()
|
||||
|
||||
if len(name_parts) > len(parent_name_parts) + 1:
|
||||
missing_table = True
|
||||
@@ -939,6 +947,7 @@ class Parser:
|
||||
is_aot,
|
||||
name=name_parts[0].key if name_parts else key.key,
|
||||
display_name=full_key.as_string(),
|
||||
is_super_table=False,
|
||||
)
|
||||
|
||||
if len(name_parts) > 1:
|
||||
@@ -960,10 +969,9 @@ class Parser:
|
||||
key = name_parts[0]
|
||||
|
||||
for i, _name in enumerate(name_parts[1:]):
|
||||
if _name in table:
|
||||
child = table[_name]
|
||||
else:
|
||||
child = Table(
|
||||
child = table.get(
|
||||
_name,
|
||||
Table(
|
||||
Container(True),
|
||||
Trivia(indent, cws, comment, trail),
|
||||
is_aot and i == len(name_parts) - 2,
|
||||
@@ -972,7 +980,8 @@ class Parser:
|
||||
display_name=full_key.as_string()
|
||||
if i == len(name_parts) - 2
|
||||
else None,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
if is_aot and i == len(name_parts) - 2:
|
||||
table.raw_append(_name, AoT([child], name=table.name, parsed=True))
|
||||
|
||||
Vendored
+4
-8
@@ -4,9 +4,9 @@ from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
|
||||
from .exceptions import ParseError
|
||||
from .exceptions import UnexpectedCharError
|
||||
from .toml_char import TOMLChar
|
||||
from pipenv.vendor.tomlkit.exceptions import ParseError
|
||||
from pipenv.vendor.tomlkit.exceptions import UnexpectedCharError
|
||||
from pipenv.vendor.tomlkit.toml_char import TOMLChar
|
||||
|
||||
|
||||
class _State:
|
||||
@@ -129,11 +129,7 @@ class Source(str):
|
||||
Increments the parser by n characters
|
||||
if the end of the input has not been reached.
|
||||
"""
|
||||
for _ in range(n):
|
||||
if not self.inc(exception=exception):
|
||||
return False
|
||||
|
||||
return True
|
||||
return all(self.inc(exception=exception) for _ in range(n))
|
||||
|
||||
def consume(self, chars, min=0, max=-1):
|
||||
"""
|
||||
|
||||
Vendored
+1
-9
@@ -1,7 +1,5 @@
|
||||
import string
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class TOMLChar(str):
|
||||
def __init__(self, c):
|
||||
@@ -17,42 +15,36 @@ class TOMLChar(str):
|
||||
NL = "\n\r"
|
||||
WS = SPACES + NL
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_bare_key_char(self) -> bool:
|
||||
"""
|
||||
Whether the character is a valid bare key name or not.
|
||||
"""
|
||||
return self in self.BARE
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_kv_sep(self) -> bool:
|
||||
"""
|
||||
Whether the character is a valid key/value separator ot not.
|
||||
Whether the character is a valid key/value separator or not.
|
||||
"""
|
||||
return self in self.KV
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_int_float_char(self) -> bool:
|
||||
"""
|
||||
Whether the character if a valid integer or float value character or not.
|
||||
"""
|
||||
return self in self.NUMBER
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_ws(self) -> bool:
|
||||
"""
|
||||
Whether the character is a whitespace character or not.
|
||||
"""
|
||||
return self in self.WS
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_nl(self) -> bool:
|
||||
"""
|
||||
Whether the character is a new line character or not.
|
||||
"""
|
||||
return self in self.NL
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_spaces(self) -> bool:
|
||||
"""
|
||||
Whether the character is a space or not
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
from .container import Container
|
||||
from pipenv.vendor.tomlkit.container import Container
|
||||
|
||||
|
||||
class TOMLDocument(Container):
|
||||
|
||||
Vendored
+40
-5
@@ -1,5 +1,18 @@
|
||||
from .api import loads
|
||||
from .toml_document import TOMLDocument
|
||||
import os
|
||||
import re
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pipenv.vendor.tomlkit.api import loads
|
||||
from pipenv.vendor.tomlkit.toml_document import TOMLDocument
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import StrPath as _StrPath
|
||||
else:
|
||||
from typing import Union
|
||||
|
||||
_StrPath = Union[str, os.PathLike]
|
||||
|
||||
|
||||
class TOMLFile:
|
||||
@@ -9,15 +22,37 @@ class TOMLFile:
|
||||
:param path: path to the TOML file
|
||||
"""
|
||||
|
||||
def __init__(self, path: str) -> None:
|
||||
def __init__(self, path: _StrPath) -> None:
|
||||
self._path = path
|
||||
self._linesep = os.linesep
|
||||
|
||||
def read(self) -> TOMLDocument:
|
||||
"""Read the file content as a :class:`tomlkit.toml_document.TOMLDocument`."""
|
||||
with open(self._path, encoding="utf-8", newline="") as f:
|
||||
return loads(f.read())
|
||||
content = f.read()
|
||||
|
||||
# check if consistent line endings
|
||||
num_newline = content.count("\n")
|
||||
if num_newline > 0:
|
||||
num_win_eol = content.count("\r\n")
|
||||
if num_win_eol == num_newline:
|
||||
self._linesep = "\r\n"
|
||||
elif num_win_eol == 0:
|
||||
self._linesep = "\n"
|
||||
else:
|
||||
self._linesep = "mixed"
|
||||
|
||||
return loads(content)
|
||||
|
||||
def write(self, data: TOMLDocument) -> None:
|
||||
"""Write the TOMLDocument to the file."""
|
||||
content = data.as_string()
|
||||
|
||||
# apply linesep
|
||||
if self._linesep == "\n":
|
||||
content = content.replace("\r\n", "\n")
|
||||
elif self._linesep == "\r\n":
|
||||
content = re.sub(r"(?<!\r)\n", "\r\n", content)
|
||||
|
||||
with open(self._path, "w", encoding="utf-8", newline="") as f:
|
||||
f.write(data.as_string())
|
||||
f.write(content)
|
||||
|
||||
Vendored
+7
-7
@@ -1,20 +1,20 @@
|
||||
attrs==22.1.0
|
||||
attrs==23.1.0
|
||||
cerberus==1.3.4
|
||||
click-didyoumean==0.0.3
|
||||
click==8.0.3
|
||||
click-didyoumean==0.3.0
|
||||
click==8.1.3
|
||||
colorama==0.4.6
|
||||
dparse==0.6.2
|
||||
markupsafe==2.0.1
|
||||
markupsafe==2.1.2
|
||||
pep517==0.13.0
|
||||
pexpect==4.8.0
|
||||
pipdeptree==2.3.1
|
||||
pipdeptree==2.7.0
|
||||
plette[validation]==0.4.4
|
||||
ptyprocess==0.7.0
|
||||
python-dotenv==1.0.0
|
||||
pythonfinder==1.3.2
|
||||
requirementslib==2.2.4
|
||||
ruamel.yaml==0.17.21
|
||||
shellingham==1.5.0
|
||||
shellingham==1.5.0.post1
|
||||
toml==0.10.2
|
||||
tomlkit==0.9.2
|
||||
tomlkit==0.11.7
|
||||
vistir==0.8.0
|
||||
|
||||
Reference in New Issue
Block a user