rapydscript-ns 0.9.2 → 0.9.3
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.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +19 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/PYTHON_GAPS.md +420 -0
- package/README.md +153 -29
- package/TODO.md +16 -118
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +237 -8
- package/memory/project_string_impl.md +43 -0
- package/package.json +1 -1
- package/publish.py +37 -37
- package/release/baselib-plain-pretty.js +248 -38
- package/release/baselib-plain-ugly.js +8 -8
- package/release/compiler.js +778 -277
- package/release/signatures.json +30 -30
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +4 -1
- package/src/baselib-builtins.pyj +56 -2
- package/src/baselib-containers.pyj +2 -0
- package/src/baselib-errors.pyj +7 -3
- package/src/baselib-internal.pyj +51 -6
- package/src/baselib-str.pyj +5 -3
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/asyncio.pyj +534 -0
- package/src/lib/base64.pyj +399 -0
- package/src/lib/bisect.pyj +73 -0
- package/src/lib/collections.pyj +1 -1
- package/src/lib/copy.pyj +120 -120
- package/src/lib/csv.pyj +494 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/heapq.pyj +98 -0
- package/src/lib/html.pyj +382 -0
- package/src/lib/http/__init__.pyj +98 -0
- package/src/lib/http/client.pyj +304 -0
- package/src/lib/http/cookies.pyj +236 -0
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/logging.pyj +672 -0
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/react.pyj +74 -74
- package/src/lib/string.pyj +357 -0
- package/src/lib/textwrap.pyj +329 -0
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/urllib/__init__.pyj +14 -0
- package/src/lib/urllib/error.pyj +66 -0
- package/src/lib/urllib/parse.pyj +475 -0
- package/src/lib/urllib/request.pyj +86 -0
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/analyzer.js +5 -2
- package/src/monaco-language-service/completions.js +26 -0
- package/src/monaco-language-service/diagnostics.js +202 -3
- package/src/monaco-language-service/dts.js +550 -550
- package/src/monaco-language-service/scope.js +1 -0
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/functions.pyj +152 -6
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +17 -2
- package/src/output/modules.pyj +1 -1
- package/src/output/operators.pyj +15 -0
- package/src/output/stream.pyj +0 -1
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +80 -17
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +1 -1
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/async_generators.pyj +144 -0
- package/test/asyncio.pyj +307 -0
- package/test/base64.pyj +202 -0
- package/test/bisect.pyj +178 -0
- package/test/csv.pyj +405 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/float_special.pyj +64 -0
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/heapq.pyj +174 -0
- package/test/html.pyj +212 -0
- package/test/http.pyj +259 -0
- package/test/imports.pyj +79 -72
- package/test/internationalization.pyj +73 -73
- package/test/lint.pyj +164 -164
- package/test/logging.pyj +356 -0
- package/test/long.pyj +130 -0
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/parenthesized_with.pyj +141 -0
- package/test/python_compat.pyj +3 -5
- package/test/python_modulo.pyj +76 -0
- package/test/python_modulo_off.pyj +21 -0
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/str.pyj +14 -0
- package/test/string.pyj +245 -0
- package/test/textwrap.pyj +172 -0
- package/test/type_display.pyj +48 -0
- package/test/type_enforcement.pyj +164 -0
- package/test/unit/index.js +14 -6
- package/test/unit/language-service-completions.js +119 -0
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service-scope.js +32 -0
- package/test/unit/language-service.js +127 -3
- package/test/unit/run-language-service.js +17 -3
- package/test/unit/web-repl.js +2094 -29
- package/test/urllib.pyj +193 -0
- package/tools/compile.js +1 -1
- package/tools/compiler.d.ts +367 -367
- package/tools/completer.js +131 -131
- package/tools/embedded_compiler.js +7 -7
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/main.js +1 -1
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
- package/test/omit_function_metadata.pyj +0 -20
package/src/output/treeshake.pyj
CHANGED
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: BSD
|
|
3
|
-
from __python__ import hash_literals
|
|
4
|
-
|
|
5
|
-
from ast import (
|
|
6
|
-
AST_Function, AST_Class, AST_SimpleStatement, AST_Assign,
|
|
7
|
-
AST_SymbolRef, AST_Dot, AST_Sub, AST_Imports, TreeWalker, is_node_type
|
|
8
|
-
)
|
|
9
|
-
from utils import has_prop
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def get_top_level_name(stmt):
|
|
13
|
-
if is_node_type(stmt, AST_Function) or is_node_type(stmt, AST_Class):
|
|
14
|
-
if stmt.name:
|
|
15
|
-
return stmt.name.name
|
|
16
|
-
return None
|
|
17
|
-
if is_node_type(stmt, AST_SimpleStatement):
|
|
18
|
-
body = stmt.body
|
|
19
|
-
if is_node_type(body, AST_Assign):
|
|
20
|
-
lhs = body.left
|
|
21
|
-
if is_node_type(lhs, AST_SymbolRef):
|
|
22
|
-
return lhs.name
|
|
23
|
-
return None
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def collect_refs_in_node(stmt, top_level_set, refs):
|
|
27
|
-
def visit_fn(node, descend):
|
|
28
|
-
if is_node_type(node, AST_SymbolRef):
|
|
29
|
-
if has_prop(top_level_set, node.name):
|
|
30
|
-
refs[node.name] = True
|
|
31
|
-
stmt.walk(TreeWalker(visit_fn))
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def compute_transitive_closure(body, direct_names, nonlocalvars):
|
|
35
|
-
nonlocal_set = {}
|
|
36
|
-
if nonlocalvars:
|
|
37
|
-
for nv in nonlocalvars:
|
|
38
|
-
nonlocal_set[nv] = True
|
|
39
|
-
|
|
40
|
-
name_map = {}
|
|
41
|
-
unnamed_stmts = []
|
|
42
|
-
for stmt in body:
|
|
43
|
-
name = get_top_level_name(stmt)
|
|
44
|
-
if name is not None:
|
|
45
|
-
name_map[name] = stmt
|
|
46
|
-
else:
|
|
47
|
-
unnamed_stmts.push(stmt)
|
|
48
|
-
|
|
49
|
-
top_level_set = {}
|
|
50
|
-
for name in Object.keys(name_map):
|
|
51
|
-
top_level_set[name] = True
|
|
52
|
-
|
|
53
|
-
needed = {}
|
|
54
|
-
queue = []
|
|
55
|
-
|
|
56
|
-
# Always include top-level assignments to nonlocal vars — they affect the
|
|
57
|
-
# global JavaScript scope and must never be filtered out.
|
|
58
|
-
for name in Object.keys(name_map):
|
|
59
|
-
if has_prop(nonlocal_set, name):
|
|
60
|
-
needed[name] = True
|
|
61
|
-
queue.push(name)
|
|
62
|
-
|
|
63
|
-
# Add directly imported names
|
|
64
|
-
for name in Object.keys(direct_names):
|
|
65
|
-
if not has_prop(needed, name):
|
|
66
|
-
needed[name] = True
|
|
67
|
-
queue.push(name)
|
|
68
|
-
|
|
69
|
-
# Unnamed statements (imports, if-blocks, bare expressions) are always
|
|
70
|
-
# included in output, so their references to named top-level items must
|
|
71
|
-
# also be included transitively.
|
|
72
|
-
always_refs = {}
|
|
73
|
-
for stmt in unnamed_stmts:
|
|
74
|
-
collect_refs_in_node(stmt, top_level_set, always_refs)
|
|
75
|
-
for ref_name in Object.keys(always_refs):
|
|
76
|
-
if not has_prop(needed, ref_name):
|
|
77
|
-
needed[ref_name] = True
|
|
78
|
-
queue.push(ref_name)
|
|
79
|
-
|
|
80
|
-
while queue.length > 0:
|
|
81
|
-
current = queue.shift()
|
|
82
|
-
if not has_prop(name_map, current):
|
|
83
|
-
continue
|
|
84
|
-
refs = {}
|
|
85
|
-
collect_refs_in_node(name_map[current], top_level_set, refs)
|
|
86
|
-
for ref_name in Object.keys(refs):
|
|
87
|
-
if not has_prop(needed, ref_name):
|
|
88
|
-
needed[ref_name] = True
|
|
89
|
-
queue.push(ref_name)
|
|
90
|
-
|
|
91
|
-
return needed
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def check_module_attr_access(main_body, info, alias_set):
|
|
95
|
-
def visit_fn(node, descend):
|
|
96
|
-
if is_node_type(node, AST_Dot):
|
|
97
|
-
expr = node.expression
|
|
98
|
-
if is_node_type(expr, AST_SymbolRef) and has_prop(alias_set, expr.name):
|
|
99
|
-
info.direct_names[node.property] = True
|
|
100
|
-
return True
|
|
101
|
-
if is_node_type(node, AST_Sub):
|
|
102
|
-
expr = node.expression
|
|
103
|
-
if is_node_type(expr, AST_SymbolRef) and has_prop(alias_set, expr.name):
|
|
104
|
-
info.can_tree_shake = False
|
|
105
|
-
for stmt in main_body:
|
|
106
|
-
stmt.walk(TreeWalker(visit_fn))
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def analyze_imports(main_body):
|
|
110
|
-
result = {}
|
|
111
|
-
|
|
112
|
-
# First pass: walk the entire AST recursively to collect all from-imports
|
|
113
|
-
# (including those nested inside functions or other scopes).
|
|
114
|
-
def visit_from_imports(node, descend):
|
|
115
|
-
if is_node_type(node, AST_Imports):
|
|
116
|
-
for imp in node.imports:
|
|
117
|
-
if imp.argnames:
|
|
118
|
-
key = imp.key
|
|
119
|
-
if not has_prop(result, key):
|
|
120
|
-
result[key] = {'direct_names': {}, 'can_tree_shake': True}
|
|
121
|
-
for argname in imp.argnames:
|
|
122
|
-
result[key].direct_names[argname.name] = True
|
|
123
|
-
for stmt in main_body:
|
|
124
|
-
stmt.walk(TreeWalker(visit_from_imports))
|
|
125
|
-
|
|
126
|
-
# Second pass: handle top-level plain imports (import X as Y, import X).
|
|
127
|
-
# Attribute tracking via check_module_attr_access applies to main_body scope.
|
|
128
|
-
for stmt in main_body:
|
|
129
|
-
if not is_node_type(stmt, AST_Imports):
|
|
130
|
-
continue
|
|
131
|
-
for imp in stmt.imports:
|
|
132
|
-
if imp.argnames:
|
|
133
|
-
continue # already handled in first pass
|
|
134
|
-
key = imp.key
|
|
135
|
-
if not has_prop(result, key):
|
|
136
|
-
result[key] = {'direct_names': {}, 'can_tree_shake': True}
|
|
137
|
-
info = result[key]
|
|
138
|
-
if imp.alias:
|
|
139
|
-
alias_set = {}
|
|
140
|
-
alias_set[imp.alias.name] = True
|
|
141
|
-
check_module_attr_access(main_body, info, alias_set)
|
|
142
|
-
else:
|
|
143
|
-
parts = key.split('.')
|
|
144
|
-
if parts.length > 1:
|
|
145
|
-
# import X.Y with no alias: user accesses via X.Y.attr which
|
|
146
|
-
# requires two-level dot traversal — disable tree-shaking safely
|
|
147
|
-
info.can_tree_shake = False
|
|
148
|
-
else:
|
|
149
|
-
alias_set = {}
|
|
150
|
-
alias_set[parts[0]] = True
|
|
151
|
-
check_module_attr_access(main_body, info, alias_set)
|
|
152
|
-
|
|
153
|
-
return result
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def tree_shake(ast, context):
|
|
157
|
-
import_infos = analyze_imports(ast.body)
|
|
158
|
-
for mod_key in Object.keys(import_infos):
|
|
159
|
-
info = import_infos[mod_key]
|
|
160
|
-
if not info.can_tree_shake:
|
|
161
|
-
continue
|
|
162
|
-
if not has_prop(ast.imports, mod_key):
|
|
163
|
-
continue
|
|
164
|
-
mod = ast.imports[mod_key]
|
|
165
|
-
# If body is missing (cached module), re-parse to get it
|
|
166
|
-
if not mod.body and mod.src_code:
|
|
167
|
-
parsed = context.parse(mod.src_code, {
|
|
168
|
-
'filename': mod.filename,
|
|
169
|
-
'module_id': mod_key,
|
|
170
|
-
'libdir': context.libdir,
|
|
171
|
-
'import_dirs': context.import_dirs or [],
|
|
172
|
-
'discard_asserts': context.discard_asserts,
|
|
173
|
-
'for_linting': True,
|
|
174
|
-
})
|
|
175
|
-
mod.body = parsed.body
|
|
176
|
-
mod.localvars = parsed.localvars
|
|
177
|
-
if not mod.nonlocalvars:
|
|
178
|
-
mod.nonlocalvars = parsed.nonlocalvars
|
|
179
|
-
if not mod.body:
|
|
180
|
-
continue
|
|
181
|
-
needed = compute_transitive_closure(mod.body, info.direct_names, mod.nonlocalvars)
|
|
182
|
-
mod.needed_names = needed
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD
|
|
3
|
+
from __python__ import hash_literals
|
|
4
|
+
|
|
5
|
+
from ast import (
|
|
6
|
+
AST_Function, AST_Class, AST_SimpleStatement, AST_Assign,
|
|
7
|
+
AST_SymbolRef, AST_Dot, AST_Sub, AST_Imports, TreeWalker, is_node_type
|
|
8
|
+
)
|
|
9
|
+
from utils import has_prop
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_top_level_name(stmt):
|
|
13
|
+
if is_node_type(stmt, AST_Function) or is_node_type(stmt, AST_Class):
|
|
14
|
+
if stmt.name:
|
|
15
|
+
return stmt.name.name
|
|
16
|
+
return None
|
|
17
|
+
if is_node_type(stmt, AST_SimpleStatement):
|
|
18
|
+
body = stmt.body
|
|
19
|
+
if is_node_type(body, AST_Assign):
|
|
20
|
+
lhs = body.left
|
|
21
|
+
if is_node_type(lhs, AST_SymbolRef):
|
|
22
|
+
return lhs.name
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def collect_refs_in_node(stmt, top_level_set, refs):
|
|
27
|
+
def visit_fn(node, descend):
|
|
28
|
+
if is_node_type(node, AST_SymbolRef):
|
|
29
|
+
if has_prop(top_level_set, node.name):
|
|
30
|
+
refs[node.name] = True
|
|
31
|
+
stmt.walk(TreeWalker(visit_fn))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def compute_transitive_closure(body, direct_names, nonlocalvars):
|
|
35
|
+
nonlocal_set = {}
|
|
36
|
+
if nonlocalvars:
|
|
37
|
+
for nv in nonlocalvars:
|
|
38
|
+
nonlocal_set[nv] = True
|
|
39
|
+
|
|
40
|
+
name_map = {}
|
|
41
|
+
unnamed_stmts = []
|
|
42
|
+
for stmt in body:
|
|
43
|
+
name = get_top_level_name(stmt)
|
|
44
|
+
if name is not None:
|
|
45
|
+
name_map[name] = stmt
|
|
46
|
+
else:
|
|
47
|
+
unnamed_stmts.push(stmt)
|
|
48
|
+
|
|
49
|
+
top_level_set = {}
|
|
50
|
+
for name in Object.keys(name_map):
|
|
51
|
+
top_level_set[name] = True
|
|
52
|
+
|
|
53
|
+
needed = {}
|
|
54
|
+
queue = []
|
|
55
|
+
|
|
56
|
+
# Always include top-level assignments to nonlocal vars — they affect the
|
|
57
|
+
# global JavaScript scope and must never be filtered out.
|
|
58
|
+
for name in Object.keys(name_map):
|
|
59
|
+
if has_prop(nonlocal_set, name):
|
|
60
|
+
needed[name] = True
|
|
61
|
+
queue.push(name)
|
|
62
|
+
|
|
63
|
+
# Add directly imported names
|
|
64
|
+
for name in Object.keys(direct_names):
|
|
65
|
+
if not has_prop(needed, name):
|
|
66
|
+
needed[name] = True
|
|
67
|
+
queue.push(name)
|
|
68
|
+
|
|
69
|
+
# Unnamed statements (imports, if-blocks, bare expressions) are always
|
|
70
|
+
# included in output, so their references to named top-level items must
|
|
71
|
+
# also be included transitively.
|
|
72
|
+
always_refs = {}
|
|
73
|
+
for stmt in unnamed_stmts:
|
|
74
|
+
collect_refs_in_node(stmt, top_level_set, always_refs)
|
|
75
|
+
for ref_name in Object.keys(always_refs):
|
|
76
|
+
if not has_prop(needed, ref_name):
|
|
77
|
+
needed[ref_name] = True
|
|
78
|
+
queue.push(ref_name)
|
|
79
|
+
|
|
80
|
+
while queue.length > 0:
|
|
81
|
+
current = queue.shift()
|
|
82
|
+
if not has_prop(name_map, current):
|
|
83
|
+
continue
|
|
84
|
+
refs = {}
|
|
85
|
+
collect_refs_in_node(name_map[current], top_level_set, refs)
|
|
86
|
+
for ref_name in Object.keys(refs):
|
|
87
|
+
if not has_prop(needed, ref_name):
|
|
88
|
+
needed[ref_name] = True
|
|
89
|
+
queue.push(ref_name)
|
|
90
|
+
|
|
91
|
+
return needed
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def check_module_attr_access(main_body, info, alias_set):
|
|
95
|
+
def visit_fn(node, descend):
|
|
96
|
+
if is_node_type(node, AST_Dot):
|
|
97
|
+
expr = node.expression
|
|
98
|
+
if is_node_type(expr, AST_SymbolRef) and has_prop(alias_set, expr.name):
|
|
99
|
+
info.direct_names[node.property] = True
|
|
100
|
+
return True
|
|
101
|
+
if is_node_type(node, AST_Sub):
|
|
102
|
+
expr = node.expression
|
|
103
|
+
if is_node_type(expr, AST_SymbolRef) and has_prop(alias_set, expr.name):
|
|
104
|
+
info.can_tree_shake = False
|
|
105
|
+
for stmt in main_body:
|
|
106
|
+
stmt.walk(TreeWalker(visit_fn))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def analyze_imports(main_body):
|
|
110
|
+
result = {}
|
|
111
|
+
|
|
112
|
+
# First pass: walk the entire AST recursively to collect all from-imports
|
|
113
|
+
# (including those nested inside functions or other scopes).
|
|
114
|
+
def visit_from_imports(node, descend):
|
|
115
|
+
if is_node_type(node, AST_Imports):
|
|
116
|
+
for imp in node.imports:
|
|
117
|
+
if imp.argnames:
|
|
118
|
+
key = imp.key
|
|
119
|
+
if not has_prop(result, key):
|
|
120
|
+
result[key] = {'direct_names': {}, 'can_tree_shake': True}
|
|
121
|
+
for argname in imp.argnames:
|
|
122
|
+
result[key].direct_names[argname.name] = True
|
|
123
|
+
for stmt in main_body:
|
|
124
|
+
stmt.walk(TreeWalker(visit_from_imports))
|
|
125
|
+
|
|
126
|
+
# Second pass: handle top-level plain imports (import X as Y, import X).
|
|
127
|
+
# Attribute tracking via check_module_attr_access applies to main_body scope.
|
|
128
|
+
for stmt in main_body:
|
|
129
|
+
if not is_node_type(stmt, AST_Imports):
|
|
130
|
+
continue
|
|
131
|
+
for imp in stmt.imports:
|
|
132
|
+
if imp.argnames:
|
|
133
|
+
continue # already handled in first pass
|
|
134
|
+
key = imp.key
|
|
135
|
+
if not has_prop(result, key):
|
|
136
|
+
result[key] = {'direct_names': {}, 'can_tree_shake': True}
|
|
137
|
+
info = result[key]
|
|
138
|
+
if imp.alias:
|
|
139
|
+
alias_set = {}
|
|
140
|
+
alias_set[imp.alias.name] = True
|
|
141
|
+
check_module_attr_access(main_body, info, alias_set)
|
|
142
|
+
else:
|
|
143
|
+
parts = key.split('.')
|
|
144
|
+
if parts.length > 1:
|
|
145
|
+
# import X.Y with no alias: user accesses via X.Y.attr which
|
|
146
|
+
# requires two-level dot traversal — disable tree-shaking safely
|
|
147
|
+
info.can_tree_shake = False
|
|
148
|
+
else:
|
|
149
|
+
alias_set = {}
|
|
150
|
+
alias_set[parts[0]] = True
|
|
151
|
+
check_module_attr_access(main_body, info, alias_set)
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def tree_shake(ast, context):
|
|
157
|
+
import_infos = analyze_imports(ast.body)
|
|
158
|
+
for mod_key in Object.keys(import_infos):
|
|
159
|
+
info = import_infos[mod_key]
|
|
160
|
+
if not info.can_tree_shake:
|
|
161
|
+
continue
|
|
162
|
+
if not has_prop(ast.imports, mod_key):
|
|
163
|
+
continue
|
|
164
|
+
mod = ast.imports[mod_key]
|
|
165
|
+
# If body is missing (cached module), re-parse to get it
|
|
166
|
+
if not mod.body and mod.src_code:
|
|
167
|
+
parsed = context.parse(mod.src_code, {
|
|
168
|
+
'filename': mod.filename,
|
|
169
|
+
'module_id': mod_key,
|
|
170
|
+
'libdir': context.libdir,
|
|
171
|
+
'import_dirs': context.import_dirs or [],
|
|
172
|
+
'discard_asserts': context.discard_asserts,
|
|
173
|
+
'for_linting': True,
|
|
174
|
+
})
|
|
175
|
+
mod.body = parsed.body
|
|
176
|
+
mod.localvars = parsed.localvars
|
|
177
|
+
if not mod.nonlocalvars:
|
|
178
|
+
mod.nonlocalvars = parsed.nonlocalvars
|
|
179
|
+
if not mod.body:
|
|
180
|
+
continue
|
|
181
|
+
needed = compute_transitive_closure(mod.body, info.direct_names, mod.nonlocalvars)
|
|
182
|
+
mod.needed_names = needed
|
package/src/output/utils.pyj
CHANGED
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
-
from __python__ import hash_literals
|
|
4
|
-
|
|
5
|
-
from ast import AST_BlockStatement, is_node_type
|
|
6
|
-
|
|
7
|
-
def best_of(a):
|
|
8
|
-
best = a[0]
|
|
9
|
-
len_ = best.length
|
|
10
|
-
for i in range(1, a.length):
|
|
11
|
-
if a[i].length < len_:
|
|
12
|
-
best = a[i]
|
|
13
|
-
len_ = best.length
|
|
14
|
-
return best
|
|
15
|
-
|
|
16
|
-
def make_num(num):
|
|
17
|
-
str_ = num.toString(10)
|
|
18
|
-
a = [ str_.replace(/^0\./, ".").replace("e+", "e") ]
|
|
19
|
-
m = None
|
|
20
|
-
|
|
21
|
-
if Math.floor(num) is num:
|
|
22
|
-
if num >= 0:
|
|
23
|
-
a.push("0x" + num.toString(16).toLowerCase(), # probably pointless
|
|
24
|
-
"0" + num.toString(8))
|
|
25
|
-
else:
|
|
26
|
-
a.push("-0x" + (-num).toString(16).toLowerCase(), # probably pointless
|
|
27
|
-
"-0" + (-num).toString(8))
|
|
28
|
-
|
|
29
|
-
if m = /^(.*?)(0+)$/.exec(num):
|
|
30
|
-
a.push(m[1] + "e" + m[2].length)
|
|
31
|
-
|
|
32
|
-
elif m = /^0?\.(0+)(.*)$/.exec(num):
|
|
33
|
-
a.push(m[2] + "e-" + (m[1].length + m[2].length), str_.substr(str_.indexOf(".")))
|
|
34
|
-
|
|
35
|
-
return best_of(a)
|
|
36
|
-
|
|
37
|
-
def make_block(stmt, output):
|
|
38
|
-
if is_node_type(stmt, AST_BlockStatement):
|
|
39
|
-
stmt.print(output)
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
output.with_block(def():
|
|
43
|
-
output.indent()
|
|
44
|
-
stmt.print(output)
|
|
45
|
-
output.newline()
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
def create_doctring(docstrings):
|
|
49
|
-
ans = v'[]'
|
|
50
|
-
for ds in docstrings:
|
|
51
|
-
ds = str.rstrip(ds.value)
|
|
52
|
-
lines = v'[]'
|
|
53
|
-
min_leading_whitespace = ''
|
|
54
|
-
for line in ds.split(/$/gm):
|
|
55
|
-
r = /^\s+/.exec(line)
|
|
56
|
-
leading_whitespace = ''
|
|
57
|
-
if r:
|
|
58
|
-
leading_whitespace = r[0].replace(/[\n\r]/g, '') if r else ''
|
|
59
|
-
line = line[r[0].length:]
|
|
60
|
-
if not str.strip(line):
|
|
61
|
-
lines.push(v'["", ""]')
|
|
62
|
-
else:
|
|
63
|
-
leading_whitespace = leading_whitespace.replace(/\t/g, ' ')
|
|
64
|
-
if leading_whitespace and (not min_leading_whitespace or leading_whitespace.length < min_leading_whitespace.length):
|
|
65
|
-
min_leading_whitespace = leading_whitespace
|
|
66
|
-
lines.push(v'[leading_whitespace, line]')
|
|
67
|
-
for lw, l in lines:
|
|
68
|
-
if min_leading_whitespace:
|
|
69
|
-
lw = lw[min_leading_whitespace.length:]
|
|
70
|
-
ans.push(lw + l)
|
|
71
|
-
ans.push('')
|
|
72
|
-
return str.rstrip(ans.join('\n'))
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
+
from __python__ import hash_literals
|
|
4
|
+
|
|
5
|
+
from ast import AST_BlockStatement, is_node_type
|
|
6
|
+
|
|
7
|
+
def best_of(a):
|
|
8
|
+
best = a[0]
|
|
9
|
+
len_ = best.length
|
|
10
|
+
for i in range(1, a.length):
|
|
11
|
+
if a[i].length < len_:
|
|
12
|
+
best = a[i]
|
|
13
|
+
len_ = best.length
|
|
14
|
+
return best
|
|
15
|
+
|
|
16
|
+
def make_num(num):
|
|
17
|
+
str_ = num.toString(10)
|
|
18
|
+
a = [ str_.replace(/^0\./, ".").replace("e+", "e") ]
|
|
19
|
+
m = None
|
|
20
|
+
|
|
21
|
+
if Math.floor(num) is num:
|
|
22
|
+
if num >= 0:
|
|
23
|
+
a.push("0x" + num.toString(16).toLowerCase(), # probably pointless
|
|
24
|
+
"0" + num.toString(8))
|
|
25
|
+
else:
|
|
26
|
+
a.push("-0x" + (-num).toString(16).toLowerCase(), # probably pointless
|
|
27
|
+
"-0" + (-num).toString(8))
|
|
28
|
+
|
|
29
|
+
if m = /^(.*?)(0+)$/.exec(num):
|
|
30
|
+
a.push(m[1] + "e" + m[2].length)
|
|
31
|
+
|
|
32
|
+
elif m = /^0?\.(0+)(.*)$/.exec(num):
|
|
33
|
+
a.push(m[2] + "e-" + (m[1].length + m[2].length), str_.substr(str_.indexOf(".")))
|
|
34
|
+
|
|
35
|
+
return best_of(a)
|
|
36
|
+
|
|
37
|
+
def make_block(stmt, output):
|
|
38
|
+
if is_node_type(stmt, AST_BlockStatement):
|
|
39
|
+
stmt.print(output)
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
output.with_block(def():
|
|
43
|
+
output.indent()
|
|
44
|
+
stmt.print(output)
|
|
45
|
+
output.newline()
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def create_doctring(docstrings):
|
|
49
|
+
ans = v'[]'
|
|
50
|
+
for ds in docstrings:
|
|
51
|
+
ds = str.rstrip(ds.value)
|
|
52
|
+
lines = v'[]'
|
|
53
|
+
min_leading_whitespace = ''
|
|
54
|
+
for line in ds.split(/$/gm):
|
|
55
|
+
r = /^\s+/.exec(line)
|
|
56
|
+
leading_whitespace = ''
|
|
57
|
+
if r:
|
|
58
|
+
leading_whitespace = r[0].replace(/[\n\r]/g, '') if r else ''
|
|
59
|
+
line = line[r[0].length:]
|
|
60
|
+
if not str.strip(line):
|
|
61
|
+
lines.push(v'["", ""]')
|
|
62
|
+
else:
|
|
63
|
+
leading_whitespace = leading_whitespace.replace(/\t/g, ' ')
|
|
64
|
+
if leading_whitespace and (not min_leading_whitespace or leading_whitespace.length < min_leading_whitespace.length):
|
|
65
|
+
min_leading_whitespace = leading_whitespace
|
|
66
|
+
lines.push(v'[leading_whitespace, line]')
|
|
67
|
+
for lw, l in lines:
|
|
68
|
+
if min_leading_whitespace:
|
|
69
|
+
lw = lw[min_leading_whitespace.length:]
|
|
70
|
+
ans.push(lw + l)
|
|
71
|
+
ans.push('')
|
|
72
|
+
return str.rstrip(ans.join('\n'))
|
package/src/parse.pyj
CHANGED
|
@@ -31,7 +31,7 @@ TreeWalker
|
|
|
31
31
|
from tokenizer import tokenizer, is_token, RESERVED_WORDS
|
|
32
32
|
|
|
33
33
|
COMPILER_VERSION = '__COMPILER_VERSION__'
|
|
34
|
-
PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True, 'strict_arithmetic':True}
|
|
34
|
+
PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True, 'jsx':True, 'strict_arithmetic':True, 'python_modulo':True, 'type_enforcement':True}
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def get_compiler_version():
|
|
@@ -620,8 +620,15 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
620
620
|
})
|
|
621
621
|
elif tmp_ is "async":
|
|
622
622
|
start = prev()
|
|
623
|
+
if is_("keyword", "for"):
|
|
624
|
+
# async for: consume an async iterable using JS `for await ... of`
|
|
625
|
+
next()
|
|
626
|
+
forstmt = for_()
|
|
627
|
+
forstmt.is_async = True
|
|
628
|
+
forstmt.start = start
|
|
629
|
+
return forstmt
|
|
623
630
|
if not is_("keyword", "def"):
|
|
624
|
-
croak("Expected 'def' after 'async'")
|
|
631
|
+
croak("Expected 'def' or 'for' after 'async'")
|
|
625
632
|
next()
|
|
626
633
|
func = function_(S.in_class[-1], False, True)
|
|
627
634
|
func.start = start
|
|
@@ -708,21 +715,70 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
708
715
|
def with_():
|
|
709
716
|
clauses = v'[]'
|
|
710
717
|
start = S.token
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
718
|
+
|
|
719
|
+
# Detect Python 3.10+ parenthesized form: with (cm1 as a, cm2 as b):
|
|
720
|
+
# vs traditional: with cm as a: or with (expr) as a:
|
|
721
|
+
# Lookahead: scan tokens inside the `(` for an `as` keyword at depth 0
|
|
722
|
+
# before the matching `)`. If found, it's the parenthesized form.
|
|
723
|
+
is_parenthesized_with = False
|
|
724
|
+
if is_('punc', '('):
|
|
725
|
+
depth = 0
|
|
726
|
+
i = 0
|
|
727
|
+
while True:
|
|
728
|
+
while S.peeked.length <= i:
|
|
729
|
+
S.peeked.push(S.input())
|
|
730
|
+
tok = S.peeked[i]
|
|
731
|
+
if tok.type is 'eof':
|
|
732
|
+
break
|
|
733
|
+
if tok.type is 'punc':
|
|
734
|
+
if tok.value is '(' or tok.value is '[' or tok.value is '{':
|
|
735
|
+
depth += 1
|
|
736
|
+
elif tok.value is ')' or tok.value is ']' or tok.value is '}':
|
|
737
|
+
if depth is 0:
|
|
738
|
+
break
|
|
739
|
+
depth -= 1
|
|
740
|
+
elif tok.type is 'keyword' and tok.value is 'as' and depth is 0:
|
|
741
|
+
is_parenthesized_with = True
|
|
742
|
+
break
|
|
743
|
+
i += 1
|
|
744
|
+
|
|
745
|
+
if is_parenthesized_with:
|
|
746
|
+
next() # consume `(`
|
|
747
|
+
while True:
|
|
748
|
+
if is_('eof'):
|
|
749
|
+
unexpected()
|
|
750
|
+
if is_('punc', ')'):
|
|
751
|
+
next() # trailing comma before closing `)`
|
|
752
|
+
break
|
|
753
|
+
expr = expression()
|
|
754
|
+
alias = None
|
|
755
|
+
if is_('keyword', 'as'):
|
|
756
|
+
next()
|
|
757
|
+
alias = as_symbol(AST_SymbolAlias)
|
|
758
|
+
clauses.push(new AST_WithClause({'expression': expr, 'alias': alias}))
|
|
759
|
+
if is_('punc', ','):
|
|
760
|
+
next()
|
|
761
|
+
continue
|
|
762
|
+
if not is_('punc', ')'):
|
|
763
|
+
unexpected()
|
|
764
|
+
next() # consume `)`
|
|
765
|
+
break
|
|
766
|
+
else:
|
|
767
|
+
while True:
|
|
768
|
+
if is_('eof'):
|
|
769
|
+
unexpected()
|
|
770
|
+
expr = expression()
|
|
771
|
+
alias = None
|
|
772
|
+
if is_('keyword', 'as'):
|
|
773
|
+
next()
|
|
774
|
+
alias = as_symbol(AST_SymbolAlias)
|
|
775
|
+
clauses.push(new AST_WithClause({'expression':expr, 'alias':alias}))
|
|
776
|
+
if is_('punc', ','):
|
|
777
|
+
next()
|
|
778
|
+
continue
|
|
779
|
+
if not is_('punc', ':'):
|
|
780
|
+
unexpected()
|
|
781
|
+
break
|
|
726
782
|
|
|
727
783
|
if not clauses.length:
|
|
728
784
|
token_error(start, 'with statement must have at least one clause')
|
|
@@ -1396,6 +1452,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1396
1452
|
argnames.push(aname)
|
|
1397
1453
|
if is_('punc', ','):
|
|
1398
1454
|
next()
|
|
1455
|
+
if bracketed and is_('punc', ')'):
|
|
1456
|
+
next()
|
|
1457
|
+
break
|
|
1399
1458
|
else:
|
|
1400
1459
|
if bracketed:
|
|
1401
1460
|
if is_('punc', ')'):
|
|
@@ -1824,6 +1883,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
1824
1883
|
)(S.in_loop, S.labels)
|
|
1825
1884
|
})
|
|
1826
1885
|
definition.return_annotation = return_annotation
|
|
1886
|
+
definition.type_enforce = S.scoped_flags.get('type_enforcement', False)
|
|
1827
1887
|
definition.is_generator = is_generator[0]
|
|
1828
1888
|
definition.is_async = is_async
|
|
1829
1889
|
if is_node_type(definition, AST_Method):
|
|
@@ -3242,6 +3302,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
3242
3302
|
'end': right.end,
|
|
3243
3303
|
'overloaded': S.scoped_flags.get('overload_operators', False),
|
|
3244
3304
|
'strict_arith': S.scoped_flags.get('overload_operators', False) and S.scoped_flags.get('strict_arithmetic', True),
|
|
3305
|
+
'python_mod': (op is '%') and S.scoped_flags.get('python_modulo', True),
|
|
3245
3306
|
'python_truthiness': S.scoped_flags.get('truthiness', False) and (op is '&&' or op is '||')
|
|
3246
3307
|
})
|
|
3247
3308
|
return expr_op(ret, min_prec, no_in)
|
|
@@ -3324,6 +3385,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
|
|
|
3324
3385
|
if S.scoped_flags.get('overload_operators', False) and val is not '=':
|
|
3325
3386
|
asgn.overloaded = True
|
|
3326
3387
|
asgn.strict_arith = S.scoped_flags.get('strict_arithmetic', True)
|
|
3388
|
+
if val is '%=':
|
|
3389
|
+
asgn.python_mod = S.scoped_flags.get('python_modulo', True)
|
|
3327
3390
|
return asgn
|
|
3328
3391
|
return left
|
|
3329
3392
|
|