jaclang 0.2.5__py3-none-any.whl → 0.3.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.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (72) hide show
  1. jaclang/__init__.py +3 -3
  2. jaclang/cli/__init__.py +0 -1
  3. jaclang/cli/__jac_gen__/cli.py +4 -4
  4. jaclang/cli/__jac_gen__/cmds.py +1 -1
  5. jaclang/cli/__jac_gen__/cmds_impl.py +1 -1
  6. jaclang/core/__init__.py +5 -11
  7. jaclang/core/__jac_gen__/corelib.py +289 -0
  8. jaclang/core/__jac_gen__/corelib_impl.py +220 -0
  9. jaclang/core/corelib.jac +21 -34
  10. jaclang/core/corelib_impl.jac +317 -0
  11. jaclang/jac/__init__.py +1 -0
  12. jaclang/jac/__jac_gen__/jac_parser.py +2 -2
  13. jaclang/jac/absyntree.py +28 -8
  14. jaclang/jac/constant.py +3 -7
  15. jaclang/jac/parser.py +13 -9
  16. jaclang/jac/passes/main/__init__.py +2 -0
  17. jaclang/jac/passes/main/def_use_pass.py +3 -2
  18. jaclang/jac/passes/main/pyast_gen_pass.py +99 -34
  19. jaclang/jac/passes/main/schedules.py +6 -0
  20. jaclang/jac/passes/main/sym_tab_build_pass.py +3 -5
  21. jaclang/jac/passes/main/tests/test_jac_format_pass.py +22 -4
  22. jaclang/jac/passes/main/tests/test_type_check_pass.py +42 -0
  23. jaclang/jac/passes/main/type_check_pass.py +103 -0
  24. jaclang/jac/passes/tool/fuse_comments_pass.py +57 -39
  25. jaclang/jac/passes/tool/jac_formatter_pass.py +419 -192
  26. jaclang/jac/passes/transform.py +0 -39
  27. jaclang/jac/passes/utils/__init__.py +1 -0
  28. jaclang/jac/passes/utils/mypy_ast_build.py +302 -0
  29. jaclang/jac/plugin/__init__.py +5 -2
  30. jaclang/jac/plugin/default.py +20 -4
  31. jaclang/jac/plugin/feature.py +15 -6
  32. jaclang/jac/plugin/spec.py +34 -6
  33. jaclang/jac/tests/test_workspace.py +45 -5
  34. jaclang/jac/transpiler.py +4 -9
  35. jaclang/utils/helpers.py +0 -33
  36. jaclang/utils/lang_tools.py +3 -0
  37. jaclang/utils/test.py +3 -1
  38. jaclang/vendor/lark/py.typed +0 -0
  39. jaclang/vendor/mypy/checker.py +19 -12
  40. jaclang/vendor/mypy/checkexpr.py +31 -10
  41. jaclang/vendor/mypy/constraints.py +56 -38
  42. jaclang/vendor/mypy/expandtype.py +1 -0
  43. jaclang/vendor/mypy/meet.py +10 -1
  44. jaclang/vendor/mypy/messages.py +16 -4
  45. jaclang/vendor/mypy/moduleinspect.py +10 -4
  46. jaclang/vendor/mypy/py.typed +1 -0
  47. jaclang/vendor/mypy/semanal.py +18 -17
  48. jaclang/vendor/mypy/semanal_enum.py +7 -4
  49. jaclang/vendor/mypy/semanal_namedtuple.py +11 -1
  50. jaclang/vendor/mypy/semanal_typeddict.py +25 -11
  51. jaclang/vendor/mypy/stubdoc.py +18 -4
  52. jaclang/vendor/mypy/stubgen.py +80 -1
  53. jaclang/vendor/mypy/stubgenc.py +47 -5
  54. jaclang/vendor/mypy/stubtest.py +53 -3
  55. jaclang/vendor/mypy/stubutil.py +9 -9
  56. jaclang/vendor/mypy/test/testipc.py +16 -7
  57. jaclang/vendor/mypy/test/teststubtest.py +20 -2
  58. jaclang/vendor/mypy/types.py +1 -1
  59. jaclang/vendor/mypyc/irbuild/prebuildvisitor.py +2 -1
  60. jaclang/vendor/mypyc/test/test_run.py +2 -4
  61. jaclang/vendor/pluggy/py.typed +0 -0
  62. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/METADATA +1 -1
  63. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/RECORD +67 -62
  64. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/WHEEL +1 -1
  65. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/entry_points.txt +3 -0
  66. jaclang/core/arch_impl.jac +0 -131
  67. jaclang/core/element_impl.jac +0 -109
  68. jaclang/core/exec_ctx_impl.jac +0 -14
  69. jaclang/core/memory_impl.jac +0 -57
  70. jaclang/jac/tests/fixtures/__jac_gen__/hello_world.py +0 -5
  71. /jaclang/{jac/tests/fixtures → core}/__jac_gen__/__init__.py +0 -0
  72. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/top_level.txt +0 -0
