pyscript-programming-language 1.15.1__tar.gz → 1.15.2__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 (92) hide show
  1. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/PKG-INFO +9 -8
  2. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/README.md +8 -7
  3. pyscript_programming_language-1.15.2/changelog.md +16 -0
  4. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyproject.toml +1 -1
  5. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/__main__.py +2 -2
  6. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/constants.py +2 -1
  7. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/handlers.py +13 -1
  8. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/highlight.py +0 -1
  9. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/interpreter.py +9 -5
  10. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/nodes.py +44 -16
  11. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/parser.py +47 -42
  12. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/pysbuiltins.py +10 -6
  13. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/pystypes.py +8 -3
  14. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/shell.py +15 -17
  15. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/decorators.py +4 -3
  16. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/string.py +4 -2
  17. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/version.py +2 -2
  18. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/dual/py_dual.py +11 -17
  19. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/explorer.pys +53 -75
  20. pyscript_programming_language-1.15.2/pyscript/lib/getch/__init__.pys +11 -0
  21. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/getch/py_getch.py +5 -2
  22. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/history.pys +15 -5
  23. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/site.pys +19 -19
  24. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/other/message.txt +11 -1
  25. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript_programming_language.egg-info/PKG-INFO +9 -8
  26. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript_programming_language.egg-info/SOURCES.txt +1 -1
  27. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/setup.py +1 -1
  28. pyscript_programming_language-1.15.1/changelog.md +0 -13
  29. pyscript_programming_language-1.15.1/pyscript/lib/getch/__init__.pys +0 -10
  30. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/MANIFEST.in +0 -0
  31. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/__init__.py +0 -0
  32. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/__init__.pyi +0 -0
  33. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/__init__.py +0 -0
  34. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/analyzer.py +0 -0
  35. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/bases.py +0 -0
  36. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/buffer.py +0 -0
  37. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/cache.py +0 -0
  38. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/checks.py +0 -0
  39. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/context.py +0 -0
  40. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/editor/__init__.py +0 -0
  41. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/editor/bases.py +0 -0
  42. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/editor/gui.py +0 -0
  43. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/editor/terminal.py +0 -0
  44. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/exceptions.py +0 -0
  45. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/lexer.py +0 -0
  46. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/mapping.py +0 -0
  47. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/position.py +0 -0
  48. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/results.py +0 -0
  49. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/runner.py +0 -0
  50. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/symtab.py +0 -0
  51. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/token.py +0 -0
  52. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/__init__.py +0 -0
  53. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/ansi.py +0 -0
  54. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/debug.py +0 -0
  55. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/generic.py +0 -0
  56. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/module.py +0 -0
  57. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/path.py +0 -0
  58. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/core/utils/similarity.py +0 -0
  59. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/__hello__.pys +0 -0
  60. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/ansi.pys +0 -0
  61. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/ast/__init__.pys +0 -0
  62. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/ast/ast_dump.py +0 -0
  63. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/ast/ast_literal_eval.py +0 -0
  64. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/ast/ast_unparse.py +0 -0
  65. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/ast/ast_walk.py +0 -0
  66. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/brainfuck.pys +0 -0
  67. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/code.pys +0 -0
  68. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/dis.pys +0 -0
  69. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/dual/__init__.pys +0 -0
  70. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/fpstimer/__init__.pys +0 -0
  71. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/fpstimer/py_fpstimer.py +0 -0
  72. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/inspect.pys +0 -0
  73. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/jsdict.pys +0 -0
  74. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/keyword.pys +0 -0
  75. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/opcode.pys +0 -0
  76. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/parser.pys +0 -0
  77. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/pdisplay.pys +0 -0
  78. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/symtable.pys +0 -0
  79. {pyscript_programming_language-1.15.1/pyscript/site-packages → pyscript_programming_language-1.15.2/pyscript/lib}/this.pys +0 -0
  80. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/token.pys +0 -0
  81. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/tokenize/__init__.pys +0 -0
  82. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/lib/tokenize/tok_untokenize.py +0 -0
  83. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/other/.nomedia +0 -0
  84. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/other/PyScript.ico +0 -0
  85. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/other/copyright +0 -0
  86. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/other/credits +0 -0
  87. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/other/license +0 -0
  88. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript/this.py +0 -0
  89. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript_programming_language.egg-info/dependency_links.txt +0 -0
  90. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript_programming_language.egg-info/requires.txt +0 -0
  91. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/pyscript_programming_language.egg-info/top_level.txt +0 -0
  92. {pyscript_programming_language-1.15.1 → pyscript_programming_language-1.15.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyscript-programming-language
3
- Version: 1.15.1
3
+ Version: 1.15.2
4
4
  Summary: PyScript Programming Language
5
5
  Home-page: https://azzammuhyala.github.io/pyscript
6
6
  Author: AzzamMuhyala
@@ -69,8 +69,8 @@ Try [PyScript Shell (_REPL_)](https://azzammuhyala.github.io/pyscript/shell.html
69
69
  (no installation required).
70
70
 
71
71
  ## Installation ⬇️
72
- First, you'll need to download Python. Make sure you're using the latest version above `3.10`, to ensure the code runs
73
- correctly. Visit the official [Python website](https://python.org) to download it.
72
+ First, you'll need to download Python. Make sure you're using the latest version **above `3.10`**, to ensure the code
73
+ runs correctly. Visit the official [Python website](https://python.org) to download it.
74
74
 
75
75
  Next, after downloading and configuring the Python application, you can download the PyScript from
76
76
  [PyScript releases](https://github.com/azzammuhyala/pyscript/releases) or from PIP with this command (_Recommended_):
@@ -119,11 +119,12 @@ Familiar? There it is!
119
119
  | 14 | `os` | **required** |
120
120
  | 15 | `pkgutil` | **required** |
121
121
  | 16 | `re` | **required** |
122
- | 17 | `subprocess` | **required** |
123
- | 18 | `sys` | **required** |
124
- | 19 | `types` | **required** |
125
- | 20 | `typing` | **required** |
126
- | 21 | `unicodedata` | **required** |
122
+ | 17 | `site` | **required** |
123
+ | 18 | `subprocess` | **required** |
124
+ | 19 | `sys` | **required** |
125
+ | 20 | `types` | **required** |
126
+ | 21 | `typing` | **required** |
127
+ | 22 | `unicodedata` | **required** |
127
128
  | 1 | `ast` | **required (library)** |
128
129
  | 2 | `math` | **required (library)** |
129
130
  | 3 | `msvcrt` | **required (library)** |
@@ -37,8 +37,8 @@ Try [PyScript Shell (_REPL_)](https://azzammuhyala.github.io/pyscript/shell.html
37
37
  (no installation required).
38
38
 
39
39
  ## Installation ⬇️
40
- First, you'll need to download Python. Make sure you're using the latest version above `3.10`, to ensure the code runs
41
- correctly. Visit the official [Python website](https://python.org) to download it.
40
+ First, you'll need to download Python. Make sure you're using the latest version **above `3.10`**, to ensure the code
41
+ runs correctly. Visit the official [Python website](https://python.org) to download it.
42
42
 
43
43
  Next, after downloading and configuring the Python application, you can download the PyScript from
44
44
  [PyScript releases](https://github.com/azzammuhyala/pyscript/releases) or from PIP with this command (_Recommended_):
@@ -87,11 +87,12 @@ Familiar? There it is!
87
87
  | 14 | `os` | **required** |
88
88
  | 15 | `pkgutil` | **required** |
89
89
  | 16 | `re` | **required** |
90
- | 17 | `subprocess` | **required** |
91
- | 18 | `sys` | **required** |
92
- | 19 | `types` | **required** |
93
- | 20 | `typing` | **required** |
94
- | 21 | `unicodedata` | **required** |
90
+ | 17 | `site` | **required** |
91
+ | 18 | `subprocess` | **required** |
92
+ | 19 | `sys` | **required** |
93
+ | 20 | `types` | **required** |
94
+ | 21 | `typing` | **required** |
95
+ | 22 | `unicodedata` | **required** |
95
96
  | 1 | `ast` | **required (library)** |
96
97
  | 2 | `math` | **required (library)** |
97
98
  | 3 | `msvcrt` | **required (library)** |
@@ -0,0 +1,16 @@
1
+ # Change Logs
2
+
3
+ ## [1.15.2] - 08/11/2026
4
+
5
+ ### Added
6
+ - Site packages have been moved to `site.getusersitepackages()` within the `pyscript` folder.
7
+ - The body of a `switch` statement can use semicolons `;`.
8
+ - Optimizations applied to several nodes.
9
+ - `getch` library includes the function `getch.is_initialized()`.
10
+ - `site` library includes the function `site.getusersitepackages()`.
11
+ - Added support for conversion to `complex` types in the `dual` library.
12
+ - Added the `make_dirs` parameter to the `newfile`, `move`, and `copy` functions in the `explorer` library.
13
+
14
+ ### Fixed
15
+ - Fixed several bugs.
16
+ - _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.1"
7
+ version = "1.15.2"
8
8
  description = "PyScript Programming Language"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.10"
@@ -139,7 +139,7 @@ parser.add_argument(
139
139
  parser.add_argument(
140
140
  '-t', '--terminal',
141
141
  action='store_true',
142
- help="configure terminal encoding to UTF-8 and enable ANSI escape code processing on Windows"
142
+ help=f"configure terminal encoding to {pys_sys.encoding} and enable ANSI escape code processing on Windows"
143
143
  )
144
144
 
145
145
  parser.add_argument(
@@ -216,7 +216,7 @@ if args.terminal:
216
216
 
217
217
  for fd in (sys.stdout, sys.stderr, sys.stdin):
218
218
  try:
219
- fd.reconfigure(encoding='utf-8')
219
+ fd.reconfigure(encoding=pys_sys.encoding)
220
220
  except:
221
221
  continue
222
222
 
@@ -1,10 +1,11 @@
1
1
  import os
2
+ import site
2
3
 
3
4
  PYSCRIPT_PATH = os.path.sep.join(__file__.split(os.path.sep)[:-2])
4
5
  CORE_PATH = os.path.join(PYSCRIPT_PATH, 'core')
5
6
  LIBRARIES_PATH = os.path.join(PYSCRIPT_PATH, 'lib')
6
7
  OTHER_PATH = os.path.join(PYSCRIPT_PATH, 'other')
7
- SITE_PACKAGES_PATH = os.path.join(PYSCRIPT_PATH, 'site-packages')
8
+ SITE_PACKAGES_PATH = os.path.join(site.getusersitepackages(), 'pyscript')
8
9
  ICON_PATH = os.path.join(OTHER_PATH, 'PyScript.ico')
9
10
  GLOBAL_HISTORY_PATH = os.path.join(os.path.expanduser('~'), '.pyscript_history')
10
11
  CONFIGURATIONS_PATH = os.path.join(OTHER_PATH, 'configurations.json')
@@ -1,15 +1,27 @@
1
1
  from .cache import pys_sys
2
- from .constants import ENV_PYSCRIPT_WITH_GIL
2
+ from .constants import SITE_PACKAGES_PATH, ENV_PYSCRIPT_WITH_GIL
3
3
  from .context import PysContext
4
4
  from .position import PysPosition
5
5
  from .pystypes import PysFunction
6
6
  from .utils.generic import is_environ
7
7
 
8
+ from site import ENABLE_USER_SITE
8
9
  from types import MethodType
9
10
  from typing import Any
10
11
 
12
+ import os
13
+
11
14
  wrapper_function = (MethodType, classmethod, staticmethod)
12
15
 
16
+ if ENABLE_USER_SITE:
17
+ try:
18
+ if os.path.isfile(SITE_PACKAGES_PATH):
19
+ os.remove(SITE_PACKAGES_PATH)
20
+ if not os.path.exists(SITE_PACKAGES_PATH):
21
+ os.mkdir(SITE_PACKAGES_PATH)
22
+ except:
23
+ pass
24
+
13
25
  GIL = pys_sys.gil = False
14
26
  if is_environ(ENV_PYSCRIPT_WITH_GIL):
15
27
  try:
@@ -28,7 +28,6 @@ BUILTIN_FUNCTIONS = frozenset(
28
28
  )
29
29
 
30
30
  try:
31
- # if pygments module already exists
32
31
  from pygments.lexer import RegexLexer, include, bygroups
33
32
  from pygments.style import Style
34
33
  from pygments.token import (
@@ -308,17 +308,19 @@ def visit_BinaryOperatorNode(node: PysBinaryOperatorNode, context: PysContext) -
308
308
  result._context = context
309
309
  result._position = node.position
310
310
  with result:
311
- return_right = True
312
311
 
313
312
  if otype == T_OR:
314
313
  if left:
315
314
  return result.success(left)
315
+ return_right = True
316
316
  elif otype == T_AND:
317
317
  if not left:
318
318
  return result.success(left)
319
+ return_right = True
319
320
  elif otype == T_NULLISH:
320
321
  if left is not None:
321
322
  return result.success(left)
323
+ return_right = True
322
324
  else:
323
325
  return_right = False
324
326
 
@@ -595,13 +597,13 @@ def visit_MatchNode(node: PysMatchNode, context: PysContext) -> PysRunTimeResult
595
597
  should_return = result.should_return
596
598
  ntarget = node.target
597
599
 
598
- compare = False
599
-
600
600
  if ntarget:
601
601
  target = register(get_visitor(ntarget.__class__)(ntarget, context))
602
602
  if should_return():
603
603
  return result
604
604
  compare = True
605
+ else:
606
+ compare = False
605
607
 
606
608
  for ncondition, nvalue in node.cases:
607
609
  condition = register(get_visitor(ncondition.__class__)(ncondition, context))
@@ -611,7 +613,7 @@ def visit_MatchNode(node: PysMatchNode, context: PysContext) -> PysRunTimeResult
611
613
  result._position = ncondition.position
612
614
  with result:
613
615
 
614
- if target == condition if compare else (True if condition else False):
616
+ if (target == condition) if compare else condition:
615
617
  value = register(get_visitor(nvalue.__class__)(nvalue, context))
616
618
  if should_return():
617
619
  return result
@@ -654,7 +656,6 @@ def visit_TryNode(node: PysTryNode, context: PysContext) -> PysRunTimeResult:
654
656
  failure(None)
655
657
 
656
658
  for (targets, tparameter), body in node.catches:
657
- handle_exception = True
658
659
  stop = False
659
660
 
660
661
  if targets:
@@ -683,6 +684,9 @@ def visit_TryNode(node: PysTryNode, context: PysContext) -> PysRunTimeResult:
683
684
  handle_exception = True
684
685
  break
685
686
 
687
+ else:
688
+ handle_exception = True
689
+
686
690
  if stop:
687
691
  break
688
692
 
@@ -274,6 +274,7 @@ class PysTernaryOperatorNode(PysNode):
274
274
  invalid: PysNode,
275
275
  style: Literal['general', 'pythonic']
276
276
  ) -> 'PysTernaryOperatorNode | PysNode':
277
+
277
278
  if (l_condition := _get_literal_value(condition)) is not undefined:
278
279
  return valid if l_condition else invalid
279
280
 
@@ -398,17 +399,15 @@ class PysStatementsNode(PysNode):
398
399
  return elements[0]
399
400
 
400
401
  new = []
401
- constant_nodes = {PysConstantNode, PysDebugNode, PysEllipsisNode}
402
- interrupt_nodes = {PysReturnNode, PysThrowNode, PysContinueNode, PysBreakNode}
403
402
 
404
403
  for node in elements:
405
- if node.__class__ in constant_nodes:
404
+ if node.__class__ in _constant_nodes:
406
405
  continue
407
406
  elif node.__class__ is PysStatementsNode:
408
407
  new.extend(node.elements)
409
408
  else:
410
409
  new.append(node)
411
- if node.__class__ in interrupt_nodes:
410
+ if node.__class__ in _interrupt_nodes:
412
411
  break
413
412
 
414
413
  self = super().__new__(cls, position)
@@ -510,12 +509,16 @@ class PysSwitchNode(PysNode):
510
509
  target: PysNode,
511
510
  cases: list[tuple[PysNode, PysNode]],
512
511
  default: PysNode | None
513
- ) -> 'PysSwitchNode':
512
+ ) -> 'PysSwitchNode | PysNode':
513
+ default = _optional_part(default)
514
+
515
+ if not (cases or default):
516
+ return target
514
517
 
515
518
  self = super().__new__(cls, position)
516
519
  setimuattr(self, 'target', target)
517
520
  setimuattr(self, 'cases', tuple(cases))
518
- setimuattr(self, 'default', _optional_part(default))
521
+ setimuattr(self, 'default', default)
519
522
  return self
520
523
 
521
524
  def __repr__(self) -> str:
@@ -532,12 +535,31 @@ class PysMatchNode(PysNode):
532
535
  target: PysNode | None,
533
536
  cases: list[tuple[PysNode, PysNode]],
534
537
  default: PysNode | None
535
- ) -> 'PysMatchNode':
538
+ ) -> 'PysMatchNode | PysNode':
539
+
540
+ if target and (l_target := _get_literal_value(target)) is not undefined:
541
+ new_cases = []
542
+
543
+ for condition, value in cases:
544
+ if (l_condition := _get_literal_value(condition)) is undefined:
545
+ new_cases.append((condition, value))
546
+ elif l_target == l_condition:
547
+ default = value
548
+ break
549
+
550
+ cases = new_cases
551
+ default = _optional_part(default, expression=True)
552
+
553
+ if not cases:
554
+ return default if default else PysConstantNode(position, None)
555
+
556
+ else:
557
+ default = _optional_part(default, expression=True)
536
558
 
537
559
  self = super().__new__(cls, position)
538
560
  setimuattr(self, 'target', target)
539
561
  setimuattr(self, 'cases', tuple(cases))
540
- setimuattr(self, 'default', _optional_part(default))
562
+ setimuattr(self, 'default', default)
541
563
  return self
542
564
 
543
565
  def __repr__(self) -> str:
@@ -602,8 +624,7 @@ class PysForNode(PysNode):
602
624
  def __new__(
603
625
  cls,
604
626
  position: PysPosition,
605
- header: tuple[PysNode | None, PysNode | None, PysNode | None] |
606
- tuple[PysNode, PysNode],
627
+ header: tuple[PysNode | None, PysNode | None, PysNode | None] | tuple[PysNode, PysNode],
607
628
  body: PysNode,
608
629
  else_: PysNode | None
609
630
  ) -> 'PysForNode':
@@ -628,12 +649,16 @@ class PysWhileNode(PysNode):
628
649
  condition: PysNode,
629
650
  body: PysNode,
630
651
  else_: PysNode | None
631
- ) -> 'PysWhileNode':
652
+ ) -> 'PysWhileNode | PysNode':
653
+ else_ = _optional_part(else_)
654
+
655
+ if (l_condition := _get_literal_value(condition)) is not undefined and not l_condition:
656
+ return else_ if else_ else PysStatementsNode(position, [])
632
657
 
633
658
  self = super().__new__(cls, position)
634
659
  setimuattr(self, 'condition', condition)
635
660
  setimuattr(self, 'body', body)
636
- setimuattr(self, 'else_', _optional_part(else_))
661
+ setimuattr(self, 'else_', else_)
637
662
  return self
638
663
 
639
664
  def __repr__(self) -> str:
@@ -770,7 +795,7 @@ class PysReturnNode(PysNode):
770
795
  @typecheck
771
796
  def __new__(cls, position: PysPosition, value: PysNode | None) -> 'PysReturnNode':
772
797
  self = super().__new__(cls, position)
773
- setimuattr(self, 'value', _optional_part(value))
798
+ setimuattr(self, 'value', _optional_part(value, expression=True))
774
799
  return self
775
800
 
776
801
  def __repr__(self) -> str:
@@ -855,6 +880,9 @@ class PysBreakNode(PysNode):
855
880
  def __repr__(self) -> Literal['Break()']:
856
881
  return 'Break()'
857
882
 
883
+ _constant_nodes = frozenset([PysConstantNode, PysDebugNode, PysEllipsisNode])
884
+ _interrupt_nodes = frozenset([PysReturnNode, PysThrowNode, PysContinueNode, PysBreakNode])
885
+
858
886
  def _get_literal_value(item: Any) -> Any | PysUndefined:
859
887
 
860
888
  if isinstance(item, (PysConstantNode, PysToken)):
@@ -886,9 +914,9 @@ def _get_literal_value(item: Any) -> Any | PysUndefined:
886
914
 
887
915
  return undefined
888
916
 
889
- def _optional_part(part: PysNode | None) -> PysNode | None:
917
+ def _optional_part(part: PysNode | None, *, expression: bool = False) -> PysNode | None:
890
918
  return None if (
919
+ (not expression and part.__class__ in _constant_nodes) or
891
920
  _get_literal_value(part) is None or
892
- (part.__class__ is PysStatementsNode and not part.elements) or
893
- (part.__class__ in (PysDebugNode, PysEllipsisNode))
921
+ (part.__class__ is PysStatementsNode and not part.elements)
894
922
  ) else part
@@ -1197,66 +1197,71 @@ class PysParser(Pys):
1197
1197
 
1198
1198
  self.skip(result)
1199
1199
 
1200
- if self.current_token.type != TOKENS['LEFT_CURLY']:
1201
- return result.failure(self.new_error("expected '{'"))
1200
+ cases = []
1201
+ default = None
1202
1202
 
1203
- left_bracket_token = self.current_token
1203
+ if self.current_token.type == TOKENS['SEMICOLON']:
1204
+ result.register_advancement()
1205
+ self.advance()
1204
1206
 
1205
- result.register_advancement()
1206
- self.advance()
1207
- self.skip(result)
1207
+ elif self.current_token.type == TOKENS['LEFT_CURLY']:
1208
+ left_bracket_token = self.current_token
1208
1209
 
1209
- cases = []
1210
- default = None
1210
+ result.register_advancement()
1211
+ self.advance()
1212
+ self.skip(result)
1211
1213
 
1212
- while True:
1214
+ while True:
1213
1215
 
1214
- if self.current_token.match(TOKENS['KEYWORD'], 'case'):
1215
- result.register_advancement()
1216
- self.advance()
1217
- self.skip(result)
1216
+ if self.current_token.match(TOKENS['KEYWORD'], 'case'):
1217
+ result.register_advancement()
1218
+ self.advance()
1219
+ self.skip(result)
1218
1220
 
1219
- case = result.register(self.walrus(), True)
1220
- if result.error:
1221
- return result
1221
+ case = result.register(self.walrus(), True)
1222
+ if result.error:
1223
+ return result
1222
1224
 
1223
- self.skip(result)
1225
+ self.skip(result)
1224
1226
 
1225
- if self.current_token.type != TOKENS['COLON']:
1226
- return result.failure(self.new_error("expected ':'"))
1227
+ if self.current_token.type != TOKENS['COLON']:
1228
+ return result.failure(self.new_error("expected ':'"))
1227
1229
 
1228
- result.register_advancement()
1229
- self.advance()
1230
+ result.register_advancement()
1231
+ self.advance()
1230
1232
 
1231
- body = result.register(self.statements())
1232
- if result.error:
1233
- return result
1233
+ body = result.register(self.statements())
1234
+ if result.error:
1235
+ return result
1234
1236
 
1235
- cases.append((case, body))
1237
+ cases.append((case, body))
1236
1238
 
1237
- elif self.current_token.match(TOKENS['KEYWORD'], 'default', 'else'):
1238
- result.register_advancement()
1239
- self.advance()
1240
- self.skip(result)
1239
+ elif self.current_token.match(TOKENS['KEYWORD'], 'default', 'else'):
1240
+ result.register_advancement()
1241
+ self.advance()
1242
+ self.skip(result)
1241
1243
 
1242
- if self.current_token.type != TOKENS['COLON']:
1243
- return result.failure(self.new_error("expected ':'"))
1244
+ if self.current_token.type != TOKENS['COLON']:
1245
+ return result.failure(self.new_error("expected ':'"))
1244
1246
 
1245
- result.register_advancement()
1246
- self.advance()
1247
+ result.register_advancement()
1248
+ self.advance()
1247
1249
 
1248
- default = result.register(self.statements())
1249
- if result.error:
1250
- return result
1250
+ default = result.register(self.statements())
1251
+ if result.error:
1252
+ return result
1251
1253
 
1252
- break
1254
+ break
1253
1255
 
1254
- else:
1255
- break
1256
+ else:
1257
+ break
1256
1258
 
1257
- self.close_bracket(result, left_bracket_token)
1258
- if result.error:
1259
- return result
1259
+ self.close_bracket(result, left_bracket_token)
1260
+ if result.error:
1261
+ return result
1262
+
1263
+ else:
1264
+ return result.failure(self.new_error("expected '{'"))
1260
1265
 
1261
1266
  return result.success(
1262
1267
  PysSwitchNode(
@@ -461,7 +461,8 @@ def ce(context, position, a, b, *, rel_tol=1e-9, abs_tol=0):
461
461
  abs_tol: maximum difference for being considered "close", regardless of the magnitude of the input values.
462
462
  """
463
463
 
464
- if isinstance(a, real_complex_number) and isinstance(b, real_complex_number):
464
+ b_isinstance = isinstance
465
+ if b_isinstance(a, real_complex_number) and b_isinstance(b, real_complex_number):
465
466
  return isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
466
467
 
467
468
  result = _magic_method(context, position, a, '__ce__', b, rel_tol=rel_tol, abs_tol=abs_tol)
@@ -495,7 +496,8 @@ def nce(context, position, a, b, *, rel_tol=1e-9, abs_tol=0):
495
496
  abs_tol: maximum difference for being considered "close", regardless of the magnitude of the input values.
496
497
  """
497
498
 
498
- if isinstance(a, real_complex_number) and isinstance(b, real_complex_number):
499
+ b_isinstance = isinstance
500
+ if b_isinstance(a, real_complex_number) and b_isinstance(b, real_complex_number):
499
501
  return not isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
500
502
 
501
503
  result = _magic_method(context, position, a, '__nce__', b, rel_tol=rel_tol, abs_tol=abs_tol)
@@ -526,9 +528,10 @@ def increment(context, position, object):
526
528
  __increment__ method, which if unsuccessful will throw a TypeError.
527
529
  """
528
530
 
529
- if isinstance(object, real_number):
531
+ b_isinstance = isinstance
532
+ if b_isinstance(object, real_number):
530
533
  return object + 1
531
- elif isinstance(object, sequence):
534
+ elif b_isinstance(object, sequence):
532
535
  return tuple(_pyincrement(context, position, obj) for obj in object)
533
536
 
534
537
  result = _magic_method(context, position, object, '__increment__')
@@ -550,9 +553,10 @@ def decrement(context, position, object):
550
553
  call the __decrement__ method, which if unsuccessful will throw a TypeError.
551
554
  """
552
555
 
553
- if isinstance(object, real_number):
556
+ b_isinstance = isinstance
557
+ if b_isinstance(object, real_number):
554
558
  return object - 1
555
- elif isinstance(object, sequence):
559
+ elif b_isinstance(object, sequence):
556
560
  return tuple(_pydecrement(context, position, obj) for obj in object)
557
561
 
558
562
  result = _magic_method(context, position, object, '__decrement__')
@@ -11,7 +11,6 @@ from .utils.generic import setimuattr, dinit, drepr, dor, dror, dior, dsetitem,
11
11
  from .utils.similarity import diff_get_closest
12
12
  from .utils.string import join
13
13
 
14
- from builtins import help as pyhelp
15
14
  from itertools import islice
16
15
  from types import MethodType
17
16
  from typing import Any, Callable, Optional
@@ -311,10 +310,16 @@ class PysPrinter(PysObject):
311
310
 
312
311
  class PysHelper(PysPrinter):
313
312
 
314
- __slots__ = ()
313
+ __slots__ = ('_pyhelp',)
315
314
 
316
315
  def __init__(self) -> None:
317
316
  super().__init__('help')
317
+ try:
318
+ from builtins import help
319
+ except:
320
+ def help(*args, **kwargs):
321
+ raise NotImplementedError('python builtins.help not found')
322
+ setimuattr(self, '_pyhelp', help)
318
323
 
319
324
  def __repr__(self) -> str:
320
325
  return f'Type {self.name}() for interactive help, or {self.name}(object) for help about object.'
@@ -328,4 +333,4 @@ class PysHelper(PysPrinter):
328
333
  "To get the list of builtin functions, types, exceptions, and other objects, type 'help(\"builtins\")'."
329
334
  )
330
335
  else:
331
- pyhelp(*args, **kwargs)
336
+ self._pyhelp(*args, **kwargs)
@@ -233,10 +233,8 @@ try:
233
233
  @singleton
234
234
  class PysHistory(Pys, History):
235
235
 
236
- def __new_singleton__(cls) -> 'PysHistory':
237
- if USE_FILE_HISTORY:
238
- return super().__new__(cls)
239
- raise NotImplementedError('not using file history')
236
+ def __new_singleton__(cls) -> 'PysHistory | InMemoryHistory':
237
+ return super().__new__(cls) if USE_FILE_HISTORY else InMemoryHistory()
240
238
 
241
239
  def _write_history_safely(self, mode: str, function: str, *args) -> None:
242
240
  try:
@@ -254,16 +252,16 @@ try:
254
252
  self._write_history_safely('w', 'write', '')
255
253
  return []
256
254
 
257
- try:
258
- strings = []
259
- lines = []
255
+ strings = []
256
+ lines = []
260
257
 
261
- def add_to_string():
262
- if not lines:
263
- return
264
- strings.insert(0, '\n'.join(lines))
265
- lines.clear()
258
+ def add_to_string():
259
+ if not lines:
260
+ return
261
+ strings.insert(0, '\n'.join(lines))
262
+ lines.clear()
266
263
 
264
+ try:
267
265
  with open(HISTORY_PATH, 'r', encoding=pys_sys.encoding) as file:
268
266
  for line in file:
269
267
  line = line[:-1]
@@ -273,13 +271,15 @@ try:
273
271
  lines.append(line)
274
272
  add_to_string()
275
273
 
276
- if HISTORY_LIMIT > 0 and len(strings) > HISTORY_LIMIT:
274
+ if 0 < HISTORY_LIMIT < len(strings):
277
275
  self._manage_history_capacity(strings)
278
276
 
279
- return strings
280
277
  except:
281
278
  return []
282
279
 
280
+ else:
281
+ return strings
282
+
283
283
  # this function is called within `append_string()` which adds a new string or history entry to `_loaded_strings`
284
284
  # before invoking this function
285
285
  def store_string(self, string: str) -> None:
@@ -290,8 +290,6 @@ try:
290
290
  else:
291
291
  self._manage_history_capacity(self._loaded_strings)
292
292
 
293
- history = (PysHistory if USE_FILE_HISTORY else InMemoryHistory)()
294
-
295
293
  class PysPromptToolkitLineShell(PysLineShell, PromptSession):
296
294
 
297
295
  def __init__(self, ps1: str = '>>> ', ps2: str = '... ', colored: bool = True) -> None:
@@ -381,7 +379,7 @@ try:
381
379
  input_processors=[TabsProcessor(tabstop=4)],
382
380
  key_bindings=key_bindings,
383
381
  prompt_continuation=lambda width, line_number, is_soft_wrap: self._ps2,
384
- history=history,
382
+ history=PysHistory(),
385
383
  **other_keyword
386
384
  )
387
385
 
@@ -13,9 +13,10 @@ class _PysUtilities(Pys):
13
13
  # circular import problem solved
14
14
  from ..cache import pys_sys
15
15
  singletons = pys_sys.singletons
16
- if type(singletons.get(cls, None)) is not cls:
17
- singletons[cls] = cls.__new_singleton__(cls, *args, **kwargs)
18
- return singletons[cls]
16
+ object = singletons.get(cls, None)
17
+ if type(object) is not cls:
18
+ singletons[cls] = object = cls.__new_singleton__(cls, *args, **kwargs)
19
+ return object
19
20
 
20
21
  @staticmethod
21
22
  def readonly_attribute(*args, **kwargs):
@@ -43,8 +43,10 @@ def join(sequence: Sequence[str], conjunction: str = 'and') -> str:
43
43
  elif length == 1:
44
44
  return sequence[0]
45
45
  elif length == 2:
46
- return f'{sequence[0]} {conjunction} {sequence[1]}'
47
- return f'{", ".join(sequence[:-1])}, {conjunction} {sequence[-1]}'
46
+ a, b = sequence
47
+ return f'{a} {conjunction} {b}'
48
+ *others, end = sequence
49
+ return f'{", ".join(others)}, {conjunction} {end}'
48
50
 
49
51
  def indent(string: str, length: int, indent: str = ' ') -> str:
50
52
  prefix = indent * length