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.
Files changed (151) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +19 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_GAPS.md +420 -0
  8. package/README.md +153 -29
  9. package/TODO.md +16 -118
  10. package/add-toc-to-readme +2 -2
  11. package/bin/export +75 -75
  12. package/bin/rapydscript +70 -70
  13. package/bin/web-repl-export +102 -102
  14. package/build +2 -2
  15. package/language-service/index.js +237 -8
  16. package/memory/project_string_impl.md +43 -0
  17. package/package.json +1 -1
  18. package/publish.py +37 -37
  19. package/release/baselib-plain-pretty.js +248 -38
  20. package/release/baselib-plain-ugly.js +8 -8
  21. package/release/compiler.js +778 -277
  22. package/release/signatures.json +30 -30
  23. package/session.vim +4 -4
  24. package/setup.cfg +2 -2
  25. package/src/ast.pyj +4 -1
  26. package/src/baselib-builtins.pyj +56 -2
  27. package/src/baselib-containers.pyj +2 -0
  28. package/src/baselib-errors.pyj +7 -3
  29. package/src/baselib-internal.pyj +51 -6
  30. package/src/baselib-str.pyj +5 -3
  31. package/src/compiler.pyj +36 -36
  32. package/src/errors.pyj +30 -30
  33. package/src/lib/aes.pyj +646 -646
  34. package/src/lib/asyncio.pyj +534 -0
  35. package/src/lib/base64.pyj +399 -0
  36. package/src/lib/bisect.pyj +73 -0
  37. package/src/lib/collections.pyj +1 -1
  38. package/src/lib/copy.pyj +120 -120
  39. package/src/lib/csv.pyj +494 -0
  40. package/src/lib/elementmaker.pyj +83 -83
  41. package/src/lib/encodings.pyj +126 -126
  42. package/src/lib/gettext.pyj +569 -569
  43. package/src/lib/heapq.pyj +98 -0
  44. package/src/lib/html.pyj +382 -0
  45. package/src/lib/http/__init__.pyj +98 -0
  46. package/src/lib/http/client.pyj +304 -0
  47. package/src/lib/http/cookies.pyj +236 -0
  48. package/src/lib/itertools.pyj +580 -580
  49. package/src/lib/logging.pyj +672 -0
  50. package/src/lib/math.pyj +193 -193
  51. package/src/lib/operator.pyj +11 -11
  52. package/src/lib/pythonize.pyj +20 -20
  53. package/src/lib/random.pyj +118 -118
  54. package/src/lib/react.pyj +74 -74
  55. package/src/lib/string.pyj +357 -0
  56. package/src/lib/textwrap.pyj +329 -0
  57. package/src/lib/traceback.pyj +63 -63
  58. package/src/lib/urllib/__init__.pyj +14 -0
  59. package/src/lib/urllib/error.pyj +66 -0
  60. package/src/lib/urllib/parse.pyj +475 -0
  61. package/src/lib/urllib/request.pyj +86 -0
  62. package/src/lib/uuid.pyj +77 -77
  63. package/src/monaco-language-service/analyzer.js +5 -2
  64. package/src/monaco-language-service/completions.js +26 -0
  65. package/src/monaco-language-service/diagnostics.js +202 -3
  66. package/src/monaco-language-service/dts.js +550 -550
  67. package/src/monaco-language-service/scope.js +1 -0
  68. package/src/output/comments.pyj +45 -45
  69. package/src/output/exceptions.pyj +201 -201
  70. package/src/output/functions.pyj +152 -6
  71. package/src/output/jsx.pyj +164 -164
  72. package/src/output/loops.pyj +17 -2
  73. package/src/output/modules.pyj +1 -1
  74. package/src/output/operators.pyj +15 -0
  75. package/src/output/stream.pyj +0 -1
  76. package/src/output/treeshake.pyj +182 -182
  77. package/src/output/utils.pyj +72 -72
  78. package/src/parse.pyj +80 -17
  79. package/src/string_interpolation.pyj +72 -72
  80. package/src/tokenizer.pyj +1 -1
  81. package/src/unicode_aliases.pyj +576 -576
  82. package/src/utils.pyj +192 -192
  83. package/test/_import_one.pyj +37 -37
  84. package/test/_import_two/__init__.pyj +11 -11
  85. package/test/_import_two/level2/deep.pyj +4 -4
  86. package/test/_import_two/other.pyj +6 -6
  87. package/test/_import_two/sub.pyj +13 -13
  88. package/test/aes_vectors.pyj +421 -421
  89. package/test/annotations.pyj +80 -80
  90. package/test/async_generators.pyj +144 -0
  91. package/test/asyncio.pyj +307 -0
  92. package/test/base64.pyj +202 -0
  93. package/test/bisect.pyj +178 -0
  94. package/test/csv.pyj +405 -0
  95. package/test/decorators.pyj +77 -77
  96. package/test/docstrings.pyj +39 -39
  97. package/test/elementmaker_test.pyj +45 -45
  98. package/test/float_special.pyj +64 -0
  99. package/test/functions.pyj +151 -151
  100. package/test/generators.pyj +41 -41
  101. package/test/generic.pyj +370 -370
  102. package/test/heapq.pyj +174 -0
  103. package/test/html.pyj +212 -0
  104. package/test/http.pyj +259 -0
  105. package/test/imports.pyj +79 -72
  106. package/test/internationalization.pyj +73 -73
  107. package/test/lint.pyj +164 -164
  108. package/test/logging.pyj +356 -0
  109. package/test/long.pyj +130 -0
  110. package/test/loops.pyj +85 -85
  111. package/test/numpy.pyj +734 -734
  112. package/test/parenthesized_with.pyj +141 -0
  113. package/test/python_compat.pyj +3 -5
  114. package/test/python_modulo.pyj +76 -0
  115. package/test/python_modulo_off.pyj +21 -0
  116. package/test/repl.pyj +121 -121
  117. package/test/scoped_flags.pyj +76 -76
  118. package/test/str.pyj +14 -0
  119. package/test/string.pyj +245 -0
  120. package/test/textwrap.pyj +172 -0
  121. package/test/type_display.pyj +48 -0
  122. package/test/type_enforcement.pyj +164 -0
  123. package/test/unit/index.js +14 -6
  124. package/test/unit/language-service-completions.js +119 -0
  125. package/test/unit/language-service-dts.js +543 -543
  126. package/test/unit/language-service-hover.js +455 -455
  127. package/test/unit/language-service-scope.js +32 -0
  128. package/test/unit/language-service.js +127 -3
  129. package/test/unit/run-language-service.js +17 -3
  130. package/test/unit/web-repl.js +2094 -29
  131. package/test/urllib.pyj +193 -0
  132. package/tools/compile.js +1 -1
  133. package/tools/compiler.d.ts +367 -367
  134. package/tools/completer.js +131 -131
  135. package/tools/embedded_compiler.js +7 -7
  136. package/tools/gettext.js +185 -185
  137. package/tools/ini.js +65 -65
  138. package/tools/msgfmt.js +187 -187
  139. package/tools/repl.js +223 -223
  140. package/tools/test.js +118 -118
  141. package/tools/utils.js +128 -128
  142. package/tools/web_repl.js +95 -95
  143. package/try +41 -41
  144. package/web-repl/env.js +196 -196
  145. package/web-repl/index.html +163 -163
  146. package/web-repl/main.js +1 -1
  147. package/web-repl/prism.css +139 -139
  148. package/web-repl/prism.js +113 -113
  149. package/web-repl/rapydscript.js +224 -224
  150. package/web-repl/sha1.js +25 -25
  151. package/test/omit_function_metadata.pyj +0 -20
