rapydscript-ns 0.8.4 → 0.9.0

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.
Files changed (132) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +18 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/README.md +715 -169
  8. package/TODO.md +9 -2
  9. package/add-toc-to-readme +2 -2
  10. package/bin/export +75 -75
  11. package/bin/rapydscript +70 -70
  12. package/bin/web-repl-export +102 -102
  13. package/build +2 -2
  14. package/language-service/index.js +36 -27
  15. package/package.json +1 -1
  16. package/publish.py +37 -37
  17. package/release/baselib-plain-pretty.js +2358 -168
  18. package/release/baselib-plain-ugly.js +73 -3
  19. package/release/compiler.js +6282 -3092
  20. package/release/signatures.json +31 -30
  21. package/session.vim +4 -4
  22. package/setup.cfg +2 -2
  23. package/src/ast.pyj +1 -0
  24. package/src/baselib-builtins.pyj +340 -2
  25. package/src/baselib-bytes.pyj +664 -0
  26. package/src/baselib-errors.pyj +1 -1
  27. package/src/baselib-internal.pyj +267 -60
  28. package/src/baselib-itertools.pyj +110 -97
  29. package/src/baselib-str.pyj +22 -4
  30. package/src/compiler.pyj +36 -36
  31. package/src/errors.pyj +30 -30
  32. package/src/lib/abc.pyj +317 -0
  33. package/src/lib/aes.pyj +646 -646
  34. package/src/lib/copy.pyj +120 -120
  35. package/src/lib/dataclasses.pyj +532 -0
  36. package/src/lib/elementmaker.pyj +83 -83
  37. package/src/lib/encodings.pyj +126 -126
  38. package/src/lib/enum.pyj +125 -0
  39. package/src/lib/gettext.pyj +569 -569
  40. package/src/lib/itertools.pyj +580 -580
  41. package/src/lib/math.pyj +193 -193
  42. package/src/lib/operator.pyj +11 -11
  43. package/src/lib/pythonize.pyj +20 -20
  44. package/src/lib/random.pyj +118 -118
  45. package/src/lib/re.pyj +504 -470
  46. package/src/lib/react.pyj +74 -74
  47. package/src/lib/traceback.pyj +63 -63
  48. package/src/lib/typing.pyj +577 -0
  49. package/src/lib/uuid.pyj +77 -77
  50. package/src/monaco-language-service/builtins.js +14 -4
  51. package/src/monaco-language-service/diagnostics.js +19 -20
  52. package/src/monaco-language-service/dts.js +550 -550
  53. package/src/output/classes.pyj +62 -26
  54. package/src/output/comments.pyj +45 -45
  55. package/src/output/exceptions.pyj +201 -201
  56. package/src/output/functions.pyj +78 -5
  57. package/src/output/jsx.pyj +164 -164
  58. package/src/output/loops.pyj +5 -2
  59. package/src/output/operators.pyj +100 -34
  60. package/src/output/treeshake.pyj +182 -182
  61. package/src/output/utils.pyj +72 -72
  62. package/src/parse.pyj +80 -16
  63. package/src/string_interpolation.pyj +72 -72
  64. package/src/tokenizer.pyj +9 -4
  65. package/src/unicode_aliases.pyj +576 -576
  66. package/src/utils.pyj +192 -192
  67. package/test/_import_one.pyj +37 -37
  68. package/test/_import_two/__init__.pyj +11 -11
  69. package/test/_import_two/level2/deep.pyj +4 -4
  70. package/test/_import_two/other.pyj +6 -6
  71. package/test/_import_two/sub.pyj +13 -13
  72. package/test/abc.pyj +291 -0
  73. package/test/aes_vectors.pyj +421 -421
  74. package/test/annotations.pyj +80 -80
  75. package/test/arithmetic_nostrict.pyj +88 -0
  76. package/test/arithmetic_types.pyj +169 -0
  77. package/test/baselib.pyj +91 -0
  78. package/test/bytes.pyj +467 -0
  79. package/test/classes.pyj +1 -0
  80. package/test/comparison_ops.pyj +173 -0
  81. package/test/dataclasses.pyj +253 -0
  82. package/test/decorators.pyj +77 -77
  83. package/test/docstrings.pyj +39 -39
  84. package/test/elementmaker_test.pyj +45 -45
  85. package/test/enum.pyj +134 -0
  86. package/test/eval_exec.pyj +56 -0
  87. package/test/format.pyj +148 -0
  88. package/test/functions.pyj +151 -151
  89. package/test/generators.pyj +41 -41
  90. package/test/generic.pyj +370 -370
  91. package/test/imports.pyj +72 -72
  92. package/test/internationalization.pyj +73 -73
  93. package/test/lint.pyj +164 -164
  94. package/test/loops.pyj +85 -85
  95. package/test/numpy.pyj +734 -734
  96. package/test/object.pyj +64 -0
  97. package/test/omit_function_metadata.pyj +20 -20
  98. package/test/python_compat.pyj +17 -15
  99. package/test/python_features.pyj +70 -15
  100. package/test/regexp.pyj +83 -55
  101. package/test/repl.pyj +121 -121
  102. package/test/scoped_flags.pyj +76 -76
  103. package/test/tuples.pyj +96 -0
  104. package/test/typing.pyj +469 -0
  105. package/test/unit/index.js +116 -7
  106. package/test/unit/language-service-dts.js +543 -543
  107. package/test/unit/language-service-hover.js +455 -455
  108. package/test/unit/language-service.js +84 -0
  109. package/test/unit/web-repl.js +804 -1
  110. package/test/vars_locals_globals.pyj +94 -0
  111. package/tools/cli.js +558 -547
  112. package/tools/compile.js +224 -219
  113. package/tools/completer.js +131 -131
  114. package/tools/embedded_compiler.js +262 -251
  115. package/tools/gettext.js +185 -185
  116. package/tools/ini.js +65 -65
  117. package/tools/lint.js +16 -19
  118. package/tools/msgfmt.js +187 -187
  119. package/tools/repl.js +223 -223
  120. package/tools/test.js +118 -118
  121. package/tools/utils.js +128 -128
  122. package/tools/web_repl.js +95 -95
  123. package/try +41 -41
  124. package/web-repl/env.js +196 -196
  125. package/web-repl/index.html +163 -163
  126. package/web-repl/main.js +252 -252
  127. package/web-repl/prism.css +139 -139
  128. package/web-repl/prism.js +113 -113
  129. package/web-repl/rapydscript.js +224 -224
  130. package/web-repl/sha1.js +25 -25
  131. package/PYTHON_DIFFERENCES_REPORT.md +0 -291
  132. package/PYTHON_FEATURE_COVERAGE.md +0 -200
@@ -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
@@ -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'))