jaclang 0.7.11__py3-none-any.whl → 0.7.13__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.
- jaclang/cli/cli.py +10 -2
- jaclang/compiler/absyntree.py +10 -2
- jaclang/compiler/parser.py +2 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +238 -32
- jaclang/compiler/passes/main/pyast_load_pass.py +3 -1
- jaclang/compiler/passes/main/type_check_pass.py +0 -17
- jaclang/compiler/tests/test_importer.py +1 -1
- jaclang/core/importer.py +126 -89
- jaclang/langserve/engine.py +177 -165
- jaclang/langserve/server.py +19 -7
- jaclang/langserve/tests/fixtures/base_module_structure.jac +28 -2
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/test_server.py +53 -64
- jaclang/langserve/utils.py +266 -0
- jaclang/plugin/default.py +2 -2
- jaclang/plugin/feature.py +2 -2
- jaclang/plugin/spec.py +2 -2
- jaclang/tests/fixtures/deep/one_lev.jac +3 -0
- jaclang/tests/fixtures/needs_import.jac +1 -1
- jaclang/tests/test_cli.py +6 -6
- {jaclang-0.7.11.dist-info → jaclang-0.7.13.dist-info}/METADATA +1 -1
- {jaclang-0.7.11.dist-info → jaclang-0.7.13.dist-info}/RECORD +24 -24
- {jaclang-0.7.11.dist-info → jaclang-0.7.13.dist-info}/WHEEL +0 -0
- {jaclang-0.7.11.dist-info → jaclang-0.7.13.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.py
CHANGED
|
@@ -92,22 +92,30 @@ def run(
|
|
|
92
92
|
base = base if base else "./"
|
|
93
93
|
mod = mod[:-4]
|
|
94
94
|
if filename.endswith(".jac"):
|
|
95
|
-
|
|
95
|
+
ret_module = jac_import(
|
|
96
96
|
target=mod,
|
|
97
97
|
base_path=base,
|
|
98
98
|
cachable=cache,
|
|
99
99
|
override_name="__main__" if main else None,
|
|
100
100
|
)
|
|
101
|
+
if ret_module is None:
|
|
102
|
+
loaded_mod = None
|
|
103
|
+
else:
|
|
104
|
+
(loaded_mod,) = ret_module
|
|
101
105
|
elif filename.endswith(".jir"):
|
|
102
106
|
with open(filename, "rb") as f:
|
|
103
107
|
ir = pickle.load(f)
|
|
104
|
-
|
|
108
|
+
ret_module = jac_import(
|
|
105
109
|
target=mod,
|
|
106
110
|
base_path=base,
|
|
107
111
|
cachable=cache,
|
|
108
112
|
override_name="__main__" if main else None,
|
|
109
113
|
mod_bundle=ir,
|
|
110
114
|
)
|
|
115
|
+
if ret_module is None:
|
|
116
|
+
loaded_mod = None
|
|
117
|
+
else:
|
|
118
|
+
(loaded_mod,) = ret_module
|
|
111
119
|
else:
|
|
112
120
|
print("Not a .jac file.")
|
|
113
121
|
return
|
jaclang/compiler/absyntree.py
CHANGED
|
@@ -916,9 +916,17 @@ class ModulePath(AstSymbolNode):
|
|
|
916
916
|
self.sub_module: Optional[Module] = None
|
|
917
917
|
|
|
918
918
|
name_spec = alias if alias else path[0] if path else None
|
|
919
|
+
|
|
920
|
+
AstNode.__init__(self, kid=kid)
|
|
921
|
+
if not name_spec:
|
|
922
|
+
pkg_name = self.loc.mod_path
|
|
923
|
+
for _ in range(self.level):
|
|
924
|
+
pkg_name = os.path.dirname(pkg_name)
|
|
925
|
+
pkg_name = pkg_name.split(os.sep)[-1]
|
|
926
|
+
name_spec = Name.gen_stub_from_node(self, pkg_name)
|
|
927
|
+
self.level += 1
|
|
919
928
|
if not isinstance(name_spec, Name):
|
|
920
929
|
raise ValueError("ModulePath should have a name spec. Impossible.")
|
|
921
|
-
AstNode.__init__(self, kid=kid)
|
|
922
930
|
AstSymbolNode.__init__(
|
|
923
931
|
self,
|
|
924
932
|
sym_name=name_spec.sym_name,
|
|
@@ -930,7 +938,7 @@ class ModulePath(AstSymbolNode):
|
|
|
930
938
|
def path_str(self) -> str:
|
|
931
939
|
"""Get path string."""
|
|
932
940
|
return ("." * self.level) + ".".join(
|
|
933
|
-
[p.value for p in self.path] if self.path else
|
|
941
|
+
[p.value for p in self.path] if self.path else [self.name_spec.sym_name]
|
|
934
942
|
)
|
|
935
943
|
|
|
936
944
|
def normalize(self, deep: bool = False) -> bool:
|
jaclang/compiler/parser.py
CHANGED
|
@@ -131,7 +131,8 @@ class JacParser(Pass):
|
|
|
131
131
|
def nu(self, node: ast.T) -> ast.T:
|
|
132
132
|
"""Update node."""
|
|
133
133
|
self.parse_ref.cur_node = node
|
|
134
|
-
self.parse_ref.node_list
|
|
134
|
+
if node not in self.parse_ref.node_list:
|
|
135
|
+
self.parse_ref.node_list.append(node)
|
|
135
136
|
return node
|
|
136
137
|
|
|
137
138
|
def start(self, kid: list[ast.Module]) -> ast.Module:
|
|
@@ -50,7 +50,15 @@ class PyastGenPass(Pass):
|
|
|
50
50
|
level=0,
|
|
51
51
|
),
|
|
52
52
|
jac_node=self.ir,
|
|
53
|
-
)
|
|
53
|
+
),
|
|
54
|
+
self.sync(
|
|
55
|
+
ast3.ImportFrom(
|
|
56
|
+
module="typing",
|
|
57
|
+
names=[self.sync(ast3.alias(name="TYPE_CHECKING", asname=None))],
|
|
58
|
+
level=0,
|
|
59
|
+
),
|
|
60
|
+
jac_node=self.ir,
|
|
61
|
+
),
|
|
54
62
|
]
|
|
55
63
|
|
|
56
64
|
def enter_node(self, node: ast.AstNode) -> None:
|
|
@@ -480,7 +488,7 @@ class PyastGenPass(Pass):
|
|
|
480
488
|
def exit_import(self, node: ast.Import) -> None:
|
|
481
489
|
"""Sub objects.
|
|
482
490
|
|
|
483
|
-
|
|
491
|
+
hint: SubTag[Name],
|
|
484
492
|
paths: list[ModulePath],
|
|
485
493
|
alias: Optional[Name],
|
|
486
494
|
items: Optional[SubNodeList[ModuleItem]],
|
|
@@ -488,12 +496,6 @@ class PyastGenPass(Pass):
|
|
|
488
496
|
doc: Optional[String],
|
|
489
497
|
sub_module: Optional[Module],
|
|
490
498
|
"""
|
|
491
|
-
py_nodes: list[ast3.AST] = []
|
|
492
|
-
|
|
493
|
-
if node.doc:
|
|
494
|
-
py_nodes.append(
|
|
495
|
-
self.sync(ast3.Expr(value=node.doc.gen.py_ast[0]), jac_node=node.doc)
|
|
496
|
-
)
|
|
497
499
|
path_alias: dict[str, Optional[str]] = (
|
|
498
500
|
{node.from_loc.path_str: None} if node.from_loc else {}
|
|
499
501
|
)
|
|
@@ -502,25 +504,75 @@ class PyastGenPass(Pass):
|
|
|
502
504
|
for item in node.items.items:
|
|
503
505
|
if isinstance(item, ast.ModuleItem):
|
|
504
506
|
imp_from[item.name.sym_name] = (
|
|
505
|
-
item.alias.sym_name if item.alias else
|
|
507
|
+
item.alias.sym_name if item.alias else None
|
|
506
508
|
)
|
|
507
509
|
elif isinstance(item, ast.ModulePath):
|
|
508
510
|
path_alias[item.path_str] = (
|
|
509
511
|
item.alias.sym_name if item.alias else None
|
|
510
512
|
)
|
|
511
513
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
for k in imp_from.
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
values.append(self.sync(ast3.Constant(value=v)))
|
|
518
|
-
|
|
514
|
+
item_keys = []
|
|
515
|
+
item_values = []
|
|
516
|
+
for k, v in imp_from.items():
|
|
517
|
+
item_keys.append(self.sync(ast3.Constant(value=k)))
|
|
518
|
+
item_values.append(self.sync(ast3.Constant(value=v)))
|
|
519
519
|
self.needs_jac_import()
|
|
520
|
-
|
|
520
|
+
path_named_value: str
|
|
521
|
+
py_nodes: list[ast3.AST] = []
|
|
522
|
+
typecheck_nodes: list[ast3.AST] = []
|
|
523
|
+
runtime_nodes: list[ast3.AST] = []
|
|
524
|
+
|
|
525
|
+
if node.doc:
|
|
521
526
|
py_nodes.append(
|
|
527
|
+
self.sync(ast3.Expr(value=node.doc.gen.py_ast[0]), jac_node=node.doc)
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
for path, alias in path_alias.items():
|
|
531
|
+
path_named_value = ("_jac_inc_" if node.is_absorb else "") + (
|
|
532
|
+
alias if alias else path
|
|
533
|
+
).lstrip(".").split(".")[0]
|
|
534
|
+
# target_named_value = ""
|
|
535
|
+
# for i in path.split("."):
|
|
536
|
+
# target_named_value += i if i else "."
|
|
537
|
+
# if i:
|
|
538
|
+
# break
|
|
539
|
+
runtime_nodes.append(
|
|
522
540
|
self.sync(
|
|
523
|
-
ast3.
|
|
541
|
+
ast3.Assign(
|
|
542
|
+
targets=(
|
|
543
|
+
[
|
|
544
|
+
self.sync(
|
|
545
|
+
ast3.Tuple(
|
|
546
|
+
elts=(
|
|
547
|
+
[
|
|
548
|
+
self.sync(
|
|
549
|
+
ast3.Name(
|
|
550
|
+
id=path_named_value,
|
|
551
|
+
ctx=ast3.Store(),
|
|
552
|
+
)
|
|
553
|
+
)
|
|
554
|
+
]
|
|
555
|
+
if not len(item_keys)
|
|
556
|
+
else []
|
|
557
|
+
+ [
|
|
558
|
+
self.sync(
|
|
559
|
+
ast3.Name(
|
|
560
|
+
id=(
|
|
561
|
+
v.value
|
|
562
|
+
if v.value
|
|
563
|
+
else k.value
|
|
564
|
+
),
|
|
565
|
+
ctx=ast3.Store(),
|
|
566
|
+
)
|
|
567
|
+
)
|
|
568
|
+
for k, v in zip(item_keys, item_values)
|
|
569
|
+
]
|
|
570
|
+
),
|
|
571
|
+
ctx=ast3.Store(),
|
|
572
|
+
)
|
|
573
|
+
)
|
|
574
|
+
]
|
|
575
|
+
),
|
|
524
576
|
value=self.sync(
|
|
525
577
|
ast3.Call(
|
|
526
578
|
func=self.sync(
|
|
@@ -532,7 +584,7 @@ class PyastGenPass(Pass):
|
|
|
532
584
|
ast3.keyword(
|
|
533
585
|
arg="target",
|
|
534
586
|
value=self.sync(
|
|
535
|
-
ast3.Constant(value=
|
|
587
|
+
ast3.Constant(value=path),
|
|
536
588
|
),
|
|
537
589
|
)
|
|
538
590
|
),
|
|
@@ -541,7 +593,8 @@ class PyastGenPass(Pass):
|
|
|
541
593
|
arg="base_path",
|
|
542
594
|
value=self.sync(
|
|
543
595
|
ast3.Name(
|
|
544
|
-
id="__file__",
|
|
596
|
+
id="__file__",
|
|
597
|
+
ctx=ast3.Load(),
|
|
545
598
|
)
|
|
546
599
|
),
|
|
547
600
|
)
|
|
@@ -580,7 +633,7 @@ class PyastGenPass(Pass):
|
|
|
580
633
|
ast3.keyword(
|
|
581
634
|
arg="mdl_alias",
|
|
582
635
|
value=self.sync(
|
|
583
|
-
ast3.Constant(value=
|
|
636
|
+
ast3.Constant(value=alias),
|
|
584
637
|
),
|
|
585
638
|
)
|
|
586
639
|
),
|
|
@@ -588,21 +641,170 @@ class PyastGenPass(Pass):
|
|
|
588
641
|
ast3.keyword(
|
|
589
642
|
arg="items",
|
|
590
643
|
value=self.sync(
|
|
591
|
-
ast3.Dict(
|
|
644
|
+
ast3.Dict(
|
|
645
|
+
keys=item_keys, values=item_values
|
|
646
|
+
),
|
|
592
647
|
),
|
|
593
648
|
)
|
|
594
649
|
),
|
|
595
650
|
],
|
|
596
651
|
)
|
|
597
|
-
)
|
|
652
|
+
),
|
|
598
653
|
),
|
|
654
|
+
),
|
|
655
|
+
)
|
|
656
|
+
if node.is_absorb:
|
|
657
|
+
absorb_exec = f"={path_named_value}.__dict__['"
|
|
658
|
+
runtime_nodes.append(
|
|
659
|
+
self.sync(
|
|
660
|
+
ast3.For(
|
|
661
|
+
target=self.sync(ast3.Name(id="i", ctx=ast3.Store())),
|
|
662
|
+
iter=self.sync(
|
|
663
|
+
ast3.IfExp(
|
|
664
|
+
test=self.sync(
|
|
665
|
+
ast3.Compare(
|
|
666
|
+
left=self.sync(ast3.Constant(value="__all__")),
|
|
667
|
+
ops=[self.sync(ast3.In())],
|
|
668
|
+
comparators=[
|
|
669
|
+
self.sync(
|
|
670
|
+
ast3.Attribute(
|
|
671
|
+
value=self.sync(
|
|
672
|
+
ast3.Name(
|
|
673
|
+
id=path_named_value,
|
|
674
|
+
ctx=ast3.Load(),
|
|
675
|
+
)
|
|
676
|
+
),
|
|
677
|
+
attr="__dict__",
|
|
678
|
+
ctx=ast3.Load(),
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
],
|
|
682
|
+
)
|
|
683
|
+
),
|
|
684
|
+
body=self.sync(
|
|
685
|
+
ast3.Attribute(
|
|
686
|
+
value=self.sync(
|
|
687
|
+
ast3.Name(
|
|
688
|
+
id=path_named_value, ctx=ast3.Load()
|
|
689
|
+
)
|
|
690
|
+
),
|
|
691
|
+
attr="__all__",
|
|
692
|
+
ctx=ast3.Load(),
|
|
693
|
+
)
|
|
694
|
+
),
|
|
695
|
+
orelse=self.sync(
|
|
696
|
+
ast3.Attribute(
|
|
697
|
+
value=self.sync(
|
|
698
|
+
ast3.Name(
|
|
699
|
+
id=path_named_value, ctx=ast3.Load()
|
|
700
|
+
)
|
|
701
|
+
),
|
|
702
|
+
attr="__dict__",
|
|
703
|
+
ctx=ast3.Load(),
|
|
704
|
+
)
|
|
705
|
+
),
|
|
706
|
+
)
|
|
707
|
+
),
|
|
708
|
+
body=[
|
|
709
|
+
self.sync(
|
|
710
|
+
ast3.If(
|
|
711
|
+
test=self.sync(
|
|
712
|
+
ast3.UnaryOp(
|
|
713
|
+
op=self.sync(ast3.Not()),
|
|
714
|
+
operand=self.sync(
|
|
715
|
+
ast3.Call(
|
|
716
|
+
func=self.sync(
|
|
717
|
+
ast3.Attribute(
|
|
718
|
+
value=self.sync(
|
|
719
|
+
ast3.Name(
|
|
720
|
+
id="i",
|
|
721
|
+
ctx=ast3.Load(),
|
|
722
|
+
)
|
|
723
|
+
),
|
|
724
|
+
attr="startswith",
|
|
725
|
+
ctx=ast3.Load(),
|
|
726
|
+
)
|
|
727
|
+
),
|
|
728
|
+
args=[
|
|
729
|
+
self.sync(
|
|
730
|
+
ast3.Constant(value="_")
|
|
731
|
+
)
|
|
732
|
+
],
|
|
733
|
+
keywords=[],
|
|
734
|
+
)
|
|
735
|
+
),
|
|
736
|
+
)
|
|
737
|
+
),
|
|
738
|
+
body=[
|
|
739
|
+
self.sync(
|
|
740
|
+
ast3.Expr(
|
|
741
|
+
value=self.sync(
|
|
742
|
+
ast3.Call(
|
|
743
|
+
func=self.sync(
|
|
744
|
+
ast3.Name(
|
|
745
|
+
id="exec",
|
|
746
|
+
ctx=ast3.Load(),
|
|
747
|
+
)
|
|
748
|
+
),
|
|
749
|
+
args=[
|
|
750
|
+
self.sync(
|
|
751
|
+
ast3.JoinedStr(
|
|
752
|
+
values=[
|
|
753
|
+
self.sync(
|
|
754
|
+
ast3.FormattedValue(
|
|
755
|
+
value=self.sync(
|
|
756
|
+
ast3.Name(
|
|
757
|
+
id="i",
|
|
758
|
+
ctx=ast3.Load(),
|
|
759
|
+
)
|
|
760
|
+
),
|
|
761
|
+
conversion=-1,
|
|
762
|
+
)
|
|
763
|
+
),
|
|
764
|
+
self.sync(
|
|
765
|
+
ast3.Constant(
|
|
766
|
+
value=absorb_exec
|
|
767
|
+
)
|
|
768
|
+
),
|
|
769
|
+
self.sync(
|
|
770
|
+
ast3.FormattedValue(
|
|
771
|
+
value=self.sync(
|
|
772
|
+
ast3.Name(
|
|
773
|
+
id="i",
|
|
774
|
+
ctx=ast3.Load(),
|
|
775
|
+
)
|
|
776
|
+
),
|
|
777
|
+
conversion=-1,
|
|
778
|
+
)
|
|
779
|
+
),
|
|
780
|
+
self.sync(
|
|
781
|
+
ast3.Constant(
|
|
782
|
+
value="']"
|
|
783
|
+
)
|
|
784
|
+
),
|
|
785
|
+
]
|
|
786
|
+
)
|
|
787
|
+
)
|
|
788
|
+
],
|
|
789
|
+
keywords=[],
|
|
790
|
+
)
|
|
791
|
+
)
|
|
792
|
+
)
|
|
793
|
+
)
|
|
794
|
+
],
|
|
795
|
+
orelse=[],
|
|
796
|
+
)
|
|
797
|
+
)
|
|
798
|
+
],
|
|
799
|
+
orelse=[],
|
|
800
|
+
)
|
|
599
801
|
)
|
|
600
802
|
)
|
|
601
803
|
if node.is_absorb:
|
|
602
804
|
source = node.items.items[0]
|
|
603
805
|
if not isinstance(source, ast.ModulePath):
|
|
604
806
|
raise self.ice()
|
|
605
|
-
|
|
807
|
+
typecheck_nodes.append(
|
|
606
808
|
self.sync(
|
|
607
809
|
py_node=ast3.ImportFrom(
|
|
608
810
|
module=(source.path_str.lstrip(".") if source else None),
|
|
@@ -612,15 +814,10 @@ class PyastGenPass(Pass):
|
|
|
612
814
|
jac_node=node,
|
|
613
815
|
)
|
|
614
816
|
)
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
# self.warning(
|
|
618
|
-
# "Includes import * in target module into current namespace."
|
|
619
|
-
# )
|
|
620
|
-
if not node.from_loc:
|
|
621
|
-
py_nodes.append(self.sync(ast3.Import(names=node.items.gen.py_ast)))
|
|
817
|
+
elif not node.from_loc:
|
|
818
|
+
typecheck_nodes.append(self.sync(ast3.Import(names=node.items.gen.py_ast)))
|
|
622
819
|
else:
|
|
623
|
-
|
|
820
|
+
typecheck_nodes.append(
|
|
624
821
|
self.sync(
|
|
625
822
|
ast3.ImportFrom(
|
|
626
823
|
module=(
|
|
@@ -633,6 +830,15 @@ class PyastGenPass(Pass):
|
|
|
633
830
|
)
|
|
634
831
|
)
|
|
635
832
|
)
|
|
833
|
+
py_nodes.append(
|
|
834
|
+
self.sync(
|
|
835
|
+
ast3.If(
|
|
836
|
+
test=self.sync(ast3.Name(id="TYPE_CHECKING", ctx=ast3.Load())),
|
|
837
|
+
body=typecheck_nodes,
|
|
838
|
+
orelse=runtime_nodes,
|
|
839
|
+
)
|
|
840
|
+
)
|
|
841
|
+
)
|
|
636
842
|
node.gen.py_ast = py_nodes
|
|
637
843
|
|
|
638
844
|
def exit_module_path(self, node: ast.ModulePath) -> None:
|
|
@@ -1507,11 +1507,13 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
|
|
|
1507
1507
|
pos_end=0,
|
|
1508
1508
|
)
|
|
1509
1509
|
)
|
|
1510
|
+
moddots = [self.operator(Tok.DOT, ".") for _ in range(node.level)]
|
|
1511
|
+
modparts = moddots + modpaths
|
|
1510
1512
|
path = ast.ModulePath(
|
|
1511
1513
|
path=modpaths,
|
|
1512
1514
|
level=node.level,
|
|
1513
1515
|
alias=None,
|
|
1514
|
-
kid=
|
|
1516
|
+
kid=modparts,
|
|
1515
1517
|
)
|
|
1516
1518
|
names = [self.convert(name) for name in node.names]
|
|
1517
1519
|
valid_names = []
|
|
@@ -94,29 +94,12 @@ class JacTypeCheckPass(Pass):
|
|
|
94
94
|
mypy_graph[module.name] = st
|
|
95
95
|
new_modules.append(st)
|
|
96
96
|
|
|
97
|
-
# def get_stub(mod: str) -> myab.BuildSource:
|
|
98
|
-
# """Get stub file path."""
|
|
99
|
-
# return myab.BuildSource(
|
|
100
|
-
# path=str(
|
|
101
|
-
# pathlib.Path(os.path.dirname(jaclang.__file__)).parent
|
|
102
|
-
# / "stubs"
|
|
103
|
-
# / "jaclang"
|
|
104
|
-
# / "plugin"
|
|
105
|
-
# / f"{mod}.pyi"
|
|
106
|
-
# ),
|
|
107
|
-
# module=f"jaclang.plugin.{mod}",
|
|
108
|
-
# )
|
|
109
|
-
|
|
110
97
|
graph = myab.load_graph(
|
|
111
98
|
[
|
|
112
99
|
myab.BuildSource(
|
|
113
100
|
path=str(self.__path / "typeshed" / "stdlib" / "builtins.pyi"),
|
|
114
101
|
module="builtins",
|
|
115
102
|
),
|
|
116
|
-
# get_stub("default"),
|
|
117
|
-
# get_stub("feature"),
|
|
118
|
-
# get_stub("spec"),
|
|
119
|
-
# get_stub("builtin"),
|
|
120
103
|
],
|
|
121
104
|
manager,
|
|
122
105
|
old_graph=mypy_graph,
|
|
@@ -17,7 +17,7 @@ class TestLoader(TestCase):
|
|
|
17
17
|
|
|
18
18
|
def test_import_basic_python(self) -> None:
|
|
19
19
|
"""Test basic self loading."""
|
|
20
|
-
h = jac_import("fixtures.hello_world", base_path=__file__)
|
|
20
|
+
(h,) = jac_import("fixtures.hello_world", base_path=__file__)
|
|
21
21
|
self.assertEqual(h.hello(), "Hello World!") # type: ignore
|
|
22
22
|
|
|
23
23
|
def test_modules_correct(self) -> None:
|