rapydscript-ng 0.7.24 → 0.8.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.
- package/CHANGELOG.md +19 -0
- package/README.md +72 -26
- package/bin/package.json +1 -0
- package/bin/rapydscript +65 -62
- package/editor-plugins/nvim/rapydscript/README.md +184 -0
- package/editor-plugins/nvim/rapydscript/bin/rapydscript.so +0 -0
- package/editor-plugins/nvim/rapydscript/ftdetect/rapydscript.lua +10 -0
- package/editor-plugins/nvim/rapydscript/lua/rapydscript/health.lua +88 -0
- package/editor-plugins/nvim/rapydscript/lua/rapydscript/init.lua +279 -0
- package/local-agent.md +28 -0
- package/package.json +4 -5
- package/release/baselib-plain-pretty.js +448 -326
- package/release/baselib-plain-ugly.js +3 -3
- package/release/compiler.js +2528 -1672
- package/release/signatures.json +17 -17
- package/src/ast.pyj +73 -25
- package/src/baselib-builtins.pyj +2 -2
- package/src/baselib-containers.pyj +153 -151
- package/src/baselib-internal.pyj +3 -3
- package/src/baselib-str.pyj +5 -5
- package/src/lib/aes.pyj +1 -1
- package/src/lib/encodings.pyj +1 -1
- package/src/lib/pythonize.pyj +1 -1
- package/src/lib/re.pyj +2 -2
- package/src/lib/traceback.pyj +165 -28
- package/src/lib/uuid.pyj +1 -1
- package/src/output/codegen.pyj +10 -3
- package/src/output/functions.pyj +31 -40
- package/src/output/literals.pyj +11 -14
- package/src/output/loops.pyj +25 -87
- package/src/output/modules.pyj +5 -47
- package/src/output/statements.pyj +1 -1
- package/src/output/stream.pyj +58 -31
- package/src/parse.pyj +777 -427
- package/src/tokenizer.pyj +16 -4
- package/src/utils.pyj +1 -1
- package/test/_treeshake_mod.pyj +30 -0
- package/test/_treeshake_side_effect.pyj +9 -0
- package/test/ast_serialization.pyj +392 -0
- package/test/async.pyj +95 -0
- package/test/baselib.pyj +1 -2
- package/test/embedded_compiler.pyj +57 -0
- package/test/fmt.pyj +201 -0
- package/test/generic.pyj +7 -3
- package/test/imports.pyj +8 -6
- package/test/internationalization.pyj +38 -37
- package/test/lint.pyj +120 -117
- package/test/lsp.pyj +222 -0
- package/test/repl.pyj +70 -65
- package/test/sourcemaps.pyj +315 -0
- package/test/starargs.pyj +46 -39
- package/test/traceback.pyj +263 -0
- package/test/treeshaking.pyj +181 -0
- package/test/web_repl_export.pyj +88 -0
- package/tools/ast_serialize.mjs +557 -0
- package/tools/cli.mjs +510 -0
- package/tools/compile.mjs +201 -0
- package/tools/compiler.mjs +127 -0
- package/tools/{completer.js → completer.mjs} +6 -6
- package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
- package/tools/export.js +109 -56
- package/tools/fmt.mjs +735 -0
- package/tools/{gettext.js → gettext.mjs} +46 -53
- package/tools/{ini.js → ini.mjs} +9 -10
- package/tools/{lint.js → lint.mjs} +78 -55
- package/tools/lsp.mjs +914 -0
- package/tools/lsp_protocol.mjs +259 -0
- package/tools/lsp_symbols.mjs +418 -0
- package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
- package/tools/{repl.js → repl.mjs} +52 -41
- package/tools/{self.js → self.mjs} +56 -46
- package/tools/sourcemap.mjs +123 -0
- package/tools/test.mjs +152 -0
- package/tools/treeshake.mjs +400 -0
- package/tools/{utils.js → utils.mjs} +26 -23
- package/tools/{web_repl.js → web_repl.mjs} +15 -13
- package/tools/web_repl_export.mjs +93 -0
- package/tree-sitter/README.md +101 -0
- package/tree-sitter/grammar.js +992 -0
- package/tree-sitter/package.json +43 -0
- package/tree-sitter/queries/rapydscript/highlights.scm +232 -0
- package/tree-sitter/queries/rapydscript/indents.scm +64 -0
- package/tree-sitter/queries/rapydscript/injections.scm +14 -0
- package/tree-sitter/queries/rapydscript/locals.scm +108 -0
- package/tree-sitter/src/grammar.json +4976 -0
- package/tree-sitter/src/node-types.json +2901 -0
- package/tree-sitter/src/parser.c +196465 -0
- package/tree-sitter/src/scanner.c +294 -0
- package/tree-sitter/src/tree_sitter/alloc.h +54 -0
- package/tree-sitter/src/tree_sitter/array.h +330 -0
- package/tree-sitter/src/tree_sitter/parser.h +286 -0
- package/tree-sitter/test/corpus/chaining.txt +99 -0
- package/tree-sitter/test/corpus/classes.txt +147 -0
- package/tree-sitter/test/corpus/comprehensions.txt +155 -0
- package/tree-sitter/test/corpus/containers.txt +242 -0
- package/tree-sitter/test/corpus/control_flow.txt +361 -0
- package/tree-sitter/test/corpus/decorators.txt +64 -0
- package/tree-sitter/test/corpus/existential.txt +102 -0
- package/tree-sitter/test/corpus/functions.txt +293 -0
- package/tree-sitter/test/corpus/imports.txt +117 -0
- package/tree-sitter/test/corpus/literals.txt +135 -0
- package/tree-sitter/test/corpus/operators.txt +296 -0
- package/tree-sitter/test/corpus/statements.txt +189 -0
- package/tree-sitter/test/corpus/strings.txt +90 -0
- package/tree-sitter/test/corpus/subscripts.txt +227 -0
- package/tree-sitter/tree-sitter.json +36 -0
- package/web-repl/env.js +35 -23
- package/web-repl/main.js +8 -8
- package/bin/export +0 -75
- package/bin/web-repl-export +0 -102
- package/tools/cli.js +0 -523
- package/tools/compile.js +0 -184
- package/tools/compiler.js +0 -84
- package/tools/test.js +0 -110
package/test/fmt.pyj
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# globals: assrt, fs, require, test_path, compiler_dir, console, RapydScript
|
|
2
|
+
|
|
3
|
+
# Tests for the "fmt" sub-command (tools/fmt.mjs), the PEP8 style formatter for
|
|
4
|
+
# RapydScript source code.
|
|
5
|
+
|
|
6
|
+
fmt = require('./fmt.mjs')
|
|
7
|
+
path = require('path')
|
|
8
|
+
os = require('os')
|
|
9
|
+
|
|
10
|
+
base_path = path.resolve(compiler_dir, '..')
|
|
11
|
+
libdir = path.join(base_path, 'src', 'lib')
|
|
12
|
+
|
|
13
|
+
async def parses(code):
|
|
14
|
+
ok = True
|
|
15
|
+
try:
|
|
16
|
+
await RapydScript.parse(code, {
|
|
17
|
+
'filename': 'fmt_test.pyj', 'basedir': test_path,
|
|
18
|
+
'libdir': libdir, 'for_linting': True
|
|
19
|
+
})
|
|
20
|
+
except:
|
|
21
|
+
ok = False
|
|
22
|
+
console.log('Formatted output failed to parse:\n' + code)
|
|
23
|
+
return ok
|
|
24
|
+
|
|
25
|
+
nchecks = {'n': 0}
|
|
26
|
+
|
|
27
|
+
async def check(name, src, expected, opts):
|
|
28
|
+
opts = opts or {}
|
|
29
|
+
out = fmt.format_string(src, opts)
|
|
30
|
+
assrt.equal(out, expected, 'format output mismatch for: ' + name)
|
|
31
|
+
# The formatter must be idempotent.
|
|
32
|
+
assrt.equal(fmt.format_string(out, opts), out, 'formatter not idempotent for: ' + name)
|
|
33
|
+
# The formatted output must remain valid RapydScript.
|
|
34
|
+
ok = await parses(out)
|
|
35
|
+
assrt.ok(ok, 'formatted output does not parse for: ' + name)
|
|
36
|
+
nchecks.n += 1
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def run_tests():
|
|
40
|
+
|
|
41
|
+
# --- Whitespace / operator normalization -------------------------------
|
|
42
|
+
await check('binary-ops', 'x=1+2*3\n', 'x = 1 + 2 * 3\n')
|
|
43
|
+
await check('collapse-spaces', 'y = f( a ,b )\n', 'y = f(a, b)\n')
|
|
44
|
+
await check('unary-minus', 'x = -1\ny = a - 1\n', 'x = -1\ny = a - 1\n')
|
|
45
|
+
await check('unary-not', 'z = not x\n', 'z = not x\n')
|
|
46
|
+
await check('word-ops', 'q=a and b or c\n', 'q = a and b or c\n')
|
|
47
|
+
await check('is-not', 'r = a is not b\n', 'r = a is not b\n')
|
|
48
|
+
await check('power', 'p = a**b\n', 'p = a ** b\n')
|
|
49
|
+
await check('attribute', 'v = a . b . c\n', 'v = a.b.c\n')
|
|
50
|
+
|
|
51
|
+
# --- kwargs / defaults vs assignment -----------------------------------
|
|
52
|
+
await check('kwargs', 'f( a=1, b = 2 )\n', 'f(a=1, b=2)\n')
|
|
53
|
+
await check('defaults', 'def g(a, b=3, c = 4):\n return a\n',
|
|
54
|
+
'def g(a, b=3, c=4):\n return a\n')
|
|
55
|
+
await check('assignment-spaces', 'x=1\n', 'x = 1\n')
|
|
56
|
+
await check('augmented', 'x+=1\n', 'x += 1\n')
|
|
57
|
+
|
|
58
|
+
# --- splat / unpacking --------------------------------------------------
|
|
59
|
+
await check('splat', 'f( *args, **kwargs )\n', 'f(*args, **kwargs)\n')
|
|
60
|
+
|
|
61
|
+
# --- slices / index / dict / list --------------------------------------
|
|
62
|
+
await check('slice', 'a = b[ 1 : 2 ]\n', 'a = b[1:2]\n')
|
|
63
|
+
await check('slice-step', 'a = b[::2]\n', 'a = b[::2]\n')
|
|
64
|
+
await check('index', 'c = d[ x ]\n', 'c = d[x]\n')
|
|
65
|
+
await check('negative-index', 'c = d[-1]\n', 'c = d[-1]\n')
|
|
66
|
+
await check('dict', 'e = {"a":1,"b":2}\n', "e = {'a': 1, 'b': 2}\n")
|
|
67
|
+
await check('list', 'l = [ 1,2,3 ]\n', 'l = [1, 2, 3]\n')
|
|
68
|
+
|
|
69
|
+
# --- comprehensions -----------------------------------------------------
|
|
70
|
+
await check('list-comp', 'a=[i*i for i in range(1,20) if i*i%3==0]\n',
|
|
71
|
+
'a = [i * i for i in range(1, 20) if i * i % 3 == 0]\n')
|
|
72
|
+
await check('dict-comp', 'd={x:x+1 for x in range(20) if x>2}\n',
|
|
73
|
+
'd = {x: x + 1 for x in range(20) if x > 2}\n')
|
|
74
|
+
|
|
75
|
+
# --- quote normalization -----------------------------------------------
|
|
76
|
+
await check('quote-to-single', 'a = "hi"\n', "a = 'hi'\n")
|
|
77
|
+
await check('quote-to-double', "a = 'hi'\n", 'a = "hi"\n', {'preferred_quote': 'double'})
|
|
78
|
+
# Do not re-quote when it would add escapes.
|
|
79
|
+
await check('quote-keep-apostrophe', 'a = "it\'s"\n', 'a = "it\'s"\n')
|
|
80
|
+
await check('quote-keep-embedded-double', 'a = \'say "hi"\'\n', 'a = \'say "hi"\'\n')
|
|
81
|
+
# Raw / f / verbatim strings are never re-quoted.
|
|
82
|
+
await check('quote-raw-untouched', 'a = r"\\d+"\n', 'a = r"\\d+"\n')
|
|
83
|
+
await check('quote-fstring-untouched', 'a = f"x{y}"\n', 'a = f"x{y}"\n')
|
|
84
|
+
|
|
85
|
+
# --- indentation normalization (tabs and 2-space -> 4-space) -----------
|
|
86
|
+
await check('tab-indent', 'if True:\n\tx = 1\n', 'if True:\n x = 1\n')
|
|
87
|
+
await check('two-space-indent', 'if True:\n x = 1\n if x:\n y = 2\n',
|
|
88
|
+
'if True:\n x = 1\n if x:\n y = 2\n')
|
|
89
|
+
|
|
90
|
+
# --- blank line policy --------------------------------------------------
|
|
91
|
+
await check('cap-blank-lines', 'a = 1\n\n\n\n\nb = 2\n', 'a = 1\n\n\nb = 2\n')
|
|
92
|
+
await check('two-blanks-between-defs',
|
|
93
|
+
'def f():\n pass\ndef g():\n pass\n',
|
|
94
|
+
'def f():\n pass\n\n\ndef g():\n pass\n')
|
|
95
|
+
await check('one-blank-between-methods',
|
|
96
|
+
'class A:\n def f(self):\n pass\n def g(self):\n pass\n',
|
|
97
|
+
'class A:\n def f(self):\n pass\n\n def g(self):\n pass\n')
|
|
98
|
+
await check('collapse-def-blanks',
|
|
99
|
+
'def f():\n pass\n\n\n\n\ndef g():\n pass\n',
|
|
100
|
+
'def f():\n pass\n\n\ndef g():\n pass\n')
|
|
101
|
+
|
|
102
|
+
# --- comments -----------------------------------------------------------
|
|
103
|
+
await check('comment-space', '#hello\nx = 1\n', '# hello\nx = 1\n')
|
|
104
|
+
await check('trailing-comment', 'x = 1 #note\n', 'x = 1 # note\n')
|
|
105
|
+
await check('comment-before-def',
|
|
106
|
+
'# a helper\ndef f():\n pass\n',
|
|
107
|
+
'# a helper\ndef f():\n pass\n')
|
|
108
|
+
|
|
109
|
+
# --- decorators ---------------------------------------------------------
|
|
110
|
+
await check('decorators',
|
|
111
|
+
'@makebold\n@makeitalic\ndef hello():\n return "hi"\n',
|
|
112
|
+
"@makebold\n@makeitalic\ndef hello():\n return 'hi'\n")
|
|
113
|
+
|
|
114
|
+
# --- line wrapping ------------------------------------------------------
|
|
115
|
+
await check('wrap-call',
|
|
116
|
+
'result = fn(aaaaaaaa, bbbbbbbb, cccccccc, dddddddd, eeeeeeee)\n',
|
|
117
|
+
'result = fn(\n aaaaaaaa,\n bbbbbbbb,\n cccccccc,\n dddddddd,\n eeeeeeee\n)\n',
|
|
118
|
+
{'line_length': 30})
|
|
119
|
+
await check('wrap-list-trailing-comma',
|
|
120
|
+
'data = [11111111, 22222222, 33333333, 44444444]\n',
|
|
121
|
+
'data = [\n 11111111,\n 22222222,\n 33333333,\n 44444444,\n]\n',
|
|
122
|
+
{'line_length': 20})
|
|
123
|
+
# Short lines are left alone.
|
|
124
|
+
await check('no-wrap-short', 'x = f(a, b)\n', 'x = f(a, b)\n', {'line_length': 80})
|
|
125
|
+
# Comprehensions must never be wrapped on their (tuple-unpacking) commas.
|
|
126
|
+
await check('no-wrap-comprehension',
|
|
127
|
+
'result = [transform(it) for it, index in enumerate(collection) if it.is_valid]\n',
|
|
128
|
+
'result = [transform(it) for it, index in enumerate(collection) if it.is_valid]\n',
|
|
129
|
+
{'line_length': 40})
|
|
130
|
+
|
|
131
|
+
# --- reflow of simple multi-line statements ----------------------------
|
|
132
|
+
await check('reflow-simple-call',
|
|
133
|
+
'x = fn(\n a,\n b,\n c\n)\n',
|
|
134
|
+
'x = fn(a, b, c)\n')
|
|
135
|
+
|
|
136
|
+
# --- RapydScript specific constructs are preserved ----------------------
|
|
137
|
+
await check('preserve-anon-function',
|
|
138
|
+
"params = {\n 'onclick': def(event):\n alert('hi')\n ,\n 'x': 5\n}\n",
|
|
139
|
+
"params = {\n 'onclick': def(event):\n alert('hi')\n ,\n 'x': 5\n}\n")
|
|
140
|
+
await check('preserve-chain',
|
|
141
|
+
"$(element)\n.css('background-color', 'red')\n.show()\n",
|
|
142
|
+
"$(element)\n.css('background-color', 'red')\n.show()\n")
|
|
143
|
+
await check('preserve-verbatim-js', "a = v'var x = {foo: 1};'\n", "a = v'var x = {foo: 1};'\n")
|
|
144
|
+
await check('preserve-regex', 'b = /a(b)/g\n', 'b = /a(b)/g\n')
|
|
145
|
+
await check('preserve-verbose-regex',
|
|
146
|
+
"c = re.match(///\n a # comment\n b\n///, 'ab')\n",
|
|
147
|
+
"c = re.match(///\n a # comment\n b\n///, 'ab')\n")
|
|
148
|
+
await check('preserve-fstring', "msg = f'hello {name} world'\n", "msg = f'hello {name} world'\n")
|
|
149
|
+
await check('preserve-triple-string',
|
|
150
|
+
"text = '''\nline one\n line two\n'''\n",
|
|
151
|
+
"text = '''\nline one\n line two\n'''\n")
|
|
152
|
+
await check('preserve-existential',
|
|
153
|
+
'ans = a?.b?[1]?()\nv = b ? c\n',
|
|
154
|
+
'ans = a?.b?[1]?()\nv = b ? c\n')
|
|
155
|
+
|
|
156
|
+
# --- backslash continuation is preserved --------------------------------
|
|
157
|
+
await check('preserve-backslash',
|
|
158
|
+
'x = a + \\\n b + \\\n c\n',
|
|
159
|
+
'x = a + \\\n b + \\\n c\n')
|
|
160
|
+
|
|
161
|
+
# --- inline anonymous function on a single line -------------------------
|
|
162
|
+
await check('inline-anon', 'add = def(a,b): return a+b\n',
|
|
163
|
+
'add = def(a, b): return a + b\n')
|
|
164
|
+
|
|
165
|
+
# --- shebang preserved --------------------------------------------------
|
|
166
|
+
await check('shebang', '#!/usr/bin/env rapydscript\nx=1\n',
|
|
167
|
+
'#!/usr/bin/env rapydscript\nx = 1\n')
|
|
168
|
+
|
|
169
|
+
# --- empty / whitespace only input --------------------------------------
|
|
170
|
+
assrt.equal(fmt.format_string('', {}), '', 'empty input -> empty output')
|
|
171
|
+
assrt.equal(fmt.format_string('\n\n\n', {}), '', 'blank input -> empty output')
|
|
172
|
+
|
|
173
|
+
# --- check_report -------------------------------------------------------
|
|
174
|
+
errs = fmt.check_report('a.pyj', 'x=1\n', fmt.format_string('x=1\n', {}), {})
|
|
175
|
+
assrt.ok(errs.length >= 1, 'check_report must flag files that would be reformatted')
|
|
176
|
+
assrt.ok(errs[0].indexOf('would be reformatted') >= 0, 'check_report message content')
|
|
177
|
+
|
|
178
|
+
errs2 = fmt.check_report('a.pyj', 'x = 1\n', 'x = 1\n', {})
|
|
179
|
+
assrt.equal(errs2.length, 0, 'check_report must be silent for already-formatted, short files')
|
|
180
|
+
|
|
181
|
+
longline = 'x = ' + "'" + Array(90).join('a') + "'" + '\n'
|
|
182
|
+
errs3 = fmt.check_report('a.pyj', longline, longline, {'line_length': 80})
|
|
183
|
+
assrt.ok(errs3.length >= 1, 'check_report must flag over-length lines')
|
|
184
|
+
assrt.ok(errs3.join('\n').indexOf('exceeds') >= 0, 'check_report over-length message content')
|
|
185
|
+
|
|
186
|
+
# --- collect_pyj_files (directory recursion) ----------------------------
|
|
187
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'rs-fmt-'))
|
|
188
|
+
fs.mkdirSync(path.join(tmp, 'sub'))
|
|
189
|
+
fs.writeFileSync(path.join(tmp, 'a.pyj'), 'x=1\n')
|
|
190
|
+
fs.writeFileSync(path.join(tmp, 'sub', 'b.pyj'), 'y=2\n')
|
|
191
|
+
fs.writeFileSync(path.join(tmp, 'c.txt'), 'not rapydscript\n')
|
|
192
|
+
collected = await fmt.collect_pyj_files([tmp])
|
|
193
|
+
assrt.equal(collected.length, 2, 'collect_pyj_files must recurse and find 2 .pyj files')
|
|
194
|
+
for f in collected:
|
|
195
|
+
assrt.ok(f.indexOf('.txt') < 0, 'collect_pyj_files must skip non .pyj files in directories')
|
|
196
|
+
# An explicitly named non .pyj file is still included.
|
|
197
|
+
collected2 = await fmt.collect_pyj_files([path.join(tmp, 'c.txt')])
|
|
198
|
+
assrt.equal(collected2.length, 1, 'explicitly named files are always included')
|
|
199
|
+
fs.rmSync(tmp, {'recursive': True})
|
|
200
|
+
|
|
201
|
+
__test_async_done__ = run_tests()
|
package/test/generic.pyj
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import traceback
|
|
4
4
|
|
|
5
|
+
_throw_test_promises = []
|
|
6
|
+
|
|
5
7
|
def throw_test(code):
|
|
6
|
-
assrt.
|
|
7
|
-
RapydScript.parse(code, {'filename':code})
|
|
8
|
-
, RapydScript.SyntaxError)
|
|
8
|
+
_throw_test_promises.append(assrt.rejects(async def():
|
|
9
|
+
await RapydScript.parse(code, {'filename':code})
|
|
10
|
+
, RapydScript.SyntaxError))
|
|
9
11
|
|
|
10
12
|
# unary operators
|
|
11
13
|
assrt.equal(-(1), -1)
|
|
@@ -368,3 +370,5 @@ if "1" + "2" not in ["1", "2"]:
|
|
|
368
370
|
assrt.ok(1)
|
|
369
371
|
else:
|
|
370
372
|
assrt.ok(0)
|
|
373
|
+
|
|
374
|
+
Promise.all(_throw_test_promises)
|
package/test/imports.pyj
CHANGED
|
@@ -64,9 +64,11 @@ eq(GLOBAL_SYMBOL, 'i am global')
|
|
|
64
64
|
# Import errors happen during parsing, so we cannot test them directly as they would
|
|
65
65
|
# prevent this file from being parsed.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
,
|
|
67
|
+
Promise.all([
|
|
68
|
+
assrt.rejects(async def():
|
|
69
|
+
await RapydScript.parse('from _import_one import not_exported', {'basedir':test_path})
|
|
70
|
+
, /not exported/),
|
|
71
|
+
assrt.rejects(async def():
|
|
72
|
+
await RapydScript.parse('import xxxx', {'basedir':test_path})
|
|
73
|
+
, /doesn't exist/),
|
|
74
|
+
])
|
|
@@ -3,30 +3,29 @@
|
|
|
3
3
|
|
|
4
4
|
from gettext import gettext as _, ngettext, install
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def gettext(code):
|
|
6
|
+
async def gettext(code):
|
|
9
7
|
ans = {}
|
|
10
|
-
|
|
8
|
+
await rs_gettext(ans, code, '<test>')
|
|
11
9
|
return ans
|
|
12
10
|
|
|
13
|
-
def test_string(code, *args):
|
|
14
|
-
catalog = gettext(code)
|
|
11
|
+
async def test_string(code, *args):
|
|
12
|
+
catalog = await gettext(code)
|
|
15
13
|
assrt.equal(len(catalog), len(args))
|
|
16
14
|
for msgid, q in zip(Object.keys(catalog), args):
|
|
17
|
-
assrt.equal(
|
|
15
|
+
assrt.equal(rs_entry_to_string(msgid, catalog[msgid]), q)
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
test_string('a = _("one")
|
|
21
|
-
test_string('
|
|
17
|
+
async def run_all_tests():
|
|
18
|
+
await test_string('a = _("one")', '#: <test>:1\nmsgid "one"\nmsgstr ""')
|
|
19
|
+
await test_string('a = _("one")\nb = gettext("one")', '#: <test>:1\n#: <test>:2\nmsgid "one"\nmsgstr ""')
|
|
20
|
+
await test_string('''a = _("""one
|
|
22
21
|
two""")''', '#: <test>:1\nmsgid "one\\ntwo"\nmsgstr ""')
|
|
23
|
-
test_string('a = _("{}one")', '#: <test>:1\n#, python-brace-format\nmsgid "{}one"\nmsgstr ""')
|
|
24
|
-
test_string('a = _("{one}")', '#: <test>:1\n#, python-brace-format\nmsgid "{one}"\nmsgstr ""')
|
|
25
|
-
test_string('ngettext("one", "two", 1)', '#: <test>:1\nmsgid "one"\nmsgid_plural "two"\nmsgstr[0] ""\nmsgstr[1] ""')
|
|
26
|
-
test_string('''_('o"ne')''', '#: <test>:1\nmsgid "o\\"ne"\nmsgstr ""')
|
|
22
|
+
await test_string('a = _("{}one")', '#: <test>:1\n#, python-brace-format\nmsgid "{}one"\nmsgstr ""')
|
|
23
|
+
await test_string('a = _("{one}")', '#: <test>:1\n#, python-brace-format\nmsgid "{one}"\nmsgstr ""')
|
|
24
|
+
await test_string('ngettext("one", "two", 1)', '#: <test>:1\nmsgid "one"\nmsgid_plural "two"\nmsgstr[0] ""\nmsgstr[1] ""')
|
|
25
|
+
await test_string('''_('o"ne')''', '#: <test>:1\nmsgid "o\\"ne"\nmsgstr ""')
|
|
27
26
|
|
|
28
|
-
m =
|
|
29
|
-
catalog = m.parse(r'''
|
|
27
|
+
m = rs_msgfmt
|
|
28
|
+
catalog = m.parse(r'''
|
|
30
29
|
msgid ""
|
|
31
30
|
msgstr ""
|
|
32
31
|
"Language: en\n"
|
|
@@ -48,26 +47,28 @@ msgid "test \"quote\" escape"
|
|
|
48
47
|
msgstr "good"
|
|
49
48
|
''')
|
|
50
49
|
|
|
51
|
-
assrt.equal(2, catalog['nplurals'])
|
|
52
|
-
assrt.equal('en', catalog['language'])
|
|
53
|
-
assrt.equal(catalog['entries'].length, 3)
|
|
54
|
-
item = catalog['entries'][0]
|
|
55
|
-
assrt.equal(item['msgid'], 'one\ncontinued')
|
|
56
|
-
assrt.deepEqual(item['msgstr'], v"['ONE']")
|
|
57
|
-
assrt.ok(item['fuzzy'], 'item not fuzzy')
|
|
58
|
-
item = catalog['entries'][1]
|
|
59
|
-
assrt.equal(item['msgid'], 'two')
|
|
60
|
-
assrt.deepEqual(item['msgstr'], v"['abc', 'def']")
|
|
61
|
-
assrt.ok(not item['fuzzy'], 'item not fuzzy')
|
|
62
|
-
item = catalog['entries'][2]
|
|
63
|
-
assrt.equal(item['msgid'], 'test "quote" escape')
|
|
64
|
-
assrt.deepEqual(item['msgstr'], v"['good']")
|
|
50
|
+
assrt.equal(2, catalog['nplurals'])
|
|
51
|
+
assrt.equal('en', catalog['language'])
|
|
52
|
+
assrt.equal(catalog['entries'].length, 3)
|
|
53
|
+
item = catalog['entries'][0]
|
|
54
|
+
assrt.equal(item['msgid'], 'one\ncontinued')
|
|
55
|
+
assrt.deepEqual(item['msgstr'], v"['ONE']")
|
|
56
|
+
assrt.ok(item['fuzzy'], 'item not fuzzy')
|
|
57
|
+
item = catalog['entries'][1]
|
|
58
|
+
assrt.equal(item['msgid'], 'two')
|
|
59
|
+
assrt.deepEqual(item['msgstr'], v"['abc', 'def']")
|
|
60
|
+
assrt.ok(not item['fuzzy'], 'item not fuzzy')
|
|
61
|
+
item = catalog['entries'][2]
|
|
62
|
+
assrt.equal(item['msgid'], 'test "quote" escape')
|
|
63
|
+
assrt.deepEqual(item['msgstr'], v"['good']")
|
|
64
|
+
|
|
65
|
+
install({'entries': {
|
|
66
|
+
'one':['ONE'],
|
|
67
|
+
'two':['1', '2'],
|
|
68
|
+
}})
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
'
|
|
68
|
-
'two'
|
|
69
|
-
}})
|
|
70
|
+
assrt.equal(_('one'), 'ONE')
|
|
71
|
+
assrt.equal(ngettext('two', 'xxx', 1), '1')
|
|
72
|
+
assrt.equal(ngettext('two', 'xxx', 100), '2')
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
assrt.equal(ngettext('two', 'xxx', 1), '1')
|
|
73
|
-
assrt.equal(ngettext('two', 'xxx', 100), '2')
|
|
74
|
+
run_all_tests()
|
package/test/lint.pyj
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
# License: BSD
|
|
3
3
|
# Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
|
4
4
|
|
|
5
|
-
linter = require('../tools/lint.
|
|
5
|
+
linter = require('../tools/lint.mjs')
|
|
6
6
|
|
|
7
|
-
def lint(code):
|
|
8
|
-
return linter.lint_code(code, {'filename':'<test>', 'report':def():
|
|
7
|
+
async def lint(code):
|
|
8
|
+
return await linter.lint_code(code, {'filename':'<test>', 'report':def():
|
|
9
9
|
pass
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
-
def err_test(code, ident, line, name, other_line):
|
|
13
|
-
msgs = lint(code)
|
|
12
|
+
async def err_test(code, ident, line, name, other_line):
|
|
13
|
+
msgs = await lint(code)
|
|
14
14
|
assrt.ok(len(msgs) > 0, 'failed to find error in: ' + code)
|
|
15
15
|
assrt.equal(len(msgs), 1, 'found ' + (len(msgs) - 1) + ' extra errors in: ' + code)
|
|
16
16
|
assrt.equal(msgs[0].ident, ident, 'incorrect ident: ' + msgs[0].ident + ' for: ' + code)
|
|
@@ -21,110 +21,111 @@ def err_test(code, ident, line, name, other_line):
|
|
|
21
21
|
if other_line != undefined:
|
|
22
22
|
assrt.equal(msgs[0].other_line, other_line, 'incorrect other_line: ' + msgs[0].other_line + ' for: ' + code)
|
|
23
23
|
|
|
24
|
-
def ok_test(code):
|
|
25
|
-
msgs = lint(code)
|
|
24
|
+
async def ok_test(code):
|
|
25
|
+
msgs = await lint(code)
|
|
26
26
|
assrt.equal(msgs.length, 0, 'Got unexpected msg: ' + (msgs[0] or {}).message + ' for code: ' + code)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
err_test('import foo
|
|
31
|
-
err_test('import foo
|
|
32
|
-
err_test('
|
|
33
|
-
err_test('from x import foo
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
ok_test('def f(a
|
|
39
|
-
ok_test('def f(
|
|
40
|
-
ok_test('def f(
|
|
41
|
-
ok_test('def f(a):\n return
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
ok_test('def f():\n a
|
|
52
|
-
ok_test('
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
err_test('def f():\n
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
err_test('
|
|
66
|
-
err_test('
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ok_test('[
|
|
71
|
-
ok_test('[1 for x in [1,2]]')
|
|
72
|
-
ok_test('
|
|
73
|
-
ok_test('def f():\n l=[1,2];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ok_test('def f():\n for x in "":\n
|
|
79
|
-
ok_test('def f():\n for x
|
|
80
|
-
ok_test('
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
err_test('a=1
|
|
86
|
-
|
|
87
|
-
ok_test(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
ok_test('f() # noqa
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
err_test('if 1:\n
|
|
101
|
-
err_test('
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
ok_test('def a():\n x = 1\n def b():\n nonlocal x\n x\n
|
|
114
|
-
ok_test('def a():\n x = 1\n def ():\n nonlocal x\n x =
|
|
115
|
-
ok_test('
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
28
|
+
async def run_tests():
|
|
29
|
+
# Imports
|
|
30
|
+
await err_test('import foo', 'unused-import', 1, 'foo')
|
|
31
|
+
await err_test('import foo.boo', 'unused-import', 1, 'foo')
|
|
32
|
+
await err_test('import foo as boo', 'unused-import', 1, 'boo')
|
|
33
|
+
await err_test('from x import foo', 'unused-import', 1, 'foo')
|
|
34
|
+
await err_test('from x import foo as boo', 'unused-import', 1, 'boo')
|
|
35
|
+
await ok_test('import foo\nfoo')
|
|
36
|
+
|
|
37
|
+
# Function arguments
|
|
38
|
+
await ok_test('def f(a):\n return a')
|
|
39
|
+
await ok_test('def f(a=1):\n return a')
|
|
40
|
+
await ok_test('def f(*a):\n return a')
|
|
41
|
+
await ok_test('def f(**a):\n return a')
|
|
42
|
+
await ok_test('def f(a):\n return 1')
|
|
43
|
+
|
|
44
|
+
# Extended slices
|
|
45
|
+
await ok_test('a = []; a[::2], a[1:-1]')
|
|
46
|
+
|
|
47
|
+
# Top level unused ignored
|
|
48
|
+
await ok_test('a=1\ndef f():\n pass')
|
|
49
|
+
|
|
50
|
+
# Destructuring assignment
|
|
51
|
+
await ok_test('def f():\n a, b = 1, 2; a, b')
|
|
52
|
+
await ok_test('def f():\n a = 1; b, c = a, 2; return b, c')
|
|
53
|
+
await ok_test('for x, (y, z) in [ [1, [2, 3]] ]:\n x + y + z')
|
|
54
|
+
await err_test('def f():\n a, b = 1, 1; return a', 'unused-local', 2, 'b')
|
|
55
|
+
|
|
56
|
+
# Compound assignment
|
|
57
|
+
await err_test('a += 1', 'undef', 1, 'a')
|
|
58
|
+
await ok_test('def f():\n a = 1; a += 1')
|
|
59
|
+
|
|
60
|
+
# Unused bindings
|
|
61
|
+
await err_test('def f():\n a=1', 'unused-local', 2, 'a')
|
|
62
|
+
await err_test('def f():\n def a():\n pass', 'unused-local', 2, 'a')
|
|
63
|
+
|
|
64
|
+
# Undefined references
|
|
65
|
+
await err_test('f()', 'undef', 1, 'f')
|
|
66
|
+
await err_test('a', 'undef', 1, 'a')
|
|
67
|
+
await err_test('def f():\n a=1; return a\na', 'undef', 3, 'a')
|
|
68
|
+
|
|
69
|
+
# Comprehensions
|
|
70
|
+
await ok_test('[x for x in [1,2]]')
|
|
71
|
+
await ok_test('[1 for x in [1,2] if x]')
|
|
72
|
+
await ok_test('[1 for x in [1,2]]')
|
|
73
|
+
await ok_test('def f():\n l=[1,2];[x for x in l]')
|
|
74
|
+
await ok_test('def f():\n l=[1,2];{x:True for x in l}')
|
|
75
|
+
await err_test('def f():\n [x for x in [1,2]]; return x', 'undef', 2, 'x')
|
|
76
|
+
|
|
77
|
+
# Loops
|
|
78
|
+
await ok_test('def f():\n for x in "":\n pass\n return x')
|
|
79
|
+
await ok_test('def f():\n for x in "":\n x += 1\n')
|
|
80
|
+
await ok_test('def f():\n for x, y in "":\n pass\n return x, y')
|
|
81
|
+
await ok_test('for v"var i = 0; i < 1; i++":\n print(i)')
|
|
82
|
+
await err_test('def f():\n a = 1\n for a in "":\n a', 'loop-shadowed', 3, 'a', 2)
|
|
83
|
+
|
|
84
|
+
# Semi-colons
|
|
85
|
+
await err_test('a=1;;a', 'extra-semicolon', 1, ';')
|
|
86
|
+
await err_test('a=1;', 'eol-semicolon', 1, ';')
|
|
87
|
+
await ok_test('a = """\nhello;\n"""')
|
|
88
|
+
await ok_test("a = '''\nhello;\n'''")
|
|
89
|
+
|
|
90
|
+
# Builtins
|
|
91
|
+
for k in 'String Symbol this self window Map arguments print len range dir undefined'.split(' '):
|
|
92
|
+
await ok_test(k)
|
|
93
|
+
|
|
94
|
+
# noqa
|
|
95
|
+
await ok_test('f() # noqa')
|
|
96
|
+
await ok_test('f() # noqa:undef')
|
|
97
|
+
await err_test('f() # noqa:xxx', 'undef')
|
|
98
|
+
|
|
99
|
+
# Named func in branch
|
|
100
|
+
await err_test('if 1:\n def f():\n pass', 'func-in-branch', 2, 'f')
|
|
101
|
+
await err_test('if 1:\n pass\nelse:\n def f():\n pass', 'func-in-branch', 4, 'f')
|
|
102
|
+
await err_test('try:\n def f():\n pass\nexcept:\n pass', 'func-in-branch', 2, 'f')
|
|
103
|
+
await ok_test('if 1:\n a = def():\n pass')
|
|
104
|
+
|
|
105
|
+
# Syntax errors
|
|
106
|
+
await err_test('def f(:\n pass', 'syntax-err', 1)
|
|
107
|
+
|
|
108
|
+
# Functions
|
|
109
|
+
await ok_test('def f():\n pass\nf()')
|
|
110
|
+
|
|
111
|
+
# Non-locals
|
|
112
|
+
await err_test('def a():\n x = 1\n def b():\n nonlocal x\n b()', 'unused-local', 2, 'x')
|
|
113
|
+
await ok_test('def a():\n x = 1\n def b():\n nonlocal x\n x\n b()')
|
|
114
|
+
await ok_test('def a():\n x = 1\n def b():\n nonlocal x\n x\n x = 2\n b()')
|
|
115
|
+
await ok_test('def a():\n x = 1\n def ():\n nonlocal x\n x = 1\n x')
|
|
116
|
+
await ok_test('nonlocal a\na()')
|
|
117
|
+
|
|
118
|
+
# Define after use
|
|
119
|
+
await err_test('def g():\n f()\n f()\n def f():\n pass', 'def-after-use', 3, 'f')
|
|
120
|
+
|
|
121
|
+
# Decorators
|
|
122
|
+
await ok_test('from x import y\n@y\ndef f():\n pass')
|
|
123
|
+
|
|
124
|
+
# try/except
|
|
125
|
+
await ok_test('try:\n 1\nexcept Error as e:\n e')
|
|
126
|
+
|
|
127
|
+
# Classes
|
|
128
|
+
await ok_test('''
|
|
128
129
|
class A:
|
|
129
130
|
|
|
130
131
|
h = j = 1
|
|
@@ -146,21 +147,23 @@ class B(A):
|
|
|
146
147
|
def __init__(self):
|
|
147
148
|
A.__init__(self, 'b')
|
|
148
149
|
''')
|
|
149
|
-
err_test('class A:\n def a(self):\n a()', 'undef', 3, 'a')
|
|
150
|
-
err_test('class A:\n def a(self):\n pass\n def a(self):\n pass', 'dup-method', 4, 'a')
|
|
150
|
+
await err_test('class A:\n def a(self):\n a()', 'undef', 3, 'a')
|
|
151
|
+
await err_test('class A:\n def a(self):\n pass\n def a(self):\n pass', 'dup-method', 4, 'a')
|
|
151
152
|
|
|
152
|
-
# Object literals
|
|
153
|
-
err_test('{"a":1, "a":2}', 'dup-key', 1, 'a')
|
|
153
|
+
# Object literals
|
|
154
|
+
await err_test('{"a":1, "a":2}', 'dup-key', 1, 'a')
|
|
154
155
|
|
|
155
|
-
# keyword arg values
|
|
156
|
-
ok_test('def f():\n a=1\n f(b=a)')
|
|
157
|
-
ok_test('def f():\n a={}\n f(**a)')
|
|
156
|
+
# keyword arg values
|
|
157
|
+
await ok_test('def f():\n a=1\n f(b=a)')
|
|
158
|
+
await ok_test('def f():\n a={}\n f(**a)')
|
|
158
159
|
|
|
159
|
-
# with statement
|
|
160
|
-
ok_test('''
|
|
160
|
+
# with statement
|
|
161
|
+
await ok_test('''
|
|
161
162
|
def f():
|
|
162
163
|
a = 1
|
|
163
164
|
with a as b:
|
|
164
165
|
b()
|
|
165
166
|
''')
|
|
166
|
-
err_test('with a:\n pass', 'undef', '1', 'a')
|
|
167
|
+
await err_test('with a:\n pass', 'undef', '1', 'a')
|
|
168
|
+
|
|
169
|
+
__test_async_done__ = run_tests()
|