arcane-typing 0.1.0__py3-none-any.whl
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.
- arcane_typing/__init__.py +8 -0
- arcane_typing/__init__.pyi +6 -0
- arcane_typing/core.py +15 -0
- arcane_typing/core.pyi +3 -0
- arcane_typing/narrowing.py +26 -0
- arcane_typing/narrowing.pyi +17 -0
- arcane_typing/py.typed +0 -0
- arcane_typing/typed_dict.py +3 -0
- arcane_typing/typed_dict.pyi +18 -0
- arcane_typing-0.1.0.dist-info/METADATA +51 -0
- arcane_typing-0.1.0.dist-info/RECORD +13 -0
- arcane_typing-0.1.0.dist-info/WHEEL +4 -0
- arcane_typing-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Experimental and advanced Python typing patterns for ty users."""
|
|
2
|
+
|
|
3
|
+
from .core import Bottom as Bottom
|
|
4
|
+
from .core import Top as Top
|
|
5
|
+
from .core import TypeOf as TypeOf
|
|
6
|
+
from .narrowing import hasattr_of_type as hasattr_of_type
|
|
7
|
+
from .narrowing import is_sequence_of as is_sequence_of
|
|
8
|
+
from .typed_dict import Keys as Keys
|
arcane_typing/core.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _RuntimeSpecialForm:
|
|
5
|
+
def __class_getitem__(cls, _argument: object, /) -> Any:
|
|
6
|
+
return Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TypeOf(_RuntimeSpecialForm): ...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Top(_RuntimeSpecialForm): ...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Bottom(_RuntimeSpecialForm): ...
|
arcane_typing/core.pyi
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from types import UnionType
|
|
2
|
+
from typing import Sequence, get_args
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def hasattr_of_type(obj: object, key: str, clsinfo: object, /) -> bool:
|
|
6
|
+
try:
|
|
7
|
+
value = getattr(obj, key)
|
|
8
|
+
except AttributeError:
|
|
9
|
+
return False
|
|
10
|
+
|
|
11
|
+
return isinstance(value, _validate_clsinfo(clsinfo))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_sequence_of(obj: object, clsinfo: object, /) -> bool:
|
|
15
|
+
clsinfo = _validate_clsinfo(clsinfo)
|
|
16
|
+
return isinstance(obj, Sequence) and all(isinstance(item, clsinfo) for item in obj)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _validate_clsinfo(clsinfo: object) -> type | UnionType:
|
|
20
|
+
if not isinstance(clsinfo, (type, UnionType)) or (
|
|
21
|
+
isinstance(clsinfo, UnionType)
|
|
22
|
+
and any(not isinstance(member, type) for member in get_args(clsinfo))
|
|
23
|
+
):
|
|
24
|
+
raise TypeError("Expected a class or a union of classes")
|
|
25
|
+
|
|
26
|
+
return clsinfo
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import LiteralString, Protocol, Sequence, TypeIs, overload
|
|
2
|
+
|
|
3
|
+
from typing_extensions import TypeForm
|
|
4
|
+
|
|
5
|
+
class _HasAttr[K: LiteralString, V](Protocol):
|
|
6
|
+
@overload
|
|
7
|
+
def __getattr__(self, name: K, /) -> V: ...
|
|
8
|
+
@overload
|
|
9
|
+
def __getattr__(self, name: str, /) -> object: ...
|
|
10
|
+
|
|
11
|
+
def hasattr_of_type[K: LiteralString, V](
|
|
12
|
+
obj: object, key: K, clsinfo: TypeForm[V], /
|
|
13
|
+
) -> TypeIs[_HasAttr[K, V]]:
|
|
14
|
+
"""Check whether `obj` has an attribute `key` that matches a class or union of classes."""
|
|
15
|
+
|
|
16
|
+
def is_sequence_of[T](obj: object, clsinfo: TypeForm[T], /) -> TypeIs[Sequence[T]]:
|
|
17
|
+
"""Check whether `obj` is a sequence whose elements match a class or union of classes."""
|
arcane_typing/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Iterable, LiteralString, Mapping
|
|
2
|
+
|
|
3
|
+
from typing_extensions import TypeForm
|
|
4
|
+
|
|
5
|
+
class Keys:
|
|
6
|
+
"""`TypeOf[Keys[T]]` is a union of string literals representing the keys of the TypedDict `T`.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
class Movie(TypedDict, closed=True):
|
|
10
|
+
title: str
|
|
11
|
+
year: int
|
|
12
|
+
|
|
13
|
+
type MovieKey = TypeOf[Keys[Movie]] # Literal["title", "year"]
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __class_getitem__[K: LiteralString](
|
|
17
|
+
cls, schema: TypeForm[Mapping[str, object] & Iterable[K]], /
|
|
18
|
+
) -> K: ...
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arcane-typing
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Experimental and advanced Python typing patterns for ty users.
|
|
5
|
+
Author: Astral Software Inc.
|
|
6
|
+
Author-email: Astral Software Inc. <hey@astral.sh>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.14
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# `arcane-typing`
|
|
13
|
+
|
|
14
|
+
This package provides advanced typing patterns for Python projects that use [ty](https://docs.astral.sh/ty/).
|
|
15
|
+
|
|
16
|
+
> [!WARNING]
|
|
17
|
+
> This project is highly experimental and currently makes no promises regarding backward compatibility.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
### Core utilities
|
|
22
|
+
|
|
23
|
+
- `TypeOf[expr]` can be used to obtain ty's inferred type for an expression in a type annotation or type alias.
|
|
24
|
+
- `Top[T]` can be used to refer to the top-materialization of `T`.
|
|
25
|
+
- `Bottom[T]` can be used to refer to the bottom-materialization of `T`.
|
|
26
|
+
|
|
27
|
+
### TypedDict features
|
|
28
|
+
|
|
29
|
+
- `TypeOf[Keys[T]]` gives a union of string literals for the keys that can appear in a `TypedDict` `T`.
|
|
30
|
+
|
|
31
|
+
### Type Narrowing
|
|
32
|
+
|
|
33
|
+
- `hasattr_of_type(obj, "name", T)` narrows `obj.name` to type `T` if the attribute exists and if
|
|
34
|
+
it is an instance of `T` (which can be a class or a union of classes)
|
|
35
|
+
- `is_sequence_of(obj, T)` checks that `obj` is a sequence and that every element is an instance of `T` (which can be a class or a union of classes). It narrows `obj` to `Sequence[T]` on success.
|
|
36
|
+
|
|
37
|
+
## Development
|
|
38
|
+
|
|
39
|
+
Use [uv](https://docs.astral.sh/uv/) to run the following development tasks:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
# Linting and formatting
|
|
43
|
+
uv run ruff check
|
|
44
|
+
uv run ruff format --check
|
|
45
|
+
|
|
46
|
+
# Type checking
|
|
47
|
+
TY_UV=scripts uv run ty check
|
|
48
|
+
|
|
49
|
+
# Markdown tests
|
|
50
|
+
uv run mdtest.py
|
|
51
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
arcane_typing/__init__.py,sha256=7V1z2t20E8uLzawsd2mQ-e55xxirVFmqzDrjIm-t-q4,320
|
|
2
|
+
arcane_typing/__init__.pyi,sha256=ETknq9-z7ZbvF0WeuxMEwXe6geiGS3rZwjKGjHKvsfU,250
|
|
3
|
+
arcane_typing/core.py,sha256=EOF3ESeyAtx9E2n2ekVUFjEaysyh5LS_fARwibZ7QfU,252
|
|
4
|
+
arcane_typing/core.pyi,sha256=RDgqCIcje56quIsgmXTzPSQAWQ30Nu994ISk64l4bts,133
|
|
5
|
+
arcane_typing/narrowing.py,sha256=Jq_7CbT3QsBWTbTBwcAz0ewDCZlZYeob_epCyF-4MTE,808
|
|
6
|
+
arcane_typing/narrowing.pyi,sha256=l87M6V93BfeQNBM8M6exTElBpZdmLt2liFTC8j5HZog,685
|
|
7
|
+
arcane_typing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
arcane_typing/typed_dict.py,sha256=U5iBG3DKqKgrwdwUeKSfqwNYZnx7t4p0A5e6lyl-G-4,92
|
|
9
|
+
arcane_typing/typed_dict.pyi,sha256=sdZG45yvsZjMdjUkumji_V_4ZWZ0h4Yw-bLp-cjyjKc,520
|
|
10
|
+
arcane_typing-0.1.0.dist-info/licenses/LICENSE,sha256=fu2Ui0u0Eitn8XQofwtsZ1kW1MvfVNqJMMLfb5I6fuw,1077
|
|
11
|
+
arcane_typing-0.1.0.dist-info/WHEEL,sha256=si2p8vW03RnhyBnhWl4tj-ObJa78SpE-c9HsEtblGR0,80
|
|
12
|
+
arcane_typing-0.1.0.dist-info/METADATA,sha256=AlxCM2tBYcr9dbCxCT7eg3yfZEBXa-nE22-ty85o4cs,1626
|
|
13
|
+
arcane_typing-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Astral Software Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|