rapydscript-ng 0.7.23 → 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.
Files changed (114) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +72 -26
  3. package/bin/package.json +1 -0
  4. package/bin/rapydscript +65 -62
  5. package/editor-plugins/nvim/rapydscript/README.md +184 -0
  6. package/editor-plugins/nvim/rapydscript/bin/rapydscript.so +0 -0
  7. package/editor-plugins/nvim/rapydscript/ftdetect/rapydscript.lua +10 -0
  8. package/editor-plugins/nvim/rapydscript/lua/rapydscript/health.lua +88 -0
  9. package/editor-plugins/nvim/rapydscript/lua/rapydscript/init.lua +279 -0
  10. package/local-agent.md +28 -0
  11. package/package.json +4 -5
  12. package/release/baselib-plain-pretty.js +448 -326
  13. package/release/baselib-plain-ugly.js +3 -3
  14. package/release/compiler.js +2528 -1672
  15. package/release/signatures.json +17 -17
  16. package/src/ast.pyj +73 -25
  17. package/src/baselib-builtins.pyj +2 -2
  18. package/src/baselib-containers.pyj +153 -151
  19. package/src/baselib-internal.pyj +3 -3
  20. package/src/baselib-str.pyj +5 -5
  21. package/src/lib/aes.pyj +1 -1
  22. package/src/lib/encodings.pyj +1 -1
  23. package/src/lib/pythonize.pyj +1 -1
  24. package/src/lib/re.pyj +2 -2
  25. package/src/lib/traceback.pyj +165 -28
  26. package/src/lib/uuid.pyj +1 -1
  27. package/src/output/codegen.pyj +10 -3
  28. package/src/output/functions.pyj +31 -40
  29. package/src/output/literals.pyj +11 -14
  30. package/src/output/loops.pyj +25 -87
  31. package/src/output/modules.pyj +5 -47
  32. package/src/output/statements.pyj +1 -1
  33. package/src/output/stream.pyj +58 -31
  34. package/src/parse.pyj +777 -427
  35. package/src/tokenizer.pyj +16 -4
  36. package/src/utils.pyj +1 -1
  37. package/test/_treeshake_mod.pyj +30 -0
  38. package/test/_treeshake_side_effect.pyj +9 -0
  39. package/test/ast_serialization.pyj +392 -0
  40. package/test/async.pyj +95 -0
  41. package/test/baselib.pyj +1 -2
  42. package/test/embedded_compiler.pyj +57 -0
  43. package/test/fmt.pyj +201 -0
  44. package/test/generic.pyj +7 -3
  45. package/test/imports.pyj +8 -6
  46. package/test/internationalization.pyj +38 -37
  47. package/test/lint.pyj +120 -115
  48. package/test/lsp.pyj +222 -0
  49. package/test/repl.pyj +70 -65
  50. package/test/sourcemaps.pyj +315 -0
  51. package/test/starargs.pyj +46 -39
  52. package/test/traceback.pyj +263 -0
  53. package/test/treeshaking.pyj +181 -0
  54. package/test/web_repl_export.pyj +88 -0
  55. package/tools/ast_serialize.mjs +557 -0
  56. package/tools/cli.mjs +510 -0
  57. package/tools/compile.mjs +201 -0
  58. package/tools/compiler.mjs +127 -0
  59. package/tools/{completer.js → completer.mjs} +6 -6
  60. package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
  61. package/tools/export.js +109 -56
  62. package/tools/fmt.mjs +735 -0
  63. package/tools/{gettext.js → gettext.mjs} +46 -53
  64. package/tools/{ini.js → ini.mjs} +9 -10
  65. package/tools/{lint.js → lint.mjs} +107 -56
  66. package/tools/lsp.mjs +914 -0
  67. package/tools/lsp_protocol.mjs +259 -0
  68. package/tools/lsp_symbols.mjs +418 -0
  69. package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
  70. package/tools/{repl.js → repl.mjs} +52 -41
  71. package/tools/{self.js → self.mjs} +56 -46
  72. package/tools/sourcemap.mjs +123 -0
  73. package/tools/test.mjs +152 -0
  74. package/tools/treeshake.mjs +400 -0
  75. package/tools/{utils.js → utils.mjs} +26 -23
  76. package/tools/{web_repl.js → web_repl.mjs} +15 -13
  77. package/tools/web_repl_export.mjs +93 -0
  78. package/tree-sitter/README.md +101 -0
  79. package/tree-sitter/grammar.js +992 -0
  80. package/tree-sitter/package.json +43 -0
  81. package/tree-sitter/queries/rapydscript/highlights.scm +232 -0
  82. package/tree-sitter/queries/rapydscript/indents.scm +64 -0
  83. package/tree-sitter/queries/rapydscript/injections.scm +14 -0
  84. package/tree-sitter/queries/rapydscript/locals.scm +108 -0
  85. package/tree-sitter/src/grammar.json +4976 -0
  86. package/tree-sitter/src/node-types.json +2901 -0
  87. package/tree-sitter/src/parser.c +196465 -0
  88. package/tree-sitter/src/scanner.c +294 -0
  89. package/tree-sitter/src/tree_sitter/alloc.h +54 -0
  90. package/tree-sitter/src/tree_sitter/array.h +330 -0
  91. package/tree-sitter/src/tree_sitter/parser.h +286 -0
  92. package/tree-sitter/test/corpus/chaining.txt +99 -0
  93. package/tree-sitter/test/corpus/classes.txt +147 -0
  94. package/tree-sitter/test/corpus/comprehensions.txt +155 -0
  95. package/tree-sitter/test/corpus/containers.txt +242 -0
  96. package/tree-sitter/test/corpus/control_flow.txt +361 -0
  97. package/tree-sitter/test/corpus/decorators.txt +64 -0
  98. package/tree-sitter/test/corpus/existential.txt +102 -0
  99. package/tree-sitter/test/corpus/functions.txt +293 -0
  100. package/tree-sitter/test/corpus/imports.txt +117 -0
  101. package/tree-sitter/test/corpus/literals.txt +135 -0
  102. package/tree-sitter/test/corpus/operators.txt +296 -0
  103. package/tree-sitter/test/corpus/statements.txt +189 -0
  104. package/tree-sitter/test/corpus/strings.txt +90 -0
  105. package/tree-sitter/test/corpus/subscripts.txt +227 -0
  106. package/tree-sitter/tree-sitter.json +36 -0
  107. package/web-repl/env.js +35 -23
  108. package/web-repl/main.js +8 -8
  109. package/bin/export +0 -75
  110. package/bin/web-repl-export +0 -102
  111. package/tools/cli.js +0 -523
  112. package/tools/compile.js +0 -184
  113. package/tools/compiler.js +0 -84
  114. 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.throws(def():
7
- RapydScript.parse(code, {'filename':code}).body[0]
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
- assrt.throws(def():
68
- RapydScript.parse('from _import_one import not_exported', {'basedir':test_path}).body[0]
69
- , /not exported/)
70
- assrt.throws(def():
71
- RapydScript.parse('import xxxx', {'basedir':test_path}).body[0]
72
- , /doesn't exist/)
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
- g = require('../tools/gettext.js')
7
-
8
- def gettext(code):
6
+ async def gettext(code):
9
7
  ans = {}
10
- g.gettext(ans, code, '<test>')
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(g.entry_to_string(msgid, catalog[msgid]), q)
15
+ assrt.equal(rs_entry_to_string(msgid, catalog[msgid]), q)
18
16
 
19
- test_string('a = _("one")', '#: <test>:1\nmsgid "one"\nmsgstr ""')
20
- test_string('a = _("one")\nb = gettext("one")', '#: <test>:1\n#: <test>:2\nmsgid "one"\nmsgstr ""')
21
- test_string('''a = _("""one
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 = require('../tools/msgfmt.js')
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
- install({'entries': {
67
- 'one':['ONE'],
68
- 'two':['1', '2'],
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
- assrt.equal(_('one'), 'ONE')
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.js')
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,108 +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
- # Imports
29
- err_test('import foo', 'unused-import', 1, 'foo')
30
- err_test('import foo.boo', 'unused-import', 1, 'foo')
31
- err_test('import foo as boo', 'unused-import', 1, 'boo')
32
- err_test('from x import foo', 'unused-import', 1, 'foo')
33
- err_test('from x import foo as boo', 'unused-import', 1, 'boo')
34
- ok_test('import foo\nfoo')
35
-
36
- # Function arguments
37
- ok_test('def f(a):\n return a')
38
- ok_test('def f(a=1):\n return a')
39
- ok_test('def f(*a):\n return a')
40
- ok_test('def f(**a):\n return a')
41
- ok_test('def f(a):\n return 1')
42
-
43
- # Extended slices
44
- ok_test('a = []; a[::2], a[1:-1]')
45
-
46
- # Top level unused ignored
47
- ok_test('a=1\ndef f():\n pass')
48
-
49
- # Destructuring assignment
50
- ok_test('def f():\n a, b = 1, 2; a, b')
51
- ok_test('def f():\n a = 1; b, c = a, 2; return b, c')
52
- ok_test('for x, (y, z) in [ [1, [2, 3]] ]:\n x + y + z')
53
- err_test('def f():\n a, b = 1, 1; return a', 'unused-local', 2, 'b')
54
-
55
- # Compound assignment
56
- err_test('a += 1', 'undef', 1, 'a')
57
- ok_test('def f():\n a = 1; a += 1')
58
-
59
- # Unused bindings
60
- err_test('def f():\n a=1', 'unused-local', 2, 'a')
61
- err_test('def f():\n def a():\n pass', 'unused-local', 2, 'a')
62
-
63
- # Undefined references
64
- err_test('f()', 'undef', 1, 'f')
65
- err_test('a', 'undef', 1, 'a')
66
- err_test('def f():\n a=1; return a\na', 'undef', 3, 'a')
67
-
68
- # Comprehensions
69
- ok_test('[x for x in [1,2]]')
70
- ok_test('[1 for x in [1,2] if x]')
71
- ok_test('[1 for x in [1,2]]')
72
- ok_test('def f():\n l=[1,2];[x for x in l]')
73
- ok_test('def f():\n l=[1,2];{x:True for x in l}')
74
- err_test('def f():\n [x for x in [1,2]]; return x', 'undef', 2, 'x')
75
-
76
- # Loops
77
- ok_test('def f():\n for x in "":\n pass\n return x')
78
- ok_test('def f():\n for x in "":\n x += 1\n')
79
- ok_test('def f():\n for x, y in "":\n pass\n return x, y')
80
- ok_test('for v"var i = 0; i < 1; i++":\n print(i)')
81
- err_test('def f():\n a = 1\n for a in "":\n a', 'loop-shadowed', 3, 'a', 2)
82
-
83
- # Semi-colons
84
- err_test('a=1;;a', 'extra-semicolon', 1, ';')
85
- err_test('a=1;', 'eol-semicolon', 1, ';')
86
-
87
- # Builtins
88
- for k in 'String Symbol this self window Map arguments print len range dir undefined'.split(' '):
89
- ok_test(k)
90
-
91
- # noqa
92
- ok_test('f() # noqa')
93
- ok_test('f() # noqa:undef')
94
- err_test('f() # noqa:xxx', 'undef')
95
-
96
- # Named func in branch
97
- err_test('if 1:\n def f():\n pass', 'func-in-branch', 2, 'f')
98
- err_test('if 1:\n pass\nelse:\n def f():\n pass', 'func-in-branch', 4, 'f')
99
- err_test('try:\n def f():\n pass\nexcept:\n pass', 'func-in-branch', 2, 'f')
100
- ok_test('if 1:\n a = def():\n pass')
101
-
102
- # Syntax errors
103
- err_test('def f(:\n pass', 'syntax-err', 1)
104
-
105
- # Functions
106
- ok_test('def f():\n pass\nf()')
107
-
108
- # Non-locals
109
- err_test('def a():\n x = 1\n def b():\n nonlocal x\n b()', 'unused-local', 2, 'x')
110
- ok_test('def a():\n x = 1\n def b():\n nonlocal x\n x\n b()')
111
- ok_test('def a():\n x = 1\n def b():\n nonlocal x\n x\n x = 2\n b()')
112
- ok_test('def a():\n x = 1\n def ():\n nonlocal x\n x = 1\n x')
113
- ok_test('nonlocal a\na()')
114
-
115
- # Define after use
116
- err_test('def g():\n f()\n f()\n def f():\n pass', 'def-after-use', 3, 'f')
117
-
118
- # Decorators
119
- ok_test('from x import y\n@y\ndef f():\n pass')
120
-
121
- # try/except
122
- ok_test('try:\n 1\nexcept Error as e:\n e')
123
-
124
- # Classes
125
- ok_test('''
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('''
126
129
  class A:
127
130
 
128
131
  h = j = 1
@@ -144,21 +147,23 @@ class B(A):
144
147
  def __init__(self):
145
148
  A.__init__(self, 'b')
146
149
  ''')
147
- err_test('class A:\n def a(self):\n a()', 'undef', 3, 'a')
148
- 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')
149
152
 
150
- # Object literals
151
- 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')
152
155
 
153
- # keyword arg values
154
- ok_test('def f():\n a=1\n f(b=a)')
155
- 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)')
156
159
 
157
- # with statement
158
- ok_test('''
160
+ # with statement
161
+ await ok_test('''
159
162
  def f():
160
163
  a = 1
161
164
  with a as b:
162
165
  b()
163
166
  ''')
164
- 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()