pyscript-programming-language 1.15.0__tar.gz → 1.15.1__tar.gz

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 (91) hide show
  1. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/PKG-INFO +20 -17
  2. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/README.md +19 -16
  3. pyscript_programming_language-1.15.1/changelog.md +13 -0
  4. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyproject.toml +1 -1
  5. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/__main__.py +6 -4
  6. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/editor/gui.py +1 -1
  7. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/interpreter.py +6 -21
  8. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/mapping.py +8 -5
  9. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/nodes.py +14 -5
  10. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/parser.py +45 -19
  11. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/pysbuiltins.py +28 -27
  12. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/pystypes.py +1 -1
  13. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/runner.py +4 -3
  14. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/token.py +1 -0
  15. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/debug.py +2 -2
  16. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/decorators.py +3 -2
  17. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/generic.py +3 -2
  18. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/module.py +3 -0
  19. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/string.py +3 -1
  20. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/version.py +2 -2
  21. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/brainfuck.pys +17 -23
  22. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/dual/__init__.pys +5 -3
  23. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/dual/py_dual.py +94 -49
  24. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/getch/py_getch.py +2 -2
  25. pyscript_programming_language-1.15.1/pyscript/other/message.txt +62 -0
  26. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript_programming_language.egg-info/PKG-INFO +20 -17
  27. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript_programming_language.egg-info/SOURCES.txt +1 -0
  28. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/setup.py +2 -2
  29. pyscript_programming_language-1.15.0/changelog.md +0 -7
  30. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/MANIFEST.in +0 -0
  31. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/__init__.py +0 -0
  32. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/__init__.pyi +0 -0
  33. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/__init__.py +0 -0
  34. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/analyzer.py +0 -0
  35. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/bases.py +0 -0
  36. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/buffer.py +0 -0
  37. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/cache.py +0 -0
  38. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/checks.py +0 -0
  39. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/constants.py +0 -0
  40. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/context.py +0 -0
  41. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/editor/__init__.py +0 -0
  42. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/editor/bases.py +0 -0
  43. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/editor/terminal.py +0 -0
  44. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/exceptions.py +0 -0
  45. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/handlers.py +0 -0
  46. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/highlight.py +0 -0
  47. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/lexer.py +0 -0
  48. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/position.py +0 -0
  49. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/results.py +0 -0
  50. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/shell.py +0 -0
  51. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/symtab.py +0 -0
  52. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/__init__.py +0 -0
  53. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/ansi.py +0 -0
  54. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/path.py +0 -0
  55. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/core/utils/similarity.py +0 -0
  56. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/__hello__.pys +0 -0
  57. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/ansi.pys +0 -0
  58. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/ast/__init__.pys +0 -0
  59. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/ast/ast_dump.py +0 -0
  60. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/ast/ast_literal_eval.py +0 -0
  61. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/ast/ast_unparse.py +0 -0
  62. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/ast/ast_walk.py +0 -0
  63. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/code.pys +0 -0
  64. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/dis.pys +0 -0
  65. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/explorer.pys +0 -0
  66. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/fpstimer/__init__.pys +0 -0
  67. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/fpstimer/py_fpstimer.py +0 -0
  68. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/getch/__init__.pys +0 -0
  69. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/history.pys +0 -0
  70. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/inspect.pys +0 -0
  71. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/jsdict.pys +0 -0
  72. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/keyword.pys +0 -0
  73. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/opcode.pys +0 -0
  74. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/parser.pys +0 -0
  75. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/pdisplay.pys +0 -0
  76. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/site.pys +0 -0
  77. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/symtable.pys +0 -0
  78. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/token.pys +0 -0
  79. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/tokenize/__init__.pys +0 -0
  80. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/lib/tokenize/tok_untokenize.py +0 -0
  81. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/other/.nomedia +0 -0
  82. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/other/PyScript.ico +0 -0
  83. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/other/copyright +0 -0
  84. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/other/credits +0 -0
  85. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/other/license +0 -0
  86. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/site-packages/this.pys +0 -0
  87. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript/this.py +0 -0
  88. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript_programming_language.egg-info/dependency_links.txt +0 -0
  89. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript_programming_language.egg-info/requires.txt +0 -0
  90. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/pyscript_programming_language.egg-info/top_level.txt +0 -0
  91. {pyscript_programming_language-1.15.0 → pyscript_programming_language-1.15.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyscript-programming-language
3
- Version: 1.15.0
3
+ Version: 1.15.1
4
4
  Summary: PyScript Programming Language
5
5
  Home-page: https://azzammuhyala.github.io/pyscript
6
6
  Author: AzzamMuhyala
@@ -125,22 +125,25 @@ Familiar? There it is!
125
125
  | 20 | `typing` | **required** |
126
126
  | 21 | `unicodedata` | **required** |
127
127
  | 1 | `ast` | **required (library)** |
128
- | 2 | `msvcrt` | **required (library)** |
129
- | 3 | `pprint` | **required (library)** |
130
- | 4 | `shutil` | **required (library)** |
131
- | 5 | `signal` | **required (library)** |
132
- | 6 | `stat` | **required (library)** |
133
- | 7 | `termios` | **required (library)** |
134
- | 8 | `time` | **required (library)** |
135
- | 9 | `tty` | **required (library)** |
128
+ | 2 | `math` | **required (library)** |
129
+ | 3 | `msvcrt` | **required (library)** |
130
+ | 4 | `pprint` | **required (library)** |
131
+ | 5 | `shutil` | **required (library)** |
132
+ | 6 | `signal` | **required (library)** |
133
+ | 7 | `stat` | **required (library)** |
134
+ | 8 | `termios` | **required (library)** |
135
+ | 9 | `time` | **required (library)** |
136
+ | 10 | `tty` | **required (library)** |
136
137
  | 1 | `_colorize` | **optional** |
137
138
  | 2 | `beartype` | **optional** |
138
- | 3 | `prompt_toolkit` | **optional** |
139
- | 4 | `pygments` | **optional** |
140
- | 5 | `readline` | **optional** |
141
- | 6 | `threading` | **optional** |
142
- | 7 | `tkinter` | **optional** |
143
- | 8 | `IPython` | **optional** |
139
+ | 3 | `ctypes` | **optional** |
140
+ | 4 | `numpy` | **optional** |
141
+ | 5 | `prompt_toolkit` | **optional** |
142
+ | 6 | `pygments` | **optional** |
143
+ | 7 | `readline` | **optional** |
144
+ | 8 | `threading` | **optional** |
145
+ | 9 | `tkinter` | **optional** |
146
+ | 10 | `IPython` | **optional** |
144
147
 
145
148
  ### Status Explanation
146
149
  - **required**: Required by PyScript entirely.
@@ -148,9 +151,9 @@ Familiar? There it is!
148
151
  - **optional**: Not required, but if it is present, some features can be used without issue.
149
152
 
150
153
  ## Highlights 🎨
151
- - [PyScript plugin for Acode (Ace Editor)](https://github.com/azzammuhyala/pyscript/releases/download/v1.13.3/plugin-1.0.2.zip)
154
+ - [PyScript plugin for Acode](https://github.com/azzammuhyala/pyscript/tree/main/highlight/acode)
152
155
  - [PyScript nano for Linux](https://github.com/azzammuhyala/pyscript/tree/main/highlight/nano)
153
- - [PyScript extension for VSCode](https://marketplace.visualstudio.com/items?itemName=azzammuhyala.pyslang)
156
+ - [PyScript extension for VSCode](https://github.com/azzammuhyala/pyscript/tree/main/highlight/vscode)
154
157
 
155
158
  ## Behind It 🕰️
156
159
  This language created from based up on a
@@ -93,22 +93,25 @@ Familiar? There it is!
93
93
  | 20 | `typing` | **required** |
94
94
  | 21 | `unicodedata` | **required** |
95
95
  | 1 | `ast` | **required (library)** |
96
- | 2 | `msvcrt` | **required (library)** |
97
- | 3 | `pprint` | **required (library)** |
98
- | 4 | `shutil` | **required (library)** |
99
- | 5 | `signal` | **required (library)** |
100
- | 6 | `stat` | **required (library)** |
101
- | 7 | `termios` | **required (library)** |
102
- | 8 | `time` | **required (library)** |
103
- | 9 | `tty` | **required (library)** |
96
+ | 2 | `math` | **required (library)** |
97
+ | 3 | `msvcrt` | **required (library)** |
98
+ | 4 | `pprint` | **required (library)** |
99
+ | 5 | `shutil` | **required (library)** |
100
+ | 6 | `signal` | **required (library)** |
101
+ | 7 | `stat` | **required (library)** |
102
+ | 8 | `termios` | **required (library)** |
103
+ | 9 | `time` | **required (library)** |
104
+ | 10 | `tty` | **required (library)** |
104
105
  | 1 | `_colorize` | **optional** |
105
106
  | 2 | `beartype` | **optional** |
106
- | 3 | `prompt_toolkit` | **optional** |
107
- | 4 | `pygments` | **optional** |
108
- | 5 | `readline` | **optional** |
109
- | 6 | `threading` | **optional** |
110
- | 7 | `tkinter` | **optional** |
111
- | 8 | `IPython` | **optional** |
107
+ | 3 | `ctypes` | **optional** |
108
+ | 4 | `numpy` | **optional** |
109
+ | 5 | `prompt_toolkit` | **optional** |
110
+ | 6 | `pygments` | **optional** |
111
+ | 7 | `readline` | **optional** |
112
+ | 8 | `threading` | **optional** |
113
+ | 9 | `tkinter` | **optional** |
114
+ | 10 | `IPython` | **optional** |
112
115
 
113
116
  ### Status Explanation
114
117
  - **required**: Required by PyScript entirely.
@@ -116,9 +119,9 @@ Familiar? There it is!
116
119
  - **optional**: Not required, but if it is present, some features can be used without issue.
117
120
 
118
121
  ## Highlights 🎨
119
- - [PyScript plugin for Acode (Ace Editor)](https://github.com/azzammuhyala/pyscript/releases/download/v1.13.3/plugin-1.0.2.zip)
122
+ - [PyScript plugin for Acode](https://github.com/azzammuhyala/pyscript/tree/main/highlight/acode)
120
123
  - [PyScript nano for Linux](https://github.com/azzammuhyala/pyscript/tree/main/highlight/nano)
121
- - [PyScript extension for VSCode](https://marketplace.visualstudio.com/items?itemName=azzammuhyala.pyslang)
124
+ - [PyScript extension for VSCode](https://github.com/azzammuhyala/pyscript/tree/main/highlight/vscode)
122
125
 
123
126
  ## Behind It 🕰️
124
127
  This language created from based up on a
@@ -0,0 +1,13 @@
1
+ # Change Logs
2
+
3
+ ## [1.15.1] - 30/10/2026
4
+
5
+ ### Added
6
+ - `dual` supports conversion to a NumPy array represented as a 2x2 matrix definition of a dual number
7
+ $a + b\epsilon = \begin{pmatrix} a & b \\ 0 & a \end{pmatrix}$ and includes several magic methods.
8
+ - Added a new syntax `not instanceof`.
9
+
10
+ ### Fixed
11
+ - Fixed several bugs.
12
+ - The refresh delay for highlights in the GUI editor has been set to 1000ms.
13
+ - _etc._
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyscript-programming-language"
7
- version = "1.15.0"
7
+ version = "1.15.1"
8
8
  description = "PyScript Programming Language"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.10"
@@ -13,6 +13,7 @@ from .core.utils.debug import USE_NOTEBOOK, custom_print
13
13
  from .core.utils.generic import is_environ
14
14
  from .core.utils.module import find_module_path, remove_python_path
15
15
  from .core.utils.path import getcwd, base, normpath
16
+ from .core.utils.string import join
16
17
  from .core.version import __version__
17
18
 
18
19
  if PYGMENTS:
@@ -41,7 +42,7 @@ if sys.version_info >= (3, 13):
41
42
  from _colorize import can_colorize
42
43
  COLORIZE = can_colorize()
43
44
  except:
44
- # respect for NO_COLOR, see more here: https://no-color.org
45
+ # rule NO_COLOR, see more here: https://no-color.org
45
46
  COLORIZE = not is_environ('NO_COLOR')
46
47
  else:
47
48
  COLORIZE = True
@@ -354,10 +355,11 @@ def execute(file: PysFileBuffer) -> None:
354
355
  code, _ = result.end_process()
355
356
 
356
357
  if inspect:
357
- if sys.stdout.closed or sys.stderr.closed:
358
+ if sys.stderr.closed:
358
359
  code = 1
359
- elif sys.stdin.closed:
360
- custom_print("Can't run interactive shell: sys.stdin closed", file=sys.stderr)
360
+ elif sys.stdout.closed or sys.stdin.closed:
361
+ io_closed = join([f'sys.{std}' for std in ('stdout', 'stdin') if getattr(sys, std).closed])
362
+ custom_print(f"Cannot run interactive shell: {io_closed} closed", file=sys.stderr)
361
363
  code = 1
362
364
  else:
363
365
  code = pys_shell(
@@ -258,7 +258,7 @@ try:
258
258
  self.title(f'PyScript {__version__} - {self.basename}{"*" if self.modified else ""}')
259
259
  if self.refresh_job is not None:
260
260
  self.after_cancel(self.refresh_job)
261
- self.refresh_job = self.after(500, refresh_highlight)
261
+ self.refresh_job = self.after(1000, refresh_highlight)
262
262
 
263
263
  else:
264
264
  def update():
@@ -582,11 +582,8 @@ def visit_SwitchNode(node: PysSwitchNode, context: PysContext) -> PysRunTimeResu
582
582
 
583
583
  if (fall_through or no_match_found) and (ndefault := node.default):
584
584
  register(get_visitor(ndefault.__class__)(ndefault, context))
585
- if should_return():
586
- if result.should_break:
587
- result.should_break = False
588
- else:
589
- return result
585
+ if should_return() and not result.should_break:
586
+ return result
590
587
 
591
588
  return result.success(None)
592
589
 
@@ -902,10 +899,7 @@ def visit_ForNode(node: PysForNode, context: PysContext) -> PysRunTimeResult:
902
899
  if should_return():
903
900
  return result
904
901
 
905
- if result.should_break:
906
- result.should_break = False
907
-
908
- elif nelse := node.else_:
902
+ if not result.should_break and (nelse := node.else_):
909
903
  register(get_visitor(nelse.__class__)(nelse, context))
910
904
  if should_return():
911
905
  return result
@@ -945,10 +939,7 @@ def visit_WhileNode(node: PysWhileNode, context: PysContext) -> PysRunTimeResult
945
939
  else:
946
940
  return result
947
941
 
948
- if result.should_break:
949
- result.should_break = False
950
-
951
- elif nelse := node.else_:
942
+ if not result.should_break and (nelse := node.else_):
952
943
  register(get_visitor(nelse.__class__)(nelse, context))
953
944
  if should_return():
954
945
  return result
@@ -988,10 +979,7 @@ def visit_DoWhileNode(node: PysDoWhileNode, context: PysContext) -> PysRunTimeRe
988
979
  if should_return():
989
980
  return result
990
981
 
991
- if result.should_break:
992
- result.should_break = False
993
-
994
- elif nelse := node.else_:
982
+ if not result.should_break and (nelse := node.else_):
995
983
  register(get_visitor(nelse.__class__)(nelse, context))
996
984
  if should_return():
997
985
  return result
@@ -1033,10 +1021,7 @@ def visit_RepeatNode(node: PysRepeatNode, context: PysContext) -> PysRunTimeResu
1033
1021
  if should_return():
1034
1022
  return result
1035
1023
 
1036
- if result.should_break:
1037
- result.should_break = False
1038
-
1039
- elif nelse := node.else_:
1024
+ if not result.should_break and (nelse := node.else_):
1040
1025
  register(get_visitor(nelse.__class__)(nelse, context))
1041
1026
  if should_return():
1042
1027
  return result
@@ -9,16 +9,18 @@ from operator import (
9
9
  neg, inv
10
10
  )
11
11
 
12
- contains = lambda a, b : a in b
13
- not_contains = lambda a, b : a not in b
14
- and_op = lambda a, b : a and b
15
- or_op = lambda a, b : a or b
16
- typeof = lambda a : type(a).__name__
12
+ contains = lambda a, b : a in b
13
+ not_contains = lambda a, b : a not in b
14
+ and_op = lambda a, b : a and b
15
+ or_op = lambda a, b : a or b
16
+ not_isinstance = lambda a, b : not isinstance(a, b)
17
+ typeof = lambda a : type(a).__name__
17
18
 
18
19
  GET_BINARY_FUNCTION = {
19
20
  TOKENS['IS']: is_,
20
21
  TOKENS['IS_NOT']: is_not,
21
22
  TOKENS['INSTANCEOF']: isinstance,
23
+ TOKENS['NOT_INSTANCEOF']: not_isinstance,
22
24
  TOKENS['PERCENT']: mod,
23
25
  TOKENS['AMPERSAND']: and_,
24
26
  TOKENS['STAR']: mul,
@@ -150,6 +152,7 @@ SYMBOLS_TOKEN_MAP = dfrozen({
150
152
  TOKENS['IS_NOT']: 'is not',
151
153
  TOKENS['TYPEOF']: 'typeof',
152
154
  TOKENS['INSTANCEOF']: 'instanceof',
155
+ TOKENS['NOT_INSTANCEOF']: 'not instanceof',
153
156
  TOKENS['NEWLINE']: '\n',
154
157
  TOKENS['EXCLAMATION']: '!',
155
158
  TOKENS['COMMENT']: '#',
@@ -398,8 +398,8 @@ class PysStatementsNode(PysNode):
398
398
  return elements[0]
399
399
 
400
400
  new = []
401
- constant_nodes = (PysConstantNode, PysDebugNode, PysEllipsisNode)
402
- interrupt_nodes = (PysReturnNode, PysThrowNode, PysContinueNode, PysBreakNode)
401
+ constant_nodes = {PysConstantNode, PysDebugNode, PysEllipsisNode}
402
+ interrupt_nodes = {PysReturnNode, PysThrowNode, PysContinueNode, PysBreakNode}
403
403
 
404
404
  for node in elements:
405
405
  if node.__class__ in constant_nodes:
@@ -478,9 +478,9 @@ class PysIfNode(PysNode):
478
478
  if not new_cases:
479
479
  return else_ if else_ else PysStatementsNode(position, [])
480
480
 
481
- elif len(new_cases) == 1:
481
+ elif not else_ and len(new_cases) == 1:
482
482
  condition, body = new_cases[0]
483
- if body.__class__ is PysIfNode and len(body.cases) == 1 and body.else_:
483
+ if body.__class__ is PysIfNode and len(body.cases) == 1 and not body.else_:
484
484
  inner_condition, inner_body = body.cases[0]
485
485
  new_cases = [(
486
486
  PysBinaryOperatorNode(
@@ -747,8 +747,17 @@ class PysGlobalNode(PysNode):
747
747
 
748
748
  @typecheck
749
749
  def __new__(cls, position: PysPosition, identifiers: list[PysToken]) -> 'PysGlobalNode':
750
+ registered = set()
751
+ new = []
752
+
753
+ for identifier in identifiers:
754
+ if identifier.value in registered:
755
+ continue
756
+ registered.add(identifier.value)
757
+ new.append(identifier)
758
+
750
759
  self = super().__new__(cls, position)
751
- setimuattr(self, 'identifiers', tuple(set(identifiers)))
760
+ setimuattr(self, 'identifiers', tuple(new))
752
761
  return self
753
762
 
754
763
  def __repr__(self) -> str:
@@ -401,7 +401,22 @@ class PysParser(Pys):
401
401
  )
402
402
 
403
403
  def instanceof(self) -> PysParserResult:
404
- return self.binary_operator(self.bitwise, (TOKENS['KEYWORD'], 'instanceof'), instanceof=True)
404
+ result = PysParserResult()
405
+
406
+ node = result.try_register(self.binary_operator(
407
+ self.bitwise,
408
+ (TOKENS['KEYWORD'], 'instanceof'),
409
+ (TOKENS['KEYWORD'], 'not'),
410
+ instanceof=True
411
+ ))
412
+
413
+ if result.error:
414
+ return result
415
+ elif node:
416
+ return result.success(node)
417
+
418
+ self.reverse(result.to_reverse_count)
419
+ return self.bitwise()
405
420
 
406
421
  def bitwise(self) -> PysParserResult:
407
422
  return self.binary_operator(
@@ -2325,15 +2340,12 @@ class PysParser(Pys):
2325
2340
  if result.error:
2326
2341
  return result
2327
2342
 
2328
- if body.__class__ == PysStatementsNode:
2329
- setimuattr(
2330
- body, 'position',
2331
- PysPosition(
2332
- self.current_token.position.file,
2333
- left_bracket_token.position.start,
2334
- end
2335
- )
2336
- )
2343
+ if body.__class__ is PysStatementsNode:
2344
+ setimuattr(body, 'position', PysPosition(
2345
+ self.current_token.position.file,
2346
+ left_bracket_token.position.start,
2347
+ end
2348
+ ))
2337
2349
 
2338
2350
  return result.success(body)
2339
2351
 
@@ -2350,10 +2362,7 @@ class PysParser(Pys):
2350
2362
 
2351
2363
  body = result.register(self.statement())
2352
2364
  if result.error:
2353
- return result.failure(
2354
- self.new_error("expected statement, expression, '{', or ';'"),
2355
- fatal=False
2356
- )
2365
+ return result.failure(self.new_error("expected statement, expression, '{', or ';'"), fatal=False)
2357
2366
 
2358
2367
  return result.success(body)
2359
2368
 
@@ -2393,7 +2402,7 @@ class PysParser(Pys):
2393
2402
  self.skip_expression(result)
2394
2403
 
2395
2404
  if not self.current_token.match(TOKENS['KEYWORD'], 'in'):
2396
- return result.failure(self.new_error("expected 'in'"))
2405
+ return result.failure(self.new_error("expected 'in' or 'instanceof'"))
2397
2406
 
2398
2407
  operations[-1] = PysToken(
2399
2408
  TOKENS['EXCLAMATION_GREATER_THAN'],
@@ -2483,11 +2492,28 @@ class PysParser(Pys):
2483
2492
  TOKENS['DOUBLE_AMPERSAND' if token.value == 'and' else 'DOUBLE_PIPE'],
2484
2493
  token.position
2485
2494
  )
2495
+
2486
2496
  elif instanceof:
2487
- operand = PysToken(
2488
- TOKENS['INSTANCEOF'],
2489
- token.position
2490
- )
2497
+
2498
+ if token.match(TOKENS['KEYWORD'], 'not'):
2499
+ result.register_advancement()
2500
+ self.advance()
2501
+ self.skip_expression(result)
2502
+
2503
+ if not self.current_token.match(TOKENS['KEYWORD'], 'instanceof'):
2504
+ return result.failure(self.new_error("expected 'instanceof'"), fatal=False)
2505
+
2506
+ operand = PysToken(
2507
+ TOKENS['NOT_INSTANCEOF'],
2508
+ token.position
2509
+ )
2510
+
2511
+ else:
2512
+ operand = PysToken(
2513
+ TOKENS['INSTANCEOF'],
2514
+ token.position
2515
+ )
2516
+
2491
2517
  else:
2492
2518
  operand = token
2493
2519
 
@@ -37,6 +37,7 @@ static_wrapper_function = (staticmethod,)
37
37
  wrapper_function = (MethodType, PysPythonFunction, classmethod)
38
38
  python_function = (BuiltinFunctionType, BuiltinMethodType, FunctionType)
39
39
 
40
+ pynewobject = object.__new__
40
41
  pyvars = builtins.vars
41
42
  pydir = builtins.dir
42
43
 
@@ -55,34 +56,36 @@ def _magic_method(
55
56
  return NotImplemented
56
57
 
57
58
  def _unpack_comprehension_function(context: PysContext, position: PysPosition, function: Callable) -> Callable:
58
- check = function
59
- final = function
60
- offset = 0
59
+ check = final = function
60
+ length = offset = 0
61
61
 
62
- if isinstance(function, wrapper_function):
62
+ b_isinstance = isinstance
63
+
64
+ if b_isinstance(function, wrapper_function):
63
65
  check = function.__func__
64
- offset += 2 if isinstance(function, PysPythonFunction) else 1
65
- elif isinstance(function, static_wrapper_function):
66
+ offset += 2 if b_isinstance(function, PysPythonFunction) else 1
67
+ elif b_isinstance(function, static_wrapper_function):
66
68
  check = function.__func__
69
+ elif b_isinstance(function, type):
70
+ offset += 1
71
+ check = function.__new__
72
+ if check is pynewobject:
73
+ check = function.__init__
67
74
 
68
- if isinstance(check, PysFunction):
75
+ if b_isinstance(check, PysFunction):
69
76
  length = max(check.__code__.parameters_length - offset, 0)
70
- if length == 0:
71
- def final(item):
72
- return function()
73
- elif length > 1:
74
- def final(item):
75
- return function(*item)
76
-
77
- elif isinstance(check, python_function):
77
+ elif b_isinstance(check, python_function):
78
78
  parameters = signature(check).parameters
79
79
  length = max(len(parameters) - offset, 0)
80
- if length == 0:
81
- def final(item):
82
- return function()
83
- elif length > 1 or any(p.kind == p.VAR_POSITIONAL for p in parameters.values()):
84
- def final(item):
85
- return function(*item)
80
+ if length == 1 and any(p.kind == p.VAR_POSITIONAL for p in parameters.values()):
81
+ length = 2
82
+
83
+ if length == 0:
84
+ def final(item):
85
+ return function()
86
+ elif length > 1:
87
+ def final(item):
88
+ return function(*item)
86
89
 
87
90
  handle_call(function, context, position)
88
91
  return final
@@ -116,8 +119,7 @@ def require(context, position, name):
116
119
  name: A name or path of the module to be imported.
117
120
  """
118
121
 
119
- name, *other_components = normstr(name).split('>')
120
-
122
+ name, *components = normstr(name).split('>')
121
123
  filename = context.file.name
122
124
  path, module_path = find_module_path(filename, name)
123
125
 
@@ -189,9 +191,8 @@ def require(context, position, name):
189
191
  finally:
190
192
  loading_modules.discard(module_path)
191
193
 
192
- for component in other_components:
194
+ for component in components:
193
195
  module = getattr(module, component)
194
-
195
196
  return module
196
197
 
197
198
  @PysBuiltinFunction
@@ -386,7 +387,7 @@ def dir(context, position, *args):
386
387
  def exec(context, position, source, globals=None):
387
388
 
388
389
  """
389
- exec(source: str | bytes, globals: Optional[dict]) -> None
390
+ exec(source: str | bytes | bytearray | Iterable | IOBase, globals: Optional[dict] = None) -> None
390
391
 
391
392
  Executes PyScript code statements from the given source.
392
393
 
@@ -416,7 +417,7 @@ def exec(context, position, source, globals=None):
416
417
  def eval(context, position, source, globals=None):
417
418
 
418
419
  """
419
- eval(source: str | bytes, globals: Optional[dict]) -> None
420
+ eval(source: str | bytes | bytearray | Iterable | IOBase, globals: Optional[dict] = None) -> None
420
421
 
421
422
  Executes a PyScript code expression from the given source.
422
423
 
@@ -203,7 +203,7 @@ class PysFunction(PysObject):
203
203
  PysTraceback(
204
204
  TypeError(
205
205
  f"{self.__qualname__}() missing {total_missing} required positional argument"
206
- f"{'' if total_missing == 1 else 's'}: {join(missing_arguments, conjunction='and')}"
206
+ f"{'' if total_missing == 1 else 's'}: {join(missing_arguments)}"
207
207
  ),
208
208
  context,
209
209
  position
@@ -153,13 +153,14 @@ def pys_runner(
153
153
  pys_sys.flags = context.flags
154
154
 
155
155
  try:
156
- del file, flags, symbol_table, context_parent, context_parent_entry_position, \
157
- lexer, tokens, error, parser, analyzer
156
+ del (
157
+ file, flags, symbol_table, context_parent, context_parent_entry_position, lexer, tokens, error, parser,
158
+ analyzer
159
+ )
158
160
  except:
159
161
  pass
160
162
 
161
163
  visitor_result = get_visitor(node.__class__)(node, context)
162
-
163
164
  if visitor_result.error:
164
165
  return result.failure(visitor_result.error)
165
166
 
@@ -25,6 +25,7 @@ TOKENS = dfrozen({
25
25
  'TYPEOF': 0x08,
26
26
  'INSTANCEOF': 0x09,
27
27
  'NEWLINE': ord('\n'),
28
+ 'NOT_INSTANCEOF': 0x0b,
28
29
  'EXCLAMATION': ord('!'),
29
30
  'COMMENT': ord('#'),
30
31
  'PERCENT': ord('%'),
@@ -40,13 +40,13 @@ def import_readline() -> Literal[False]:
40
40
  USE_NOTEBOOK = False
41
41
 
42
42
  try:
43
- from IPython import get_ipython
43
+ from IPython import get_ipython # type: ignore
44
44
  if type(get_ipython()).__name__ in (
45
45
  'ZMQInteractiveShell', # Jupyter Notebook / Lab
46
46
  'Shell', # Google Colab
47
47
  'TerminalInteractiveShell' # IPython Terminal
48
48
  ):
49
- from IPython.display import clear_output
49
+ from IPython.display import clear_output # type: ignore
50
50
  def clear_shell() -> None:
51
51
  clear_output()
52
52
  USE_NOTEBOOK = True
@@ -34,10 +34,11 @@ def typecheck(func, *args, **kwargs):
34
34
  if not is_environ(ENV_PYSCRIPT_NO_TYPECHECK):
35
35
  try:
36
36
  from beartype import beartype as typecheck
37
- TYPECHECK_STACK += 1
38
- BEARTYPE = True
39
37
  except:
40
38
  pass
39
+ else:
40
+ TYPECHECK_STACK += 1
41
+ BEARTYPE = True
41
42
 
42
43
  def immutable(cls):
43
44
  cls.__setattr__ = _PysUtilities.readonly_attribute
@@ -1,5 +1,5 @@
1
1
  from inspect import currentframe
2
- from types import FrameType, MappingProxyType, UnionType
2
+ from types import FrameType, UnionType
3
3
  from typing import Any, Optional, Sequence
4
4
 
5
5
  import os
@@ -21,10 +21,11 @@ dclear = dict.clear
21
21
  dget = dict.get
22
22
  dkeys = dict.keys
23
23
  ditems = dict.items
24
- dfrozen = MappingProxyType
25
24
 
26
25
  if sys.version_info >= (3, 15):
27
26
  dfrozen = frozendict
27
+ else:
28
+ from types import MappingProxyType as dfrozen
28
29
 
29
30
  def get_frame(deep: int = 0) -> FrameType | None:
30
31
  deep += 1
@@ -22,6 +22,9 @@ def get_module_candidate(path: str, entry: str = '__init__') -> str | None:
22
22
  return candidate
23
23
 
24
24
  def find_module_path(filename: str | None, name: str, entry: str = '__init__') -> tuple[str | None, str | None]:
25
+ if name in ('_pyscript', 'builtins', 'sys'):
26
+ return None, None
27
+
25
28
  # circular import problem solved
26
29
  from ..cache import pys_sys
27
30
 
@@ -38,7 +38,9 @@ def normstr(obj) -> str:
38
38
 
39
39
  def join(sequence: Sequence[str], conjunction: str = 'and') -> str:
40
40
  length = len(sequence)
41
- if length == 1:
41
+ if length == 0:
42
+ return ''
43
+ elif length == 1:
42
44
  return sequence[0]
43
45
  elif length == 2:
44
46
  return f'{sequence[0]} {conjunction} {sequence[1]}'
@@ -7,8 +7,8 @@ from re import match as match_regex
7
7
  from types import NotImplementedType
8
8
  from typing import Any
9
9
 
10
- __version__ = '1.15.0'
11
- __date__ = '23 October 2026, 21:43 UTC+7'
10
+ __version__ = '1.15.1'
11
+ __date__ = '30 October 2026, 19:40 UTC+7'
12
12
  __author__ = ('AzzamMuhyala',)
13
13
 
14
14
  version = pys_sys.version = f'{__version__} ({__date__})'