Skip to content

Commit d55bb0f

Browse files
feat: add python-typemap Claude Code skill (#63)
Add Claude Code skill for python-typemap - a PEP 827 type manipulation library for Python 3.14+. The skill includes: - SKILL.md: Main skill file with overview and quick usage - type-operators.md: All type operators (KeyOf, Partial, Pick, Omit, etc.) - runtime-evaluation.md: How eval_typing works internally - member.md: Member type descriptor explanation - patterns.md: Common usage patterns - examples.md: Practical real-world examples - internals.md: Architecture and internals documentation - errors.md: Error handling guide Co-authored-by: martyy-code <nesalia.inc@gmail.com>
1 parent 5d96bbb commit d55bb0f

8 files changed

Lines changed: 1865 additions & 0 deletions

File tree

skills/SKILL.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
name: python-typemap
3+
description: Python 3.14+ type manipulation library inspired by TypeScript. Use when working with type-level operations, evaluating type expressions at runtime, or when asked about PEP 827 type operators. Supports type introspection, transformation, and runtime type evaluation.
4+
disable-model-invocation: false
5+
allowed-tools: Read,Grep,Glob,Bash
6+
---
7+
8+
# Python Typemap Skill
9+
10+
A skill for working with python-typemap - a PEP 827 type manipulation library for Python 3.14+.
11+
12+
## Quick Usage
13+
14+
```python
15+
from typemap import eval_typing
16+
import typemap_extensions as tm
17+
18+
# Get type operators
19+
tm.KeyOf[T] # TypeScript's keyof
20+
tm.Partial[T] # Make fields optional
21+
tm.Pick[T, K] # Select fields
22+
tm.Omit[T, K] # Exclude fields
23+
tm.Iter[T] # Iterate over type
24+
tm.Attrs[T] # Get type attributes
25+
```
26+
27+
## What is python-typemap?
28+
29+
Python-typemap brings **TypeScript-inspired type operators** to Python. It provides:
30+
31+
- **Type Operators**: `Member`, `KeyOf`, `Partial`, `Pick`, `Omit`, `Iter`, `Attrs`, etc.
32+
- **Runtime Evaluation**: `eval_typing()` evaluates type expressions at runtime
33+
- **Type Introspection**: Inspect and transform types programmatically
34+
35+
## Core Concepts
36+
37+
| Concept | Description |
38+
|---------|-------------|
39+
| **Type Operators** | Classes that manipulate types (like TypeScript's utility types) |
40+
| **Member** | Access type members with name, type, and qualifiers |
41+
| **EvalContext** | Context for tracking resolved types during evaluation |
42+
| **Singledispatch** | Pattern for handling different type implementations |
43+
44+
## Key Type Operators
45+
46+
| Operator | Purpose | Example |
47+
|----------|---------|---------|
48+
| `KeyOf[T]` | Get all keys of a type | `KeyOf[User]``tuple[Literal['name'], Literal['age']]` |
49+
| `Partial[T]` | Make all fields optional | `Partial[User]` → all fields `\| None` |
50+
| `Pick[T, K]` | Select specific fields | `Pick[User, 'name']` → only `name` field |
51+
| `Omit[T, K]` | Remove specific fields | `Omit[User, 'password']` → without password |
52+
| `Iter[T]` | Iterate over type elements | `Iter[list[int]]``int` |
53+
| `Attrs[T]` | Get type attributes | `Attrs[User]` → tuple of Member descriptors |
54+
| `Param[T, N]` | Get Nth parameter | `Param[func, 0]` → first param type |
55+
| `Return[T]` | Get return type | `Return[func]` → function return type |
56+
| `Required[T]` | Make all fields required | `Required[Partial[User]]` → revert Partial |
57+
| `Readonly[T]` | Make fields immutable | `Readonly[User]` → immutable fields |
58+
59+
## Runtime Evaluation
60+
61+
The `eval_typing()` function evaluates type expressions at runtime:
62+
63+
```python
64+
from typemap import eval_typing
65+
import typemap_extensions as tm
66+
67+
class User:
68+
name: str
69+
age: int
70+
71+
# Get keys of User
72+
keys = eval_typing(tm.KeyOf[User])
73+
# Result: tuple[Literal['name'], Literal['age']]
74+
75+
# Make User partial
76+
PartialUser = eval_typing(tm.Partial[User])
77+
# Result: User with all fields optional
78+
```
79+
80+
## Additional Resources
81+
82+
For detailed information on each feature:
83+
84+
- [type-operators.md](python-typemap/type-operators.md) - All type operators explained
85+
- [runtime-evaluation.md](python-typemap/runtime-evaluation.md) - How eval_typing works
86+
- [member.md](python-typemap/member.md) - Member type descriptor
87+
- [patterns.md](python-typemap/patterns.md) - Common usage patterns
88+
- [examples.md](python-typemap/examples.md) - Practical examples
89+
- [internals.md](python-typemap/internals.md) - Architecture and internals
90+
- [errors.md](python-typemap/errors.md) - Error handling

skills/python-typemap/errors.md

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# Error Handling
2+
3+
Understanding and handling errors in python-typemap.
4+
5+
## Exception Types
6+
7+
### TypeMapError
8+
9+
Base exception for all typemap errors.
10+
11+
```python
12+
from typemap import TypeMapError
13+
14+
try:
15+
result = eval_typing(expr)
16+
except TypeMapError as e:
17+
print(f"Type evaluation failed: {e}")
18+
```
19+
20+
### StuckException
21+
22+
Raised when type evaluation cannot proceed because a type variable hasn't been resolved.
23+
24+
```python
25+
from typemap.type_eval import StuckException
26+
27+
try:
28+
# T is still a TypeVar, can't evaluate
29+
result = eval_typing(KeyOf[T])
30+
except StuckException:
31+
# T needs to be bound to a concrete type
32+
result = eval_typing(KeyOf[ConcreteType])
33+
```
34+
35+
**Common causes:**
36+
- Passing a TypeVar directly to an operator
37+
- Using unevaluated generics
38+
- Circular type references
39+
40+
### RecursionError
41+
42+
Python's built-in recursion limit exceeded during type evaluation.
43+
44+
```python
45+
# Recursive type that doesn't terminate
46+
type Infinite = list[Infinite]
47+
48+
try:
49+
eval_typing(Partial[Infinite])
50+
except RecursionError:
51+
print("Type is infinitely recursive")
52+
```
53+
54+
## Common Errors
55+
56+
### 1. Type Variable Not Bound
57+
58+
```python
59+
from typing import TypeVar
60+
61+
T = TypeVar('T')
62+
63+
# Wrong - T is not bound
64+
KeyOf[T] # StuckException
65+
66+
# Correct - T is bound to a concrete type
67+
KeyOf[User]
68+
```
69+
70+
### 2. Missing Type Annotation
71+
72+
```python
73+
class MissingAnnotation:
74+
name: str
75+
data # No annotation!
76+
77+
# May cause evaluation issues
78+
eval_typing(Attrs[MissingAnnotation])
79+
# May raise: AttributeError or StuckException
80+
```
81+
82+
### 3. Invalid Type Expression
83+
84+
```python
85+
# Wrong - this is not a valid type expression
86+
eval_typing("User") # String, not a type
87+
88+
# Correct
89+
eval_typing(User)
90+
eval_typing(KeyOf[User])
91+
```
92+
93+
### 4. Union with Non-Types
94+
95+
```python
96+
# Wrong
97+
Union[str, 123] # 123 is not a type
98+
99+
# Correct
100+
Union[str, int]
101+
```
102+
103+
## Debugging Tips
104+
105+
### Enable Context Manager
106+
107+
```python
108+
from typemap.type_eval import _ensure_context
109+
110+
with _ensure_context() as ctx:
111+
result = _eval_types_impl(expr, ctx)
112+
print(f"Resolved: {ctx.resolved}")
113+
print(f"Seen: {ctx.seen}")
114+
```
115+
116+
### Check What Was Resolved
117+
118+
```python
119+
ctx = EvalContext()
120+
result = eval_typing(expr, ensure_context=False)
121+
122+
# Inspect cache
123+
for type_obj, resolved in ctx.resolved.items():
124+
print(f"{type_obj} -> {resolved}")
125+
```
126+
127+
### Verify Type Structure
128+
129+
```python
130+
import typing
131+
132+
def inspect_type(t: type) -> dict:
133+
"""Debug type structure."""
134+
info = {
135+
'is_generic': isinstance(t, typing._GenericAlias),
136+
'is_union': hasattr(t, '__origin__') and t.__origin__ is Union,
137+
'args': getattr(t, '__args__', ()),
138+
'origin': getattr(t, '__origin__', None),
139+
}
140+
return info
141+
```
142+
143+
## Error Recovery
144+
145+
### Fallback to Default
146+
147+
```python
148+
def safe_eval(expr, default=None):
149+
"""Evaluate with fallback on error."""
150+
try:
151+
return eval_typing(expr)
152+
except (StuckException, TypeMapError) as e:
153+
warnings.warn(f"Evaluation failed: {e}")
154+
return default
155+
```
156+
157+
### Partial Evaluation
158+
159+
```python
160+
def try_partial_eval(cls: type, operators: list):
161+
"""Try applying operators, collecting failures."""
162+
results = {}
163+
errors = []
164+
165+
for op in operators:
166+
try:
167+
results[op] = eval_typing(op[cls])
168+
except Exception as e:
169+
errors.append((op, str(e)))
170+
171+
return results, errors
172+
```
173+
174+
## Validation Before Evaluation
175+
176+
### Check for TypeVars
177+
178+
```python
179+
from typing import TypeVar, get_args, get_origin
180+
181+
def has_unbound_typevars(t: type) -> bool:
182+
"""Check if type has unresolved TypeVars."""
183+
if isinstance(t, TypeVar):
184+
return True
185+
if hasattr(t, '__args__'):
186+
return any(has_unbound_typevars(arg) for arg in t.__args__)
187+
return False
188+
189+
# Before evaluation
190+
if has_unbound_typevars(MyType):
191+
raise ValueError("Type has unbound TypeVars")
192+
```
193+
194+
### Validate Type Structure
195+
196+
```python
197+
def validate_for_eval(t: type) -> tuple[bool, str]:
198+
"""Validate type can be evaluated."""
199+
if t is None:
200+
return False, "Type is None"
201+
if isinstance(t, str):
202+
return False, "Type is a string, not a type"
203+
if isinstance(t, TypeVar):
204+
return False, f"TypeVar {t} is unbound"
205+
if not callable(t):
206+
return False, f"{t} is not callable"
207+
return True, "OK"
208+
```
209+
210+
## Logging
211+
212+
```python
213+
import logging
214+
215+
logging.basicConfig(level=logging.DEBUG)
216+
logger = logging.getLogger('typemap')
217+
218+
# Enable debug logging
219+
logger.setLevel(logging.DEBUG)
220+
```
221+
222+
## Common Error Messages
223+
224+
| Error | Cause | Solution |
225+
|-------|-------|----------|
226+
| `StuckException: cannot evaluate KeyOf[T]` | TypeVar not bound | Provide concrete type |
227+
| `RecursionError` | Infinite type recursion | Break recursive cycle |
228+
| `TypeError: not a type` | Invalid type passed | Check type annotation |
229+
| `AttributeError: no __args__` | Non-generic used as generic | Use correct type |
230+
| `KeyError` | Type not in namespace | Provide namespace context |
231+
232+
## Best Practices
233+
234+
1. **Always bind TypeVars** before passing to operators
235+
2. **Use concrete types** not type aliases when possible
236+
3. **Catch specific exceptions** not just `Exception`
237+
4. **Validate inputs** before expensive evaluations
238+
5. **Cache results** to avoid repeated evaluations
239+
6. **Provide namespace** for custom type resolution

0 commit comments

Comments
 (0)