mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Merge pull request #4533 from pypa/vendor/update
Update vendored dependencies
This commit is contained in:
+11
-27
@@ -58,7 +58,6 @@ Alternatively, you can set the ``PIPENV_PYPI_MIRROR`` environment variable.
|
||||
☤ Injecting credentials into Pipfiles via environment variables
|
||||
-----------------------------------------------------------------
|
||||
|
||||
|
||||
Pipenv will expand environment variables (if defined) in your Pipfile. Quite
|
||||
useful if you need to authenticate to a private PyPI::
|
||||
|
||||
@@ -76,6 +75,17 @@ If your credentials contain a special character, surround the references to the
|
||||
[[source]]
|
||||
url = "https://$USERNAME:'${PASSWORD}'@mypypi.example.com/simple"
|
||||
|
||||
Environment variables may be specified as ``${MY_ENVAR}`` or ``$MY_ENVAR``.
|
||||
|
||||
On Windows, ``%MY_ENVAR%`` is supported in addition to ``${MY_ENVAR}`` or ``$MY_ENVAR``.
|
||||
|
||||
Environment variables in the URL part of requirement specifiers can also be expanded, where the variable must be in the form of ``${VAR_NAME}``. Neither ``$VAR_NAME`` nor ``%VAR_NAME%`` is acceptable::
|
||||
|
||||
[[package]]
|
||||
requests = {git = "git://${USERNAME}:${PASSWORD}@private.git.com/psf/requests.git", ref = "2.22.0"}
|
||||
|
||||
Keep in mind that environment variables are expanded in runtime, leaving the entries in ``Pipfile`` or ``Pipfile.lock`` untouched. This is to avoid the accidental leakage of credentials in the source code.
|
||||
|
||||
|
||||
☤ Specifying Basically Anything
|
||||
-------------------------------
|
||||
@@ -436,32 +446,6 @@ You can then display the names and commands of your shortcuts by running ``pipen
|
||||
command script
|
||||
echospam echo I am really a very silly example
|
||||
|
||||
|
||||
☤ Support for Environment Variables
|
||||
-----------------------------------
|
||||
|
||||
Pipenv supports the usage of environment variables in place of authentication fragments
|
||||
in your Pipfile. These will only be parsed if they are present in the ``[[source]]``
|
||||
section. For example:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[[source]]
|
||||
url = "https://${PYPI_USERNAME}:${PYPI_PASSWORD}@my_private_repo.example.com/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
requests = {version="*", index="home"}
|
||||
maya = {version="*", index="pypi"}
|
||||
records = "*"
|
||||
|
||||
Environment variables may be specified as ``${MY_ENVAR}`` or ``$MY_ENVAR``.
|
||||
|
||||
On Windows, ``%MY_ENVAR%`` is supported in addition to ``${MY_ENVAR}`` or ``$MY_ENVAR``.
|
||||
|
||||
.. _configuration-with-environment-variables:
|
||||
|
||||
☤ Configuration With Environment Variables
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Support expanding environment variables in requirement URLs.
|
||||
@@ -0,0 +1,25 @@
|
||||
Update vendored dependencies:
|
||||
- ``colorama`` from ``0.4.3`` to ``0.4.4``
|
||||
- ``python-dotenv`` from ``0.10.3`` to ``0.15.0``
|
||||
- ``first`` from ``2.0.1`` to ``2.0.2``
|
||||
- ``iso8601`` from ``0.1.12`` to ``0.1.13``
|
||||
- ``parse`` from ``1.15.0`` to ``1.18.0``
|
||||
- ``pipdeptree`` from ``0.13.2`` to ``1.0.0``
|
||||
- ``requests`` from ``2.23.0`` to ``2.25.0``
|
||||
- ``idna`` from ``2.9`` to ``2.10``
|
||||
- ``urllib3`` from ``1.25.9`` to ``1.26.1``
|
||||
- ``certifi`` from ``2020.4.5.1`` to ``2020.11.8``
|
||||
- ``requirementslib`` from ``1.5.15`` to ``1.5.16``
|
||||
- ``attrs`` from ``19.3.0`` to ``20.3.0``
|
||||
- ``distlib`` from ``0.3.0`` to ``0.3.1``
|
||||
- ``packaging`` from ``20.3`` to ``20.4``
|
||||
- ``six`` from ``1.14.0`` to ``1.15.0``
|
||||
- ``semver`` from ``2.9.0`` to ``2.13.0``
|
||||
- ``toml`` from ``0.10.1`` to ``0.10.2``
|
||||
- ``cached-property`` from ``1.5.1`` to ``1.5.2``
|
||||
- ``yaspin`` from ``0.14.3`` to ``1.2.0``
|
||||
- ``resolvelib`` from ``0.3.0`` to ``0.5.2``
|
||||
- ``pep517`` from ``0.8.2`` to ``0.9.1``
|
||||
- ``zipp`` from ``0.6.0`` to ``1.2.0``
|
||||
- ``importlib-metadata`` from ``1.6.0`` to ``2.0.0``
|
||||
- ``importlib-resources`` from ``1.5.0`` to ``3.3.0``
|
||||
Vendored
+12
-4
@@ -1,10 +1,12 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import sys
|
||||
|
||||
from functools import partial
|
||||
|
||||
from . import converters, exceptions, filters, validators
|
||||
from . import converters, exceptions, filters, setters, validators
|
||||
from ._config import get_run_validators, set_run_validators
|
||||
from ._funcs import asdict, assoc, astuple, evolve, has
|
||||
from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types
|
||||
from ._make import (
|
||||
NOTHING,
|
||||
Attribute,
|
||||
@@ -19,7 +21,7 @@ from ._make import (
|
||||
from ._version_info import VersionInfo
|
||||
|
||||
|
||||
__version__ = "19.3.0"
|
||||
__version__ = "20.3.0"
|
||||
__version_info__ = VersionInfo._from_version_string(__version__)
|
||||
|
||||
__title__ = "attrs"
|
||||
@@ -39,7 +41,6 @@ s = attributes = attrs
|
||||
ib = attr = attrib
|
||||
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Attribute",
|
||||
"Factory",
|
||||
@@ -61,8 +62,15 @@ __all__ = [
|
||||
"has",
|
||||
"ib",
|
||||
"make_class",
|
||||
"resolve_types",
|
||||
"s",
|
||||
"set_run_validators",
|
||||
"setters",
|
||||
"validate",
|
||||
"validators",
|
||||
]
|
||||
|
||||
if sys.version_info[:2] >= (3, 6):
|
||||
from ._next_gen import define, field, frozen, mutable
|
||||
|
||||
__all__.extend((define, field, frozen, mutable))
|
||||
|
||||
Vendored
+170
-15
@@ -18,6 +18,7 @@ from typing import (
|
||||
from . import exceptions as exceptions
|
||||
from . import filters as filters
|
||||
from . import converters as converters
|
||||
from . import setters as setters
|
||||
from . import validators as validators
|
||||
|
||||
from ._version_info import VersionInfo
|
||||
@@ -37,20 +38,26 @@ _T = TypeVar("_T")
|
||||
_C = TypeVar("_C", bound=type)
|
||||
|
||||
_ValidatorType = Callable[[Any, Attribute[_T], _T], Any]
|
||||
_ConverterType = Callable[[Any], _T]
|
||||
_ConverterType = Callable[[Any], Any]
|
||||
_FilterType = Callable[[Attribute[_T], _T], bool]
|
||||
_ReprType = Callable[[Any], str]
|
||||
_ReprArgType = Union[bool, _ReprType]
|
||||
# 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.
|
||||
_OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any]
|
||||
_OnSetAttrArgType = Union[
|
||||
_OnSetAttrType, List[_OnSetAttrType], setters._NoOpType
|
||||
]
|
||||
_FieldTransformer = Callable[[type, List[Attribute]], List[Attribute]]
|
||||
# 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]]]
|
||||
|
||||
# _make --
|
||||
|
||||
NOTHING: object
|
||||
|
||||
# NOTE: Factory lies about its return type to make this possible: `x: List[int] = Factory(list)`
|
||||
# NOTE: Factory lies about its return type to make this possible:
|
||||
# `x: List[int] # = Factory(list)`
|
||||
# Work around mypy issue #4554 in the common case by using an overload.
|
||||
@overload
|
||||
def Factory(factory: Callable[[], _T]) -> _T: ...
|
||||
@@ -70,16 +77,17 @@ class Attribute(Generic[_T]):
|
||||
order: bool
|
||||
hash: Optional[bool]
|
||||
init: bool
|
||||
converter: Optional[_ConverterType[_T]]
|
||||
converter: Optional[_ConverterType]
|
||||
metadata: Dict[Any, Any]
|
||||
type: Optional[Type[_T]]
|
||||
kw_only: bool
|
||||
on_setattr: _OnSetAttrType
|
||||
|
||||
# NOTE: We had several choices for the annotation to use for type arg:
|
||||
# 1) Type[_T]
|
||||
# - Pros: Handles simple cases correctly
|
||||
# - Cons: Might produce less informative errors in the case of conflicting TypeVars
|
||||
# e.g. `attr.ib(default='bad', type=int)`
|
||||
# - Cons: Might produce less informative errors in the case of conflicting
|
||||
# TypeVars e.g. `attr.ib(default='bad', type=int)`
|
||||
# 2) Callable[..., _T]
|
||||
# - Pros: Better error messages than #1 for conflicting TypeVars
|
||||
# - Cons: Terrible error messages for validator checks.
|
||||
@@ -97,7 +105,8 @@ class Attribute(Generic[_T]):
|
||||
# This makes this type of assignments possible:
|
||||
# x: int = attr(8)
|
||||
#
|
||||
# This form catches explicit None or no default but with no other arguments returns Any.
|
||||
# This form catches explicit None or no default but with no other arguments
|
||||
# returns Any.
|
||||
@overload
|
||||
def attrib(
|
||||
default: None = ...,
|
||||
@@ -113,9 +122,11 @@ def attrib(
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the other arguments.
|
||||
# This form catches an explicit None or no default and infers the type from the
|
||||
# other arguments.
|
||||
@overload
|
||||
def attrib(
|
||||
default: None = ...,
|
||||
@@ -126,11 +137,12 @@ def attrib(
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: Optional[Type[_T]] = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
@@ -144,11 +156,12 @@ def attrib(
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: Optional[Type[_T]] = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
@@ -162,11 +175,83 @@ def attrib(
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: object = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: None = ...,
|
||||
validator: None = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: None = ...,
|
||||
factory: None = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the
|
||||
# other arguments.
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: None = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: _T,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: Optional[_T] = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
def attrs(
|
||||
@@ -187,6 +272,11 @@ def attrs(
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
auto_detect: bool = ...,
|
||||
collect_by_mro: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
def attrs(
|
||||
@@ -207,7 +297,61 @@ def attrs(
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
auto_detect: bool = ...,
|
||||
collect_by_mro: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
@overload
|
||||
def define(
|
||||
maybe_cls: _C,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: 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] = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
def define(
|
||||
maybe_cls: None = ...,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: 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] = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
|
||||
mutable = define
|
||||
frozen = define # they differ only in their defaults
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
class _Fields(Tuple[Attribute[Any], ...]):
|
||||
@@ -216,9 +360,15 @@ class _Fields(Tuple[Attribute[Any], ...]):
|
||||
def fields(cls: type) -> _Fields: ...
|
||||
def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ...
|
||||
def validate(inst: Any) -> None: ...
|
||||
def resolve_types(
|
||||
cls: _C,
|
||||
globalns: Optional[Dict[str, Any]] = ...,
|
||||
localns: Optional[Dict[str, Any]] = ...,
|
||||
) -> _C: ...
|
||||
|
||||
# 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', [attr.ib()])` is valid
|
||||
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
|
||||
# [attr.ib()])` is valid
|
||||
def make_class(
|
||||
name: str,
|
||||
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
|
||||
@@ -238,12 +388,16 @@ def make_class(
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
collect_by_mro: bool = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
) -> type: ...
|
||||
|
||||
# _funcs --
|
||||
|
||||
# TODO: add support for returning TypedDict from the mypy plugin
|
||||
# FIXME: asdict/astuple do not honor their factory args. waiting on one of these:
|
||||
# FIXME: asdict/astuple do not honor their factory args. Waiting on one of
|
||||
# these:
|
||||
# https://github.com/python/mypy/issues/4236
|
||||
# https://github.com/python/typing/issues/253
|
||||
def asdict(
|
||||
@@ -252,6 +406,7 @@ def asdict(
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
dict_factory: Type[Mapping[Any, Any]] = ...,
|
||||
retain_collection_types: bool = ...,
|
||||
value_serializer: Optional[Callable[[type, Attribute, Any], Any]] = ...,
|
||||
) -> Dict[str, Any]: ...
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
|
||||
Vendored
+4
-3
@@ -19,9 +19,10 @@ else:
|
||||
|
||||
|
||||
if PY2:
|
||||
from UserDict import IterableUserDict
|
||||
from collections import Mapping, Sequence
|
||||
|
||||
from UserDict import IterableUserDict
|
||||
|
||||
# We 'bundle' isclass instead of using inspect as importing inspect is
|
||||
# fairly expensive (order of 10-15 ms for a modern machine in 2016)
|
||||
def isclass(klass):
|
||||
@@ -90,7 +91,7 @@ if PY2:
|
||||
res.data.update(d) # We blocked update, so we have to do it like this.
|
||||
return res
|
||||
|
||||
def just_warn(*args, **kw): # pragma: nocover
|
||||
def just_warn(*args, **kw): # pragma: no cover
|
||||
"""
|
||||
We only warn on Python 3 because we are not aware of any concrete
|
||||
consequences of not setting the cell on Python 2.
|
||||
@@ -131,7 +132,7 @@ def make_set_closure_cell():
|
||||
"""
|
||||
# pypy makes this easy. (It also supports the logic below, but
|
||||
# why not do the easy/fast thing?)
|
||||
if PYPY: # pragma: no cover
|
||||
if PYPY:
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
cell.__setstate__((value,))
|
||||
|
||||
Vendored
+112
-12
@@ -13,6 +13,7 @@ def asdict(
|
||||
filter=None,
|
||||
dict_factory=dict,
|
||||
retain_collection_types=False,
|
||||
value_serializer=None,
|
||||
):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a dict.
|
||||
@@ -32,6 +33,10 @@ def asdict(
|
||||
:param bool retain_collection_types: Do not convert to ``list`` when
|
||||
encountering an attribute whose type is ``tuple`` or ``set``. Only
|
||||
meaningful if ``recurse`` is ``True``.
|
||||
:param Optional[callable] value_serializer: A hook that is called for every
|
||||
attribute or dict key/value. It receives the current instance, field
|
||||
and value and must return the (updated) value. The hook is run *after*
|
||||
the optional *filter* has been applied.
|
||||
|
||||
:rtype: return type of *dict_factory*
|
||||
|
||||
@@ -40,6 +45,7 @@ def asdict(
|
||||
|
||||
.. versionadded:: 16.0.0 *dict_factory*
|
||||
.. versionadded:: 16.1.0 *retain_collection_types*
|
||||
.. versionadded:: 20.3.0 *value_serializer*
|
||||
"""
|
||||
attrs = fields(inst.__class__)
|
||||
rv = dict_factory()
|
||||
@@ -47,17 +53,30 @@ def asdict(
|
||||
v = getattr(inst, a.name)
|
||||
if filter is not None and not filter(a, v):
|
||||
continue
|
||||
|
||||
if value_serializer is not None:
|
||||
v = value_serializer(inst, a, v)
|
||||
|
||||
if recurse is True:
|
||||
if has(v.__class__):
|
||||
rv[a.name] = asdict(
|
||||
v, True, filter, dict_factory, retain_collection_types
|
||||
v,
|
||||
True,
|
||||
filter,
|
||||
dict_factory,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
)
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
elif isinstance(v, (tuple, list, set, frozenset)):
|
||||
cf = v.__class__ if retain_collection_types is True else list
|
||||
rv[a.name] = cf(
|
||||
[
|
||||
_asdict_anything(
|
||||
i, filter, dict_factory, retain_collection_types
|
||||
i,
|
||||
filter,
|
||||
dict_factory,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
)
|
||||
for i in v
|
||||
]
|
||||
@@ -67,10 +86,18 @@ def asdict(
|
||||
rv[a.name] = df(
|
||||
(
|
||||
_asdict_anything(
|
||||
kk, filter, df, retain_collection_types
|
||||
kk,
|
||||
filter,
|
||||
df,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
),
|
||||
_asdict_anything(
|
||||
vv, filter, df, retain_collection_types
|
||||
vv,
|
||||
filter,
|
||||
df,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
),
|
||||
)
|
||||
for kk, vv in iteritems(v)
|
||||
@@ -82,19 +109,36 @@ def asdict(
|
||||
return rv
|
||||
|
||||
|
||||
def _asdict_anything(val, filter, dict_factory, retain_collection_types):
|
||||
def _asdict_anything(
|
||||
val,
|
||||
filter,
|
||||
dict_factory,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
):
|
||||
"""
|
||||
``asdict`` only works on attrs instances, this works on anything.
|
||||
"""
|
||||
if getattr(val.__class__, "__attrs_attrs__", None) is not None:
|
||||
# Attrs class.
|
||||
rv = asdict(val, True, filter, dict_factory, retain_collection_types)
|
||||
elif isinstance(val, (tuple, list, set)):
|
||||
rv = asdict(
|
||||
val,
|
||||
True,
|
||||
filter,
|
||||
dict_factory,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
)
|
||||
elif isinstance(val, (tuple, list, set, frozenset)):
|
||||
cf = val.__class__ if retain_collection_types is True else list
|
||||
rv = cf(
|
||||
[
|
||||
_asdict_anything(
|
||||
i, filter, dict_factory, retain_collection_types
|
||||
i,
|
||||
filter,
|
||||
dict_factory,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
)
|
||||
for i in val
|
||||
]
|
||||
@@ -103,13 +147,20 @@ def _asdict_anything(val, filter, dict_factory, retain_collection_types):
|
||||
df = dict_factory
|
||||
rv = df(
|
||||
(
|
||||
_asdict_anything(kk, filter, df, retain_collection_types),
|
||||
_asdict_anything(vv, filter, df, retain_collection_types),
|
||||
_asdict_anything(
|
||||
kk, filter, df, retain_collection_types, value_serializer
|
||||
),
|
||||
_asdict_anything(
|
||||
vv, filter, df, retain_collection_types, value_serializer
|
||||
),
|
||||
)
|
||||
for kk, vv in iteritems(val)
|
||||
)
|
||||
else:
|
||||
rv = val
|
||||
if value_serializer is not None:
|
||||
rv = value_serializer(None, None, rv)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
@@ -164,7 +215,7 @@ def astuple(
|
||||
retain_collection_types=retain,
|
||||
)
|
||||
)
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
elif isinstance(v, (tuple, list, set, frozenset)):
|
||||
cf = v.__class__ if retain is True else list
|
||||
rv.append(
|
||||
cf(
|
||||
@@ -209,6 +260,7 @@ def astuple(
|
||||
rv.append(v)
|
||||
else:
|
||||
rv.append(v)
|
||||
|
||||
return rv if tuple_factory is list else tuple_factory(rv)
|
||||
|
||||
|
||||
@@ -287,4 +339,52 @@ def evolve(inst, **changes):
|
||||
init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
|
||||
if init_name not in changes:
|
||||
changes[init_name] = getattr(inst, attr_name)
|
||||
|
||||
return cls(**changes)
|
||||
|
||||
|
||||
def resolve_types(cls, globalns=None, localns=None):
|
||||
"""
|
||||
Resolve any strings and forward annotations in type annotations.
|
||||
|
||||
This is only required if you need concrete types in `Attribute`'s *type*
|
||||
field. In other words, you don't need to resolve your types if you only
|
||||
use them for static type checking.
|
||||
|
||||
With no arguments, names will be looked up in the module in which the class
|
||||
was created. If this is not what you want, e.g. if the name only exists
|
||||
inside a method, you may pass *globalns* or *localns* to specify other
|
||||
dictionaries in which to look up these names. See the docs of
|
||||
`typing.get_type_hints` for more details.
|
||||
|
||||
:param type cls: Class to resolve.
|
||||
:param Optional[dict] globalns: Dictionary containing global variables.
|
||||
:param Optional[dict] localns: Dictionary containing local variables.
|
||||
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
class.
|
||||
:raise NameError: If types cannot be resolved because of missing variables.
|
||||
|
||||
:returns: *cls* so you can use this function also as a class decorator.
|
||||
Please note that you have to apply it **after** `attr.s`. That means
|
||||
the decorator has to come in the line **before** `attr.s`.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
try:
|
||||
# Since calling get_type_hints is expensive we cache whether we've
|
||||
# done it already.
|
||||
cls.__attrs_types_resolved__
|
||||
except AttributeError:
|
||||
import typing
|
||||
|
||||
hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
|
||||
for field in fields(cls):
|
||||
if field.name in hints:
|
||||
# Since fields have been frozen we must work around it.
|
||||
_obj_setattr(field, "type", hints[field.name])
|
||||
cls.__attrs_types_resolved__ = True
|
||||
|
||||
# Return the class so you can use it as a decorator too.
|
||||
return cls
|
||||
|
||||
Vendored
+923
-326
File diff suppressed because it is too large
Load Diff
Vendored
+160
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
This is a Python 3.6 and later-only, keyword-only, and **provisional** API that
|
||||
calls `attr.s` with different default values.
|
||||
|
||||
Provisional APIs that shall become "import attrs" one glorious day.
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
|
||||
from attr.exceptions import UnannotatedAttributeError
|
||||
|
||||
from . import setters
|
||||
from ._make import NOTHING, _frozen_setattrs, attrib, attrs
|
||||
|
||||
|
||||
def define(
|
||||
maybe_cls=None,
|
||||
*,
|
||||
these=None,
|
||||
repr=None,
|
||||
hash=None,
|
||||
init=None,
|
||||
slots=True,
|
||||
frozen=False,
|
||||
weakref_slot=True,
|
||||
str=False,
|
||||
auto_attribs=None,
|
||||
kw_only=False,
|
||||
cache_hash=False,
|
||||
auto_exc=True,
|
||||
eq=None,
|
||||
order=False,
|
||||
auto_detect=True,
|
||||
getstate_setstate=None,
|
||||
on_setattr=None,
|
||||
field_transformer=None,
|
||||
):
|
||||
r"""
|
||||
The only behavioral differences are the handling of the *auto_attribs*
|
||||
option:
|
||||
|
||||
:param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves
|
||||
exactly like `attr.s`. If left `None`, `attr.s` will try to guess:
|
||||
|
||||
1. If all attributes are annotated and no `attr.ib` is found, it assumes
|
||||
*auto_attribs=True*.
|
||||
2. Otherwise it assumes *auto_attribs=False* and tries to collect
|
||||
`attr.ib`\ s.
|
||||
|
||||
and that mutable classes (``frozen=False``) validate on ``__setattr__``.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
def do_it(cls, auto_attribs):
|
||||
return attrs(
|
||||
maybe_cls=cls,
|
||||
these=these,
|
||||
repr=repr,
|
||||
hash=hash,
|
||||
init=init,
|
||||
slots=slots,
|
||||
frozen=frozen,
|
||||
weakref_slot=weakref_slot,
|
||||
str=str,
|
||||
auto_attribs=auto_attribs,
|
||||
kw_only=kw_only,
|
||||
cache_hash=cache_hash,
|
||||
auto_exc=auto_exc,
|
||||
eq=eq,
|
||||
order=order,
|
||||
auto_detect=auto_detect,
|
||||
collect_by_mro=True,
|
||||
getstate_setstate=getstate_setstate,
|
||||
on_setattr=on_setattr,
|
||||
field_transformer=field_transformer,
|
||||
)
|
||||
|
||||
def wrap(cls):
|
||||
"""
|
||||
Making this a wrapper ensures this code runs during class creation.
|
||||
|
||||
We also ensure that frozen-ness of classes is inherited.
|
||||
"""
|
||||
nonlocal frozen, on_setattr
|
||||
|
||||
had_on_setattr = on_setattr not in (None, setters.NO_OP)
|
||||
|
||||
# By default, mutable classes validate on setattr.
|
||||
if frozen is False and on_setattr is None:
|
||||
on_setattr = setters.validate
|
||||
|
||||
# However, if we subclass a frozen class, we inherit the immutability
|
||||
# and disable on_setattr.
|
||||
for base_cls in cls.__bases__:
|
||||
if base_cls.__setattr__ is _frozen_setattrs:
|
||||
if had_on_setattr:
|
||||
raise ValueError(
|
||||
"Frozen classes can't use on_setattr "
|
||||
"(frozen-ness was inherited)."
|
||||
)
|
||||
|
||||
on_setattr = setters.NO_OP
|
||||
break
|
||||
|
||||
if auto_attribs is not None:
|
||||
return do_it(cls, auto_attribs)
|
||||
|
||||
try:
|
||||
return do_it(cls, True)
|
||||
except UnannotatedAttributeError:
|
||||
return do_it(cls, False)
|
||||
|
||||
# maybe_cls's type depends on the usage of the decorator. It's a class
|
||||
# if it's used as `@attrs` but ``None`` if used as `@attrs()`.
|
||||
if maybe_cls is None:
|
||||
return wrap
|
||||
else:
|
||||
return wrap(maybe_cls)
|
||||
|
||||
|
||||
mutable = define
|
||||
frozen = partial(define, frozen=True, on_setattr=None)
|
||||
|
||||
|
||||
def field(
|
||||
*,
|
||||
default=NOTHING,
|
||||
validator=None,
|
||||
repr=True,
|
||||
hash=None,
|
||||
init=True,
|
||||
metadata=None,
|
||||
converter=None,
|
||||
factory=None,
|
||||
kw_only=False,
|
||||
eq=None,
|
||||
order=None,
|
||||
on_setattr=None,
|
||||
):
|
||||
"""
|
||||
Identical to `attr.ib`, except keyword-only and with some arguments
|
||||
removed.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
return attrib(
|
||||
default=default,
|
||||
validator=validator,
|
||||
repr=repr,
|
||||
hash=hash,
|
||||
init=init,
|
||||
metadata=metadata,
|
||||
converter=converter,
|
||||
factory=factory,
|
||||
kw_only=kw_only,
|
||||
eq=eq,
|
||||
order=order,
|
||||
on_setattr=on_setattr,
|
||||
)
|
||||
Vendored
+8
-1
@@ -4,7 +4,14 @@ Commonly useful converters.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ._make import NOTHING, Factory
|
||||
from ._make import NOTHING, Factory, pipe
|
||||
|
||||
|
||||
__all__ = [
|
||||
"pipe",
|
||||
"optional",
|
||||
"default_if_none",
|
||||
]
|
||||
|
||||
|
||||
def optional(converter):
|
||||
|
||||
Vendored
+4
-5
@@ -3,10 +3,9 @@ from . import _ConverterType
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
def optional(
|
||||
converter: _ConverterType[_T]
|
||||
) -> _ConverterType[Optional[_T]]: ...
|
||||
def pipe(*validators: _ConverterType) -> _ConverterType: ...
|
||||
def optional(converter: _ConverterType) -> _ConverterType: ...
|
||||
@overload
|
||||
def default_if_none(default: _T) -> _ConverterType[_T]: ...
|
||||
def default_if_none(default: _T) -> _ConverterType: ...
|
||||
@overload
|
||||
def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType[_T]: ...
|
||||
def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ...
|
||||
|
||||
Vendored
+22
-4
@@ -1,20 +1,37 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
class FrozenInstanceError(AttributeError):
|
||||
class FrozenError(AttributeError):
|
||||
"""
|
||||
A frozen/immutable instance has been attempted to be modified.
|
||||
A frozen/immutable instance or attribute haave been attempted to be
|
||||
modified.
|
||||
|
||||
It mirrors the behavior of ``namedtuples`` by using the same error message
|
||||
and subclassing `AttributeError`.
|
||||
|
||||
.. versionadded:: 16.1.0
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
msg = "can't set attribute"
|
||||
args = [msg]
|
||||
|
||||
|
||||
class FrozenInstanceError(FrozenError):
|
||||
"""
|
||||
A frozen instance has been attempted to be modified.
|
||||
|
||||
.. versionadded:: 16.1.0
|
||||
"""
|
||||
|
||||
|
||||
class FrozenAttributeError(FrozenError):
|
||||
"""
|
||||
A frozen attribute has been attempted to be modified.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
|
||||
class AttrsAttributeNotFoundError(ValueError):
|
||||
"""
|
||||
An ``attrs`` function couldn't find an attribute that the user asked for.
|
||||
@@ -51,7 +68,8 @@ class UnannotatedAttributeError(RuntimeError):
|
||||
|
||||
class PythonTooOldError(RuntimeError):
|
||||
"""
|
||||
An ``attrs`` feature requiring a more recent python version has been used.
|
||||
It was attempted to use an ``attrs`` feature that requires a newer Python
|
||||
version.
|
||||
|
||||
.. versionadded:: 18.2.0
|
||||
"""
|
||||
|
||||
Vendored
+3
-1
@@ -1,8 +1,10 @@
|
||||
from typing import Any
|
||||
|
||||
class FrozenInstanceError(AttributeError):
|
||||
class FrozenError(AttributeError):
|
||||
msg: str = ...
|
||||
|
||||
class FrozenInstanceError(FrozenError): ...
|
||||
class FrozenAttributeError(FrozenError): ...
|
||||
class AttrsAttributeNotFoundError(ValueError): ...
|
||||
class NotAnAttrsClassError(ValueError): ...
|
||||
class DefaultAlreadySetError(RuntimeError): ...
|
||||
|
||||
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Commonly used hooks for on_setattr.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from . import _config
|
||||
from .exceptions import FrozenAttributeError
|
||||
|
||||
|
||||
def pipe(*setters):
|
||||
"""
|
||||
Run all *setters* and return the return value of the last one.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
def wrapped_pipe(instance, attrib, new_value):
|
||||
rv = new_value
|
||||
|
||||
for setter in setters:
|
||||
rv = setter(instance, attrib, rv)
|
||||
|
||||
return rv
|
||||
|
||||
return wrapped_pipe
|
||||
|
||||
|
||||
def frozen(_, __, ___):
|
||||
"""
|
||||
Prevent an attribute to be modified.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
raise FrozenAttributeError()
|
||||
|
||||
|
||||
def validate(instance, attrib, new_value):
|
||||
"""
|
||||
Run *attrib*'s validator on *new_value* if it has one.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
if _config._run_validators is False:
|
||||
return new_value
|
||||
|
||||
v = attrib.validator
|
||||
if not v:
|
||||
return new_value
|
||||
|
||||
v(instance, attrib, new_value)
|
||||
|
||||
return new_value
|
||||
|
||||
|
||||
def convert(instance, attrib, new_value):
|
||||
"""
|
||||
Run *attrib*'s converter -- if it has one -- on *new_value* and return the
|
||||
result.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
c = attrib.converter
|
||||
if c:
|
||||
return c(new_value)
|
||||
|
||||
return new_value
|
||||
|
||||
|
||||
NO_OP = object()
|
||||
"""
|
||||
Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.
|
||||
|
||||
Does not work in `pipe` or within lists.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
from . import _OnSetAttrType, Attribute
|
||||
from typing import TypeVar, Any, NewType, NoReturn, cast
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
def frozen(
|
||||
instance: Any, attribute: Attribute, new_value: Any
|
||||
) -> NoReturn: ...
|
||||
def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ...
|
||||
def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ...
|
||||
|
||||
# convert is allowed to return Any, because they can be chained using pipe.
|
||||
def convert(
|
||||
instance: Any, attribute: Attribute[Any], new_value: Any
|
||||
) -> Any: ...
|
||||
|
||||
_NoOpType = NewType("_NoOpType", object)
|
||||
NO_OP: _NoOpType
|
||||
Vendored
+3
-2
@@ -67,7 +67,7 @@ def instance_of(type):
|
||||
return _InstanceOfValidator(type)
|
||||
|
||||
|
||||
@attrs(repr=False, frozen=True)
|
||||
@attrs(repr=False, frozen=True, slots=True)
|
||||
class _MatchesReValidator(object):
|
||||
regex = attrib()
|
||||
flags = attrib()
|
||||
@@ -171,7 +171,8 @@ def provides(interface):
|
||||
performed using ``interface.providedBy(value)`` (see `zope.interface
|
||||
<https://zopeinterface.readthedocs.io/en/latest/>`_).
|
||||
|
||||
:param zope.interface.Interface interface: The interface to check for.
|
||||
:param interface: The interface to check for.
|
||||
:type interface: ``zope.interface.Interface``
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attr.Attribute`), the expected interface, and the
|
||||
|
||||
Vendored
+3
-2
@@ -2,9 +2,10 @@
|
||||
|
||||
__author__ = "Daniel Greenfeld"
|
||||
__email__ = "pydanny@gmail.com"
|
||||
__version__ = "1.5.1"
|
||||
__version__ = "1.5.2"
|
||||
__license__ = "BSD"
|
||||
|
||||
from functools import wraps
|
||||
from time import time
|
||||
import threading
|
||||
|
||||
@@ -36,7 +37,7 @@ class cached_property(object):
|
||||
return value
|
||||
|
||||
def _wrap_in_coroutine(self, obj):
|
||||
|
||||
@wraps(obj)
|
||||
@asyncio.coroutine
|
||||
def wrapper():
|
||||
future = asyncio.ensure_future(self.func(obj))
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
from .core import contents, where
|
||||
|
||||
__version__ = "2020.04.05.1"
|
||||
__version__ = "2020.11.08"
|
||||
|
||||
Vendored
+216
-251
@@ -58,38 +58,6 @@ AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
|
||||
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
|
||||
# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
|
||||
# Label: "Verisign Class 3 Public Primary Certification Authority - G3"
|
||||
# Serial: 206684696279472310254277870180966723415
|
||||
# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09
|
||||
# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6
|
||||
# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
|
||||
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
|
||||
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
|
||||
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
|
||||
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
|
||||
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
|
||||
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
|
||||
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
|
||||
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
|
||||
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
|
||||
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
|
||||
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
|
||||
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
|
||||
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
|
||||
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
|
||||
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
|
||||
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
|
||||
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
|
||||
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
|
||||
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
|
||||
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
|
||||
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
|
||||
# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
|
||||
# Label: "Entrust.net Premium 2048 Secure Server CA"
|
||||
@@ -152,39 +120,6 @@ ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
|
||||
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
|
||||
# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
|
||||
# Label: "AddTrust External Root"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f
|
||||
# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68
|
||||
# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
|
||||
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
|
||||
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
|
||||
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
|
||||
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
|
||||
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
|
||||
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
|
||||
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
|
||||
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
|
||||
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
|
||||
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
|
||||
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
|
||||
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
|
||||
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
|
||||
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
|
||||
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
|
||||
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
|
||||
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
|
||||
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
|
||||
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
|
||||
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
|
||||
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
|
||||
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
|
||||
# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
|
||||
# Label: "Entrust Root Certification Authority"
|
||||
@@ -640,46 +575,6 @@ VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
|
||||
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=Government Root Certification Authority
|
||||
# Subject: O=Government Root Certification Authority
|
||||
# Label: "Taiwan GRCA"
|
||||
# Serial: 42023070807708724159991140556527066870
|
||||
# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e
|
||||
# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9
|
||||
# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
|
||||
MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
|
||||
YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
|
||||
PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
|
||||
Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
|
||||
IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
|
||||
gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
|
||||
yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
|
||||
F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
|
||||
jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
|
||||
ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
|
||||
VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
|
||||
YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
|
||||
EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
|
||||
Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
|
||||
DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
|
||||
MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
|
||||
UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
|
||||
TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
|
||||
qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
|
||||
ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
|
||||
JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
|
||||
hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
|
||||
EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
|
||||
nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
|
||||
udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
|
||||
ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
|
||||
LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
|
||||
pYYsfPQS
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Assured ID Root CA"
|
||||
@@ -1127,38 +1022,6 @@ fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
|
||||
GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
|
||||
# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
|
||||
# Label: "OISTE WISeKey Global Root GA CA"
|
||||
# Serial: 86718877871133159090080555911823548314
|
||||
# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93
|
||||
# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9
|
||||
# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB
|
||||
ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly
|
||||
aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
|
||||
ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w
|
||||
NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G
|
||||
A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD
|
||||
VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX
|
||||
SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR
|
||||
VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2
|
||||
w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF
|
||||
mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg
|
||||
4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9
|
||||
4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw
|
||||
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw
|
||||
EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx
|
||||
SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2
|
||||
ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8
|
||||
vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
|
||||
hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi
|
||||
Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ
|
||||
/L7fCg0=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Certigna O=Dhimyotis
|
||||
# Subject: CN=Certigna O=Dhimyotis
|
||||
# Label: "Certigna"
|
||||
@@ -1499,47 +1362,6 @@ uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
|
||||
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden
|
||||
# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden
|
||||
# Label: "Staat der Nederlanden Root CA - G2"
|
||||
# Serial: 10000012
|
||||
# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a
|
||||
# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16
|
||||
# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
|
||||
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
|
||||
dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX
|
||||
DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
|
||||
ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
|
||||
b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291
|
||||
qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp
|
||||
uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU
|
||||
Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE
|
||||
pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp
|
||||
5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M
|
||||
UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN
|
||||
GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy
|
||||
5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv
|
||||
6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK
|
||||
eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6
|
||||
B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/
|
||||
BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov
|
||||
L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
|
||||
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG
|
||||
SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS
|
||||
CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen
|
||||
5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897
|
||||
IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK
|
||||
gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL
|
||||
+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL
|
||||
vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm
|
||||
bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk
|
||||
N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC
|
||||
Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z
|
||||
ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post
|
||||
# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post
|
||||
# Label: "Hongkong Post Root CA 1"
|
||||
@@ -2391,38 +2213,6 @@ e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
|
||||
TpPDpFQUWw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus
|
||||
# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus
|
||||
# Label: "EE Certification Centre Root CA"
|
||||
# Serial: 112324828676200291871926431888494945866
|
||||
# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f
|
||||
# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7
|
||||
# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
|
||||
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
|
||||
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
|
||||
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
|
||||
MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
|
||||
ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
|
||||
b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
|
||||
euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
|
||||
bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
|
||||
WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
|
||||
MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
|
||||
1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
|
||||
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
|
||||
zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
|
||||
BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
|
||||
v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
|
||||
E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
|
||||
uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
|
||||
iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
|
||||
GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
|
||||
# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
|
||||
# Label: "D-TRUST Root Class 3 CA 2 2009"
|
||||
@@ -3788,47 +3578,6 @@ CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
|
||||
1KyLa2tJElMzrdfkviT8tQp21KW8EA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=LuxTrust Global Root 2 O=LuxTrust S.A.
|
||||
# Subject: CN=LuxTrust Global Root 2 O=LuxTrust S.A.
|
||||
# Label: "LuxTrust Global Root 2"
|
||||
# Serial: 59914338225734147123941058376788110305822489521
|
||||
# MD5 Fingerprint: b2:e1:09:00:61:af:f7:f1:91:6f:c4:ad:8d:5e:3b:7c
|
||||
# SHA1 Fingerprint: 1e:0e:56:19:0a:d1:8b:25:98:b2:04:44:ff:66:8a:04:17:99:5f:3f
|
||||
# SHA256 Fingerprint: 54:45:5f:71:29:c2:0b:14:47:c4:18:f9:97:16:8f:24:c5:8f:c5:02:3b:f5:da:5b:e2:eb:6e:1d:d8:90:2e:d5
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL
|
||||
BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV
|
||||
BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw
|
||||
MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B
|
||||
LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN
|
||||
AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F
|
||||
ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem
|
||||
hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1
|
||||
EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn
|
||||
Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4
|
||||
zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ
|
||||
96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m
|
||||
j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g
|
||||
DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+
|
||||
8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j
|
||||
X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH
|
||||
hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB
|
||||
KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0
|
||||
Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT
|
||||
+Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL
|
||||
BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9
|
||||
BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO
|
||||
jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9
|
||||
loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c
|
||||
qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+
|
||||
2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/
|
||||
JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre
|
||||
zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf
|
||||
LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+
|
||||
x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6
|
||||
oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM
|
||||
# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM
|
||||
# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1"
|
||||
@@ -4639,3 +4388,219 @@ IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk
|
||||
5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY
|
||||
n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation
|
||||
# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation
|
||||
# Label: "Microsoft ECC Root Certificate Authority 2017"
|
||||
# Serial: 136839042543790627607696632466672567020
|
||||
# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67
|
||||
# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5
|
||||
# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw
|
||||
CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD
|
||||
VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
|
||||
MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV
|
||||
UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy
|
||||
b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq
|
||||
hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR
|
||||
ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb
|
||||
hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E
|
||||
BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3
|
||||
FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV
|
||||
L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB
|
||||
iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation
|
||||
# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation
|
||||
# Label: "Microsoft RSA Root Certificate Authority 2017"
|
||||
# Serial: 40975477897264996090493496164228220339
|
||||
# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47
|
||||
# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74
|
||||
# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl
|
||||
MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw
|
||||
NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
|
||||
IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG
|
||||
EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N
|
||||
aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi
|
||||
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ
|
||||
Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0
|
||||
ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1
|
||||
HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm
|
||||
gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ
|
||||
jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc
|
||||
aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG
|
||||
YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6
|
||||
W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K
|
||||
UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH
|
||||
+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q
|
||||
W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/
|
||||
BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC
|
||||
NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC
|
||||
LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC
|
||||
gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6
|
||||
tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh
|
||||
SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2
|
||||
TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3
|
||||
pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR
|
||||
xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp
|
||||
GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9
|
||||
dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN
|
||||
AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB
|
||||
RA+GsCyRxj3qrg+E
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd.
|
||||
# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd.
|
||||
# Label: "e-Szigno Root CA 2017"
|
||||
# Serial: 411379200276854331539784714
|
||||
# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98
|
||||
# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1
|
||||
# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV
|
||||
BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk
|
||||
LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv
|
||||
b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ
|
||||
BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg
|
||||
THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v
|
||||
IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv
|
||||
xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H
|
||||
Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
|
||||
A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB
|
||||
eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo
|
||||
jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ
|
||||
+efcMQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2
|
||||
# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2
|
||||
# Label: "certSIGN Root CA G2"
|
||||
# Serial: 313609486401300475190
|
||||
# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7
|
||||
# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32
|
||||
# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
|
||||
BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g
|
||||
Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ
|
||||
BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ
|
||||
R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF
|
||||
dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw
|
||||
vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ
|
||||
uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp
|
||||
n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs
|
||||
cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW
|
||||
xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P
|
||||
rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF
|
||||
DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx
|
||||
DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy
|
||||
LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C
|
||||
eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB
|
||||
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ
|
||||
d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq
|
||||
kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC
|
||||
b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl
|
||||
qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0
|
||||
OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c
|
||||
NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk
|
||||
ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO
|
||||
pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj
|
||||
03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk
|
||||
PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE
|
||||
1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX
|
||||
QRBdJ3NghVdJIgc=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc.
|
||||
# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc.
|
||||
# Label: "Trustwave Global Certification Authority"
|
||||
# Serial: 1846098327275375458322922162
|
||||
# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e
|
||||
# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5
|
||||
# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw
|
||||
CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x
|
||||
ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1
|
||||
c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx
|
||||
OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI
|
||||
SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI
|
||||
b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp
|
||||
Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn
|
||||
swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu
|
||||
7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8
|
||||
1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW
|
||||
80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP
|
||||
JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l
|
||||
RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw
|
||||
hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10
|
||||
coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc
|
||||
BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n
|
||||
twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud
|
||||
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud
|
||||
DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W
|
||||
0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe
|
||||
uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q
|
||||
lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB
|
||||
aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE
|
||||
sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT
|
||||
MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe
|
||||
qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh
|
||||
VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8
|
||||
h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9
|
||||
EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK
|
||||
yeC2nOnOcXHebD8WpHk=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc.
|
||||
# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc.
|
||||
# Label: "Trustwave Global ECC P256 Certification Authority"
|
||||
# Serial: 4151900041497450638097112925
|
||||
# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54
|
||||
# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf
|
||||
# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf
|
||||
BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3
|
||||
YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x
|
||||
NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G
|
||||
A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0
|
||||
d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF
|
||||
Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG
|
||||
SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN
|
||||
FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w
|
||||
DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw
|
||||
CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh
|
||||
DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc.
|
||||
# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc.
|
||||
# Label: "Trustwave Global ECC P384 Certification Authority"
|
||||
# Serial: 2704997926503831671788816187
|
||||
# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6
|
||||
# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2
|
||||
# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf
|
||||
BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3
|
||||
YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x
|
||||
NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G
|
||||
A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0
|
||||
d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF
|
||||
Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB
|
||||
BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ
|
||||
j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF
|
||||
1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G
|
||||
A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3
|
||||
AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC
|
||||
MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu
|
||||
Sw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Vendored
+35
-5
@@ -9,7 +9,36 @@ This module returns the installation location of cacert.pem or its contents.
|
||||
import os
|
||||
|
||||
try:
|
||||
from importlib.resources import read_text
|
||||
from importlib.resources import path as get_path, read_text
|
||||
|
||||
_CACERT_CTX = None
|
||||
_CACERT_PATH = None
|
||||
|
||||
def where():
|
||||
# This is slightly terrible, but we want to delay extracting the file
|
||||
# in cases where we're inside of a zipimport situation until someone
|
||||
# actually calls where(), but we don't want to re-extract the file
|
||||
# on every call of where(), so we'll do it once then store it in a
|
||||
# global variable.
|
||||
global _CACERT_CTX
|
||||
global _CACERT_PATH
|
||||
if _CACERT_PATH is None:
|
||||
# This is slightly janky, the importlib.resources API wants you to
|
||||
# manage the cleanup of this file, so it doesn't actually return a
|
||||
# path, it returns a context manager that will give you the path
|
||||
# when you enter it and will do any cleanup when you leave it. In
|
||||
# the common case of not needing a temporary file, it will just
|
||||
# return the file system location and the __exit__() is a no-op.
|
||||
#
|
||||
# We also have to hold onto the actual context manager, because
|
||||
# it will do the cleanup whenever it gets garbage collected, so
|
||||
# we will also store that at the global level as well.
|
||||
_CACERT_CTX = get_path("certifi", "cacert.pem")
|
||||
_CACERT_PATH = str(_CACERT_CTX.__enter__())
|
||||
|
||||
return _CACERT_PATH
|
||||
|
||||
|
||||
except ImportError:
|
||||
# This fallback will work for Python versions prior to 3.7 that lack the
|
||||
# importlib.resources module but relies on the existing `where` function
|
||||
@@ -19,11 +48,12 @@ except ImportError:
|
||||
with open(where(), "r", encoding=encoding) as data:
|
||||
return data.read()
|
||||
|
||||
# If we don't have importlib.resources, then we will just do the old logic
|
||||
# of assuming we're on the filesystem and munge the path directly.
|
||||
def where():
|
||||
f = os.path.dirname(__file__)
|
||||
|
||||
def where():
|
||||
f = os.path.dirname(__file__)
|
||||
|
||||
return os.path.join(f, "cacert.pem")
|
||||
return os.path.join(f, "cacert.pem")
|
||||
|
||||
|
||||
def contents():
|
||||
|
||||
Vendored
+1
-1
@@ -3,4 +3,4 @@ from .initialise import init, deinit, reinit, colorama_text
|
||||
from .ansi import Fore, Back, Style, Cursor
|
||||
from .ansitowin32 import AnsiToWin32
|
||||
|
||||
__version__ = '0.4.3'
|
||||
__version__ = '0.4.4'
|
||||
|
||||
Vendored
+1
-1
@@ -6,7 +6,7 @@ See: http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
|
||||
CSI = '\033['
|
||||
OSC = '\033]'
|
||||
BEL = '\007'
|
||||
BEL = '\a'
|
||||
|
||||
|
||||
def code_to_chars(code):
|
||||
|
||||
+10
-9
@@ -3,7 +3,7 @@ import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
|
||||
from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL
|
||||
from .winterm import WinTerm, WinColor, WinStyle
|
||||
from .win32 import windll, winapi_test
|
||||
|
||||
@@ -68,7 +68,7 @@ class AnsiToWin32(object):
|
||||
win32 function calls.
|
||||
'''
|
||||
ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer
|
||||
ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command
|
||||
ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command
|
||||
|
||||
def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
|
||||
# The wrapped stream (normally sys.stdout or sys.stderr)
|
||||
@@ -247,11 +247,12 @@ class AnsiToWin32(object):
|
||||
start, end = match.span()
|
||||
text = text[:start] + text[end:]
|
||||
paramstring, command = match.groups()
|
||||
if command in '\x07': # \x07 = BEL
|
||||
params = paramstring.split(";")
|
||||
# 0 - change title and icon (we will only change title)
|
||||
# 1 - change icon (we don't support this)
|
||||
# 2 - change title
|
||||
if params[0] in '02':
|
||||
winterm.set_title(params[1])
|
||||
if command == BEL:
|
||||
if paramstring.count(";") == 1:
|
||||
params = paramstring.split(";")
|
||||
# 0 - change title and icon (we will only change title)
|
||||
# 1 - change icon (we don't support this)
|
||||
# 2 - change title
|
||||
if params[0] in '02':
|
||||
winterm.set_title(params[1])
|
||||
return text
|
||||
|
||||
Vendored
+1
-1
@@ -6,7 +6,7 @@
|
||||
#
|
||||
import logging
|
||||
|
||||
__version__ = '0.3.0'
|
||||
__version__ = '0.3.1'
|
||||
|
||||
class DistlibException(Exception):
|
||||
pass
|
||||
|
||||
+6
-3
@@ -14,7 +14,10 @@ import sys
|
||||
import stat
|
||||
from os.path import abspath
|
||||
import fnmatch
|
||||
import collections
|
||||
try:
|
||||
from collections.abc import Callable
|
||||
except ImportError:
|
||||
from collections import Callable
|
||||
import errno
|
||||
from . import tarfile
|
||||
|
||||
@@ -528,7 +531,7 @@ def register_archive_format(name, function, extra_args=None, description=''):
|
||||
"""
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
if not isinstance(function, collections.Callable):
|
||||
if not isinstance(function, Callable):
|
||||
raise TypeError('The %s object is not callable' % function)
|
||||
if not isinstance(extra_args, (tuple, list)):
|
||||
raise TypeError('extra_args needs to be a sequence')
|
||||
@@ -621,7 +624,7 @@ def _check_unpack_options(extensions, function, extra_args):
|
||||
raise RegistryError(msg % (extension,
|
||||
existing_extensions[extension]))
|
||||
|
||||
if not isinstance(function, collections.Callable):
|
||||
if not isinstance(function, Callable):
|
||||
raise TypeError('The registered function must be a callable')
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -319,7 +319,7 @@ except ImportError: # pragma: no cover
|
||||
try:
|
||||
callable = callable
|
||||
except NameError: # pragma: no cover
|
||||
from collections import Callable
|
||||
from collections.abc import Callable
|
||||
|
||||
def callable(obj):
|
||||
return isinstance(obj, Callable)
|
||||
|
||||
Vendored
+1
-1
@@ -550,7 +550,7 @@ class InstalledDistribution(BaseInstalledDistribution):
|
||||
r = finder.find(WHEEL_METADATA_FILENAME)
|
||||
# Temporary - for legacy support
|
||||
if r is None:
|
||||
r = finder.find('METADATA')
|
||||
r = finder.find(LEGACY_METADATA_FILENAME)
|
||||
if r is None:
|
||||
raise ValueError('no %s found in %s' % (METADATA_FILENAME,
|
||||
path))
|
||||
|
||||
Vendored
+41
-81
@@ -5,7 +5,7 @@
|
||||
#
|
||||
"""Implementation of the Metadata for Python packages PEPs.
|
||||
|
||||
Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental).
|
||||
Supports all metadata formats (1.0, 1.1, 1.2, 1.3/2.1 and withdrawn 2.0).
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
@@ -194,38 +194,12 @@ def _best_version(fields):
|
||||
|
||||
return '2.0'
|
||||
|
||||
# This follows the rules about transforming keys as described in
|
||||
# https://www.python.org/dev/peps/pep-0566/#id17
|
||||
_ATTR2FIELD = {
|
||||
'metadata_version': 'Metadata-Version',
|
||||
'name': 'Name',
|
||||
'version': 'Version',
|
||||
'platform': 'Platform',
|
||||
'supported_platform': 'Supported-Platform',
|
||||
'summary': 'Summary',
|
||||
'description': 'Description',
|
||||
'keywords': 'Keywords',
|
||||
'home_page': 'Home-page',
|
||||
'author': 'Author',
|
||||
'author_email': 'Author-email',
|
||||
'maintainer': 'Maintainer',
|
||||
'maintainer_email': 'Maintainer-email',
|
||||
'license': 'License',
|
||||
'classifier': 'Classifier',
|
||||
'download_url': 'Download-URL',
|
||||
'obsoletes_dist': 'Obsoletes-Dist',
|
||||
'provides_dist': 'Provides-Dist',
|
||||
'requires_dist': 'Requires-Dist',
|
||||
'setup_requires_dist': 'Setup-Requires-Dist',
|
||||
'requires_python': 'Requires-Python',
|
||||
'requires_external': 'Requires-External',
|
||||
'requires': 'Requires',
|
||||
'provides': 'Provides',
|
||||
'obsoletes': 'Obsoletes',
|
||||
'project_url': 'Project-URL',
|
||||
'private_version': 'Private-Version',
|
||||
'obsoleted_by': 'Obsoleted-By',
|
||||
'extension': 'Extension',
|
||||
'provides_extra': 'Provides-Extra',
|
||||
name.lower().replace("-", "_"): name for name in _ALL_FIELDS
|
||||
}
|
||||
_FIELD2ATTR = {field: attr for attr, field in _ATTR2FIELD.items()}
|
||||
|
||||
_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist')
|
||||
_VERSIONS_FIELDS = ('Requires-Python',)
|
||||
@@ -262,7 +236,7 @@ def _get_name_and_version(name, version, for_filename=False):
|
||||
class LegacyMetadata(object):
|
||||
"""The legacy metadata of a release.
|
||||
|
||||
Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can
|
||||
Supports versions 1.0, 1.1, 1.2, 2.0 and 1.3/2.1 (auto-detected). You can
|
||||
instantiate the class with one of these arguments (or none):
|
||||
- *path*, the path to a metadata file
|
||||
- *fileobj* give a file-like object with metadata as content
|
||||
@@ -381,6 +355,11 @@ class LegacyMetadata(object):
|
||||
value = msg[field]
|
||||
if value is not None and value != 'UNKNOWN':
|
||||
self.set(field, value)
|
||||
|
||||
# PEP 566 specifies that the body be used for the description, if
|
||||
# available
|
||||
body = msg.get_payload()
|
||||
self["Description"] = body if body else self["Description"]
|
||||
# logger.debug('Attempting to set metadata for %s', self)
|
||||
# self.set_metadata_version()
|
||||
|
||||
@@ -567,57 +546,21 @@ class LegacyMetadata(object):
|
||||
|
||||
Field names will be converted to use the underscore-lowercase style
|
||||
instead of hyphen-mixed case (i.e. home_page instead of Home-page).
|
||||
This is as per https://www.python.org/dev/peps/pep-0566/#id17.
|
||||
"""
|
||||
self.set_metadata_version()
|
||||
|
||||
mapping_1_0 = (
|
||||
('metadata_version', 'Metadata-Version'),
|
||||
('name', 'Name'),
|
||||
('version', 'Version'),
|
||||
('summary', 'Summary'),
|
||||
('home_page', 'Home-page'),
|
||||
('author', 'Author'),
|
||||
('author_email', 'Author-email'),
|
||||
('license', 'License'),
|
||||
('description', 'Description'),
|
||||
('keywords', 'Keywords'),
|
||||
('platform', 'Platform'),
|
||||
('classifiers', 'Classifier'),
|
||||
('download_url', 'Download-URL'),
|
||||
)
|
||||
fields = _version2fieldlist(self['Metadata-Version'])
|
||||
|
||||
data = {}
|
||||
for key, field_name in mapping_1_0:
|
||||
|
||||
for field_name in fields:
|
||||
if not skip_missing or field_name in self._fields:
|
||||
data[key] = self[field_name]
|
||||
|
||||
if self['Metadata-Version'] == '1.2':
|
||||
mapping_1_2 = (
|
||||
('requires_dist', 'Requires-Dist'),
|
||||
('requires_python', 'Requires-Python'),
|
||||
('requires_external', 'Requires-External'),
|
||||
('provides_dist', 'Provides-Dist'),
|
||||
('obsoletes_dist', 'Obsoletes-Dist'),
|
||||
('project_url', 'Project-URL'),
|
||||
('maintainer', 'Maintainer'),
|
||||
('maintainer_email', 'Maintainer-email'),
|
||||
)
|
||||
for key, field_name in mapping_1_2:
|
||||
if not skip_missing or field_name in self._fields:
|
||||
if key != 'project_url':
|
||||
data[key] = self[field_name]
|
||||
else:
|
||||
data[key] = [','.join(u) for u in self[field_name]]
|
||||
|
||||
elif self['Metadata-Version'] == '1.1':
|
||||
mapping_1_1 = (
|
||||
('provides', 'Provides'),
|
||||
('requires', 'Requires'),
|
||||
('obsoletes', 'Obsoletes'),
|
||||
)
|
||||
for key, field_name in mapping_1_1:
|
||||
if not skip_missing or field_name in self._fields:
|
||||
key = _FIELD2ATTR[field_name]
|
||||
if key != 'project_url':
|
||||
data[key] = self[field_name]
|
||||
else:
|
||||
data[key] = [','.join(u) for u in self[field_name]]
|
||||
|
||||
return data
|
||||
|
||||
@@ -1003,10 +946,14 @@ class Metadata(object):
|
||||
LEGACY_MAPPING = {
|
||||
'name': 'Name',
|
||||
'version': 'Version',
|
||||
'license': 'License',
|
||||
('extensions', 'python.details', 'license'): 'License',
|
||||
'summary': 'Summary',
|
||||
'description': 'Description',
|
||||
'classifiers': 'Classifier',
|
||||
('extensions', 'python.project', 'project_urls', 'Home'): 'Home-page',
|
||||
('extensions', 'python.project', 'contacts', 0, 'name'): 'Author',
|
||||
('extensions', 'python.project', 'contacts', 0, 'email'): 'Author-email',
|
||||
'source_url': 'Download-URL',
|
||||
('extensions', 'python.details', 'classifiers'): 'Classifier',
|
||||
}
|
||||
|
||||
def _to_legacy(self):
|
||||
@@ -1034,16 +981,29 @@ class Metadata(object):
|
||||
assert self._data and not self._legacy
|
||||
result = LegacyMetadata()
|
||||
nmd = self._data
|
||||
# import pdb; pdb.set_trace()
|
||||
for nk, ok in self.LEGACY_MAPPING.items():
|
||||
if nk in nmd:
|
||||
result[ok] = nmd[nk]
|
||||
if not isinstance(nk, tuple):
|
||||
if nk in nmd:
|
||||
result[ok] = nmd[nk]
|
||||
else:
|
||||
d = nmd
|
||||
found = True
|
||||
for k in nk:
|
||||
try:
|
||||
d = d[k]
|
||||
except (KeyError, IndexError):
|
||||
found = False
|
||||
break
|
||||
if found:
|
||||
result[ok] = d
|
||||
r1 = process_entries(self.run_requires + self.meta_requires)
|
||||
r2 = process_entries(self.build_requires + self.dev_requires)
|
||||
if self.extras:
|
||||
result['Provides-Extra'] = sorted(self.extras)
|
||||
result['Requires-Dist'] = sorted(r1)
|
||||
result['Setup-Requires-Dist'] = sorted(r2)
|
||||
# TODO: other fields such as contacts
|
||||
# TODO: any other fields wanted
|
||||
return result
|
||||
|
||||
def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True):
|
||||
|
||||
Vendored
+8
-5
@@ -48,7 +48,7 @@ if __name__ == '__main__':
|
||||
'''
|
||||
|
||||
|
||||
def _enquote_executable(executable):
|
||||
def enquote_executable(executable):
|
||||
if ' ' in executable:
|
||||
# make sure we quote only the executable in case of env
|
||||
# for example /usr/bin/env "/dir with spaces/bin/jython"
|
||||
@@ -63,6 +63,8 @@ def _enquote_executable(executable):
|
||||
executable = '"%s"' % executable
|
||||
return executable
|
||||
|
||||
# Keep the old name around (for now), as there is at least one project using it!
|
||||
_enquote_executable = enquote_executable
|
||||
|
||||
class ScriptMaker(object):
|
||||
"""
|
||||
@@ -88,6 +90,7 @@ class ScriptMaker(object):
|
||||
|
||||
self._is_nt = os.name == 'nt' or (
|
||||
os.name == 'java' and os._name == 'nt')
|
||||
self.version_info = sys.version_info
|
||||
|
||||
def _get_alternate_executable(self, executable, options):
|
||||
if options.get('gui', False) and self._is_nt: # pragma: no cover
|
||||
@@ -185,7 +188,7 @@ class ScriptMaker(object):
|
||||
# If the user didn't specify an executable, it may be necessary to
|
||||
# cater for executable paths with spaces (not uncommon on Windows)
|
||||
if enquote:
|
||||
executable = _enquote_executable(executable)
|
||||
executable = enquote_executable(executable)
|
||||
# Issue #51: don't use fsencode, since we later try to
|
||||
# check that the shebang is decodable using utf-8.
|
||||
executable = executable.encode('utf-8')
|
||||
@@ -293,10 +296,10 @@ class ScriptMaker(object):
|
||||
if '' in self.variants:
|
||||
scriptnames.add(name)
|
||||
if 'X' in self.variants:
|
||||
scriptnames.add('%s%s' % (name, sys.version_info[0]))
|
||||
scriptnames.add('%s%s' % (name, self.version_info[0]))
|
||||
if 'X.Y' in self.variants:
|
||||
scriptnames.add('%s-%s.%s' % (name, sys.version_info[0],
|
||||
sys.version_info[1]))
|
||||
scriptnames.add('%s-%s.%s' % (name, self.version_info[0],
|
||||
self.version_info[1]))
|
||||
if options and options.get('gui', False):
|
||||
ext = 'pyw'
|
||||
else:
|
||||
|
||||
Vendored
+26
-12
@@ -26,7 +26,8 @@ import zipfile
|
||||
from . import __version__, DistlibException
|
||||
from .compat import sysconfig, ZipFile, fsdecode, text_type, filter
|
||||
from .database import InstalledDistribution
|
||||
from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME
|
||||
from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME,
|
||||
LEGACY_METADATA_FILENAME)
|
||||
from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache,
|
||||
cached_property, get_cache_base, read_exports, tempdir)
|
||||
from .version import NormalizedVersion, UnsupportedVersionError
|
||||
@@ -221,10 +222,12 @@ class Wheel(object):
|
||||
wheel_metadata = self.get_wheel_metadata(zf)
|
||||
wv = wheel_metadata['Wheel-Version'].split('.', 1)
|
||||
file_version = tuple([int(i) for i in wv])
|
||||
if file_version < (1, 1):
|
||||
fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, 'METADATA']
|
||||
else:
|
||||
fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME]
|
||||
# if file_version < (1, 1):
|
||||
# fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME,
|
||||
# LEGACY_METADATA_FILENAME]
|
||||
# else:
|
||||
# fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME]
|
||||
fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME]
|
||||
result = None
|
||||
for fn in fns:
|
||||
try:
|
||||
@@ -299,10 +302,9 @@ class Wheel(object):
|
||||
return hash_kind, result
|
||||
|
||||
def write_record(self, records, record_path, base):
|
||||
records = list(records) # make a copy for sorting
|
||||
records = list(records) # make a copy, as mutated
|
||||
p = to_posix(os.path.relpath(record_path, base))
|
||||
records.append((p, '', ''))
|
||||
records.sort()
|
||||
with CSVWriter(record_path) as writer:
|
||||
for row in records:
|
||||
writer.writerow(row)
|
||||
@@ -425,6 +427,18 @@ class Wheel(object):
|
||||
ap = to_posix(os.path.join(info_dir, 'WHEEL'))
|
||||
archive_paths.append((ap, p))
|
||||
|
||||
# sort the entries by archive path. Not needed by any spec, but it
|
||||
# keeps the archive listing and RECORD tidier than they would otherwise
|
||||
# be. Use the number of path segments to keep directory entries together,
|
||||
# and keep the dist-info stuff at the end.
|
||||
def sorter(t):
|
||||
ap = t[0]
|
||||
n = ap.count('/')
|
||||
if '.dist-info' in ap:
|
||||
n += 10000
|
||||
return (n, ap)
|
||||
archive_paths = sorted(archive_paths, key=sorter)
|
||||
|
||||
# Now, at last, RECORD.
|
||||
# Paths in here are archive paths - nothing else makes sense.
|
||||
self.write_records((distinfo, info_dir), libdir, archive_paths)
|
||||
@@ -476,7 +490,7 @@ class Wheel(object):
|
||||
data_dir = '%s.data' % name_ver
|
||||
info_dir = '%s.dist-info' % name_ver
|
||||
|
||||
metadata_name = posixpath.join(info_dir, METADATA_FILENAME)
|
||||
metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME)
|
||||
wheel_metadata_name = posixpath.join(info_dir, 'WHEEL')
|
||||
record_name = posixpath.join(info_dir, 'RECORD')
|
||||
|
||||
@@ -619,7 +633,7 @@ class Wheel(object):
|
||||
for v in epdata[k].values():
|
||||
s = '%s:%s' % (v.prefix, v.suffix)
|
||||
if v.flags:
|
||||
s += ' %s' % v.flags
|
||||
s += ' [%s]' % ','.join(v.flags)
|
||||
d[v.name] = s
|
||||
except Exception:
|
||||
logger.warning('Unable to read legacy script '
|
||||
@@ -773,7 +787,7 @@ class Wheel(object):
|
||||
data_dir = '%s.data' % name_ver
|
||||
info_dir = '%s.dist-info' % name_ver
|
||||
|
||||
metadata_name = posixpath.join(info_dir, METADATA_FILENAME)
|
||||
metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME)
|
||||
wheel_metadata_name = posixpath.join(info_dir, 'WHEEL')
|
||||
record_name = posixpath.join(info_dir, 'RECORD')
|
||||
|
||||
@@ -842,7 +856,7 @@ class Wheel(object):
|
||||
|
||||
def get_version(path_map, info_dir):
|
||||
version = path = None
|
||||
key = '%s/%s' % (info_dir, METADATA_FILENAME)
|
||||
key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME)
|
||||
if key not in path_map:
|
||||
key = '%s/PKG-INFO' % info_dir
|
||||
if key in path_map:
|
||||
@@ -868,7 +882,7 @@ class Wheel(object):
|
||||
if updated:
|
||||
md = Metadata(path=path)
|
||||
md.version = updated
|
||||
legacy = not path.endswith(METADATA_FILENAME)
|
||||
legacy = path.endswith(LEGACY_METADATA_FILENAME)
|
||||
md.write(path=path, legacy=legacy)
|
||||
logger.debug('Version updated from %r to %r', version,
|
||||
updated)
|
||||
|
||||
Vendored
+29
-8
@@ -9,7 +9,7 @@ except ImportError:
|
||||
'Run pip install "python-dotenv[cli]" to fix this.')
|
||||
sys.exit(1)
|
||||
|
||||
from .compat import IS_TYPE_CHECKING
|
||||
from .compat import IS_TYPE_CHECKING, to_env
|
||||
from .main import dotenv_values, get_key, set_key, unset_key
|
||||
from .version import __version__
|
||||
|
||||
@@ -19,19 +19,23 @@ if IS_TYPE_CHECKING:
|
||||
|
||||
@click.group()
|
||||
@click.option('-f', '--file', default=os.path.join(os.getcwd(), '.env'),
|
||||
type=click.Path(exists=True),
|
||||
type=click.Path(file_okay=True),
|
||||
help="Location of the .env file, defaults to .env file in current working directory.")
|
||||
@click.option('-q', '--quote', default='always',
|
||||
type=click.Choice(['always', 'never', 'auto']),
|
||||
help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.")
|
||||
@click.option('-e', '--export', default=False,
|
||||
type=click.BOOL,
|
||||
help="Whether to write the dot file as an executable bash script.")
|
||||
@click.version_option(version=__version__)
|
||||
@click.pass_context
|
||||
def cli(ctx, file, quote):
|
||||
# type: (click.Context, Any, Any) -> None
|
||||
def cli(ctx, file, quote, export):
|
||||
# type: (click.Context, Any, Any, Any) -> None
|
||||
'''This script is used to set, get or unset values from a .env file.'''
|
||||
ctx.obj = {}
|
||||
ctx.obj['FILE'] = file
|
||||
ctx.obj['QUOTE'] = quote
|
||||
ctx.obj['EXPORT'] = export
|
||||
ctx.obj['FILE'] = file
|
||||
|
||||
|
||||
@cli.command()
|
||||
@@ -40,6 +44,11 @@ def list(ctx):
|
||||
# type: (click.Context) -> None
|
||||
'''Display all the stored key/value.'''
|
||||
file = ctx.obj['FILE']
|
||||
if not os.path.isfile(file):
|
||||
raise click.BadParameter(
|
||||
'Path "%s" does not exist.' % (file),
|
||||
ctx=ctx
|
||||
)
|
||||
dotenv_as_dict = dotenv_values(file)
|
||||
for k, v in dotenv_as_dict.items():
|
||||
click.echo('%s=%s' % (k, v))
|
||||
@@ -54,7 +63,8 @@ def set(ctx, key, value):
|
||||
'''Store the given key/value.'''
|
||||
file = ctx.obj['FILE']
|
||||
quote = ctx.obj['QUOTE']
|
||||
success, key, value = set_key(file, key, value, quote)
|
||||
export = ctx.obj['EXPORT']
|
||||
success, key, value = set_key(file, key, value, quote, export)
|
||||
if success:
|
||||
click.echo('%s=%s' % (key, value))
|
||||
else:
|
||||
@@ -68,6 +78,11 @@ def get(ctx, key):
|
||||
# type: (click.Context, Any) -> None
|
||||
'''Retrieve the value for the given key.'''
|
||||
file = ctx.obj['FILE']
|
||||
if not os.path.isfile(file):
|
||||
raise click.BadParameter(
|
||||
'Path "%s" does not exist.' % (file),
|
||||
ctx=ctx
|
||||
)
|
||||
stored_value = get_key(file, key)
|
||||
if stored_value:
|
||||
click.echo('%s=%s' % (key, stored_value))
|
||||
@@ -97,11 +112,17 @@ def run(ctx, commandline):
|
||||
# type: (click.Context, List[str]) -> None
|
||||
"""Run command with environment variables present."""
|
||||
file = ctx.obj['FILE']
|
||||
dotenv_as_dict = dotenv_values(file)
|
||||
if not os.path.isfile(file):
|
||||
raise click.BadParameter(
|
||||
'Invalid value for \'-f\' "%s" does not exist.' % (file),
|
||||
ctx=ctx
|
||||
)
|
||||
dotenv_as_dict = {to_env(k): to_env(v) for (k, v) in dotenv_values(file).items() if v is not None}
|
||||
|
||||
if not commandline:
|
||||
click.echo('No command given.')
|
||||
exit(1)
|
||||
ret = run_command(commandline, dotenv_as_dict) # type: ignore
|
||||
ret = run_command(commandline, dotenv_as_dict)
|
||||
exit(ret)
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -12,7 +12,7 @@ def is_type_checking():
|
||||
# type: () -> bool
|
||||
try:
|
||||
from typing import TYPE_CHECKING
|
||||
except ImportError: # pragma: no cover
|
||||
except ImportError:
|
||||
return False
|
||||
return TYPE_CHECKING
|
||||
|
||||
|
||||
Vendored
+85
-52
@@ -2,21 +2,23 @@
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
from contextlib import contextmanager
|
||||
|
||||
from .compat import StringIO, PY2, to_env, IS_TYPE_CHECKING
|
||||
from .parser import parse_stream
|
||||
from .compat import IS_TYPE_CHECKING, PY2, StringIO, to_env
|
||||
from .parser import Binding, parse_stream
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if IS_TYPE_CHECKING:
|
||||
from typing import (
|
||||
Dict, Iterator, Match, Optional, Pattern, Union, Text, IO, Tuple
|
||||
Dict, Iterable, Iterator, Match, Optional, Pattern, Union, Text, IO, Tuple
|
||||
)
|
||||
if sys.version_info >= (3, 6):
|
||||
_PathLike = os.PathLike
|
||||
@@ -28,17 +30,39 @@ if IS_TYPE_CHECKING:
|
||||
else:
|
||||
_StringIO = StringIO[Text]
|
||||
|
||||
__posix_variable = re.compile(r'\$\{[^\}]*\}') # type: Pattern[Text]
|
||||
__posix_variable = re.compile(
|
||||
r"""
|
||||
\$\{
|
||||
(?P<name>[^\}:]*)
|
||||
(?::-
|
||||
(?P<default>[^\}]*)
|
||||
)?
|
||||
\}
|
||||
""",
|
||||
re.VERBOSE,
|
||||
) # type: Pattern[Text]
|
||||
|
||||
|
||||
def with_warn_for_invalid_lines(mappings):
|
||||
# type: (Iterator[Binding]) -> Iterator[Binding]
|
||||
for mapping in mappings:
|
||||
if mapping.error:
|
||||
logger.warning(
|
||||
"Python-dotenv could not parse statement starting at line %s",
|
||||
mapping.original.line,
|
||||
)
|
||||
yield mapping
|
||||
|
||||
|
||||
class DotEnv():
|
||||
|
||||
def __init__(self, dotenv_path, verbose=False, encoding=None):
|
||||
# type: (Union[Text, _PathLike, _StringIO], bool, Union[None, Text]) -> None
|
||||
def __init__(self, dotenv_path, verbose=False, encoding=None, interpolate=True):
|
||||
# type: (Union[Text, _PathLike, _StringIO], bool, Union[None, Text], bool) -> None
|
||||
self.dotenv_path = dotenv_path # type: Union[Text,_PathLike, _StringIO]
|
||||
self._dict = None # type: Optional[Dict[Text, Text]]
|
||||
self._dict = None # type: Optional[Dict[Text, Optional[Text]]]
|
||||
self.verbose = verbose # type: bool
|
||||
self.encoding = encoding # type: Union[None, Text]
|
||||
self.interpolate = interpolate # type: bool
|
||||
|
||||
@contextmanager
|
||||
def _get_stream(self):
|
||||
@@ -50,24 +74,28 @@ class DotEnv():
|
||||
yield stream
|
||||
else:
|
||||
if self.verbose:
|
||||
warnings.warn("File doesn't exist {}".format(self.dotenv_path)) # type: ignore
|
||||
logger.info("Python-dotenv could not find configuration file %s.", self.dotenv_path or '.env')
|
||||
yield StringIO('')
|
||||
|
||||
def dict(self):
|
||||
# type: () -> Dict[Text, Text]
|
||||
# type: () -> Dict[Text, Optional[Text]]
|
||||
"""Return dotenv as dict"""
|
||||
if self._dict:
|
||||
return self._dict
|
||||
|
||||
values = OrderedDict(self.parse())
|
||||
self._dict = resolve_nested_variables(values)
|
||||
return self._dict
|
||||
if self.interpolate:
|
||||
values = resolve_nested_variables(self.parse())
|
||||
else:
|
||||
values = OrderedDict(self.parse())
|
||||
|
||||
self._dict = values
|
||||
return values
|
||||
|
||||
def parse(self):
|
||||
# type: () -> Iterator[Tuple[Text, Text]]
|
||||
# type: () -> Iterator[Tuple[Text, Optional[Text]]]
|
||||
with self._get_stream() as stream:
|
||||
for mapping in parse_stream(stream):
|
||||
if mapping.key is not None and mapping.value is not None:
|
||||
for mapping in with_warn_for_invalid_lines(parse_stream(stream)):
|
||||
if mapping.key is not None:
|
||||
yield mapping.key, mapping.value
|
||||
|
||||
def set_as_environment_variables(self, override=False):
|
||||
@@ -78,7 +106,8 @@ class DotEnv():
|
||||
for k, v in self.dict().items():
|
||||
if k in os.environ and not override:
|
||||
continue
|
||||
os.environ[to_env(k)] = to_env(v)
|
||||
if v is not None:
|
||||
os.environ[to_env(k)] = to_env(v)
|
||||
|
||||
return True
|
||||
|
||||
@@ -92,7 +121,7 @@ class DotEnv():
|
||||
return data[key]
|
||||
|
||||
if self.verbose:
|
||||
warnings.warn("key %s not found in %s." % (key, self.dotenv_path)) # type: ignore
|
||||
logger.warning("Key %s not found in %s.", key, self.dotenv_path)
|
||||
|
||||
return None
|
||||
|
||||
@@ -111,6 +140,9 @@ def get_key(dotenv_path, key_to_get):
|
||||
def rewrite(path):
|
||||
# type: (_PathLike) -> Iterator[Tuple[IO[Text], IO[Text]]]
|
||||
try:
|
||||
if not os.path.isfile(path):
|
||||
with io.open(path, "w+") as source:
|
||||
source.write("")
|
||||
with tempfile.NamedTemporaryFile(mode="w+", delete=False) as dest:
|
||||
with io.open(path) as source:
|
||||
yield (source, dest) # type: ignore
|
||||
@@ -122,8 +154,8 @@ def rewrite(path):
|
||||
shutil.move(dest.name, path)
|
||||
|
||||
|
||||
def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always"):
|
||||
# type: (_PathLike, Text, Text, Text) -> Tuple[Optional[bool], Text, Text]
|
||||
def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always", export=False):
|
||||
# type: (_PathLike, Text, Text, Text, bool) -> Tuple[Optional[bool], Text, Text]
|
||||
"""
|
||||
Adds or Updates a key/value to the given .env
|
||||
|
||||
@@ -131,24 +163,27 @@ def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always"):
|
||||
an orphan .env somewhere in the filesystem
|
||||
"""
|
||||
value_to_set = value_to_set.strip("'").strip('"')
|
||||
if not os.path.exists(dotenv_path):
|
||||
warnings.warn("can't write to %s - it doesn't exist." % dotenv_path) # type: ignore
|
||||
return None, key_to_set, value_to_set
|
||||
|
||||
if " " in value_to_set:
|
||||
quote_mode = "always"
|
||||
|
||||
line_template = '{}="{}"\n' if quote_mode == "always" else '{}={}\n'
|
||||
line_out = line_template.format(key_to_set, value_to_set)
|
||||
if quote_mode == "always":
|
||||
value_out = '"{}"'.format(value_to_set.replace('"', '\\"'))
|
||||
else:
|
||||
value_out = value_to_set
|
||||
if export:
|
||||
line_out = 'export {}={}\n'.format(key_to_set, value_out)
|
||||
else:
|
||||
line_out = "{}={}\n".format(key_to_set, value_out)
|
||||
|
||||
with rewrite(dotenv_path) as (source, dest):
|
||||
replaced = False
|
||||
for mapping in parse_stream(source):
|
||||
for mapping in with_warn_for_invalid_lines(parse_stream(source)):
|
||||
if mapping.key == key_to_set:
|
||||
dest.write(line_out)
|
||||
replaced = True
|
||||
else:
|
||||
dest.write(mapping.original)
|
||||
dest.write(mapping.original.string)
|
||||
if not replaced:
|
||||
dest.write(line_out)
|
||||
|
||||
@@ -164,48 +199,45 @@ def unset_key(dotenv_path, key_to_unset, quote_mode="always"):
|
||||
If the given key doesn't exist in the .env, fails
|
||||
"""
|
||||
if not os.path.exists(dotenv_path):
|
||||
warnings.warn("can't delete from %s - it doesn't exist." % dotenv_path) # type: ignore
|
||||
logger.warning("Can't delete from %s - it doesn't exist.", dotenv_path)
|
||||
return None, key_to_unset
|
||||
|
||||
removed = False
|
||||
with rewrite(dotenv_path) as (source, dest):
|
||||
for mapping in parse_stream(source):
|
||||
for mapping in with_warn_for_invalid_lines(parse_stream(source)):
|
||||
if mapping.key == key_to_unset:
|
||||
removed = True
|
||||
else:
|
||||
dest.write(mapping.original)
|
||||
dest.write(mapping.original.string)
|
||||
|
||||
if not removed:
|
||||
warnings.warn("key %s not removed from %s - key doesn't exist." % (key_to_unset, dotenv_path)) # type: ignore
|
||||
logger.warning("Key %s not removed from %s - key doesn't exist.", key_to_unset, dotenv_path)
|
||||
return None, key_to_unset
|
||||
|
||||
return removed, key_to_unset
|
||||
|
||||
|
||||
def resolve_nested_variables(values):
|
||||
# type: (Dict[Text, Text]) -> Dict[Text, Text]
|
||||
def _replacement(name):
|
||||
# type: (Text) -> Text
|
||||
"""
|
||||
get appropriate value for a variable name.
|
||||
first search in environ, if not found,
|
||||
then look into the dotenv variables
|
||||
"""
|
||||
ret = os.getenv(name, new_values.get(name, ""))
|
||||
return ret
|
||||
# type: (Iterable[Tuple[Text, Optional[Text]]]) -> Dict[Text, Optional[Text]]
|
||||
def _replacement(name, default):
|
||||
# type: (Text, Optional[Text]) -> Text
|
||||
default = default if default is not None else ""
|
||||
ret = new_values.get(name, os.getenv(name, default))
|
||||
return ret # type: ignore
|
||||
|
||||
def _re_sub_callback(match_object):
|
||||
def _re_sub_callback(match):
|
||||
# type: (Match[Text]) -> Text
|
||||
"""
|
||||
From a match object gets the variable name and returns
|
||||
the correct replacement
|
||||
"""
|
||||
return _replacement(match_object.group()[2:-1])
|
||||
matches = match.groupdict()
|
||||
return _replacement(name=matches["name"], default=matches["default"]) # type: ignore
|
||||
|
||||
new_values = {}
|
||||
|
||||
for k, v in values.items():
|
||||
new_values[k] = __posix_variable.sub(_re_sub_callback, v)
|
||||
for (k, v) in values:
|
||||
new_values[k] = __posix_variable.sub(_re_sub_callback, v) if v is not None else None
|
||||
|
||||
return new_values
|
||||
|
||||
@@ -242,7 +274,7 @@ def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False):
|
||||
main = __import__('__main__', None, None, fromlist=['__file__'])
|
||||
return not hasattr(main, '__file__')
|
||||
|
||||
if usecwd or _is_interactive():
|
||||
if usecwd or _is_interactive() or getattr(sys, 'frozen', False):
|
||||
# Should work without __file__, e.g. in REPL or IPython notebook.
|
||||
path = os.getcwd()
|
||||
else:
|
||||
@@ -257,6 +289,7 @@ def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False):
|
||||
current_file = __file__
|
||||
|
||||
while frame.f_code.co_filename == current_file:
|
||||
assert frame.f_back is not None
|
||||
frame = frame.f_back
|
||||
frame_filename = frame.f_code.co_filename
|
||||
path = os.path.dirname(os.path.abspath(frame_filename))
|
||||
@@ -272,8 +305,8 @@ def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False):
|
||||
return ''
|
||||
|
||||
|
||||
def load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, **kwargs):
|
||||
# type: (Union[Text, _PathLike, None], Optional[_StringIO], bool, bool, Union[None, Text]) -> bool
|
||||
def load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, interpolate=True, **kwargs):
|
||||
# type: (Union[Text, _PathLike, None], Optional[_StringIO], bool, bool, bool, Union[None, Text]) -> bool
|
||||
"""Parse a .env file and then load all the variables found as environment variables.
|
||||
|
||||
- *dotenv_path*: absolute or relative path to .env file.
|
||||
@@ -283,10 +316,10 @@ def load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, **
|
||||
Defaults to `False`.
|
||||
"""
|
||||
f = dotenv_path or stream or find_dotenv()
|
||||
return DotEnv(f, verbose=verbose, **kwargs).set_as_environment_variables(override=override)
|
||||
return DotEnv(f, verbose=verbose, interpolate=interpolate, **kwargs).set_as_environment_variables(override=override)
|
||||
|
||||
|
||||
def dotenv_values(dotenv_path=None, stream=None, verbose=False, **kwargs):
|
||||
# type: (Union[Text, _PathLike, None], Optional[_StringIO], bool, Union[None, Text]) -> Dict[Text, Text]
|
||||
def dotenv_values(dotenv_path=None, stream=None, verbose=False, interpolate=True, **kwargs):
|
||||
# type: (Union[Text, _PathLike, None], Optional[_StringIO], bool, bool, Union[None, Text]) -> Dict[Text, Optional[Text]] # noqa: E501
|
||||
f = dotenv_path or stream or find_dotenv()
|
||||
return DotEnv(f, verbose=verbose, **kwargs).dict()
|
||||
return DotEnv(f, verbose=verbose, interpolate=interpolate, **kwargs).dict()
|
||||
|
||||
Vendored
+113
-45
@@ -1,8 +1,7 @@
|
||||
import codecs
|
||||
import re
|
||||
|
||||
from .compat import to_text, IS_TYPE_CHECKING
|
||||
|
||||
from .compat import IS_TYPE_CHECKING, to_text
|
||||
|
||||
if IS_TYPE_CHECKING:
|
||||
from typing import ( # noqa:F401
|
||||
@@ -16,16 +15,18 @@ def make_regex(string, extra_flags=0):
|
||||
return re.compile(to_text(string), re.UNICODE | extra_flags)
|
||||
|
||||
|
||||
_whitespace = make_regex(r"\s*", extra_flags=re.MULTILINE)
|
||||
_newline = make_regex(r"(\r\n|\n|\r)")
|
||||
_multiline_whitespace = make_regex(r"\s*", extra_flags=re.MULTILINE)
|
||||
_whitespace = make_regex(r"[^\S\r\n]*")
|
||||
_export = make_regex(r"(?:export[^\S\r\n]+)?")
|
||||
_single_quoted_key = make_regex(r"'([^']+)'")
|
||||
_unquoted_key = make_regex(r"([^=\#\s]+)")
|
||||
_equal_sign = make_regex(r"[^\S\r\n]*=[^\S\r\n]*")
|
||||
_equal_sign = make_regex(r"(=[^\S\r\n]*)")
|
||||
_single_quoted_value = make_regex(r"'((?:\\'|[^'])*)'")
|
||||
_double_quoted_value = make_regex(r'"((?:\\"|[^"])*)"')
|
||||
_unquoted_value_part = make_regex(r"([^ \r\n]*)")
|
||||
_comment = make_regex(r"(?:\s*#[^\r\n]*)?")
|
||||
_end_of_line = make_regex(r"[^\S\r\n]*(?:\r\n|\n|\r)?")
|
||||
_unquoted_value = make_regex(r"([^\r\n]*)")
|
||||
_comment = make_regex(r"(?:[^\S\r\n]*#[^\r\n]*)?")
|
||||
_end_of_line = make_regex(r"[^\S\r\n]*(?:\r\n|\n|\r|$)")
|
||||
_rest_of_line = make_regex(r"[^\r\n]*(?:\r|\n|\r\n)?")
|
||||
_double_quote_escapes = make_regex(r"\\[\\'\"abfnrtv]")
|
||||
_single_quote_escapes = make_regex(r"\\[\\']")
|
||||
@@ -36,14 +37,64 @@ try:
|
||||
# when we are type checking, and the linter is upset if we
|
||||
# re-import
|
||||
import typing
|
||||
Binding = typing.NamedTuple("Binding", [("key", typing.Optional[typing.Text]),
|
||||
("value", typing.Optional[typing.Text]),
|
||||
("original", typing.Text)])
|
||||
except ImportError: # pragma: no cover
|
||||
|
||||
Original = typing.NamedTuple(
|
||||
"Original",
|
||||
[
|
||||
("string", typing.Text),
|
||||
("line", int),
|
||||
],
|
||||
)
|
||||
|
||||
Binding = typing.NamedTuple(
|
||||
"Binding",
|
||||
[
|
||||
("key", typing.Optional[typing.Text]),
|
||||
("value", typing.Optional[typing.Text]),
|
||||
("original", Original),
|
||||
("error", bool),
|
||||
],
|
||||
)
|
||||
except (ImportError, AttributeError):
|
||||
from collections import namedtuple
|
||||
Binding = namedtuple("Binding", ["key", # type: ignore
|
||||
"value",
|
||||
"original"]) # type: Tuple[Optional[Text], Optional[Text], Text]
|
||||
Original = namedtuple( # type: ignore
|
||||
"Original",
|
||||
[
|
||||
"string",
|
||||
"line",
|
||||
],
|
||||
)
|
||||
Binding = namedtuple( # type: ignore
|
||||
"Binding",
|
||||
[
|
||||
"key",
|
||||
"value",
|
||||
"original",
|
||||
"error",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class Position:
|
||||
def __init__(self, chars, line):
|
||||
# type: (int, int) -> None
|
||||
self.chars = chars
|
||||
self.line = line
|
||||
|
||||
@classmethod
|
||||
def start(cls):
|
||||
# type: () -> Position
|
||||
return cls(chars=0, line=1)
|
||||
|
||||
def set(self, other):
|
||||
# type: (Position) -> None
|
||||
self.chars = other.chars
|
||||
self.line = other.line
|
||||
|
||||
def advance(self, string):
|
||||
# type: (Text) -> None
|
||||
self.chars += len(string)
|
||||
self.line += len(re.findall(_newline, string))
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
@@ -54,39 +105,42 @@ class Reader:
|
||||
def __init__(self, stream):
|
||||
# type: (IO[Text]) -> None
|
||||
self.string = stream.read()
|
||||
self.position = 0
|
||||
self.mark = 0
|
||||
self.position = Position.start()
|
||||
self.mark = Position.start()
|
||||
|
||||
def has_next(self):
|
||||
# type: () -> bool
|
||||
return self.position < len(self.string)
|
||||
return self.position.chars < len(self.string)
|
||||
|
||||
def set_mark(self):
|
||||
# type: () -> None
|
||||
self.mark = self.position
|
||||
self.mark.set(self.position)
|
||||
|
||||
def get_marked(self):
|
||||
# type: () -> Text
|
||||
return self.string[self.mark:self.position]
|
||||
# type: () -> Original
|
||||
return Original(
|
||||
string=self.string[self.mark.chars:self.position.chars],
|
||||
line=self.mark.line,
|
||||
)
|
||||
|
||||
def peek(self, count):
|
||||
# type: (int) -> Text
|
||||
return self.string[self.position:self.position + count]
|
||||
return self.string[self.position.chars:self.position.chars + count]
|
||||
|
||||
def read(self, count):
|
||||
# type: (int) -> Text
|
||||
result = self.string[self.position:self.position + count]
|
||||
result = self.string[self.position.chars:self.position.chars + count]
|
||||
if len(result) < count:
|
||||
raise Error("read: End of string")
|
||||
self.position += count
|
||||
self.position.advance(result)
|
||||
return result
|
||||
|
||||
def read_regex(self, regex):
|
||||
# type: (Pattern[Text]) -> Sequence[Text]
|
||||
match = regex.match(self.string, self.position)
|
||||
match = regex.match(self.string, self.position.chars)
|
||||
if match is None:
|
||||
raise Error("read_regex: Pattern not found")
|
||||
self.position = match.end()
|
||||
self.position.advance(self.string[match.start():match.end()])
|
||||
return match.groups()
|
||||
|
||||
|
||||
@@ -100,9 +154,11 @@ def decode_escapes(regex, string):
|
||||
|
||||
|
||||
def parse_key(reader):
|
||||
# type: (Reader) -> Text
|
||||
# type: (Reader) -> Optional[Text]
|
||||
char = reader.peek(1)
|
||||
if char == "'":
|
||||
if char == "#":
|
||||
return None
|
||||
elif char == "'":
|
||||
(key,) = reader.read_regex(_single_quoted_key)
|
||||
else:
|
||||
(key,) = reader.read_regex(_unquoted_key)
|
||||
@@ -111,14 +167,8 @@ def parse_key(reader):
|
||||
|
||||
def parse_unquoted_value(reader):
|
||||
# type: (Reader) -> Text
|
||||
value = u""
|
||||
while True:
|
||||
(part,) = reader.read_regex(_unquoted_value_part)
|
||||
value += part
|
||||
after = reader.peek(2)
|
||||
if len(after) < 2 or after[0] in u"\r\n" or after[1] in u" #\r\n":
|
||||
return value
|
||||
value += reader.read(2)
|
||||
(part,) = reader.read_regex(_unquoted_value)
|
||||
return re.sub(r"\s+#.*", "", part).rstrip()
|
||||
|
||||
|
||||
def parse_value(reader):
|
||||
@@ -140,24 +190,42 @@ def parse_binding(reader):
|
||||
# type: (Reader) -> Binding
|
||||
reader.set_mark()
|
||||
try:
|
||||
reader.read_regex(_whitespace)
|
||||
reader.read_regex(_multiline_whitespace)
|
||||
if not reader.has_next():
|
||||
return Binding(
|
||||
key=None,
|
||||
value=None,
|
||||
original=reader.get_marked(),
|
||||
error=False,
|
||||
)
|
||||
reader.read_regex(_export)
|
||||
key = parse_key(reader)
|
||||
reader.read_regex(_equal_sign)
|
||||
value = parse_value(reader)
|
||||
reader.read_regex(_whitespace)
|
||||
if reader.peek(1) == "=":
|
||||
reader.read_regex(_equal_sign)
|
||||
value = parse_value(reader) # type: Optional[Text]
|
||||
else:
|
||||
value = None
|
||||
reader.read_regex(_comment)
|
||||
reader.read_regex(_end_of_line)
|
||||
return Binding(key=key, value=value, original=reader.get_marked())
|
||||
return Binding(
|
||||
key=key,
|
||||
value=value,
|
||||
original=reader.get_marked(),
|
||||
error=False,
|
||||
)
|
||||
except Error:
|
||||
reader.read_regex(_rest_of_line)
|
||||
return Binding(key=None, value=None, original=reader.get_marked())
|
||||
return Binding(
|
||||
key=None,
|
||||
value=None,
|
||||
original=reader.get_marked(),
|
||||
error=True,
|
||||
)
|
||||
|
||||
|
||||
def parse_stream(stream):
|
||||
# type:(IO[Text]) -> Iterator[Binding]
|
||||
# type: (IO[Text]) -> Iterator[Binding]
|
||||
reader = Reader(stream)
|
||||
while reader.has_next():
|
||||
try:
|
||||
yield parse_binding(reader)
|
||||
except Error:
|
||||
return
|
||||
yield parse_binding(reader)
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "0.10.3"
|
||||
__version__ = "0.15.0"
|
||||
|
||||
Vendored
+2
-2
@@ -33,10 +33,10 @@ match in a more advanced way.
|
||||
"""
|
||||
|
||||
__title__ = 'first'
|
||||
__version__ = '2.0.1'
|
||||
__version__ = '2.0.2'
|
||||
__author__ = 'Hynek Schlawack'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2012–2013 Hynek Schlawack'
|
||||
__copyright__ = 'Copyright 2012 Hynek Schlawack'
|
||||
|
||||
|
||||
def first(iterable, default=None, key=None):
|
||||
|
||||
Vendored
+2
@@ -300,6 +300,8 @@ def ulabel(label):
|
||||
label = label.lower()
|
||||
if label.startswith(_alabel_prefix):
|
||||
label = label[len(_alabel_prefix):]
|
||||
if not label:
|
||||
raise IDNAError('Malformed A-label, no Punycode eligible content found')
|
||||
if label.decode('ascii')[-1] == '-':
|
||||
raise IDNAError('A-label must not end with a hyphen')
|
||||
else:
|
||||
|
||||
Vendored
+83
-24
@@ -1,6 +1,6 @@
|
||||
# This file is automatically generated by tools/idna-data
|
||||
|
||||
__version__ = "12.1.0"
|
||||
__version__ = "13.0.0"
|
||||
scripts = {
|
||||
'Greek': (
|
||||
0x37000000374,
|
||||
@@ -48,16 +48,18 @@ scripts = {
|
||||
0x300700003008,
|
||||
0x30210000302a,
|
||||
0x30380000303c,
|
||||
0x340000004db6,
|
||||
0x4e0000009ff0,
|
||||
0x340000004dc0,
|
||||
0x4e0000009ffd,
|
||||
0xf9000000fa6e,
|
||||
0xfa700000fada,
|
||||
0x200000002a6d7,
|
||||
0x16ff000016ff2,
|
||||
0x200000002a6de,
|
||||
0x2a7000002b735,
|
||||
0x2b7400002b81e,
|
||||
0x2b8200002cea2,
|
||||
0x2ceb00002ebe1,
|
||||
0x2f8000002fa1e,
|
||||
0x300000003134b,
|
||||
),
|
||||
'Hebrew': (
|
||||
0x591000005c8,
|
||||
@@ -389,9 +391,9 @@ joining_types = {
|
||||
0x853: 68,
|
||||
0x854: 82,
|
||||
0x855: 68,
|
||||
0x856: 85,
|
||||
0x857: 85,
|
||||
0x858: 85,
|
||||
0x856: 82,
|
||||
0x857: 82,
|
||||
0x858: 82,
|
||||
0x860: 68,
|
||||
0x861: 85,
|
||||
0x862: 68,
|
||||
@@ -432,6 +434,16 @@ joining_types = {
|
||||
0x8bb: 68,
|
||||
0x8bc: 68,
|
||||
0x8bd: 68,
|
||||
0x8be: 68,
|
||||
0x8bf: 68,
|
||||
0x8c0: 68,
|
||||
0x8c1: 68,
|
||||
0x8c2: 68,
|
||||
0x8c3: 68,
|
||||
0x8c4: 68,
|
||||
0x8c5: 68,
|
||||
0x8c6: 68,
|
||||
0x8c7: 68,
|
||||
0x8e2: 85,
|
||||
0x1806: 85,
|
||||
0x1807: 68,
|
||||
@@ -756,6 +768,34 @@ joining_types = {
|
||||
0x10f52: 68,
|
||||
0x10f53: 68,
|
||||
0x10f54: 82,
|
||||
0x10fb0: 68,
|
||||
0x10fb1: 85,
|
||||
0x10fb2: 68,
|
||||
0x10fb3: 68,
|
||||
0x10fb4: 82,
|
||||
0x10fb5: 82,
|
||||
0x10fb6: 82,
|
||||
0x10fb7: 85,
|
||||
0x10fb8: 68,
|
||||
0x10fb9: 82,
|
||||
0x10fba: 82,
|
||||
0x10fbb: 68,
|
||||
0x10fbc: 68,
|
||||
0x10fbd: 82,
|
||||
0x10fbe: 68,
|
||||
0x10fbf: 68,
|
||||
0x10fc0: 85,
|
||||
0x10fc1: 68,
|
||||
0x10fc2: 82,
|
||||
0x10fc3: 82,
|
||||
0x10fc4: 68,
|
||||
0x10fc5: 85,
|
||||
0x10fc6: 85,
|
||||
0x10fc7: 85,
|
||||
0x10fc8: 85,
|
||||
0x10fc9: 82,
|
||||
0x10fca: 68,
|
||||
0x10fcb: 76,
|
||||
0x110bd: 85,
|
||||
0x110cd: 85,
|
||||
0x1e900: 68,
|
||||
@@ -1129,7 +1169,7 @@ codepoint_classes = {
|
||||
0x8400000085c,
|
||||
0x8600000086b,
|
||||
0x8a0000008b5,
|
||||
0x8b6000008be,
|
||||
0x8b6000008c8,
|
||||
0x8d3000008e2,
|
||||
0x8e300000958,
|
||||
0x96000000964,
|
||||
@@ -1188,7 +1228,7 @@ codepoint_classes = {
|
||||
0xb3c00000b45,
|
||||
0xb4700000b49,
|
||||
0xb4b00000b4e,
|
||||
0xb5600000b58,
|
||||
0xb5500000b58,
|
||||
0xb5f00000b64,
|
||||
0xb6600000b70,
|
||||
0xb7100000b72,
|
||||
@@ -1233,8 +1273,7 @@ codepoint_classes = {
|
||||
0xce000000ce4,
|
||||
0xce600000cf0,
|
||||
0xcf100000cf3,
|
||||
0xd0000000d04,
|
||||
0xd0500000d0d,
|
||||
0xd0000000d0d,
|
||||
0xd0e00000d11,
|
||||
0xd1200000d45,
|
||||
0xd4600000d49,
|
||||
@@ -1243,7 +1282,7 @@ codepoint_classes = {
|
||||
0xd5f00000d64,
|
||||
0xd6600000d70,
|
||||
0xd7a00000d80,
|
||||
0xd8200000d84,
|
||||
0xd8100000d84,
|
||||
0xd8500000d97,
|
||||
0xd9a00000db2,
|
||||
0xdb300000dbc,
|
||||
@@ -1358,6 +1397,7 @@ codepoint_classes = {
|
||||
0x1a9000001a9a,
|
||||
0x1aa700001aa8,
|
||||
0x1ab000001abe,
|
||||
0x1abf00001ac1,
|
||||
0x1b0000001b4c,
|
||||
0x1b5000001b5a,
|
||||
0x1b6b00001b74,
|
||||
@@ -1609,10 +1649,10 @@ codepoint_classes = {
|
||||
0x30a1000030fb,
|
||||
0x30fc000030ff,
|
||||
0x310500003130,
|
||||
0x31a0000031bb,
|
||||
0x31a0000031c0,
|
||||
0x31f000003200,
|
||||
0x340000004db6,
|
||||
0x4e0000009ff0,
|
||||
0x340000004dc0,
|
||||
0x4e0000009ffd,
|
||||
0xa0000000a48d,
|
||||
0xa4d00000a4fe,
|
||||
0xa5000000a60d,
|
||||
@@ -1727,8 +1767,11 @@ codepoint_classes = {
|
||||
0xa7bd0000a7be,
|
||||
0xa7bf0000a7c0,
|
||||
0xa7c30000a7c4,
|
||||
0xa7f70000a7f8,
|
||||
0xa7c80000a7c9,
|
||||
0xa7ca0000a7cb,
|
||||
0xa7f60000a7f8,
|
||||
0xa7fa0000a828,
|
||||
0xa82c0000a82d,
|
||||
0xa8400000a874,
|
||||
0xa8800000a8c6,
|
||||
0xa8d00000a8da,
|
||||
@@ -1753,7 +1796,7 @@ codepoint_classes = {
|
||||
0xab200000ab27,
|
||||
0xab280000ab2f,
|
||||
0xab300000ab5b,
|
||||
0xab600000ab68,
|
||||
0xab600000ab6a,
|
||||
0xabc00000abeb,
|
||||
0xabec0000abee,
|
||||
0xabf00000abfa,
|
||||
@@ -1827,9 +1870,13 @@ codepoint_classes = {
|
||||
0x10cc000010cf3,
|
||||
0x10d0000010d28,
|
||||
0x10d3000010d3a,
|
||||
0x10e8000010eaa,
|
||||
0x10eab00010ead,
|
||||
0x10eb000010eb2,
|
||||
0x10f0000010f1d,
|
||||
0x10f2700010f28,
|
||||
0x10f3000010f51,
|
||||
0x10fb000010fc5,
|
||||
0x10fe000010ff7,
|
||||
0x1100000011047,
|
||||
0x1106600011070,
|
||||
@@ -1838,12 +1885,12 @@ codepoint_classes = {
|
||||
0x110f0000110fa,
|
||||
0x1110000011135,
|
||||
0x1113600011140,
|
||||
0x1114400011147,
|
||||
0x1114400011148,
|
||||
0x1115000011174,
|
||||
0x1117600011177,
|
||||
0x11180000111c5,
|
||||
0x111c9000111cd,
|
||||
0x111d0000111db,
|
||||
0x111ce000111db,
|
||||
0x111dc000111dd,
|
||||
0x1120000011212,
|
||||
0x1121300011238,
|
||||
@@ -1872,7 +1919,7 @@ codepoint_classes = {
|
||||
0x1137000011375,
|
||||
0x114000001144b,
|
||||
0x114500001145a,
|
||||
0x1145e00011460,
|
||||
0x1145e00011462,
|
||||
0x11480000114c6,
|
||||
0x114c7000114c8,
|
||||
0x114d0000114da,
|
||||
@@ -1889,7 +1936,14 @@ codepoint_classes = {
|
||||
0x117300001173a,
|
||||
0x118000001183b,
|
||||
0x118c0000118ea,
|
||||
0x118ff00011900,
|
||||
0x118ff00011907,
|
||||
0x119090001190a,
|
||||
0x1190c00011914,
|
||||
0x1191500011917,
|
||||
0x1191800011936,
|
||||
0x1193700011939,
|
||||
0x1193b00011944,
|
||||
0x119500001195a,
|
||||
0x119a0000119a8,
|
||||
0x119aa000119d8,
|
||||
0x119da000119e2,
|
||||
@@ -1920,6 +1974,7 @@ codepoint_classes = {
|
||||
0x11d9300011d99,
|
||||
0x11da000011daa,
|
||||
0x11ee000011ef7,
|
||||
0x11fb000011fb1,
|
||||
0x120000001239a,
|
||||
0x1248000012544,
|
||||
0x130000001342f,
|
||||
@@ -1939,9 +1994,11 @@ codepoint_classes = {
|
||||
0x16f4f00016f88,
|
||||
0x16f8f00016fa0,
|
||||
0x16fe000016fe2,
|
||||
0x16fe300016fe4,
|
||||
0x16fe300016fe5,
|
||||
0x16ff000016ff2,
|
||||
0x17000000187f8,
|
||||
0x1880000018af3,
|
||||
0x1880000018cd6,
|
||||
0x18d0000018d09,
|
||||
0x1b0000001b11f,
|
||||
0x1b1500001b153,
|
||||
0x1b1640001b168,
|
||||
@@ -1971,11 +2028,13 @@ codepoint_classes = {
|
||||
0x1e8d00001e8d7,
|
||||
0x1e9220001e94c,
|
||||
0x1e9500001e95a,
|
||||
0x200000002a6d7,
|
||||
0x1fbf00001fbfa,
|
||||
0x200000002a6de,
|
||||
0x2a7000002b735,
|
||||
0x2b7400002b81e,
|
||||
0x2b8200002cea2,
|
||||
0x2ceb00002ebe1,
|
||||
0x300000003134b,
|
||||
),
|
||||
'CONTEXTJ': (
|
||||
0x200c0000200e,
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
__version__ = '2.9'
|
||||
__version__ = '2.10'
|
||||
|
||||
|
||||
Vendored
+266
-226
File diff suppressed because it is too large
Load Diff
+24
-6
@@ -29,6 +29,7 @@ from ._compat import (
|
||||
email_message_from_string,
|
||||
PyPy_repr,
|
||||
unique_ordered,
|
||||
str,
|
||||
)
|
||||
from importlib import import_module
|
||||
from itertools import starmap
|
||||
@@ -54,6 +55,15 @@ __all__ = [
|
||||
class PackageNotFoundError(ModuleNotFoundError):
|
||||
"""The package was not found."""
|
||||
|
||||
def __str__(self):
|
||||
tmpl = "No package metadata was found for {self.name}"
|
||||
return tmpl.format(**locals())
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
name, = self.args
|
||||
return name
|
||||
|
||||
|
||||
class EntryPoint(
|
||||
PyPy_repr,
|
||||
@@ -198,7 +208,7 @@ class Distribution:
|
||||
"""
|
||||
for resolver in cls._discover_resolvers():
|
||||
dists = resolver(DistributionFinder.Context(name=name))
|
||||
dist = next(dists, None)
|
||||
dist = next(iter(dists), None)
|
||||
if dist is not None:
|
||||
return dist
|
||||
else:
|
||||
@@ -241,6 +251,17 @@ class Distribution:
|
||||
)
|
||||
return filter(None, declared)
|
||||
|
||||
@classmethod
|
||||
def _local(cls, root='.'):
|
||||
from pep517 import build, meta
|
||||
system = build.compat_system(root)
|
||||
builder = functools.partial(
|
||||
meta.build,
|
||||
source_dir=root,
|
||||
system=system,
|
||||
)
|
||||
return PathDistribution(zipp.Path(meta.build_as_zip(builder)))
|
||||
|
||||
@property
|
||||
def metadata(self):
|
||||
"""Return the parsed metadata for this Distribution.
|
||||
@@ -418,8 +439,8 @@ class FastPath:
|
||||
"""
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.base = os.path.basename(root).lower()
|
||||
self.root = str(root)
|
||||
self.base = os.path.basename(self.root).lower()
|
||||
|
||||
def joinpath(self, child):
|
||||
return pathlib.Path(self.root, child)
|
||||
@@ -597,6 +618,3 @@ def requires(distribution_name):
|
||||
packaging.requirement.Requirement.
|
||||
"""
|
||||
return distribution(distribution_name).requires
|
||||
|
||||
|
||||
__version__ = version(__name__)
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import io
|
||||
import abc
|
||||
@@ -26,6 +26,8 @@ else: # pragma: nocover
|
||||
NotADirectoryError = IOError, OSError
|
||||
PermissionError = IOError, OSError
|
||||
|
||||
str = type('')
|
||||
|
||||
suppress = contextlib.suppress
|
||||
|
||||
if sys.version_info > (3, 5): # pragma: nocover
|
||||
|
||||
@@ -2,6 +2,38 @@
|
||||
importlib_metadata NEWS
|
||||
=========================
|
||||
|
||||
v2.0.0
|
||||
======
|
||||
|
||||
* ``importlib_metadata`` no longer presents a
|
||||
``__version__`` attribute. Consumers wishing to
|
||||
resolve the version of the package should query it
|
||||
directly with
|
||||
``importlib_metadata.version('importlib-metadata')``.
|
||||
Closes #71.
|
||||
|
||||
v1.7.0
|
||||
======
|
||||
|
||||
* ``PathNotFoundError`` now has a custom ``__str__``
|
||||
mentioning "package metadata" being missing to help
|
||||
guide users to the cause when the package is installed
|
||||
but no metadata is present. Closes #124.
|
||||
|
||||
v1.6.1
|
||||
======
|
||||
|
||||
* Added ``Distribution._local()`` as a provisional
|
||||
demonstration of how to load metadata for a local
|
||||
package. Implicitly requires that
|
||||
`pep517 <https://pypi.org/project/pep517>`_ is
|
||||
installed. Ref #42.
|
||||
* Ensure inputs to FastPath are Unicode. Closes #121.
|
||||
* Tests now rely on ``importlib.resources.files`` (and
|
||||
backport) instead of the older ``path`` function.
|
||||
* Support any iterable from ``find_distributions``.
|
||||
Closes #122.
|
||||
|
||||
v1.6.0
|
||||
======
|
||||
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ The ``group`` and ``name`` are arbitrary values defined by the package author
|
||||
and usually a client will wish to resolve all entry points for a particular
|
||||
group. Read `the setuptools docs
|
||||
<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_
|
||||
for more information on entrypoints, their definition, and usage.
|
||||
for more information on entry points, their definition, and usage.
|
||||
|
||||
|
||||
.. _metadata:
|
||||
@@ -236,7 +236,7 @@ method::
|
||||
"""
|
||||
|
||||
The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
|
||||
properties indicating the path to search and names to match and may
|
||||
properties indicating the path to search and name to match and may
|
||||
supply other relevant context.
|
||||
|
||||
What this means in practice is that to support finding distribution package
|
||||
|
||||
@@ -5,6 +5,7 @@ import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import textwrap
|
||||
import test.support
|
||||
|
||||
from .._compat import pathlib, contextlib
|
||||
|
||||
@@ -166,6 +167,21 @@ class EggInfoFile(OnSysPath, SiteDir):
|
||||
build_files(EggInfoFile.files, prefix=self.site_dir)
|
||||
|
||||
|
||||
class LocalPackage:
|
||||
files = {
|
||||
"setup.py": """
|
||||
import setuptools
|
||||
setuptools.setup(name="local-pkg", version="2.0.1")
|
||||
""",
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.fixtures = contextlib.ExitStack()
|
||||
self.addCleanup(self.fixtures.close)
|
||||
self.fixtures.enter_context(tempdir_as_cwd())
|
||||
build_files(self.files)
|
||||
|
||||
|
||||
def build_files(file_defs, prefix=pathlib.Path()):
|
||||
"""Build a set of files/directories, as described by the
|
||||
|
||||
@@ -200,6 +216,12 @@ def build_files(file_defs, prefix=pathlib.Path()):
|
||||
f.write(DALS(contents))
|
||||
|
||||
|
||||
class FileBuilder:
|
||||
def unicode_filename(self):
|
||||
return test.support.FS_NONASCII or \
|
||||
self.skip("File system does not support non-ascii.")
|
||||
|
||||
|
||||
def DALS(str):
|
||||
"Dedent and left-strip"
|
||||
return textwrap.dedent(str).lstrip()
|
||||
|
||||
+3
-2
@@ -4,7 +4,7 @@ import unittest
|
||||
|
||||
from . import fixtures
|
||||
from .. import (
|
||||
Distribution, PackageNotFoundError, __version__, distribution,
|
||||
Distribution, PackageNotFoundError, distribution,
|
||||
entry_points, files, metadata, requires, version,
|
||||
)
|
||||
|
||||
@@ -68,7 +68,8 @@ class APITests(
|
||||
assert 'Topic :: Software Development :: Libraries' in classifiers
|
||||
|
||||
def test_importlib_metadata_version(self):
|
||||
assert re.match(self.version_pattern, __version__)
|
||||
resolved = version('importlib-metadata')
|
||||
assert re.match(self.version_pattern, resolved)
|
||||
|
||||
@staticmethod
|
||||
def _test_files(files):
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
import packaging.requirements
|
||||
import packaging.version
|
||||
|
||||
from . import fixtures
|
||||
from .. import (
|
||||
Distribution,
|
||||
_compat,
|
||||
version,
|
||||
)
|
||||
@@ -40,3 +45,10 @@ class FinderTests(fixtures.Fixtures, unittest.TestCase):
|
||||
self.fixtures.enter_context(
|
||||
fixtures.install_finder(ModuleFreeFinder()))
|
||||
_compat.disable_stdlib_finder()
|
||||
|
||||
|
||||
class LocalProjectTests(fixtures.LocalPackage, unittest.TestCase):
|
||||
def test_find_local(self):
|
||||
dist = Distribution._local()
|
||||
assert dist.metadata['Name'] == 'local-pkg'
|
||||
assert dist.version == '2.0.1'
|
||||
|
||||
@@ -35,6 +35,18 @@ class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
|
||||
with self.assertRaises(PackageNotFoundError):
|
||||
Distribution.from_name('does-not-exist')
|
||||
|
||||
def test_package_not_found_mentions_metadata(self):
|
||||
"""
|
||||
When a package is not found, that could indicate that the
|
||||
packgae is not installed or that it is installed without
|
||||
metadata. Ensure the exception mentions metadata to help
|
||||
guide users toward the cause. See #124.
|
||||
"""
|
||||
with self.assertRaises(PackageNotFoundError) as ctx:
|
||||
Distribution.from_name('does-not-exist')
|
||||
|
||||
assert "metadata" in str(ctx.exception)
|
||||
|
||||
def test_new_style_classes(self):
|
||||
self.assertIsInstance(Distribution, type)
|
||||
self.assertIsInstance(MetadataPathFinder, type)
|
||||
@@ -256,3 +268,18 @@ class TestEntryPoints(unittest.TestCase):
|
||||
|
||||
def test_attr(self):
|
||||
assert self.ep.attr is None
|
||||
|
||||
|
||||
class FileSystem(
|
||||
fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder,
|
||||
unittest.TestCase):
|
||||
def test_unicode_dir_on_sys_path(self):
|
||||
"""
|
||||
Ensure a Unicode subdirectory of a directory on sys.path
|
||||
does not crash.
|
||||
"""
|
||||
fixtures.build_files(
|
||||
{self.unicode_filename(): {}},
|
||||
prefix=self.site_dir,
|
||||
)
|
||||
list(distributions())
|
||||
|
||||
+14
-11
@@ -7,9 +7,11 @@ from .. import (
|
||||
)
|
||||
|
||||
try:
|
||||
from importlib.resources import path
|
||||
except ImportError:
|
||||
from importlib_resources import path
|
||||
from importlib import resources
|
||||
getattr(resources, 'files')
|
||||
getattr(resources, 'as_file')
|
||||
except (ImportError, AttributeError):
|
||||
import importlib_resources as resources
|
||||
|
||||
try:
|
||||
from contextlib import ExitStack
|
||||
@@ -20,15 +22,19 @@ except ImportError:
|
||||
class TestZip(unittest.TestCase):
|
||||
root = 'importlib_metadata.tests.data'
|
||||
|
||||
def _fixture_on_path(self, filename):
|
||||
pkg_file = resources.files(self.root).joinpath(filename)
|
||||
file = self.resources.enter_context(resources.as_file(pkg_file))
|
||||
assert file.name.startswith('example-'), file.name
|
||||
sys.path.insert(0, str(file))
|
||||
self.resources.callback(sys.path.pop, 0)
|
||||
|
||||
def setUp(self):
|
||||
# Find the path to the example-*.whl so we can add it to the front of
|
||||
# sys.path, where we'll then try to find the metadata thereof.
|
||||
self.resources = ExitStack()
|
||||
self.addCleanup(self.resources.close)
|
||||
wheel = self.resources.enter_context(
|
||||
path(self.root, 'example-21.12-py3-none-any.whl'))
|
||||
sys.path.insert(0, str(wheel))
|
||||
self.resources.callback(sys.path.pop, 0)
|
||||
self._fixture_on_path('example-21.12-py3-none-any.whl')
|
||||
|
||||
def test_zip_version(self):
|
||||
self.assertEqual(version('example'), '21.12')
|
||||
@@ -66,10 +72,7 @@ class TestEgg(TestZip):
|
||||
# sys.path, where we'll then try to find the metadata thereof.
|
||||
self.resources = ExitStack()
|
||||
self.addCleanup(self.resources.close)
|
||||
egg = self.resources.enter_context(
|
||||
path(self.root, 'example-21.12-py3.6.egg'))
|
||||
sys.path.insert(0, str(egg))
|
||||
self.resources.callback(sys.path.pop, 0)
|
||||
self._fixture_on_path('example-21.12-py3.6.egg')
|
||||
|
||||
def test_files(self):
|
||||
for file in files('example'):
|
||||
|
||||
+5
-8
@@ -2,10 +2,12 @@
|
||||
|
||||
import sys
|
||||
|
||||
from ._compat import metadata
|
||||
from ._common import as_file
|
||||
from ._common import (
|
||||
as_file, files,
|
||||
)
|
||||
|
||||
# for compatibility. Ref #88
|
||||
# For compatibility. Ref #88.
|
||||
# Also requires hook-importlib_resources.py (Ref #101).
|
||||
__import__('importlib_resources.trees')
|
||||
|
||||
|
||||
@@ -30,7 +32,6 @@ if sys.version_info >= (3,):
|
||||
Package,
|
||||
Resource,
|
||||
contents,
|
||||
files,
|
||||
is_resource,
|
||||
open_binary,
|
||||
open_text,
|
||||
@@ -42,7 +43,6 @@ if sys.version_info >= (3,):
|
||||
else:
|
||||
from importlib_resources._py2 import (
|
||||
contents,
|
||||
files,
|
||||
is_resource,
|
||||
open_binary,
|
||||
open_text,
|
||||
@@ -51,6 +51,3 @@ else:
|
||||
read_text,
|
||||
)
|
||||
del __all__[:3]
|
||||
|
||||
|
||||
__version__ = metadata.version('importlib_resources')
|
||||
|
||||
+70
-26
@@ -3,12 +3,76 @@ from __future__ import absolute_import
|
||||
import os
|
||||
import tempfile
|
||||
import contextlib
|
||||
import types
|
||||
import importlib
|
||||
|
||||
from ._compat import (
|
||||
Path, package_spec, FileNotFoundError, ZipPath,
|
||||
singledispatch, suppress,
|
||||
Path, FileNotFoundError,
|
||||
singledispatch, package_spec,
|
||||
)
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
from typing import Union, Any, Optional
|
||||
from .abc import ResourceReader
|
||||
Package = Union[types.ModuleType, str]
|
||||
|
||||
|
||||
def files(package):
|
||||
"""
|
||||
Get a Traversable resource from a package
|
||||
"""
|
||||
return from_package(get_package(package))
|
||||
|
||||
|
||||
def normalize_path(path):
|
||||
# type: (Any) -> str
|
||||
"""Normalize a path by ensuring it is a string.
|
||||
|
||||
If the resulting string contains path separators, an exception is raised.
|
||||
"""
|
||||
str_path = str(path)
|
||||
parent, file_name = os.path.split(str_path)
|
||||
if parent:
|
||||
raise ValueError('{!r} must be only a file name'.format(path))
|
||||
return file_name
|
||||
|
||||
|
||||
def get_resource_reader(package):
|
||||
# type: (types.ModuleType) -> Optional[ResourceReader]
|
||||
"""
|
||||
Return the package's loader if it's a ResourceReader.
|
||||
"""
|
||||
# We can't use
|
||||
# a issubclass() check here because apparently abc.'s __subclasscheck__()
|
||||
# hook wants to create a weak reference to the object, but
|
||||
# zipimport.zipimporter does not support weak references, resulting in a
|
||||
# TypeError. That seems terrible.
|
||||
spec = package.__spec__
|
||||
reader = getattr(spec.loader, 'get_resource_reader', None)
|
||||
if reader is None:
|
||||
return None
|
||||
return reader(spec.name)
|
||||
|
||||
|
||||
def resolve(cand):
|
||||
# type: (Package) -> types.ModuleType
|
||||
return (
|
||||
cand if isinstance(cand, types.ModuleType)
|
||||
else importlib.import_module(cand)
|
||||
)
|
||||
|
||||
|
||||
def get_package(package):
|
||||
# type: (Package) -> types.ModuleType
|
||||
"""Take a package name or module object and return the module.
|
||||
|
||||
Raise an exception if the resolved module is not a package.
|
||||
"""
|
||||
resolved = resolve(package)
|
||||
if package_spec(resolved).submodule_search_locations is None:
|
||||
raise TypeError('{!r} is not a package'.format(package))
|
||||
return resolved
|
||||
|
||||
|
||||
def from_package(package):
|
||||
"""
|
||||
@@ -16,27 +80,8 @@ def from_package(package):
|
||||
|
||||
"""
|
||||
spec = package_spec(package)
|
||||
return from_traversable_resources(spec) or fallback_resources(spec)
|
||||
|
||||
|
||||
def from_traversable_resources(spec):
|
||||
"""
|
||||
If the spec.loader implements TraversableResources,
|
||||
directly or implicitly, it will have a ``files()`` method.
|
||||
"""
|
||||
with suppress(AttributeError):
|
||||
return spec.loader.files()
|
||||
|
||||
|
||||
def fallback_resources(spec):
|
||||
package_directory = Path(spec.origin).parent
|
||||
try:
|
||||
archive_path = spec.loader.archive
|
||||
rel_path = package_directory.relative_to(archive_path)
|
||||
return ZipPath(archive_path, str(rel_path) + '/')
|
||||
except Exception:
|
||||
pass
|
||||
return package_directory
|
||||
reader = spec.loader.get_resource_reader(spec.name)
|
||||
return reader.files()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@@ -48,6 +93,7 @@ def _tempfile(reader, suffix=''):
|
||||
try:
|
||||
os.write(fd, reader())
|
||||
os.close(fd)
|
||||
del reader
|
||||
yield Path(raw_path)
|
||||
finally:
|
||||
try:
|
||||
@@ -57,14 +103,12 @@ def _tempfile(reader, suffix=''):
|
||||
|
||||
|
||||
@singledispatch
|
||||
@contextlib.contextmanager
|
||||
def as_file(path):
|
||||
"""
|
||||
Given a Traversable object, return that object as a
|
||||
path on the local file system in a context manager.
|
||||
"""
|
||||
with _tempfile(path.read_bytes, suffix=path.name) as local:
|
||||
yield local
|
||||
return _tempfile(path.read_bytes, suffix=path.name)
|
||||
|
||||
|
||||
@as_file.register(Path)
|
||||
|
||||
+81
-15
@@ -1,16 +1,17 @@
|
||||
from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
# flake8: noqa
|
||||
|
||||
try:
|
||||
if sys.version_info > (3,5):
|
||||
from pathlib import Path, PurePath
|
||||
except ImportError:
|
||||
else:
|
||||
from pathlib2 import Path, PurePath # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
if sys.version_info > (3,):
|
||||
from contextlib import suppress
|
||||
except ImportError:
|
||||
else:
|
||||
from contextlib2 import suppress # type: ignore
|
||||
|
||||
|
||||
@@ -36,9 +37,9 @@ except NameError:
|
||||
|
||||
|
||||
try:
|
||||
from importlib import metadata
|
||||
except ImportError:
|
||||
import importlib_metadata as metadata # type: ignore
|
||||
NotADirectoryError = NotADirectoryError # type: ignore
|
||||
except NameError:
|
||||
NotADirectoryError = OSError # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
@@ -60,14 +61,79 @@ except ImportError:
|
||||
Protocol = ABC # type: ignore
|
||||
|
||||
|
||||
class PackageSpec(object):
|
||||
def __init__(self, **kwargs):
|
||||
vars(self).update(kwargs)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class PackageSpec:
|
||||
def __init__(self, **kwargs):
|
||||
vars(self).update(kwargs)
|
||||
|
||||
|
||||
class TraversableResourcesAdapter:
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
self.loader = LoaderAdapter(spec)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.spec, name)
|
||||
|
||||
|
||||
class LoaderAdapter:
|
||||
"""
|
||||
Adapt loaders to provide TraversableResources and other
|
||||
compatibility.
|
||||
"""
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
# Python < 3
|
||||
return self.spec.origin
|
||||
|
||||
def get_resource_reader(self, name):
|
||||
# Python < 3.9
|
||||
from . import readers
|
||||
|
||||
def _zip_reader(spec):
|
||||
with suppress(AttributeError):
|
||||
return readers.ZipReader(spec.loader, spec.name)
|
||||
|
||||
def _namespace_reader(spec):
|
||||
with suppress(AttributeError, ValueError):
|
||||
return readers.NamespaceReader(spec.submodule_search_locations)
|
||||
|
||||
def _available_reader(spec):
|
||||
with suppress(AttributeError):
|
||||
return spec.loader.get_resource_reader(spec.name)
|
||||
|
||||
def _native_reader(spec):
|
||||
reader = _available_reader(spec)
|
||||
return reader if hasattr(reader, 'files') else None
|
||||
|
||||
return (
|
||||
# native reader if it supplies 'files'
|
||||
_native_reader(self.spec) or
|
||||
# local ZipReader if a zip module
|
||||
_zip_reader(self.spec) or
|
||||
# local NamespaceReader if a namespace module
|
||||
_namespace_reader(self.spec) or
|
||||
# local FileReader
|
||||
readers.FileReader(self)
|
||||
)
|
||||
|
||||
|
||||
def package_spec(package):
|
||||
return getattr(package, '__spec__', None) or \
|
||||
PackageSpec(
|
||||
origin=package.__file__,
|
||||
loader=getattr(package, '__loader__', None),
|
||||
)
|
||||
"""
|
||||
Construct a minimal package spec suitable for
|
||||
matching the interfaces this library relies upon
|
||||
in later Python versions.
|
||||
"""
|
||||
spec = getattr(package, '__spec__', None) or \
|
||||
PackageSpec(
|
||||
origin=package.__file__,
|
||||
loader=getattr(package, '__loader__', None),
|
||||
name=package.__name__,
|
||||
submodule_search_locations=getattr(package, '__path__', None),
|
||||
)
|
||||
return TraversableResourcesAdapter(spec)
|
||||
|
||||
+6
-41
@@ -3,44 +3,13 @@ import errno
|
||||
|
||||
from . import _common
|
||||
from ._compat import FileNotFoundError
|
||||
from importlib import import_module
|
||||
from io import BytesIO, TextIOWrapper, open as io_open
|
||||
|
||||
|
||||
def _resolve(name):
|
||||
"""If name is a string, resolve to a module."""
|
||||
if not isinstance(name, basestring): # noqa: F821
|
||||
return name
|
||||
return import_module(name)
|
||||
|
||||
|
||||
def _get_package(package):
|
||||
"""Normalize a path by ensuring it is a string.
|
||||
|
||||
If the resulting string contains path separators, an exception is raised.
|
||||
"""
|
||||
module = _resolve(package)
|
||||
if not hasattr(module, '__path__'):
|
||||
raise TypeError("{!r} is not a package".format(package))
|
||||
return module
|
||||
|
||||
|
||||
def _normalize_path(path):
|
||||
"""Normalize a path by ensuring it is a string.
|
||||
|
||||
If the resulting string contains path separators, an exception is raised.
|
||||
"""
|
||||
str_path = str(path)
|
||||
parent, file_name = os.path.split(str_path)
|
||||
if parent:
|
||||
raise ValueError("{!r} must be only a file name".format(path))
|
||||
return file_name
|
||||
|
||||
|
||||
def open_binary(package, resource):
|
||||
"""Return a file-like object opened for binary reading of the resource."""
|
||||
resource = _normalize_path(resource)
|
||||
package = _get_package(package)
|
||||
resource = _common.normalize_path(resource)
|
||||
package = _common.get_package(package)
|
||||
# Using pathlib doesn't work well here due to the lack of 'strict' argument
|
||||
# for pathlib.Path.resolve() prior to Python 3.6.
|
||||
package_path = os.path.dirname(package.__file__)
|
||||
@@ -89,10 +58,6 @@ def read_text(package, resource, encoding='utf-8', errors='strict'):
|
||||
return fp.read()
|
||||
|
||||
|
||||
def files(package):
|
||||
return _common.from_package(_get_package(package))
|
||||
|
||||
|
||||
def path(package, resource):
|
||||
"""A context manager providing a file path object to the resource.
|
||||
|
||||
@@ -102,7 +67,7 @@ def path(package, resource):
|
||||
raised if the file was deleted prior to the context manager
|
||||
exiting).
|
||||
"""
|
||||
path = files(package).joinpath(_normalize_path(resource))
|
||||
path = _common.files(package).joinpath(_common.normalize_path(resource))
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(path)
|
||||
return _common.as_file(path)
|
||||
@@ -113,8 +78,8 @@ def is_resource(package, name):
|
||||
|
||||
Directories are *not* resources.
|
||||
"""
|
||||
package = _get_package(package)
|
||||
_normalize_path(name)
|
||||
package = _common.get_package(package)
|
||||
_common.normalize_path(name)
|
||||
try:
|
||||
package_contents = set(contents(package))
|
||||
except OSError as error:
|
||||
@@ -138,5 +103,5 @@ def contents(package):
|
||||
not considered resources. Use `is_resource()` on each entry returned here
|
||||
to check if it is a resource or not.
|
||||
"""
|
||||
package = _get_package(package)
|
||||
package = _common.get_package(package)
|
||||
return list(item.name for item in _common.from_package(package).iterdir())
|
||||
|
||||
+66
-109
@@ -1,10 +1,8 @@
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
|
||||
from . import abc as resources_abc
|
||||
from . import _common
|
||||
from contextlib import contextmanager, suppress
|
||||
from importlib import import_module
|
||||
from contextlib import suppress
|
||||
from importlib.abc import ResourceLoader
|
||||
from io import BytesIO, TextIOWrapper
|
||||
from pathlib import Path
|
||||
@@ -12,92 +10,48 @@ from types import ModuleType
|
||||
from typing import Iterable, Iterator, Optional, Set, Union # noqa: F401
|
||||
from typing import cast
|
||||
from typing.io import BinaryIO, TextIO
|
||||
from collections.abc import Sequence
|
||||
from functools import singledispatch
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
from typing import ContextManager
|
||||
|
||||
Package = Union[ModuleType, str]
|
||||
if sys.version_info >= (3, 6):
|
||||
Resource = Union[str, os.PathLike] # pragma: <=35
|
||||
else:
|
||||
Resource = str # pragma: >=36
|
||||
|
||||
|
||||
def _resolve(name) -> ModuleType:
|
||||
"""If name is a string, resolve to a module."""
|
||||
if hasattr(name, '__spec__'):
|
||||
return name
|
||||
return import_module(name)
|
||||
|
||||
|
||||
def _get_package(package) -> ModuleType:
|
||||
"""Take a package name or module object and return the module.
|
||||
|
||||
If a name, the module is imported. If the resolved module
|
||||
object is not a package, raise an exception.
|
||||
"""
|
||||
module = _resolve(package)
|
||||
if module.__spec__.submodule_search_locations is None:
|
||||
raise TypeError('{!r} is not a package'.format(package))
|
||||
return module
|
||||
|
||||
|
||||
def _normalize_path(path) -> str:
|
||||
"""Normalize a path by ensuring it is a string.
|
||||
|
||||
If the resulting string contains path separators, an exception is raised.
|
||||
"""
|
||||
str_path = str(path)
|
||||
parent, file_name = os.path.split(str_path)
|
||||
if parent:
|
||||
raise ValueError('{!r} must be only a file name'.format(path))
|
||||
return file_name
|
||||
|
||||
|
||||
def _get_resource_reader(
|
||||
package: ModuleType) -> Optional[resources_abc.ResourceReader]:
|
||||
# Return the package's loader if it's a ResourceReader. We can't use
|
||||
# a issubclass() check here because apparently abc.'s __subclasscheck__()
|
||||
# hook wants to create a weak reference to the object, but
|
||||
# zipimport.zipimporter does not support weak references, resulting in a
|
||||
# TypeError. That seems terrible.
|
||||
spec = package.__spec__
|
||||
reader = getattr(spec.loader, 'get_resource_reader', None)
|
||||
if reader is None:
|
||||
return None
|
||||
return cast(resources_abc.ResourceReader, reader(spec.name))
|
||||
Resource = Union[str, os.PathLike]
|
||||
|
||||
|
||||
def open_binary(package: Package, resource: Resource) -> BinaryIO:
|
||||
"""Return a file-like object opened for binary reading of the resource."""
|
||||
resource = _normalize_path(resource)
|
||||
package = _get_package(package)
|
||||
reader = _get_resource_reader(package)
|
||||
resource = _common.normalize_path(resource)
|
||||
package = _common.get_package(package)
|
||||
reader = _common.get_resource_reader(package)
|
||||
if reader is not None:
|
||||
return reader.open_resource(resource)
|
||||
# Using pathlib doesn't work well here due to the lack of 'strict'
|
||||
# argument for pathlib.Path.resolve() prior to Python 3.6.
|
||||
absolute_package_path = os.path.abspath(
|
||||
package.__spec__.origin or 'non-existent file')
|
||||
package_path = os.path.dirname(absolute_package_path)
|
||||
full_path = os.path.join(package_path, resource)
|
||||
try:
|
||||
return open(full_path, mode='rb')
|
||||
except OSError:
|
||||
# Just assume the loader is a resource loader; all the relevant
|
||||
# importlib.machinery loaders are and an AttributeError for
|
||||
# get_data() will make it clear what is needed from the loader.
|
||||
loader = cast(ResourceLoader, package.__spec__.loader)
|
||||
data = None
|
||||
if hasattr(package.__spec__.loader, 'get_data'):
|
||||
with suppress(OSError):
|
||||
data = loader.get_data(full_path)
|
||||
if data is None:
|
||||
package_name = package.__spec__.name
|
||||
message = '{!r} resource not found in {!r}'.format(
|
||||
resource, package_name)
|
||||
raise FileNotFoundError(message)
|
||||
return BytesIO(data)
|
||||
if package.__spec__.submodule_search_locations is not None:
|
||||
paths = package.__spec__.submodule_search_locations
|
||||
elif package.__spec__.origin is not None:
|
||||
paths = [os.path.dirname(os.path.abspath(package.__spec__.origin))]
|
||||
|
||||
for package_path in paths:
|
||||
full_path = os.path.join(package_path, resource)
|
||||
try:
|
||||
return open(full_path, mode='rb')
|
||||
except OSError:
|
||||
# Just assume the loader is a resource loader; all the relevant
|
||||
# importlib.machinery loaders are and an AttributeError for
|
||||
# get_data() will make it clear what is needed from the loader.
|
||||
loader = cast(ResourceLoader, package.__spec__.loader)
|
||||
data = None
|
||||
if hasattr(package.__spec__.loader, 'get_data'):
|
||||
with suppress(OSError):
|
||||
data = loader.get_data(full_path)
|
||||
if data is not None:
|
||||
return BytesIO(data)
|
||||
|
||||
raise FileNotFoundError('{!r} resource not found in {!r}'.format(
|
||||
resource, package.__spec__.name))
|
||||
|
||||
|
||||
def open_text(package: Package,
|
||||
@@ -128,13 +82,6 @@ def read_text(package: Package,
|
||||
return fp.read()
|
||||
|
||||
|
||||
def files(package: Package) -> resources_abc.Traversable:
|
||||
"""
|
||||
Get a Traversable resource from a package
|
||||
"""
|
||||
return _common.from_package(_get_package(package))
|
||||
|
||||
|
||||
def path(
|
||||
package: Package, resource: Resource,
|
||||
) -> 'ContextManager[Path]':
|
||||
@@ -146,23 +93,28 @@ def path(
|
||||
raised if the file was deleted prior to the context manager
|
||||
exiting).
|
||||
"""
|
||||
reader = _get_resource_reader(_get_package(package))
|
||||
reader = _common.get_resource_reader(_common.get_package(package))
|
||||
return (
|
||||
_path_from_reader(reader, resource)
|
||||
_path_from_reader(reader, _common.normalize_path(resource))
|
||||
if reader else
|
||||
_common.as_file(files(package).joinpath(_normalize_path(resource)))
|
||||
_common.as_file(
|
||||
_common.files(package).joinpath(_common.normalize_path(resource)))
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _path_from_reader(reader, resource):
|
||||
norm_resource = _normalize_path(resource)
|
||||
return _path_from_resource_path(reader, resource) or \
|
||||
_path_from_open_resource(reader, resource)
|
||||
|
||||
|
||||
def _path_from_resource_path(reader, resource):
|
||||
with suppress(FileNotFoundError):
|
||||
yield Path(reader.resource_path(norm_resource))
|
||||
return
|
||||
opener_reader = reader.open_resource(norm_resource)
|
||||
with _common._tempfile(opener_reader.read, suffix=norm_resource) as res:
|
||||
yield res
|
||||
return Path(reader.resource_path(resource))
|
||||
|
||||
|
||||
def _path_from_open_resource(reader, resource):
|
||||
saved = io.BytesIO(reader.open_resource(resource).read())
|
||||
return _common._tempfile(saved.read, suffix=resource)
|
||||
|
||||
|
||||
def is_resource(package: Package, name: str) -> bool:
|
||||
@@ -170,9 +122,9 @@ def is_resource(package: Package, name: str) -> bool:
|
||||
|
||||
Directories are *not* resources.
|
||||
"""
|
||||
package = _get_package(package)
|
||||
_normalize_path(name)
|
||||
reader = _get_resource_reader(package)
|
||||
package = _common.get_package(package)
|
||||
_common.normalize_path(name)
|
||||
reader = _common.get_resource_reader(package)
|
||||
if reader is not None:
|
||||
return reader.is_resource(name)
|
||||
package_contents = set(contents(package))
|
||||
@@ -188,16 +140,21 @@ def contents(package: Package) -> Iterable[str]:
|
||||
not considered resources. Use `is_resource()` on each entry returned here
|
||||
to check if it is a resource or not.
|
||||
"""
|
||||
package = _get_package(package)
|
||||
reader = _get_resource_reader(package)
|
||||
package = _common.get_package(package)
|
||||
reader = _common.get_resource_reader(package)
|
||||
if reader is not None:
|
||||
return reader.contents()
|
||||
# Is the package a namespace package? By definition, namespace packages
|
||||
# cannot have resources.
|
||||
namespace = (
|
||||
package.__spec__.origin is None or
|
||||
package.__spec__.origin == 'namespace'
|
||||
)
|
||||
if namespace or not package.__spec__.has_location:
|
||||
return ()
|
||||
return list(item.name for item in _common.from_package(package).iterdir())
|
||||
return _ensure_sequence(reader.contents())
|
||||
transversable = _common.from_package(package)
|
||||
if transversable.is_dir():
|
||||
return list(item.name for item in transversable.iterdir())
|
||||
return []
|
||||
|
||||
|
||||
@singledispatch
|
||||
def _ensure_sequence(iterable):
|
||||
return list(iterable)
|
||||
|
||||
|
||||
@_ensure_sequence.register(Sequence)
|
||||
def _(iterable):
|
||||
return iterable
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ class TraversableResources(ResourceReader):
|
||||
raise FileNotFoundError(resource)
|
||||
|
||||
def is_resource(self, path):
|
||||
return self.files().joinpath(path).isfile()
|
||||
return self.files().joinpath(path).is_file()
|
||||
|
||||
def contents(self):
|
||||
return (item.name for item in self.files().iterdir())
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import os.path
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from . import abc
|
||||
|
||||
from ._compat import Path, ZipPath
|
||||
from ._compat import FileNotFoundError, NotADirectoryError
|
||||
|
||||
|
||||
class FileReader(abc.TraversableResources):
|
||||
def __init__(self, loader):
|
||||
self.path = Path(loader.path).parent
|
||||
|
||||
def resource_path(self, resource):
|
||||
"""
|
||||
Return the file system path to prevent
|
||||
`resources.path()` from creating a temporary
|
||||
copy.
|
||||
"""
|
||||
return str(self.path.joinpath(resource))
|
||||
|
||||
def files(self):
|
||||
return self.path
|
||||
|
||||
|
||||
class ZipReader(abc.TraversableResources):
|
||||
def __init__(self, loader, module):
|
||||
_, _, name = module.rpartition('.')
|
||||
self.prefix = loader.prefix.replace('\\', '/') + name + '/'
|
||||
self.archive = loader.archive
|
||||
|
||||
def open_resource(self, resource):
|
||||
try:
|
||||
return super().open_resource(resource)
|
||||
except KeyError as exc:
|
||||
raise FileNotFoundError(exc.args[0])
|
||||
|
||||
def is_resource(self, path):
|
||||
# workaround for `zipfile.Path.is_file` returning true
|
||||
# for non-existent paths.
|
||||
target = self.files().joinpath(path)
|
||||
return target.is_file() and target.exists()
|
||||
|
||||
def files(self):
|
||||
return ZipPath(self.archive, self.prefix)
|
||||
|
||||
|
||||
class MultiplexedPath(abc.Traversable):
|
||||
"""
|
||||
Given a series of Traversable objects, implement a merged
|
||||
version of the interface across all objects. Useful for
|
||||
namespace packages which may be multihomed at a single
|
||||
name.
|
||||
"""
|
||||
def __init__(self, *paths):
|
||||
paths = list(OrderedDict.fromkeys(paths)) # remove duplicates
|
||||
self._paths = list(map(Path, paths))
|
||||
if not self._paths:
|
||||
message = 'MultiplexedPath must contain at least one path'
|
||||
raise FileNotFoundError(message)
|
||||
if any(not path.is_dir() for path in self._paths):
|
||||
raise NotADirectoryError(
|
||||
'MultiplexedPath only supports directories')
|
||||
|
||||
def iterdir(self):
|
||||
visited = []
|
||||
for path in self._paths:
|
||||
for file in path.iterdir():
|
||||
if file.name in visited:
|
||||
continue
|
||||
visited.append(file.name)
|
||||
yield file
|
||||
|
||||
def read_bytes(self):
|
||||
raise FileNotFoundError('{} is not a file'.format(self))
|
||||
|
||||
def read_text(self, *args, **kwargs):
|
||||
raise FileNotFoundError('{} is not a file'.format(self))
|
||||
|
||||
def is_dir(self):
|
||||
return True
|
||||
|
||||
def is_file(self):
|
||||
return False
|
||||
|
||||
def joinpath(self, child):
|
||||
# first try to find child in current paths
|
||||
for file in self.iterdir():
|
||||
if file.name == child:
|
||||
return file
|
||||
# if it does not exist, construct it with the first path
|
||||
return self._paths[0] / child
|
||||
|
||||
__truediv__ = joinpath
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
raise FileNotFoundError('{} is not a file'.format(self))
|
||||
|
||||
def name(self):
|
||||
return os.path.basename(self._paths[0])
|
||||
|
||||
def __repr__(self):
|
||||
return 'MultiplexedPath({})'.format(
|
||||
', '.join("'{}'".format(path) for path in self._paths))
|
||||
|
||||
|
||||
class NamespaceReader(abc.TraversableResources):
|
||||
def __init__(self, namespace_path):
|
||||
if 'NamespacePath' not in str(namespace_path):
|
||||
raise ValueError('Invalid path')
|
||||
self.path = MultiplexedPath(*list(namespace_path))
|
||||
|
||||
def resource_path(self, resource):
|
||||
"""
|
||||
Return the file system path to prevent
|
||||
`resources.path()` from creating a temporary
|
||||
copy.
|
||||
"""
|
||||
return str(self.path.joinpath(resource))
|
||||
|
||||
def files(self):
|
||||
return self.path
|
||||
Vendored
+2
-2
@@ -18,10 +18,10 @@ __title__ = "packaging"
|
||||
__summary__ = "Core utilities for Python packages"
|
||||
__uri__ = "https://github.com/pypa/packaging"
|
||||
|
||||
__version__ = "20.3"
|
||||
__version__ = "20.4"
|
||||
|
||||
__author__ = "Donald Stufft and individual contributors"
|
||||
__email__ = "donald@stufft.io"
|
||||
|
||||
__license__ = "BSD or Apache License, Version 2.0"
|
||||
__license__ = "BSD-2-Clause or Apache-2.0"
|
||||
__copyright__ = "Copyright 2014-2019 %s" % __author__
|
||||
|
||||
Vendored
+2
-2
@@ -5,9 +5,9 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
import sys
|
||||
|
||||
from ._typing import MYPY_CHECK_RUNNING
|
||||
from ._typing import TYPE_CHECKING
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import Any, Dict, Tuple, Type
|
||||
|
||||
|
||||
|
||||
Vendored
+19
-10
@@ -18,22 +18,31 @@ curious maintainer can reach here to read this.
|
||||
|
||||
In packaging, all static-typing related imports should be guarded as follows:
|
||||
|
||||
from packaging._typing import MYPY_CHECK_RUNNING
|
||||
from packaging._typing import TYPE_CHECKING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
if TYPE_CHECKING:
|
||||
from typing import ...
|
||||
|
||||
Ref: https://github.com/python/mypy/issues/3216
|
||||
"""
|
||||
|
||||
MYPY_CHECK_RUNNING = False
|
||||
__all__ = ["TYPE_CHECKING", "cast"]
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
import typing
|
||||
|
||||
cast = typing.cast
|
||||
# The TYPE_CHECKING constant defined by the typing module is False at runtime
|
||||
# but True while type checking.
|
||||
if False: # pragma: no cover
|
||||
from typing import TYPE_CHECKING
|
||||
else:
|
||||
# typing's cast() is needed at runtime, but we don't want to import typing.
|
||||
# Thus, we use a dummy no-op version, which we tell mypy to ignore.
|
||||
def cast(type_, value): # type: ignore
|
||||
TYPE_CHECKING = False
|
||||
|
||||
# typing's cast syntax requires calling typing.cast at runtime, but we don't
|
||||
# want to import typing at runtime. Here, we inform the type checkers that
|
||||
# we're importing `typing.cast` as `cast` and re-implement typing.cast's
|
||||
# runtime behavior in a block that is ignored by type checkers.
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
# not executed at runtime
|
||||
from typing import cast
|
||||
else:
|
||||
# executed at runtime
|
||||
def cast(type_, value): # noqa
|
||||
return value
|
||||
|
||||
Vendored
+2
-2
@@ -13,10 +13,10 @@ from pyparsing import ZeroOrMore, Group, Forward, QuotedString
|
||||
from pyparsing import Literal as L # noqa
|
||||
|
||||
from ._compat import string_types
|
||||
from ._typing import MYPY_CHECK_RUNNING
|
||||
from ._typing import TYPE_CHECKING
|
||||
from .specifiers import Specifier, InvalidSpecifier
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
Operator = Callable[[str, str], bool]
|
||||
|
||||
+2
-2
@@ -11,11 +11,11 @@ from pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
|
||||
from pyparsing import Literal as L # noqa
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
from ._typing import MYPY_CHECK_RUNNING
|
||||
from ._typing import TYPE_CHECKING
|
||||
from .markers import MARKER_EXPR, Marker
|
||||
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import List
|
||||
|
||||
|
||||
|
||||
+20
-6
@@ -9,10 +9,11 @@ import itertools
|
||||
import re
|
||||
|
||||
from ._compat import string_types, with_metaclass
|
||||
from ._typing import MYPY_CHECK_RUNNING
|
||||
from ._typing import TYPE_CHECKING
|
||||
from .utils import canonicalize_version
|
||||
from .version import Version, LegacyVersion, parse
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import (
|
||||
List,
|
||||
Dict,
|
||||
@@ -132,9 +133,14 @@ class _IndividualSpecifier(BaseSpecifier):
|
||||
# type: () -> str
|
||||
return "{0}{1}".format(*self._spec)
|
||||
|
||||
@property
|
||||
def _canonical_spec(self):
|
||||
# type: () -> Tuple[str, Union[Version, str]]
|
||||
return self._spec[0], canonicalize_version(self._spec[1])
|
||||
|
||||
def __hash__(self):
|
||||
# type: () -> int
|
||||
return hash(self._spec)
|
||||
return hash(self._canonical_spec)
|
||||
|
||||
def __eq__(self, other):
|
||||
# type: (object) -> bool
|
||||
@@ -146,7 +152,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
||||
elif not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self._spec == other._spec
|
||||
return self._canonical_spec == other._canonical_spec
|
||||
|
||||
def __ne__(self, other):
|
||||
# type: (object) -> bool
|
||||
@@ -510,12 +516,20 @@ class Specifier(_IndividualSpecifier):
|
||||
@_require_version_compare
|
||||
def _compare_less_than_equal(self, prospective, spec):
|
||||
# type: (ParsedVersion, str) -> bool
|
||||
return prospective <= Version(spec)
|
||||
|
||||
# NB: Local version identifiers are NOT permitted in the version
|
||||
# specifier, so local version labels can be universally removed from
|
||||
# the prospective version.
|
||||
return Version(prospective.public) <= Version(spec)
|
||||
|
||||
@_require_version_compare
|
||||
def _compare_greater_than_equal(self, prospective, spec):
|
||||
# type: (ParsedVersion, str) -> bool
|
||||
return prospective >= Version(spec)
|
||||
|
||||
# NB: Local version identifiers are NOT permitted in the version
|
||||
# specifier, so local version labels can be universally removed from
|
||||
# the prospective version.
|
||||
return Version(prospective.public) >= Version(spec)
|
||||
|
||||
@_require_version_compare
|
||||
def _compare_less_than(self, prospective, spec_str):
|
||||
|
||||
Vendored
+15
-3
@@ -22,9 +22,9 @@ import sys
|
||||
import sysconfig
|
||||
import warnings
|
||||
|
||||
from ._typing import MYPY_CHECK_RUNNING, cast
|
||||
from ._typing import TYPE_CHECKING, cast
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import (
|
||||
Dict,
|
||||
FrozenSet,
|
||||
@@ -58,6 +58,12 @@ _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
|
||||
|
||||
|
||||
class Tag(object):
|
||||
"""
|
||||
A representation of the tag triple for a wheel.
|
||||
|
||||
Instances are considered immutable and thus are hashable. Equality checking
|
||||
is also supported.
|
||||
"""
|
||||
|
||||
__slots__ = ["_interpreter", "_abi", "_platform"]
|
||||
|
||||
@@ -108,6 +114,12 @@ class Tag(object):
|
||||
|
||||
def parse_tag(tag):
|
||||
# type: (str) -> FrozenSet[Tag]
|
||||
"""
|
||||
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
|
||||
|
||||
Returning a set is required due to the possibility that the tag is a
|
||||
compressed tag set.
|
||||
"""
|
||||
tags = set()
|
||||
interpreters, abis, platforms = tag.split("-")
|
||||
for interpreter in interpreters.split("."):
|
||||
@@ -541,7 +553,7 @@ class _ELFFileHeader(object):
|
||||
def unpack(fmt):
|
||||
# type: (str) -> int
|
||||
try:
|
||||
result, = struct.unpack(
|
||||
(result,) = struct.unpack(
|
||||
fmt, file.read(struct.calcsize(fmt))
|
||||
) # type: (int, )
|
||||
except struct.error:
|
||||
|
||||
Vendored
+8
-5
@@ -5,19 +5,22 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
import re
|
||||
|
||||
from ._typing import MYPY_CHECK_RUNNING
|
||||
from ._typing import TYPE_CHECKING, cast
|
||||
from .version import InvalidVersion, Version
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
from typing import Union
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import NewType, Union
|
||||
|
||||
NormalizedName = NewType("NormalizedName", str)
|
||||
|
||||
_canonicalize_regex = re.compile(r"[-_.]+")
|
||||
|
||||
|
||||
def canonicalize_name(name):
|
||||
# type: (str) -> str
|
||||
# type: (str) -> NormalizedName
|
||||
# This is taken from PEP 503.
|
||||
return _canonicalize_regex.sub("-", name).lower()
|
||||
value = _canonicalize_regex.sub("-", name).lower()
|
||||
return cast("NormalizedName", value)
|
||||
|
||||
|
||||
def canonicalize_version(_version):
|
||||
|
||||
Vendored
+2
-2
@@ -8,9 +8,9 @@ import itertools
|
||||
import re
|
||||
|
||||
from ._structures import Infinity, NegativeInfinity
|
||||
from ._typing import MYPY_CHECK_RUNNING
|
||||
from ._typing import TYPE_CHECKING
|
||||
|
||||
if MYPY_CHECK_RUNNING: # pragma: no cover
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
|
||||
|
||||
from ._structures import InfinityType, NegativeInfinityType
|
||||
|
||||
Vendored
+362
-237
@@ -9,30 +9,38 @@ and ``with_pattern()`` when ``import \*`` is used:
|
||||
|
||||
From there it's a simple thing to parse a string:
|
||||
|
||||
>>> parse("It's {}, I love it!", "It's spam, I love it!")
|
||||
<Result ('spam',) {}>
|
||||
>>> _[0]
|
||||
'spam'
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> parse("It's {}, I love it!", "It's spam, I love it!")
|
||||
<Result ('spam',) {}>
|
||||
>>> _[0]
|
||||
'spam'
|
||||
|
||||
Or to search a string for some pattern:
|
||||
|
||||
>>> search('Age: {:d}\n', 'Name: Rufus\nAge: 42\nColor: red\n')
|
||||
<Result (42,) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> search('Age: {:d}\n', 'Name: Rufus\nAge: 42\nColor: red\n')
|
||||
<Result (42,) {}>
|
||||
|
||||
Or find all the occurrences of some pattern in a string:
|
||||
|
||||
>>> ''.join(r[0] for r in findall(">{}<", "<p>the <b>bold</b> text</p>"))
|
||||
'the bold text'
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> ''.join(r[0] for r in findall(">{}<", "<p>the <b>bold</b> text</p>"))
|
||||
'the bold text'
|
||||
|
||||
If you're going to use the same pattern to match lots of strings you can
|
||||
compile it once:
|
||||
|
||||
>>> from parse import compile
|
||||
>>> p = compile("It's {}, I love it!")
|
||||
>>> print(p)
|
||||
<Parser "It's {}, I love it!">
|
||||
>>> p.parse("It's spam, I love it!")
|
||||
<Result ('spam',) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from parse import compile
|
||||
>>> p = compile("It's {}, I love it!")
|
||||
>>> print(p)
|
||||
<Parser "It's {}, I love it!">
|
||||
>>> p.parse("It's spam, I love it!")
|
||||
<Result ('spam',) {}>
|
||||
|
||||
("compile" is not exported for ``import *`` usage as it would override the
|
||||
built-in ``compile()`` function)
|
||||
@@ -40,8 +48,10 @@ built-in ``compile()`` function)
|
||||
The default behaviour is to match strings case insensitively. You may match with
|
||||
case by specifying `case_sensitive=True`:
|
||||
|
||||
>>> parse('SPAM', 'spam', case_sensitive=True) is None
|
||||
True
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> parse('SPAM', 'spam', case_sensitive=True) is None
|
||||
True
|
||||
|
||||
|
||||
Format Syntax
|
||||
@@ -64,40 +74,44 @@ There are no "!" field conversions like ``format()`` has.
|
||||
|
||||
Some simple parse() format string examples:
|
||||
|
||||
>>> parse("Bring me a {}", "Bring me a shrubbery")
|
||||
<Result ('shrubbery',) {}>
|
||||
>>> r = parse("The {} who say {}", "The knights who say Ni!")
|
||||
>>> print(r)
|
||||
<Result ('knights', 'Ni!') {}>
|
||||
>>> print(r.fixed)
|
||||
('knights', 'Ni!')
|
||||
>>> r = parse("Bring out the holy {item}", "Bring out the holy hand grenade")
|
||||
>>> print(r)
|
||||
<Result () {'item': 'hand grenade'}>
|
||||
>>> print(r.named)
|
||||
{'item': 'hand grenade'}
|
||||
>>> print(r['item'])
|
||||
hand grenade
|
||||
>>> 'item' in r
|
||||
True
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> parse("Bring me a {}", "Bring me a shrubbery")
|
||||
<Result ('shrubbery',) {}>
|
||||
>>> r = parse("The {} who say {}", "The knights who say Ni!")
|
||||
>>> print(r)
|
||||
<Result ('knights', 'Ni!') {}>
|
||||
>>> print(r.fixed)
|
||||
('knights', 'Ni!')
|
||||
>>> r = parse("Bring out the holy {item}", "Bring out the holy hand grenade")
|
||||
>>> print(r)
|
||||
<Result () {'item': 'hand grenade'}>
|
||||
>>> print(r.named)
|
||||
{'item': 'hand grenade'}
|
||||
>>> print(r['item'])
|
||||
hand grenade
|
||||
>>> 'item' in r
|
||||
True
|
||||
|
||||
Note that `in` only works if you have named fields. Dotted names and indexes
|
||||
are possible though the application must make additional sense of the result:
|
||||
|
||||
>>> r = parse("Mmm, {food.type}, I love it!", "Mmm, spam, I love it!")
|
||||
>>> print(r)
|
||||
<Result () {'food.type': 'spam'}>
|
||||
>>> print(r.named)
|
||||
{'food.type': 'spam'}
|
||||
>>> print(r['food.type'])
|
||||
spam
|
||||
>>> r = parse("My quest is {quest[name]}", "My quest is to seek the holy grail!")
|
||||
>>> print(r)
|
||||
<Result () {'quest': {'name': 'to seek the holy grail!'}}>
|
||||
>>> print(r['quest'])
|
||||
{'name': 'to seek the holy grail!'}
|
||||
>>> print(r['quest']['name'])
|
||||
to seek the holy grail!
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> r = parse("Mmm, {food.type}, I love it!", "Mmm, spam, I love it!")
|
||||
>>> print(r)
|
||||
<Result () {'food.type': 'spam'}>
|
||||
>>> print(r.named)
|
||||
{'food.type': 'spam'}
|
||||
>>> print(r['food.type'])
|
||||
spam
|
||||
>>> r = parse("My quest is {quest[name]}", "My quest is to seek the holy grail!")
|
||||
>>> print(r)
|
||||
<Result () {'quest': {'name': 'to seek the holy grail!'}}>
|
||||
>>> print(r['quest'])
|
||||
{'name': 'to seek the holy grail!'}
|
||||
>>> print(r['quest']['name'])
|
||||
to seek the holy grail!
|
||||
|
||||
If the text you're matching has braces in it you can match those by including
|
||||
a double-brace ``{{`` or ``}}`` in your format string, just like format() does.
|
||||
@@ -174,18 +188,22 @@ tt Time time
|
||||
Some examples of typed parsing with ``None`` returned if the typing
|
||||
does not match:
|
||||
|
||||
>>> parse('Our {:d} {:w} are...', 'Our 3 weapons are...')
|
||||
<Result (3, 'weapons') {}>
|
||||
>>> parse('Our {:d} {:w} are...', 'Our three weapons are...')
|
||||
>>> parse('Meet at {:tg}', 'Meet at 1/2/2011 11:00 PM')
|
||||
<Result (datetime.datetime(2011, 2, 1, 23, 0),) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> parse('Our {:d} {:w} are...', 'Our 3 weapons are...')
|
||||
<Result (3, 'weapons') {}>
|
||||
>>> parse('Our {:d} {:w} are...', 'Our three weapons are...')
|
||||
>>> parse('Meet at {:tg}', 'Meet at 1/2/2011 11:00 PM')
|
||||
<Result (datetime.datetime(2011, 2, 1, 23, 0),) {}>
|
||||
|
||||
And messing about with alignment:
|
||||
|
||||
>>> parse('with {:>} herring', 'with a herring')
|
||||
<Result ('a',) {}>
|
||||
>>> parse('spam {:^} spam', 'spam lovely spam')
|
||||
<Result ('lovely',) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> parse('with {:>} herring', 'with a herring')
|
||||
<Result ('a',) {}>
|
||||
>>> parse('spam {:^} spam', 'spam lovely spam')
|
||||
<Result ('lovely',) {}>
|
||||
|
||||
Note that the "center" alignment does not test to make sure the value is
|
||||
centered - it just strips leading and trailing whitespace.
|
||||
@@ -194,14 +212,16 @@ Width and precision may be used to restrict the size of matched text
|
||||
from the input. Width specifies a minimum size and precision specifies
|
||||
a maximum. For example:
|
||||
|
||||
>>> parse('{:.2}{:.2}', 'look') # specifying precision
|
||||
<Result ('lo', 'ok') {}>
|
||||
>>> parse('{:4}{:4}', 'look at that') # specifying width
|
||||
<Result ('look', 'at that') {}>
|
||||
>>> parse('{:4}{:.4}', 'look at that') # specifying both
|
||||
<Result ('look at ', 'that') {}>
|
||||
>>> parse('{:2d}{:2d}', '0440') # parsing two contiguous numbers
|
||||
<Result (4, 40) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> parse('{:.2}{:.2}', 'look') # specifying precision
|
||||
<Result ('lo', 'ok') {}>
|
||||
>>> parse('{:4}{:4}', 'look at that') # specifying width
|
||||
<Result ('look', 'at that') {}>
|
||||
>>> parse('{:4}{:.4}', 'look at that') # specifying both
|
||||
<Result ('look at ', 'that') {}>
|
||||
>>> parse('{:2d}{:2d}', '0440') # parsing two contiguous numbers
|
||||
<Result (4, 40) {}>
|
||||
|
||||
Some notes for the date and time types:
|
||||
|
||||
@@ -246,18 +266,18 @@ The result of a ``parse()`` and ``search()`` operation is either ``None`` (no ma
|
||||
|
||||
The ``Result`` instance has three attributes:
|
||||
|
||||
fixed
|
||||
``fixed``
|
||||
A tuple of the fixed-position, anonymous fields extracted from the input.
|
||||
named
|
||||
``named``
|
||||
A dictionary of the named fields extracted from the input.
|
||||
spans
|
||||
``spans``
|
||||
A dictionary mapping the names and fixed position indices matched to a
|
||||
2-tuple slice range of where the match occurred in the input.
|
||||
The span does not include any stripped padding (alignment or width).
|
||||
|
||||
The ``Match`` instance has one method:
|
||||
|
||||
evaluate_result()
|
||||
``evaluate_result()``
|
||||
Generates and returns a ``Result`` instance for this ``Match`` object.
|
||||
|
||||
|
||||
@@ -273,56 +293,66 @@ The converter will be passed the field string matched. Whatever it returns
|
||||
will be substituted in the ``Result`` instance for that field.
|
||||
|
||||
Your custom type conversions may override the builtin types if you supply one
|
||||
with the same identifier.
|
||||
with the same identifier:
|
||||
|
||||
>>> def shouty(string):
|
||||
... return string.upper()
|
||||
...
|
||||
>>> parse('{:shouty} world', 'hello world', dict(shouty=shouty))
|
||||
<Result ('HELLO',) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> def shouty(string):
|
||||
... return string.upper()
|
||||
...
|
||||
>>> parse('{:shouty} world', 'hello world', dict(shouty=shouty))
|
||||
<Result ('HELLO',) {}>
|
||||
|
||||
If the type converter has the optional ``pattern`` attribute, it is used as
|
||||
regular expression for better pattern matching (instead of the default one).
|
||||
regular expression for better pattern matching (instead of the default one):
|
||||
|
||||
>>> def parse_number(text):
|
||||
... return int(text)
|
||||
>>> parse_number.pattern = r'\d+'
|
||||
>>> parse('Answer: {number:Number}', 'Answer: 42', dict(Number=parse_number))
|
||||
<Result () {'number': 42}>
|
||||
>>> _ = parse('Answer: {:Number}', 'Answer: Alice', dict(Number=parse_number))
|
||||
>>> assert _ is None, "MISMATCH"
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> def parse_number(text):
|
||||
... return int(text)
|
||||
>>> parse_number.pattern = r'\d+'
|
||||
>>> parse('Answer: {number:Number}', 'Answer: 42', dict(Number=parse_number))
|
||||
<Result () {'number': 42}>
|
||||
>>> _ = parse('Answer: {:Number}', 'Answer: Alice', dict(Number=parse_number))
|
||||
>>> assert _ is None, "MISMATCH"
|
||||
|
||||
You can also use the ``with_pattern(pattern)`` decorator to add this
|
||||
information to a type converter function:
|
||||
|
||||
>>> from parse import with_pattern
|
||||
>>> @with_pattern(r'\d+')
|
||||
... def parse_number(text):
|
||||
... return int(text)
|
||||
>>> parse('Answer: {number:Number}', 'Answer: 42', dict(Number=parse_number))
|
||||
<Result () {'number': 42}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from parse import with_pattern
|
||||
>>> @with_pattern(r'\d+')
|
||||
... def parse_number(text):
|
||||
... return int(text)
|
||||
>>> parse('Answer: {number:Number}', 'Answer: 42', dict(Number=parse_number))
|
||||
<Result () {'number': 42}>
|
||||
|
||||
A more complete example of a custom type might be:
|
||||
|
||||
>>> yesno_mapping = {
|
||||
... "yes": True, "no": False,
|
||||
... "on": True, "off": False,
|
||||
... "true": True, "false": False,
|
||||
... }
|
||||
>>> @with_pattern(r"|".join(yesno_mapping))
|
||||
... def parse_yesno(text):
|
||||
... return yesno_mapping[text.lower()]
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> yesno_mapping = {
|
||||
... "yes": True, "no": False,
|
||||
... "on": True, "off": False,
|
||||
... "true": True, "false": False,
|
||||
... }
|
||||
>>> @with_pattern(r"|".join(yesno_mapping))
|
||||
... def parse_yesno(text):
|
||||
... return yesno_mapping[text.lower()]
|
||||
|
||||
|
||||
If the type converter ``pattern`` uses regex-grouping (with parenthesis),
|
||||
you should indicate this by using the optional ``regex_group_count`` parameter
|
||||
in the ``with_pattern()`` decorator:
|
||||
|
||||
>>> @with_pattern(r'((\d+))', regex_group_count=2)
|
||||
... def parse_number2(text):
|
||||
... return int(text)
|
||||
>>> parse('Answer: {:Number2} {:Number2}', 'Answer: 42 43', dict(Number2=parse_number2))
|
||||
<Result (42, 43) {}>
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> @with_pattern(r'((\d+))', regex_group_count=2)
|
||||
... def parse_number2(text):
|
||||
... return int(text)
|
||||
>>> parse('Answer: {:Number2} {:Number2}', 'Answer: 42 43', dict(Number2=parse_number2))
|
||||
<Result (42, 43) {}>
|
||||
|
||||
Otherwise, this may cause parsing problems with unnamed/fixed parameters.
|
||||
|
||||
@@ -330,22 +360,26 @@ Otherwise, this may cause parsing problems with unnamed/fixed parameters.
|
||||
Potential Gotchas
|
||||
-----------------
|
||||
|
||||
`parse()` will always match the shortest text necessary (from left to right)
|
||||
``parse()`` will always match the shortest text necessary (from left to right)
|
||||
to fulfil the parse pattern, so for example:
|
||||
|
||||
>>> pattern = '{dir1}/{dir2}'
|
||||
>>> data = 'root/parent/subdir'
|
||||
>>> sorted(parse(pattern, data).named.items())
|
||||
[('dir1', 'root'), ('dir2', 'parent/subdir')]
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> pattern = '{dir1}/{dir2}'
|
||||
>>> data = 'root/parent/subdir'
|
||||
>>> sorted(parse(pattern, data).named.items())
|
||||
[('dir1', 'root'), ('dir2', 'parent/subdir')]
|
||||
|
||||
So, even though `{'dir1': 'root/parent', 'dir2': 'subdir'}` would also fit
|
||||
the pattern, the actual match represents the shortest successful match for
|
||||
`dir1`.
|
||||
``dir1``.
|
||||
|
||||
----
|
||||
|
||||
**Version history (in brief)**:
|
||||
|
||||
- 1.18.0 Correct bug in int parsing introduced in 1.16.0 (thanks @maxxk)
|
||||
- 1.17.0 Make left- and center-aligned search consume up to next space
|
||||
- 1.16.0 Make compiled parse objects pickleable (thanks @martinResearch)
|
||||
- 1.15.0 Several fixes for parsing non-base 10 numbers (thanks @vladikcomper)
|
||||
- 1.14.0 More broad acceptance of Fortran number format (thanks @purpleskyfall)
|
||||
- 1.13.1 Project metadata correction.
|
||||
@@ -419,12 +453,13 @@ the pattern, the actual match represents the shortest successful match for
|
||||
and removed the restriction on mixing fixed-position and named fields
|
||||
- 1.0.0 initial release
|
||||
|
||||
This code is copyright 2012-2019 Richard Jones <richard@python.org>
|
||||
This code is copyright 2012-2020 Richard Jones <richard@python.org>
|
||||
See the end of the source file for the license of use.
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
__version__ = '1.15.0'
|
||||
|
||||
__version__ = '1.18.0'
|
||||
|
||||
# yes, I now have two problems
|
||||
import re
|
||||
@@ -461,29 +496,35 @@ def with_pattern(pattern, regex_group_count=None):
|
||||
:param regex_group_count: Indicates how many regex-groups are in pattern.
|
||||
:return: wrapped function
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
func.pattern = pattern
|
||||
func.regex_group_count = regex_group_count
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def int_convert(base=None):
|
||||
'''Convert a string to an integer.
|
||||
class int_convert:
|
||||
"""Convert a string to an integer.
|
||||
|
||||
The string may start with a sign.
|
||||
|
||||
It may be of a base other than 2, 8, 10 or 16.
|
||||
|
||||
If base isn't specified, it will be detected automatically based
|
||||
on a string format. When string starts with a base indicator, 0#nnnn,
|
||||
on a string format. When string starts with a base indicator, 0#nnnn,
|
||||
it overrides the default base of 10.
|
||||
|
||||
It may also have other non-numeric characters that we can ignore.
|
||||
'''
|
||||
"""
|
||||
|
||||
CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
|
||||
|
||||
def f(string, match, base=base):
|
||||
def __init__(self, base=None):
|
||||
self.base = base
|
||||
|
||||
def __call__(self, string, match):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
number_start = 1
|
||||
@@ -494,34 +535,46 @@ def int_convert(base=None):
|
||||
sign = 1
|
||||
number_start = 0
|
||||
|
||||
base = self.base
|
||||
# If base wasn't specified, detect it automatically
|
||||
if base is None:
|
||||
|
||||
# Assume decimal number, unless different base is detected
|
||||
base = 10
|
||||
# Assume decimal number, unless different base is detected
|
||||
base = 10
|
||||
|
||||
# For number formats starting with 0b, 0o, 0x, use corresponding base ...
|
||||
if string[number_start] == '0' and len(string) - number_start > 2:
|
||||
if string[number_start+1] in 'bB':
|
||||
base = 2
|
||||
elif string[number_start+1] in 'oO':
|
||||
base = 8
|
||||
elif string[number_start+1] in 'xX':
|
||||
base = 16
|
||||
# For number formats starting with 0b, 0o, 0x, use corresponding base ...
|
||||
if string[number_start] == '0' and len(string) - number_start > 2:
|
||||
if string[number_start + 1] in 'bB':
|
||||
base = 2
|
||||
elif string[number_start + 1] in 'oO':
|
||||
base = 8
|
||||
elif string[number_start + 1] in 'xX':
|
||||
base = 16
|
||||
|
||||
chars = CHARS[:base]
|
||||
chars = int_convert.CHARS[: base]
|
||||
string = re.sub('[^%s]' % chars, '', string.lower())
|
||||
return sign * int(string, base)
|
||||
return f
|
||||
|
||||
|
||||
class convert_first:
|
||||
"""Convert the first element of a pair.
|
||||
This equivalent to lambda s,m: converter(s). But unlike a lambda function, it can be pickled
|
||||
"""
|
||||
|
||||
def __init__(self, converter):
|
||||
self.converter = converter
|
||||
|
||||
def __call__(self, string, match):
|
||||
return self.converter(string)
|
||||
|
||||
|
||||
def percentage(string, match):
|
||||
return float(string[:-1]) / 100.
|
||||
return float(string[:-1]) / 100.0
|
||||
|
||||
|
||||
class FixedTzOffset(tzinfo):
|
||||
"""Fixed offset in minutes east from UTC.
|
||||
"""
|
||||
"""Fixed offset in minutes east from UTC."""
|
||||
|
||||
ZERO = timedelta(0)
|
||||
|
||||
def __init__(self, offset, name):
|
||||
@@ -529,8 +582,7 @@ class FixedTzOffset(tzinfo):
|
||||
self._name = name
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s %s>' % (self.__class__.__name__, self._name,
|
||||
self._offset)
|
||||
return '<%s %s %s>' % (self.__class__.__name__, self._name, self._offset)
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self._offset
|
||||
@@ -548,18 +600,29 @@ class FixedTzOffset(tzinfo):
|
||||
|
||||
|
||||
MONTHS_MAP = dict(
|
||||
Jan=1, January=1,
|
||||
Feb=2, February=2,
|
||||
Mar=3, March=3,
|
||||
Apr=4, April=4,
|
||||
Jan=1,
|
||||
January=1,
|
||||
Feb=2,
|
||||
February=2,
|
||||
Mar=3,
|
||||
March=3,
|
||||
Apr=4,
|
||||
April=4,
|
||||
May=5,
|
||||
Jun=6, June=6,
|
||||
Jul=7, July=7,
|
||||
Aug=8, August=8,
|
||||
Sep=9, September=9,
|
||||
Oct=10, October=10,
|
||||
Nov=11, November=11,
|
||||
Dec=12, December=12
|
||||
Jun=6,
|
||||
June=6,
|
||||
Jul=7,
|
||||
July=7,
|
||||
Aug=8,
|
||||
August=8,
|
||||
Sep=9,
|
||||
September=9,
|
||||
Oct=10,
|
||||
October=10,
|
||||
Nov=11,
|
||||
November=11,
|
||||
Dec=12,
|
||||
December=12,
|
||||
)
|
||||
DAYS_PAT = r'(Mon|Tue|Wed|Thu|Fri|Sat|Sun)'
|
||||
MONTHS_PAT = r'(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'
|
||||
@@ -569,17 +632,28 @@ AM_PAT = r'(\s+[AP]M)'
|
||||
TZ_PAT = r'(\s+[-+]\d\d?:?\d\d)'
|
||||
|
||||
|
||||
def date_convert(string, match, ymd=None, mdy=None, dmy=None,
|
||||
d_m_y=None, hms=None, am=None, tz=None, mm=None, dd=None):
|
||||
'''Convert the incoming string containing some date / time info into a
|
||||
def date_convert(
|
||||
string,
|
||||
match,
|
||||
ymd=None,
|
||||
mdy=None,
|
||||
dmy=None,
|
||||
d_m_y=None,
|
||||
hms=None,
|
||||
am=None,
|
||||
tz=None,
|
||||
mm=None,
|
||||
dd=None,
|
||||
):
|
||||
"""Convert the incoming string containing some date / time info into a
|
||||
datetime instance.
|
||||
'''
|
||||
"""
|
||||
groups = match.groups()
|
||||
time_only = False
|
||||
if mm and dd:
|
||||
y=datetime.today().year
|
||||
m=groups[mm]
|
||||
d=groups[dd]
|
||||
y = datetime.today().year
|
||||
m = groups[mm]
|
||||
d = groups[dd]
|
||||
elif ymd is not None:
|
||||
y, m, d = re.split(r'[-/\s]', groups[ymd])
|
||||
elif mdy is not None:
|
||||
@@ -670,13 +744,11 @@ class RepeatedNameError(ValueError):
|
||||
REGEX_SAFETY = re.compile(r'([?\\\\.[\]()*+\^$!\|])')
|
||||
|
||||
# allowed field types
|
||||
ALLOWED_TYPES = set(list('nbox%fFegwWdDsSl') +
|
||||
['t' + c for c in 'ieahgcts'])
|
||||
ALLOWED_TYPES = set(list('nbox%fFegwWdDsSl') + ['t' + c for c in 'ieahgcts'])
|
||||
|
||||
|
||||
def extract_format(format, extra_types):
|
||||
'''Pull apart the format [[fill]align][0][width][.precision][type]
|
||||
'''
|
||||
"""Pull apart the format [[fill]align][0][width][.precision][type]"""
|
||||
fill = align = None
|
||||
if format[0] in '<>=^':
|
||||
align = format[0]
|
||||
@@ -721,8 +793,8 @@ PARSE_RE = re.compile(r"""({{|}}|{\w*(?:(?:\.\w+)|(?:\[[^\]]+\]))*(?::[^}]+)?})"
|
||||
|
||||
|
||||
class Parser(object):
|
||||
'''Encapsulate a format string that may be used to parse other strings.
|
||||
'''
|
||||
"""Encapsulate a format string that may be used to parse other strings."""
|
||||
|
||||
def __init__(self, format, extra_types=None, case_sensitive=False):
|
||||
# a mapping of a name as in {hello.world} to a regex-group compatible
|
||||
# name, like hello__world Its used to prevent the transformation of
|
||||
@@ -756,8 +828,7 @@ class Parser(object):
|
||||
|
||||
def __repr__(self):
|
||||
if len(self._format) > 20:
|
||||
return '<%s %r>' % (self.__class__.__name__,
|
||||
self._format[:17] + '...')
|
||||
return '<%s %r>' % (self.__class__.__name__, self._format[:17] + '...')
|
||||
return '<%s %r>' % (self.__class__.__name__, self._format)
|
||||
|
||||
@property
|
||||
@@ -769,8 +840,9 @@ class Parser(object):
|
||||
# access error through sys to keep py3k and backward compat
|
||||
e = str(sys.exc_info()[1])
|
||||
if e.endswith('this version only supports 100 named groups'):
|
||||
raise TooManyFields('sorry, you are attempting to parse '
|
||||
'too many complex fields')
|
||||
raise TooManyFields(
|
||||
'sorry, you are attempting to parse ' 'too many complex fields'
|
||||
)
|
||||
return self.__search_re
|
||||
|
||||
@property
|
||||
@@ -783,19 +855,29 @@ class Parser(object):
|
||||
# access error through sys to keep py3k and backward compat
|
||||
e = str(sys.exc_info()[1])
|
||||
if e.endswith('this version only supports 100 named groups'):
|
||||
raise TooManyFields('sorry, you are attempting to parse '
|
||||
'too many complex fields')
|
||||
raise TooManyFields(
|
||||
'sorry, you are attempting to parse ' 'too many complex fields'
|
||||
)
|
||||
except re.error:
|
||||
raise NotImplementedError("Group names (e.g. (?P<name>) can "
|
||||
"cause failure, as they are not escaped properly: '%s'" %
|
||||
expression)
|
||||
raise NotImplementedError(
|
||||
"Group names (e.g. (?P<name>) can "
|
||||
"cause failure, as they are not escaped properly: '%s'" % expression
|
||||
)
|
||||
return self.__match_re
|
||||
|
||||
@property
|
||||
def named_fields(self):
|
||||
return self._named_fields.copy()
|
||||
|
||||
@property
|
||||
def fixed_fields(self):
|
||||
return self._fixed_fields.copy()
|
||||
|
||||
def parse(self, string, evaluate_result=True):
|
||||
'''Match my format to the string exactly.
|
||||
"""Match my format to the string exactly.
|
||||
|
||||
Return a Result or Match instance or None if there's no match.
|
||||
'''
|
||||
"""
|
||||
m = self._match_re.match(string)
|
||||
if m is None:
|
||||
return None
|
||||
@@ -806,7 +888,7 @@ class Parser(object):
|
||||
return Match(self, m)
|
||||
|
||||
def search(self, string, pos=0, endpos=None, evaluate_result=True):
|
||||
'''Search the string for my format.
|
||||
"""Search the string for my format.
|
||||
|
||||
Optionally start the search at "pos" character index and limit the
|
||||
search to a maximum index of endpos - equivalent to
|
||||
@@ -816,7 +898,7 @@ class Parser(object):
|
||||
Match instance is returned instead of the actual Result instance.
|
||||
|
||||
Return either a Result instance or None if there's no match.
|
||||
'''
|
||||
"""
|
||||
if endpos is None:
|
||||
endpos = len(string)
|
||||
m = self._search_re.search(string, pos, endpos)
|
||||
@@ -828,8 +910,10 @@ class Parser(object):
|
||||
else:
|
||||
return Match(self, m)
|
||||
|
||||
def findall(self, string, pos=0, endpos=None, extra_types=None, evaluate_result=True):
|
||||
'''Search "string" for all occurrences of "format".
|
||||
def findall(
|
||||
self, string, pos=0, endpos=None, extra_types=None, evaluate_result=True
|
||||
):
|
||||
"""Search "string" for all occurrences of "format".
|
||||
|
||||
Optionally start the search at "pos" character index and limit the
|
||||
search to a maximum index of endpos - equivalent to
|
||||
@@ -837,10 +921,12 @@ class Parser(object):
|
||||
|
||||
Returns an iterator that holds Result or Match instances for each format match
|
||||
found.
|
||||
'''
|
||||
"""
|
||||
if endpos is None:
|
||||
endpos = len(string)
|
||||
return ResultIterator(self, string, pos, endpos, evaluate_result=evaluate_result)
|
||||
return ResultIterator(
|
||||
self, string, pos, endpos, evaluate_result=evaluate_result
|
||||
)
|
||||
|
||||
def _expand_named_fields(self, named_fields):
|
||||
result = {}
|
||||
@@ -854,7 +940,7 @@ class Parser(object):
|
||||
|
||||
if subkeys:
|
||||
for subkey in re.findall(r'\[[^\]]+\]', subkeys):
|
||||
d = d.setdefault(k,{})
|
||||
d = d.setdefault(k, {})
|
||||
k = subkey[1:-1]
|
||||
|
||||
# assign the value to the last key
|
||||
@@ -887,8 +973,7 @@ class Parser(object):
|
||||
|
||||
# now figure the match spans
|
||||
spans = dict((n, m.span(name_map[n])) for n in named_fields)
|
||||
spans.update((i, m.span(n + 1))
|
||||
for i, n in enumerate(self._fixed_fields))
|
||||
spans.update((i, m.span(n + 1)) for i, n in enumerate(self._fixed_fields))
|
||||
|
||||
# and that's our result
|
||||
return Result(fixed_fields, self._expand_named_fields(named_fields), spans)
|
||||
@@ -949,9 +1034,11 @@ class Parser(object):
|
||||
name = field
|
||||
if name in self._name_to_group_map:
|
||||
if self._name_types[name] != format:
|
||||
raise RepeatedNameError('field type %r for field "%s" '
|
||||
'does not match previous seen type %r' % (format,
|
||||
name, self._name_types[name]))
|
||||
raise RepeatedNameError(
|
||||
'field type %r for field "%s" '
|
||||
'does not match previous seen type %r'
|
||||
% (format, name, self._name_types[name])
|
||||
)
|
||||
group = self._name_to_group_map[name]
|
||||
# match previously-seen value
|
||||
return r'(?P=%s)' % group
|
||||
@@ -986,10 +1073,7 @@ class Parser(object):
|
||||
if regex_group_count is None:
|
||||
regex_group_count = 0
|
||||
self._group_index += regex_group_count
|
||||
|
||||
def f(string, m):
|
||||
return type_converter(string)
|
||||
self._type_conversions[group] = f
|
||||
self._type_conversions[group] = convert_first(type_converter)
|
||||
elif type == 'n':
|
||||
s = r'\d{1,3}([,.]\d{3})*'
|
||||
self._group_index += 1
|
||||
@@ -1012,79 +1096,104 @@ class Parser(object):
|
||||
self._type_conversions[group] = percentage
|
||||
elif type == 'f':
|
||||
s = r'\d*\.\d+'
|
||||
self._type_conversions[group] = lambda s, m: float(s)
|
||||
self._type_conversions[group] = convert_first(float)
|
||||
elif type == 'F':
|
||||
s = r'\d*\.\d+'
|
||||
self._type_conversions[group] = lambda s, m: Decimal(s)
|
||||
self._type_conversions[group] = convert_first(Decimal)
|
||||
elif type == 'e':
|
||||
s = r'\d*\.\d+[eE][-+]?\d+|nan|NAN|[-+]?inf|[-+]?INF'
|
||||
self._type_conversions[group] = lambda s, m: float(s)
|
||||
self._type_conversions[group] = convert_first(float)
|
||||
elif type == 'g':
|
||||
s = r'\d+(\.\d+)?([eE][-+]?\d+)?|nan|NAN|[-+]?inf|[-+]?INF'
|
||||
self._group_index += 2
|
||||
self._type_conversions[group] = lambda s, m: float(s)
|
||||
self._type_conversions[group] = convert_first(float)
|
||||
elif type == 'd':
|
||||
if format.get('width'):
|
||||
width = r'{1,%s}' % int(format['width'])
|
||||
else:
|
||||
width = '+'
|
||||
s = r'\d{w}|[-+ ]?0[xX][0-9a-fA-F]{w}|[-+ ]?0[bB][01]{w}|[-+ ]?0[oO][0-7]{w}'.format(w=width)
|
||||
self._type_conversions[group] = int_convert() # do not specify numeber base, determine it automatically
|
||||
s = r'\d{w}|[-+ ]?0[xX][0-9a-fA-F]{w}|[-+ ]?0[bB][01]{w}|[-+ ]?0[oO][0-7]{w}'.format(
|
||||
w=width
|
||||
)
|
||||
self._type_conversions[
|
||||
group
|
||||
] = int_convert() # do not specify number base, determine it automatically
|
||||
elif type == 'ti':
|
||||
s = r'(\d{4}-\d\d-\d\d)((\s+|T)%s)?(Z|\s*[-+]\d\d:?\d\d)?' % \
|
||||
TIME_PAT
|
||||
s = r'(\d{4}-\d\d-\d\d)((\s+|T)%s)?(Z|\s*[-+]\d\d:?\d\d)?' % TIME_PAT
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, ymd=n + 1,
|
||||
hms=n + 4, tz=n + 7)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, ymd=n + 1, hms=n + 4, tz=n + 7
|
||||
)
|
||||
self._group_index += 7
|
||||
elif type == 'tg':
|
||||
s = r'(\d{1,2}[-/](\d{1,2}|%s)[-/]\d{4})(\s+%s)?%s?%s?' % (
|
||||
ALL_MONTHS_PAT, TIME_PAT, AM_PAT, TZ_PAT)
|
||||
ALL_MONTHS_PAT,
|
||||
TIME_PAT,
|
||||
AM_PAT,
|
||||
TZ_PAT,
|
||||
)
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, dmy=n + 1,
|
||||
hms=n + 5, am=n + 8, tz=n + 9)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, dmy=n + 1, hms=n + 5, am=n + 8, tz=n + 9
|
||||
)
|
||||
self._group_index += 9
|
||||
elif type == 'ta':
|
||||
s = r'((\d{1,2}|%s)[-/]\d{1,2}[-/]\d{4})(\s+%s)?%s?%s?' % (
|
||||
ALL_MONTHS_PAT, TIME_PAT, AM_PAT, TZ_PAT)
|
||||
ALL_MONTHS_PAT,
|
||||
TIME_PAT,
|
||||
AM_PAT,
|
||||
TZ_PAT,
|
||||
)
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, mdy=n + 1,
|
||||
hms=n + 5, am=n + 8, tz=n + 9)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, mdy=n + 1, hms=n + 5, am=n + 8, tz=n + 9
|
||||
)
|
||||
self._group_index += 9
|
||||
elif type == 'te':
|
||||
# this will allow microseconds through if they're present, but meh
|
||||
s = r'(%s,\s+)?(\d{1,2}\s+%s\s+\d{4})\s+%s%s' % (DAYS_PAT,
|
||||
MONTHS_PAT, TIME_PAT, TZ_PAT)
|
||||
s = r'(%s,\s+)?(\d{1,2}\s+%s\s+\d{4})\s+%s%s' % (
|
||||
DAYS_PAT,
|
||||
MONTHS_PAT,
|
||||
TIME_PAT,
|
||||
TZ_PAT,
|
||||
)
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, dmy=n + 3,
|
||||
hms=n + 5, tz=n + 8)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, dmy=n + 3, hms=n + 5, tz=n + 8
|
||||
)
|
||||
self._group_index += 8
|
||||
elif type == 'th':
|
||||
# slight flexibility here from the stock Apache format
|
||||
s = r'(\d{1,2}[-/]%s[-/]\d{4}):%s%s' % (MONTHS_PAT, TIME_PAT,
|
||||
TZ_PAT)
|
||||
s = r'(\d{1,2}[-/]%s[-/]\d{4}):%s%s' % (MONTHS_PAT, TIME_PAT, TZ_PAT)
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, dmy=n + 1,
|
||||
hms=n + 3, tz=n + 6)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, dmy=n + 1, hms=n + 3, tz=n + 6
|
||||
)
|
||||
self._group_index += 6
|
||||
elif type == 'tc':
|
||||
s = r'(%s)\s+%s\s+(\d{1,2})\s+%s\s+(\d{4})' % (
|
||||
DAYS_PAT, MONTHS_PAT, TIME_PAT)
|
||||
DAYS_PAT,
|
||||
MONTHS_PAT,
|
||||
TIME_PAT,
|
||||
)
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert,
|
||||
d_m_y=(n + 4, n + 3, n + 8), hms=n + 5)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, d_m_y=(n + 4, n + 3, n + 8), hms=n + 5
|
||||
)
|
||||
self._group_index += 8
|
||||
elif type == 'tt':
|
||||
s = r'%s?%s?%s?' % (TIME_PAT, AM_PAT, TZ_PAT)
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, hms=n + 1,
|
||||
am=n + 4, tz=n + 5)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, hms=n + 1, am=n + 4, tz=n + 5
|
||||
)
|
||||
self._group_index += 5
|
||||
elif type == 'ts':
|
||||
s = r'%s(\s+)(\d+)(\s+)(\d{1,2}:\d{1,2}:\d{1,2})?' % MONTHS_PAT
|
||||
n = self._group_index
|
||||
self._type_conversions[group] = partial(date_convert, mm=n+1, dd=n+3,
|
||||
hms=n + 5)
|
||||
self._type_conversions[group] = partial(
|
||||
date_convert, mm=n + 1, dd=n + 3, hms=n + 5
|
||||
)
|
||||
self._group_index += 5
|
||||
elif type == 'l':
|
||||
s = r'[A-Za-z]+'
|
||||
@@ -1138,24 +1247,25 @@ class Parser(object):
|
||||
|
||||
# align "=" has been handled
|
||||
if align == '<':
|
||||
s = '%s%s*' % (s, fill)
|
||||
s = '%s%s+' % (s, fill)
|
||||
elif align == '>':
|
||||
s = '%s*%s' % (fill, s)
|
||||
elif align == '^':
|
||||
s = '%s*%s%s*' % (fill, s, fill)
|
||||
s = '%s*%s%s+' % (fill, s, fill)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
class Result(object):
|
||||
'''The result of a parse() or search().
|
||||
"""The result of a parse() or search().
|
||||
|
||||
Fixed results may be looked up using `result[index]`.
|
||||
|
||||
Named results may be looked up using `result['name']`.
|
||||
|
||||
Named results may be tested for existence using `'name' in result`.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, fixed, named, spans):
|
||||
self.fixed = fixed
|
||||
self.named = named
|
||||
@@ -1167,19 +1277,19 @@ class Result(object):
|
||||
return self.named[item]
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r %r>' % (self.__class__.__name__, self.fixed,
|
||||
self.named)
|
||||
return '<%s %r %r>' % (self.__class__.__name__, self.fixed, self.named)
|
||||
|
||||
def __contains__(self, name):
|
||||
return name in self.named
|
||||
|
||||
|
||||
class Match(object):
|
||||
'''The result of a parse() or search() if no results are generated.
|
||||
"""The result of a parse() or search() if no results are generated.
|
||||
|
||||
This class is only used to expose internal used regex match objects
|
||||
to the user and use them for external Parser.evaluate_result calls.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, parser, match):
|
||||
self.parser = parser
|
||||
self.match = match
|
||||
@@ -1190,10 +1300,11 @@ class Match(object):
|
||||
|
||||
|
||||
class ResultIterator(object):
|
||||
'''The result of a findall() operation.
|
||||
"""The result of a findall() operation.
|
||||
|
||||
Each element is a Result instance.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, parser, string, pos, endpos, evaluate_result=True):
|
||||
self.parser = parser
|
||||
self.string = string
|
||||
@@ -1220,7 +1331,7 @@ class ResultIterator(object):
|
||||
|
||||
|
||||
def parse(format, string, extra_types=None, evaluate_result=True, case_sensitive=False):
|
||||
'''Using "format" attempt to pull values from "string".
|
||||
"""Using "format" attempt to pull values from "string".
|
||||
|
||||
The format must match the string contents exactly. If the value
|
||||
you're looking for is instead just a part of the string use
|
||||
@@ -1244,14 +1355,21 @@ def parse(format, string, extra_types=None, evaluate_result=True, case_sensitive
|
||||
See the module documentation for the use of "extra_types".
|
||||
|
||||
In the case there is no match parse() will return None.
|
||||
'''
|
||||
"""
|
||||
p = Parser(format, extra_types=extra_types, case_sensitive=case_sensitive)
|
||||
return p.parse(string, evaluate_result=evaluate_result)
|
||||
|
||||
|
||||
def search(format, string, pos=0, endpos=None, extra_types=None, evaluate_result=True,
|
||||
case_sensitive=False):
|
||||
'''Search "string" for the first occurrence of "format".
|
||||
def search(
|
||||
format,
|
||||
string,
|
||||
pos=0,
|
||||
endpos=None,
|
||||
extra_types=None,
|
||||
evaluate_result=True,
|
||||
case_sensitive=False,
|
||||
):
|
||||
"""Search "string" for the first occurrence of "format".
|
||||
|
||||
The format may occur anywhere within the string. If
|
||||
instead you wish for the format to exactly match the string
|
||||
@@ -1278,14 +1396,21 @@ def search(format, string, pos=0, endpos=None, extra_types=None, evaluate_result
|
||||
See the module documentation for the use of "extra_types".
|
||||
|
||||
In the case there is no match parse() will return None.
|
||||
'''
|
||||
"""
|
||||
p = Parser(format, extra_types=extra_types, case_sensitive=case_sensitive)
|
||||
return p.search(string, pos, endpos, evaluate_result=evaluate_result)
|
||||
|
||||
|
||||
def findall(format, string, pos=0, endpos=None, extra_types=None, evaluate_result=True,
|
||||
case_sensitive=False):
|
||||
'''Search "string" for all occurrences of "format".
|
||||
def findall(
|
||||
format,
|
||||
string,
|
||||
pos=0,
|
||||
endpos=None,
|
||||
extra_types=None,
|
||||
evaluate_result=True,
|
||||
case_sensitive=False,
|
||||
):
|
||||
"""Search "string" for all occurrences of "format".
|
||||
|
||||
You will be returned an iterator that holds Result instances
|
||||
for each format match found.
|
||||
@@ -1309,13 +1434,13 @@ def findall(format, string, pos=0, endpos=None, extra_types=None, evaluate_resul
|
||||
If the format is invalid a ValueError will be raised.
|
||||
|
||||
See the module documentation for the use of "extra_types".
|
||||
'''
|
||||
"""
|
||||
p = Parser(format, extra_types=extra_types, case_sensitive=case_sensitive)
|
||||
return Parser(format, extra_types=extra_types).findall(string, pos, endpos, evaluate_result=evaluate_result)
|
||||
return p.findall(string, pos, endpos, evaluate_result=evaluate_result)
|
||||
|
||||
|
||||
def compile(format, extra_types=None, case_sensitive=False):
|
||||
'''Create a Parser instance to parse "format".
|
||||
"""Create a Parser instance to parse "format".
|
||||
|
||||
The resultant Parser has a method .parse(string) which
|
||||
behaves in the same manner as parse(format, string).
|
||||
@@ -1329,7 +1454,7 @@ def compile(format, extra_types=None, case_sensitive=False):
|
||||
See the module documentation for the use of "extra_types".
|
||||
|
||||
Returns a Parser instance.
|
||||
'''
|
||||
"""
|
||||
return Parser(format, extra_types=extra_types, case_sensitive=case_sensitive)
|
||||
|
||||
|
||||
|
||||
Vendored
+3
-1
@@ -1,4 +1,6 @@
|
||||
"""Wrappers to build Python packages using PEP 517 hooks
|
||||
"""
|
||||
|
||||
__version__ = '0.8.2'
|
||||
__version__ = '0.9.1'
|
||||
|
||||
from .wrappers import * # noqa: F401, F403
|
||||
|
||||
Vendored
+33
-14
@@ -9,6 +9,15 @@ from tempfile import mkdtemp
|
||||
|
||||
from . import compat
|
||||
|
||||
__all__ = [
|
||||
'BackendUnavailable',
|
||||
'BackendInvalid',
|
||||
'HookMissing',
|
||||
'UnsupportedOperation',
|
||||
'default_subprocess_runner',
|
||||
'quiet_subprocess_runner',
|
||||
'Pep517HookCaller',
|
||||
]
|
||||
|
||||
try:
|
||||
import importlib.resources as resources
|
||||
@@ -102,19 +111,22 @@ def norm_and_check(source_tree, requested):
|
||||
class Pep517HookCaller(object):
|
||||
"""A wrapper around a source directory to be built with a PEP 517 backend.
|
||||
|
||||
source_dir : The path to the source directory, containing pyproject.toml.
|
||||
build_backend : The build backend spec, as per PEP 517, from
|
||||
:param source_dir: The path to the source directory, containing
|
||||
pyproject.toml.
|
||||
backend_path : The backend path, as per PEP 517, from pyproject.toml.
|
||||
runner : A callable that invokes the wrapper subprocess.
|
||||
:param build_backend: The build backend spec, as per PEP 517, from
|
||||
pyproject.toml.
|
||||
:param backend_path: The backend path, as per PEP 517, from pyproject.toml.
|
||||
:param runner: A callable that invokes the wrapper subprocess.
|
||||
:param python_executable: The Python executable used to invoke the backend
|
||||
|
||||
The 'runner', if provided, must expect the following:
|
||||
cmd : a list of strings representing the command and arguments to
|
||||
execute, as would be passed to e.g. 'subprocess.check_call'.
|
||||
cwd : a string representing the working directory that must be
|
||||
used for the subprocess. Corresponds to the provided source_dir.
|
||||
extra_environ : a dict mapping environment variable names to values
|
||||
which must be set for the subprocess execution.
|
||||
|
||||
- cmd: a list of strings representing the command and arguments to
|
||||
execute, as would be passed to e.g. 'subprocess.check_call'.
|
||||
- cwd: a string representing the working directory that must be
|
||||
used for the subprocess. Corresponds to the provided source_dir.
|
||||
- extra_environ: a dict mapping environment variable names to values
|
||||
which must be set for the subprocess execution.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
@@ -122,6 +134,7 @@ class Pep517HookCaller(object):
|
||||
build_backend,
|
||||
backend_path=None,
|
||||
runner=None,
|
||||
python_executable=None,
|
||||
):
|
||||
if runner is None:
|
||||
runner = default_subprocess_runner
|
||||
@@ -134,6 +147,9 @@ class Pep517HookCaller(object):
|
||||
]
|
||||
self.backend_path = backend_path
|
||||
self._subprocess_runner = runner
|
||||
if not python_executable:
|
||||
python_executable = sys.executable
|
||||
self.python_executable = python_executable
|
||||
|
||||
@contextmanager
|
||||
def subprocess_runner(self, runner):
|
||||
@@ -150,7 +166,8 @@ class Pep517HookCaller(object):
|
||||
def get_requires_for_build_wheel(self, config_settings=None):
|
||||
"""Identify packages required for building a wheel
|
||||
|
||||
Returns a list of dependency specifications, e.g.:
|
||||
Returns a list of dependency specifications, e.g.::
|
||||
|
||||
["wheel >= 0.25", "setuptools"]
|
||||
|
||||
This does not include requirements specified in pyproject.toml.
|
||||
@@ -164,7 +181,7 @@ class Pep517HookCaller(object):
|
||||
def prepare_metadata_for_build_wheel(
|
||||
self, metadata_directory, config_settings=None,
|
||||
_allow_fallback=True):
|
||||
"""Prepare a *.dist-info folder with metadata for this project.
|
||||
"""Prepare a ``*.dist-info`` folder with metadata for this project.
|
||||
|
||||
Returns the name of the newly created folder.
|
||||
|
||||
@@ -202,7 +219,8 @@ class Pep517HookCaller(object):
|
||||
def get_requires_for_build_sdist(self, config_settings=None):
|
||||
"""Identify packages required for building a wheel
|
||||
|
||||
Returns a list of dependency specifications, e.g.:
|
||||
Returns a list of dependency specifications, e.g.::
|
||||
|
||||
["setuptools >= 26"]
|
||||
|
||||
This does not include requirements specified in pyproject.toml.
|
||||
@@ -252,8 +270,9 @@ class Pep517HookCaller(object):
|
||||
|
||||
# Run the hook in a subprocess
|
||||
with _in_proc_script_path() as script:
|
||||
python = self.python_executable
|
||||
self._subprocess_runner(
|
||||
[sys.executable, str(script), hook_name, td],
|
||||
[python, abspath(str(script)), hook_name, td],
|
||||
cwd=self.source_dir,
|
||||
extra_environ=extra_environ
|
||||
)
|
||||
|
||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ import pkg_resources
|
||||
# from graphviz import backend, Digraph
|
||||
|
||||
|
||||
__version__ = '0.13.2'
|
||||
__version__ = '1.0.0'
|
||||
|
||||
|
||||
flatten = chain.from_iterable
|
||||
|
||||
Vendored
+172
-10
@@ -1,13 +1,175 @@
|
||||
Copyright 2019 Kenneth Reitz
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
Vendored
+16
-8
@@ -57,10 +57,10 @@ def check_compatibility(urllib3_version, chardet_version):
|
||||
# Check urllib3 for compatibility.
|
||||
major, minor, patch = urllib3_version # noqa: F811
|
||||
major, minor, patch = int(major), int(minor), int(patch)
|
||||
# urllib3 >= 1.21.1, <= 1.25
|
||||
# urllib3 >= 1.21.1, <= 1.26
|
||||
assert major == 1
|
||||
assert minor >= 21
|
||||
assert minor <= 25
|
||||
assert minor <= 26
|
||||
|
||||
# Check chardet for compatibility.
|
||||
major, minor, patch = chardet_version.split('.')[:3]
|
||||
@@ -90,14 +90,22 @@ except (AssertionError, ValueError):
|
||||
"version!".format(urllib3.__version__, chardet.__version__),
|
||||
RequestsDependencyWarning)
|
||||
|
||||
# Attempt to enable urllib3's SNI support, if possible
|
||||
# Attempt to enable urllib3's fallback for SNI support
|
||||
# if the standard library doesn't support SNI or the
|
||||
# 'ssl' library isn't available.
|
||||
try:
|
||||
from urllib3.contrib import pyopenssl
|
||||
pyopenssl.inject_into_urllib3()
|
||||
try:
|
||||
import ssl
|
||||
except ImportError:
|
||||
ssl = None
|
||||
|
||||
# Check cryptography version
|
||||
from cryptography import __version__ as cryptography_version
|
||||
_check_cryptography(cryptography_version)
|
||||
if not getattr(ssl, "HAS_SNI", False):
|
||||
from urllib3.contrib import pyopenssl
|
||||
pyopenssl.inject_into_urllib3()
|
||||
|
||||
# Check cryptography version
|
||||
from cryptography import __version__ as cryptography_version
|
||||
_check_cryptography(cryptography_version)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
__title__ = 'requests'
|
||||
__description__ = 'Python HTTP for Humans.'
|
||||
__url__ = 'https://requests.readthedocs.io'
|
||||
__version__ = '2.23.0'
|
||||
__build__ = 0x022300
|
||||
__version__ = '2.25.0'
|
||||
__build__ = 0x022500
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__author_email__ = 'me@kennethreitz.org'
|
||||
__license__ = 'Apache 2.0'
|
||||
|
||||
Vendored
+3
-6
@@ -94,11 +94,11 @@ class ChunkedEncodingError(RequestException):
|
||||
|
||||
|
||||
class ContentDecodingError(RequestException, BaseHTTPError):
|
||||
"""Failed to decode response content"""
|
||||
"""Failed to decode response content."""
|
||||
|
||||
|
||||
class StreamConsumedError(RequestException, TypeError):
|
||||
"""The content for this response was already consumed"""
|
||||
"""The content for this response was already consumed."""
|
||||
|
||||
|
||||
class RetryError(RequestException):
|
||||
@@ -106,21 +106,18 @@ class RetryError(RequestException):
|
||||
|
||||
|
||||
class UnrewindableBodyError(RequestException):
|
||||
"""Requests encountered an error when trying to rewind a body"""
|
||||
"""Requests encountered an error when trying to rewind a body."""
|
||||
|
||||
# Warnings
|
||||
|
||||
|
||||
class RequestsWarning(Warning):
|
||||
"""Base warning for Requests."""
|
||||
pass
|
||||
|
||||
|
||||
class FileModeWarning(RequestsWarning, DeprecationWarning):
|
||||
"""A file was opened in text mode, but Requests determined its binary length."""
|
||||
pass
|
||||
|
||||
|
||||
class RequestsDependencyWarning(RequestsWarning):
|
||||
"""An imported dependency doesn't match the expected version range."""
|
||||
pass
|
||||
|
||||
Vendored
+9
-7
@@ -273,7 +273,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
|
||||
"""The fully mutable :class:`PreparedRequest <PreparedRequest>` object,
|
||||
containing the exact bytes that will be sent to the server.
|
||||
|
||||
Generated from either a :class:`Request <Request>` object or manually.
|
||||
Instances are generated from a :class:`Request <Request>` object, and
|
||||
should not be instantiated manually; doing so may produce undesirable
|
||||
effects.
|
||||
|
||||
Usage::
|
||||
|
||||
@@ -473,12 +475,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
|
||||
not isinstance(data, (basestring, list, tuple, Mapping))
|
||||
])
|
||||
|
||||
try:
|
||||
length = super_len(data)
|
||||
except (TypeError, AttributeError, UnsupportedOperation):
|
||||
length = None
|
||||
|
||||
if is_stream:
|
||||
try:
|
||||
length = super_len(data)
|
||||
except (TypeError, AttributeError, UnsupportedOperation):
|
||||
length = None
|
||||
|
||||
body = data
|
||||
|
||||
if getattr(body, 'tell', None) is not None:
|
||||
@@ -916,7 +918,7 @@ class Response(object):
|
||||
return l
|
||||
|
||||
def raise_for_status(self):
|
||||
"""Raises stored :class:`HTTPError`, if one occurred."""
|
||||
"""Raises :class:`HTTPError`, if one occurred."""
|
||||
|
||||
http_error_msg = ''
|
||||
if isinstance(self.reason, bytes):
|
||||
|
||||
Vendored
+19
-5
@@ -387,6 +387,13 @@ class Session(SessionRedirectMixin):
|
||||
self.stream = False
|
||||
|
||||
#: SSL Verification default.
|
||||
#: Defaults to `True`, requiring requests to verify the TLS certificate at the
|
||||
#: remote end.
|
||||
#: If verify is set to `False`, requests will accept any TLS certificate
|
||||
#: presented by the server, and will ignore hostname mismatches and/or
|
||||
#: expired certificates, which will make your application vulnerable to
|
||||
#: man-in-the-middle (MitM) attacks.
|
||||
#: Only set this to `False` for testing.
|
||||
self.verify = True
|
||||
|
||||
#: SSL client certificate default, if String, path to ssl client
|
||||
@@ -495,7 +502,12 @@ class Session(SessionRedirectMixin):
|
||||
content. Defaults to ``False``.
|
||||
:param verify: (optional) Either a boolean, in which case it controls whether we verify
|
||||
the server's TLS certificate, or a string, in which case it must be a path
|
||||
to a CA bundle to use. Defaults to ``True``.
|
||||
to a CA bundle to use. Defaults to ``True``. When set to
|
||||
``False``, requests will accept any TLS certificate presented by
|
||||
the server, and will ignore hostname mismatches and/or expired
|
||||
certificates, which will make your application vulnerable to
|
||||
man-in-the-middle (MitM) attacks. Setting verify to ``False``
|
||||
may be useful during local development or testing.
|
||||
:param cert: (optional) if String, path to ssl client cert file (.pem).
|
||||
If Tuple, ('cert', 'key') pair.
|
||||
:rtype: requests.Response
|
||||
@@ -658,11 +670,13 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
extract_cookies_to_jar(self.cookies, request, r.raw)
|
||||
|
||||
# Redirect resolving generator.
|
||||
gen = self.resolve_redirects(r, request, **kwargs)
|
||||
|
||||
# Resolve redirects if allowed.
|
||||
history = [resp for resp in gen] if allow_redirects else []
|
||||
if allow_redirects:
|
||||
# Redirect resolving generator.
|
||||
gen = self.resolve_redirects(r, request, **kwargs)
|
||||
history = [resp for resp in gen]
|
||||
else:
|
||||
history = []
|
||||
|
||||
# Shuffle things around if there's history.
|
||||
if history:
|
||||
|
||||
Vendored
+9
-3
@@ -169,14 +169,20 @@ def super_len(o):
|
||||
def get_netrc_auth(url, raise_errors=False):
|
||||
"""Returns the Requests tuple auth for a given url from netrc."""
|
||||
|
||||
netrc_file = os.environ.get('NETRC')
|
||||
if netrc_file is not None:
|
||||
netrc_locations = (netrc_file,)
|
||||
else:
|
||||
netrc_locations = ('~/{}'.format(f) for f in NETRC_FILES)
|
||||
|
||||
try:
|
||||
from netrc import netrc, NetrcParseError
|
||||
|
||||
netrc_path = None
|
||||
|
||||
for f in NETRC_FILES:
|
||||
for f in netrc_locations:
|
||||
try:
|
||||
loc = os.path.expanduser('~/{}'.format(f))
|
||||
loc = os.path.expanduser(f)
|
||||
except KeyError:
|
||||
# os.path.expanduser can fail when $HOME is undefined and
|
||||
# getpwuid fails. See https://bugs.python.org/issue20164 &
|
||||
@@ -212,7 +218,7 @@ def get_netrc_auth(url, raise_errors=False):
|
||||
if raise_errors:
|
||||
raise
|
||||
|
||||
# AppEngine hackiness.
|
||||
# App Engine hackiness.
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ from .models.lockfile import Lockfile
|
||||
from .models.pipfile import Pipfile
|
||||
from .models.requirements import Requirement
|
||||
|
||||
__version__ = "1.5.14"
|
||||
__version__ = "1.5.16"
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import copy
|
||||
import functools
|
||||
import os
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import packaging.markers
|
||||
import packaging.version
|
||||
import pip_shims.shims
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import copy
|
||||
import itertools
|
||||
import os
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import plette.lockfiles
|
||||
import six
|
||||
from vistir.compat import FileNotFoundError, JSONDecodeError, Path
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import itertools
|
||||
import operator
|
||||
import re
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import distlib.markers
|
||||
import packaging.version
|
||||
import six
|
||||
@@ -673,7 +673,7 @@ def parse_marker_dict(marker_dict):
|
||||
|
||||
|
||||
def _contains_micro_version(version_string):
|
||||
return re.search("\d+\.\d+\.\d+", version_string) is not None
|
||||
return re.search(r"\d+\.\d+\.\d+", version_string) is not None
|
||||
|
||||
|
||||
def format_pyversion(parts):
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import os
|
||||
import zipfile
|
||||
from collections import defaultdict
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import dateutil.parser
|
||||
import distlib.metadata
|
||||
import distlib.wheel
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import itertools
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import plette.models.base
|
||||
import plette.pipfiles
|
||||
import tomlkit
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import collections
|
||||
import io
|
||||
import os
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import packaging.markers
|
||||
import packaging.utils
|
||||
import plette
|
||||
|
||||
+46
-40
@@ -8,12 +8,10 @@ import os
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from distutils.sysconfig import get_python_lib
|
||||
from functools import partial
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import pip_shims
|
||||
import six
|
||||
import vistir
|
||||
from cached_property import cached_property
|
||||
from packaging.markers import Marker
|
||||
from packaging.requirements import Requirement as PackagingRequirement
|
||||
@@ -50,10 +48,6 @@ from ..utils import (
|
||||
strip_ssh_from_git_uri,
|
||||
)
|
||||
from .markers import (
|
||||
cleanup_pyspecs,
|
||||
contains_pyversion,
|
||||
format_pyversion,
|
||||
get_contained_pyversions,
|
||||
normalize_marker_str,
|
||||
)
|
||||
from .setup_info import (
|
||||
@@ -67,10 +61,10 @@ from .url import URI
|
||||
from .utils import (
|
||||
DIRECT_URL_RE,
|
||||
HASH_STRING,
|
||||
URL_RE,
|
||||
build_vcs_uri,
|
||||
convert_direct_url_to_url,
|
||||
create_link,
|
||||
expand_env_variables,
|
||||
extras_to_string,
|
||||
filter_none,
|
||||
format_requirement,
|
||||
@@ -82,7 +76,6 @@ from .utils import (
|
||||
make_install_requirement,
|
||||
normalize_name,
|
||||
parse_extras,
|
||||
read_source,
|
||||
specs_to_string,
|
||||
split_markers_from_line,
|
||||
split_ref_from_uri,
|
||||
@@ -926,7 +919,7 @@ class Line(object):
|
||||
if self.is_named:
|
||||
ireq = pip_shims.shims.install_req_from_line(self.line)
|
||||
if self.is_file or self.is_remote_url:
|
||||
ireq.link = self.link
|
||||
ireq.link = pip_shims.shims.Link(expand_env_variables(self.link.url))
|
||||
if self.extras and not ireq.extras:
|
||||
ireq.extras = set(self.extras)
|
||||
if self.parsed_marker is not None and not ireq.markers:
|
||||
@@ -1413,43 +1406,51 @@ LinkInfo = collections.namedtuple(
|
||||
)
|
||||
|
||||
|
||||
@attr.s(slots=True, cmp=True, hash=True)
|
||||
@attr.s(slots=True, eq=True, order=True, hash=True)
|
||||
class FileRequirement(object):
|
||||
"""File requirements for tar.gz installable files or wheels or setup.py
|
||||
containing directories."""
|
||||
|
||||
#: Path to the relevant `setup.py` location
|
||||
setup_path = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
setup_path = attr.ib(default=None, eq=True, order=True) # type: Optional[STRING_TYPE]
|
||||
#: path to hit - without any of the VCS prefixes (like git+ / http+ / etc)
|
||||
path = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
path = attr.ib(default=None, eq=True, order=True) # type: Optional[STRING_TYPE]
|
||||
#: Whether the package is editable
|
||||
editable = attr.ib(default=False, cmp=True) # type: bool
|
||||
editable = attr.ib(default=False, eq=True, order=True) # type: bool
|
||||
#: Extras if applicable
|
||||
extras = attr.ib(
|
||||
default=attr.Factory(tuple), cmp=True
|
||||
default=attr.Factory(tuple), eq=True, order=True
|
||||
) # type: Tuple[STRING_TYPE, ...]
|
||||
_uri_scheme = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
_uri_scheme = attr.ib(
|
||||
default=None, eq=True, order=True
|
||||
) # type: Optional[STRING_TYPE]
|
||||
#: URI of the package
|
||||
uri = attr.ib(cmp=True) # type: Optional[STRING_TYPE]
|
||||
uri = attr.ib(eq=True, order=True) # type: Optional[STRING_TYPE]
|
||||
#: Link object representing the package to clone
|
||||
link = attr.ib(cmp=True) # type: Optional[Link]
|
||||
link = attr.ib(eq=True, order=True) # type: Optional[Link]
|
||||
#: PyProject Requirements
|
||||
pyproject_requires = attr.ib(
|
||||
factory=tuple, cmp=True
|
||||
factory=tuple, eq=True, order=True
|
||||
) # type: Optional[Tuple[STRING_TYPE, ...]]
|
||||
#: PyProject Build System
|
||||
pyproject_backend = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
pyproject_backend = attr.ib(
|
||||
default=None, eq=True, order=True
|
||||
) # type: Optional[STRING_TYPE]
|
||||
#: PyProject Path
|
||||
pyproject_path = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
pyproject_path = attr.ib(
|
||||
default=None, eq=True, order=True
|
||||
) # type: Optional[STRING_TYPE]
|
||||
subdirectory = attr.ib(default=None) # type: Optional[STRING_TYPE]
|
||||
#: Setup metadata e.g. dependencies
|
||||
_setup_info = attr.ib(default=None, cmp=True) # type: Optional[SetupInfo]
|
||||
_has_hashed_name = attr.ib(default=False, cmp=True) # type: bool
|
||||
_parsed_line = attr.ib(default=None, cmp=False, hash=True) # type: Optional[Line]
|
||||
_setup_info = attr.ib(default=None, eq=True, order=True) # type: Optional[SetupInfo]
|
||||
_has_hashed_name = attr.ib(default=False, eq=True, order=True) # type: bool
|
||||
_parsed_line = attr.ib(
|
||||
default=None, eq=False, order=False, hash=True
|
||||
) # type: Optional[Line]
|
||||
#: Package name
|
||||
name = attr.ib(cmp=True) # type: Optional[STRING_TYPE]
|
||||
name = attr.ib(eq=True, order=True) # type: Optional[STRING_TYPE]
|
||||
#: A :class:`~pkg_resources.Requirement` instance
|
||||
req = attr.ib(cmp=True) # type: Optional[PackagingRequirement]
|
||||
req = attr.ib(eq=True, order=True) # type: Optional[PackagingRequirement]
|
||||
|
||||
@classmethod
|
||||
def get_link_from_line(cls, line):
|
||||
@@ -2140,7 +2141,7 @@ class VCSRequirement(FileRequirement):
|
||||
if checkout_dir is None:
|
||||
checkout_dir = self.get_checkout_dir(src_dir=src_dir)
|
||||
vcsrepo = VCSRepository(
|
||||
url=self.url,
|
||||
url=expand_env_variables(self.url),
|
||||
name=self.name,
|
||||
ref=self.ref if self.ref else None,
|
||||
checkout_directory=checkout_dir,
|
||||
@@ -2373,29 +2374,34 @@ class VCSRequirement(FileRequirement):
|
||||
return {name: pipfile_dict} # type: ignore
|
||||
|
||||
|
||||
@attr.s(cmp=True, hash=True)
|
||||
@attr.s(eq=True, order=True, hash=True)
|
||||
class Requirement(object):
|
||||
_name = attr.ib(cmp=True) # type: STRING_TYPE
|
||||
_name = attr.ib(eq=True, order=True) # type: STRING_TYPE
|
||||
vcs = attr.ib(
|
||||
default=None, validator=attr.validators.optional(validate_vcs), cmp=True
|
||||
default=None,
|
||||
validator=attr.validators.optional(validate_vcs),
|
||||
eq=True,
|
||||
order=True,
|
||||
) # type: Optional[STRING_TYPE]
|
||||
req = attr.ib(
|
||||
default=None, cmp=True
|
||||
default=None, eq=True, order=True
|
||||
) # type: Optional[Union[VCSRequirement, FileRequirement, NamedRequirement]]
|
||||
markers = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
markers = attr.ib(default=None, eq=True, order=True) # type: Optional[STRING_TYPE]
|
||||
_specifiers = attr.ib(
|
||||
validator=attr.validators.optional(validate_specifiers), cmp=True
|
||||
validator=attr.validators.optional(validate_specifiers), eq=True, order=True
|
||||
) # type: Optional[STRING_TYPE]
|
||||
index = attr.ib(default=None, cmp=True) # type: Optional[STRING_TYPE]
|
||||
editable = attr.ib(default=None, cmp=True) # type: Optional[bool]
|
||||
index = attr.ib(default=None, eq=True, order=True) # type: Optional[STRING_TYPE]
|
||||
editable = attr.ib(default=None, eq=True, order=True) # type: Optional[bool]
|
||||
hashes = attr.ib(
|
||||
factory=frozenset, converter=frozenset, cmp=True
|
||||
factory=frozenset, converter=frozenset, eq=True, order=True
|
||||
) # type: FrozenSet[STRING_TYPE]
|
||||
extras = attr.ib(factory=tuple, cmp=True) # type: Tuple[STRING_TYPE, ...]
|
||||
abstract_dep = attr.ib(default=None, cmp=False) # type: Optional[AbstractDependency]
|
||||
_line_instance = attr.ib(default=None, cmp=False) # type: Optional[Line]
|
||||
extras = attr.ib(factory=tuple, eq=True, order=True) # type: Tuple[STRING_TYPE, ...]
|
||||
abstract_dep = attr.ib(
|
||||
default=None, eq=False, order=False
|
||||
) # type: Optional[AbstractDependency]
|
||||
_line_instance = attr.ib(default=None, eq=False, order=False) # type: Optional[Line]
|
||||
_ireq = attr.ib(
|
||||
default=None, cmp=False
|
||||
default=None, eq=False, order=False
|
||||
) # type: Optional[pip_shims.InstallRequirement]
|
||||
|
||||
def __hash__(self):
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from contextlib import contextmanager
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import six
|
||||
from pip_shims.shims import Wheel
|
||||
|
||||
|
||||
+11
-8
@@ -12,7 +12,7 @@ import shutil
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import chardet
|
||||
import packaging.specifiers
|
||||
import packaging.utils
|
||||
@@ -1027,7 +1027,9 @@ def ast_unparse(item, initial_mapping=False, analyzer=None, recurse=True): # no
|
||||
constant = ast.Ellipsis
|
||||
unparsed = item
|
||||
if isinstance(item, ast.Dict):
|
||||
unparsed = dict(zip(map(_ensure_hashable, unparse(item.keys)), unparse(item.values)))
|
||||
unparsed = dict(
|
||||
zip(map(_ensure_hashable, unparse(item.keys)), unparse(item.values))
|
||||
)
|
||||
elif isinstance(item, ast.List):
|
||||
unparsed = [unparse(el) for el in item.elts]
|
||||
elif isinstance(item, ast.Tuple):
|
||||
@@ -1175,7 +1177,7 @@ def ast_unparse(item, initial_mapping=False, analyzer=None, recurse=True): # no
|
||||
# XXX: Original reference
|
||||
try:
|
||||
targets = item.targets # for ast.Assign
|
||||
except AttributeError: # for ast.AnnAssign
|
||||
except AttributeError: # for ast.AnnAssign
|
||||
targets = (item.target,)
|
||||
if not initial_mapping:
|
||||
target = unparse(next(iter(targets)), recurse=False)
|
||||
@@ -1326,9 +1328,9 @@ def run_setup(script_path, egg_base=None):
|
||||
|
||||
@attr.s(slots=True, frozen=True)
|
||||
class BaseRequirement(object):
|
||||
name = attr.ib(default="", cmp=True) # type: STRING_TYPE
|
||||
name = attr.ib(default="", eq=True, order=True) # type: STRING_TYPE
|
||||
requirement = attr.ib(
|
||||
default=None, cmp=True
|
||||
default=None, eq=True, order=True
|
||||
) # type: Optional[PkgResourcesRequirement]
|
||||
|
||||
def __str__(self):
|
||||
@@ -1368,8 +1370,8 @@ class BaseRequirement(object):
|
||||
|
||||
@attr.s(slots=True, frozen=True)
|
||||
class Extra(object):
|
||||
name = attr.ib(default=None, cmp=True) # type: STRING_TYPE
|
||||
requirements = attr.ib(factory=frozenset, cmp=True, type=frozenset)
|
||||
name = attr.ib(default=None, eq=True, order=True) # type: STRING_TYPE
|
||||
requirements = attr.ib(factory=frozenset, eq=True, order=True, type=frozenset)
|
||||
|
||||
def __str__(self):
|
||||
# type: () -> S
|
||||
@@ -1933,7 +1935,8 @@ build-backend = "{1}"
|
||||
if not ireq.source_dir:
|
||||
build_kwargs = {
|
||||
"build_dir": kwargs["build_dir"],
|
||||
"autodelete": False, "parallel_builds": True
|
||||
"autodelete": False,
|
||||
"parallel_builds": True,
|
||||
}
|
||||
call_function_with_correct_args(build_location_func, **build_kwargs)
|
||||
ireq.ensure_has_source_dir(kwargs["src_dir"])
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import pip_shims.shims
|
||||
from orderedmultidict import omdict
|
||||
from six.moves.urllib.parse import quote, unquote_plus, unquote as url_unquote
|
||||
|
||||
+16
@@ -1028,6 +1028,22 @@ def read_source(path, encoding="utf-8"):
|
||||
return fp.read()
|
||||
|
||||
|
||||
def expand_env_variables(line):
|
||||
# type: (AnyStr) -> AnyStr
|
||||
"""Expand the env vars in a line following pip's standard.
|
||||
https://pip.pypa.io/en/stable/reference/pip_install/#id10
|
||||
|
||||
Matches environment variable-style values in '${MY_VARIABLE_1}' with the
|
||||
variable name consisting of only uppercase letters, digits or the '_'
|
||||
"""
|
||||
|
||||
def replace_with_env(match):
|
||||
value = os.getenv(match.group(1))
|
||||
return value if value else match.group()
|
||||
|
||||
return re.sub(r"\$\{([A-Z0-9_]+)\}", replace_with_env, line)
|
||||
|
||||
|
||||
SETUPTOOLS_SHIM = (
|
||||
"import setuptools, tokenize;__file__=%r;"
|
||||
"f=getattr(tokenize, 'open', open)(__file__);"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import importlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import attr
|
||||
import pip_shims
|
||||
import six
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ __all__ = [
|
||||
"ResolutionTooDeep",
|
||||
]
|
||||
|
||||
__version__ = "0.3.0"
|
||||
__version__ = "0.5.2"
|
||||
|
||||
|
||||
from .providers import AbstractProvider, AbstractResolver
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
__all__ = ["Sequence"]
|
||||
|
||||
try:
|
||||
from collections.abc import Sequence
|
||||
except ImportError:
|
||||
from collections import Sequence
|
||||
+43
-45
@@ -1,33 +1,37 @@
|
||||
class AbstractProvider(object):
|
||||
"""Delegate class to provide requirement interface for the resolver.
|
||||
"""
|
||||
"""Delegate class to provide requirement interface for the resolver."""
|
||||
|
||||
def identify(self, dependency):
|
||||
"""Given a dependency, return an identifier for it.
|
||||
def identify(self, requirement_or_candidate):
|
||||
"""Given a requirement or candidate, return an identifier for it.
|
||||
|
||||
This is used in many places to identify the dependency, e.g. whether
|
||||
two requirements should have their specifier parts merged, whether
|
||||
two specifications would conflict with each other (because they the
|
||||
same name but different versions).
|
||||
This is used in many places to identify a requirement or candidate,
|
||||
e.g. whether two requirements should have their specifier parts merged,
|
||||
whether two candidates would conflict with each other (because they
|
||||
have same name but different versions).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_preference(self, resolution, candidates, information):
|
||||
"""Produce a sort key for given specification based on preference.
|
||||
"""Produce a sort key for given requirement based on preference.
|
||||
|
||||
The preference is defined as "I think this requirement should be
|
||||
resolved first". The lower the return value is, the more preferred
|
||||
this group of arguments is.
|
||||
|
||||
:param resolution: Currently pinned candidate, or `None`.
|
||||
:param candidates: A list of possible candidates.
|
||||
:param candidates: An iterable of possible candidates.
|
||||
:param information: A list of requirement information.
|
||||
|
||||
Each information instance is a named tuple with two entries:
|
||||
The `candidates` iterable's exact type depends on the return type of
|
||||
`find_matches()`. A sequence is passed-in as-is if possible. If it
|
||||
returns a callble, the iterator returned by that callable is passed
|
||||
in here.
|
||||
|
||||
Each element in `information` is a named tuple with two entries:
|
||||
|
||||
* `requirement` specifies a requirement contributing to the current
|
||||
candidate list
|
||||
* `parent` specifies the candidate that provids (dependend on) the
|
||||
candidate list.
|
||||
* `parent` specifies the candidate that provides (dependend on) the
|
||||
requirement, or `None` to indicate a root requirement.
|
||||
|
||||
The preference could depend on a various of issues, including (not
|
||||
@@ -43,28 +47,40 @@ class AbstractProvider(object):
|
||||
|
||||
A sortable value should be returned (this will be used as the `key`
|
||||
parameter of the built-in sorting function). The smaller the value is,
|
||||
the more preferred this specification is (i.e. the sorting function
|
||||
the more preferred this requirement is (i.e. the sorting function
|
||||
is called with `reverse=False`).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def find_matches(self, requirement):
|
||||
"""Find all possible candidates that satisfy a requirement.
|
||||
def find_matches(self, requirements):
|
||||
"""Find all possible candidates that satisfy the given requirements.
|
||||
|
||||
This should try to get candidates based on the requirement's type.
|
||||
This should try to get candidates based on the requirements' types.
|
||||
For VCS, local, and archive requirements, the one-and-only match is
|
||||
returned, and for a "named" requirement, the index(es) should be
|
||||
consulted to find concrete candidates for this requirement.
|
||||
|
||||
The returned candidates should be sorted by reversed preference, e.g.
|
||||
the most preferred should be LAST. This is done so list-popping can be
|
||||
as efficient as possible.
|
||||
The return value should produce candidates ordered by preference; the
|
||||
most preferred candidate should come first. The return type may be one
|
||||
of the following:
|
||||
|
||||
* A callable that returns an iterator that yields candidates.
|
||||
* An collection of candidates.
|
||||
* An iterable of candidates. This will be consumed immediately into a
|
||||
list of candidates.
|
||||
|
||||
:param requirements: A collection of requirements which all of the
|
||||
returned candidates must match. All requirements are guaranteed to
|
||||
have the same identifier. The collection is never empty.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_satisfied_by(self, requirement, candidate):
|
||||
"""Whether the given requirement can be satisfied by a candidate.
|
||||
|
||||
The candidate is guarenteed to have been generated from the
|
||||
requirement.
|
||||
|
||||
A boolean should be returned to indicate whether `candidate` is a
|
||||
viable solution to the requirement.
|
||||
"""
|
||||
@@ -80,8 +96,7 @@ class AbstractProvider(object):
|
||||
|
||||
|
||||
class AbstractResolver(object):
|
||||
"""The thing that performs the actual resolution work.
|
||||
"""
|
||||
"""The thing that performs the actual resolution work."""
|
||||
|
||||
base_exception = Exception
|
||||
|
||||
@@ -92,30 +107,13 @@ class AbstractResolver(object):
|
||||
def resolve(self, requirements, **kwargs):
|
||||
"""Take a collection of constraints, spit out the resolution result.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
requirements : Collection
|
||||
A collection of constraints
|
||||
kwargs : optional
|
||||
Additional keyword arguments that subclasses may accept.
|
||||
This returns a representation of the final resolution state, with one
|
||||
guarenteed attribute ``mapping`` that contains resolved candidates as
|
||||
values. The keys are their respective identifiers.
|
||||
|
||||
Raises
|
||||
------
|
||||
self.base_exception
|
||||
Any raised exception is guaranteed to be a subclass of
|
||||
self.base_exception. The string representation of an exception
|
||||
should be human readable and provide context for why it occurred.
|
||||
:param requirements: A collection of constraints.
|
||||
:param kwargs: Additional keyword arguments that subclasses may accept.
|
||||
|
||||
Returns
|
||||
-------
|
||||
retval : object
|
||||
A representation of the final resolution state. It can be any object
|
||||
with a `mapping` attribute that is a Mapping. Other attributes can
|
||||
be used to provide resolver-specific information.
|
||||
|
||||
The `mapping` attribute MUST be key-value pair is an identifier of a
|
||||
requirement (as returned by the provider's `identify` method) mapped
|
||||
to the resolved candidate (chosen from the return value of the
|
||||
provider's `find_matches` method).
|
||||
:raises: ``self.base_exception`` or its subclass.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
+13
-12
@@ -1,10 +1,8 @@
|
||||
class BaseReporter(object):
|
||||
"""Delegate class to provider progress reporting for the resolver.
|
||||
"""
|
||||
"""Delegate class to provider progress reporting for the resolver."""
|
||||
|
||||
def starting(self):
|
||||
"""Called before the resolution actually starts.
|
||||
"""
|
||||
"""Called before the resolution actually starts."""
|
||||
|
||||
def starting_round(self, index):
|
||||
"""Called before each round of resolution starts.
|
||||
@@ -20,17 +18,20 @@ class BaseReporter(object):
|
||||
"""
|
||||
|
||||
def ending(self, state):
|
||||
"""Called before the resolution ends successfully.
|
||||
"""
|
||||
"""Called before the resolution ends successfully."""
|
||||
|
||||
def adding_requirement(self, requirement):
|
||||
"""Called when the resolver adds a new requirement into the resolve criteria.
|
||||
def adding_requirement(self, requirement, parent):
|
||||
"""Called when adding a new requirement into the resolve criteria.
|
||||
|
||||
:param requirement: The additional requirement to be applied to filter
|
||||
the available candidaites.
|
||||
:param parent: The candidate that requires ``requirement`` as a
|
||||
dependency, or None if ``requirement`` is one of the root
|
||||
requirements passed in from ``Resolver.resolve()``.
|
||||
"""
|
||||
|
||||
def backtracking(self, candidate):
|
||||
"""Called when the resolver rejects a candidate during backtracking.
|
||||
"""
|
||||
"""Called when rejecting a candidate during backtracking."""
|
||||
|
||||
def pinning(self, candidate):
|
||||
"""Called when adding a candidate to the potential solution.
|
||||
"""
|
||||
"""Called when adding a candidate to the potential solution."""
|
||||
|
||||
+53
-52
@@ -1,7 +1,7 @@
|
||||
import collections
|
||||
|
||||
from .providers import AbstractResolver
|
||||
from .structs import DirectedGraph
|
||||
from .structs import DirectedGraph, build_iter_view
|
||||
|
||||
|
||||
RequirementInformation = collections.namedtuple(
|
||||
@@ -68,22 +68,18 @@ class Criterion(object):
|
||||
|
||||
def __repr__(self):
|
||||
requirements = ", ".join(
|
||||
"{!r} from {!r}".format(req, parent)
|
||||
"({!r}, via={!r})".format(req, parent)
|
||||
for req, parent in self.information
|
||||
)
|
||||
return "<Criterion {}>".format(requirements)
|
||||
return "Criterion({})".format(requirements)
|
||||
|
||||
@classmethod
|
||||
def from_requirement(cls, provider, requirement, parent):
|
||||
"""Build an instance from a requirement.
|
||||
"""
|
||||
candidates = provider.find_matches(requirement)
|
||||
criterion = cls(
|
||||
candidates=candidates,
|
||||
information=[RequirementInformation(requirement, parent)],
|
||||
incompatibilities=[],
|
||||
)
|
||||
if not candidates:
|
||||
"""Build an instance from a requirement."""
|
||||
cands = build_iter_view(provider.find_matches([requirement]))
|
||||
infos = [RequirementInformation(requirement, parent)]
|
||||
criterion = cls(cands, infos, incompatibilities=[])
|
||||
if not cands:
|
||||
raise RequirementsConflicted(criterion)
|
||||
return criterion
|
||||
|
||||
@@ -94,17 +90,12 @@ class Criterion(object):
|
||||
return (i.parent for i in self.information)
|
||||
|
||||
def merged_with(self, provider, requirement, parent):
|
||||
"""Build a new instance from this and a new requirement.
|
||||
"""
|
||||
"""Build a new instance from this and a new requirement."""
|
||||
infos = list(self.information)
|
||||
infos.append(RequirementInformation(requirement, parent))
|
||||
candidates = [
|
||||
c
|
||||
for c in self.candidates
|
||||
if provider.is_satisfied_by(requirement, c)
|
||||
]
|
||||
criterion = type(self)(candidates, infos, list(self.incompatibilities))
|
||||
if not candidates:
|
||||
cands = build_iter_view(provider.find_matches([r for r, _ in infos]))
|
||||
criterion = type(self)(cands, infos, list(self.incompatibilities))
|
||||
if not cands:
|
||||
raise RequirementsConflicted(criterion)
|
||||
return criterion
|
||||
|
||||
@@ -113,13 +104,12 @@ class Criterion(object):
|
||||
|
||||
Returns the new instance, or None if we still have no valid candidates.
|
||||
"""
|
||||
cands = self.candidates.excluding(candidate)
|
||||
if not cands:
|
||||
return None
|
||||
incompats = list(self.incompatibilities)
|
||||
incompats.append(candidate)
|
||||
candidates = [c for c in self.candidates if c != candidate]
|
||||
if not candidates:
|
||||
return None
|
||||
criterion = type(self)(candidates, list(self.information), incompats)
|
||||
return criterion
|
||||
return type(self)(cands, list(self.information), incompats)
|
||||
|
||||
|
||||
class ResolutionError(ResolverException):
|
||||
@@ -174,12 +164,13 @@ class Resolution(object):
|
||||
state = State(mapping=collections.OrderedDict(), criteria={})
|
||||
else:
|
||||
state = State(
|
||||
mapping=base.mapping.copy(), criteria=base.criteria.copy(),
|
||||
mapping=base.mapping.copy(),
|
||||
criteria=base.criteria.copy(),
|
||||
)
|
||||
self._states.append(state)
|
||||
|
||||
def _merge_into_criterion(self, requirement, parent):
|
||||
self._r.adding_requirement(requirement)
|
||||
self._r.adding_requirement(requirement, parent)
|
||||
name = self._p.identify(requirement)
|
||||
try:
|
||||
crit = self.state.criteria[name]
|
||||
@@ -191,12 +182,10 @@ class Resolution(object):
|
||||
|
||||
def _get_criterion_item_preference(self, item):
|
||||
name, criterion = item
|
||||
try:
|
||||
pinned = self.state.mapping[name]
|
||||
except KeyError:
|
||||
pinned = None
|
||||
return self._p.get_preference(
|
||||
pinned, criterion.candidates, criterion.information,
|
||||
self.state.mapping.get(name),
|
||||
criterion.candidates.for_preference(),
|
||||
criterion.information,
|
||||
)
|
||||
|
||||
def _is_current_pin_satisfying(self, name, criterion):
|
||||
@@ -218,13 +207,24 @@ class Resolution(object):
|
||||
|
||||
def _attempt_to_pin_criterion(self, name, criterion):
|
||||
causes = []
|
||||
for candidate in reversed(criterion.candidates):
|
||||
for candidate in criterion.candidates:
|
||||
try:
|
||||
criteria = self._get_criteria_to_update(candidate)
|
||||
except RequirementsConflicted as e:
|
||||
causes.append(e.criterion)
|
||||
continue
|
||||
|
||||
# Check the newly-pinned candidate actually works. This should
|
||||
# always pass under normal circumstances, but in the case of a
|
||||
# faulty provider, we will raise an error to notify the implementer
|
||||
# to fix find_matches() and/or is_satisfied_by().
|
||||
satisfied = all(
|
||||
self._p.is_satisfied_by(r, candidate)
|
||||
for r in criterion.iter_requirement()
|
||||
)
|
||||
if not satisfied:
|
||||
raise InconsistentCandidate(candidate, criterion)
|
||||
|
||||
# Put newly-pinned candidate at the end. This is essential because
|
||||
# backtracking looks at this mapping to get the last pin.
|
||||
self._r.pinning(candidate)
|
||||
@@ -232,13 +232,6 @@ class Resolution(object):
|
||||
self.state.mapping[name] = candidate
|
||||
self.state.criteria.update(criteria)
|
||||
|
||||
# Check the newly-pinned candidate actually works. This should
|
||||
# always pass under normal circumstances, but in the case of a
|
||||
# faulty provider, we will raise an error to notify the implementer
|
||||
# to fix find_matches() and/or is_satisfied_by().
|
||||
if not self._is_current_pin_satisfying(name, criterion):
|
||||
raise InconsistentCandidate(candidate, criterion)
|
||||
|
||||
return []
|
||||
|
||||
# All candidates tried, nothing works. This criterion is a dead
|
||||
@@ -246,23 +239,32 @@ class Resolution(object):
|
||||
return causes
|
||||
|
||||
def _backtrack(self):
|
||||
# We need at least 3 states here:
|
||||
# (a) One known not working, to drop.
|
||||
# (b) One to backtrack to.
|
||||
# (c) One to restore state (b) to its state prior to candidate-pinning,
|
||||
# so we can pin another one instead.
|
||||
while len(self._states) >= 3:
|
||||
del self._states[-1]
|
||||
# Drop the current state, it's known not to work.
|
||||
del self._states[-1]
|
||||
|
||||
# Retract the last candidate pin, and create a new (b).
|
||||
name, candidate = self._states.pop().mapping.popitem()
|
||||
# We need at least 2 states here:
|
||||
# (a) One to backtrack to.
|
||||
# (b) One to restore state (a) to its state prior to candidate-pinning,
|
||||
# so we can pin another one instead.
|
||||
|
||||
while len(self._states) >= 2:
|
||||
# Retract the last candidate pin.
|
||||
prev_state = self._states.pop()
|
||||
try:
|
||||
name, candidate = prev_state.mapping.popitem()
|
||||
except KeyError:
|
||||
continue
|
||||
self._r.backtracking(candidate)
|
||||
|
||||
# Create a new state to work on, with the newly known not-working
|
||||
# candidate excluded.
|
||||
self._push_new_state()
|
||||
|
||||
# Mark the retracted candidate as incompatible.
|
||||
criterion = self.state.criteria[name].excluded_of(candidate)
|
||||
if criterion is None:
|
||||
# This state still does not work. Try the still previous state.
|
||||
del self._states[-1]
|
||||
continue
|
||||
self.state.criteria[name] = criterion
|
||||
|
||||
@@ -376,8 +378,7 @@ def _build_result(state):
|
||||
|
||||
|
||||
class Resolver(AbstractResolver):
|
||||
"""The thing that performs the actual resolution work.
|
||||
"""
|
||||
"""The thing that performs the actual resolution work."""
|
||||
|
||||
base_exception = ResolverException
|
||||
|
||||
|
||||
Vendored
+83
-8
@@ -1,6 +1,8 @@
|
||||
from .compat import collections_abc
|
||||
|
||||
|
||||
class DirectedGraph(object):
|
||||
"""A graph structure with directed edges.
|
||||
"""
|
||||
"""A graph structure with directed edges."""
|
||||
|
||||
def __init__(self):
|
||||
self._vertices = set()
|
||||
@@ -17,8 +19,7 @@ class DirectedGraph(object):
|
||||
return key in self._vertices
|
||||
|
||||
def copy(self):
|
||||
"""Return a shallow copy of this graph.
|
||||
"""
|
||||
"""Return a shallow copy of this graph."""
|
||||
other = DirectedGraph()
|
||||
other._vertices = set(self._vertices)
|
||||
other._forwards = {k: set(v) for k, v in self._forwards.items()}
|
||||
@@ -26,8 +27,7 @@ class DirectedGraph(object):
|
||||
return other
|
||||
|
||||
def add(self, key):
|
||||
"""Add a new vertex to the graph.
|
||||
"""
|
||||
"""Add a new vertex to the graph."""
|
||||
if key in self._vertices:
|
||||
raise ValueError("vertex exists")
|
||||
self._vertices.add(key)
|
||||
@@ -35,8 +35,7 @@ class DirectedGraph(object):
|
||||
self._backwards[key] = set()
|
||||
|
||||
def remove(self, key):
|
||||
"""Remove a vertex from the graph, disconnecting all edges from/to it.
|
||||
"""
|
||||
"""Remove a vertex from the graph, disconnecting all edges from/to it."""
|
||||
self._vertices.remove(key)
|
||||
for f in self._forwards.pop(key):
|
||||
self._backwards[f].remove(key)
|
||||
@@ -66,3 +65,79 @@ class DirectedGraph(object):
|
||||
|
||||
def iter_parents(self, key):
|
||||
return iter(self._backwards[key])
|
||||
|
||||
|
||||
class _FactoryIterableView(object):
|
||||
"""Wrap an iterator factory returned by `find_matches()`.
|
||||
|
||||
Calling `iter()` on this class would invoke the underlying iterator
|
||||
factory, making it a "collection with ordering" that can be iterated
|
||||
through multiple times, but lacks random access methods presented in
|
||||
built-in Python sequence types.
|
||||
"""
|
||||
|
||||
def __init__(self, factory):
|
||||
self._factory = factory
|
||||
|
||||
def __bool__(self):
|
||||
try:
|
||||
next(self._factory())
|
||||
except StopIteration:
|
||||
return False
|
||||
return True
|
||||
|
||||
__nonzero__ = __bool__ # XXX: Python 2.
|
||||
|
||||
def __iter__(self):
|
||||
return self._factory()
|
||||
|
||||
def for_preference(self):
|
||||
"""Provide an candidate iterable for `get_preference()`"""
|
||||
return self._factory()
|
||||
|
||||
def excluding(self, candidate):
|
||||
"""Create a new `Candidates` instance excluding `candidate`."""
|
||||
|
||||
def factory():
|
||||
return (c for c in self._factory() if c != candidate)
|
||||
|
||||
return type(self)(factory)
|
||||
|
||||
|
||||
class _SequenceIterableView(object):
|
||||
"""Wrap an iterable returned by find_matches().
|
||||
|
||||
This is essentially just a proxy to the underlying sequence that provides
|
||||
the same interface as `_FactoryIterableView`.
|
||||
"""
|
||||
|
||||
def __init__(self, sequence):
|
||||
self._sequence = sequence
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._sequence)
|
||||
|
||||
__nonzero__ = __bool__ # XXX: Python 2.
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._sequence)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._sequence)
|
||||
|
||||
def for_preference(self):
|
||||
"""Provide an candidate iterable for `get_preference()`"""
|
||||
return self._sequence
|
||||
|
||||
def excluding(self, candidate):
|
||||
"""Create a new instance excluding `candidate`."""
|
||||
return type(self)([c for c in self._sequence if c != candidate])
|
||||
|
||||
|
||||
def build_iter_view(matches):
|
||||
"""Build an iterable view from the value returned by `find_matches()`."""
|
||||
if callable(matches):
|
||||
return _FactoryIterableView(matches)
|
||||
if not isinstance(matches, collections_abc.Sequence):
|
||||
matches = list(matches)
|
||||
return _SequenceIterableView(matches)
|
||||
|
||||
Vendored
+851
-341
File diff suppressed because it is too large
Load Diff
Vendored
+11
-9
@@ -29,7 +29,7 @@ import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.14.0"
|
||||
__version__ = "1.15.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
@@ -890,12 +890,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'):
|
||||
- `str` -> encoded to `bytes`
|
||||
- `bytes` -> `bytes`
|
||||
"""
|
||||
if isinstance(s, binary_type):
|
||||
return s
|
||||
if isinstance(s, text_type):
|
||||
return s.encode(encoding, errors)
|
||||
elif isinstance(s, binary_type):
|
||||
return s
|
||||
else:
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||
@@ -909,12 +908,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
if not isinstance(s, (text_type, binary_type)):
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
# Optimization: Fast return for the common case.
|
||||
if type(s) is str:
|
||||
return s
|
||||
if PY2 and isinstance(s, text_type):
|
||||
s = s.encode(encoding, errors)
|
||||
return s.encode(encoding, errors)
|
||||
elif PY3 and isinstance(s, binary_type):
|
||||
s = s.decode(encoding, errors)
|
||||
return s.decode(encoding, errors)
|
||||
elif not isinstance(s, (text_type, binary_type)):
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
return s
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user