rapydscript-ns 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +10 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +7 -6
- package/TODO.md +116 -1
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +9 -9
- package/language-service/language-service.d.ts +1 -1
- package/package.json +6 -2
- package/publish.py +37 -37
- package/release/compiler.js +246 -231
- package/release/signatures.json +23 -23
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/contextlib.pyj +379 -0
- package/src/lib/copy.pyj +120 -120
- package/src/lib/datetime.pyj +712 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/io.pyj +500 -0
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/json.pyj +227 -0
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/react.pyj +74 -74
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/diagnostics.js +4 -4
- package/src/monaco-language-service/dts.js +550 -550
- package/src/monaco-language-service/index.js +2 -2
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +9 -0
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +1 -1
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/contextlib.pyj +362 -0
- package/test/datetime.pyj +500 -0
- package/test/debugger_stmt.pyj +41 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/imports.pyj +72 -72
- package/test/internationalization.pyj +73 -73
- package/test/io.pyj +316 -0
- package/test/json.pyj +196 -0
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/omit_function_metadata.pyj +20 -20
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/unit/index.js +66 -0
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +1 -1
- package/test/unit/web-repl.js +533 -0
- package/tools/compiler.d.ts +367 -0
- package/tools/completer.js +131 -131
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD
|
|
3
|
+
# RapydScript implementation of Python's contextlib standard library.
|
|
4
|
+
#
|
|
5
|
+
# Supported:
|
|
6
|
+
# AbstractContextManager — base class with default __enter__ (returns self)
|
|
7
|
+
# @contextmanager — turn a generator function into a context manager
|
|
8
|
+
# closing(thing) — calls thing.close() on exit
|
|
9
|
+
# nullcontext(val) — no-op context manager; __enter__ returns val
|
|
10
|
+
# suppress(*excs) — silently swallow specified exception types
|
|
11
|
+
# ExitStack — dynamic LIFO stack of context managers / callbacks
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# from contextlib import contextmanager, suppress, closing, nullcontext, ExitStack
|
|
15
|
+
#
|
|
16
|
+
# @contextmanager
|
|
17
|
+
# def managed(name):
|
|
18
|
+
# print('enter', name)
|
|
19
|
+
# try:
|
|
20
|
+
# yield name.upper()
|
|
21
|
+
# finally:
|
|
22
|
+
# print('exit', name)
|
|
23
|
+
#
|
|
24
|
+
# with managed('hello') as val:
|
|
25
|
+
# print(val) # HELLO
|
|
26
|
+
# # prints: enter hello / HELLO / exit hello
|
|
27
|
+
#
|
|
28
|
+
# with suppress(ValueError):
|
|
29
|
+
# int('not-a-number')
|
|
30
|
+
# # no exception raised
|
|
31
|
+
#
|
|
32
|
+
# stack = ExitStack()
|
|
33
|
+
# with stack:
|
|
34
|
+
# f = stack.enter_context(some_cm())
|
|
35
|
+
#
|
|
36
|
+
# Implementation notes:
|
|
37
|
+
# - contextmanager uses the JS generator protocol (.next() / .throw()).
|
|
38
|
+
# Since 'throw' is a reserved JS keyword it cannot appear as a bare
|
|
39
|
+
# identifier in RapydScript method-call syntax; the call is made via a
|
|
40
|
+
# verbatim v"""...""" block.
|
|
41
|
+
# - The with-statement machinery in RapydScript calls __exit__() (0 args)
|
|
42
|
+
# when no exception occurred and __exit__(exc_type, exc_val, exc_tb)
|
|
43
|
+
# (3 args) when one did. All __exit__ implementations here use default
|
|
44
|
+
# parameters (=None) so both signatures work transparently.
|
|
45
|
+
# - ExitStack.__exit__ may raise a new exception that replaces the original;
|
|
46
|
+
# the with-statement machinery propagates whatever __exit__ throws.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# RuntimeError is not defined in the RapydScript baselib; define it here so
|
|
50
|
+
# that the contextmanager helpers can raise it for protocol violations.
|
|
51
|
+
class RuntimeError(Exception):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AbstractContextManager:
|
|
56
|
+
"""
|
|
57
|
+
ABC for context managers that provides a default __enter__ returning self.
|
|
58
|
+
|
|
59
|
+
Subclasses must override __exit__. A default no-op __exit__ is also
|
|
60
|
+
provided so that plain subclasses work without override if no cleanup is
|
|
61
|
+
needed.
|
|
62
|
+
|
|
63
|
+
Example::
|
|
64
|
+
|
|
65
|
+
class Timed(AbstractContextManager):
|
|
66
|
+
def __enter__(self):
|
|
67
|
+
self._start = Date.now()
|
|
68
|
+
return self
|
|
69
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
70
|
+
print('elapsed:', Date.now() - self._start, 'ms')
|
|
71
|
+
return False
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __enter__(self):
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class closing:
|
|
82
|
+
"""
|
|
83
|
+
Context manager that calls thing.close() when the block exits.
|
|
84
|
+
|
|
85
|
+
Equivalent to Python's contextlib.closing.
|
|
86
|
+
|
|
87
|
+
Example::
|
|
88
|
+
|
|
89
|
+
from contextlib import closing
|
|
90
|
+
|
|
91
|
+
with closing(open_connection()) as conn:
|
|
92
|
+
conn.send(data)
|
|
93
|
+
# conn.close() is guaranteed to be called
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, thing):
|
|
97
|
+
self.thing = thing
|
|
98
|
+
|
|
99
|
+
def __enter__(self):
|
|
100
|
+
return self.thing
|
|
101
|
+
|
|
102
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
103
|
+
self.thing.close()
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class nullcontext:
|
|
108
|
+
"""
|
|
109
|
+
No-op context manager. __enter__ returns enter_result (default None).
|
|
110
|
+
|
|
111
|
+
Useful as a conditional stand-in when you optionally want a real context
|
|
112
|
+
manager::
|
|
113
|
+
|
|
114
|
+
cm = open_lock() if need_lock else nullcontext()
|
|
115
|
+
with cm:
|
|
116
|
+
do_work()
|
|
117
|
+
|
|
118
|
+
Also handy for parameterised tests::
|
|
119
|
+
|
|
120
|
+
with nullcontext(42) as val:
|
|
121
|
+
assert val == 42
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def __init__(self, enter_result=None):
|
|
125
|
+
self.enter_result = enter_result
|
|
126
|
+
|
|
127
|
+
def __enter__(self):
|
|
128
|
+
return self.enter_result
|
|
129
|
+
|
|
130
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class suppress:
|
|
135
|
+
"""
|
|
136
|
+
Context manager that silently suppresses the listed exception types.
|
|
137
|
+
|
|
138
|
+
If a listed exception is raised inside the block the block is exited
|
|
139
|
+
and execution continues after the with statement. Any other exception
|
|
140
|
+
type propagates normally.
|
|
141
|
+
|
|
142
|
+
Example::
|
|
143
|
+
|
|
144
|
+
from contextlib import suppress
|
|
145
|
+
|
|
146
|
+
with suppress(KeyError, ValueError):
|
|
147
|
+
d = {}
|
|
148
|
+
_ = d['missing'] # KeyError suppressed
|
|
149
|
+
# execution continues here
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def __init__(self, *exceptions):
|
|
153
|
+
self._exceptions = exceptions
|
|
154
|
+
|
|
155
|
+
def __enter__(self):
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
159
|
+
if exc_type is None:
|
|
160
|
+
return False
|
|
161
|
+
for exc_class in self._exceptions:
|
|
162
|
+
if isinstance(exc_val, exc_class):
|
|
163
|
+
return True
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def contextmanager(func):
|
|
168
|
+
"""
|
|
169
|
+
Decorator that turns a generator function into a context manager.
|
|
170
|
+
|
|
171
|
+
The decorated function must contain exactly one ``yield`` expression.
|
|
172
|
+
Everything before the yield is the __enter__ body; everything after is
|
|
173
|
+
the __exit__ body. The yielded value is bound to the ``as`` target.
|
|
174
|
+
|
|
175
|
+
Exception handling works as in Python: if an exception is raised inside
|
|
176
|
+
the with block it is thrown into the generator at the yield point. The
|
|
177
|
+
generator may catch and suppress it (by not re-raising) or let it
|
|
178
|
+
propagate.
|
|
179
|
+
|
|
180
|
+
Example::
|
|
181
|
+
|
|
182
|
+
from contextlib import contextmanager
|
|
183
|
+
|
|
184
|
+
@contextmanager
|
|
185
|
+
def temp_value(d, key, val):
|
|
186
|
+
old = d.get(key)
|
|
187
|
+
d[key] = val
|
|
188
|
+
try:
|
|
189
|
+
yield val
|
|
190
|
+
finally:
|
|
191
|
+
if old is None:
|
|
192
|
+
del d[key]
|
|
193
|
+
else:
|
|
194
|
+
d[key] = old
|
|
195
|
+
|
|
196
|
+
d = {}
|
|
197
|
+
with temp_value(d, 'x', 99) as v:
|
|
198
|
+
assert v == 99
|
|
199
|
+
assert d['x'] == 99
|
|
200
|
+
assert 'x' not in d
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
def _make_cm(*args, **kwargs):
|
|
204
|
+
gen = func(*args, **kwargs)
|
|
205
|
+
|
|
206
|
+
class _GeneratorContextManager:
|
|
207
|
+
|
|
208
|
+
def __enter__(self):
|
|
209
|
+
r = gen.next()
|
|
210
|
+
if r.done:
|
|
211
|
+
raise RuntimeError('generator did not yield')
|
|
212
|
+
return r.value
|
|
213
|
+
|
|
214
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
215
|
+
if exc_type is None:
|
|
216
|
+
# No-exception path: advance the generator to completion.
|
|
217
|
+
r = gen.next()
|
|
218
|
+
if not r.done:
|
|
219
|
+
raise RuntimeError('generator did not stop after yield')
|
|
220
|
+
return False
|
|
221
|
+
# Exception path: throw exc_val into the generator.
|
|
222
|
+
# 'throw' is a reserved JS keyword so we use verbatim JS.
|
|
223
|
+
v"""
|
|
224
|
+
var ρσ_cm_result;
|
|
225
|
+
try {
|
|
226
|
+
ρσ_cm_result = gen.throw(exc_val);
|
|
227
|
+
} catch (ρσ_cm_err) {
|
|
228
|
+
// Generator did not catch the exception (re-raised or threw a
|
|
229
|
+
// different one). If it's the same object, return false so
|
|
230
|
+
// the with-machinery re-raises it. Otherwise let the new
|
|
231
|
+
// exception propagate from __exit__.
|
|
232
|
+
if (ρσ_cm_err === exc_val) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
throw ρσ_cm_err;
|
|
236
|
+
}
|
|
237
|
+
// Generator caught the exception and returned normally.
|
|
238
|
+
if (!ρσ_cm_result.done) {
|
|
239
|
+
throw new RuntimeError('generator did not stop after throw()');
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
return _GeneratorContextManager()
|
|
245
|
+
|
|
246
|
+
return _make_cm
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class ExitStack:
|
|
250
|
+
"""
|
|
251
|
+
Context manager for dynamic stacks of context managers and callbacks.
|
|
252
|
+
|
|
253
|
+
Maintains a LIFO stack of registered cleanup actions. All registered
|
|
254
|
+
actions are guaranteed to run on exit, even if earlier ones raise.
|
|
255
|
+
|
|
256
|
+
Key methods:
|
|
257
|
+
enter_context(cm) — enter *cm* and push its __exit__ for cleanup
|
|
258
|
+
push(exit_cb) — push a raw (exc_type, exc_val, exc_tb) callback
|
|
259
|
+
or a context manager (its __exit__ is pushed)
|
|
260
|
+
callback(func, *a, **kw) — push func(*a, **kw) as a no-exc callback
|
|
261
|
+
close() — trigger exit immediately (convenience)
|
|
262
|
+
|
|
263
|
+
Example::
|
|
264
|
+
|
|
265
|
+
from contextlib import ExitStack
|
|
266
|
+
|
|
267
|
+
with ExitStack() as stack:
|
|
268
|
+
files = [stack.enter_context(open_file(name)) for name in names]
|
|
269
|
+
# all files closed on exit, even if some raises
|
|
270
|
+
|
|
271
|
+
# Manually-built stack:
|
|
272
|
+
stack = ExitStack()
|
|
273
|
+
conn = stack.enter_context(get_connection())
|
|
274
|
+
stack.callback(log, 'connection cleaned up')
|
|
275
|
+
try:
|
|
276
|
+
use(conn)
|
|
277
|
+
finally:
|
|
278
|
+
stack.close()
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
def __init__(self):
|
|
282
|
+
self._exit_callbacks = []
|
|
283
|
+
|
|
284
|
+
def __enter__(self):
|
|
285
|
+
return self
|
|
286
|
+
|
|
287
|
+
def push(self, exit_or_cm):
|
|
288
|
+
"""
|
|
289
|
+
Register an exit callback or a context manager.
|
|
290
|
+
|
|
291
|
+
If *exit_or_cm* has an ``__exit__`` attribute it is treated as a
|
|
292
|
+
context manager (its __exit__ is registered but __enter__ is NOT
|
|
293
|
+
called — use enter_context() if you need that). Otherwise it must be
|
|
294
|
+
a callable with the ``(exc_type, exc_val, exc_tb)`` signature.
|
|
295
|
+
|
|
296
|
+
Returns *exit_or_cm* unchanged, so it can be used as a decorator.
|
|
297
|
+
"""
|
|
298
|
+
if hasattr(exit_or_cm, '__exit__'):
|
|
299
|
+
_cm = exit_or_cm
|
|
300
|
+
def _cm_exit(exc_type=None, exc_val=None, exc_tb=None):
|
|
301
|
+
return _cm.__exit__(exc_type, exc_val, exc_tb)
|
|
302
|
+
self._exit_callbacks.append(_cm_exit)
|
|
303
|
+
else:
|
|
304
|
+
self._exit_callbacks.append(exit_or_cm)
|
|
305
|
+
return exit_or_cm
|
|
306
|
+
|
|
307
|
+
def enter_context(self, cm):
|
|
308
|
+
"""
|
|
309
|
+
Enter a context manager and push its __exit__ for cleanup.
|
|
310
|
+
|
|
311
|
+
Returns the value returned by cm.__enter__().
|
|
312
|
+
"""
|
|
313
|
+
result = cm.__enter__()
|
|
314
|
+
_cm2 = cm
|
|
315
|
+
def _cm2_exit(exc_type=None, exc_val=None, exc_tb=None):
|
|
316
|
+
return _cm2.__exit__(exc_type, exc_val, exc_tb)
|
|
317
|
+
self._exit_callbacks.append(_cm2_exit)
|
|
318
|
+
return result
|
|
319
|
+
|
|
320
|
+
def callback(self, func, *args, **kwargs):
|
|
321
|
+
"""
|
|
322
|
+
Register a plain callback to run on exit, ignoring exception info.
|
|
323
|
+
|
|
324
|
+
The callback is called as func(*args, **kwargs) and its return value
|
|
325
|
+
is ignored. Returns *func* unchanged.
|
|
326
|
+
"""
|
|
327
|
+
stored_args = args
|
|
328
|
+
stored_kwargs = kwargs
|
|
329
|
+
def _plain_cb(exc_type=None, exc_val=None, exc_tb=None):
|
|
330
|
+
func(*stored_args, **stored_kwargs)
|
|
331
|
+
return False
|
|
332
|
+
self._exit_callbacks.append(_plain_cb)
|
|
333
|
+
return func
|
|
334
|
+
|
|
335
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
336
|
+
"""
|
|
337
|
+
Run all registered cleanups in LIFO order.
|
|
338
|
+
|
|
339
|
+
Returns True if an active exception was suppressed by any cleanup.
|
|
340
|
+
If a cleanup raises, that exception becomes the active exception for
|
|
341
|
+
subsequent cleanups; if it is still active at the end it propagates
|
|
342
|
+
from __exit__ (replacing the original).
|
|
343
|
+
"""
|
|
344
|
+
received_exc = exc_type is not None
|
|
345
|
+
suppressed = False
|
|
346
|
+
|
|
347
|
+
current_type = exc_type
|
|
348
|
+
current_val = exc_val
|
|
349
|
+
current_tb = exc_tb
|
|
350
|
+
|
|
351
|
+
new_exc = None
|
|
352
|
+
raised_new = False
|
|
353
|
+
|
|
354
|
+
while len(self._exit_callbacks) > 0:
|
|
355
|
+
cb = self._exit_callbacks.pop()
|
|
356
|
+
try:
|
|
357
|
+
result = cb(current_type, current_val, current_tb)
|
|
358
|
+
if result:
|
|
359
|
+
suppressed = True
|
|
360
|
+
current_type = None
|
|
361
|
+
current_val = None
|
|
362
|
+
current_tb = None
|
|
363
|
+
except Exception as e:
|
|
364
|
+
# Cleanup raised — this becomes the active exception.
|
|
365
|
+
new_exc = e
|
|
366
|
+
raised_new = True
|
|
367
|
+
current_type = e.__class__
|
|
368
|
+
current_val = e
|
|
369
|
+
current_tb = None
|
|
370
|
+
suppressed = False
|
|
371
|
+
|
|
372
|
+
if raised_new:
|
|
373
|
+
raise new_exc
|
|
374
|
+
|
|
375
|
+
return received_exc and suppressed
|
|
376
|
+
|
|
377
|
+
def close(self):
|
|
378
|
+
"""Exit the stack without an active exception."""
|
|
379
|
+
self.__exit__(None, None, None)
|
package/src/lib/copy.pyj
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: BSD
|
|
3
|
-
# RapydScript implementation of Python's copy standard library.
|
|
4
|
-
#
|
|
5
|
-
# Supported: copy, deepcopy
|
|
6
|
-
# Classes may define __copy__() and __deepcopy__(memo) for custom behaviour.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def _is_primitive(x):
|
|
10
|
-
t = jstype(x)
|
|
11
|
-
return x is None or t is 'number' or t is 'boolean' or t is 'string' or t is 'undefined'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def copy(x):
|
|
15
|
-
"""Return a shallow copy of x.
|
|
16
|
-
|
|
17
|
-
For immutable primitives (numbers, strings, booleans, None) the object
|
|
18
|
-
itself is returned unchanged. For containers a new container of the same
|
|
19
|
-
type is created whose top-level items are the same objects as in the
|
|
20
|
-
original.
|
|
21
|
-
|
|
22
|
-
Dispatch order:
|
|
23
|
-
1. Primitive → return as-is.
|
|
24
|
-
2. ``__copy__`` method → call and return.
|
|
25
|
-
3. list → ``list(x)`` (slice).
|
|
26
|
-
4. set → ``set(x)``.
|
|
27
|
-
5. frozenset → ``frozenset(x)``.
|
|
28
|
-
6. dict (ρσ_dict) → ``x.copy()``.
|
|
29
|
-
7. Plain JS object (constructor is Object or null-proto) → Object.assign.
|
|
30
|
-
8. Other object (class instance) → Object.create + Object.assign.
|
|
31
|
-
"""
|
|
32
|
-
if _is_primitive(x):
|
|
33
|
-
return x
|
|
34
|
-
if jstype(x.__copy__) is 'function':
|
|
35
|
-
return x.__copy__()
|
|
36
|
-
if Array.isArray(x):
|
|
37
|
-
return list(x)
|
|
38
|
-
if isinstance(x, set):
|
|
39
|
-
return set(x)
|
|
40
|
-
if isinstance(x, frozenset):
|
|
41
|
-
return frozenset(x)
|
|
42
|
-
if isinstance(x, dict):
|
|
43
|
-
return x.copy()
|
|
44
|
-
proto = Object.getPrototypeOf(x)
|
|
45
|
-
if x.constructor is Object or proto is None:
|
|
46
|
-
return Object.assign({}, x)
|
|
47
|
-
# Class instance: create a same-prototype object and copy own properties.
|
|
48
|
-
result = Object.create(proto)
|
|
49
|
-
Object.assign(result, x)
|
|
50
|
-
return result
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def deepcopy(x, memo=None):
|
|
54
|
-
"""Return a deep (recursive) copy of x.
|
|
55
|
-
|
|
56
|
-
Circular references are handled via the *memo* mapping (a JS Map), which
|
|
57
|
-
stores already-copied objects so that they are only copied once.
|
|
58
|
-
|
|
59
|
-
Dispatch order (same structure as ``copy`` but recursive):
|
|
60
|
-
1. Primitive → return as-is.
|
|
61
|
-
2. Memo hit → return the previously copied object.
|
|
62
|
-
3. ``__deepcopy__(memo)`` method → call and return.
|
|
63
|
-
4. list → recurse into elements.
|
|
64
|
-
5. set → recurse into elements, build new set.
|
|
65
|
-
6. frozenset → recurse into elements, build new frozenset.
|
|
66
|
-
7. dict → recurse into keys and values.
|
|
67
|
-
8. Plain JS object → recurse into own-enumerable properties.
|
|
68
|
-
9. Class instance → recurse into own-enumerable properties.
|
|
69
|
-
"""
|
|
70
|
-
if memo is None:
|
|
71
|
-
memo = v'new Map()'
|
|
72
|
-
if _is_primitive(x):
|
|
73
|
-
return x
|
|
74
|
-
if memo.has(x):
|
|
75
|
-
return memo.get(x)
|
|
76
|
-
if jstype(x.__deepcopy__) is 'function':
|
|
77
|
-
result = x.__deepcopy__(memo)
|
|
78
|
-
memo.set(x, result)
|
|
79
|
-
return result
|
|
80
|
-
if Array.isArray(x):
|
|
81
|
-
result = []
|
|
82
|
-
memo.set(x, result)
|
|
83
|
-
for i in range(x.length):
|
|
84
|
-
result.push(deepcopy(x[i], memo))
|
|
85
|
-
return result
|
|
86
|
-
if isinstance(x, set):
|
|
87
|
-
result = set()
|
|
88
|
-
memo.set(x, result)
|
|
89
|
-
iterator = x[ρσ_iterator_symbol]()
|
|
90
|
-
r = iterator.next()
|
|
91
|
-
while not r.done:
|
|
92
|
-
result.add(deepcopy(r.value, memo))
|
|
93
|
-
r = iterator.next()
|
|
94
|
-
return result
|
|
95
|
-
if isinstance(x, frozenset):
|
|
96
|
-
items = []
|
|
97
|
-
iterator = x[ρσ_iterator_symbol]()
|
|
98
|
-
r = iterator.next()
|
|
99
|
-
while not r.done:
|
|
100
|
-
items.push(deepcopy(r.value, memo))
|
|
101
|
-
r = iterator.next()
|
|
102
|
-
result = frozenset(items)
|
|
103
|
-
memo.set(x, result)
|
|
104
|
-
return result
|
|
105
|
-
if isinstance(x, dict):
|
|
106
|
-
result = dict()
|
|
107
|
-
memo.set(x, result)
|
|
108
|
-
iterator = x.items()
|
|
109
|
-
r = iterator.next()
|
|
110
|
-
while not r.done:
|
|
111
|
-
result.set(deepcopy(r.value[0], memo), deepcopy(r.value[1], memo))
|
|
112
|
-
r = iterator.next()
|
|
113
|
-
return result
|
|
114
|
-
proto = Object.getPrototypeOf(x)
|
|
115
|
-
result = Object.create(proto)
|
|
116
|
-
memo.set(x, result)
|
|
117
|
-
keys = Object.keys(x)
|
|
118
|
-
for i in range(keys.length):
|
|
119
|
-
result[keys[i]] = deepcopy(x[keys[i]], memo)
|
|
120
|
-
return result
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD
|
|
3
|
+
# RapydScript implementation of Python's copy standard library.
|
|
4
|
+
#
|
|
5
|
+
# Supported: copy, deepcopy
|
|
6
|
+
# Classes may define __copy__() and __deepcopy__(memo) for custom behaviour.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _is_primitive(x):
|
|
10
|
+
t = jstype(x)
|
|
11
|
+
return x is None or t is 'number' or t is 'boolean' or t is 'string' or t is 'undefined'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def copy(x):
|
|
15
|
+
"""Return a shallow copy of x.
|
|
16
|
+
|
|
17
|
+
For immutable primitives (numbers, strings, booleans, None) the object
|
|
18
|
+
itself is returned unchanged. For containers a new container of the same
|
|
19
|
+
type is created whose top-level items are the same objects as in the
|
|
20
|
+
original.
|
|
21
|
+
|
|
22
|
+
Dispatch order:
|
|
23
|
+
1. Primitive → return as-is.
|
|
24
|
+
2. ``__copy__`` method → call and return.
|
|
25
|
+
3. list → ``list(x)`` (slice).
|
|
26
|
+
4. set → ``set(x)``.
|
|
27
|
+
5. frozenset → ``frozenset(x)``.
|
|
28
|
+
6. dict (ρσ_dict) → ``x.copy()``.
|
|
29
|
+
7. Plain JS object (constructor is Object or null-proto) → Object.assign.
|
|
30
|
+
8. Other object (class instance) → Object.create + Object.assign.
|
|
31
|
+
"""
|
|
32
|
+
if _is_primitive(x):
|
|
33
|
+
return x
|
|
34
|
+
if jstype(x.__copy__) is 'function':
|
|
35
|
+
return x.__copy__()
|
|
36
|
+
if Array.isArray(x):
|
|
37
|
+
return list(x)
|
|
38
|
+
if isinstance(x, set):
|
|
39
|
+
return set(x)
|
|
40
|
+
if isinstance(x, frozenset):
|
|
41
|
+
return frozenset(x)
|
|
42
|
+
if isinstance(x, dict):
|
|
43
|
+
return x.copy()
|
|
44
|
+
proto = Object.getPrototypeOf(x)
|
|
45
|
+
if x.constructor is Object or proto is None:
|
|
46
|
+
return Object.assign({}, x)
|
|
47
|
+
# Class instance: create a same-prototype object and copy own properties.
|
|
48
|
+
result = Object.create(proto)
|
|
49
|
+
Object.assign(result, x)
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def deepcopy(x, memo=None):
|
|
54
|
+
"""Return a deep (recursive) copy of x.
|
|
55
|
+
|
|
56
|
+
Circular references are handled via the *memo* mapping (a JS Map), which
|
|
57
|
+
stores already-copied objects so that they are only copied once.
|
|
58
|
+
|
|
59
|
+
Dispatch order (same structure as ``copy`` but recursive):
|
|
60
|
+
1. Primitive → return as-is.
|
|
61
|
+
2. Memo hit → return the previously copied object.
|
|
62
|
+
3. ``__deepcopy__(memo)`` method → call and return.
|
|
63
|
+
4. list → recurse into elements.
|
|
64
|
+
5. set → recurse into elements, build new set.
|
|
65
|
+
6. frozenset → recurse into elements, build new frozenset.
|
|
66
|
+
7. dict → recurse into keys and values.
|
|
67
|
+
8. Plain JS object → recurse into own-enumerable properties.
|
|
68
|
+
9. Class instance → recurse into own-enumerable properties.
|
|
69
|
+
"""
|
|
70
|
+
if memo is None:
|
|
71
|
+
memo = v'new Map()'
|
|
72
|
+
if _is_primitive(x):
|
|
73
|
+
return x
|
|
74
|
+
if memo.has(x):
|
|
75
|
+
return memo.get(x)
|
|
76
|
+
if jstype(x.__deepcopy__) is 'function':
|
|
77
|
+
result = x.__deepcopy__(memo)
|
|
78
|
+
memo.set(x, result)
|
|
79
|
+
return result
|
|
80
|
+
if Array.isArray(x):
|
|
81
|
+
result = []
|
|
82
|
+
memo.set(x, result)
|
|
83
|
+
for i in range(x.length):
|
|
84
|
+
result.push(deepcopy(x[i], memo))
|
|
85
|
+
return result
|
|
86
|
+
if isinstance(x, set):
|
|
87
|
+
result = set()
|
|
88
|
+
memo.set(x, result)
|
|
89
|
+
iterator = x[ρσ_iterator_symbol]()
|
|
90
|
+
r = iterator.next()
|
|
91
|
+
while not r.done:
|
|
92
|
+
result.add(deepcopy(r.value, memo))
|
|
93
|
+
r = iterator.next()
|
|
94
|
+
return result
|
|
95
|
+
if isinstance(x, frozenset):
|
|
96
|
+
items = []
|
|
97
|
+
iterator = x[ρσ_iterator_symbol]()
|
|
98
|
+
r = iterator.next()
|
|
99
|
+
while not r.done:
|
|
100
|
+
items.push(deepcopy(r.value, memo))
|
|
101
|
+
r = iterator.next()
|
|
102
|
+
result = frozenset(items)
|
|
103
|
+
memo.set(x, result)
|
|
104
|
+
return result
|
|
105
|
+
if isinstance(x, dict):
|
|
106
|
+
result = dict()
|
|
107
|
+
memo.set(x, result)
|
|
108
|
+
iterator = x.items()
|
|
109
|
+
r = iterator.next()
|
|
110
|
+
while not r.done:
|
|
111
|
+
result.set(deepcopy(r.value[0], memo), deepcopy(r.value[1], memo))
|
|
112
|
+
r = iterator.next()
|
|
113
|
+
return result
|
|
114
|
+
proto = Object.getPrototypeOf(x)
|
|
115
|
+
result = Object.create(proto)
|
|
116
|
+
memo.set(x, result)
|
|
117
|
+
keys = Object.keys(x)
|
|
118
|
+
for i in range(keys.length):
|
|
119
|
+
result[keys[i]] = deepcopy(x[keys[i]], memo)
|
|
120
|
+
return result
|