arcane-typing 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.
@@ -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.
@@ -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,40 @@
1
+ # `arcane-typing`
2
+
3
+ This package provides advanced typing patterns for Python projects that use [ty](https://docs.astral.sh/ty/).
4
+
5
+ > [!WARNING]
6
+ > This project is highly experimental and currently makes no promises regarding backward compatibility.
7
+
8
+ ## Features
9
+
10
+ ### Core utilities
11
+
12
+ - `TypeOf[expr]` can be used to obtain ty's inferred type for an expression in a type annotation or type alias.
13
+ - `Top[T]` can be used to refer to the top-materialization of `T`.
14
+ - `Bottom[T]` can be used to refer to the bottom-materialization of `T`.
15
+
16
+ ### TypedDict features
17
+
18
+ - `TypeOf[Keys[T]]` gives a union of string literals for the keys that can appear in a `TypedDict` `T`.
19
+
20
+ ### Type Narrowing
21
+
22
+ - `hasattr_of_type(obj, "name", T)` narrows `obj.name` to type `T` if the attribute exists and if
23
+ it is an instance of `T` (which can be a class or a union of classes)
24
+ - `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.
25
+
26
+ ## Development
27
+
28
+ Use [uv](https://docs.astral.sh/uv/) to run the following development tasks:
29
+
30
+ ```sh
31
+ # Linting and formatting
32
+ uv run ruff check
33
+ uv run ruff format --check
34
+
35
+ # Type checking
36
+ TY_UV=scripts uv run ty check
37
+
38
+ # Markdown tests
39
+ uv run mdtest.py
40
+ ```
@@ -0,0 +1,36 @@
1
+ [project]
2
+ name = "arcane-typing"
3
+ version = "0.1.0"
4
+ description = "Experimental and advanced Python typing patterns for ty users."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.14"
9
+ dependencies = []
10
+
11
+ [[project.authors]]
12
+ name = "Astral Software Inc."
13
+ email = "hey@astral.sh"
14
+
15
+ [build-system]
16
+ requires = ["uv_build>=0.13.5,<0.14.0"]
17
+ build-backend = "uv_build"
18
+
19
+ [dependency-groups]
20
+ dev = [
21
+ "ruff>=0.17.0",
22
+ "ty>=0.0.101",
23
+ "typing-extensions>=4.13",
24
+ ]
25
+
26
+ [tool.uv]
27
+ preview-features = ["check-command"]
28
+
29
+ [tool.ruff]
30
+ preview = true
31
+
32
+ [tool.ruff.lint]
33
+ ignore = ["deprecated-import"]
34
+
35
+ [tool.ty.rules]
36
+ experimental-syntax = "ignore"
@@ -0,0 +1,34 @@
1
+ [project]
2
+ name = "arcane-typing"
3
+ version = "0.1.0"
4
+ description = "Experimental and advanced Python typing patterns for ty users."
5
+ authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ requires-python = ">=3.14"
10
+ dependencies = []
11
+
12
+ [build-system]
13
+ requires = ["uv_build>=0.13.5,<0.14.0"]
14
+ build-backend = "uv_build"
15
+
16
+ [dependency-groups]
17
+ dev = [
18
+ "ruff>=0.17.0",
19
+ "ty>=0.0.101",
20
+ "typing-extensions>=4.13",
21
+ ]
22
+
23
+ [tool.uv]
24
+ preview-features = ["check-command"]
25
+
26
+ [tool.ruff]
27
+ preview = true
28
+
29
+ [tool.ruff.lint]
30
+ # Prefer the typing aliases for collection annotations.
31
+ ignore = ["deprecated-import"]
32
+
33
+ [tool.ty.rules]
34
+ experimental-syntax = "ignore"
@@ -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
@@ -0,0 +1,6 @@
1
+ from .core import Bottom as Bottom
2
+ from .core import Top as Top
3
+ from .core import TypeOf as TypeOf
4
+ from .narrowing import hasattr_of_type as hasattr_of_type
5
+ from .narrowing import is_sequence_of as is_sequence_of
6
+ from .typed_dict import Keys as Keys
@@ -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): ...
@@ -0,0 +1,3 @@
1
+ from ty_extensions import Bottom as Bottom
2
+ from ty_extensions import Top as Top
3
+ from ty_extensions._internal import TypeOf as TypeOf
@@ -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."""
File without changes
@@ -0,0 +1,3 @@
1
+ class Keys:
2
+ def __class_getitem__(cls, _schema: object, /) -> None:
3
+ return None
@@ -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: ...