rapydscript-ns 0.9.2 → 0.9.4

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 (88) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/PYTHON_GAPS.md +352 -0
  3. package/README.md +176 -32
  4. package/TODO.md +1 -128
  5. package/bin/rapydscript +70 -70
  6. package/language-service/index.js +242 -11
  7. package/memory/project_string_impl.md +43 -0
  8. package/package.json +1 -1
  9. package/release/baselib-plain-pretty.js +248 -38
  10. package/release/baselib-plain-ugly.js +8 -8
  11. package/release/compiler.js +778 -277
  12. package/release/signatures.json +30 -30
  13. package/src/ast.pyj +10 -1
  14. package/src/baselib-builtins.pyj +56 -2
  15. package/src/baselib-containers.pyj +25 -1
  16. package/src/baselib-errors.pyj +7 -3
  17. package/src/baselib-internal.pyj +51 -6
  18. package/src/baselib-str.pyj +18 -5
  19. package/src/lib/asyncio.pyj +534 -0
  20. package/src/lib/base64.pyj +399 -0
  21. package/src/lib/bisect.pyj +73 -0
  22. package/src/lib/collections.pyj +228 -4
  23. package/src/lib/csv.pyj +494 -0
  24. package/src/lib/heapq.pyj +98 -0
  25. package/src/lib/html.pyj +382 -0
  26. package/src/lib/http/__init__.pyj +98 -0
  27. package/src/lib/http/client.pyj +304 -0
  28. package/src/lib/http/cookies.pyj +236 -0
  29. package/src/lib/logging.pyj +672 -0
  30. package/src/lib/pprint.pyj +455 -0
  31. package/src/lib/pythonize.pyj +20 -20
  32. package/src/lib/statistics.pyj +0 -0
  33. package/src/lib/string.pyj +357 -0
  34. package/src/lib/textwrap.pyj +329 -0
  35. package/src/lib/urllib/__init__.pyj +14 -0
  36. package/src/lib/urllib/error.pyj +66 -0
  37. package/src/lib/urllib/parse.pyj +475 -0
  38. package/src/lib/urllib/request.pyj +86 -0
  39. package/src/monaco-language-service/analyzer.js +5 -2
  40. package/src/monaco-language-service/completions.js +26 -0
  41. package/src/monaco-language-service/diagnostics.js +203 -4
  42. package/src/monaco-language-service/scope.js +1 -0
  43. package/src/output/codegen.pyj +4 -1
  44. package/src/output/functions.pyj +152 -6
  45. package/src/output/loops.pyj +17 -2
  46. package/src/output/modules.pyj +1 -1
  47. package/src/output/operators.pyj +15 -0
  48. package/src/output/stream.pyj +0 -1
  49. package/src/parse.pyj +108 -24
  50. package/src/tokenizer.pyj +19 -3
  51. package/test/async_generators.pyj +144 -0
  52. package/test/asyncio.pyj +307 -0
  53. package/test/base64.pyj +202 -0
  54. package/test/baselib.pyj +23 -0
  55. package/test/bisect.pyj +178 -0
  56. package/test/chainmap.pyj +185 -0
  57. package/test/csv.pyj +405 -0
  58. package/test/float_special.pyj +64 -0
  59. package/test/heapq.pyj +174 -0
  60. package/test/html.pyj +212 -0
  61. package/test/http.pyj +259 -0
  62. package/test/imports.pyj +79 -72
  63. package/test/logging.pyj +356 -0
  64. package/test/long.pyj +130 -0
  65. package/test/parenthesized_with.pyj +141 -0
  66. package/test/pprint.pyj +232 -0
  67. package/test/python_compat.pyj +3 -5
  68. package/test/python_modulo.pyj +76 -0
  69. package/test/python_modulo_off.pyj +21 -0
  70. package/test/statistics.pyj +224 -0
  71. package/test/str.pyj +14 -0
  72. package/test/string.pyj +245 -0
  73. package/test/textwrap.pyj +172 -0
  74. package/test/type_display.pyj +48 -0
  75. package/test/type_enforcement.pyj +164 -0
  76. package/test/unit/index.js +94 -6
  77. package/test/unit/language-service-completions.js +121 -0
  78. package/test/unit/language-service-scope.js +32 -0
  79. package/test/unit/language-service.js +190 -5
  80. package/test/unit/run-language-service.js +17 -3
  81. package/test/unit/web-repl.js +2401 -13
  82. package/test/urllib.pyj +193 -0
  83. package/tools/compile.js +1 -1
  84. package/tools/embedded_compiler.js +7 -7
  85. package/tools/export.js +4 -2
  86. package/web-repl/main.js +1 -1
  87. package/web-repl/rapydscript.js +7 -5
  88. package/test/omit_function_metadata.pyj +0 -20
