subtype 0.1.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.
- subtype-0.1.0/.gitattributes +8 -0
- subtype-0.1.0/.github/workflows/release.yml +18 -0
- subtype-0.1.0/.gitignore +13 -0
- subtype-0.1.0/.gitmodules +5 -0
- subtype-0.1.0/MANIFEST.in +1 -0
- subtype-0.1.0/PKG-INFO +96 -0
- subtype-0.1.0/README.md +88 -0
- subtype-0.1.0/pyproject.toml +33 -0
- subtype-0.1.0/setup.cfg +4 -0
- subtype-0.1.0/subtype/__init__.py +1797 -0
- subtype-0.1.0/subtype/__init__.pyi +18 -0
- subtype-0.1.0/subtype.egg-info/PKG-INFO +96 -0
- subtype-0.1.0/subtype.egg-info/SOURCES.txt +23 -0
- subtype-0.1.0/subtype.egg-info/dependency_links.txt +1 -0
- subtype-0.1.0/subtype.egg-info/requires.txt +1 -0
- subtype-0.1.0/subtype.egg-info/scm_file_list.json +20 -0
- subtype-0.1.0/subtype.egg-info/scm_version.json +8 -0
- subtype-0.1.0/subtype.egg-info/top_level.txt +1 -0
- subtype-0.1.0/tests/test_callable.py +355 -0
- subtype-0.1.0/tests/test_generic.py +401 -0
- subtype-0.1.0/tests/test_protocol.py +306 -0
- subtype-0.1.0/tests/test_subtype.py +272 -0
- subtype-0.1.0/tests/test_tuple.py +228 -0
- subtype-0.1.0/tests/test_type.py +125 -0
- subtype-0.1.0/tests/test_variance.py +249 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [released]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- uses: astral-sh/setup-uv@v7
|
|
17
|
+
- run: uv build
|
|
18
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
subtype-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
prune extern/typeshed
|
subtype-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: subtype
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Author: crypt0lith
|
|
5
|
+
Requires-Python: >=3.14
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: typing-extensions>=4.15
|
|
8
|
+
|
|
9
|
+
Provides a metaclass that extends runtime type checking to the full Python typing spec.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### `isinstance`
|
|
14
|
+
|
|
15
|
+
`subtype.isinstance(obj, T)` answers whether `obj` is assignable to the type expression `T` under the typing spec, and narrows `obj` to `T` for a static checker when it is. Code can then enforce its own annotations on values that only exist at runtime, where a static checker can't see them.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import subtype
|
|
19
|
+
|
|
20
|
+
assert subtype.isinstance([[1, 2], [3]], list[list[int]])
|
|
21
|
+
assert not subtype.isinstance([[1, 2], ["3"]], list[list[int]])
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Where the builtin `isinstance` raises `TypeError` for a parameterized generic, `subtype.isinstance` checks the items of a collection against its type arguments, including nested collections.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from collections.abc import Iterator
|
|
28
|
+
|
|
29
|
+
assert subtype.isinstance(zip(range(3), "abc"), Iterator[tuple[int, str]])
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`subtype.isinstance` infers an iterator's item type from the objects the iterator was built from, without advancing it.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from collections.abc import Callable
|
|
36
|
+
from typing import Protocol
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class StrCall(Protocol):
|
|
40
|
+
def __call__(self, o: object, /) -> str:
|
|
41
|
+
return str(o)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
assert subtype.isinstance(str, StrCall)
|
|
45
|
+
assert subtype.isinstance(str, Callable[[object], str])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A class matches a `__call__` protocol by its constructor, the same way it matches `Callable[...]`.
|
|
49
|
+
|
|
50
|
+
### `subtype`
|
|
51
|
+
|
|
52
|
+
`subtype(T)` turns the type expression `T` into a class that `issubclass` accepts, and answers whether a type is assignable to `T` under the typing spec.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from collections.abc import Sequence
|
|
56
|
+
|
|
57
|
+
from subtype import subtype
|
|
58
|
+
|
|
59
|
+
assert issubclass(list[bool], subtype(Sequence[int]))
|
|
60
|
+
assert not issubclass(list[bool], subtype(list[int]))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`issubclass` takes a type expression as its first argument, and compares type arguments by the variance of their type parameters.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from typing import Self
|
|
67
|
+
|
|
68
|
+
assert issubclass(str, subtype(Sequence[Self]))
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`subtype` binds `Self` to the class being checked, so `str` is checked against `Sequence[str]`.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from collections.abc import Callable
|
|
75
|
+
from typing import Protocol, overload
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Decode(Protocol):
|
|
79
|
+
@overload
|
|
80
|
+
def __call__(self, data: bytes) -> str: ...
|
|
81
|
+
@overload
|
|
82
|
+
def __call__(self, data: None) -> None: ...
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
assert issubclass(Decode, subtype(Callable[[bytes], str]))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Protocols are matched by the signatures of their members, not only by member names as with `runtime_checkable`. A protocol's `__call__` is checked against `Callable[...]` like any other callable, overloads included.
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
Install the package via `pip`:
|
|
93
|
+
|
|
94
|
+
```shell
|
|
95
|
+
pip install subtype
|
|
96
|
+
```
|
subtype-0.1.0/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Provides a metaclass that extends runtime type checking to the full Python typing spec.
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
### `isinstance`
|
|
6
|
+
|
|
7
|
+
`subtype.isinstance(obj, T)` answers whether `obj` is assignable to the type expression `T` under the typing spec, and narrows `obj` to `T` for a static checker when it is. Code can then enforce its own annotations on values that only exist at runtime, where a static checker can't see them.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import subtype
|
|
11
|
+
|
|
12
|
+
assert subtype.isinstance([[1, 2], [3]], list[list[int]])
|
|
13
|
+
assert not subtype.isinstance([[1, 2], ["3"]], list[list[int]])
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Where the builtin `isinstance` raises `TypeError` for a parameterized generic, `subtype.isinstance` checks the items of a collection against its type arguments, including nested collections.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from collections.abc import Iterator
|
|
20
|
+
|
|
21
|
+
assert subtype.isinstance(zip(range(3), "abc"), Iterator[tuple[int, str]])
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`subtype.isinstance` infers an iterator's item type from the objects the iterator was built from, without advancing it.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from collections.abc import Callable
|
|
28
|
+
from typing import Protocol
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class StrCall(Protocol):
|
|
32
|
+
def __call__(self, o: object, /) -> str:
|
|
33
|
+
return str(o)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
assert subtype.isinstance(str, StrCall)
|
|
37
|
+
assert subtype.isinstance(str, Callable[[object], str])
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A class matches a `__call__` protocol by its constructor, the same way it matches `Callable[...]`.
|
|
41
|
+
|
|
42
|
+
### `subtype`
|
|
43
|
+
|
|
44
|
+
`subtype(T)` turns the type expression `T` into a class that `issubclass` accepts, and answers whether a type is assignable to `T` under the typing spec.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from collections.abc import Sequence
|
|
48
|
+
|
|
49
|
+
from subtype import subtype
|
|
50
|
+
|
|
51
|
+
assert issubclass(list[bool], subtype(Sequence[int]))
|
|
52
|
+
assert not issubclass(list[bool], subtype(list[int]))
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`issubclass` takes a type expression as its first argument, and compares type arguments by the variance of their type parameters.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from typing import Self
|
|
59
|
+
|
|
60
|
+
assert issubclass(str, subtype(Sequence[Self]))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`subtype` binds `Self` to the class being checked, so `str` is checked against `Sequence[str]`.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from collections.abc import Callable
|
|
67
|
+
from typing import Protocol, overload
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Decode(Protocol):
|
|
71
|
+
@overload
|
|
72
|
+
def __call__(self, data: bytes) -> str: ...
|
|
73
|
+
@overload
|
|
74
|
+
def __call__(self, data: None) -> None: ...
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
assert issubclass(Decode, subtype(Callable[[bytes], str]))
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Protocols are matched by the signatures of their members, not only by member names as with `runtime_checkable`. A protocol's `__call__` is checked against `Callable[...]` like any other callable, overloads included.
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
Install the package via `pip`:
|
|
85
|
+
|
|
86
|
+
```shell
|
|
87
|
+
pip install subtype
|
|
88
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=80", "setuptools-scm[simple]>=9.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "subtype"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
authors = [{ name = "crypt0lith" }]
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"typing-extensions>=4.15",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
tests = [
|
|
17
|
+
"pytest>=9.1.1",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
include = ["subtype*"]
|
|
22
|
+
|
|
23
|
+
[tool.black]
|
|
24
|
+
preview = true
|
|
25
|
+
skip-magic-trailing-comma = true
|
|
26
|
+
target-version = ["py314"]
|
|
27
|
+
extend-exclude = "^/subtype/typeshed/"
|
|
28
|
+
|
|
29
|
+
[tool.isort]
|
|
30
|
+
profile = "black"
|
|
31
|
+
split_on_trailing_comma = false
|
|
32
|
+
combine_as_imports = true
|
|
33
|
+
skip_gitignore = true
|
subtype-0.1.0/setup.cfg
ADDED