inscript-lang 2.12.0__tar.gz → 3.0.0__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.
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/PKG-INFO +1 -1
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/export_pipeline.py +112 -1
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript.py +63 -1
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/PKG-INFO +1 -1
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/setup.py +1 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib.py +43 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_game.py +40 -0
- inscript_lang-3.0.0/studio_bridge.py +741 -0
- inscript_lang-2.12.0/studio_bridge.py +0 -239
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/README.md +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/analyzer.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/ast_nodes.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/compiler.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/environment.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/errors.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/hot_reload.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_dap.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_fmt.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/SOURCES.txt +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/dependency_links.txt +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/entry_points.txt +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/requires.txt +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/top_level.txt +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_studio_api.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_test.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/interpreter.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/lexer.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/parser.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/pygame_backend.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/pyproject.toml +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/repl.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/scene_tree.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/setup.cfg +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_assets.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_extended.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_extended_2.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_values.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/studio_readiness.py +0 -0
- {inscript_lang-2.12.0 → inscript_lang-3.0.0}/vm.py +0 -0
|
@@ -42,7 +42,7 @@ MANIFEST_FILE = "inscript.toml"
|
|
|
42
42
|
ENTRY_DEFAULT = os.path.join("src", "main.ins")
|
|
43
43
|
BUILD_MANIFEST = "build.toml"
|
|
44
44
|
|
|
45
|
-
VALID_TARGETS = ("desktop", "web", "android", "ibc")
|
|
45
|
+
VALID_TARGETS = ("desktop", "web", "android", "ios", "ibc")
|
|
46
46
|
|
|
47
47
|
# Pyodide CDN base used in web builds
|
|
48
48
|
PYODIDE_CDN = "https://cdn.jsdelivr.net/pyodide/v0.27.0/full/"
|
|
@@ -645,3 +645,114 @@ def run_build(target: str, project_dir: str = ".") -> int:
|
|
|
645
645
|
for e in r.errors:
|
|
646
646
|
print(f"[build] {e}", file=sys.stderr)
|
|
647
647
|
return 0 if r.success else 1
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
651
|
+
# v2.13.0: Target: iOS
|
|
652
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
653
|
+
|
|
654
|
+
def build_ios(project_dir: str = ".") -> BuildResult:
|
|
655
|
+
"""
|
|
656
|
+
Build an iOS app via BeeWare Briefcase.
|
|
657
|
+
Requires: macOS + Xcode + pip install briefcase
|
|
658
|
+
|
|
659
|
+
Output layout:
|
|
660
|
+
build/ios/
|
|
661
|
+
pyproject.toml — Briefcase config
|
|
662
|
+
app.py — InScript iOS entry point
|
|
663
|
+
build.toml — build manifest
|
|
664
|
+
"""
|
|
665
|
+
import time, subprocess
|
|
666
|
+
t0 = time.perf_counter()
|
|
667
|
+
errors: List[str] = []
|
|
668
|
+
artifacts: List[str] = []
|
|
669
|
+
|
|
670
|
+
manifest = _read_manifest(project_dir)
|
|
671
|
+
pkg = manifest.get("package", {})
|
|
672
|
+
version = pkg.get("version", "0.0.0")
|
|
673
|
+
name = pkg.get("name", "mygame")
|
|
674
|
+
entry = pkg.get("entry", ENTRY_DEFAULT)
|
|
675
|
+
bundle = pkg.get("bundle", f"dev.inscript.{name}")
|
|
676
|
+
|
|
677
|
+
out_dir = os.path.join(project_dir, BUILD_DIR, "ios")
|
|
678
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
679
|
+
|
|
680
|
+
bp_path = os.path.join(out_dir, "pyproject.toml")
|
|
681
|
+
with open(bp_path, "w") as f:
|
|
682
|
+
f.write(textwrap.dedent(f"""\
|
|
683
|
+
[tool.briefcase]
|
|
684
|
+
project_name = "{name}"
|
|
685
|
+
bundle = "{bundle}"
|
|
686
|
+
version = "{version}"
|
|
687
|
+
description = "Built with InScript"
|
|
688
|
+
license = "MIT"
|
|
689
|
+
|
|
690
|
+
[tool.briefcase.app.{name}]
|
|
691
|
+
formal_name = "{name}"
|
|
692
|
+
description = "Built with InScript"
|
|
693
|
+
sources = ["."]
|
|
694
|
+
requires = ["inscript-lang"]
|
|
695
|
+
|
|
696
|
+
[tool.briefcase.app.{name}.iOS]
|
|
697
|
+
requires = ["toga-iOS"]
|
|
698
|
+
"""))
|
|
699
|
+
artifacts.append(bp_path)
|
|
700
|
+
|
|
701
|
+
app_py = os.path.join(out_dir, "app.py")
|
|
702
|
+
with open(app_py, "w") as f:
|
|
703
|
+
f.write(textwrap.dedent(f"""\
|
|
704
|
+
# InScript iOS entry — generated by inscript build --target ios
|
|
705
|
+
import sys
|
|
706
|
+
try:
|
|
707
|
+
import inscript
|
|
708
|
+
inscript.main(["{entry}"])
|
|
709
|
+
except Exception as e:
|
|
710
|
+
print(f"InScript launch error: {{e}}", file=sys.stderr)
|
|
711
|
+
"""))
|
|
712
|
+
artifacts.append(app_py)
|
|
713
|
+
|
|
714
|
+
for sub in ("src", "assets", "scenes"):
|
|
715
|
+
s = os.path.join(project_dir, sub)
|
|
716
|
+
d = os.path.join(out_dir, sub)
|
|
717
|
+
if os.path.isdir(s):
|
|
718
|
+
if os.path.exists(d): shutil.rmtree(d)
|
|
719
|
+
shutil.copytree(s, d)
|
|
720
|
+
|
|
721
|
+
from inscript import VERSION as _IV
|
|
722
|
+
bm = _write_build_manifest(out_dir, {
|
|
723
|
+
"target": "ios", "version": version, "entry": entry,
|
|
724
|
+
"inscript_version": _IV, "bundle": bundle,
|
|
725
|
+
"artifacts": {"pyproject": "pyproject.toml", "app": "app.py"},
|
|
726
|
+
"asset_hashes": {},
|
|
727
|
+
})
|
|
728
|
+
artifacts.append(bm)
|
|
729
|
+
|
|
730
|
+
# Check platform and briefcase
|
|
731
|
+
if sys.platform != "darwin":
|
|
732
|
+
msg = "iOS builds require macOS. Current platform: " + sys.platform
|
|
733
|
+
errors.append(msg)
|
|
734
|
+
print(f"[build:ios] ⚠ {msg}")
|
|
735
|
+
elapsed = (time.perf_counter() - t0) * 1000
|
|
736
|
+
return BuildResult("ios", False, out_dir, artifacts, errors, elapsed)
|
|
737
|
+
|
|
738
|
+
briefcase_ok = shutil.which("briefcase") is not None
|
|
739
|
+
if not briefcase_ok:
|
|
740
|
+
msg = ("BeeWare Briefcase not installed. "
|
|
741
|
+
"Install: pip install briefcase "
|
|
742
|
+
"Then: cd build/ios && briefcase create iOS && briefcase build iOS")
|
|
743
|
+
errors.append(msg)
|
|
744
|
+
print(f"[build:ios] ⚠ {msg}")
|
|
745
|
+
elapsed = (time.perf_counter() - t0) * 1000
|
|
746
|
+
return BuildResult("ios", False, out_dir, artifacts, errors, elapsed)
|
|
747
|
+
|
|
748
|
+
try:
|
|
749
|
+
subprocess.run([sys.executable, "-m", "briefcase", "create", "iOS"],
|
|
750
|
+
cwd=out_dir, check=True)
|
|
751
|
+
subprocess.run([sys.executable, "-m", "briefcase", "build", "iOS"],
|
|
752
|
+
cwd=out_dir, check=True)
|
|
753
|
+
print(f"[build:ios] ✅ iOS app built → {out_dir}")
|
|
754
|
+
except subprocess.CalledProcessError as e:
|
|
755
|
+
errors.append(f"briefcase failed: {e}")
|
|
756
|
+
|
|
757
|
+
elapsed = (time.perf_counter() - t0) * 1000
|
|
758
|
+
return BuildResult("ios", len(errors) == 0, out_dir, artifacts, errors, elapsed)
|
|
@@ -24,7 +24,7 @@ from errors import (InScriptError, LexerError, ParseError,
|
|
|
24
24
|
SemanticError, InScriptRuntimeError,
|
|
25
25
|
MultiError, InScriptWarning)
|
|
26
26
|
|
|
27
|
-
VERSION = "
|
|
27
|
+
VERSION = "3.0.0"
|
|
28
28
|
|
|
29
29
|
MANIFEST_FILENAME = "inscript.toml"
|
|
30
30
|
LOCK_FILENAME = "inscript.lock"
|
|
@@ -2752,6 +2752,17 @@ Examples:
|
|
|
2752
2752
|
help="v2.12.0: Start Electron bridge JSON-RPC server")
|
|
2753
2753
|
parser.add_argument("--bridge-port", type=int, default=8765,
|
|
2754
2754
|
help="v2.12.0: Port for --studio-bridge (default: 8765)")
|
|
2755
|
+
# v3.0.0: Studio + Visual Scripting
|
|
2756
|
+
parser.add_argument("--studio", action="store_true",
|
|
2757
|
+
help="v3.0.0: Launch InScript Studio web IDE in browser")
|
|
2758
|
+
parser.add_argument("--studio-port", type=int, default=8080,
|
|
2759
|
+
help="v3.0.0: Port for Studio web app (default: 8080)")
|
|
2760
|
+
parser.add_argument("--visual-compile", metavar="FILE",
|
|
2761
|
+
help="v3.0.0: Compile a .vins visual script to .ins source")
|
|
2762
|
+
parser.add_argument("--vins-output", metavar="FILE",
|
|
2763
|
+
help="v3.0.0: Output path for --visual-compile (default: <input>.ins)")
|
|
2764
|
+
parser.add_argument("--vins-template", metavar="NAME",
|
|
2765
|
+
help="v3.0.0: Generate a .vins template for visual scripting")
|
|
2755
2766
|
parser.add_argument("--lsp", action="store_true",
|
|
2756
2767
|
help="Start the Language Server (requires: pip install pygls)")
|
|
2757
2768
|
parser.add_argument("--game", action="store_true",
|
|
@@ -3067,6 +3078,57 @@ Examples:
|
|
|
3067
3078
|
bridge.stop()
|
|
3068
3079
|
return 0
|
|
3069
3080
|
|
|
3081
|
+
# ── v3.0.0 Studio + Visual Scripting ──────────────────────────────────
|
|
3082
|
+
if getattr(args, 'studio', False):
|
|
3083
|
+
import time as _time
|
|
3084
|
+
studio_port = getattr(args, 'studio_port', 8080)
|
|
3085
|
+
bridge_port = getattr(args, 'bridge_port', 8765)
|
|
3086
|
+
proj_dir = getattr(args, 'project_dir', '.') or '.'
|
|
3087
|
+
from studio_app import StudioApp
|
|
3088
|
+
app_s = StudioApp(proj_dir, studio_port=studio_port, bridge_port=bridge_port)
|
|
3089
|
+
app_s.start()
|
|
3090
|
+
url = app_s.url
|
|
3091
|
+
print(f"[InScript Studio] ✅ Running at {url}")
|
|
3092
|
+
print(f"[InScript Studio] Project: {os.path.abspath(proj_dir)}")
|
|
3093
|
+
print(f"[InScript Studio] Ctrl+C to stop")
|
|
3094
|
+
# Open browser
|
|
3095
|
+
try:
|
|
3096
|
+
import webbrowser as _wb
|
|
3097
|
+
_wb.open(url)
|
|
3098
|
+
except Exception:
|
|
3099
|
+
pass
|
|
3100
|
+
try:
|
|
3101
|
+
while True: _time.sleep(1)
|
|
3102
|
+
except KeyboardInterrupt:
|
|
3103
|
+
app_s.stop()
|
|
3104
|
+
print(f"[InScript Studio] Stopped.")
|
|
3105
|
+
return 0
|
|
3106
|
+
|
|
3107
|
+
if getattr(args, 'visual_compile', None) is not None:
|
|
3108
|
+
from visual_script import compile_file
|
|
3109
|
+
vins_path = args.visual_compile
|
|
3110
|
+
out_path = getattr(args, 'vins_output', None)
|
|
3111
|
+
if not os.path.isfile(vins_path):
|
|
3112
|
+
print(f"[visual] File not found: {vins_path}", file=sys.stderr); return 1
|
|
3113
|
+
try:
|
|
3114
|
+
result_path = compile_file(vins_path, out_path)
|
|
3115
|
+
print(f"[visual] Written to: {result_path}")
|
|
3116
|
+
except Exception as _ve:
|
|
3117
|
+
print(f"[visual] Compile error: {_ve}", file=sys.stderr); return 1
|
|
3118
|
+
return 0
|
|
3119
|
+
|
|
3120
|
+
if getattr(args, 'vins_template', None) is not None:
|
|
3121
|
+
import json as _j
|
|
3122
|
+
from visual_script import make_template
|
|
3123
|
+
name = args.vins_template
|
|
3124
|
+
out_path = f"{name}.vins"
|
|
3125
|
+
tmpl = make_template(name)
|
|
3126
|
+
with open(out_path, "w") as f:
|
|
3127
|
+
_j.dump(tmpl, f, indent=2)
|
|
3128
|
+
print(f"[visual] Template written to {out_path}")
|
|
3129
|
+
print(f"[visual] Compile with: inscript --visual-compile {out_path}")
|
|
3130
|
+
return 0
|
|
3131
|
+
|
|
3070
3132
|
# v1.9.1: --no-typecheck is deprecated — emit a warning and honour it
|
|
3071
3133
|
if getattr(args, 'no_typecheck', False):
|
|
3072
3134
|
print(
|
|
@@ -57,6 +57,7 @@ setup(
|
|
|
57
57
|
# ── v2.7.0+ runtime modules ───────────────────────────────────────────
|
|
58
58
|
"scene_tree", "hot_reload", "export_pipeline",
|
|
59
59
|
"studio_bridge", "inscript_studio_api", "studio_readiness",
|
|
60
|
+
"visual_script", "studio_app", "vins_editor",
|
|
60
61
|
],
|
|
61
62
|
package_data = {"": ["examples/*.ins", "lsp/*.py", "*.md"]},
|
|
62
63
|
install_requires = [],
|
|
@@ -1163,3 +1163,46 @@ try:
|
|
|
1163
1163
|
import stdlib_assets # noqa: F401
|
|
1164
1164
|
except Exception as _e4:
|
|
1165
1165
|
import sys; print(f'[stdlib_assets load error] {_e4}', file=sys.stderr)
|
|
1166
|
+
|
|
1167
|
+
# ── Studio IPC (v3.0.0): allows subprocess games to publish scene state ──────
|
|
1168
|
+
try:
|
|
1169
|
+
from studio_bridge import _IPC_STATE_FILE, _ipc_write_scene
|
|
1170
|
+
|
|
1171
|
+
def _studio_publish_scene(scene_manager_or_dict=None):
|
|
1172
|
+
"""
|
|
1173
|
+
Called from .ins: import "studio_ipc" as ipc; ipc.publish_scene()
|
|
1174
|
+
Writes current scene state to the IPC file so Studio can inspect it.
|
|
1175
|
+
"""
|
|
1176
|
+
import json as _j
|
|
1177
|
+
data = {"nodes": [], "count": 0, "source": "ipc"}
|
|
1178
|
+
if scene_manager_or_dict is not None:
|
|
1179
|
+
try:
|
|
1180
|
+
from scene_tree import SceneManager, SceneTree
|
|
1181
|
+
if hasattr(scene_manager_or_dict, 'current'):
|
|
1182
|
+
sm = scene_manager_or_dict
|
|
1183
|
+
if sm.current:
|
|
1184
|
+
nodes = []
|
|
1185
|
+
def _walk(inst, parent=None):
|
|
1186
|
+
nodes.append({
|
|
1187
|
+
"name": inst.name,
|
|
1188
|
+
"blueprint": inst.blueprint.name,
|
|
1189
|
+
"parent": parent,
|
|
1190
|
+
"children": [c.name for c in inst.get_children()],
|
|
1191
|
+
"props": {k: str(v) for k, v in inst._props.items()
|
|
1192
|
+
if not callable(v)},
|
|
1193
|
+
})
|
|
1194
|
+
for child in inst.get_children():
|
|
1195
|
+
_walk(child, inst.name)
|
|
1196
|
+
_walk(sm.current.root)
|
|
1197
|
+
data = {"nodes": nodes, "count": len(nodes), "source": "ipc"}
|
|
1198
|
+
except Exception:
|
|
1199
|
+
pass
|
|
1200
|
+
_ipc_write_scene(data)
|
|
1201
|
+
|
|
1202
|
+
from stdlib import register_module as _rm
|
|
1203
|
+
_rm("studio_ipc", {
|
|
1204
|
+
"publish_scene": _studio_publish_scene,
|
|
1205
|
+
"ipc_file": _IPC_STATE_FILE,
|
|
1206
|
+
})
|
|
1207
|
+
except Exception as _se:
|
|
1208
|
+
pass
|
|
@@ -1121,6 +1121,36 @@ class _InputManager:
|
|
|
1121
1121
|
self._key_state = {} # key -> (pressed_this_frame, held, released_this_frame)
|
|
1122
1122
|
self._mouse_x = 0.0; self._mouse_y = 0.0
|
|
1123
1123
|
self._mouse_buttons = {}
|
|
1124
|
+
# v2.13.0: headless emulation — inject key/mouse state without pygame
|
|
1125
|
+
self._emulated_keys: dict = {} # key_name -> bool (held)
|
|
1126
|
+
self._emulated_mouse: tuple = (0.0, 0.0)
|
|
1127
|
+
self._emulated_buttons: dict = {}
|
|
1128
|
+
self._headless = False # set True when pygame unavailable
|
|
1129
|
+
|
|
1130
|
+
def _try_pygame(self) -> bool:
|
|
1131
|
+
try:
|
|
1132
|
+
import pygame
|
|
1133
|
+
return pygame.get_init()
|
|
1134
|
+
except Exception:
|
|
1135
|
+
return False
|
|
1136
|
+
|
|
1137
|
+
# v2.13.0: headless injection API
|
|
1138
|
+
def emulate_key(self, key: str, held: bool = True):
|
|
1139
|
+
"""Inject a key state for headless/Studio preview mode."""
|
|
1140
|
+
self._emulated_keys[key.lower()] = bool(held)
|
|
1141
|
+
self._headless = True
|
|
1142
|
+
|
|
1143
|
+
def emulate_mouse(self, x: float, y: float, buttons: dict | None = None):
|
|
1144
|
+
"""Inject mouse position and button state."""
|
|
1145
|
+
self._emulated_mouse = (float(x), float(y))
|
|
1146
|
+
self._emulated_buttons = buttons or {}
|
|
1147
|
+
self._headless = True
|
|
1148
|
+
|
|
1149
|
+
def clear_emulation(self):
|
|
1150
|
+
"""Clear all emulated input state."""
|
|
1151
|
+
self._emulated_keys.clear()
|
|
1152
|
+
self._emulated_buttons.clear()
|
|
1153
|
+
self._headless = False
|
|
1124
1154
|
|
|
1125
1155
|
def map(self, action, keys=None, axes=None, gamepad=None, gamepad_axis=None):
|
|
1126
1156
|
self._actions[action] = {
|
|
@@ -1129,6 +1159,9 @@ class _InputManager:
|
|
|
1129
1159
|
}
|
|
1130
1160
|
|
|
1131
1161
|
def _is_key_down(self, key):
|
|
1162
|
+
# v2.13.0: headless emulation takes priority over pygame
|
|
1163
|
+
if self._headless or self._emulated_keys:
|
|
1164
|
+
return self._emulated_keys.get(key.lower(), False)
|
|
1132
1165
|
try:
|
|
1133
1166
|
import pygame
|
|
1134
1167
|
kmap = {
|
|
@@ -1169,12 +1202,19 @@ class _InputManager:
|
|
|
1169
1202
|
return max(-1.0, min(1.0, value))
|
|
1170
1203
|
|
|
1171
1204
|
def mouse_pos(self):
|
|
1205
|
+
# v2.13.0: headless emulation
|
|
1206
|
+
if self._headless or self._emulated_mouse != (0.0, 0.0):
|
|
1207
|
+
x, y = self._emulated_mouse
|
|
1208
|
+
return {"x": float(x), "y": float(y)}
|
|
1172
1209
|
try:
|
|
1173
1210
|
import pygame; x, y = pygame.mouse.get_pos(); return {"x": float(x), "y": float(y)}
|
|
1174
1211
|
except Exception:
|
|
1175
1212
|
return {"x": self._mouse_x, "y": self._mouse_y}
|
|
1176
1213
|
|
|
1177
1214
|
def mouse_pressed(self, button=0):
|
|
1215
|
+
# v2.13.0: headless emulation
|
|
1216
|
+
if self._headless or self._emulated_buttons:
|
|
1217
|
+
return self._emulated_buttons.get(int(button), False)
|
|
1178
1218
|
try:
|
|
1179
1219
|
import pygame; return bool(pygame.mouse.get_pressed()[int(button)])
|
|
1180
1220
|
except Exception:
|