rapydscript-ns 0.8.4 → 0.9.0
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 +18 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +715 -169
- package/TODO.md +9 -2
- 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 +36 -27
- package/package.json +1 -1
- package/publish.py +37 -37
- package/release/baselib-plain-pretty.js +2358 -168
- package/release/baselib-plain-ugly.js +73 -3
- package/release/compiler.js +6282 -3092
- package/release/signatures.json +31 -30
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +1 -0
- package/src/baselib-builtins.pyj +340 -2
- package/src/baselib-bytes.pyj +664 -0
- package/src/baselib-errors.pyj +1 -1
- package/src/baselib-internal.pyj +267 -60
- package/src/baselib-itertools.pyj +110 -97
- package/src/baselib-str.pyj +22 -4
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/abc.pyj +317 -0
- package/src/lib/aes.pyj +646 -646
- package/src/lib/copy.pyj +120 -120
- package/src/lib/dataclasses.pyj +532 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/enum.pyj +125 -0
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/itertools.pyj +580 -580
- 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/re.pyj +504 -470
- package/src/lib/react.pyj +74 -74
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/typing.pyj +577 -0
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/builtins.js +14 -4
- package/src/monaco-language-service/diagnostics.js +19 -20
- package/src/monaco-language-service/dts.js +550 -550
- package/src/output/classes.pyj +62 -26
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/functions.pyj +78 -5
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +5 -2
- package/src/output/operators.pyj +100 -34
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +80 -16
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +9 -4
- 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/abc.pyj +291 -0
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/arithmetic_nostrict.pyj +88 -0
- package/test/arithmetic_types.pyj +169 -0
- package/test/baselib.pyj +91 -0
- package/test/bytes.pyj +467 -0
- package/test/classes.pyj +1 -0
- package/test/comparison_ops.pyj +173 -0
- package/test/dataclasses.pyj +253 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/enum.pyj +134 -0
- package/test/eval_exec.pyj +56 -0
- package/test/format.pyj +148 -0
- 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/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/object.pyj +64 -0
- package/test/omit_function_metadata.pyj +20 -20
- package/test/python_compat.pyj +17 -15
- package/test/python_features.pyj +70 -15
- package/test/regexp.pyj +83 -55
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/tuples.pyj +96 -0
- package/test/typing.pyj +469 -0
- package/test/unit/index.js +116 -7
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +84 -0
- package/test/unit/web-repl.js +804 -1
- package/test/vars_locals_globals.pyj +94 -0
- package/tools/cli.js +558 -547
- package/tools/compile.js +224 -219
- package/tools/completer.js +131 -131
- package/tools/embedded_compiler.js +262 -251
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/lint.js +16 -19
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/main.js +252 -252
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
- package/PYTHON_DIFFERENCES_REPORT.md +0 -291
- package/PYTHON_FEATURE_COVERAGE.md +0 -200
package/test/object.pyj
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# globals: assrt
|
|
2
|
+
|
|
3
|
+
ae = assrt.equal
|
|
4
|
+
ade = assrt.deepEqual
|
|
5
|
+
ok = assrt.ok
|
|
6
|
+
|
|
7
|
+
# ── Basic instantiation ───────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
o = object()
|
|
10
|
+
ok(o is not None)
|
|
11
|
+
ok(o is not undefined)
|
|
12
|
+
|
|
13
|
+
# ── Sentinel pattern: each call returns a distinct instance ───────────────────
|
|
14
|
+
|
|
15
|
+
s1 = object()
|
|
16
|
+
s2 = object()
|
|
17
|
+
ok(s1 is not s2)
|
|
18
|
+
|
|
19
|
+
# ── isinstance ────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
ok(isinstance(o, object))
|
|
22
|
+
ok(isinstance(s1, object))
|
|
23
|
+
ok(isinstance(s2, object))
|
|
24
|
+
|
|
25
|
+
# ── repr / str ────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
r = repr(o)
|
|
28
|
+
ok(r.startsWith('<object object'))
|
|
29
|
+
|
|
30
|
+
# ── hash: stable and distinct between instances ───────────────────────────────
|
|
31
|
+
|
|
32
|
+
h1 = hash(s1)
|
|
33
|
+
h2 = hash(s2)
|
|
34
|
+
ok(jstype(h1) is 'number')
|
|
35
|
+
ok(h1 is not h2)
|
|
36
|
+
ae(hash(s1), h1) # hash is stable across calls
|
|
37
|
+
|
|
38
|
+
# ── Subclassing: class Foo(object) ────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
class Widget(object):
|
|
41
|
+
def __init__(self, name):
|
|
42
|
+
self.name = name
|
|
43
|
+
|
|
44
|
+
def greet(self):
|
|
45
|
+
return 'Hello, ' + self.name
|
|
46
|
+
|
|
47
|
+
w = Widget('world')
|
|
48
|
+
ok(isinstance(w, Widget))
|
|
49
|
+
ok(isinstance(w, object))
|
|
50
|
+
ae(w.greet(), 'Hello, world')
|
|
51
|
+
|
|
52
|
+
# ── Multiple inheritance with object as one parent ────────────────────────────
|
|
53
|
+
|
|
54
|
+
class Mixin:
|
|
55
|
+
def tag(self):
|
|
56
|
+
return 'mixin'
|
|
57
|
+
|
|
58
|
+
class Combined(Mixin, object):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
c = Combined()
|
|
62
|
+
ok(isinstance(c, Combined))
|
|
63
|
+
ok(isinstance(c, object))
|
|
64
|
+
ae(c.tag(), 'mixin')
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
# globals: RapydScript
|
|
2
|
-
|
|
3
|
-
# Test that omit_function_metadata flag suppresses __module__ and __argnames__
|
|
4
|
-
code = 'def foo(a, b):\n return a + b'
|
|
5
|
-
ast = RapydScript.parse(code, {'filename': '<test>'})
|
|
6
|
-
|
|
7
|
-
# Without flag: output should contain __module__ and __argnames__
|
|
8
|
-
output_with = v'new RapydScript.OutputStream({"beautify": true, "omit_baselib": true})'
|
|
9
|
-
ast.print(output_with)
|
|
10
|
-
result_with = output_with.get()
|
|
11
|
-
assrt.ok(result_with.indexOf('__module__') >= 0, '__module__ should appear in default output')
|
|
12
|
-
assrt.ok(result_with.indexOf('__argnames__') >= 0, '__argnames__ should appear in default output')
|
|
13
|
-
|
|
14
|
-
# With flag: output should not contain __module__, __argnames__, or Object.defineProperties
|
|
15
|
-
output_without = v'new RapydScript.OutputStream({"beautify": true, "omit_baselib": true, "omit_function_metadata": true})'
|
|
16
|
-
ast.print(output_without)
|
|
17
|
-
result_without = output_without.get()
|
|
18
|
-
assrt.ok(result_without.indexOf('__module__') is -1, '__module__ should not appear when omit_function_metadata is set')
|
|
19
|
-
assrt.ok(result_without.indexOf('__argnames__') is -1, '__argnames__ should not appear when omit_function_metadata is set')
|
|
20
|
-
assrt.ok(result_without.indexOf('Object.defineProperties') is -1, 'Object.defineProperties should not appear when omit_function_metadata is set')
|
|
1
|
+
# globals: RapydScript
|
|
2
|
+
|
|
3
|
+
# Test that omit_function_metadata flag suppresses __module__ and __argnames__
|
|
4
|
+
code = 'def foo(a, b):\n return a + b'
|
|
5
|
+
ast = RapydScript.parse(code, {'filename': '<test>'})
|
|
6
|
+
|
|
7
|
+
# Without flag: output should contain __module__ and __argnames__
|
|
8
|
+
output_with = v'new RapydScript.OutputStream({"beautify": true, "omit_baselib": true})'
|
|
9
|
+
ast.print(output_with)
|
|
10
|
+
result_with = output_with.get()
|
|
11
|
+
assrt.ok(result_with.indexOf('__module__') >= 0, '__module__ should appear in default output')
|
|
12
|
+
assrt.ok(result_with.indexOf('__argnames__') >= 0, '__argnames__ should appear in default output')
|
|
13
|
+
|
|
14
|
+
# With flag: output should not contain __module__, __argnames__, or Object.defineProperties
|
|
15
|
+
output_without = v'new RapydScript.OutputStream({"beautify": true, "omit_baselib": true, "omit_function_metadata": true})'
|
|
16
|
+
ast.print(output_without)
|
|
17
|
+
result_without = output_without.get()
|
|
18
|
+
assrt.ok(result_without.indexOf('__module__') is -1, '__module__ should not appear when omit_function_metadata is set')
|
|
19
|
+
assrt.ok(result_without.indexOf('__argnames__') is -1, '__argnames__ should not appear when omit_function_metadata is set')
|
|
20
|
+
assrt.ok(result_without.indexOf('Object.defineProperties') is -1, 'Object.defineProperties should not appear when omit_function_metadata is set')
|
package/test/python_compat.pyj
CHANGED
|
@@ -115,20 +115,22 @@ _a8 += [3, 4]
|
|
|
115
115
|
ade(_a8, [1, 2, 3, 4])
|
|
116
116
|
ade(_b8, [1, 2, 3, 4]) # _b8 still points to same object, now extended
|
|
117
117
|
|
|
118
|
-
# ── 8. Ordering operators on lists —
|
|
118
|
+
# ── 8. Ordering operators on lists — behaviour depends on overload_operators ──
|
|
119
119
|
# Python: [10] < [9] compares element-wise → False (10 > 9).
|
|
120
|
-
#
|
|
121
|
-
#
|
|
120
|
+
# Without overload_operators (tested here): < falls through to JS string
|
|
121
|
+
# coercion → '[10]' < '[9]' → True (char comparison on the first differing
|
|
122
|
+
# digit: '1' < '9'). This is a common gotcha with multi-digit numbers.
|
|
123
|
+
#
|
|
124
|
+
# With overload_operators (the default when using rapydscript compile or the
|
|
125
|
+
# web-repl), < dispatches through ρσ_op_lt which does lexicographic comparison
|
|
126
|
+
# of list elements — matching Python semantics ([10] < [9] → False).
|
|
122
127
|
|
|
123
128
|
# RapydScript lists have a Python-style __str__, so [10].toString() = '[10]'.
|
|
124
|
-
# String compare of '[10]' vs '[9]': '[' = '[', then '1' < '9' →
|
|
125
|
-
ok([10] < [9]) # True
|
|
126
|
-
ae([9] < [10], False) # False
|
|
127
|
-
|
|
128
|
-
# Compare: Python uses numeric element-wise ordering, so [10] > [9] (10 > 9).
|
|
129
|
-
# RapydScript gives the opposite result for these specific values.
|
|
129
|
+
# String compare of '[10]' vs '[9]': '[' = '[', then '1' < '9' → True.
|
|
130
|
+
ok([10] < [9]) # True (string: '[10]' < '[9]') — no overload_operators
|
|
131
|
+
ae([9] < [10], False) # False (string: '[9]' > '[10]')
|
|
130
132
|
|
|
131
|
-
#
|
|
133
|
+
# Direct numeric comparison is always unambiguous:
|
|
132
134
|
ok(10 > 9) # direct numeric comparison always works
|
|
133
135
|
|
|
134
136
|
# ── 9. List sort() — Python numeric sort (jssort() for JS lexicographic) ──────
|
|
@@ -233,18 +235,18 @@ strings()
|
|
|
233
235
|
ae(' hello '.strip(), 'hello')
|
|
234
236
|
ae('hello world'.upper(), 'HELLO WORLD')
|
|
235
237
|
|
|
236
|
-
# ── 15. strings() leaves
|
|
238
|
+
# ── 15. strings() leaves replace() as JS version; split() defaults to ' ' ─────
|
|
237
239
|
# (strings() is active from section 14 above)
|
|
238
240
|
# Python str.split() with no args: splits on any whitespace, strips leading/trailing.
|
|
239
|
-
#
|
|
241
|
+
# RapydScript .split() with no args: transpiles to .split(" ") — splits on single space.
|
|
240
242
|
|
|
241
243
|
# Python semantics via str module:
|
|
242
244
|
ade(str.split('a b'), ['a', 'b']) # Python split on any whitespace
|
|
243
245
|
ade(str.split('a b c'), ['a', 'b', 'c'])
|
|
244
246
|
|
|
245
|
-
#
|
|
246
|
-
ade('a b'.split(), ['a
|
|
247
|
-
ade('a b'.split(' '), ['a', 'b']) #
|
|
247
|
+
# .split() with no args now defaults to splitting on ' ':
|
|
248
|
+
ade('a b'.split(), ['a', '', 'b']) # double space → empty token between
|
|
249
|
+
ade('a b'.split(' '), ['a', 'b']) # explicit ' ' separator still works
|
|
248
250
|
|
|
249
251
|
# Python str.replace() vs JS String.prototype.replace():
|
|
250
252
|
# Python replaces ALL occurrences by default.
|
package/test/python_features.pyj
CHANGED
|
@@ -366,14 +366,41 @@ def _test_frozenset():
|
|
|
366
366
|
_test_frozenset()
|
|
367
367
|
|
|
368
368
|
# ── 17. int.bit_length() ─────────────────────────────────────────────────────
|
|
369
|
-
# STATUS:
|
|
370
|
-
|
|
371
|
-
|
|
369
|
+
# STATUS: ✓ SUPPORTED — Number.prototype.bit_length added to baselib.
|
|
370
|
+
|
|
371
|
+
def _test_int_bit_length():
|
|
372
|
+
ae((0).bit_length(), 0)
|
|
373
|
+
ae((1).bit_length(), 1)
|
|
374
|
+
ae((2).bit_length(), 2)
|
|
375
|
+
ae((3).bit_length(), 2)
|
|
376
|
+
ae((4).bit_length(), 3)
|
|
377
|
+
ae((255).bit_length(), 8)
|
|
378
|
+
ae((256).bit_length(), 9)
|
|
379
|
+
ae((1023).bit_length(), 10)
|
|
380
|
+
ae((1024).bit_length(), 11)
|
|
381
|
+
# Negative numbers: bit_length ignores sign (Python semantics)
|
|
382
|
+
ae((-1).bit_length(), 1)
|
|
383
|
+
ae((-5).bit_length(), 3)
|
|
384
|
+
ae((-255).bit_length(), 8)
|
|
385
|
+
|
|
386
|
+
_test_int_bit_length()
|
|
372
387
|
|
|
373
388
|
# ── 18. float.is_integer() ───────────────────────────────────────────────────
|
|
374
|
-
# STATUS:
|
|
375
|
-
|
|
376
|
-
|
|
389
|
+
# STATUS: ✓ SUPPORTED — Number.prototype.is_integer added to baselib.
|
|
390
|
+
|
|
391
|
+
def _test_float_is_integer():
|
|
392
|
+
ae((1.0).is_integer(), True)
|
|
393
|
+
ae((1.5).is_integer(), False)
|
|
394
|
+
ae((0.0).is_integer(), True)
|
|
395
|
+
ae((-2.0).is_integer(), True)
|
|
396
|
+
ae((-2.5).is_integer(), False)
|
|
397
|
+
ae((1e10).is_integer(), True)
|
|
398
|
+
ae((1.0000000001).is_integer(), False)
|
|
399
|
+
# NaN and Infinity are not integers (JS literals)
|
|
400
|
+
ae(v'Infinity'.is_integer(), False)
|
|
401
|
+
ae(v'NaN'.is_integer(), False)
|
|
402
|
+
|
|
403
|
+
_test_float_is_integer()
|
|
377
404
|
|
|
378
405
|
# ── 19. dict | merge operator (Python 3.9+) ──────────────────────────────────
|
|
379
406
|
# STATUS: ✓ WORKS — requires `from __python__ import overload_operators, dict_literals`
|
|
@@ -526,9 +553,20 @@ ae(g.next().value, 1)
|
|
|
526
553
|
ae(g.next('hello').value, 'hello')
|
|
527
554
|
|
|
528
555
|
# ── 27. zip(strict=True) ─────────────────────────────────────────────────────
|
|
529
|
-
# STATUS:
|
|
556
|
+
# STATUS: ✓ WORKS — equal-length iterables succeed; mismatched raise ValueError.
|
|
530
557
|
|
|
531
558
|
ade(list(zip([1, 2], [3, 4])), [[1, 3], [2, 4]]) # basic zip still works
|
|
559
|
+
ade(list(zip([1, 2], [3, 4], strict=True)), [[1, 3], [2, 4]]) # strict, equal lengths OK
|
|
560
|
+
try:
|
|
561
|
+
list(zip([1, 2], [3], strict=True))
|
|
562
|
+
ae(True, False) # should not reach here
|
|
563
|
+
except ValueError:
|
|
564
|
+
pass # expected: first iterable longer
|
|
565
|
+
try:
|
|
566
|
+
list(zip([1], [3, 4], strict=True))
|
|
567
|
+
ae(True, False) # should not reach here
|
|
568
|
+
except ValueError:
|
|
569
|
+
pass # expected: second iterable longer
|
|
532
570
|
|
|
533
571
|
# ── 28. Walrus operator := ────────────────────────────────────────────────────
|
|
534
572
|
# STATUS: ✓ WORKS — hoisting in if/while conditions; comprehension filter scope
|
|
@@ -874,11 +912,25 @@ ae(_c(), 2)
|
|
|
874
912
|
ae(_c(), 3)
|
|
875
913
|
|
|
876
914
|
# ── 42. Complex number literals 3+4j ─────────────────────────────────────────
|
|
877
|
-
# STATUS:
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
915
|
+
# STATUS: ✓ SUPPORTED — j suffix, complex() builtin, arithmetic, abs(), conjugate()
|
|
916
|
+
def _test_complex():
|
|
917
|
+
from __python__ import overload_operators
|
|
918
|
+
c = 3+4j
|
|
919
|
+
ae(c.real, 3)
|
|
920
|
+
ae(c.imag, 4)
|
|
921
|
+
ae(abs(c), 5.0)
|
|
922
|
+
ade(c.conjugate(), complex(3, -4))
|
|
923
|
+
ade(c + complex(1, 2), complex(4, 6))
|
|
924
|
+
ade(c - complex(1, 1), complex(2, 3))
|
|
925
|
+
ade(c * complex(0, 1), complex(-4, 3))
|
|
926
|
+
ade(c / complex(1, 0), complex(3, 4))
|
|
927
|
+
ade(complex(5), complex(5, 0))
|
|
928
|
+
ade(complex(), complex(0, 0))
|
|
929
|
+
ae(repr(1j), '1j')
|
|
930
|
+
ae(repr(complex(3, -4)), '(3-4j)')
|
|
931
|
+
ade(1 + 2j, complex(1, 2))
|
|
932
|
+
ade(-1j, complex(0, -1))
|
|
933
|
+
_test_complex()
|
|
882
934
|
|
|
883
935
|
# ── 43. b'...' bytes literals ────────────────────────────────────────────────
|
|
884
936
|
# STATUS: ✗ NOT SUPPORTED — no b prefix; no native bytes type.
|
|
@@ -940,8 +992,10 @@ ae(_c(), 3)
|
|
|
940
992
|
# STATUS: ✗ NOT SUPPORTED — no attribute-lookup override.
|
|
941
993
|
|
|
942
994
|
# ── 50. __format__ dunder ────────────────────────────────────────────────────
|
|
943
|
-
# STATUS: ✓ WORKS — format(
|
|
944
|
-
#
|
|
995
|
+
# STATUS: ✓ WORKS — format(), str.format(), and f-strings all dispatch to
|
|
996
|
+
# obj.__format__(spec). Default __format__ auto-generated for classes.
|
|
997
|
+
# !r/!s/!a transformers bypass __format__ correctly.
|
|
998
|
+
# Tested fully in test/format.pyj.
|
|
945
999
|
|
|
946
1000
|
# ── 51. __class_getitem__ ────────────────────────────────────────────────────
|
|
947
1001
|
# STATUS: ✓ WORKS — Class[item] dispatches to __class_getitem__(cls, item).
|
|
@@ -1099,7 +1153,8 @@ _check_unhashable({'a': 1})
|
|
|
1099
1153
|
|
|
1100
1154
|
# ── 55. format(value, spec) builtin ──────────────────────────────────────────
|
|
1101
1155
|
# STATUS: ✓ WORKS — format(value, spec) is defined and dispatches to __format__.
|
|
1102
|
-
#
|
|
1156
|
+
# str.format() and f-strings also dispatch to __format__.
|
|
1157
|
+
# Tested fully in test/format.pyj and test/str.pyj.
|
|
1103
1158
|
#
|
|
1104
1159
|
# ae(format(3.14159, '.2f'), '3.14')
|
|
1105
1160
|
|
package/test/regexp.pyj
CHANGED
|
@@ -1,55 +1,83 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: BSD
|
|
3
|
-
# Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
|
4
|
-
|
|
5
|
-
# Test the re module
|
|
6
|
-
import re
|
|
7
|
-
s = "Isaac Newton, physicist"
|
|
8
|
-
c = re.search("(\\w+) (\\w+)", s)
|
|
9
|
-
assrt.equal(c.group(1), 'Isaac')
|
|
10
|
-
assrt.equal(c.group(2), 'Newton')
|
|
11
|
-
c = re.match("(\\w+) (\\w+)", s)
|
|
12
|
-
assrt.equal(c.group(1), 'Isaac')
|
|
13
|
-
assrt.equal(c.start(1), 0)
|
|
14
|
-
assrt.equal(c.end(1), c.group(1).length)
|
|
15
|
-
assrt.equal(c.start(2), c.group(1).length + 1)
|
|
16
|
-
assrt.equal(c.group(2), 'Newton')
|
|
17
|
-
m = re.search('a(b)cd', 'abc abcd')
|
|
18
|
-
assrt.equal(m.group(1), 'b')
|
|
19
|
-
assrt.equal(m.start(1), m.string.lastIndexOf('b'))
|
|
20
|
-
assrt.deepEqual(re.split('\\s', s), ['Isaac', 'Newton,', 'physicist'])
|
|
21
|
-
assrt.deepEqual(re.findall('s[a-z]', s), ['sa', 'si', 'st'])
|
|
22
|
-
assrt.deepEqual([m.group() for m in re.finditer('s[a-z]', s)], ['sa', 'si', 'st'])
|
|
23
|
-
assrt.deepEqual(re.findall(/s[a-z]/, s), ['sa', 'si', 'st'])
|
|
24
|
-
assrt.equal(re.sub('[A-Z]', '_', s), '_saac _ewton, physicist')
|
|
25
|
-
assrt.equal(re.sub('[A-Z]', '_', s, count=1), '_saac Newton, physicist')
|
|
26
|
-
assrt.equal(re.search('a[.]b', 'axb'), None)
|
|
27
|
-
assrt.equal(re.search(r'a\.b', 'axb'), None)
|
|
28
|
-
assrt.equal(re.search('a[.]b', 'axb', flags=re.D), None)
|
|
29
|
-
assrt.equal(re.search('.+', 'a\nb').group(), 'a')
|
|
30
|
-
assrt.equal(re.search('.+', 'a\nb', flags=re.D).group(), 'a\nb')
|
|
31
|
-
assrt.equal(re.search('(?s).+', 'a\nb').group(), 'a\nb')
|
|
32
|
-
assrt.equal(re.sub('a(b)', r'xx', 'ab'), r'xx')
|
|
33
|
-
assrt.equal(re.sub('a(b)', r'\\1', 'ab'), r'\1')
|
|
34
|
-
assrt.equal(re.sub('a(b)', r'\\\1', 'ab'), r'\b')
|
|
35
|
-
assrt.equal(re.sub('a(b)', r'\g<1>', 'ab'), r'b')
|
|
36
|
-
assrt.equal(re.sub('a(b)', def(m):return m.group(1);, 'ab'), r'b')
|
|
37
|
-
assrt.equal(']', re.match('[]]', ']').group())
|
|
38
|
-
|
|
39
|
-
assrt.throws(def():re.search(r'(?(1)a|b)b', 'ab');, re.error)
|
|
40
|
-
|
|
41
|
-
# Test lookbehind assertions
|
|
42
|
-
assrt.equal('acdb', re.sub(r'(?<=a)b', 'c', 'abdb'))
|
|
43
|
-
|
|
44
|
-
# Test
|
|
45
|
-
assrt.equal('
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
assrt.
|
|
49
|
-
|
|
50
|
-
# Test
|
|
51
|
-
assrt.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD
|
|
3
|
+
# Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
|
4
|
+
|
|
5
|
+
# Test the re module
|
|
6
|
+
import re
|
|
7
|
+
s = "Isaac Newton, physicist"
|
|
8
|
+
c = re.search("(\\w+) (\\w+)", s)
|
|
9
|
+
assrt.equal(c.group(1), 'Isaac')
|
|
10
|
+
assrt.equal(c.group(2), 'Newton')
|
|
11
|
+
c = re.match("(\\w+) (\\w+)", s)
|
|
12
|
+
assrt.equal(c.group(1), 'Isaac')
|
|
13
|
+
assrt.equal(c.start(1), 0)
|
|
14
|
+
assrt.equal(c.end(1), c.group(1).length)
|
|
15
|
+
assrt.equal(c.start(2), c.group(1).length + 1)
|
|
16
|
+
assrt.equal(c.group(2), 'Newton')
|
|
17
|
+
m = re.search('a(b)cd', 'abc abcd')
|
|
18
|
+
assrt.equal(m.group(1), 'b')
|
|
19
|
+
assrt.equal(m.start(1), m.string.lastIndexOf('b'))
|
|
20
|
+
assrt.deepEqual(re.split('\\s', s), ['Isaac', 'Newton,', 'physicist'])
|
|
21
|
+
assrt.deepEqual(re.findall('s[a-z]', s), ['sa', 'si', 'st'])
|
|
22
|
+
assrt.deepEqual([m.group() for m in re.finditer('s[a-z]', s)], ['sa', 'si', 'st'])
|
|
23
|
+
assrt.deepEqual(re.findall(/s[a-z]/, s), ['sa', 'si', 'st'])
|
|
24
|
+
assrt.equal(re.sub('[A-Z]', '_', s), '_saac _ewton, physicist')
|
|
25
|
+
assrt.equal(re.sub('[A-Z]', '_', s, count=1), '_saac Newton, physicist')
|
|
26
|
+
assrt.equal(re.search('a[.]b', 'axb'), None)
|
|
27
|
+
assrt.equal(re.search(r'a\.b', 'axb'), None)
|
|
28
|
+
assrt.equal(re.search('a[.]b', 'axb', flags=re.D), None)
|
|
29
|
+
assrt.equal(re.search('.+', 'a\nb').group(), 'a')
|
|
30
|
+
assrt.equal(re.search('.+', 'a\nb', flags=re.D).group(), 'a\nb')
|
|
31
|
+
assrt.equal(re.search('(?s).+', 'a\nb').group(), 'a\nb')
|
|
32
|
+
assrt.equal(re.sub('a(b)', r'xx', 'ab'), r'xx')
|
|
33
|
+
assrt.equal(re.sub('a(b)', r'\\1', 'ab'), r'\1')
|
|
34
|
+
assrt.equal(re.sub('a(b)', r'\\\1', 'ab'), r'\b')
|
|
35
|
+
assrt.equal(re.sub('a(b)', r'\g<1>', 'ab'), r'b')
|
|
36
|
+
assrt.equal(re.sub('a(b)', def(m):return m.group(1);, 'ab'), r'b')
|
|
37
|
+
assrt.equal(']', re.match('[]]', ']').group())
|
|
38
|
+
|
|
39
|
+
assrt.throws(def():re.search(r'(?(1)a|b)b', 'ab');, re.error)
|
|
40
|
+
|
|
41
|
+
# Test positive lookbehind assertions
|
|
42
|
+
assrt.equal('acdb', re.sub(r'(?<=a)b', 'c', 'abdb'))
|
|
43
|
+
|
|
44
|
+
# Test negative lookbehind assertions
|
|
45
|
+
assrt.equal('accd', re.sub(r'(?<!a)b', 'c', 'acbd'))
|
|
46
|
+
|
|
47
|
+
# Test variable-width lookbehind (ES2018+)
|
|
48
|
+
assrt.equal('c', re.search(r'(?<=ab+)c', 'abbc').group())
|
|
49
|
+
|
|
50
|
+
# Test fullmatch
|
|
51
|
+
assrt.ok(re.fullmatch(r'\w+', 'hello') is not None)
|
|
52
|
+
assrt.ok(re.fullmatch(r'\w+', 'hello world') is None)
|
|
53
|
+
assrt.ok(re.fullmatch(r'\w+', '') is None)
|
|
54
|
+
assrt.equal(re.fullmatch(r'(\w+)', 'hello').group(1), 'hello')
|
|
55
|
+
assrt.ok(re.fullmatch(r'[a-z]+', 'hello') is not None)
|
|
56
|
+
assrt.ok(re.fullmatch(r'[a-z]+', 'hello!') is None)
|
|
57
|
+
|
|
58
|
+
# Test accurate group start/end positions (ES2022 'd' flag; falls back to heuristic)
|
|
59
|
+
m = re.match(r'(a)(b)(c)', 'abc')
|
|
60
|
+
assrt.equal(m.start(1), 0)
|
|
61
|
+
assrt.equal(m.end(1), 1)
|
|
62
|
+
assrt.equal(m.start(2), 1)
|
|
63
|
+
assrt.equal(m.end(2), 2)
|
|
64
|
+
assrt.equal(m.start(3), 2)
|
|
65
|
+
assrt.equal(m.end(3), 3)
|
|
66
|
+
|
|
67
|
+
# Test NOFLAG and S (DOTALL) aliases
|
|
68
|
+
assrt.equal(re.NOFLAG, 0)
|
|
69
|
+
assrt.equal(re.S, re.DOTALL)
|
|
70
|
+
assrt.equal(re.search('.+', 'a\nb', flags=re.S).group(), 'a\nb')
|
|
71
|
+
|
|
72
|
+
# Test named groups
|
|
73
|
+
assrt.equal('aa', re.sub(r'(?P<a>a)b', r'\g<a>\1', 'ab'))
|
|
74
|
+
assrt.equal('bb', re.sub(r'(?P<a>a)(?P=a)', r'bb', 'aa'))
|
|
75
|
+
assrt.equal('ab', re.sub(r'(.)(?P<a>a)', r'\g<a>\1', 'ba'))
|
|
76
|
+
assrt.deepEqual({'a':'a', 'b':'b'}, re.search(r'(?P<a>a)(?P<b>b)', 'ab').groupdict())
|
|
77
|
+
|
|
78
|
+
# Test verbose mode literals
|
|
79
|
+
assrt.equal(re.search(///
|
|
80
|
+
a
|
|
81
|
+
. # anything
|
|
82
|
+
b
|
|
83
|
+
///, ' axb').group(), 'axb')
|