jaclang/jac/transpiler.py CHANGED
@@ -4,11 +4,7 @@ from typing import Optional, Type
4
4
  import jaclang.jac.absyntree as ast
5
5
  from jaclang.jac.parser import JacParser
6
6
  from jaclang.jac.passes import Pass
7
- from jaclang.jac.passes.main import (
8
- PyOutPass,
9
- PyastGenPass,
10
- pass_schedule,
11
- )
7
+ from jaclang.jac.passes.main import PyOutPass, pass_schedule
12
8
  from jaclang.jac.passes.tool import JacFormatPass
13
9
  from jaclang.jac.passes.tool.schedules import format_pass
14
10
  from jaclang.jac.passes.transform import Alert
@@ -18,7 +14,6 @@ def transpile_jac(file_path: str) -> list[Alert]:
18
14
  """Transpiler Jac file and return python code as string."""
19
15
  code = jac_file_to_pass(
20
16
  file_path=file_path,
21
- target=PyastGenPass,
22
17
  schedule=pass_schedule,
23
18
  )
24
19
  if isinstance(code.ir, ast.Module) and not code.errors_had:
@@ -30,7 +25,7 @@ def transpile_jac(file_path: str) -> list[Alert]:
30
25
 
31
26
  def jac_file_to_pass(
32
27
  file_path: str,
33
- target: Optional[Type[Pass]] = PyastGenPass,
28
+ target: Optional[Type[Pass]] = None,
34
29
  schedule: list[Type[Pass]] = pass_schedule,
35
30
  ) -> Pass:
36
31
  """Convert a Jac file to an AST."""
