Typhon-Language 0.1.2__py3-none-any.whl → 0.1.4__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.
- Typhon/Driver/configs.py +14 -0
- Typhon/Driver/debugging.py +148 -5
- Typhon/Driver/diagnostic.py +4 -3
- Typhon/Driver/language_server.py +25 -0
- Typhon/Driver/run.py +1 -1
- Typhon/Driver/translate.py +16 -11
- Typhon/Driver/utils.py +39 -1
- Typhon/Grammar/_typhon_parser.py +2920 -2718
- Typhon/Grammar/parser.py +80 -53
- Typhon/Grammar/parser_helper.py +68 -87
- Typhon/Grammar/syntax_errors.py +41 -20
- Typhon/Grammar/token_factory_custom.py +541 -485
- Typhon/Grammar/tokenizer_custom.py +52 -0
- Typhon/Grammar/typhon_ast.py +754 -76
- Typhon/Grammar/typhon_ast_error.py +438 -0
- Typhon/Grammar/unparse_custom.py +25 -0
- Typhon/LanguageServer/__init__.py +3 -0
- Typhon/LanguageServer/client/__init__.py +42 -0
- Typhon/LanguageServer/client/pyrefly.py +115 -0
- Typhon/LanguageServer/client/pyright.py +173 -0
- Typhon/LanguageServer/semantic_tokens.py +446 -0
- Typhon/LanguageServer/server.py +376 -0
- Typhon/LanguageServer/utils.py +65 -0
- Typhon/SourceMap/ast_match_based_map.py +199 -152
- Typhon/SourceMap/ast_matching.py +102 -87
- Typhon/SourceMap/datatype.py +275 -264
- Typhon/SourceMap/defined_name_retrieve.py +145 -0
- Typhon/Transform/comprehension_to_function.py +2 -5
- Typhon/Transform/const_member_to_final.py +12 -7
- Typhon/Transform/extended_patterns.py +139 -0
- Typhon/Transform/forbidden_statements.py +25 -0
- Typhon/Transform/if_while_let.py +122 -11
- Typhon/Transform/inline_statement_block_capture.py +22 -15
- Typhon/Transform/optional_operators_to_checked.py +14 -6
- Typhon/Transform/placeholder_to_function.py +0 -1
- Typhon/Transform/record_to_dataclass.py +22 -238
- Typhon/Transform/scope_check_rename.py +109 -29
- Typhon/Transform/transform.py +16 -12
- Typhon/Transform/type_abbrev_desugar.py +11 -15
- Typhon/Transform/type_annotation_check_expand.py +2 -2
- Typhon/Transform/utils/__init__.py +0 -0
- Typhon/Transform/utils/imports.py +83 -0
- Typhon/Transform/{utils.py → utils/jump_away.py} +2 -38
- Typhon/Transform/utils/make_class.py +135 -0
- Typhon/Transform/visitor.py +25 -0
- Typhon/Typing/pyrefly.py +145 -0
- Typhon/Typing/pyright.py +141 -144
- Typhon/Typing/result_diagnostic.py +1 -1
- Typhon/__main__.py +15 -1
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/METADATA +13 -6
- typhon_language-0.1.4.dist-info/RECORD +65 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/WHEEL +1 -1
- typhon_language-0.1.4.dist-info/licenses/LICENSE +201 -0
- typhon_language-0.1.2.dist-info/RECORD +0 -48
- typhon_language-0.1.2.dist-info/licenses/LICENSE +0 -21
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/entry_points.txt +0 -0
- {typhon_language-0.1.2.dist-info → typhon_language-0.1.4.dist-info}/top_level.txt +0 -0
Typhon/Transform/transform.py
CHANGED
|
@@ -13,34 +13,38 @@ from .if_while_let import if_while_let_transform
|
|
|
13
13
|
from .comprehension_to_function import comprehension_to_function
|
|
14
14
|
from .placeholder_to_function import placeholder_to_func
|
|
15
15
|
from .record_to_dataclass import record_to_dataclass
|
|
16
|
+
from .extended_patterns import extended_protocol
|
|
16
17
|
|
|
17
18
|
from ..Grammar.syntax_errors import raise_from_module_syntax_errors
|
|
19
|
+
from ..Grammar.unparse_custom import unparse_custom
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
def transform(mod: ast.Module):
|
|
21
23
|
check_forbidden_statements(mod)
|
|
22
24
|
record_to_dataclass(mod)
|
|
23
|
-
debug_verbose_print(f"After record_to_dataclass:\n{
|
|
25
|
+
debug_verbose_print(f"After record_to_dataclass:\n{unparse_custom(mod)}\n")
|
|
26
|
+
extended_protocol(mod)
|
|
27
|
+
debug_verbose_print(f"After extended_protocol:\n{unparse_custom(mod)}\n")
|
|
24
28
|
inline_statement_block_capture(mod)
|
|
25
|
-
debug_print(f"After inline_statement_block_capture:\n{
|
|
29
|
+
debug_print(f"After inline_statement_block_capture:\n{unparse_custom(mod)}\n")
|
|
26
30
|
if_while_let_transform(mod)
|
|
27
|
-
debug_verbose_print(f"After if_while_let_transform:\n{
|
|
31
|
+
debug_verbose_print(f"After if_while_let_transform:\n{unparse_custom(mod)}\n")
|
|
28
32
|
insert_self_to_method(mod)
|
|
29
|
-
debug_verbose_print(f"After insert_self_to_method:\n{
|
|
33
|
+
debug_verbose_print(f"After insert_self_to_method:\n{unparse_custom(mod)}\n")
|
|
30
34
|
comprehension_to_function(mod)
|
|
31
|
-
debug_verbose_print(f"After comprehension_to_function:\n{
|
|
35
|
+
debug_verbose_print(f"After comprehension_to_function:\n{unparse_custom(mod)}\n")
|
|
32
36
|
func_literal_to_def(mod)
|
|
33
|
-
debug_print(f"After func_literal_to_def:\n{
|
|
37
|
+
debug_print(f"After func_literal_to_def:\n{unparse_custom(mod)}\n")
|
|
34
38
|
scope_check_rename(mod)
|
|
35
|
-
debug_print(f"After scope_check_rename:\n{
|
|
39
|
+
debug_print(f"After scope_check_rename:\n{unparse_custom(mod)}\n")
|
|
36
40
|
placeholder_to_func(mod)
|
|
37
|
-
debug_print(f"After placeholder_to_func:\n{
|
|
41
|
+
debug_print(f"After placeholder_to_func:\n{unparse_custom(mod)}\n")
|
|
38
42
|
optional_to_checked(mod)
|
|
39
|
-
debug_verbose_print(f"After optional_to_checked:\n{
|
|
43
|
+
debug_verbose_print(f"After optional_to_checked:\n{unparse_custom(mod)}\n")
|
|
40
44
|
type_annotation_check_expand(mod)
|
|
41
|
-
debug_verbose_print(f"After type_annotation_check_expand:\n{
|
|
45
|
+
debug_verbose_print(f"After type_annotation_check_expand:\n{unparse_custom(mod)}\n")
|
|
42
46
|
type_abbrev_desugar(mod)
|
|
43
|
-
debug_verbose_print(f"After func_type_to_protocol:\n{
|
|
47
|
+
debug_verbose_print(f"After func_type_to_protocol:\n{unparse_custom(mod)}\n")
|
|
44
48
|
const_member_to_final(mod)
|
|
45
|
-
debug_print(f"After transform:\n{
|
|
49
|
+
debug_print(f"After transform:\n{unparse_custom(mod)}\n")
|
|
46
50
|
raise_from_module_syntax_errors(mod)
|
|
@@ -12,12 +12,11 @@ from ..Grammar.typhon_ast import (
|
|
|
12
12
|
is_typing_expression,
|
|
13
13
|
is_optional_question,
|
|
14
14
|
)
|
|
15
|
-
from ..Grammar.syntax_errors import (
|
|
16
|
-
raise_type_annotation_error,
|
|
17
|
-
try_handle_syntax_error_or,
|
|
18
|
-
)
|
|
19
15
|
from .visitor import TyphonASTVisitor, TyphonASTTransformer
|
|
20
|
-
from .utils import
|
|
16
|
+
from .utils.imports import (
|
|
17
|
+
get_insert_point_for_class,
|
|
18
|
+
get_protocol,
|
|
19
|
+
)
|
|
21
20
|
from ..Driver.debugging import debug_print, debug_verbose_print
|
|
22
21
|
|
|
23
22
|
|
|
@@ -34,7 +33,7 @@ class _GatherArrowType(TyphonASTVisitor):
|
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
def _protocol_for_function_type(
|
|
37
|
-
|
|
36
|
+
mod: ast.Module, func_type: FunctionType, arrow_type_name: str
|
|
38
37
|
) -> ast.ClassDef:
|
|
39
38
|
func_type.id = arrow_type_name
|
|
40
39
|
args = get_args_of_function_type(func_type)
|
|
@@ -77,9 +76,7 @@ def _protocol_for_function_type(
|
|
|
77
76
|
)
|
|
78
77
|
protocol_def = ast.ClassDef(
|
|
79
78
|
name=arrow_type_name,
|
|
80
|
-
bases=[
|
|
81
|
-
ast.Name(id=protocol_name, ctx=ast.Load()),
|
|
82
|
-
],
|
|
79
|
+
bases=[get_protocol(mod, ctx=ast.Load(), **get_empty_pos_attributes())],
|
|
83
80
|
keywords=[],
|
|
84
81
|
body=[func_def],
|
|
85
82
|
decorator_list=[],
|
|
@@ -92,18 +89,18 @@ def _protocol_for_function_type(
|
|
|
92
89
|
|
|
93
90
|
|
|
94
91
|
def _add_protocols(
|
|
95
|
-
mod: ast.Module, func_types: list[tuple[FunctionType, str]]
|
|
92
|
+
mod: ast.Module, func_types: list[tuple[FunctionType, str]]
|
|
96
93
|
) -> dict[FunctionType, ast.ClassDef]:
|
|
97
94
|
result: dict[FunctionType, ast.ClassDef] = {}
|
|
95
|
+
# Ensure protocol is imported before classes.
|
|
96
|
+
get_protocol(mod, ctx=ast.Load(), **get_empty_pos_attributes())
|
|
98
97
|
# Insert after imports.
|
|
99
98
|
insert_point = get_insert_point_for_class(mod)
|
|
100
99
|
for func_type, arrow_type_name in func_types:
|
|
101
100
|
debug_print(
|
|
102
101
|
f"Adding protocol for function type: {func_type.__dict__} as {arrow_type_name}"
|
|
103
102
|
)
|
|
104
|
-
protocol_def = _protocol_for_function_type(
|
|
105
|
-
func_type, arrow_type_name, protocol_name
|
|
106
|
-
)
|
|
103
|
+
protocol_def = _protocol_for_function_type(mod, func_type, arrow_type_name)
|
|
107
104
|
result[func_type] = protocol_def
|
|
108
105
|
mod.body.insert(insert_point, protocol_def)
|
|
109
106
|
insert_point += 1
|
|
@@ -193,8 +190,7 @@ def type_abbrev_desugar(mod: ast.Module):
|
|
|
193
190
|
gatherer = _GatherArrowType(mod)
|
|
194
191
|
gatherer.run()
|
|
195
192
|
if gatherer.func_types:
|
|
196
|
-
|
|
197
|
-
_add_protocols(mod, gatherer.func_types, protocol_name)
|
|
193
|
+
_add_protocols(mod, gatherer.func_types)
|
|
198
194
|
# Run optional question first, as it is represented as Tuple nodes.
|
|
199
195
|
_OptionalQuestionTransformer(mod).run()
|
|
200
196
|
_TupleListTransformer(mod).run()
|
|
@@ -13,7 +13,7 @@ from ..Grammar.typhon_ast import (
|
|
|
13
13
|
from ..Grammar.syntax_errors import (
|
|
14
14
|
raise_type_annotation_error,
|
|
15
15
|
handle_syntax_error,
|
|
16
|
-
|
|
16
|
+
TyphonTransformSyntaxError,
|
|
17
17
|
)
|
|
18
18
|
from .visitor import TyphonASTTransformer, TyphonASTVisitor, flat_append
|
|
19
19
|
from ..Driver.debugging import debug_print, debug_verbose_print
|
|
@@ -50,7 +50,7 @@ def _expand_target_annotation(
|
|
|
50
50
|
result.append(new_assign)
|
|
51
51
|
copy_is_let_var(orig_node, new_assign)
|
|
52
52
|
return result
|
|
53
|
-
except
|
|
53
|
+
except TyphonTransformSyntaxError as e:
|
|
54
54
|
handle_syntax_error(module, e)
|
|
55
55
|
return []
|
|
56
56
|
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from ...Grammar.typhon_ast import (
|
|
2
|
+
PosAttributes,
|
|
3
|
+
add_import_alias_top,
|
|
4
|
+
is_case_irrefutable,
|
|
5
|
+
get_pos_attributes,
|
|
6
|
+
pos_attribute_to_range,
|
|
7
|
+
set_is_internal_name,
|
|
8
|
+
set_is_var,
|
|
9
|
+
)
|
|
10
|
+
from ..name_generator import (
|
|
11
|
+
get_protocol_name,
|
|
12
|
+
get_final_name,
|
|
13
|
+
get_dataclass_name,
|
|
14
|
+
get_runtime_checkable_name,
|
|
15
|
+
)
|
|
16
|
+
import ast
|
|
17
|
+
from contextlib import contextmanager
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Protocol, Iterable, Final, Unpack
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _add_import_for_protocol(mod: ast.Module):
|
|
23
|
+
name = get_protocol_name()
|
|
24
|
+
add_import_alias_top(mod, "typing", "Protocol", name)
|
|
25
|
+
return name
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_protocol(
|
|
29
|
+
mod: ast.Module, ctx: ast.expr_context, **kwargs: Unpack[PosAttributes]
|
|
30
|
+
) -> ast.Name:
|
|
31
|
+
protocol_name = _add_import_for_protocol(mod)
|
|
32
|
+
return set_is_internal_name(ast.Name(id=protocol_name, ctx=ctx, **kwargs))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def add_import_for_final(mod: ast.Module):
|
|
36
|
+
name = get_final_name()
|
|
37
|
+
add_import_alias_top(mod, "typing", "Final", name)
|
|
38
|
+
return name
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_final(ctx: ast.expr_context, **kwargs: Unpack[PosAttributes]) -> ast.Name:
|
|
42
|
+
final_name = get_final_name()
|
|
43
|
+
return set_is_internal_name(ast.Name(id=final_name, ctx=ctx, **kwargs))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def add_import_get_final(
|
|
47
|
+
mod: ast.Module, ctx: ast.expr_context, **kwargs: Unpack[PosAttributes]
|
|
48
|
+
) -> ast.Name:
|
|
49
|
+
final_name = add_import_for_final(mod)
|
|
50
|
+
return set_is_internal_name(ast.Name(id=final_name, ctx=ctx, **kwargs))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _add_import_for_dataclass(mod: ast.Module):
|
|
54
|
+
name = get_dataclass_name()
|
|
55
|
+
add_import_alias_top(mod, "dataclasses", "dataclass", name)
|
|
56
|
+
return name
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_dataclass(
|
|
60
|
+
mod: ast.Module, ctx: ast.expr_context, **kwargs: Unpack[PosAttributes]
|
|
61
|
+
) -> ast.Name:
|
|
62
|
+
dataclass_name = _add_import_for_dataclass(mod)
|
|
63
|
+
return set_is_internal_name(ast.Name(id=dataclass_name, ctx=ctx, **kwargs))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _add_import_for_runtime_checkable(mod: ast.Module):
|
|
67
|
+
name = get_runtime_checkable_name()
|
|
68
|
+
add_import_alias_top(mod, "typing", "runtime_checkable", name)
|
|
69
|
+
return name
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_runtime_checkable(
|
|
73
|
+
mod: ast.Module, ctx: ast.expr_context, **kwargs: Unpack[PosAttributes]
|
|
74
|
+
) -> ast.Name:
|
|
75
|
+
runtime_checkable_name = _add_import_for_runtime_checkable(mod)
|
|
76
|
+
return set_is_internal_name(ast.Name(id=runtime_checkable_name, ctx=ctx, **kwargs))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_insert_point_for_class(module: ast.Module) -> int:
|
|
80
|
+
for index, stmt in enumerate(module.body):
|
|
81
|
+
if not isinstance(stmt, (ast.Import, ast.ImportFrom)):
|
|
82
|
+
return index
|
|
83
|
+
return len(module.body)
|
|
@@ -1,46 +1,9 @@
|
|
|
1
|
-
from
|
|
2
|
-
from .name_generator import (
|
|
3
|
-
get_protocol_name,
|
|
4
|
-
get_final_name,
|
|
5
|
-
get_dataclass_name,
|
|
6
|
-
get_runtime_checkable_name,
|
|
7
|
-
)
|
|
1
|
+
from ...Grammar.typhon_ast import is_case_irrefutable
|
|
8
2
|
import ast
|
|
9
3
|
from contextlib import contextmanager
|
|
10
4
|
from dataclasses import dataclass
|
|
11
5
|
|
|
12
6
|
|
|
13
|
-
def add_import_for_protocol(mod: ast.Module):
|
|
14
|
-
name = get_protocol_name()
|
|
15
|
-
add_import_alias_top(mod, "typing", "Protocol", name)
|
|
16
|
-
return name
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def add_import_for_final(mod: ast.Module):
|
|
20
|
-
name = get_final_name()
|
|
21
|
-
add_import_alias_top(mod, "typing", "Final", name)
|
|
22
|
-
return name
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def add_import_for_dataclass(mod: ast.Module):
|
|
26
|
-
name = get_dataclass_name()
|
|
27
|
-
add_import_alias_top(mod, "dataclasses", "dataclass", name)
|
|
28
|
-
return name
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def add_import_for_runtime_checkable(mod: ast.Module):
|
|
32
|
-
name = get_runtime_checkable_name()
|
|
33
|
-
add_import_alias_top(mod, "typing", "runtime_checkable", name)
|
|
34
|
-
return name
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def get_insert_point_for_class(module: ast.Module) -> int:
|
|
38
|
-
for index, stmt in enumerate(module.body):
|
|
39
|
-
if not isinstance(stmt, (ast.Import, ast.ImportFrom)):
|
|
40
|
-
return index
|
|
41
|
-
return len(module.body)
|
|
42
|
-
|
|
43
|
-
|
|
44
7
|
@dataclass
|
|
45
8
|
class _Loop:
|
|
46
9
|
node: ast.AST
|
|
@@ -117,6 +80,7 @@ class _JumpAwayVisitor(ast.NodeVisitor):
|
|
|
117
80
|
return jump_away
|
|
118
81
|
|
|
119
82
|
def visit_Match(self, node: ast.Match):
|
|
83
|
+
# Check that all cases jump away, and at least one is irrefutable.
|
|
120
84
|
jump_away = True
|
|
121
85
|
found_irrefutable = False
|
|
122
86
|
for case in node.cases:
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from ...Driver.debugging import debug_print
|
|
2
|
+
from ...Grammar.typhon_ast import (
|
|
3
|
+
get_pos_attributes,
|
|
4
|
+
pos_attribute_to_range,
|
|
5
|
+
set_is_var,
|
|
6
|
+
PosAttributes,
|
|
7
|
+
)
|
|
8
|
+
import ast
|
|
9
|
+
from typing import Protocol, Iterable, Final
|
|
10
|
+
from .imports import (
|
|
11
|
+
get_dataclass,
|
|
12
|
+
add_import_get_final,
|
|
13
|
+
get_protocol,
|
|
14
|
+
get_runtime_checkable,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NameAndAnnotation(Protocol):
|
|
19
|
+
name: Final[ast.Name]
|
|
20
|
+
annotation: Final[ast.expr]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _class_contents_for_fields(
|
|
24
|
+
fields: Iterable[NameAndAnnotation],
|
|
25
|
+
final_imported_name: ast.Name,
|
|
26
|
+
) -> list[ast.stmt]:
|
|
27
|
+
result: list[ast.stmt] = []
|
|
28
|
+
for field in fields:
|
|
29
|
+
name = field.name
|
|
30
|
+
if field.annotation:
|
|
31
|
+
# Currently annotation is always Final[...] for record fields.
|
|
32
|
+
annotation = ast.Subscript(
|
|
33
|
+
value=final_imported_name,
|
|
34
|
+
slice=field.annotation,
|
|
35
|
+
ctx=ast.Load(),
|
|
36
|
+
**get_pos_attributes(name),
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
annotation = final_imported_name
|
|
40
|
+
ann_assign = ast.AnnAssign(
|
|
41
|
+
target=ast.Name(id=name.id, ctx=ast.Store(), **get_pos_attributes(name)),
|
|
42
|
+
annotation=annotation,
|
|
43
|
+
value=None,
|
|
44
|
+
simple=1,
|
|
45
|
+
**get_pos_attributes(name),
|
|
46
|
+
)
|
|
47
|
+
# "var" because dataclass is frozen, "Final" is not required.
|
|
48
|
+
set_is_var(ann_assign)
|
|
49
|
+
result.append(ann_assign)
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _type_vars_for_fields(
|
|
54
|
+
pos: PosAttributes, type_variables: list[str]
|
|
55
|
+
) -> list[ast.type_param]:
|
|
56
|
+
type_params: list[ast.type_param] = []
|
|
57
|
+
for tv in type_variables:
|
|
58
|
+
type_params.append(ast.TypeVar(name=tv, **pos_attribute_to_range(pos)))
|
|
59
|
+
return type_params
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def make_protocol_definition(
|
|
63
|
+
mod: ast.Module,
|
|
64
|
+
class_name: str,
|
|
65
|
+
type_variables: list[str],
|
|
66
|
+
fields: Iterable[NameAndAnnotation],
|
|
67
|
+
pos: PosAttributes,
|
|
68
|
+
) -> ast.ClassDef:
|
|
69
|
+
protocol_imported_name = get_protocol(mod, ctx=ast.Load(), **pos)
|
|
70
|
+
runtime_checkable_imported_name = get_runtime_checkable(mod, ctx=ast.Load(), **pos)
|
|
71
|
+
final_imported_name = add_import_get_final(mod, ctx=ast.Load(), **pos)
|
|
72
|
+
result = ast.ClassDef(
|
|
73
|
+
name=class_name,
|
|
74
|
+
type_params=_type_vars_for_fields(pos, type_variables),
|
|
75
|
+
bases=[protocol_imported_name],
|
|
76
|
+
keywords=[],
|
|
77
|
+
# Currently all fields are Final.
|
|
78
|
+
body=_class_contents_for_fields(fields, final_imported_name),
|
|
79
|
+
decorator_list=[runtime_checkable_imported_name],
|
|
80
|
+
**pos,
|
|
81
|
+
)
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _dataclass_decorator(
|
|
86
|
+
dataclass_imported_name: ast.Name, repr: bool, pos: PosAttributes
|
|
87
|
+
) -> ast.expr:
|
|
88
|
+
return ast.Call(
|
|
89
|
+
func=dataclass_imported_name,
|
|
90
|
+
args=[],
|
|
91
|
+
keywords=[
|
|
92
|
+
ast.keyword(arg="frozen", value=ast.Constant(value=True)),
|
|
93
|
+
ast.keyword(arg="repr", value=ast.Constant(value=repr)),
|
|
94
|
+
ast.keyword(arg="unsafe_hash", value=ast.Constant(value=True)),
|
|
95
|
+
ast.keyword(arg="kw_only", value=ast.Constant(value=True)),
|
|
96
|
+
],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def make_dataclass_definition(
|
|
101
|
+
mod: ast.Module,
|
|
102
|
+
class_name: str,
|
|
103
|
+
type_variables: list[str],
|
|
104
|
+
fields: Iterable[NameAndAnnotation],
|
|
105
|
+
pos: PosAttributes,
|
|
106
|
+
) -> ast.ClassDef:
|
|
107
|
+
dataclass_imported_name = get_dataclass(mod, ctx=ast.Load(), **pos)
|
|
108
|
+
final_imported_name = add_import_get_final(mod, ctx=ast.Load(), **pos)
|
|
109
|
+
result = ast.ClassDef(
|
|
110
|
+
name=class_name,
|
|
111
|
+
type_params=_type_vars_for_fields(pos, type_variables),
|
|
112
|
+
bases=[],
|
|
113
|
+
keywords=[],
|
|
114
|
+
body=_class_contents_for_fields(fields, final_imported_name),
|
|
115
|
+
decorator_list=[
|
|
116
|
+
_dataclass_decorator(dataclass_imported_name, repr=False, pos=pos)
|
|
117
|
+
],
|
|
118
|
+
**pos,
|
|
119
|
+
)
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def make_dataclass_protocol_definition(
|
|
124
|
+
mod: ast.Module,
|
|
125
|
+
class_name: str,
|
|
126
|
+
type_variables: list[str],
|
|
127
|
+
fields: Iterable[NameAndAnnotation],
|
|
128
|
+
pos: PosAttributes,
|
|
129
|
+
) -> ast.ClassDef:
|
|
130
|
+
dataclass_imported_name = get_dataclass(mod, ctx=ast.Load(), **pos)
|
|
131
|
+
result = make_protocol_definition(mod, class_name, type_variables, fields, pos)
|
|
132
|
+
result.decorator_list.append(
|
|
133
|
+
_dataclass_decorator(dataclass_imported_name, repr=True, pos=pos)
|
|
134
|
+
)
|
|
135
|
+
return result
|
Typhon/Transform/visitor.py
CHANGED
|
@@ -276,6 +276,31 @@ class _ErrorHandlingMixin:
|
|
|
276
276
|
)
|
|
277
277
|
|
|
278
278
|
|
|
279
|
+
class TyphonASTRawVisitor(
|
|
280
|
+
ast.NodeVisitor,
|
|
281
|
+
_TyphonExtendedNodeTransformerMixin,
|
|
282
|
+
):
|
|
283
|
+
def visit_PossiblyAnnotatedNode(self, node: ast.AST):
|
|
284
|
+
self._visit_Possibly_Annotated_Node(node, self, False)
|
|
285
|
+
|
|
286
|
+
@override
|
|
287
|
+
def visit(self, node: ast.AST):
|
|
288
|
+
return self._visit(node, self, super().visit)
|
|
289
|
+
|
|
290
|
+
@override
|
|
291
|
+
def generic_visit(self, node: ast.AST):
|
|
292
|
+
# visit() calls _visit() but generic_visit() does not. So we need to dispatch here too.
|
|
293
|
+
if isinstance(node, ast.Name):
|
|
294
|
+
if is_function_literal(node):
|
|
295
|
+
return self._visit_FunctionLiteral(node, self, False)
|
|
296
|
+
elif is_function_type(node):
|
|
297
|
+
return self._visit_FunctionType(node, self, False)
|
|
298
|
+
elif is_control_comprehension(node):
|
|
299
|
+
return self._visit_ControlComprehension(node, self, False)
|
|
300
|
+
self.visit_PossiblyAnnotatedNode(node)
|
|
301
|
+
return super().generic_visit(node)
|
|
302
|
+
|
|
303
|
+
|
|
279
304
|
class TyphonASTVisitor(
|
|
280
305
|
ast.NodeVisitor,
|
|
281
306
|
_ScopeManagerMixin,
|
Typhon/Typing/pyrefly.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import subprocess
|
|
4
|
+
import toml
|
|
5
|
+
from ..Driver.debugging import debug_print
|
|
6
|
+
from typing import Literal, cast, Any
|
|
7
|
+
import json
|
|
8
|
+
from .result_diagnostic import Severity, Diagnostic, TypeCheckResult
|
|
9
|
+
from ..SourceMap.datatype import Range, Pos
|
|
10
|
+
|
|
11
|
+
# https://docs.basedpyrefly.com/dev/configuration/config-files/
|
|
12
|
+
type TypeCheckLevel = Literal[
|
|
13
|
+
"off",
|
|
14
|
+
"basic",
|
|
15
|
+
"strict",
|
|
16
|
+
"all",
|
|
17
|
+
"translate", # Default for Typhon translation
|
|
18
|
+
"script", # Default for Typhon scripts
|
|
19
|
+
]
|
|
20
|
+
translate_config = {
|
|
21
|
+
"typeCheckingMode": "strict",
|
|
22
|
+
"reportUnusedExpression": "warning",
|
|
23
|
+
"reportUnusedClass": "warning",
|
|
24
|
+
"reportUnusedImport": "warning",
|
|
25
|
+
"reportUnusedFunction": "warning",
|
|
26
|
+
"reportUnusedVariable": "warning",
|
|
27
|
+
"reportUnusedCallResult": "warning",
|
|
28
|
+
"reportUnnecessaryIsInstance": "warning",
|
|
29
|
+
"reportUnnecessaryCast": "warning",
|
|
30
|
+
# "reportUnnecessaryComparison": "warning", # To check type of patterns
|
|
31
|
+
"reportUnnecessaryContains": "warning",
|
|
32
|
+
"reportDeprecated": "warning",
|
|
33
|
+
}
|
|
34
|
+
script_config = {
|
|
35
|
+
"typeCheckingMode": "strict",
|
|
36
|
+
"reportUnusedExpression": "none",
|
|
37
|
+
"reportUnusedClass": "none",
|
|
38
|
+
"reportUnusedImport": "none",
|
|
39
|
+
"reportUnusedFunction": "none",
|
|
40
|
+
"reportUnusedVariable": "none",
|
|
41
|
+
"reportUnusedCallResult": "none",
|
|
42
|
+
"reportUnnecessaryIsInstance": "none",
|
|
43
|
+
"reportUnnecessaryCast": "none",
|
|
44
|
+
# "reportUnnecessaryComparison": "none", # To check type of patterns
|
|
45
|
+
"reportUnnecessaryContains": "none",
|
|
46
|
+
"reportDeprecated": "none",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def write_pyrefly_config(
|
|
51
|
+
output_dir: Path, level: TypeCheckLevel = "translate", overwrite: bool = False
|
|
52
|
+
) -> str:
|
|
53
|
+
config = {
|
|
54
|
+
"include": ["**/*.py"],
|
|
55
|
+
"exclude": ["**/__pycache__"],
|
|
56
|
+
"typeCheckingMode": "off",
|
|
57
|
+
}
|
|
58
|
+
if level == "translate":
|
|
59
|
+
config.update(translate_config)
|
|
60
|
+
elif level == "script":
|
|
61
|
+
config.update(script_config)
|
|
62
|
+
elif level == "basic":
|
|
63
|
+
config["typeCheckingMode"] = "basic"
|
|
64
|
+
elif level == "strict":
|
|
65
|
+
config["typeCheckingMode"] = "strict"
|
|
66
|
+
elif level == "all":
|
|
67
|
+
config["typeCheckingMode"] = "all"
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError(f"Unknown type check level: {level}")
|
|
70
|
+
config_path = output_dir / "pyreflyconfig.json"
|
|
71
|
+
if not overwrite and config_path.exists():
|
|
72
|
+
debug_print(
|
|
73
|
+
f"Config file already exists at {config_path}. Use overwrite=True to replace."
|
|
74
|
+
)
|
|
75
|
+
return str(config_path)
|
|
76
|
+
toml_str = toml.dumps(config)
|
|
77
|
+
with open(config_path, "w") as f:
|
|
78
|
+
f.write(toml_str)
|
|
79
|
+
debug_print(f"Generated pyrefly config at {config_path}")
|
|
80
|
+
return str(config_path)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _try_read_attr[T](d: Any, attr: str, default: T) -> T:
|
|
84
|
+
result = d.get(attr, default)
|
|
85
|
+
return cast(T, result)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _parse_pos_range(pos: Any) -> Range:
|
|
89
|
+
start = _try_read_attr(pos, "start", {})
|
|
90
|
+
end = _try_read_attr(pos, "end", {})
|
|
91
|
+
return Range(
|
|
92
|
+
start=Pos(line=start.get("line", 0), column=start.get("character", 0)),
|
|
93
|
+
end=Pos(line=end.get("line", 0), column=end.get("character", 0)),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _parse_diagnostic(diag: Any) -> Diagnostic:
|
|
98
|
+
return Diagnostic(
|
|
99
|
+
file_path=_try_read_attr(diag, "file", ""),
|
|
100
|
+
severity=Severity[_try_read_attr(diag, "severity", "INFO").upper()],
|
|
101
|
+
message=_try_read_attr(diag, "message", ""),
|
|
102
|
+
pos=_parse_pos_range(diag["range"]),
|
|
103
|
+
rule=_try_read_attr(diag, "rule", ""),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def parse_json_output(output: str, returncode: int, stderr: str) -> TypeCheckResult:
|
|
108
|
+
data = json.loads(output)
|
|
109
|
+
diagnostics = [
|
|
110
|
+
_parse_diagnostic(diag) for diag in data.get("generalDiagnostics", [])
|
|
111
|
+
]
|
|
112
|
+
summary = _try_read_attr(data, "summary", {})
|
|
113
|
+
return TypeCheckResult(
|
|
114
|
+
returncode=returncode,
|
|
115
|
+
stderr=stderr,
|
|
116
|
+
files_analyzed=_try_read_attr(summary, "filesAnalyzed", 0),
|
|
117
|
+
num_errors=_try_read_attr(summary, "errorCount", 0),
|
|
118
|
+
num_warnings=_try_read_attr(summary, "warningCount", 0),
|
|
119
|
+
num_info=_try_read_attr(summary, "informationCount", 0),
|
|
120
|
+
time_in_sec=_try_read_attr(summary, "timeInSec", 0.0),
|
|
121
|
+
diagnostics=diagnostics,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def run_pyrefly(
|
|
126
|
+
py_file_or_dir: Path, level: TypeCheckLevel = "translate"
|
|
127
|
+
) -> TypeCheckResult:
|
|
128
|
+
output = subprocess.run(
|
|
129
|
+
[
|
|
130
|
+
sys.executable,
|
|
131
|
+
"-m",
|
|
132
|
+
"pyrefly",
|
|
133
|
+
"check",
|
|
134
|
+
"--output-format",
|
|
135
|
+
"json",
|
|
136
|
+
str(py_file_or_dir),
|
|
137
|
+
],
|
|
138
|
+
stdout=subprocess.PIPE,
|
|
139
|
+
stderr=subprocess.PIPE,
|
|
140
|
+
shell=False,
|
|
141
|
+
)
|
|
142
|
+
result = parse_json_output(
|
|
143
|
+
output.stdout.decode(), output.returncode, output.stderr.decode()
|
|
144
|
+
)
|
|
145
|
+
return result
|