@@ -1,63 +1,63 @@
1
- # vim:fileencoding=utf-8
2
- # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
- # globals: ρσ_str, ρσ_last_exception
4
-
5
-
6
- def _get_internal_traceback(err):
7
- if isinstance(err, Exception) and err.stack:
8
- lines = ρσ_str.splitlines(err.stack)
9
- final_lines = v'[]'
10
- found_sentinel = False
11
- for i, line in enumerate(lines):
12
- sline = ρσ_str.strip(line)
13
- if i is 0:
14
- final_lines.push(line)
15
- continue
16
- if found_sentinel:
17
- final_lines.push(line)
18
- continue
19
- # These two conditions work on desktop Chrome and Firefox to identify the correct
20
- # line in the traceback.
21
- if sline.startsWith('at new ' + err.name) or sline.startsWith(err.name + '@'):
22
- found_sentinel = True
23
- return final_lines.join('\n')
24
- return err and err.stack
25
-
26
- def format_exception(exc, limit):
27
- if jstype(exc) is 'undefined':
28
- exc = ρσ_last_exception
29
- if not isinstance(exc, Error):
30
- if exc and exc.toString:
31
- return [exc.toString()]
32
- return []
33
- tb = _get_internal_traceback(exc)
34
- if tb:
35
- lines = ρσ_str.splitlines(tb)
36
- e = lines[0]
37
- lines = lines[1:]
38
- if limit:
39
- lines = lines[:limit+1] if limit > 0 else lines[limit:]
40
- lines.reverse()
41
- lines.push(e)
42
- lines.insert(0, 'Traceback (most recent call last):')
43
- return [l+'\n' for l in lines]
44
- return [exc.toString()]
45
-
46
- def format_exc(limit):
47
- return format_exception(ρσ_last_exception, limit).join('')
48
-
49
- def print_exc(limit):
50
- print(format_exc(limit))
51
-
52
- def format_stack(limit):
53
- stack = Error().stack
54
- if not stack:
55
- return []
56
- lines = str.splitlines(stack)[2:]
57
- lines.reverse()
58
- if limit:
59
- lines = lines[:limit+1] if limit > 0 else lines[limit:]
60
- return [l + '\n' for l in lines]
61
-
62
- def print_stack(limit):
63
- print(format_stack(limit).join(''))
1
+ # vim:fileencoding=utf-8
2
+ # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
+ # globals: ρσ_str, ρσ_last_exception
4
+
5
+
6
+ def _get_internal_traceback(err):
7
+ if isinstance(err, Exception) and err.stack:
8
+ lines = ρσ_str.splitlines(err.stack)
9
+ final_lines = v'[]'
10
+ found_sentinel = False
11
+ for i, line in enumerate(lines):
12
+ sline = ρσ_str.strip(line)
13
+ if i is 0:
14
+ final_lines.push(line)
15
+ continue
16
+ if found_sentinel:
17
+ final_lines.push(line)
18
+ continue
19
+ # These two conditions work on desktop Chrome and Firefox to identify the correct
20
+ # line in the traceback.
21
+ if sline.startsWith('at new ' + err.name) or sline.startsWith(err.name + '@'):
22
+ found_sentinel = True
23
+ return final_lines.join('\n')
24
+ return err and err.stack
25
+
26
+ def format_exception(exc, limit):
27
+ if jstype(exc) is 'undefined':
28
+ exc = ρσ_last_exception
29
+ if not isinstance(exc, Error):
30
+ if exc and exc.toString:
31
+ return [exc.toString()]
32
+ return []
33
+ tb = _get_internal_traceback(exc)
34
+ if tb:
35
+ lines = ρσ_str.splitlines(tb)
36
+ e = lines[0]
37
+ lines = lines[1:]
38
+ if limit:
39
+ lines = lines[:limit+1] if limit > 0 else lines[limit:]
40
+ lines.reverse()
41
+ lines.push(e)
42
+ lines.insert(0, 'Traceback (most recent call last):')
43
+ return [l+'\n' for l in lines]
44
+ return [exc.toString()]
45
+
46
+ def format_exc(limit):
47
+ return format_exception(ρσ_last_exception, limit).join('')
48
+
49
+ def print_exc(limit):
50
+ print(format_exc(limit))
51
+
52
+ def format_stack(limit):
53
+ stack = Error().stack
54
+ if not stack:
55
+ return []
56
+ lines = str.splitlines(stack)[2:]
57
+ lines.reverse()
58
+ if limit:
59
+ lines = lines[:limit+1] if limit > 0 else lines[limit:]
60
+ return [l + '\n' for l in lines]
61
+
62
+ def print_stack(limit):
63
+ print(format_stack(limit).join(''))
@@ -0,0 +1,14 @@
1
+ ###########################################################
2
+ # RapydScript Standard Library
3
+ # urllib — URL handling utilities
4
+ ###########################################################
5
+ #
6
+ # Sub-modules:
7
+ # urllib.parse — URL parsing and encoding (urlparse, quote, urlencode, …)
8
+ # urllib.request — HTTP requests via fetch (urlopen)
9
+ # urllib.error — URLError, HTTPError exception classes
10
+ #
11
+ # Usage:
12
+ # from urllib.parse import urlparse, quote, unquote, urlencode, urljoin
13
+ # from urllib.request import urlopen
14
+ # from urllib.error import URLError, HTTPError
@@ -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