@@ -46,7 +41,7 @@ def jac_file_to_pass(
46
41
  def jac_str_to_pass(
47
42
  jac_str: str,
48
43
  file_path: str,
49
- target: Optional[Type[Pass]] = PyastGenPass,
44
+ target: Optional[Type[Pass]] = None,
50
45
  schedule: list[Type[Pass]] = pass_schedule,
51
46
  ) -> Pass:
52
47
  """Convert a Jac file to an AST."""
@@ -64,7 +59,7 @@ def jac_str_to_pass(
64
59
 
65
60
  def jac_pass_to_pass(
66
61
  in_pass: Pass,
67
- target: Optional[Type[Pass]] = PyastGenPass,
62
+ target: Optional[Type[Pass]] = None,
68
63
  schedule: list[Type[Pass]] = pass_schedule,
69
64
  ) -> Pass:
70
65
  """Convert a Jac file to an AST."""
jaclang/utils/helpers.py CHANGED
@@ -1,11 +1,9 @@
1
1
  """Utility functions and classes for Jac compilation toolchain."""
2
2
  import re
3
3
  import textwrap
4
- import traceback
5
4
 
6
5
 
7
6
  import jaclang.jac.absyntree as ast
8
- from jaclang.jac.constant import Constants as Con, Values as Val
9
7
 
10
8
 
11
9
  def pascal_to_snake(pascal_string: str) -> str:
@@ -44,37 +42,6 @@ def dedent_code_block(code: str) -> str:
44
42
  return textwrap.dedent(code)
45
43
 
46
44
 
47
- def handle_jac_error(code_string: str, e: Exception, tb: traceback.StackSummary) -> str:
48
- """Handle Jac Error."""
49
- except_line = e.end_lineno if isinstance(e, SyntaxError) else list(tb)[-1].lineno
50
- if not isinstance(except_line, int) or except_line == 0:
51
- return ""
52
- py_error_region = clip_code_section(
53
- add_line_numbers(code_string), except_line, Val.JAC_ERROR_LINE_RANGE
54
- )
55
- try:
56
- jac_err_line = int(code_string.splitlines()[except_line - 1].split()[-1])
57
- mod_index = int(code_string.splitlines()[except_line - 1].split()[-2])
58
- mod_paths = code_string.split(Con.JAC_DEBUG_SPLITTER)[1].strip().splitlines()
59
- target_mod = mod_paths[mod_index]
60
- with open(target_mod, "r") as file:
61
- jac_code_string = file.read()
62
- jac_error_region = clip_code_section(
63
- add_line_numbers(jac_code_string), jac_err_line, Val.JAC_ERROR_LINE_RANGE
64
- )
65
- target_mod = f"JacCode File: {target_mod}:{jac_err_line}"
66
- except Exception as e:
67
- jac_error_region = str(e)
68
- target_mod = ""
69
- snippet = (
70
- f"{Con.JAC_ERROR_PREAMBLE}\n"
71
- f"{target_mod}\n"
72
- f"JacCode Snippet:\n{jac_error_region}\n"
73
- f"PyCode Snippet:\n{py_error_region}\n"
74
- )
75
- return snippet
76
-
77
-
78
45
  def get_ast_nodes_as_snake_case() -> list[str]:
79
46
  """Get all AST nodes as snake case."""
80
47
  import inspect
@@ -191,6 +191,9 @@ class AstTool:
191
191
  arrow = "-.->" if "Optional" in kid.typ else "-->"
192
192
  typ = (
193
193
  kid.typ.replace("Optional[", "")
194
+ .replace("SubNodeList[", "")
195
+ .replace("SubTag[", "")
196
+ .replace("Sequence[", "")
194
197
  .replace("]", "")
195
198
  .replace("|", ",")
196
199
  .replace("list[", "list - ")
jaclang/utils/test.py CHANGED
@@ -65,7 +65,9 @@ class TestCaseMicroSuite(ABC, TestCase):
65
65
  """Attach micro tests."""
66
66
  for filename in [
67
67
  os.path.normpath(os.path.join(root, name))
68
- for root, _, files in os.walk(os.path.dirname(jaclang.__file__))
68
+ for root, _, files in os.walk(
69
+ os.path.dirname(os.path.dirname(jaclang.__file__))
70
+ )
69
71
  for name in files
70
72
  if name.endswith(".jac") and not name.startswith("err")
71
73
  ]:
File without changes
@@ -6106,24 +6106,31 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
6106
6106
  if node.arg_kinds[0] != nodes.ARG_POS:
6107
6107
  # the first argument might be used as a kwarg
6108
6108
  called_type = get_proper_type(self.lookup_type(node.callee))
6109
- assert isinstance(called_type, (CallableType, Overloaded))
6109
+
6110
+ # TODO: there are some more cases in check_call() to handle.
6111
+ if isinstance(called_type, Instance):
6112
+ call = find_member(
6113
+ "__call__", called_type, called_type, is_operator=True
6114
+ )
6115
+ if call is not None:
6116
+ called_type = get_proper_type(call)
6110
6117
 
6111
6118
  # *assuming* the overloaded function is correct, there's a couple cases:
6112
6119
  # 1) The first argument has different names, but is pos-only. We don't
6113
6120
  # care about this case, the argument must be passed positionally.
6114
6121
  # 2) The first argument allows keyword reference, therefore must be the
6115
6122
  # same between overloads.
6116
- name = called_type.items[0].arg_names[0]
6117
-
6118
- if name in node.arg_names:
6119
- idx = node.arg_names.index(name)
6120
- # we want the idx-th variable to be narrowed
6121
- expr = collapse_walrus(node.args[idx])
6122
- else:
6123
- self.fail(
6124
- message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node
6125
- )
6126
- return {}, {}
6123
+ if isinstance(called_type, (CallableType, Overloaded)):
6124
+ name = called_type.items[0].arg_names[0]
6125
+ if name in node.arg_names:
6126
+ idx = node.arg_names.index(name)
6127
+ # we want the idx-th variable to be narrowed
6128
+ expr = collapse_walrus(node.args[idx])
6129
+ else:
6130
+ self.fail(
6131
+ message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node
6132
+ )
6133
+ return {}, {}
6127
6134
  if literal(expr) == LITERAL_TYPE:
6128
6135
  # Note: we wrap the target type, so that we can special case later.
6129
6136
  # Namely, for isinstance() we use a normal meet, while TypeGuard is
@@ -3870,8 +3870,9 @@ class ExpressionChecker(ExpressionVisitor[Type]):
3870
3870
  self,
3871
3871
  left: Type,
3872
3872
  right: Type,
3873
- original_container: Type | None = None,
3874
3873
  *,
3874
+ original_container: Type | None = None,
3875
+ seen_types: set[tuple[Type, Type]] | None = None,
3875
3876
  prefer_literal: bool = True,
3876
3877
  ) -> bool:
3877
3878
  """Check for dangerous non-overlapping comparisons like 42 == 'no'.
@@ -3892,6 +3893,12 @@ class ExpressionChecker(ExpressionVisitor[Type]):
3892
3893
  if not self.chk.options.strict_equality:
3893
3894
  return False
3894
3895
 
3896
+ if seen_types is None:
3897
+ seen_types = set()
3898
+ if (left, right) in seen_types:
3899
+ return False
3900
+ seen_types.add((left, right))
3901
+
3895
3902
  left, right = get_proper_types((left, right))
3896
3903
 
3897
3904
  # We suppress the error if there is a custom __eq__() method on either
@@ -3949,7 +3956,9 @@ class ExpressionChecker(ExpressionVisitor[Type]):
3949
3956
  abstract_set = self.chk.lookup_typeinfo("typing.AbstractSet")
3950
3957
  left = map_instance_to_supertype(left, abstract_set)
3951
3958
  right = map_instance_to_supertype(right, abstract_set)
3952
- return self.dangerous_comparison(left.args[0], right.args[0])
3959
+ return self.dangerous_comparison(
3960
+ left.args[0], right.args[0], seen_types=seen_types
3961
+ )
3953
3962
  elif left.type.has_base("typing.Mapping") and right.type.has_base(
3954
3963
  "typing.Mapping"
3955
3964
  ):
@@ -3958,13 +3967,17 @@ class ExpressionChecker(ExpressionVisitor[Type]):
3958
3967
  left = map_instance_to_supertype(left, abstract_map)
3959
3968
  right = map_instance_to_supertype(right, abstract_map)
3960
3969
  return self.dangerous_comparison(
3961
- left.args[0], right.args[0]
3962
- ) or self.dangerous_comparison(left.args[1], right.args[1])
3970
+ left.args[0], right.args[0], seen_types=seen_types
3971
+ ) or self.dangerous_comparison(
3972
+ left.args[1], right.args[1], seen_types=seen_types
3973
+ )
3963
3974
  elif (
3964
3975
  left_name in ("builtins.list", "builtins.tuple")
3965
3976
  and right_name == left_name
3966
3977
  ):
3967
- return self.dangerous_comparison(left.args[0], right.args[0])
3978
+ return self.dangerous_comparison(
3979
+ left.args[0], right.args[0], seen_types=seen_types
3980
+ )
3968
3981
  elif left_name in OVERLAPPING_BYTES_ALLOWLIST and right_name in (
3969
3982
  OVERLAPPING_BYTES_ALLOWLIST
3970
3983
  ):
@@ -6624,11 +6637,16 @@ class PolyTranslator(TypeTranslator):
6624
6637
  See docstring for apply_poly() for details.
6625
6638
  """
6626
6639
 
6627
- def __init__(self, poly_tvars: Sequence[TypeVarLikeType]) -> None:
6640
+ def __init__(
6641
+ self,
6642
+ poly_tvars: Iterable[TypeVarLikeType],
6643
+ bound_tvars: frozenset[TypeVarLikeType] = frozenset(),
6644
+ seen_aliases: frozenset[TypeInfo] = frozenset(),
6645
+ ) -> None:
6628
6646
  self.poly_tvars = set(poly_tvars)
6629
6647
  # This is a simplified version of TypeVarScope used during semantic analysis.
6630
- self.bound_tvars: set[TypeVarLikeType] = set()
6631
- self.seen_aliases: set[TypeInfo] = set()
6648
+ self.bound_tvars = bound_tvars
6649
+ self.seen_aliases = seen_aliases
6632
6650
 
6633
6651
  def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]:
6634
6652
  found_vars = []
@@ -6706,10 +6724,13 @@ class PolyTranslator(TypeTranslator):
6706
6724
  if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]:
