-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from reagento/feature/generics
support concrete generics
- Loading branch information
Showing
13 changed files
with
1,611 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ | |
exclude_lines = | ||
pragma: not covered | ||
@overload | ||
[run] | ||
omit = | ||
src/dishka/_adaptix/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import TYPE_CHECKING, Any, Callable, Tuple, Type, TypeVar, Union | ||
|
||
K_contra = TypeVar('K_contra', contravariant=True) | ||
V_co = TypeVar('V_co', covariant=True) | ||
T = TypeVar('T') | ||
|
||
Loader = Callable[[Any], V_co] | ||
Dumper = Callable[[K_contra], Any] | ||
Converter = Callable[..., Any] | ||
Coercer = Callable[[Any], Any] | ||
|
||
TypeHint = Any | ||
|
||
VarTuple = Tuple[T, ...] | ||
|
||
Catchable = Union[Type[BaseException], VarTuple[Type[BaseException]]] | ||
|
||
# https://github.com/python/typing/issues/684#issuecomment-548203158 | ||
if TYPE_CHECKING: | ||
EllipsisType = ellipsis # pylint: disable=undefined-variable # noqa: F821 | ||
else: | ||
EllipsisType = type(Ellipsis) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import importlib.metadata | ||
import itertools | ||
import re | ||
import sys | ||
from abc import ABC, abstractmethod | ||
from typing import Any, Iterable | ||
|
||
from .common import VarTuple | ||
|
||
|
||
def _true(): | ||
return True | ||
|
||
|
||
def _false(): | ||
return False | ||
|
||
|
||
class Requirement(ABC): | ||
__slots__ = ('is_meet', '__bool__', '__dict__') | ||
|
||
def __init__(self): | ||
self.is_meet = self._evaluate() | ||
self.__bool__ = _true if self.is_meet else _false | ||
|
||
@abstractmethod | ||
def _evaluate(self) -> bool: | ||
... | ||
|
||
@property | ||
@abstractmethod | ||
def fail_reason(self) -> str: | ||
... | ||
|
||
|
||
class PythonVersionRequirement(Requirement): | ||
def __init__(self, min_version: VarTuple[int]): | ||
self.min_version = min_version | ||
super().__init__() | ||
|
||
def _evaluate(self) -> bool: | ||
return sys.version_info >= self.min_version | ||
|
||
@property | ||
def fail_reason(self) -> str: | ||
return f'Python >= {".".join(map(str, self.min_version))} is required' | ||
|
||
|
||
class DistributionRequirement(Requirement): | ||
def __init__(self, distribution_name: str): | ||
self.distribution_name = distribution_name | ||
super().__init__() | ||
|
||
def _evaluate(self) -> bool: | ||
try: | ||
importlib.metadata.distribution(self.distribution_name) | ||
except importlib.metadata.PackageNotFoundError: | ||
return False | ||
return True | ||
|
||
@property | ||
def fail_reason(self) -> str: | ||
return f'Installed distribution {self.distribution_name!r} is required' | ||
|
||
|
||
class DistributionVersionRequirement(DistributionRequirement): | ||
# Pattern from PEP 440 | ||
_VERSION_PATTERN = r""" | ||
v? | ||
(?: | ||
(?:(?P<epoch>[0-9]+)!)? # epoch | ||
(?P<release>[0-9]+(?:\.[0-9]+)*) # release segment | ||
(?P<pre> # pre-release | ||
[-_\.]? | ||
(?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview)) | ||
[-_\.]? | ||
(?P<pre_n>[0-9]+)? | ||
)? | ||
(?P<post> # post release | ||
(?:-(?P<post_n1>[0-9]+)) | ||
| | ||
(?: | ||
[-_\.]? | ||
(?P<post_l>post|rev|r) | ||
[-_\.]? | ||
(?P<post_n2>[0-9]+)? | ||
) | ||
)? | ||
(?P<dev> # dev release | ||
[-_\.]? | ||
(?P<dev_l>dev) | ||
[-_\.]? | ||
(?P<dev_n>[0-9]+)? | ||
)? | ||
) | ||
(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version | ||
""" | ||
|
||
_version_regex = re.compile( | ||
r"^\s*" + _VERSION_PATTERN + r"\s*$", | ||
re.VERBOSE | re.IGNORECASE, | ||
) | ||
|
||
def __init__(self, distribution_name: str, min_version: str): | ||
self.min_version = min_version | ||
super().__init__(distribution_name) | ||
|
||
def _evaluate(self) -> bool: | ||
try: | ||
distribution = importlib.metadata.distribution(self.distribution_name) | ||
except importlib.metadata.PackageNotFoundError: | ||
return False | ||
return ( | ||
self._make_comparator(distribution.version) | ||
>= | ||
self._make_comparator(self.min_version) | ||
) | ||
|
||
def _remove_trailing_zeros(self, data: Iterable[int]) -> VarTuple[int]: | ||
return tuple(reversed(tuple(itertools.dropwhile(lambda x: x == 0, reversed(tuple(data)))))) | ||
|
||
def _make_comparator(self, version: str) -> VarTuple[Any]: | ||
match = self._version_regex.match(version) | ||
if match is None: | ||
raise ValueError | ||
return ( | ||
match.group('epoch') or 0, | ||
self._remove_trailing_zeros(int(i) for i in match.group('release').split('.')), | ||
match.group('pre') is not None, | ||
match.group('dev') is not None, | ||
) | ||
|
||
@property | ||
def fail_reason(self) -> str: | ||
return f'Installed distribution {self.distribution_name!r} of {self.min_version!r} is required' | ||
|
||
|
||
class PythonImplementationRequirement(Requirement): | ||
def __init__(self, implementation_name: str): | ||
self.implementation_name = implementation_name | ||
super().__init__() | ||
|
||
def _evaluate(self) -> bool: | ||
return sys.implementation.name == self.implementation_name | ||
|
||
@property | ||
def fail_reason(self) -> str: | ||
return f'{self.implementation_name} is required' | ||
|
||
|
||
HAS_PY_39 = PythonVersionRequirement((3, 9)) | ||
HAS_ANNOTATED = HAS_PY_39 | ||
HAS_STD_CLASSES_GENERICS = HAS_PY_39 | ||
|
||
HAS_PY_310 = PythonVersionRequirement((3, 10)) | ||
HAS_TYPE_UNION_OP = HAS_PY_310 | ||
HAS_TYPE_GUARD = HAS_PY_310 | ||
HAS_TYPE_ALIAS = HAS_PY_310 | ||
HAS_PARAM_SPEC = HAS_PY_310 | ||
|
||
HAS_PY_311 = PythonVersionRequirement((3, 11)) | ||
HAS_NATIVE_EXC_GROUP = HAS_PY_311 | ||
HAS_TYPED_DICT_REQUIRED = HAS_PY_311 | ||
HAS_SELF_TYPE = HAS_PY_311 | ||
HAS_TV_TUPLE = HAS_PY_311 | ||
HAS_UNPACK = HAS_PY_311 | ||
|
||
HAS_PY_312 = PythonVersionRequirement((3, 12)) | ||
HAS_TV_SYNTAX = HAS_PY_312 | ||
|
||
HAS_SUPPORTED_ATTRS_PKG = DistributionVersionRequirement('attrs', '21.3.0') | ||
HAS_ATTRS_PKG = DistributionRequirement('attrs') | ||
|
||
HAS_SUPPORTED_SQLALCHEMY_PKG = DistributionVersionRequirement('sqlalchemy', '2.0.0') | ||
HAS_SQLALCHEMY_PKG = DistributionRequirement('sqlalchemy') | ||
|
||
IS_CPYTHON = PythonImplementationRequirement('cpython') | ||
IS_PYPY = PythonImplementationRequirement('pypy') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from .basic_utils import ( | ||
create_union, | ||
get_all_type_hints, | ||
is_bare_generic, | ||
is_generic, | ||
is_generic_class, | ||
is_named_tuple_class, | ||
is_new_type, | ||
is_parametrized, | ||
is_protocol, | ||
is_subclass_soft, | ||
is_typed_dict_class, | ||
is_user_defined_generic, | ||
strip_alias, | ||
) | ||
from .norm_utils import is_class_var, strip_tags | ||
from .normalize_type import ( | ||
AnyNormTypeVarLike, | ||
BaseNormType, | ||
NormParamSpecMarker, | ||
NormTV, | ||
NormTVTuple, | ||
NormTypeAlias, | ||
make_norm_type, | ||
normalize_type, | ||
) |
Oops, something went wrong.