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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
###########################################################
|
|
2
|
+
# RapydScript Standard Library
|
|
3
|
+
# urllib.request — HTTP requests via the Fetch API
|
|
4
|
+
###########################################################
|
|
5
|
+
#
|
|
6
|
+
# Provides:
|
|
7
|
+
# urlopen(url[, data[, timeout]]) — open a URL; returns a Promise
|
|
8
|
+
#
|
|
9
|
+
# Usage (inside an async function):
|
|
10
|
+
# from urllib.request import urlopen
|
|
11
|
+
#
|
|
12
|
+
# async def fetch_json(url):
|
|
13
|
+
# resp = await urlopen(url)
|
|
14
|
+
# data = await resp.json()
|
|
15
|
+
# return data
|
|
16
|
+
#
|
|
17
|
+
# The response object exposes:
|
|
18
|
+
# .status -- HTTP status code (int)
|
|
19
|
+
# .reason -- HTTP status text (str)
|
|
20
|
+
# .url -- final URL after any redirects (str)
|
|
21
|
+
# .headers -- plain dict of lowercase header names → values
|
|
22
|
+
# .read() -- Promise → response body as a string
|
|
23
|
+
# .json() -- Promise → parsed JSON response body
|
|
24
|
+
# .close() -- no-op (for API compatibility)
|
|
25
|
+
#
|
|
26
|
+
# Raises urllib.error.HTTPError for non-2xx responses.
|
|
27
|
+
# Raises urllib.error.URLError for network/timeout failures.
|
|
28
|
+
#
|
|
29
|
+
# Note: urlopen() wraps the browser/Node Fetch API and therefore returns a
|
|
30
|
+
# Promise. It must be awaited inside an async function. There is no
|
|
31
|
+
# synchronous version because JavaScript has no synchronous HTTP.
|
|
32
|
+
|
|
33
|
+
from urllib.error import URLError, HTTPError
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def urlopen(url, data=None, timeout=None):
|
|
37
|
+
"""Open url, returning a Promise that resolves to a response object.
|
|
38
|
+
|
|
39
|
+
url -- the URL to open (string)
|
|
40
|
+
data -- if given, the request uses POST and sends data as the body
|
|
41
|
+
timeout -- seconds before an AbortController cancels the request (float)
|
|
42
|
+
|
|
43
|
+
On success the Promise resolves to an object with .status, .reason, .url,
|
|
44
|
+
.headers, .read(), .json(), and .close() (see module docstring).
|
|
45
|
+
On HTTP error (non-2xx status) the Promise rejects with HTTPError.
|
|
46
|
+
On network failure the Promise rejects with URLError.
|
|
47
|
+
"""
|
|
48
|
+
v"""
|
|
49
|
+
var _opts = { method: data != null ? 'POST' : 'GET' };
|
|
50
|
+
if (data != null) {
|
|
51
|
+
_opts.body = data;
|
|
52
|
+
_opts.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
53
|
+
}
|
|
54
|
+
var _abort = null;
|
|
55
|
+
if (timeout != null && typeof AbortController !== 'undefined') {
|
|
56
|
+
var _ctrl = new AbortController();
|
|
57
|
+
_opts.signal = _ctrl.signal;
|
|
58
|
+
_abort = setTimeout(function() { _ctrl.abort(); }, timeout * 1000);
|
|
59
|
+
}
|
|
60
|
+
return Promise.resolve()
|
|
61
|
+
.then(function() { return fetch(url, _opts); })
|
|
62
|
+
.then(function(resp) {
|
|
63
|
+
if (_abort) { clearTimeout(_abort); _abort = null; }
|
|
64
|
+
var hdrs = {};
|
|
65
|
+
if (resp.headers && typeof resp.headers.forEach === 'function') {
|
|
66
|
+
resp.headers.forEach(function(v, k) { hdrs[k] = v; });
|
|
67
|
+
}
|
|
68
|
+
if (!resp.ok) {
|
|
69
|
+
throw new HTTPError(resp.url, resp.status, resp.statusText, hdrs, null);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
status: resp.status,
|
|
73
|
+
reason: resp.statusText,
|
|
74
|
+
url: resp.url,
|
|
75
|
+
headers: hdrs,
|
|
76
|
+
read: function() { return resp.text(); },
|
|
77
|
+
json: function() { return resp.json(); },
|
|
78
|
+
close: function() {}
|
|
79
|
+
};
|
|
80
|
+
})
|
|
81
|
+
.catch(function(e) {
|
|
82
|
+
if (_abort) { clearTimeout(_abort); _abort = null; }
|
|
83
|
+
if (e instanceof HTTPError) throw e;
|
|
84
|
+
throw new URLError(String(e));
|
|
85
|
+
});
|
|
86
|
+
"""
|
package/src/lib/uuid.pyj
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
-
# globals: crypto
|
|
4
|
-
from __python__ import hash_literals
|
|
5
|
-
|
|
6
|
-
from encodings import hexlify, urlsafe_b64decode, urlsafe_b64encode
|
|
7
|
-
|
|
8
|
-
RFC_4122 = 1
|
|
9
|
-
|
|
10
|
-
if jstype(crypto) is 'object' and crypto.getRandomValues:
|
|
11
|
-
random_bytes = def (num):
|
|
12
|
-
ans = Uint8Array(num or 16)
|
|
13
|
-
crypto.getRandomValues(ans)
|
|
14
|
-
return ans
|
|
15
|
-
else:
|
|
16
|
-
random_bytes = def (num):
|
|
17
|
-
ans = Uint8Array(num or 16)
|
|
18
|
-
for i in range(ans.length):
|
|
19
|
-
ans[i] = Math.floor(Math.random() * 256)
|
|
20
|
-
return ans
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def uuid4_bytes():
|
|
24
|
-
data = random_bytes()
|
|
25
|
-
data[6] = 0b01000000 | (data[6] & 0b1111)
|
|
26
|
-
data[8] = (((data[8] >> 4) & 0b11 | 0b1000) << 4) | (data[8] & 0b1111)
|
|
27
|
-
return data
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def as_str():
|
|
31
|
-
h = this.hex
|
|
32
|
-
return h[:8] + '-' + h[8:12] + '-' + h[12:16] + '-' + h[16:20] + '-' + h[20:]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def uuid4():
|
|
36
|
-
b = uuid4_bytes()
|
|
37
|
-
return {
|
|
38
|
-
'hex': hexlify(b),
|
|
39
|
-
'bytes': b,
|
|
40
|
-
'variant': RFC_4122,
|
|
41
|
-
'version': 4,
|
|
42
|
-
'__str__': as_str,
|
|
43
|
-
'toString': as_str,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def num_to_string(numbers, alphabet, pad_to_length):
|
|
48
|
-
ans = v'[]'
|
|
49
|
-
alphabet_len = alphabet.length
|
|
50
|
-
numbers = Array.prototype.slice.call(numbers)
|
|
51
|
-
for v'var i = 0; i < numbers.length - 1; i++':
|
|
52
|
-
x = divmod(numbers[i], alphabet_len)
|
|
53
|
-
numbers[i] = x[0]
|
|
54
|
-
numbers[i+1] += x[1]
|
|
55
|
-
for v'var i = 0; i < numbers.length; i++':
|
|
56
|
-
number = numbers[i]
|
|
57
|
-
while number:
|
|
58
|
-
x = divmod(number, alphabet_len)
|
|
59
|
-
number = x[0]
|
|
60
|
-
ans.push(alphabet[x[1]])
|
|
61
|
-
if pad_to_length and pad_to_length > ans.length:
|
|
62
|
-
ans.push(alphabet[0].repeat(pad_to_length - ans.length))
|
|
63
|
-
return ans.join('')
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def short_uuid():
|
|
67
|
-
# A totally random uuid encoded using only URL and filename safe characters
|
|
68
|
-
return urlsafe_b64encode(random_bytes(), '')
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def short_uuid4():
|
|
72
|
-
# A uuid4 encoded using only URL and filename safe characters
|
|
73
|
-
return urlsafe_b64encode(uuid4_bytes(), '')
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def decode_short_uuid(val):
|
|
77
|
-
return urlsafe_b64decode(val + '==')
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
+
# globals: crypto
|
|
4
|
+
from __python__ import hash_literals
|
|
5
|
+
|
|
6
|
+
from encodings import hexlify, urlsafe_b64decode, urlsafe_b64encode
|
|
7
|
+
|
|
8
|
+
RFC_4122 = 1
|
|
9
|
+
|
|
10
|
+
if jstype(crypto) is 'object' and crypto.getRandomValues:
|
|
11
|
+
random_bytes = def (num):
|
|
12
|
+
ans = Uint8Array(num or 16)
|
|
13
|
+
crypto.getRandomValues(ans)
|
|
14
|
+
return ans
|
|
15
|
+
else:
|
|
16
|
+
random_bytes = def (num):
|
|
17
|
+
ans = Uint8Array(num or 16)
|
|
18
|
+
for i in range(ans.length):
|
|
19
|
+
ans[i] = Math.floor(Math.random() * 256)
|
|
20
|
+
return ans
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def uuid4_bytes():
|
|
24
|
+
data = random_bytes()
|
|
25
|
+
data[6] = 0b01000000 | (data[6] & 0b1111)
|
|
26
|
+
data[8] = (((data[8] >> 4) & 0b11 | 0b1000) << 4) | (data[8] & 0b1111)
|
|
27
|
+
return data
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def as_str():
|
|
31
|
+
h = this.hex
|
|
32
|
+
return h[:8] + '-' + h[8:12] + '-' + h[12:16] + '-' + h[16:20] + '-' + h[20:]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def uuid4():
|
|
36
|
+
b = uuid4_bytes()
|
|
37
|
+
return {
|
|
38
|
+
'hex': hexlify(b),
|
|
39
|
+
'bytes': b,
|
|
40
|
+
'variant': RFC_4122,
|
|
41
|
+
'version': 4,
|
|
42
|
+
'__str__': as_str,
|
|
43
|
+
'toString': as_str,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def num_to_string(numbers, alphabet, pad_to_length):
|
|
48
|
+
ans = v'[]'
|
|
49
|
+
alphabet_len = alphabet.length
|
|
50
|
+
numbers = Array.prototype.slice.call(numbers)
|
|
51
|
+
for v'var i = 0; i < numbers.length - 1; i++':
|
|
52
|
+
x = divmod(numbers[i], alphabet_len)
|
|
53
|
+
numbers[i] = x[0]
|
|
54
|
+
numbers[i+1] += x[1]
|
|
55
|
+
for v'var i = 0; i < numbers.length; i++':
|
|
56
|
+
number = numbers[i]
|
|
57
|
+
while number:
|
|
58
|
+
x = divmod(number, alphabet_len)
|
|
59
|
+
number = x[0]
|
|
60
|
+
ans.push(alphabet[x[1]])
|
|
61
|
+
if pad_to_length and pad_to_length > ans.length:
|
|
62
|
+
ans.push(alphabet[0].repeat(pad_to_length - ans.length))
|
|
63
|
+
return ans.join('')
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def short_uuid():
|
|
67
|
+
# A totally random uuid encoded using only URL and filename safe characters
|
|
68
|
+
return urlsafe_b64encode(random_bytes(), '')
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def short_uuid4():
|
|
72
|
+
# A uuid4 encoded using only URL and filename safe characters
|
|
73
|
+
return urlsafe_b64encode(uuid4_bytes(), '')
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def decode_short_uuid(val):
|
|
77
|
+
return urlsafe_b64decode(val + '==')
|
|
@@ -236,6 +236,7 @@ class ScopeBuilder {
|
|
|
236
236
|
return_type: opts.return_type || null,
|
|
237
237
|
source_module: opts.source_module || null,
|
|
238
238
|
original_name: opts.original_name || null,
|
|
239
|
+
is_bare_import: opts.is_bare_import || false,
|
|
239
240
|
});
|
|
240
241
|
scope.addSymbol(sym);
|
|
241
242
|
return sym;
|
|
@@ -362,8 +363,10 @@ class ScopeBuilder {
|
|
|
362
363
|
if (name) {
|
|
363
364
|
this._add_symbol({
|
|
364
365
|
name,
|
|
365
|
-
kind:
|
|
366
|
-
defined_at:
|
|
366
|
+
kind: 'import',
|
|
367
|
+
defined_at: pos_from_token(node.start),
|
|
368
|
+
source_module: node.key || null,
|
|
369
|
+
is_bare_import: true,
|
|
367
370
|
});
|
|
368
371
|
}
|
|
369
372
|
|
|
@@ -523,6 +523,32 @@ export class CompletionEngine {
|
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
// 1.1. Module member completions — `import X; X.attr`
|
|
527
|
+
// When the symbol is a bare import, parse the module source and
|
|
528
|
+
// expose its top-level symbols as dot-completions.
|
|
529
|
+
if (!scope_matched && obj_sym && obj_sym.is_bare_import && obj_sym.source_module) {
|
|
530
|
+
const mod_src = this._virtualFiles[obj_sym.source_module]
|
|
531
|
+
|| this._stdlibFiles[obj_sym.source_module];
|
|
532
|
+
if (mod_src) {
|
|
533
|
+
let mod_map;
|
|
534
|
+
try { mod_map = this._analyzer.analyze(mod_src, {}); } catch (_e) { /* ignore */ }
|
|
535
|
+
if (mod_map) {
|
|
536
|
+
const mod_frame = mod_map.frames.find(f => f.kind === 'module');
|
|
537
|
+
if (mod_frame) {
|
|
538
|
+
scope_matched = true;
|
|
539
|
+
for (const [name, sym] of mod_frame.symbols) {
|
|
540
|
+
if (!ctx.prefix || name.startsWith(ctx.prefix)) {
|
|
541
|
+
if (!seen.has(name)) {
|
|
542
|
+
seen.add(name);
|
|
543
|
+
items.push(symbol_to_item(sym, range, monacoKind, '0'));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
526
552
|
// 1.5. Built-in type members — list, str, dict, number.
|
|
527
553
|
// Used when inferred_class names a built-in type, not a user class.
|
|
528
554
|
if (!scope_matched && this._builtins && obj_sym && obj_sym.inferred_class) {
|
|
@@ -20,15 +20,20 @@ const MESSAGES = {
|
|
|
20
20
|
'def-after-use': 'The symbol "{name}" is defined (at line {line}) after it is used',
|
|
21
21
|
'dup-key': 'Duplicate key "{name}" in object literal',
|
|
22
22
|
'dup-method': 'The method "{name}" was defined previously at line: {line}',
|
|
23
|
+
'infinite-loop': 'This while loop may never exit (condition is always true and there is no await in the body)',
|
|
24
|
+
// type_enforcement diagnostics
|
|
25
|
+
'type-posonly-kwarg': '"{name}" is positional-only and cannot be passed as a keyword argument to {func}()',
|
|
26
|
+
'type-too-many-args': '{func}() takes at most {max} positional argument{plural} but {got} were given',
|
|
27
|
+
'type-missing-kwonly': '"{name}" is a required keyword-only argument of {func}() and must be passed by name',
|
|
23
28
|
};
|
|
24
29
|
|
|
25
30
|
// Built-in stdlib modules that are always available in RapydScript (bundled
|
|
26
31
|
// with the compiler from src/lib/). These should never produce 'Unknown module'
|
|
27
32
|
// errors regardless of what virtualFiles or stdlibFiles are configured.
|
|
28
33
|
export const STDLIB_MODULES = [
|
|
29
|
-
'abc', 'aes', 'collections', 'contextlib', 'copy', 'dataclasses', 'datetime', 'elementmaker', 'encodings', 'enum',
|
|
30
|
-
'functools', 'gettext', 'io', 'itertools', 'json', 'math', 'numpy', 'operator',
|
|
31
|
-
'pythonize', 'random', 're', 'react', 'traceback', 'typing', 'uuid',
|
|
34
|
+
'abc', 'aes', 'asyncio', 'base64', 'bisect', 'collections', 'contextlib', 'copy', 'csv', 'dataclasses', 'datetime', 'elementmaker', 'encodings', 'enum',
|
|
35
|
+
'functools', 'gettext', 'heapq', 'html', 'http', 'io', 'itertools', 'json', 'logging', 'math', 'numpy', 'operator',
|
|
36
|
+
'pythonize', 'random', 're', 'react', 'string', 'textwrap', 'traceback', 'typing', 'urllib', 'uuid',
|
|
32
37
|
// Pseudo-modules for language feature flags (from __python__ import ...)
|
|
33
38
|
'__python__', '__builtins__',
|
|
34
39
|
];
|
|
@@ -497,6 +502,42 @@ function Linter(RS, toplevel, code, builtins, knownModules) {
|
|
|
497
502
|
if (node.alias) this.add_binding(node.alias.name);
|
|
498
503
|
};
|
|
499
504
|
|
|
505
|
+
this.handle_while = function() {
|
|
506
|
+
const node = this.current_node;
|
|
507
|
+
const cond = node.condition;
|
|
508
|
+
const RS = this.RS;
|
|
509
|
+
|
|
510
|
+
const is_trivially_true = (
|
|
511
|
+
(cond instanceof RS.AST_True) ||
|
|
512
|
+
(cond instanceof RS.AST_Number && cond.value !== 0)
|
|
513
|
+
);
|
|
514
|
+
if (!is_trivially_true) return;
|
|
515
|
+
|
|
516
|
+
let has_await = false;
|
|
517
|
+
const await_finder = {
|
|
518
|
+
_visit: function(n, cont) {
|
|
519
|
+
if (n instanceof RS.AST_Await) { has_await = true; return; }
|
|
520
|
+
if (cont) cont();
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
if (node.body) node.body._walk(await_finder);
|
|
524
|
+
|
|
525
|
+
if (!has_await) {
|
|
526
|
+
const sline = node.start ? node.start.line : 1;
|
|
527
|
+
const scol = node.start ? node.start.col : 0;
|
|
528
|
+
const eline = (node.condition && node.condition.end) ? node.condition.end.line : sline;
|
|
529
|
+
const ecol = (node.condition && node.condition.end) ? node.condition.end.col : scol;
|
|
530
|
+
this.messages.push({
|
|
531
|
+
start_line: sline, start_col: scol,
|
|
532
|
+
end_line: eline, end_col: ecol,
|
|
533
|
+
ident: 'infinite-loop',
|
|
534
|
+
message: MESSAGES['infinite-loop'],
|
|
535
|
+
level: WARN,
|
|
536
|
+
name: '',
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
500
541
|
// The visitor function called by toplevel.walk()
|
|
501
542
|
this._visit = function(node, cont) {
|
|
502
543
|
if (node.lint_visited) return;
|
|
@@ -530,6 +571,7 @@ function Linter(RS, toplevel, code, builtins, knownModules) {
|
|
|
530
571
|
else if (node instanceof RS.AST_EmptyStatement) this.handle_empty_statement();
|
|
531
572
|
else if (node instanceof RS.AST_WithClause) this.handle_with_clause();
|
|
532
573
|
else if (node instanceof RS.AST_Object) this.handle_object_literal();
|
|
574
|
+
else if (node instanceof RS.AST_While) this.handle_while();
|
|
533
575
|
|
|
534
576
|
if (node instanceof RS.AST_Scope) this.handle_scope();
|
|
535
577
|
|
|
@@ -580,6 +622,143 @@ function Linter(RS, toplevel, code, builtins, knownModules) {
|
|
|
580
622
|
};
|
|
581
623
|
}
|
|
582
624
|
|
|
625
|
+
// ---------------------------------------------------------------------------
|
|
626
|
+
// Type-enforcement static checker
|
|
627
|
+
// Runs a two-pass walk when any function in the file has type_enforce=true:
|
|
628
|
+
// Pass 1 — collect signatures of enforced functions.
|
|
629
|
+
// Pass 2 — inspect call sites and report violations.
|
|
630
|
+
// ---------------------------------------------------------------------------
|
|
631
|
+
|
|
632
|
+
function check_type_enforcement(RS, toplevel, messages) {
|
|
633
|
+
// Pass 1: collect signatures of functions compiled with type_enforce=true
|
|
634
|
+
const sigs = Object.create(null);
|
|
635
|
+
|
|
636
|
+
const col1 = {
|
|
637
|
+
_visit: function(node, cont) {
|
|
638
|
+
if (node instanceof RS.AST_Lambda && node.type_enforce && node.name) {
|
|
639
|
+
const a = node.argnames;
|
|
640
|
+
const is_m = node instanceof RS.AST_Method;
|
|
641
|
+
const off = (is_m && !node.static) ? 1 : 0;
|
|
642
|
+
const posonly_names = [];
|
|
643
|
+
const kwonly_req = [];
|
|
644
|
+
let max_pos = 0;
|
|
645
|
+
const defs = (a.defaults) || {};
|
|
646
|
+
for (let i = off; i < a.length; i++) {
|
|
647
|
+
const arg = a[i];
|
|
648
|
+
if (arg.posonly) posonly_names.push(arg.name);
|
|
649
|
+
if (arg.kwonly) {
|
|
650
|
+
if (!has_prop(defs, arg.name)) kwonly_req.push(arg.name);
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
max_pos++;
|
|
654
|
+
}
|
|
655
|
+
sigs[node.name.name] = {
|
|
656
|
+
node,
|
|
657
|
+
posonly_names,
|
|
658
|
+
kwonly_req,
|
|
659
|
+
max_pos,
|
|
660
|
+
has_starargs: !!a.starargs,
|
|
661
|
+
has_kwargs: !!a.kwargs,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
if (cont) cont();
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
toplevel.walk(col1);
|
|
668
|
+
|
|
669
|
+
if (Object.keys(sigs).length === 0) return;
|
|
670
|
+
|
|
671
|
+
// Pass 2: check call sites
|
|
672
|
+
const col2 = {
|
|
673
|
+
_visit: function(node, cont) {
|
|
674
|
+
if (node instanceof RS.AST_BaseCall) {
|
|
675
|
+
let fname = null;
|
|
676
|
+
const expr = node.expression;
|
|
677
|
+
if (expr instanceof RS.AST_SymbolRef) fname = expr.name;
|
|
678
|
+
else if (expr instanceof RS.AST_Dot) fname = expr.property;
|
|
679
|
+
|
|
680
|
+
if (fname && has_prop(sigs, fname)) {
|
|
681
|
+
const sig = sigs[fname];
|
|
682
|
+
const args = node.args;
|
|
683
|
+
const pos_count = args ? args.length : 0;
|
|
684
|
+
const named_kw = (args && args.kwargs) ? args.kwargs : [];
|
|
685
|
+
const kw_expand = args && args.kwarg_items && args.kwarg_items.length > 0;
|
|
686
|
+
|
|
687
|
+
// Positional-only args passed as named kwargs
|
|
688
|
+
sig.posonly_names.forEach(argname => {
|
|
689
|
+
for (let ki = 0; ki < named_kw.length; ki++) {
|
|
690
|
+
const knode = named_kw[ki][0];
|
|
691
|
+
const kname = knode.name || (knode.value !== undefined ? String(knode.value) : null);
|
|
692
|
+
if (kname === argname) {
|
|
693
|
+
const msg = MESSAGES['type-posonly-kwarg']
|
|
694
|
+
.replace('{name}', argname)
|
|
695
|
+
.replace('{func}', fname);
|
|
696
|
+
messages.push({
|
|
697
|
+
ident: 'type-posonly-kwarg',
|
|
698
|
+
message: msg,
|
|
699
|
+
level: ERROR,
|
|
700
|
+
name: argname,
|
|
701
|
+
start_line: knode.start ? knode.start.line : 1,
|
|
702
|
+
start_col: knode.start ? knode.start.col : 0,
|
|
703
|
+
end_line: knode.end ? knode.end.line : undefined,
|
|
704
|
+
end_col: knode.end ? knode.end.col : undefined,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
// Too many positional args (skip when *args or **expansion present)
|
|
711
|
+
if (!sig.has_starargs && !kw_expand && pos_count > sig.max_pos) {
|
|
712
|
+
const plural = sig.max_pos === 1 ? '' : 's';
|
|
713
|
+
const msg = MESSAGES['type-too-many-args']
|
|
714
|
+
.replace('{func}', fname)
|
|
715
|
+
.replace('{max}', String(sig.max_pos))
|
|
716
|
+
.replace('{plural}', plural)
|
|
717
|
+
.replace('{got}', String(pos_count));
|
|
718
|
+
messages.push({
|
|
719
|
+
ident: 'type-too-many-args',
|
|
720
|
+
message: msg,
|
|
721
|
+
level: ERROR,
|
|
722
|
+
name: fname,
|
|
723
|
+
start_line: node.start ? node.start.line : 1,
|
|
724
|
+
start_col: node.start ? node.start.col : 0,
|
|
725
|
+
end_line: node.end ? node.end.line : undefined,
|
|
726
|
+
end_col: node.end ? node.end.col : undefined,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Required keyword-only args not provided (skip when **expansion present)
|
|
731
|
+
if (!kw_expand) {
|
|
732
|
+
sig.kwonly_req.forEach(argname => {
|
|
733
|
+
const provided = named_kw.some(pair => {
|
|
734
|
+
const kn = pair[0].name || (pair[0].value !== undefined ? String(pair[0].value) : null);
|
|
735
|
+
return kn === argname;
|
|
736
|
+
});
|
|
737
|
+
if (!provided) {
|
|
738
|
+
const msg = MESSAGES['type-missing-kwonly']
|
|
739
|
+
.replace('{name}', argname)
|
|
740
|
+
.replace('{func}', fname);
|
|
741
|
+
messages.push({
|
|
742
|
+
ident: 'type-missing-kwonly',
|
|
743
|
+
message: msg,
|
|
744
|
+
level: ERROR,
|
|
745
|
+
name: argname,
|
|
746
|
+
start_line: node.start ? node.start.line : 1,
|
|
747
|
+
start_col: node.start ? node.start.col : 0,
|
|
748
|
+
end_line: node.end ? node.end.line : undefined,
|
|
749
|
+
end_col: node.end ? node.end.col : undefined,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (cont) cont();
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
toplevel.walk(col2);
|
|
760
|
+
}
|
|
761
|
+
|
|
583
762
|
// ---------------------------------------------------------------------------
|
|
584
763
|
// Convert lint message → Monaco IMarkerData
|
|
585
764
|
// markerSeverity should be { Error, Warning, Info, Hint } numeric values
|
|
@@ -709,6 +888,26 @@ export class Diagnostics {
|
|
|
709
888
|
toplevel.walk(linter);
|
|
710
889
|
messages = linter.resolve(noqa);
|
|
711
890
|
|
|
891
|
+
// --- Type enforcement static checks ---
|
|
892
|
+
// Run when type_enforcement is active at the file level (either via
|
|
893
|
+
// pythonFlags constructor arg or from __python__ import type_enforcement).
|
|
894
|
+
const te_active =
|
|
895
|
+
(this._scoped_flags && this._scoped_flags.type_enforcement) ||
|
|
896
|
+
(toplevel.scoped_flags && toplevel.scoped_flags.type_enforcement);
|
|
897
|
+
if (te_active) {
|
|
898
|
+
const te_messages = [];
|
|
899
|
+
check_type_enforcement(RS, toplevel, te_messages);
|
|
900
|
+
// Filter noqa, then convert to markers inline
|
|
901
|
+
te_messages.forEach(m => {
|
|
902
|
+
if (noqa && has_prop(noqa, m.ident)) return;
|
|
903
|
+
messages.push(m);
|
|
904
|
+
});
|
|
905
|
+
messages.sort((a, b) => {
|
|
906
|
+
const dl = (a.start_line || 0) - (b.start_line || 0);
|
|
907
|
+
return dl !== 0 ? dl : (a.start_col || 0) - (b.start_col || 0);
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
712
911
|
return messages.map(m => to_marker(m, markerSeverity));
|
|
713
912
|
}
|
|
714
913
|
}
|