6707
6725
  if t.type in self.seen_aliases:
6708
6726
  raise PolyTranslationError()
6709
- self.seen_aliases.add(t.type)
6710
6727
  call = find_member("__call__", t, t, is_operator=True)
6711
6728
  assert call is not None
6712
- return call.accept(self)
6729
+ return call.accept(
6730
+ PolyTranslator(
6731
+ self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type}
6732
+ )
6733
+ )
6713
6734
  return super().visit_instance(t)
6714
6735
 
6715
6736
 
@@ -238,29 +238,29 @@ def infer_constraints_for_callable(
238
238
  callee.arg_names[i],
239
239
  callee.arg_kinds[i],
240
240
  )
241
- if (
242
- param_spec
243
- and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2)
244
- and not incomplete_star_mapping
245
- ):
241
+ if param_spec and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2):
246
242
  # If actual arguments are mapped to ParamSpec type, we can't infer individual
247
243
  # constraints, instead store them and infer single constraint at the end.
248
244
  # It is impossible to map actual kind to formal kind, so use some heuristic.
249
245
  # This inference is used as a fallback, so relying on heuristic should be OK.
250
- param_spec_arg_types.append(
251
- mapper.expand_actual_type(
252
- actual_arg_type, arg_kinds[actual], None, arg_kinds[actual]
246
+ if not incomplete_star_mapping:
247
+ param_spec_arg_types.append(
248
+ mapper.expand_actual_type(
249
+ actual_arg_type,
250
+ arg_kinds[actual],
251
+ None,
252
+ arg_kinds[actual],
253
+ )
254
+ )
255
+ actual_kind = arg_kinds[actual]
256
+ param_spec_arg_kinds.append(
257
+ ARG_POS
258
+ if actual_kind not in (ARG_STAR, ARG_STAR2)
259
+ else actual_kind
260
+ )
261
+ param_spec_arg_names.append(
262
+ arg_names[actual] if arg_names else None
253
263
  )
254
- )
255
- actual_kind = arg_kinds[actual]
256
- param_spec_arg_kinds.append(
257
- ARG_POS
258
- if actual_kind not in (ARG_STAR, ARG_STAR2)
259
- else actual_kind
260
- )
261
- param_spec_arg_names.append(
262
- arg_names[actual] if arg_names else None
263
- )
264
264
  else:
265
265
  c = infer_constraints(
266
266
  callee.arg_types[i], actual_type, SUPERTYPE_OF
@@ -285,6 +285,9 @@ def infer_constraints_for_callable(
285
285
  ),
286
286
  )
287
287
  )