@@ -0,0 +1,66 @@
1
+ ###########################################################
2
+ # RapydScript Standard Library
3
+ # urllib.error — Exception classes for urllib
4
+ ###########################################################
5
+ #
6
+ # Provides:
7
+ # URLError -- base exception for URL-related errors
8
+ # HTTPError -- raised for HTTP error responses (extends URLError)
9
+ #
10
+ # Usage:
11
+ # from urllib.error import URLError, HTTPError
12
+ #
13
+ # try:
14
+ # resp = await urlopen('https://example.com/missing')
15
+ # except HTTPError as e:
16
+ # print(e.code, e.msg)
17
+ # except URLError as e:
18
+ # print('Network error:', e.reason)
19
+
20
+
21
+ class URLError(Exception):
22
+ """Exception raised when urlopen() cannot fulfil a request.
23
+
24
+ Attributes:
25
+ reason -- the error reason (string or exception)
26
+ filename -- the URL that triggered the error, if available
27
+ """
28
+
29
+ def __init__(self, reason, filename=None):
30
+ self.reason = reason
31
+ self.filename = filename
32
+ Exception.__init__(self, str(reason))
33
+
34
+ def __str__(self):
35
+ return '<urlopen error ' + str(self.reason) + '>'
36
+
37
+
38
+ class HTTPError(URLError):
39
+ """Exception raised for HTTP error responses.
40
+
41
+ Attributes:
42
+ url -- the URL that triggered the error
43
+ code -- HTTP status code (e.g. 404)
44
+ msg -- HTTP status text (e.g. 'Not Found')
45
+ hdrs -- dict of response headers
46
+ fp -- file-like object for the response body (may be None)
47
+ """
48
+
49
+ def __init__(self, url, code, msg, hdrs, fp):
50
+ self.url = url
51
+ self.code = code
52
+ self.msg = msg
53
+ self.hdrs = hdrs
54
+ self.fp = fp
55
+ URLError.__init__(self, msg)
56
+
57
+ def getcode(self):
58
+ """Return the HTTP status code."""
59
+ return self.code
60
+
61
+ def geturl(self):
62
+ """Return the URL that triggered the error."""
63
+ return self.url
64
+
65
+ def __str__(self):
66
+ return 'HTTP Error ' + str(self.code) + ': ' + str(self.msg)
@@ -0,0 +1,475 @@
1
+ ###########################################################
2
+ # RapydScript Standard Library
3
+ # urllib.parse — URL parsing and encoding utilities
4
+ ###########################################################
5
+ #
6
+ # Provides:
7
+ # quote(s[, safe[, encoding[, errors]]]) → percent-encode a string
8
+ # unquote(s[, encoding[, errors]]) → decode %xx sequences
9
+ # quote_plus(s[, safe[, encoding[, errors]]]) → like quote, spaces → '+'
10
+ # unquote_plus(s[, encoding[, errors]]) → like unquote, '+' → space
11
+ # urlencode(query[, doseq]) → encode dict/pairs as query
12
+ # urlsplit(url[, scheme[, allow_fragments]]) → SplitResult
13
+ # urlunsplit(components) → string
14
+ # urlparse(url[, scheme[, allow_fragments]]) → ParseResult
15
+ # urlunparse(components) → string
16
+ # urljoin(base, url[, allow_fragments]) → string
17
+ # parse_qs(qs[, keep_blank_values, ...]) → dict of lists
18
+ # parse_qsl(qs[, keep_blank_values, ...]) → list of [key, value] pairs
19
+ # ParseResult, SplitResult — result classes
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Internal JS helpers
24
+ # ---------------------------------------------------------------------------
25
+
26
+ v"""
27
+ // -- quote helpers -----------------------------------------------------------
28
+
29
+ // RFC 3986 unreserved characters: A-Z a-z 0-9 - _ . ~
30
+ var _uparse_unreserved = /^[A-Za-z0-9\-_.~]$/;
31
+
32
+ // Characters encodeURIComponent leaves unencoded that Python's quote() encodes.
33
+ // ! * ' ( ) are sub-delimiters; only ~ is truly unreserved and left alone.
34
+ var _uparse_extra = {'!': '%21', "'": '%27', '(': '%28', ')': '%29', '*': '%2A'};
35
+
36
+ // Percent-encode a string. Chars in `safe` and RFC 3986 unreserved chars
37
+ // are left intact; all others are encoded as UTF-8 percent-sequences.
38
+ // Uses Array.from to iterate code points so surrogate pairs encode correctly.
39
+ function _uparse_quote(s, safe) {
40
+ if (safe === null || safe === undefined) safe = '/';
41
+ s = String(s);
42
+ var chars = typeof Array.from === 'function' ? Array.from(s) : s.split('');
43
+ var out = '';
44
+ for (var i = 0; i < chars.length; i++) {
45
+ var c = chars[i];
46
+ if (safe.indexOf(c) >= 0 || _uparse_unreserved.test(c)) {
47
+ out += c;
48
+ } else if (_uparse_extra[c] !== undefined) {
49
+ out += _uparse_extra[c];
50
+ } else {
51
+ out += encodeURIComponent(c);
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+
57
+ // Decode %xx sequences in a URL component. Tolerates malformed sequences.
58
+ function _uparse_unquote(s) {
59
+ s = String(s);
60
+ return s.replace(/%([0-9A-Fa-f]{2})/g, function(m) {
61
+ try { return decodeURIComponent(m); } catch(e) { return m; }
62
+ });
63
+ }
64
+
65
+ // -- urlencode ---------------------------------------------------------------
66
+
67
+ function _uparse_urlencode(query, doseq) {
68
+ var pairs = [];
69
+ var items;
70
+ if (Array.isArray(query)) {
71
+ items = query;
72
+ } else if (query && query.jsmap instanceof Map) {
73
+ // ρσ_dict (dict_literals mode)
74
+ items = [];
75
+ query.jsmap.forEach(function(v, k) { items.push([k, v]); });
76
+ } else {
77
+ items = Object.keys(query).map(function(k) { return [k, query[k]]; });
78
+ }
79
+ for (var i = 0; i < items.length; i++) {
80
+ var k = _uparse_quote(String(items[i][0]), '');
81
+ var v = items[i][1];
82
+ if (doseq && Array.isArray(v)) {
83
+ for (var j = 0; j < v.length; j++) {
84
+ pairs.push(k + '=' + _uparse_quote(String(v[j]), ''));
85
+ }
86
+ } else {
87
+ pairs.push(k + '=' + _uparse_quote(String(v), ''));
88
+ }
89
+ }
90
+ return pairs.join('&');
91
+ }
92
+
93
+ // -- URL parsing -------------------------------------------------------------
94
+
95
+ // RFC 3986 URI-reference regex (Appendix B):
96
+ // ^(scheme:)? (//netloc)? (path) (?query)? (#fragment)?
97
+ var _uparse_URI_RE = /^(?:([a-zA-Z][a-zA-Z0-9+\-.]*):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/;
98
+
99
+ // Parse a URL string into its five RFC 3986 components.
100
+ function _uparse_split(urlstring, scheme) {
101
+ if (!urlstring) return {scheme: '', netloc: '', path: '', query: '', fragment: ''};
102
+ var m = _uparse_URI_RE.exec(urlstring);
103
+ if (!m) return {scheme: '', netloc: '', path: urlstring, query: '', fragment: ''};
104
+ return {
105
+ scheme: m[1] || scheme || '',
106
+ netloc: m[2] !== undefined ? m[2] : '',
107
+ path: m[3] || '',
108
+ query: m[4] !== undefined ? m[4] : '',
109
+ fragment: m[5] !== undefined ? m[5] : ''
110
+ };
111
+ }
112
+
113
+ // -- netloc decomposition ---------------------------------------------------
114
+
115
+ // Parse a netloc string into {hostname, port, username, password}.
116
+ // Uses native JS toLowerCase() to avoid relying on String.prototype.lower
117
+ // (which may not be installed in restricted vm contexts).
118
+ function _uparse_netloc_parts(netloc) {
119
+ if (!netloc) return {hostname: null, port: null, username: null, password: null};
120
+ var username = null, password = null;
121
+ var host = netloc;
122
+ var at = netloc.indexOf('@');
123
+ if (at >= 0) {
124
+ var ui = netloc.slice(0, at);
125
+ host = netloc.slice(at + 1);
126
+ var uc = ui.indexOf(':');
127
+ username = uc >= 0 ? ui.slice(0, uc) : ui;
128
+ password = uc >= 0 ? ui.slice(uc + 1) : null;
129
+ }
130
+ var hostname = null, port = null;
131
+ if (host.charAt(0) === '[') {
132
+ // IPv6 bracketed address: [::1] or [::1]:8080
133
+ var eb = host.indexOf(']');
134
+ hostname = eb >= 0 ? host.slice(0, eb + 1).toLowerCase() : host.toLowerCase();
135
+ var after = eb >= 0 ? host.slice(eb + 1) : '';
136
+ if (after.charAt(0) === ':') {
137
+ port = parseInt(after.slice(1), 10);
138
+ if (isNaN(port)) port = null;
139
+ }
140
+ } else {
141
+ var lc = host.lastIndexOf(':');
142
+ if (lc >= 0) {
143
+ var maybe_port = parseInt(host.slice(lc + 1), 10);
144
+ if (!isNaN(maybe_port)) {
145
+ hostname = host.slice(0, lc).toLowerCase() || null;
146
+ port = maybe_port;
147
+ } else {
148
+ hostname = host.toLowerCase() || null;
149
+ }
150
+ } else {
151
+ hostname = host ? host.toLowerCase() : null;
152
+ }
153
+ }
154
+ return {hostname: hostname, port: port, username: username, password: password};
155
+ }
156
+
157
+ // -- parse_qsl ---------------------------------------------------------------
158
+
159
+ function _uparse_parse_qsl(qs, keep_blank) {
160
+ var result = [];
161
+ if (!qs) return result;
162
+ if (qs.charAt(0) === '?') qs = qs.slice(1);
163
+ var parts = qs.split('&');
164
+ for (var i = 0; i < parts.length; i++) {
165
+ var pair = parts[i];
166
+ if (!pair) continue;
167
+ var idx = pair.indexOf('=');
168
+ var key, val;
169
+ if (idx < 0) {
170
+ key = pair; val = '';
171
+ } else {
172
+ key = pair.slice(0, idx);
173
+ val = pair.slice(idx + 1);
174
+ }
175
+ key = _uparse_unquote(key.split('+').join(' '));
176
+ val = _uparse_unquote(val.split('+').join(' '));
177
+ if (val || keep_blank) result.push([key, val]);
178
+ }
179
+ return result;
180
+ }
181
+
182
+ // -- urljoin -----------------------------------------------------------------
183
+
184
+ // Remove RFC 3986 dot segments from a path (Section 5.2.4).
185
+ function _uparse_remove_dots(path) {
186
+ var inp = path, segs = [];
187
+ while (inp.length) {
188
+ if (inp.slice(0, 3) === '../' || inp.slice(0, 2) === './') {
189
+ inp = inp.replace(/^\.\.?\//,'');
190
+ } else if (inp === '/.' || inp.slice(0, 3) === '/./') {
191
+ inp = '/' + inp.slice(inp.charAt(2) === '/' ? 3 : 2);
192
+ } else if (inp === '/..' || inp.slice(0, 4) === '/../') {
193
+ inp = '/' + inp.slice(inp.slice(0, 4) === '/../' ? 4 : 3);
194
+ segs.pop();
195
+ } else if (inp === '.' || inp === '..') {
196
+ inp = '';
197
+ } else {
198
+ var end = inp.indexOf('/', inp.charAt(0) === '/' ? 1 : 0);
199
+ if (end < 0) end = inp.length;
200
+ segs.push(inp.slice(0, end));
201
+ inp = inp.slice(end);
202
+ }
203
+ }
204
+ return segs.join('');
205
+ }
206
+
207
+ // Resolve a relative URL against a base URL (RFC 3986 Section 5.2.2).
208
+ // Uses the browser/Node URL constructor when available for correctness,
209
+ // and falls back to the RFC 3986 algorithm in restricted vm contexts.
210
+ var _uparse_URLcls = (function() {
211
+ if (typeof URL !== 'undefined') return URL;
212
+ if (typeof require !== 'undefined') { try { return require('url').URL; } catch(e) {} }
213
+ return null;
214
+ })();
215
+
216
+ function _uparse_urljoin(base, url, allow_fragments) {
217
+ if (_uparse_URLcls) {
218
+ try {
219
+ var u = new _uparse_URLcls(url, base);
220
+ if (!allow_fragments) u.hash = '';
221
+ return u.href;
222
+ } catch(e) {}
223
+ }
224
+ // RFC 3986 Section 5.2.2 reference resolution
225
+ var R = _uparse_split(url, '');
226
+ var B = _uparse_split(base, '');
227
+ var Ts, Tn, Tp, Tq, Tf;
228
+ if (R.scheme) {
229
+ Ts = R.scheme; Tn = R.netloc;
230
+ Tp = _uparse_remove_dots(R.path); Tq = R.query;
231
+ } else {
232
+ if (R.netloc !== '') {
233
+ Tn = R.netloc;
234
+ Tp = _uparse_remove_dots(R.path); Tq = R.query;
235
+ } else {
236
+ if (!R.path) {
237
+ Tp = B.path;
238
+ Tq = R.query !== '' ? R.query : B.query;
239
+ } else {
240
+ if (R.path.charAt(0) === '/') {
241
+ Tp = _uparse_remove_dots(R.path);
242
+ } else {
243
+ var bdir = (B.netloc && !B.path)
244
+ ? '/'
245
+ : B.path.slice(0, B.path.lastIndexOf('/') + 1);
246
+ Tp = _uparse_remove_dots(bdir + R.path);
247
+ }
248
+ Tq = R.query;
249
+ }
250
+ Tn = B.netloc;
251
+ }
252
+ Ts = B.scheme;
253
+ }
254
+ Tf = allow_fragments ? R.fragment : '';
255
+ var out = Ts ? Ts + ':' : '';
256
+ if (Tn !== '') out += '//' + Tn;
257
+ out += Tp;
258
+ if (Tq !== '') out += '?' + Tq;
259
+ if (Tf !== '') out += '#' + Tf;
260
+ return out;
261
+ }
262
+ """
263
+
264
+
265
+ # ---------------------------------------------------------------------------
266
+ # Result classes
267
+ # ---------------------------------------------------------------------------
268
+
269
+ class SplitResult:
270
+ """Result of urlsplit(): scheme, netloc, path, query, fragment."""
271
+
272
+ def __init__(self, scheme, netloc, path, query, fragment):
273
+ self.scheme = scheme
274
+ self.netloc = netloc
275
+ self.path = path
276
+ self.query = query
277
+ self.fragment = fragment
278
+ # Derived auth/host attributes computed via JS helper (avoids
279
+ # relying on String.prototype.lower in restricted vm contexts).
280
+ _auth = _uparse_netloc_parts(netloc)
281
+ self.hostname = _auth['hostname']
282
+ self.port = _auth['port']
283
+ self.username = _auth['username']
284
+ self.password = _auth['password']
285
+
286
+ def geturl(self):
287
+ """Reconstruct the original URL from its components."""
288
+ return urlunsplit((self.scheme, self.netloc, self.path, self.query, self.fragment))
289
+
290
+ def __repr__(self):
291
+ return ('SplitResult(scheme=' + repr(self.scheme) +
292
+ ', netloc=' + repr(self.netloc) +
293
+ ', path=' + repr(self.path) +
294
+ ', query=' + repr(self.query) +
295
+ ', fragment=' + repr(self.fragment) + ')')
296
+
297
+
298
+ class ParseResult:
299
+ """Result of urlparse(): scheme, netloc, path, params, query, fragment."""
300
+
301
+ def __init__(self, scheme, netloc, path, params, query, fragment):
302
+ self.scheme = scheme
303
+ self.netloc = netloc
304
+ self.path = path
305
+ self.params = params
306
+ self.query = query
307
+ self.fragment = fragment
308
+ # Derived auth/host attributes computed via JS helper (avoids
309
+ # relying on String.prototype.lower in restricted vm contexts).
310
+ _auth = _uparse_netloc_parts(netloc)
311
+ self.hostname = _auth['hostname']
312
+ self.port = _auth['port']
313
+ self.username = _auth['username']
314
+ self.password = _auth['password']
315
+
316
+ def geturl(self):
317
+ """Reconstruct the original URL from its components."""
318
+ return urlunparse((self.scheme, self.netloc, self.path, self.params, self.query, self.fragment))
319
+
320
+ def __repr__(self):
321
+ return ('ParseResult(scheme=' + repr(self.scheme) +
322
+ ', netloc=' + repr(self.netloc) +
323
+ ', path=' + repr(self.path) +
324
+ ', params=' + repr(self.params) +
325
+ ', query=' + repr(self.query) +
326
+ ', fragment=' + repr(self.fragment) + ')')
327
+
328
+
329
+ # ---------------------------------------------------------------------------
330
+ # Public API
331
+ # ---------------------------------------------------------------------------
332
+
333
+ def quote(string, safe='/', encoding=None, errors=None):
334
+ """Percent-encode a string for safe inclusion in a URL.
335
+
336
+ Characters listed in safe (default '/') and RFC 3986 unreserved characters
337
+ (A-Z, a-z, 0-9, -, _, ., ~) are never encoded. All other characters are
338
+ encoded as UTF-8 percent-sequences.
339
+ """
340
+ return _uparse_quote(string, safe)
341
+
342
+
343
+ def unquote(string, encoding='utf-8', errors='replace'):
344
+ """Decode percent-encoded sequences (%xx) in a URL component.
345
+
346
+ '+' signs are NOT decoded as spaces; use unquote_plus() for that.
347
+ """
348
+ return _uparse_unquote(string)
349
+
350
+
351
+ def quote_plus(string, safe='', encoding=None, errors=None):
352
+ """Like quote(), but spaces are encoded as '+' instead of '%20'."""
353
+ v"""return _uparse_quote(string, safe).split('%20').join('+');"""
354
+
355
+
356
+ def unquote_plus(string, encoding='utf-8', errors='replace'):
357
+ """Like unquote(), but '+' signs are decoded as spaces first."""
358
+ v"""return _uparse_unquote(string.split('+').join(' '));"""
359
+
360
+
361
+ def urlencode(query, doseq=False):
362
+ """Encode a mapping or sequence of pairs as a URL query string.
363
+
364
+ query may be a dict or a list of [key, value] pairs.
365
+ When doseq=True, values that are sequences are expanded into separate pairs.
366
+ """
367
+ return _uparse_urlencode(query, doseq)
368
+
369
+
370
+ def urlsplit(urlstring, scheme='', allow_fragments=True):
371
+ """Parse a URL into (scheme, netloc, path, query, fragment).
372
+
373
+ Returns a SplitResult. Unlike urlparse(), the path component is not
374
+ split on ';' — params remain part of path.
375
+ """
376
+ parts = _uparse_split(urlstring, scheme)
377
+ _scheme = parts['scheme']
378
+ _netloc = parts['netloc']
379
+ _path = parts['path']
380
+ _query = parts['query']
381
+ _frag = parts['fragment']
382
+ if not allow_fragments and _frag:
383
+ _query = _query + '#' + _frag
384
+ _frag = ''
385
+ return SplitResult(_scheme, _netloc, _path, _query, _frag)
386
+
387
+
388
+ def urlunsplit(components):
389
+ """Combine the elements of a SplitResult (or 5-tuple) back into a URL string.
390
+
391
+ components = (scheme, netloc, path, query, fragment)
392
+ """
393
+ scheme = components[0]
394
+ netloc = components[1]
395
+ path = components[2]
396
+ query = components[3]
397
+ fragment = components[4]
398
+ url = path
399
+ if netloc:
400
+ if url and v'url.charAt(0) !== "/"':
401
+ url = '/' + url
402
+ url = '//' + netloc + url
403
+ if scheme:
404
+ url = scheme + ':' + url
405
+ if query:
406
+ url = url + '?' + query
407
+ if fragment:
408
+ url = url + '#' + fragment
409
+ return url
410
+
411
+
412
+ def urlparse(urlstring, scheme='', allow_fragments=True):
413
+ """Parse a URL into (scheme, netloc, path, params, query, fragment).
414
+
415
+ Returns a ParseResult. params is the text after the first ';' in the
416
+ path (used in FTP-style URLs such as ftp://host/path;type=a).
417
+ """
418
+ sr = urlsplit(urlstring, scheme, allow_fragments)
419
+ path = sr.path
420
+ params = ''
421
+ semi = v'path.indexOf(";")'
422
+ if semi >= 0:
423
+ params = v'path.slice(semi + 1)'
424
+ path = v'path.slice(0, semi)'
425
+ return ParseResult(sr.scheme, sr.netloc, path, params, sr.query, sr.fragment)
426
+
427
+
428
+ def urlunparse(components):
429
+ """Combine the elements of a ParseResult (or 6-tuple) back into a URL string.
430
+
431
+ components = (scheme, netloc, path, params, query, fragment)
432
+ """
433
+ scheme = components[0]
434
+ netloc = components[1]
435
+ path = components[2]
436
+ params = components[3]
437
+ query = components[4]
438
+ fragment = components[5]
439
+ if params:
440
+ path = path + ';' + params
441
+ return urlunsplit((scheme, netloc, path, query, fragment))
442
+
443
+
444
+ def urljoin(base, url, allow_fragments=True):
445
+ """Resolve a possibly relative URL against a base URL.
446
+
447
+ Returns the absolute URL that results from joining base and url.
448
+ """
449
+ return _uparse_urljoin(base, url, allow_fragments)
450
+
451
+
452
+ def parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
453
+ encoding='utf-8', errors='replace', max_num_fields=None):
454
+ """Parse a query string into a list of (name, value) pairs.
455
+
456
+ keep_blank_values controls whether entries with empty values are returned.
457
+ """
458
+ return _uparse_parse_qsl(qs, keep_blank_values)
459
+
460
+
461
+ def parse_qs(qs, keep_blank_values=False, strict_parsing=False,
462
+ encoding='utf-8', errors='replace', max_num_fields=None):
463
+ """Parse a query string into a dict mapping names to lists of values.
464
+
465
+ Values in the returned dict are always lists (even for single occurrences).
466
+ """
467
+ d = {}
468
+ for pair in _uparse_parse_qsl(qs, keep_blank_values):
469
+ k = pair[0]
470
+ v = pair[1]
471
+ if k in d:
472
+ d[k].append(v)
473
+ else:
474
+ d[k] = [v]
475
+ return d
@@ -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
+ """
@@ -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: 'import',
366
- defined_at: pos_from_token(node.start),
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) {