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.
- package/CHANGELOG.md +28 -0
- package/PYTHON_GAPS.md +352 -0
- package/README.md +176 -32
- package/TODO.md +1 -128
- package/bin/rapydscript +70 -70
- package/language-service/index.js +242 -11
- package/memory/project_string_impl.md +43 -0
- package/package.json +1 -1
- 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/src/ast.pyj +10 -1
- package/src/baselib-builtins.pyj +56 -2
- package/src/baselib-containers.pyj +25 -1
- package/src/baselib-errors.pyj +7 -3
- package/src/baselib-internal.pyj +51 -6
- package/src/baselib-str.pyj +18 -5
- 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 +228 -4
- package/src/lib/csv.pyj +494 -0
- 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/logging.pyj +672 -0
- package/src/lib/pprint.pyj +455 -0
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/statistics.pyj +0 -0
- package/src/lib/string.pyj +357 -0
- package/src/lib/textwrap.pyj +329 -0
- 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/monaco-language-service/analyzer.js +5 -2
- package/src/monaco-language-service/completions.js +26 -0
- package/src/monaco-language-service/diagnostics.js +203 -4
- package/src/monaco-language-service/scope.js +1 -0
- package/src/output/codegen.pyj +4 -1
- package/src/output/functions.pyj +152 -6
- 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/parse.pyj +108 -24
- package/src/tokenizer.pyj +19 -3
- package/test/async_generators.pyj +144 -0
- package/test/asyncio.pyj +307 -0
- package/test/base64.pyj +202 -0
- package/test/baselib.pyj +23 -0
- package/test/bisect.pyj +178 -0
- package/test/chainmap.pyj +185 -0
- package/test/csv.pyj +405 -0
- package/test/float_special.pyj +64 -0
- 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/logging.pyj +356 -0
- package/test/long.pyj +130 -0
- package/test/parenthesized_with.pyj +141 -0
- package/test/pprint.pyj +232 -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/statistics.pyj +224 -0
- 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 +94 -6
- package/test/unit/language-service-completions.js +121 -0
- package/test/unit/language-service-scope.js +32 -0
- package/test/unit/language-service.js +190 -5
- package/test/unit/run-language-service.js +17 -3
- package/test/unit/web-repl.js +2401 -13
- package/test/urllib.pyj +193 -0
- package/tools/compile.js +1 -1
- package/tools/embedded_compiler.js +7 -7
- package/tools/export.js +4 -2
- package/web-repl/main.js +1 -1
- package/web-repl/rapydscript.js +7 -5
- package/test/omit_function_metadata.pyj +0 -20
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
###########################################################
|
|
2
|
+
# RapydScript Standard Library
|
|
3
|
+
# Author: RapydScript-NS Contributors
|
|
4
|
+
# Copyright 2024 RapydScript-NS Contributors
|
|
5
|
+
# License: Apache License 2.0
|
|
6
|
+
# This library is covered under Apache license, so that
|
|
7
|
+
# you can distribute it with your RapydScript applications.
|
|
8
|
+
###########################################################
|
|
9
|
+
|
|
10
|
+
# Python-compatible string module.
|
|
11
|
+
#
|
|
12
|
+
# Provides:
|
|
13
|
+
# Character constants: ascii_letters, ascii_lowercase, ascii_uppercase,
|
|
14
|
+
# digits, hexdigits, octdigits, punctuation, printable, whitespace
|
|
15
|
+
# Template: $-substitution string templates (substitute, safe_substitute)
|
|
16
|
+
# Formatter: customizable str.format() foundation with subclassing support
|
|
17
|
+
#
|
|
18
|
+
# NOTE: Formatter.format() only accepts positional args via *args.
|
|
19
|
+
# For named-field substitution use vformat(fmt, [], {'key': val}) directly.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Character constants
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
|
|
27
|
+
ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
28
|
+
ascii_letters = ascii_lowercase + ascii_uppercase
|
|
29
|
+
digits = '0123456789'
|
|
30
|
+
hexdigits = digits + 'abcdef' + 'ABCDEF'
|
|
31
|
+
octdigits = '01234567'
|
|
32
|
+
# Build punctuation via chr() to avoid backslash (92) and backtick (96) escaping.
|
|
33
|
+
punctuation = '!"#$%&' + chr(39) + '()*+,-./:;<=>?@[' + chr(92) + ']^_' + chr(96) + '{|}~'
|
|
34
|
+
# chr(11) = vertical-tab (\x0b), chr(12) = form-feed (\x0c)
|
|
35
|
+
whitespace = ' \t\n\r' + chr(11) + chr(12)
|
|
36
|
+
printable = digits + ascii_letters + punctuation + whitespace
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Internal JS helpers
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
v"""
|
|
44
|
+
// ── Shared numeric-string helper ────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
function _str_is_digits_only(s) {
|
|
47
|
+
if (!s.length) return false;
|
|
48
|
+
for (var _di = 0; _di < s.length; _di++) {
|
|
49
|
+
var _dc = s.charCodeAt(_di);
|
|
50
|
+
if (_dc < 48 || _dc > 57) return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Template helpers ─────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
// Pattern groups:
|
|
58
|
+
// 1: $$ (escaped dollar → literal $)
|
|
59
|
+
// 2: ${identifier}
|
|
60
|
+
// 3: $identifier
|
|
61
|
+
// 4: $<anything-else> (invalid placeholder; [^]? also captures trailing $)
|
|
62
|
+
var _tmpl_re = /\$(?:(\$)|\{([_a-zA-Z][_a-zA-Z0-9]*)\}|([_a-zA-Z][_a-zA-Z0-9]*)|([^]?))/g;
|
|
63
|
+
|
|
64
|
+
// Convert a mapping to a plain JS object (handles ρσ_dict with .jsmap).
|
|
65
|
+
function _tmpl_to_plain(mapping) {
|
|
66
|
+
if (mapping === null || mapping === undefined) return Object.create(null);
|
|
67
|
+
if (mapping.jsmap !== undefined && mapping.jsmap !== null) {
|
|
68
|
+
var _out = Object.create(null);
|
|
69
|
+
mapping.jsmap.forEach(function(_v, _k) { _out[_k] = _v; });
|
|
70
|
+
return _out;
|
|
71
|
+
}
|
|
72
|
+
return mapping;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function _tmpl_substitute_impl(template_str, mapping, safe) {
|
|
76
|
+
var _re = _tmpl_re;
|
|
77
|
+
_re.lastIndex = 0;
|
|
78
|
+
var _res = [];
|
|
79
|
+
var _last = 0;
|
|
80
|
+
var _m;
|
|
81
|
+
while ((_m = _re.exec(template_str)) !== null) {
|
|
82
|
+
_res.push(template_str.slice(_last, _m.index));
|
|
83
|
+
_last = _m.index + _m[0].length;
|
|
84
|
+
if (_m[1] !== undefined) {
|
|
85
|
+
_res.push('$');
|
|
86
|
+
} else if (_m[2] !== undefined || _m[3] !== undefined) {
|
|
87
|
+
var _key = _m[2] !== undefined ? _m[2] : _m[3];
|
|
88
|
+
var _val = mapping[_key];
|
|
89
|
+
if (_val === undefined) {
|
|
90
|
+
if (safe) { _res.push(_m[0]); }
|
|
91
|
+
else { throw new KeyError(_key); }
|
|
92
|
+
} else {
|
|
93
|
+
_res.push(ρσ_str(_val));
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
// m[4]: invalid placeholder char (or empty for trailing $)
|
|
97
|
+
if (safe) { _res.push(_m[0]); }
|
|
98
|
+
else { throw new ValueError('Invalid placeholder in string: ' + JSON.stringify(_m[0])); }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
_res.push(template_str.slice(_last));
|
|
102
|
+
return _res.join('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── Formatter helpers ────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
function _fmt_get_kwarg(kwargs, key) {
|
|
108
|
+
if (!kwargs) return undefined;
|
|
109
|
+
if (kwargs.jsmap !== undefined && kwargs.jsmap !== null) return kwargs.jsmap.get(key);
|
|
110
|
+
return kwargs[key];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _fmt_has_kwarg(kwargs, key) {
|
|
114
|
+
if (!kwargs) return false;
|
|
115
|
+
if (kwargs.jsmap !== undefined && kwargs.jsmap !== null) return kwargs.jsmap.has(key);
|
|
116
|
+
return Object.prototype.hasOwnProperty.call(kwargs, key);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// parse: walk format_string, return array of [literal, field_name, fmt_spec, conversion].
|
|
120
|
+
// A trailing literal with no field has field_name === null.
|
|
121
|
+
function _fmt_parse(format_string) {
|
|
122
|
+
var _items = [];
|
|
123
|
+
var _i = 0, _n = format_string.length;
|
|
124
|
+
|
|
125
|
+
while (_i <= _n) {
|
|
126
|
+
var _lit = '';
|
|
127
|
+
while (_i < _n) {
|
|
128
|
+
var _ch = format_string[_i];
|
|
129
|
+
if (_ch === '{') {
|
|
130
|
+
if (_i + 1 < _n && format_string[_i + 1] === '{') { _lit += '{'; _i += 2; }
|
|
131
|
+
else break;
|
|
132
|
+
} else if (_ch === '}') {
|
|
133
|
+
if (_i + 1 < _n && format_string[_i + 1] === '}') { _lit += '}'; _i += 2; }
|
|
134
|
+
else throw new ValueError("Single '}' encountered in format string");
|
|
135
|
+
} else { _lit += _ch; _i++; }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (_i >= _n) { _items.push([_lit, null, null, null]); break; }
|
|
139
|
+
|
|
140
|
+
_i++; // skip '{'
|
|
141
|
+
var _j = _i, _depth = 1;
|
|
142
|
+
while (_j < _n && _depth > 0) {
|
|
143
|
+
if (format_string[_j] === '{') _depth++;
|
|
144
|
+
else if (format_string[_j] === '}') _depth--;
|
|
145
|
+
_j++;
|
|
146
|
+
}
|
|
147
|
+
if (_depth !== 0) throw new ValueError("Single '{' encountered in format string");
|
|
148
|
+
|
|
149
|
+
var _ft = format_string.slice(_i, _j - 1);
|
|
150
|
+
_i = _j;
|
|
151
|
+
|
|
152
|
+
// Locate ! (conversion) and : (format spec), skipping nested {}
|
|
153
|
+
var _bang = -1, _colon = -1;
|
|
154
|
+
for (var _k = 0; _k < _ft.length; _k++) {
|
|
155
|
+
var _fc = _ft[_k];
|
|
156
|
+
if (_fc === '{') { while (_k < _ft.length && _ft[_k] !== '}') _k++; }
|
|
157
|
+
else if (_fc === '!' && _bang === -1 && _colon === -1) _bang = _k;
|
|
158
|
+
else if (_fc === ':' && _colon === -1) { _colon = _k; break; }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
var _fname, _conv, _spec;
|
|
162
|
+
if (_bang !== -1 && (_colon === -1 || _bang < _colon)) {
|
|
163
|
+
_fname = _ft.slice(0, _bang);
|
|
164
|
+
_conv = (_bang + 1 < _ft.length) ? _ft[_bang + 1] : null;
|
|
165
|
+
_spec = _colon !== -1 ? _ft.slice(_colon + 1) : '';
|
|
166
|
+
} else if (_colon !== -1) {
|
|
167
|
+
_fname = _ft.slice(0, _colon);
|
|
168
|
+
_conv = null;
|
|
169
|
+
_spec = _ft.slice(_colon + 1);
|
|
170
|
+
} else {
|
|
171
|
+
_fname = _ft;
|
|
172
|
+
_conv = null;
|
|
173
|
+
_spec = '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_items.push([_lit, _fname, _spec, _conv]);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return _items;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// get_field: resolve dotted/bracket access chain. Returns [value, first_key].
|
|
183
|
+
function _fmt_get_field(field_name, args, kwargs, self_obj) {
|
|
184
|
+
// Find first '.' or '['
|
|
185
|
+
var _fe = -1;
|
|
186
|
+
for (var _ci = 0; _ci < field_name.length; _ci++) {
|
|
187
|
+
var _cc = field_name[_ci];
|
|
188
|
+
if (_cc === '.' || _cc === '[') { _fe = _ci; break; }
|
|
189
|
+
}
|
|
190
|
+
var _fk = _fe === -1 ? field_name : field_name.slice(0, _fe);
|
|
191
|
+
var _rest = _fe === -1 ? '' : field_name.slice(_fe);
|
|
192
|
+
|
|
193
|
+
var _lookup_key = _str_is_digits_only(_fk) ? parseInt(_fk, 10) : _fk;
|
|
194
|
+
var _value = self_obj.get_value(_lookup_key, args, kwargs);
|
|
195
|
+
|
|
196
|
+
while (_rest.length > 0) {
|
|
197
|
+
if (_rest[0] === '.') {
|
|
198
|
+
var _de = _rest.length;
|
|
199
|
+
for (var _di2 = 1; _di2 < _rest.length; _di2++) {
|
|
200
|
+
if (_rest[_di2] === '.' || _rest[_di2] === '[') { _de = _di2; break; }
|
|
201
|
+
}
|
|
202
|
+
_value = _value[_rest.slice(1, _de)];
|
|
203
|
+
_rest = _rest.slice(_de);
|
|
204
|
+
} else if (_rest[0] === '[') {
|
|
205
|
+
var _cl = _rest.indexOf(']', 1);
|
|
206
|
+
if (_cl === -1) break;
|
|
207
|
+
var _idx = _rest.slice(1, _cl);
|
|
208
|
+
_value = _str_is_digits_only(_idx) ? _value[parseInt(_idx, 10)] : _value[_idx];
|
|
209
|
+
_rest = _rest.slice(_cl + 1);
|
|
210
|
+
} else {
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return [_value, _fk];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// vformat: render a format string, calling self_obj's methods for each field.
|
|
219
|
+
function _fmt_vformat(fmt_str, args, kwargs, self_obj) {
|
|
220
|
+
var _items = _fmt_parse(fmt_str);
|
|
221
|
+
var _result = [];
|
|
222
|
+
var _auto_idx = 0, _auto_used = false, _manual_used = false;
|
|
223
|
+
|
|
224
|
+
for (var _ii = 0; _ii < _items.length; _ii++) {
|
|
225
|
+
var _item = _items[_ii];
|
|
226
|
+
var _lit2 = _item[0], _fname2 = _item[1], _spec2 = _item[2], _conv2 = _item[3];
|
|
227
|
+
|
|
228
|
+
if (_lit2) _result.push(_lit2);
|
|
229
|
+
if (_fname2 === null) continue;
|
|
230
|
+
|
|
231
|
+
// Auto-numbering vs manual
|
|
232
|
+
var _ename;
|
|
233
|
+
if (_fname2 === '') {
|
|
234
|
+
if (_manual_used) throw new ValueError('cannot switch from manual field specification to automatic field numbering');
|
|
235
|
+
_auto_used = true;
|
|
236
|
+
_ename = _auto_idx++;
|
|
237
|
+
} else if (_str_is_digits_only(_fname2)) {
|
|
238
|
+
if (_auto_used) throw new ValueError('cannot switch from automatic field numbering to manual field specification');
|
|
239
|
+
_manual_used = true;
|
|
240
|
+
_ename = parseInt(_fname2, 10);
|
|
241
|
+
} else {
|
|
242
|
+
// Named or complex field (e.g. "obj.attr" — let get_field handle it)
|
|
243
|
+
_ename = _fname2;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
var _fr = self_obj.get_field(typeof _ename === 'number' ? String(_ename) : _ename, args, kwargs);
|
|
247
|
+
var _val2 = _fr[0];
|
|
248
|
+
|
|
249
|
+
if (_conv2 !== null) _val2 = self_obj.convert_field(_val2, _conv2);
|
|
250
|
+
|
|
251
|
+
// Resolve nested format specs (e.g. {:{}f})
|
|
252
|
+
var _rspec = _spec2;
|
|
253
|
+
if (_spec2 && _spec2.indexOf('{') !== -1) {
|
|
254
|
+
_rspec = _fmt_vformat(_spec2, args, kwargs, self_obj);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
_result.push(self_obj.format_field(_val2, _rspec));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return _result.join('');
|
|
261
|
+
}
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# ---------------------------------------------------------------------------
|
|
266
|
+
# Template
|
|
267
|
+
# ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
class Template:
|
|
270
|
+
"""String template with $-based substitution."""
|
|
271
|
+
delimiter = '$'
|
|
272
|
+
idpattern = '(?-i:[_a-zA-Z][_a-zA-Z0-9]*)'
|
|
273
|
+
braceidpattern = None
|
|
274
|
+
flags = 0 # re.IGNORECASE not used; provided for API compatibility
|
|
275
|
+
|
|
276
|
+
def __init__(self, template):
|
|
277
|
+
self.template = template
|
|
278
|
+
|
|
279
|
+
def substitute(self, mapping=None):
|
|
280
|
+
"""Return a copy of the template with placeholders filled from *mapping*.
|
|
281
|
+
|
|
282
|
+
Raises KeyError for missing keys and ValueError for invalid placeholders.
|
|
283
|
+
*mapping* may be a plain dict or any object supporting key lookup.
|
|
284
|
+
"""
|
|
285
|
+
m = v'_tmpl_to_plain(mapping)'
|
|
286
|
+
return v'_tmpl_substitute_impl(self.template, m, false)'
|
|
287
|
+
|
|
288
|
+
def safe_substitute(self, mapping=None):
|
|
289
|
+
"""Like substitute(), but leaves unrecognised placeholders unchanged."""
|
|
290
|
+
m = v'_tmpl_to_plain(mapping)'
|
|
291
|
+
return v'_tmpl_substitute_impl(self.template, m, true)'
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# ---------------------------------------------------------------------------
|
|
295
|
+
# Formatter
|
|
296
|
+
# ---------------------------------------------------------------------------
|
|
297
|
+
|
|
298
|
+
class Formatter:
|
|
299
|
+
"""Customisable string formatter — the foundation behind str.format().
|
|
300
|
+
|
|
301
|
+
Subclass and override format_field / convert_field / get_value / get_field
|
|
302
|
+
to change formatting behaviour.
|
|
303
|
+
|
|
304
|
+
Note: format() only accepts positional *args. For named-field substitution
|
|
305
|
+
call vformat(fmt, [], {'key': value}) directly.
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
def format(self, format_string, *args):
|
|
309
|
+
"""Format *format_string* using positional *args*."""
|
|
310
|
+
return self.vformat(format_string, list(args), {})
|
|
311
|
+
|
|
312
|
+
def vformat(self, format_string, args, kwargs):
|
|
313
|
+
"""Format *format_string* with explicit *args* list and *kwargs* dict."""
|
|
314
|
+
return v'_fmt_vformat(format_string, args, kwargs, self)'
|
|
315
|
+
|
|
316
|
+
def parse(self, format_string):
|
|
317
|
+
"""Return a list of (literal_text, field_name, format_spec, conversion) tuples."""
|
|
318
|
+
return v'_fmt_parse(format_string)'
|
|
319
|
+
|
|
320
|
+
def get_value(self, key, args, kwargs):
|
|
321
|
+
"""Retrieve a field value: integer key → args, string key → kwargs."""
|
|
322
|
+
is_int = jstype(key) is 'number'
|
|
323
|
+
is_int_str = not is_int and jstype(key) is 'string' and v'_str_is_digits_only(key)'
|
|
324
|
+
if is_int or is_int_str:
|
|
325
|
+
idx = key if is_int else int(key)
|
|
326
|
+
if idx < 0 or idx >= len(args):
|
|
327
|
+
raise IndexError('Replacement index ' + str(idx) + ' out of range for positional args tuple')
|
|
328
|
+
return args[idx]
|
|
329
|
+
val = v'_fmt_get_kwarg(kwargs, key)'
|
|
330
|
+
if val is undefined:
|
|
331
|
+
raise KeyError(key)
|
|
332
|
+
return val
|
|
333
|
+
|
|
334
|
+
def get_field(self, field_name, args, kwargs):
|
|
335
|
+
"""Look up *field_name* (may include dotted/bracket access).
|
|
336
|
+
|
|
337
|
+
Returns (value, first_key_used).
|
|
338
|
+
"""
|
|
339
|
+
return v'_fmt_get_field(field_name, args, kwargs, self)'
|
|
340
|
+
|
|
341
|
+
def convert_field(self, value, conversion):
|
|
342
|
+
"""Apply a conversion specifier (!s, !r, !a) to *value*."""
|
|
343
|
+
if conversion is None or conversion is undefined:
|
|
344
|
+
return value
|
|
345
|
+
if conversion == 's':
|
|
346
|
+
return str(value)
|
|
347
|
+
if conversion == 'r':
|
|
348
|
+
return repr(value)
|
|
349
|
+
if conversion == 'a':
|
|
350
|
+
return repr(value)
|
|
351
|
+
raise ValueError('Unknown conversion specifier: ' + str(conversion))
|
|
352
|
+
|
|
353
|
+
def format_field(self, value, format_spec):
|
|
354
|
+
"""Format a single *value* according to *format_spec*."""
|
|
355
|
+
if format_spec is not None and format_spec is not undefined and format_spec:
|
|
356
|
+
return format(value, format_spec)
|
|
357
|
+
return str(value)
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
###########################################################
|
|
2
|
+
# RapydScript Standard Library
|
|
3
|
+
# License: Apache License 2.0
|
|
4
|
+
# This library is covered under Apache license, so that
|
|
5
|
+
# you can distribute it with your RapydScript applications.
|
|
6
|
+
###########################################################
|
|
7
|
+
|
|
8
|
+
# Python-compatible textwrap module.
|
|
9
|
+
#
|
|
10
|
+
# Provides:
|
|
11
|
+
# TextWrapper class with .wrap(text), .fill(text)
|
|
12
|
+
# wrap(text[, width=70[, ...]])
|
|
13
|
+
# fill(text[, width=70[, ...]])
|
|
14
|
+
# shorten(text, width[, ...])
|
|
15
|
+
# dedent(text)
|
|
16
|
+
# indent(text, prefix[, predicate])
|
|
17
|
+
#
|
|
18
|
+
# All TextWrapper options supported:
|
|
19
|
+
# width, initial_indent, subsequent_indent, expand_tabs, tabsize,
|
|
20
|
+
# replace_whitespace, fix_sentence_endings, break_long_words,
|
|
21
|
+
# break_on_hyphens, drop_whitespace, max_lines, placeholder
|
|
22
|
+
#
|
|
23
|
+
# Notes:
|
|
24
|
+
# - break_on_hyphens=True (default) splits on em-dashes (2+ hyphens)
|
|
25
|
+
# between word characters, matching Python 3 behaviour.
|
|
26
|
+
# - shorten() normalises internal whitespace before truncating.
|
|
27
|
+
|
|
28
|
+
v"""
|
|
29
|
+
// ── textwrap internal helpers ────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function _tw_expand_tabs(text, tabsize) {
|
|
32
|
+
var _res = '';
|
|
33
|
+
var _col = 0;
|
|
34
|
+
for (var _i = 0; _i < text.length; _i++) {
|
|
35
|
+
var _c = text[_i];
|
|
36
|
+
if (_c === '\t') {
|
|
37
|
+
var _n = tabsize - (_col % tabsize);
|
|
38
|
+
for (var _j = 0; _j < _n; _j++) _res += ' ';
|
|
39
|
+
_col += _n;
|
|
40
|
+
} else {
|
|
41
|
+
_res += _c;
|
|
42
|
+
if (_c === '\n' || _c === '\r') _col = 0; else _col++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return _res;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Split into alternating non-whitespace / whitespace chunks.
|
|
49
|
+
// break_on_hyphens: also split around em-dashes (2+ hyphens) between word chars.
|
|
50
|
+
function _tw_split_chunks(text, break_on_hyphens) {
|
|
51
|
+
var _re = break_on_hyphens
|
|
52
|
+
? /(\s+|(?<=[\w!"'&.,?])-{2,}(?=\w))/g
|
|
53
|
+
: /(\s+)/g;
|
|
54
|
+
var _chunks = [];
|
|
55
|
+
var _last = 0;
|
|
56
|
+
var _m;
|
|
57
|
+
while ((_m = _re.exec(text)) !== null) {
|
|
58
|
+
if (_m.index > _last) _chunks.push(text.slice(_last, _m.index));
|
|
59
|
+
_chunks.push(_m[0]);
|
|
60
|
+
_last = _re.lastIndex;
|
|
61
|
+
}
|
|
62
|
+
if (_last < text.length) _chunks.push(text.slice(_last));
|
|
63
|
+
return _chunks;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _tw_fix_sentence_endings(chunks) {
|
|
67
|
+
var _re = /[a-z][.!?]$/;
|
|
68
|
+
for (var _i = 0; _i < chunks.length - 1; _i++) {
|
|
69
|
+
if (chunks[_i + 1] === ' ' && _re.test(chunks[_i])) {
|
|
70
|
+
chunks[_i + 1] = ' ';
|
|
71
|
+
_i++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _tw_handle_long_word(chunks, cur_line, cur_len, avail, break_long_words) {
|
|
77
|
+
var _space = avail > 0 ? avail - cur_len : 1;
|
|
78
|
+
if (break_long_words) {
|
|
79
|
+
var _word = chunks[chunks.length - 1];
|
|
80
|
+
if (_space > 0) {
|
|
81
|
+
cur_line.push(_word.slice(0, _space));
|
|
82
|
+
chunks[chunks.length - 1] = _word.slice(_space);
|
|
83
|
+
}
|
|
84
|
+
if (chunks[chunks.length - 1] === '') chunks.pop();
|
|
85
|
+
} else if (!cur_line.length) {
|
|
86
|
+
cur_line.push(chunks.pop());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function _tw_wrap_chunks(chunks, width, initial_indent, subsequent_indent,
|
|
91
|
+
drop_whitespace, break_long_words, max_lines, placeholder) {
|
|
92
|
+
var _lines = [];
|
|
93
|
+
// Reverse so Array.pop() reads chunks left-to-right.
|
|
94
|
+
chunks = chunks.slice().reverse();
|
|
95
|
+
|
|
96
|
+
while (chunks.length > 0) {
|
|
97
|
+
var _cur_line = [];
|
|
98
|
+
var _cur_len = 0;
|
|
99
|
+
var _indent = _lines.length > 0 ? subsequent_indent : initial_indent;
|
|
100
|
+
var _avail = width - _indent.length;
|
|
101
|
+
|
|
102
|
+
// Drop leading whitespace on continuation lines.
|
|
103
|
+
if (drop_whitespace && _lines.length > 0 &&
|
|
104
|
+
chunks.length > 0 && chunks[chunks.length - 1].trim() === '') {
|
|
105
|
+
chunks.pop();
|
|
106
|
+
}
|
|
107
|
+
if (chunks.length === 0) break;
|
|
108
|
+
|
|
109
|
+
// Fill the current line.
|
|
110
|
+
while (chunks.length > 0) {
|
|
111
|
+
var _l = chunks[chunks.length - 1].length;
|
|
112
|
+
if (_cur_len + _l <= _avail) {
|
|
113
|
+
_cur_len += _l;
|
|
114
|
+
_cur_line.push(chunks.pop());
|
|
115
|
+
} else {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle a word longer than the available width.
|
|
121
|
+
if (chunks.length > 0 && chunks[chunks.length - 1].length > _avail) {
|
|
122
|
+
_tw_handle_long_word(chunks, _cur_line, _cur_len, _avail, break_long_words);
|
|
123
|
+
_cur_len = 0;
|
|
124
|
+
for (var _ci = 0; _ci < _cur_line.length; _ci++) _cur_len += _cur_line[_ci].length;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Drop trailing whitespace.
|
|
128
|
+
if (drop_whitespace && _cur_line.length > 0 &&
|
|
129
|
+
_cur_line[_cur_line.length - 1].trim() === '') {
|
|
130
|
+
_cur_len -= _cur_line[_cur_line.length - 1].length;
|
|
131
|
+
_cur_line.pop();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (_cur_line.length > 0) {
|
|
135
|
+
var _will_be_last = chunks.length === 0 ||
|
|
136
|
+
(drop_whitespace && chunks.length === 1 && chunks[0].trim() === '');
|
|
137
|
+
|
|
138
|
+
if (max_lines === null || max_lines === undefined ||
|
|
139
|
+
_lines.length + 1 < max_lines ||
|
|
140
|
+
(_will_be_last && _cur_len <= _avail)) {
|
|
141
|
+
_lines.push(_indent + _cur_line.join(''));
|
|
142
|
+
} else {
|
|
143
|
+
// Truncate current line to fit placeholder.
|
|
144
|
+
var _done = false;
|
|
145
|
+
while (_cur_line.length > 0) {
|
|
146
|
+
var _tok = _cur_line[_cur_line.length - 1];
|
|
147
|
+
if (_tok.trim() !== '' && _cur_len + placeholder.length <= _avail) {
|
|
148
|
+
_lines.push(_indent + _cur_line.join('') + placeholder);
|
|
149
|
+
_done = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
_cur_len -= _tok.length;
|
|
153
|
+
_cur_line.pop();
|
|
154
|
+
}
|
|
155
|
+
if (!_done) {
|
|
156
|
+
if (_lines.length > 0) {
|
|
157
|
+
var _prev = _lines[_lines.length - 1].replace(/\s+$/, '');
|
|
158
|
+
if (_prev.length + placeholder.length <= width) {
|
|
159
|
+
_lines[_lines.length - 1] = _prev + placeholder;
|
|
160
|
+
} else {
|
|
161
|
+
_lines.push(_indent + placeholder.replace(/^\s+/, ''));
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
_lines.push(_indent + placeholder.replace(/^\s+/, ''));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return _lines;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function _tw_do_wrap(text, width, opts) {
|
|
175
|
+
var _ii = opts.initial_indent != null ? opts.initial_indent : '';
|
|
176
|
+
var _si = opts.subsequent_indent != null ? opts.subsequent_indent : '';
|
|
177
|
+
var _et = opts.expand_tabs != null ? opts.expand_tabs : true;
|
|
178
|
+
var _ts = opts.tabsize != null ? opts.tabsize : 8;
|
|
179
|
+
var _rw = opts.replace_whitespace != null ? opts.replace_whitespace : true;
|
|
180
|
+
var _fse = opts.fix_sentence_endings != null ? opts.fix_sentence_endings : false;
|
|
181
|
+
var _blw = opts.break_long_words != null ? opts.break_long_words : true;
|
|
182
|
+
var _boh = opts.break_on_hyphens != null ? opts.break_on_hyphens : true;
|
|
183
|
+
var _dw = opts.drop_whitespace != null ? opts.drop_whitespace : true;
|
|
184
|
+
var _ml = opts.max_lines != null ? opts.max_lines : null;
|
|
185
|
+
var _ph = opts.placeholder != null ? opts.placeholder : ' [...]';
|
|
186
|
+
|
|
187
|
+
if (_et) text = _tw_expand_tabs(text, _ts);
|
|
188
|
+
if (_rw) text = text.replace(/[\t\n\x0b\x0c\r]/g, ' ');
|
|
189
|
+
|
|
190
|
+
var _chunks = _tw_split_chunks(text, _boh);
|
|
191
|
+
if (_fse) _tw_fix_sentence_endings(_chunks);
|
|
192
|
+
|
|
193
|
+
return _tw_wrap_chunks(_chunks, width, _ii, _si, _dw, _blw, _ml, _ph);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function _tw_do_fill(text, width, opts) {
|
|
197
|
+
return _tw_do_wrap(text, width, opts).join('\n');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function _tw_shorten(text, width, opts) {
|
|
201
|
+
text = text.trim().split(/\s+/).join(' ');
|
|
202
|
+
// Build a copy of opts with max_lines forced to 1.
|
|
203
|
+
var _sopts = {};
|
|
204
|
+
for (var _k in opts) {
|
|
205
|
+
if (Object.prototype.hasOwnProperty.call(opts, _k)) _sopts[_k] = opts[_k];
|
|
206
|
+
}
|
|
207
|
+
_sopts.max_lines = 1;
|
|
208
|
+
if (_sopts.placeholder === null || _sopts.placeholder === undefined) {
|
|
209
|
+
_sopts.placeholder = ' [...]';
|
|
210
|
+
}
|
|
211
|
+
return _tw_do_fill(text, width, _sopts);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function _tw_dedent(text) {
|
|
215
|
+
var _lines = text.split('\n');
|
|
216
|
+
var _margin = null;
|
|
217
|
+
for (var _i = 0; _i < _lines.length; _i++) {
|
|
218
|
+
var _line = _lines[_i];
|
|
219
|
+
if (_line.trim() === '') continue;
|
|
220
|
+
var _m = /^(\s+)\S/.exec(_line);
|
|
221
|
+
if (_m) {
|
|
222
|
+
var _ind = _m[1];
|
|
223
|
+
if (_margin === null) {
|
|
224
|
+
_margin = _ind;
|
|
225
|
+
} else {
|
|
226
|
+
var _j = 0;
|
|
227
|
+
while (_j < _margin.length && _j < _ind.length && _margin[_j] === _ind[_j]) _j++;
|
|
228
|
+
_margin = _margin.slice(0, _j);
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
_margin = '';
|
|
232
|
+
}
|
|
233
|
+
if (_margin === '') break;
|
|
234
|
+
}
|
|
235
|
+
if (!_margin) return text;
|
|
236
|
+
var _result = [];
|
|
237
|
+
for (var _i2 = 0; _i2 < _lines.length; _i2++) {
|
|
238
|
+
var _l = _lines[_i2];
|
|
239
|
+
_result.push(_l.slice(0, _margin.length) === _margin ? _l.slice(_margin.length) : _l);
|
|
240
|
+
}
|
|
241
|
+
return _result.join('\n');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function _tw_indent(text, prefix, predicate) {
|
|
245
|
+
var _lines = text.split('\n');
|
|
246
|
+
var _result = [];
|
|
247
|
+
var _use_default = (predicate === null || predicate === undefined);
|
|
248
|
+
for (var _i = 0; _i < _lines.length; _i++) {
|
|
249
|
+
var _line = _lines[_i];
|
|
250
|
+
var _add = _use_default ? _line.trim() !== '' : predicate(_line);
|
|
251
|
+
_result.push(_add ? prefix + _line : _line);
|
|
252
|
+
}
|
|
253
|
+
return _result.join('\n');
|
|
254
|
+
}
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class TextWrapper:
|
|
259
|
+
def __init__(self, width=70, initial_indent='', subsequent_indent='',
|
|
260
|
+
expand_tabs=True, tabsize=8, replace_whitespace=True,
|
|
261
|
+
fix_sentence_endings=False, break_long_words=True,
|
|
262
|
+
break_on_hyphens=True, drop_whitespace=True,
|
|
263
|
+
max_lines=None, placeholder=' [...]'):
|
|
264
|
+
self.width = width
|
|
265
|
+
self.initial_indent = initial_indent
|
|
266
|
+
self.subsequent_indent = subsequent_indent
|
|
267
|
+
self.expand_tabs = expand_tabs
|
|
268
|
+
self.tabsize = tabsize
|
|
269
|
+
self.replace_whitespace = replace_whitespace
|
|
270
|
+
self.fix_sentence_endings = fix_sentence_endings
|
|
271
|
+
self.break_long_words = break_long_words
|
|
272
|
+
self.break_on_hyphens = break_on_hyphens
|
|
273
|
+
self.drop_whitespace = drop_whitespace
|
|
274
|
+
self.max_lines = max_lines
|
|
275
|
+
self.placeholder = placeholder
|
|
276
|
+
|
|
277
|
+
def wrap(self, text):
|
|
278
|
+
return v'_tw_do_wrap(text, this.width, this)'
|
|
279
|
+
|
|
280
|
+
def fill(self, text):
|
|
281
|
+
return v'_tw_do_fill(text, this.width, this)'
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def wrap(text, width=70, initial_indent='', subsequent_indent='',
|
|
285
|
+
expand_tabs=True, tabsize=8, replace_whitespace=True,
|
|
286
|
+
fix_sentence_endings=False, break_long_words=True,
|
|
287
|
+
break_on_hyphens=True, drop_whitespace=True,
|
|
288
|
+
max_lines=None, placeholder=' [...]'):
|
|
289
|
+
return TextWrapper(
|
|
290
|
+
width, initial_indent, subsequent_indent, expand_tabs,
|
|
291
|
+
tabsize, replace_whitespace, fix_sentence_endings,
|
|
292
|
+
break_long_words, break_on_hyphens, drop_whitespace,
|
|
293
|
+
max_lines, placeholder
|
|
294
|
+
).wrap(text)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def fill(text, width=70, initial_indent='', subsequent_indent='',
|
|
298
|
+
expand_tabs=True, tabsize=8, replace_whitespace=True,
|
|
299
|
+
fix_sentence_endings=False, break_long_words=True,
|
|
300
|
+
break_on_hyphens=True, drop_whitespace=True,
|
|
301
|
+
max_lines=None, placeholder=' [...]'):
|
|
302
|
+
return TextWrapper(
|
|
303
|
+
width, initial_indent, subsequent_indent, expand_tabs,
|
|
304
|
+
tabsize, replace_whitespace, fix_sentence_endings,
|
|
305
|
+
break_long_words, break_on_hyphens, drop_whitespace,
|
|
306
|
+
max_lines, placeholder
|
|
307
|
+
).fill(text)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def shorten(text, width, initial_indent='', subsequent_indent='',
|
|
311
|
+
expand_tabs=True, tabsize=8, replace_whitespace=True,
|
|
312
|
+
fix_sentence_endings=False, break_long_words=True,
|
|
313
|
+
break_on_hyphens=True, drop_whitespace=True,
|
|
314
|
+
placeholder=' [...]'):
|
|
315
|
+
opts = TextWrapper(
|
|
316
|
+
width, initial_indent, subsequent_indent, expand_tabs,
|
|
317
|
+
tabsize, replace_whitespace, fix_sentence_endings,
|
|
318
|
+
break_long_words, break_on_hyphens, drop_whitespace,
|
|
319
|
+
None, placeholder
|
|
320
|
+
)
|
|
321
|
+
return v'_tw_shorten(text, width, opts)'
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def dedent(text):
|
|
325
|
+
return v'_tw_dedent(text)'
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def indent(text, prefix, predicate=None):
|
|
329
|
+
return v'_tw_indent(text, prefix, predicate)'
|
|
@@ -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
|