288
+ if any(isinstance(v, ParamSpecType) for v in callee.variables):
289
+ # As a perf optimization filter imprecise constraints only when we can have them.
290
+ constraints = filter_imprecise_kinds(constraints)
288
291
  return constraints
289
292
 
290
293
 
@@ -1197,32 +1200,23 @@ class ConstraintBuilderVisitor(TypeVisitor[List[Constraint]]):
1197
1200
  )
1198
1201
 
1199
1202
  param_spec_target: Type | None = None
1200
- skip_imprecise = (
1201
- any(c.type_var == param_spec.id for c in res)
1202
- and cactual.imprecise_arg_kinds
1203
- )
1204
1203
  if not cactual_ps:
1205
1204
  max_prefix_len = len(
1206
1205
  [k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)]
1207
1206
  )
1208
1207
  prefix_len = min(prefix_len, max_prefix_len)
1209
- # This logic matches top-level callable constraint exception, if we managed
1210
- # to get other constraints for ParamSpec, don't infer one with imprecise kinds
1211
- if not skip_imprecise:
1212
- param_spec_target = Parameters(
1213
- arg_types=cactual.arg_types[prefix_len:],
1214
- arg_kinds=cactual.arg_kinds[prefix_len:],
1215
- arg_names=cactual.arg_names[prefix_len:],
1216
- variables=cactual.variables
1217
- if not type_state.infer_polymorphic
1218
- else [],
1219
- imprecise_arg_kinds=cactual.imprecise_arg_kinds,
1220
- )
1208
+ param_spec_target = Parameters(
1209
+ arg_types=cactual.arg_types[prefix_len:],
1210
+ arg_kinds=cactual.arg_kinds[prefix_len:],
1211
+ arg_names=cactual.arg_names[prefix_len:],
1212
+ variables=cactual.variables
1213
+ if not type_state.infer_polymorphic
1214
+ else [],
1215
+ imprecise_arg_kinds=cactual.imprecise_arg_kinds,
1216
+ )
1221
1217
  else:
