type-analyzer 0.1.2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- type_analyzer-0.2.0/PKG-INFO +142 -0
- type_analyzer-0.2.0/README.md +116 -0
- {type_analyzer-0.1.2 → type_analyzer-0.2.0}/pyproject.toml +4 -3
- type_analyzer-0.2.0/type_analyzer/_core/matching_types.py +154 -0
- type_analyzer-0.1.2/PKG-INFO +0 -75
- type_analyzer-0.1.2/README.md +0 -50
- type_analyzer-0.1.2/type_analyzer/_core/matching_types.py +0 -129
- {type_analyzer-0.1.2 → type_analyzer-0.2.0}/.gitignore +0 -0
- {type_analyzer-0.1.2 → type_analyzer-0.2.0}/LICENSE +0 -0
- {type_analyzer-0.1.2 → type_analyzer-0.2.0}/type_analyzer/__init__.py +0 -0
- {type_analyzer-0.1.2 → type_analyzer-0.2.0}/type_analyzer/_core/__init__.py +0 -0
- {type_analyzer-0.1.2 → type_analyzer-0.2.0}/type_analyzer/py.typed +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: type-analyzer
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Functions for introspecting Python type hints.
|
|
5
|
+
Project-URL: Repository, https://github.com/100nm/type-analyzer
|
|
6
|
+
Author: remimd
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: <3.16,>=3.12
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# type-analyzer
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/type-analyzer)
|
|
30
|
+
[](https://pypistats.org/packages/type-analyzer)
|
|
31
|
+
[](https://github.com/100nm/type-analyzer/stargazers)
|
|
32
|
+
[](https://github.com/100nm/type-analyzer/actions/workflows/ci.yml)
|
|
33
|
+
[](https://github.com/astral-sh/ruff)
|
|
34
|
+
|
|
35
|
+
Functions for introspecting Python type hints.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
⚠️ _Requires Python 3.12 or higher_
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install type-analyzer
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
### `matching_types`
|
|
48
|
+
|
|
49
|
+
`matching_types` returns every type a type hint matches, without duplicates. Unions
|
|
50
|
+
are unpacked into their members, and `Annotated` metadata is removed:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from typing import Annotated
|
|
54
|
+
|
|
55
|
+
from type_analyzer import matching_types
|
|
56
|
+
|
|
57
|
+
matching_types(str | int)
|
|
58
|
+
# => (str, int)
|
|
59
|
+
|
|
60
|
+
matching_types(Annotated[str, "metadata"] | None)
|
|
61
|
+
# => (str, NoneType)
|
|
62
|
+
|
|
63
|
+
matching_types(list[Annotated[int, "metadata"]])
|
|
64
|
+
# => (list[int],)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Options
|
|
68
|
+
|
|
69
|
+
`MatchingTypesConfig` adds more types to the result. Every option is disabled by
|
|
70
|
+
default:
|
|
71
|
+
|
|
72
|
+
| Option | Effect |
|
|
73
|
+
|-------------------------|----------------------------------------------------------|
|
|
74
|
+
| `ignore_none` | Leaves out `None`. |
|
|
75
|
+
| `with_bases` | Adds the base classes, with their type arguments. |
|
|
76
|
+
| `with_origin` | Adds the origin of generic types (`list[str]` → `list`). |
|
|
77
|
+
| `with_type_alias_value` | Adds the value of type aliases. |
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from type_analyzer import MatchingTypesConfig, matching_types
|
|
81
|
+
|
|
82
|
+
# ----- ignore_none -----
|
|
83
|
+
|
|
84
|
+
config = MatchingTypesConfig(ignore_none=True)
|
|
85
|
+
matching_types(str | None, config)
|
|
86
|
+
# => (str,)
|
|
87
|
+
|
|
88
|
+
# ----- with_bases -----
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class A[T]: ...
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class B[T]: ...
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class C[T1, T2](A[T1], B[T2]): ...
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
config = MatchingTypesConfig(with_bases=True)
|
|
101
|
+
matching_types(C[str, int], config)
|
|
102
|
+
# => (C[str, int], A[str], B[int])
|
|
103
|
+
|
|
104
|
+
# ----- with_origin -----
|
|
105
|
+
|
|
106
|
+
config = MatchingTypesConfig(with_origin=True)
|
|
107
|
+
matching_types(list[str], config)
|
|
108
|
+
# => (list[str], list)
|
|
109
|
+
|
|
110
|
+
# ----- with_type_alias_value -----
|
|
111
|
+
|
|
112
|
+
type StringOr[T] = str | T
|
|
113
|
+
|
|
114
|
+
config = MatchingTypesConfig(with_type_alias_value=True)
|
|
115
|
+
matching_types(StringOr[int], config)
|
|
116
|
+
# => (StringOr[int], str, int)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Options can be combined: `MatchingTypesConfig(with_bases=True, with_origin=True)`.
|
|
120
|
+
|
|
121
|
+
### `iter_matching_types`
|
|
122
|
+
|
|
123
|
+
`iter_matching_types` is the lazy counterpart of `matching_types`: it yields the
|
|
124
|
+
types one by one, and keeps the duplicates.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from type_analyzer import MatchingTypesConfig, iter_matching_types
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class Left: ...
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Right: ...
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Both(Left, Right): ...
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
config = MatchingTypesConfig(with_bases=True)
|
|
140
|
+
list(iter_matching_types(Both, config))
|
|
141
|
+
# => [Both, Left, object, Right, object]
|
|
142
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# type-analyzer
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/type-analyzer)
|
|
4
|
+
[](https://pypistats.org/packages/type-analyzer)
|
|
5
|
+
[](https://github.com/100nm/type-analyzer/stargazers)
|
|
6
|
+
[](https://github.com/100nm/type-analyzer/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/astral-sh/ruff)
|
|
8
|
+
|
|
9
|
+
Functions for introspecting Python type hints.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
⚠️ _Requires Python 3.12 or higher_
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install type-analyzer
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### `matching_types`
|
|
22
|
+
|
|
23
|
+
`matching_types` returns every type a type hint matches, without duplicates. Unions
|
|
24
|
+
are unpacked into their members, and `Annotated` metadata is removed:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from typing import Annotated
|
|
28
|
+
|
|
29
|
+
from type_analyzer import matching_types
|
|
30
|
+
|
|
31
|
+
matching_types(str | int)
|
|
32
|
+
# => (str, int)
|
|
33
|
+
|
|
34
|
+
matching_types(Annotated[str, "metadata"] | None)
|
|
35
|
+
# => (str, NoneType)
|
|
36
|
+
|
|
37
|
+
matching_types(list[Annotated[int, "metadata"]])
|
|
38
|
+
# => (list[int],)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Options
|
|
42
|
+
|
|
43
|
+
`MatchingTypesConfig` adds more types to the result. Every option is disabled by
|
|
44
|
+
default:
|
|
45
|
+
|
|
46
|
+
| Option | Effect |
|
|
47
|
+
|-------------------------|----------------------------------------------------------|
|
|
48
|
+
| `ignore_none` | Leaves out `None`. |
|
|
49
|
+
| `with_bases` | Adds the base classes, with their type arguments. |
|
|
50
|
+
| `with_origin` | Adds the origin of generic types (`list[str]` → `list`). |
|
|
51
|
+
| `with_type_alias_value` | Adds the value of type aliases. |
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from type_analyzer import MatchingTypesConfig, matching_types
|
|
55
|
+
|
|
56
|
+
# ----- ignore_none -----
|
|
57
|
+
|
|
58
|
+
config = MatchingTypesConfig(ignore_none=True)
|
|
59
|
+
matching_types(str | None, config)
|
|
60
|
+
# => (str,)
|
|
61
|
+
|
|
62
|
+
# ----- with_bases -----
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class A[T]: ...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class B[T]: ...
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class C[T1, T2](A[T1], B[T2]): ...
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
config = MatchingTypesConfig(with_bases=True)
|
|
75
|
+
matching_types(C[str, int], config)
|
|
76
|
+
# => (C[str, int], A[str], B[int])
|
|
77
|
+
|
|
78
|
+
# ----- with_origin -----
|
|
79
|
+
|
|
80
|
+
config = MatchingTypesConfig(with_origin=True)
|
|
81
|
+
matching_types(list[str], config)
|
|
82
|
+
# => (list[str], list)
|
|
83
|
+
|
|
84
|
+
# ----- with_type_alias_value -----
|
|
85
|
+
|
|
86
|
+
type StringOr[T] = str | T
|
|
87
|
+
|
|
88
|
+
config = MatchingTypesConfig(with_type_alias_value=True)
|
|
89
|
+
matching_types(StringOr[int], config)
|
|
90
|
+
# => (StringOr[int], str, int)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Options can be combined: `MatchingTypesConfig(with_bases=True, with_origin=True)`.
|
|
94
|
+
|
|
95
|
+
### `iter_matching_types`
|
|
96
|
+
|
|
97
|
+
`iter_matching_types` is the lazy counterpart of `matching_types`: it yields the
|
|
98
|
+
types one by one, and keeps the duplicates.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from type_analyzer import MatchingTypesConfig, iter_matching_types
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Left: ...
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Right: ...
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Both(Left, Right): ...
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
config = MatchingTypesConfig(with_bases=True)
|
|
114
|
+
list(iter_matching_types(Both, config))
|
|
115
|
+
# => [Both, Left, object, Right, object]
|
|
116
|
+
```
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "type-analyzer"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Functions for introspecting Python type hints."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
license-files = ["LICENSE"]
|
|
7
7
|
readme = "README.md"
|
|
8
|
-
requires-python = ">=3.12, <3.
|
|
8
|
+
requires-python = ">=3.12, <3.16"
|
|
9
9
|
authors = [{ name = "remimd" }]
|
|
10
10
|
keywords = []
|
|
11
11
|
classifiers = [
|
|
@@ -19,6 +19,7 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3.12",
|
|
20
20
|
"Programming Language :: Python :: 3.13",
|
|
21
21
|
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Programming Language :: Python :: 3.15",
|
|
22
23
|
"Operating System :: OS Independent",
|
|
23
24
|
"Intended Audience :: Developers",
|
|
24
25
|
"Natural Language :: English",
|
|
@@ -81,7 +82,7 @@ skip-magic-trailing-comma = false
|
|
|
81
82
|
line-ending = "auto"
|
|
82
83
|
|
|
83
84
|
[tool.ruff.lint]
|
|
84
|
-
extend-select = ["I", "N"]
|
|
85
|
+
extend-select = ["F", "I", "N"]
|
|
85
86
|
fixable = ["ALL"]
|
|
86
87
|
|
|
87
88
|
[tool.uv]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from types import NoneType, UnionType, get_original_bases
|
|
4
|
+
from typing import (
|
|
5
|
+
Annotated,
|
|
6
|
+
Any,
|
|
7
|
+
Generic,
|
|
8
|
+
Self,
|
|
9
|
+
TypeAliasType,
|
|
10
|
+
TypeVar,
|
|
11
|
+
Union,
|
|
12
|
+
get_args,
|
|
13
|
+
get_origin,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(repr=False, eq=False, frozen=True, kw_only=True, slots=True)
|
|
18
|
+
class MatchingTypesConfig:
|
|
19
|
+
ignore_none: bool = field(default=False)
|
|
20
|
+
with_bases: bool = field(default=False)
|
|
21
|
+
with_origin: bool = field(default=False)
|
|
22
|
+
with_type_alias_value: bool = field(default=False)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
26
|
+
class _TypeHint:
|
|
27
|
+
value: Any
|
|
28
|
+
origin: Any
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def args(self) -> tuple[Any, ...]:
|
|
32
|
+
return get_args(self.value)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def generics(self) -> Mapping[TypeVar, Any]:
|
|
36
|
+
parameters = getattr(self.origin, "__parameters__", ())
|
|
37
|
+
return dict(zip(parameters, self.args))
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def is_none(self) -> bool:
|
|
41
|
+
return self.value in (None, NoneType)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def is_union(self) -> bool:
|
|
45
|
+
return self.origin is Union or isinstance(self.value, UnionType)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def target(self) -> Any:
|
|
49
|
+
return self.value if self.origin is None else self.origin
|
|
50
|
+
|
|
51
|
+
def apply_generics(self, generics: Mapping[TypeVar, Any]) -> Self:
|
|
52
|
+
if isinstance(self.value, TypeVar):
|
|
53
|
+
return self.from_value(generics.get(self.value, self.value))
|
|
54
|
+
|
|
55
|
+
if self.origin is None:
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
if self.origin is Annotated:
|
|
59
|
+
return self.from_value(self.args[0]).apply_generics(generics)
|
|
60
|
+
|
|
61
|
+
args = tuple(
|
|
62
|
+
self.from_value(arg).apply_generics(generics).value for arg in self.args
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if self.is_union:
|
|
66
|
+
return self.from_value(Union[*args])
|
|
67
|
+
|
|
68
|
+
return self.from_value(self.origin[*args])
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_value(cls, value: Any) -> Self:
|
|
72
|
+
return cls(value, get_origin(value))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
_empty_config = MatchingTypesConfig()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def iter_matching_types(
|
|
79
|
+
type_hint: Any,
|
|
80
|
+
/,
|
|
81
|
+
config: MatchingTypesConfig | None = None,
|
|
82
|
+
) -> Iterator[Any]:
|
|
83
|
+
config = config or _empty_config
|
|
84
|
+
return _iter_matching_types((type_hint,), config, {})
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def matching_types(
|
|
88
|
+
type_hint: Any,
|
|
89
|
+
/,
|
|
90
|
+
config: MatchingTypesConfig | None = None,
|
|
91
|
+
) -> tuple[Any, ...]:
|
|
92
|
+
return tuple(dict.fromkeys(iter_matching_types(type_hint, config)))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _iter_bases(type_hint: _TypeHint, config: MatchingTypesConfig) -> Iterator[Any]:
|
|
96
|
+
try:
|
|
97
|
+
bases = get_original_bases(type_hint.target)
|
|
98
|
+
except TypeError:
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
yield from _iter_matching_types(bases, config, type_hint.generics)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _iter_matching_types(
|
|
105
|
+
type_hints: Sequence[Any],
|
|
106
|
+
config: MatchingTypesConfig,
|
|
107
|
+
generics: Mapping[TypeVar, Any],
|
|
108
|
+
) -> Iterator[Any]:
|
|
109
|
+
for value in type_hints:
|
|
110
|
+
type_hint = _TypeHint.from_value(value)
|
|
111
|
+
|
|
112
|
+
if type_hint.origin is Generic:
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
yield from _iter_type_hint_matching_types(
|
|
116
|
+
type_hint.apply_generics(generics),
|
|
117
|
+
config,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _iter_type_alias_value(
|
|
122
|
+
type_hint: _TypeHint,
|
|
123
|
+
config: MatchingTypesConfig,
|
|
124
|
+
) -> Iterator[Any]:
|
|
125
|
+
type_alias = type_hint.target
|
|
126
|
+
|
|
127
|
+
if not isinstance(type_alias, TypeAliasType):
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
values = (type_alias.__value__,)
|
|
131
|
+
yield from _iter_matching_types(values, config, type_hint.generics)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _iter_type_hint_matching_types(
|
|
135
|
+
type_hint: _TypeHint,
|
|
136
|
+
config: MatchingTypesConfig,
|
|
137
|
+
) -> Iterator[Any]:
|
|
138
|
+
if config.ignore_none and type_hint.is_none:
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
if type_hint.is_union:
|
|
142
|
+
yield from _iter_matching_types(type_hint.args, config, {})
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
yield type_hint.value
|
|
146
|
+
|
|
147
|
+
if config.with_origin and type_hint.origin is not None:
|
|
148
|
+
yield type_hint.origin
|
|
149
|
+
|
|
150
|
+
if config.with_bases:
|
|
151
|
+
yield from _iter_bases(type_hint, config)
|
|
152
|
+
|
|
153
|
+
if config.with_type_alias_value:
|
|
154
|
+
yield from _iter_type_alias_value(type_hint, config)
|
type_analyzer-0.1.2/PKG-INFO
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: type-analyzer
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: Functions for introspecting Python type hints.
|
|
5
|
-
Project-URL: Repository, https://github.com/100nm/type-analyzer
|
|
6
|
-
Author: remimd
|
|
7
|
-
License-Expression: MIT
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Natural Language :: English
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Classifier: Programming Language :: Python
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
21
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Classifier: Typing :: Typed
|
|
23
|
-
Requires-Python: <3.15,>=3.12
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
|
|
26
|
-
# type-analyzer
|
|
27
|
-
|
|
28
|
-
[](https://github.com/100nm/type-analyzer)
|
|
29
|
-
[](https://pypi.org/project/type-analyzer)
|
|
30
|
-
[](https://pypistats.org/packages/type-analyzer)
|
|
31
|
-
[](https://github.com/astral-sh/ruff)
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
⚠️ _Requires Python 3.12 or higher_
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pip install type-analyzer
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Quick start
|
|
42
|
-
|
|
43
|
-
### matching_types
|
|
44
|
-
|
|
45
|
-
```python
|
|
46
|
-
from type_analyzer import MatchingTypesConfig, matching_types
|
|
47
|
-
|
|
48
|
-
# ----- Union type -----
|
|
49
|
-
|
|
50
|
-
matching_types(str | int)
|
|
51
|
-
# => (str, int)
|
|
52
|
-
|
|
53
|
-
# ----- Generic type alias -----
|
|
54
|
-
|
|
55
|
-
type StringOr[T] = str | T
|
|
56
|
-
|
|
57
|
-
config = MatchingTypesConfig(with_type_alias_value=True)
|
|
58
|
-
matching_types(StringOr[int], config)
|
|
59
|
-
# => (StringOr[int], str, int)
|
|
60
|
-
|
|
61
|
-
# ----- Generic classes -----
|
|
62
|
-
|
|
63
|
-
class A[T]:
|
|
64
|
-
...
|
|
65
|
-
|
|
66
|
-
class B[T]:
|
|
67
|
-
...
|
|
68
|
-
|
|
69
|
-
class C[T1, T2](A[T1], B[T2]):
|
|
70
|
-
...
|
|
71
|
-
|
|
72
|
-
config = MatchingTypesConfig(with_bases=True)
|
|
73
|
-
matching_types(C[str, int], config)
|
|
74
|
-
# => (C[str, int], A[str], B[int])
|
|
75
|
-
```
|
type_analyzer-0.1.2/README.md
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# type-analyzer
|
|
2
|
-
|
|
3
|
-
[](https://github.com/100nm/type-analyzer)
|
|
4
|
-
[](https://pypi.org/project/type-analyzer)
|
|
5
|
-
[](https://pypistats.org/packages/type-analyzer)
|
|
6
|
-
[](https://github.com/astral-sh/ruff)
|
|
7
|
-
|
|
8
|
-
## Installation
|
|
9
|
-
|
|
10
|
-
⚠️ _Requires Python 3.12 or higher_
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
pip install type-analyzer
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Quick start
|
|
17
|
-
|
|
18
|
-
### matching_types
|
|
19
|
-
|
|
20
|
-
```python
|
|
21
|
-
from type_analyzer import MatchingTypesConfig, matching_types
|
|
22
|
-
|
|
23
|
-
# ----- Union type -----
|
|
24
|
-
|
|
25
|
-
matching_types(str | int)
|
|
26
|
-
# => (str, int)
|
|
27
|
-
|
|
28
|
-
# ----- Generic type alias -----
|
|
29
|
-
|
|
30
|
-
type StringOr[T] = str | T
|
|
31
|
-
|
|
32
|
-
config = MatchingTypesConfig(with_type_alias_value=True)
|
|
33
|
-
matching_types(StringOr[int], config)
|
|
34
|
-
# => (StringOr[int], str, int)
|
|
35
|
-
|
|
36
|
-
# ----- Generic classes -----
|
|
37
|
-
|
|
38
|
-
class A[T]:
|
|
39
|
-
...
|
|
40
|
-
|
|
41
|
-
class B[T]:
|
|
42
|
-
...
|
|
43
|
-
|
|
44
|
-
class C[T1, T2](A[T1], B[T2]):
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
config = MatchingTypesConfig(with_bases=True)
|
|
48
|
-
matching_types(C[str, int], config)
|
|
49
|
-
# => (C[str, int], A[str], B[int])
|
|
50
|
-
```
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
from collections.abc import Iterator, Mapping, Sequence
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
|
-
from types import NoneType, UnionType, get_original_bases
|
|
4
|
-
from typing import (
|
|
5
|
-
Annotated,
|
|
6
|
-
Any,
|
|
7
|
-
Generic,
|
|
8
|
-
TypeAliasType,
|
|
9
|
-
TypeVar,
|
|
10
|
-
Union,
|
|
11
|
-
get_args,
|
|
12
|
-
get_origin,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@dataclass(repr=False, eq=False, frozen=True, kw_only=True, slots=True)
|
|
17
|
-
class MatchingTypesConfig:
|
|
18
|
-
ignore_none: bool = field(default=False)
|
|
19
|
-
with_bases: bool = field(default=False)
|
|
20
|
-
with_origin: bool = field(default=False)
|
|
21
|
-
with_type_alias_value: bool = field(default=False)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
_empty_config = MatchingTypesConfig()
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def iter_matching_types(
|
|
28
|
-
type_hint: Any,
|
|
29
|
-
/,
|
|
30
|
-
config: MatchingTypesConfig | None = None,
|
|
31
|
-
) -> Iterator[Any]:
|
|
32
|
-
config = config or _empty_config
|
|
33
|
-
type_hints = (type_hint,)
|
|
34
|
-
return _iter_matching_types(type_hints, config)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def matching_types(
|
|
38
|
-
type_hint: Any,
|
|
39
|
-
/,
|
|
40
|
-
config: MatchingTypesConfig | None = None,
|
|
41
|
-
) -> tuple[Any, ...]:
|
|
42
|
-
return tuple(dict.fromkeys(iter_matching_types(type_hint, config)))
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _get_bases(type_hint: Any, origin: Any) -> tuple[Any, ...]:
|
|
46
|
-
target = type_hint if origin is None else origin
|
|
47
|
-
|
|
48
|
-
try:
|
|
49
|
-
return get_original_bases(target)
|
|
50
|
-
except TypeError:
|
|
51
|
-
return ()
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _get_generics(type_hint: Any, origin: Any) -> Mapping[TypeVar, Any]:
|
|
55
|
-
args = get_args(type_hint)
|
|
56
|
-
parameters = getattr(origin, "__parameters__", ())
|
|
57
|
-
return dict(zip(parameters, args))
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _is_type_var(type_hint: Any) -> bool:
|
|
61
|
-
return isinstance(type_hint, TypeVar)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _is_none_type(type_hint: Any) -> bool:
|
|
65
|
-
return type_hint in (None, NoneType)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def _iter_matching_types(
|
|
69
|
-
type_hints: Sequence[Any],
|
|
70
|
-
/,
|
|
71
|
-
config: MatchingTypesConfig,
|
|
72
|
-
params: Mapping[TypeVar, Any] | None = None,
|
|
73
|
-
) -> Iterator[Any]:
|
|
74
|
-
if params is None:
|
|
75
|
-
params = {}
|
|
76
|
-
|
|
77
|
-
for type_hint in type_hints:
|
|
78
|
-
if config.ignore_none and _is_none_type(type_hint):
|
|
79
|
-
continue
|
|
80
|
-
|
|
81
|
-
if _is_type_var(type_hint):
|
|
82
|
-
yield params.get(type_hint, type_hint)
|
|
83
|
-
continue
|
|
84
|
-
|
|
85
|
-
origin = get_origin(type_hint)
|
|
86
|
-
|
|
87
|
-
if origin is Generic:
|
|
88
|
-
continue
|
|
89
|
-
|
|
90
|
-
if unpacked_types := _unpack_types(type_hint, origin):
|
|
91
|
-
yield from _iter_matching_types(unpacked_types, config, params)
|
|
92
|
-
continue
|
|
93
|
-
|
|
94
|
-
if origin is None:
|
|
95
|
-
yield type_hint
|
|
96
|
-
|
|
97
|
-
else:
|
|
98
|
-
args = tuple(
|
|
99
|
-
next(_iter_matching_types((arg,), _empty_config, params))
|
|
100
|
-
for arg in get_args(type_hint)
|
|
101
|
-
)
|
|
102
|
-
yield origin[*args]
|
|
103
|
-
|
|
104
|
-
if config.with_origin:
|
|
105
|
-
yield origin
|
|
106
|
-
|
|
107
|
-
if config.with_bases and (bases := _get_bases(type_hint, origin)):
|
|
108
|
-
generics = _get_generics(type_hint, origin)
|
|
109
|
-
yield from _iter_matching_types(bases, config, generics)
|
|
110
|
-
|
|
111
|
-
if config.with_type_alias_value:
|
|
112
|
-
if isinstance(origin, TypeAliasType):
|
|
113
|
-
values = (origin.__value__,)
|
|
114
|
-
generics = _get_generics(type_hint, origin)
|
|
115
|
-
yield from _iter_matching_types(values, config, generics)
|
|
116
|
-
|
|
117
|
-
elif isinstance(type_hint, TypeAliasType):
|
|
118
|
-
values = (type_hint.__value__,)
|
|
119
|
-
yield from _iter_matching_types(values, config)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _unpack_types(type_hint: Any, origin: Any) -> Sequence[Any]:
|
|
123
|
-
if origin is Union or isinstance(type_hint, UnionType):
|
|
124
|
-
return get_args(type_hint)
|
|
125
|
-
|
|
126
|
-
elif origin is Annotated:
|
|
127
|
-
return get_args(type_hint)[:1]
|
|
128
|
-
|
|
129
|
-
return ()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|