Skip to content

Commit bb0e011

Browse files
authored
Improve the state of Python type hints in basilisp.lang.compiler.* (#784)
Add `typing_extensions` to take advantage of `Concatenate` and `ParamSpec` across all supported versions of Python, allowing more correct type hints primarily targeting the compiler.
1 parent 1bb2e8f commit bb0e011

File tree

8 files changed

+107
-73
lines changed

8 files changed

+107
-73
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3333
* Removed support for PyPy 3.8 (#785)
3434

3535
### Other
36-
* Improve the state of the Python type hints in `basilisp.lang.*` (#797)
36+
* Improve the state of the Python type hints in `basilisp.lang.*` (#797, #784)
3737

3838

3939
## [v0.1.0b0]

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ prompt-toolkit = "^3.0.0"
3737
pyrsistent = "^0.18.0"
3838
python-dateutil = "^2.8.1"
3939
readerwriterlock = "^1.0.8"
40+
typing_extensions = "^4.9.0"
4041

4142
astor = { version = "^0.8.1", python = "<3.9", optional = true }
4243
pytest = { version = "^7.0.0", optional = true }
@@ -217,6 +218,7 @@ disable = [
217218

218219
[tool.mypy]
219220
check_untyped_defs = true
221+
disallow_untyped_decorators = true
220222
mypy_path = "src/"
221223
show_error_codes = true
222224
warn_redundant_casts = true

src/basilisp/lang/atom.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from typing import Callable, Generic, Optional, TypeVar
22

33
from readerwriterlock.rwlock import RWLockFair
4+
from typing_extensions import Concatenate, ParamSpec
45

56
from basilisp.lang.interfaces import IPersistentMap, RefValidator
67
from basilisp.lang.map import PersistentMap
78
from basilisp.lang.reference import RefBase
89

910
T = TypeVar("T")
11+
P = ParamSpec("P")
1012

1113

1214
class Atom(RefBase[T], Generic[T]):
@@ -58,7 +60,9 @@ def reset(self, v: T) -> T:
5860
self._notify_watches(oldval, v)
5961
return v
6062

61-
def swap(self, f: Callable[..., T], *args, **kwargs) -> T:
63+
def swap(
64+
self, f: Callable[Concatenate[T, P], T], *args: P.args, **kwargs: P.kwargs
65+
) -> T:
6266
"""Atomically swap the state of the Atom to the return value of
6367
`f(old, *args, **kwargs)`, returning the new value."""
6468
while True:

src/basilisp/lang/compiler/analyzer.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
Pattern,
2929
Set,
3030
Tuple,
31+
TypeVar,
3132
Union,
3233
cast,
3334
)
@@ -647,7 +648,12 @@ def get_meta_prop(o: Union[IMeta, Var]) -> Any:
647648
_tag_meta = _meta_getter(SYM_TAG_META_KEY)
648649

649650

650-
def _loc(form: Union[LispForm, ISeq]) -> Optional[Tuple[int, int, int, int]]:
651+
T_form = TypeVar("T_form", bound=ReaderForm)
652+
T_node = TypeVar("T_node", bound=Node)
653+
LispAnalyzer = Callable[[T_form, AnalyzerContext], T_node]
654+
655+
656+
def _loc(form: T_form) -> Optional[Tuple[int, int, int, int]]:
651657
"""Fetch the location of the form in the original filename from the
652658
input form, if it has metadata."""
653659
# Technically, IMeta is sufficient for fetching `form.meta` but the
@@ -669,17 +675,17 @@ def _loc(form: Union[LispForm, ISeq]) -> Optional[Tuple[int, int, int, int]]:
669675
return None
670676

671677

672-
def _with_loc(f):
678+
def _with_loc(f: LispAnalyzer[T_form, T_node]) -> LispAnalyzer[T_form, T_node]:
673679
"""Attach any available location information from the input form to
674680
the node environment returned from the parsing function."""
675681

676682
@wraps(f)
677-
def _analyze_form(form: Union[LispForm, ISeq], ctx: AnalyzerContext) -> Node:
683+
def _analyze_form(form: T_form, ctx: AnalyzerContext) -> T_node:
678684
form_loc = _loc(form)
679685
if form_loc is None:
680686
return f(form, ctx)
681687
else:
682-
return f(form, ctx).fix_missing_locations(form_loc)
688+
return cast(T_node, f(form, ctx).fix_missing_locations(form_loc))
683689

684690
return _analyze_form
685691

@@ -795,24 +801,15 @@ def _tag_ast(form: Optional[LispForm], ctx: AnalyzerContext) -> Optional[Node]:
795801
return _analyze_form(form, ctx)
796802

797803

798-
def _with_meta(gen_node):
804+
def _with_meta(gen_node: LispAnalyzer[T_form, T_node]) -> LispAnalyzer[T_form, T_node]:
799805
"""Wraps the node generated by gen_node in a :with-meta AST node if the
800806
original form has meta.
801807
802808
:with-meta AST nodes are used for non-quoted collection literals and for
803809
function expressions."""
804810

805811
@wraps(gen_node)
806-
def with_meta(
807-
form: Union[
808-
llist.PersistentList,
809-
lmap.PersistentMap,
810-
ISeq,
811-
lset.PersistentSet,
812-
vec.PersistentVector,
813-
],
814-
ctx: AnalyzerContext,
815-
) -> Node:
812+
def with_meta(form: T_form, ctx: AnalyzerContext) -> T_node:
816813
assert not ctx.is_quoted, "with-meta nodes are not used in quoted expressions"
817814

818815
descriptor = gen_node(form, ctx)
@@ -825,11 +822,14 @@ def with_meta(
825822
assert isinstance(meta_ast, MapNode) or (
826823
isinstance(meta_ast, Const) and meta_ast.type == ConstType.MAP
827824
)
828-
return WithMeta(
829-
form=form,
830-
meta=meta_ast,
831-
expr=descriptor,
832-
env=ctx.get_node_env(pos=ctx.syntax_position),
825+
return cast(
826+
T_node,
827+
WithMeta(
828+
form=cast(LispForm, form),
829+
meta=meta_ast,
830+
expr=descriptor,
831+
env=ctx.get_node_env(pos=ctx.syntax_position),
832+
),
833833
)
834834

835835
return descriptor
@@ -3113,7 +3113,7 @@ def _yield_ast(form: ISeq, ctx: AnalyzerContext) -> Yield:
31133113
return Yield.expressionless(form, ctx.get_node_env(pos=ctx.syntax_position))
31143114

31153115

3116-
SpecialFormHandler = Callable[[ISeq, AnalyzerContext], SpecialFormNode]
3116+
SpecialFormHandler = Callable[[T_form, AnalyzerContext], SpecialFormNode]
31173117
_SPECIAL_FORM_HANDLERS: Mapping[sym.Symbol, SpecialFormHandler] = {
31183118
SpecialForm.AWAIT: _await_ast,
31193119
SpecialForm.DEF: _def_ast,

0 commit comments

Comments
 (0)