1222
- if (
1223
- len(param_spec.prefix.arg_types)
1224
- <= len(cactual_ps.prefix.arg_types)
1225
- and not skip_imprecise
1218
+ if len(param_spec.prefix.arg_types) <= len(
1219
+ cactual_ps.prefix.arg_types
1226
1220
  ):
1227
1221
  param_spec_target = cactual_ps.copy_modified(
1228
1222
  prefix=Parameters(
@@ -1770,3 +1764,27 @@ def infer_callable_arguments_constraints(
1770
1764
  )
1771
1765
  )
1772
1766
  return res
1767
+
1768
+
1769
+ def filter_imprecise_kinds(cs: list[Constraint]) -> list[Constraint]:
1770
+ """For each ParamSpec remove all imprecise constraints, if at least one precise available."""
1771
+ have_precise = set()
1772
+ for c in cs:
1773
+ if not isinstance(c.origin_type_var, ParamSpecType):
1774
+ continue
1775
+ if (
1776
+ isinstance(c.target, ParamSpecType)
1777
+ or isinstance(c.target, Parameters)
1778
+ and not c.target.imprecise_arg_kinds
1779
+ ):
1780
+ have_precise.add(c.type_var)
1781
+ new_cs = []
1782
+ for c in cs:
1783
+ if (
1784
+ not isinstance(c.origin_type_var, ParamSpecType)
1785
+ or c.type_var not in have_precise
1786
+ ):
1787
+ new_cs.append(c)
1788
+ if not isinstance(c.target, Parameters) or not c.target.imprecise_arg_kinds:
1789
+ new_cs.append(c)
1790
+ return new_cs
@@ -256,6 +256,7 @@ class ExpandTypeVisitor(TrivialSyntheticTypeTranslator):
256
256
  t.prefix.arg_kinds + repl.arg_kinds,
257
257
  t.prefix.arg_names + repl.arg_names,
258
258
  variables=[*t.prefix.variables, *repl.variables],
259
+ imprecise_arg_kinds=repl.imprecise_arg_kinds,
259
260
  )
260
261
  else:
261
262
  # We could encode Any as trivial parameters etc., but it would be too verbose.
@@ -268,6 +268,7 @@ def is_overlapping_types(
268
268
  ignore_promotions: bool = False,
269
269
  prohibit_none_typevar_overlap: bool = False,
270
270
  ignore_uninhabited: bool = False,
271
+ seen_types: set[tuple[Type, Type]] | None = None,
271
272
  ) -> bool:
272
273
  """Can a value of type 'left' also be of type 'right' or vice-versa?
273
274
 
@@ -281,12 +282,19 @@ def is_overlapping_types(
281
282
  # A type guard forces the new type even if it doesn't overlap the old.
282
283
  return True
283
284
 
285
+ if seen_types is None:
286
+ seen_types = set()
287
+ if (left, right) in seen_types:
288
+ return True
289
+ if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType):
290
+ seen_types.add((left, right))
291
+
284
292
  left, right = get_proper_types((left, right))
285
293
 
286
294
  def _is_overlapping_types(left: Type, right: Type) -> bool:
287
295
  """Encode the kind of overlapping check to perform.
288
296
 
289
- This function mostly exists so we don't have to repeat keyword arguments everywhere.
297
+ This function mostly exists, so we don't have to repeat keyword arguments everywhere.
290
298
  """
291
299
  return is_overlapping_types(
292
300
  left,
@@ -294,6 +302,7 @@ def is_overlapping_types(
294
302
  ignore_promotions=ignore_promotions,
295
303
  prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
296
304
  ignore_uninhabited=ignore_uninhabited,
305
+ seen_types=seen_types.copy(),
297
306
  )
298
307
 
299
308
  # We should never encounter this type.
@@ -1052,10 +1052,22 @@ class MessageBuilder:
1052
1052
  context,
1053
1053
  )
1054
1054
 
1055
+ def unexpected_keyword_argument_for_function(
1056
+ self,
1057
+ for_func: str,
1058
+ name: str,
1059
+ context: Context,
1060
+ *,
1061
+ matches: list[str] | None = None,
1062
+ ) -> None:
1063
+ msg = f'Unexpected keyword argument "{name}"' + for_func
1064
+ if matches:
1065
+ msg += f"; did you mean {pretty_seq(matches, 'or')}?"
1066
+ self.fail(msg, context, code=codes.CALL_ARG)
1067
+
1055
1068
  def unexpected_keyword_argument(
1056
1069
  self, callee: CallableType, name: str, arg_type: Type, context: Context
1057
1070
  ) -> None:
1058
- msg = f'Unexpected keyword argument "{name}"' + for_function(callee)
1059
1071
  # Suggest intended keyword, look for type match else fallback on any match.
1060
1072
  matching_type_args = []
1061
1073
  not_matching_type_args = []
@@ -1069,9 +1081,9 @@ class MessageBuilder:
1069
1081
  matches = best_matches(name, matching_type_args, n=3)
1070
1082
  if not matches:
1071
1083
  matches = best_matches(name, not_matching_type_args, n=3)
1072
- if matches:
1073
- msg += f"; did you mean {pretty_seq(matches, 'or')}?"
1074
- self.fail(msg, context, code=codes.CALL_ARG)
1084
+ self.unexpected_keyword_argument_for_function(
1085
+ for_function(callee), name, context, matches=matches
1086
+ )
1075
1087
  module = find_defining_module(self.modules, callee)
1076
1088
  if module:
1077
1089
  assert callee.definition is not None
@@ -8,7 +8,7 @@ import os
8
8
  import pkgutil
9
9
  import queue
10
10
  import sys
11
- from multiprocessing import Process, Queue
11
+ from multiprocessing import Queue, get_context
12
12
  from types import ModuleType
13
13
 
14
14
 
@@ -133,9 +133,15 @@ class ModuleInspect:
133
133
  self._start()
134
134
 
135
135
  def _start(self) -> None:
136
- self.tasks: Queue[str] = Queue()
137
- self.results: Queue[ModuleProperties | str] = Queue()
138
- self.proc = Process(target=worker, args=(self.tasks, self.results, sys.path))
136
+ if sys.platform == "linux":
137
+ ctx = get_context("forkserver")
138
+ else:
139
+ ctx = get_context("spawn")
140
+ self.tasks: Queue[str] = ctx.Queue()
141
+ self.results: Queue[ModuleProperties | str] = ctx.Queue()
142
+ self.proc = ctx.Process(
143
+ target=worker, args=(self.tasks, self.results, sys.path)
144
+ )
139
145
  self.proc.start()
140
146
  self.counter = 0 # Number of successful roundtrips
141
147
 
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561. The mypy package uses inline types.
@@ -811,7 +811,7 @@ class SemanticAnalyzer(
811
811
  self.globals = file_node.names
812
812
  self.tvar_scope = TypeVarLikeScope()
813
813
 
814
- self.named_tuple_analyzer = NamedTupleAnalyzer(options, self)
814
+ self.named_tuple_analyzer = NamedTupleAnalyzer(options, self, self.msg)
815
815
  self.typed_dict_analyzer = TypedDictAnalyzer(options, self, self.msg)
816
816
  self.enum_call_analyzer = EnumCallAnalyzer(options, self)
817
817
  self.newtype_analyzer = NewTypeAnalyzer(options, self, self.msg)
@@ -3035,22 +3035,23 @@ class SemanticAnalyzer(
3035
3035
  if self.check_and_set_up_type_alias(s):
3036
3036
  s.is_alias_def = True
3037
3037
  special_form = True
3038
- # * type variable definition
3039
- elif self.process_typevar_declaration(s):
3040
- special_form = True
3041
- elif self.process_paramspec_declaration(s):
3042
- special_form = True
3043
- elif self.process_typevartuple_declaration(s):
3044
- special_form = True
3045
- # * type constructors
3046
- elif self.analyze_namedtuple_assign(s):
3047
- special_form = True
3048
- elif self.analyze_typeddict_assign(s):
3049
- special_form = True
3050
- elif self.newtype_analyzer.process_newtype_declaration(s):
3051
- special_form = True
3052
- elif self.analyze_enum_assign(s):
3053
- special_form = True
3038
+ elif isinstance(s.rvalue, CallExpr):
3039
+ # * type variable definition
3040
+ if self.process_typevar_declaration(s):
3041
+ special_form = True
3042
+ elif self.process_paramspec_declaration(s):
3043
+ special_form = True
3044
+ elif self.process_typevartuple_declaration(s):
3045
+ special_form = True
3046
+ # * type constructors
3047
+ elif self.analyze_namedtuple_assign(s):
3048
+ special_form = True
3049
+ elif self.analyze_typeddict_assign(s):
3050
+ special_form = True
3051
+ elif self.newtype_analyzer.process_newtype_declaration(s):
3052
+ special_form = True
3053
+ elif self.analyze_enum_assign(s):
3054
+ special_form = True
3054
3055
 
3055
3056
  if special_form:
3056
3057
  self.record_special_form_lvalue(s)
@@ -106,16 +106,19 @@ class EnumCallAnalyzer:
106
106
  items, values, ok = self.parse_enum_call_args(call, fullname.split(".")[-1])
107
107
  if not ok:
108
108
  # Error. Construct dummy return value.
109
- info = self.build_enum_call_typeinfo(var_name, [], fullname, node.line)
109
+ name = var_name
110
+ if is_func_scope:
111
+ name += "@" + str(call.line)
112
+ info = self.build_enum_call_typeinfo(name, [], fullname, node.line)
110
113
  else:
111
114
  name = cast(StrExpr, call.args[0]).value
112
115
  if name != var_name or is_func_scope:
113
116
  # Give it a unique name derived from the line number.
114
117
  name += "@" + str(call.line)
115
118
  info = self.build_enum_call_typeinfo(name, items, fullname, call.line)
116
- # Store generated TypeInfo under both names, see semanal_namedtuple for more details.
117
- if name != var_name or is_func_scope:
118
- self.api.add_symbol_skip_local(name, info)
119
+ # Store generated TypeInfo under both names, see semanal_namedtuple for more details.
120
+ if name != var_name or is_func_scope:
121
+ self.api.add_symbol_skip_local(name, info)
119
122
  call.analyzed = EnumCallExpr(info, items, values)
120
123
  call.analyzed.set_line(call)
121
124
  info.line = node.line
@@ -9,6 +9,7 @@ from contextlib import contextmanager
9
9
  from typing import Final, Iterator, List, Mapping, cast
10
10
 
11
11
  from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
12
+ from mypy.messages import MessageBuilder
12
13
  from mypy.nodes import (
13
14
  ARG_NAMED_OPT,
14
15
  ARG_OPT,
@@ -92,9 +93,12 @@ SELF_TVAR_NAME: Final = "_NT"
92
93
 
93
94
 
94
95
  class NamedTupleAnalyzer:
95
- def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None:
96
+ def __init__(
97
+ self, options: Options, api: SemanticAnalyzerInterface, msg: MessageBuilder
98
+ ) -> None:
96
99
  self.options = options
97
100
  self.api = api
101
+ self.msg = msg
98
102
 
99
103
  def analyze_namedtuple_classdef(
100
104
  self, defn: ClassDef, is_stub_file: bool, is_func_scope: bool
@@ -212,6 +216,12 @@ class NamedTupleAnalyzer:
212
216
  )
213
217
  else:
214
218
  default_items[name] = stmt.rvalue
219
+ if defn.keywords:
220
+ for_function = ' for "__init_subclass__" of "NamedTuple"'
221
+ for key in defn.keywords:
222
+ self.msg.unexpected_keyword_argument_for_function(
223
+ for_function, key, defn
224
+ )
215
225
  return items, types, default_items, statements
216
226
 
217
227
  def check_namedtuple(