polyglot-python 0.2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Furox-Art
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: polyglot-python
3
+ Version: 0.2.0
4
+ Summary: Deterministic multilingual Python keyword transpiler: write Python in your own language.
5
+ Author: Furox-Art
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Furox-Art/polyglot-python
8
+ Project-URL: Issues, https://github.com/Furox-Art/polyglot-python/issues
9
+ Project-URL: Changelog, https://github.com/Furox-Art/polyglot-python/blob/main/CHANGELOG.md
10
+ Keywords: python,transpiler,localization,multilingual,deterministic
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Software Development :: Compilers
17
+ Requires-Python: >=3.12
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=8; extra == "dev"
22
+ Requires-Dist: pytest-cov>=5; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # polyglot-python
26
+
27
+ Write Python in your own language, deterministically.
28
+
29
+ polyglot-python is a keyword transpiler for Python. You keep Python's grammar and write
30
+ programs with localized keywords; polyglot-python maps those keywords to the real Python
31
+ keywords and hands the result to CPython. No AI, no network, no randomness:
32
+ the same source, locale and version always produce byte-identical Python.
33
+
34
+ It is not a prose-to-code translator and it does not guess intent. It is Python
35
+ syntax with your words.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install polyglot-python # after the first release
41
+ pip install -e . # from source
42
+ ```
43
+
44
+ The first release is prepared but not yet on PyPI; install from source until it
45
+ is published. Requires Python 3.12+. Python 3.10/3.11 are not supported because
46
+ their tokenizer splits Indic combining marks, so several Hindi keywords would
47
+ not translate. No runtime dependencies.
48
+
49
+ ## Quick start
50
+
51
+ `examples/classify.tr`:
52
+
53
+ ```python
54
+ fonksiyon classify(n):
55
+ eğer n % 15 == 0:
56
+ döndür "FizzBuzz"
57
+ değilse_eğer n % 3 == 0:
58
+ döndür "Fizz"
59
+ değilse_eğer n % 5 == 0:
60
+ döndür "Buzz"
61
+ değilse:
62
+ döndür str(n)
63
+
64
+ için n içinde range(1, 16):
65
+ print(classify(n))
66
+ ```
67
+
68
+ ```bash
69
+ polyglot-python run examples/classify.tr
70
+ polyglot-python check examples/classify.tr
71
+ polyglot-python translate examples/classify.tr
72
+ polyglot-python translate examples/classify.tr -o classify.py
73
+ ```
74
+
75
+ The same FizzBuzz program is included for every locale under `examples/`.
76
+
77
+ ## Locales
78
+
79
+ | Code | Language | `if` | `def` | `return` |
80
+ |------|------------|----------|-------------|-----------|
81
+ | tr | Türkçe | `eğer` | `fonksiyon` | `döndür` |
82
+ | es | Español | `si` | `función` | `devolver`|
83
+ | pt | Português | `se` | `função` | `retornar`|
84
+ | de | Deutsch | `wenn` | `funktion` | `zurückgeben` |
85
+ | fr | Français | `si` | `fonction` | `retourner` |
86
+ | zh | 中文 | `如果` | `函数` | `返回` |
87
+ | hi | हिन्दी | `अगर` | `फलन` | `लौटाओ` |
88
+ | ar | العربية | `إذا` | `دالة` | `إرجاع` |
89
+ | ru | Русский | `если` | `определить`| `вернуть` |
90
+
91
+ `polyglot-python locales` prints the full list. Every locale defines exactly the 35
92
+ Python hard keywords (`keyword.kwlist`); soft keywords such as `match`, `case`
93
+ and `type` stay English.
94
+
95
+ Locale selection order:
96
+
97
+ 1. `--lang CODE`
98
+ 2. first line directive: `# polyglot-python: tr`
99
+ 3. file suffix: `.tr`, `.es`, `.pt`, `.de`, `.fr`, `.zh`, `.hi`, `.ar`, `.ru`
100
+
101
+ ## CLI
102
+
103
+ ```text
104
+ polyglot-python run FILE [--lang CODE] [args...]
105
+ polyglot-python check FILE [--lang CODE]
106
+ polyglot-python translate FILE [--lang CODE] [-o OUT]
107
+ polyglot-python locales
108
+ polyglot-python --version
109
+ ```
110
+
111
+ For `run`, place options before `FILE`; everything after `FILE` is passed to the
112
+ program as `sys.argv[1:]`.
113
+
114
+ ## Guarantees
115
+
116
+ - Only exact `NAME` tokens that match a locale keyword are rewritten. Strings,
117
+ comments, f-strings, numbers, identifiers and attribute names are untouched.
118
+ - Line numbers are preserved one-to-one; a per-line column map translates
119
+ reported error positions back to the localized source.
120
+ - `translate -o` and stdout output are UTF-8 with LF line endings on every
121
+ platform, so exports are byte-identical and re-verifiable.
122
+ - `check` translates and compiles without executing the program, so localized
123
+ sources can be validated safely.
124
+ - A localized keyword after a dot is treated as an attribute name and never
125
+ rewritten.
126
+ - Localized keywords are reserved words in their locale.
127
+ - Missing files, invalid UTF-8, unknown locales and untokenizable input fail
128
+ with a clear message; nothing is guessed or silently skipped.
129
+ - Error headers are localized; CPython's detail text (for example
130
+ `ZeroDivisionError`) remains English for now.
131
+ - Exit codes from the executed program are propagated.
132
+
133
+ ## Limits
134
+
135
+ - Phase 1 localizes keywords only: no localization of builtins (`print`,
136
+ `range`, ...), the standard library, or English exception text.
137
+ - This is not a natural-language compiler. It does not understand Spanish or
138
+ Turkish prose; it understands Python syntax written with localized keywords.
139
+ - The local prototype ships 9 locales. Other languages can be added by editing
140
+ `src/polyglot_python/locales.json`; malformed tables are rejected at load time.
141
+ - Tested on CPython 3.12 on Windows; CI covers 3.12-3.14 on Linux and 3.12
142
+ on Windows and macOS. The code targets 3.12+.
143
+
144
+ ## Development
145
+
146
+ ```bash
147
+ python -m venv .venv
148
+ .venv/Scripts/pip install -e ".[dev]"
149
+ .venv/Scripts/python -m pytest -q --cov=polyglot_python --cov-fail-under=100
150
+ ```
151
+
152
+ The suite contains 119 tests with 100% statement coverage, including every
153
+ keyword in both directions for all 9 locales, end-to-end CLI runs of every
154
+ example, version consistency with the changelog, and byte-level determinism
155
+ checks. CI runs Ubuntu 3.12-3.14 plus Windows and macOS at 3.12, enforces the
156
+ 100% coverage gate on Linux 3.12, translates every example twice on all three
157
+ operating systems to compare bytes and line counts, and builds the wheel,
158
+ installs it into a clean environment, and smoke-tests it. Dependabot keeps
159
+ GitHub Actions and development dependencies current. A separate release
160
+ workflow publishes tagged GitHub Releases to PyPI through Trusted Publishing.
161
+
162
+ ## Releasing
163
+
164
+ Publishing uses [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
165
+ and stores no API token.
166
+
167
+ 1. On PyPI, add a pending publisher for project `polyglot-python`: owner
168
+ `Furox-Art`, repository `polyglot-python`, workflow `release.yml`,
169
+ environment `pypi`.
170
+ 2. Change `version` in `pyproject.toml` and
171
+ `src/polyglot_python/__init__.py` together; the test suite rejects drift.
172
+ 3. Publish a GitHub Release with tag `v<version>`. The release workflow verifies
173
+ the tag, builds the sdist and wheel, and uploads them to PyPI.
174
+
175
+ ## License
176
+
177
+ MIT
@@ -0,0 +1,153 @@
1
+ # polyglot-python
2
+
3
+ Write Python in your own language, deterministically.
4
+
5
+ polyglot-python is a keyword transpiler for Python. You keep Python's grammar and write
6
+ programs with localized keywords; polyglot-python maps those keywords to the real Python
7
+ keywords and hands the result to CPython. No AI, no network, no randomness:
8
+ the same source, locale and version always produce byte-identical Python.
9
+
10
+ It is not a prose-to-code translator and it does not guess intent. It is Python
11
+ syntax with your words.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install polyglot-python # after the first release
17
+ pip install -e . # from source
18
+ ```
19
+
20
+ The first release is prepared but not yet on PyPI; install from source until it
21
+ is published. Requires Python 3.12+. Python 3.10/3.11 are not supported because
22
+ their tokenizer splits Indic combining marks, so several Hindi keywords would
23
+ not translate. No runtime dependencies.
24
+
25
+ ## Quick start
26
+
27
+ `examples/classify.tr`:
28
+
29
+ ```python
30
+ fonksiyon classify(n):
31
+ eğer n % 15 == 0:
32
+ döndür "FizzBuzz"
33
+ değilse_eğer n % 3 == 0:
34
+ döndür "Fizz"
35
+ değilse_eğer n % 5 == 0:
36
+ döndür "Buzz"
37
+ değilse:
38
+ döndür str(n)
39
+
40
+ için n içinde range(1, 16):
41
+ print(classify(n))
42
+ ```
43
+
44
+ ```bash
45
+ polyglot-python run examples/classify.tr
46
+ polyglot-python check examples/classify.tr
47
+ polyglot-python translate examples/classify.tr
48
+ polyglot-python translate examples/classify.tr -o classify.py
49
+ ```
50
+
51
+ The same FizzBuzz program is included for every locale under `examples/`.
52
+
53
+ ## Locales
54
+
55
+ | Code | Language | `if` | `def` | `return` |
56
+ |------|------------|----------|-------------|-----------|
57
+ | tr | Türkçe | `eğer` | `fonksiyon` | `döndür` |
58
+ | es | Español | `si` | `función` | `devolver`|
59
+ | pt | Português | `se` | `função` | `retornar`|
60
+ | de | Deutsch | `wenn` | `funktion` | `zurückgeben` |
61
+ | fr | Français | `si` | `fonction` | `retourner` |
62
+ | zh | 中文 | `如果` | `函数` | `返回` |
63
+ | hi | हिन्दी | `अगर` | `फलन` | `लौटाओ` |
64
+ | ar | العربية | `إذا` | `دالة` | `إرجاع` |
65
+ | ru | Русский | `если` | `определить`| `вернуть` |
66
+
67
+ `polyglot-python locales` prints the full list. Every locale defines exactly the 35
68
+ Python hard keywords (`keyword.kwlist`); soft keywords such as `match`, `case`
69
+ and `type` stay English.
70
+
71
+ Locale selection order:
72
+
73
+ 1. `--lang CODE`
74
+ 2. first line directive: `# polyglot-python: tr`
75
+ 3. file suffix: `.tr`, `.es`, `.pt`, `.de`, `.fr`, `.zh`, `.hi`, `.ar`, `.ru`
76
+
77
+ ## CLI
78
+
79
+ ```text
80
+ polyglot-python run FILE [--lang CODE] [args...]
81
+ polyglot-python check FILE [--lang CODE]
82
+ polyglot-python translate FILE [--lang CODE] [-o OUT]
83
+ polyglot-python locales
84
+ polyglot-python --version
85
+ ```
86
+
87
+ For `run`, place options before `FILE`; everything after `FILE` is passed to the
88
+ program as `sys.argv[1:]`.
89
+
90
+ ## Guarantees
91
+
92
+ - Only exact `NAME` tokens that match a locale keyword are rewritten. Strings,
93
+ comments, f-strings, numbers, identifiers and attribute names are untouched.
94
+ - Line numbers are preserved one-to-one; a per-line column map translates
95
+ reported error positions back to the localized source.
96
+ - `translate -o` and stdout output are UTF-8 with LF line endings on every
97
+ platform, so exports are byte-identical and re-verifiable.
98
+ - `check` translates and compiles without executing the program, so localized
99
+ sources can be validated safely.
100
+ - A localized keyword after a dot is treated as an attribute name and never
101
+ rewritten.
102
+ - Localized keywords are reserved words in their locale.
103
+ - Missing files, invalid UTF-8, unknown locales and untokenizable input fail
104
+ with a clear message; nothing is guessed or silently skipped.
105
+ - Error headers are localized; CPython's detail text (for example
106
+ `ZeroDivisionError`) remains English for now.
107
+ - Exit codes from the executed program are propagated.
108
+
109
+ ## Limits
110
+
111
+ - Phase 1 localizes keywords only: no localization of builtins (`print`,
112
+ `range`, ...), the standard library, or English exception text.
113
+ - This is not a natural-language compiler. It does not understand Spanish or
114
+ Turkish prose; it understands Python syntax written with localized keywords.
115
+ - The local prototype ships 9 locales. Other languages can be added by editing
116
+ `src/polyglot_python/locales.json`; malformed tables are rejected at load time.
117
+ - Tested on CPython 3.12 on Windows; CI covers 3.12-3.14 on Linux and 3.12
118
+ on Windows and macOS. The code targets 3.12+.
119
+
120
+ ## Development
121
+
122
+ ```bash
123
+ python -m venv .venv
124
+ .venv/Scripts/pip install -e ".[dev]"
125
+ .venv/Scripts/python -m pytest -q --cov=polyglot_python --cov-fail-under=100
126
+ ```
127
+
128
+ The suite contains 119 tests with 100% statement coverage, including every
129
+ keyword in both directions for all 9 locales, end-to-end CLI runs of every
130
+ example, version consistency with the changelog, and byte-level determinism
131
+ checks. CI runs Ubuntu 3.12-3.14 plus Windows and macOS at 3.12, enforces the
132
+ 100% coverage gate on Linux 3.12, translates every example twice on all three
133
+ operating systems to compare bytes and line counts, and builds the wheel,
134
+ installs it into a clean environment, and smoke-tests it. Dependabot keeps
135
+ GitHub Actions and development dependencies current. A separate release
136
+ workflow publishes tagged GitHub Releases to PyPI through Trusted Publishing.
137
+
138
+ ## Releasing
139
+
140
+ Publishing uses [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
141
+ and stores no API token.
142
+
143
+ 1. On PyPI, add a pending publisher for project `polyglot-python`: owner
144
+ `Furox-Art`, repository `polyglot-python`, workflow `release.yml`,
145
+ environment `pypi`.
146
+ 2. Change `version` in `pyproject.toml` and
147
+ `src/polyglot_python/__init__.py` together; the test suite rejects drift.
148
+ 3. Publish a GitHub Release with tag `v<version>`. The release workflow verifies
149
+ the tag, builds the sdist and wheel, and uploads them to PyPI.
150
+
151
+ ## License
152
+
153
+ MIT
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "polyglot-python"
7
+ version = "0.2.0"
8
+ description = "Deterministic multilingual Python keyword transpiler: write Python in your own language."
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Furox-Art" }]
14
+ keywords = ["python", "transpiler", "localization", "multilingual", "deterministic"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ "Operating System :: OS Independent",
21
+ "Topic :: Software Development :: Compilers",
22
+ ]
23
+
24
+ [project.scripts]
25
+ polyglot-python = "polyglot_python.cli:main"
26
+
27
+ [project.optional-dependencies]
28
+ dev = ["pytest>=8", "pytest-cov>=5"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/Furox-Art/polyglot-python"
32
+ Issues = "https://github.com/Furox-Art/polyglot-python/issues"
33
+ Changelog = "https://github.com/Furox-Art/polyglot-python/blob/main/CHANGELOG.md"
34
+
35
+ [tool.setuptools]
36
+ package-dir = { "" = "src" }
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
40
+
41
+ [tool.setuptools.package-data]
42
+ polyglot_python = ["locales.json"]
43
+
44
+ [tool.pytest.ini_options]
45
+ pythonpath = ["src"]
46
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ """polyglot-python: deterministic multilingual Python keyword transpiler."""
2
+
3
+ from .translator import (
4
+ Locale,
5
+ LocaleError,
6
+ TokenizeFailure,
7
+ Translation,
8
+ load_locales,
9
+ resolve_locale,
10
+ translate,
11
+ )
12
+
13
+ __version__ = "0.2.0"
14
+
15
+ __all__ = [
16
+ "Locale",
17
+ "LocaleError",
18
+ "TokenizeFailure",
19
+ "Translation",
20
+ "__version__",
21
+ "load_locales",
22
+ "resolve_locale",
23
+ "translate",
24
+ ]
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())
@@ -0,0 +1,225 @@
1
+ """Command line interface for polyglot-python."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import sys
8
+ import traceback
9
+ from types import CodeType
10
+ from typing import Sequence
11
+
12
+ from . import __version__
13
+ from .translator import (
14
+ Locale,
15
+ TokenizeFailure,
16
+ Translation,
17
+ load_locales,
18
+ resolve_locale,
19
+ translate,
20
+ )
21
+
22
+
23
+ class _Failure(Exception):
24
+ def __init__(self, message: str) -> None:
25
+ super().__init__(message)
26
+ self.message = message
27
+
28
+
29
+ def _configure_console() -> None:
30
+ for stream in (sys.stdout, sys.stderr):
31
+ reconfigure = getattr(stream, "reconfigure", None)
32
+ if reconfigure is None:
33
+ continue
34
+ try:
35
+ reconfigure(encoding="utf-8", newline="\n")
36
+ except (ValueError, OSError):
37
+ pass
38
+
39
+
40
+ def _message(locale: Locale | None, key: str, fallback: str) -> str:
41
+ if locale is None:
42
+ return fallback
43
+ return locale.messages.get(key, fallback)
44
+
45
+
46
+ def _read_source(path: str) -> str:
47
+ try:
48
+ with open(path, "r", encoding="utf-8-sig", newline="") as handle:
49
+ return handle.read()
50
+ except UnicodeDecodeError as exc:
51
+ raise _Failure(
52
+ f"cannot decode {path} as UTF-8: {exc.reason} at byte {exc.start}"
53
+ ) from exc
54
+ except OSError as exc:
55
+ reason = exc.strerror or str(exc)
56
+ raise _Failure(f"cannot read {path}: {reason}") from exc
57
+
58
+
59
+ def _prepare(path: str, lang: str | None) -> tuple[Locale | None, Translation]:
60
+ text = _read_source(path)
61
+ code = resolve_locale(lang, text, path)
62
+ locale: Locale | None = None
63
+ if code is not None:
64
+ locales = load_locales()
65
+ locale = locales.get(code)
66
+ if locale is None:
67
+ available = ", ".join(sorted(locales))
68
+ raise _Failure(f"unknown locale {code!r}; available: {available}")
69
+ try:
70
+ translation = translate(text, locale)
71
+ except TokenizeFailure as exc:
72
+ prefix = _message(locale, "token_error", "Tokenization error")
73
+ raise _Failure(
74
+ f"{prefix}: {path}:{exc.line}:{exc.column}: {exc.detail}"
75
+ ) from exc
76
+ return locale, translation
77
+
78
+
79
+ def _compile(
80
+ path: str, locale: Locale | None, translation: Translation
81
+ ) -> CodeType | None:
82
+ try:
83
+ return compile(translation.python, path, "exec")
84
+ except SyntaxError as exc:
85
+ lineno = exc.lineno or 1
86
+ column = translation.original_column(lineno, exc.offset or 1)
87
+ prefix = _message(locale, "syntax_error", "Syntax error")
88
+ print(
89
+ f"{prefix}: {path}:{lineno}:{column}: {exc.msg}",
90
+ file=sys.stderr,
91
+ )
92
+ return None
93
+
94
+
95
+ def _run(namespace: argparse.Namespace) -> int:
96
+ try:
97
+ locale, translation = _prepare(namespace.file, namespace.lang)
98
+ except _Failure as exc:
99
+ print(f"polyglot-python: {exc.message}", file=sys.stderr)
100
+ return 1
101
+
102
+ code = _compile(namespace.file, locale, translation)
103
+ if code is None:
104
+ return 1
105
+
106
+ scope = {
107
+ "__name__": "__main__",
108
+ "__file__": os.path.abspath(namespace.file),
109
+ }
110
+ saved_argv = sys.argv
111
+ sys.argv = [namespace.file, *namespace.args]
112
+ try:
113
+ exec(code, scope)
114
+ except SystemExit as exc:
115
+ if exc.code is None:
116
+ return 0
117
+ if isinstance(exc.code, int):
118
+ return exc.code
119
+ print(exc.code, file=sys.stderr)
120
+ return 1
121
+ except KeyboardInterrupt:
122
+ raise
123
+ except Exception:
124
+ prefix = _message(locale, "runtime_error", "Runtime error")
125
+ print(f"{prefix}: {namespace.file}", file=sys.stderr)
126
+ traceback.print_exc()
127
+ return 1
128
+ finally:
129
+ sys.argv = saved_argv
130
+ return 0
131
+
132
+
133
+ def _check(namespace: argparse.Namespace) -> int:
134
+ try:
135
+ locale, translation = _prepare(namespace.file, namespace.lang)
136
+ except _Failure as exc:
137
+ print(f"polyglot-python: {exc.message}", file=sys.stderr)
138
+ return 1
139
+ return 0 if _compile(namespace.file, locale, translation) is not None else 1
140
+
141
+
142
+ def _translate(namespace: argparse.Namespace) -> int:
143
+ try:
144
+ _, translation = _prepare(namespace.file, namespace.lang)
145
+ except _Failure as exc:
146
+ print(f"polyglot-python: {exc.message}", file=sys.stderr)
147
+ return 1
148
+
149
+ if namespace.output:
150
+ try:
151
+ with open(namespace.output, "w", encoding="utf-8", newline="") as handle:
152
+ handle.write(translation.python)
153
+ except OSError as exc:
154
+ reason = exc.strerror or str(exc)
155
+ print(
156
+ f"polyglot-python: cannot write {namespace.output}: {reason}",
157
+ file=sys.stderr,
158
+ )
159
+ return 1
160
+ else:
161
+ sys.stdout.write(translation.python)
162
+ return 0
163
+
164
+
165
+ def _locales(_namespace: argparse.Namespace) -> int:
166
+ locales = load_locales()
167
+ print(f"{'code':<6}{'name':<16}keywords")
168
+ for code in sorted(locales):
169
+ locale = locales[code]
170
+ print(f"{code:<6}{locale.name:<16}{len(locale.keywords)}")
171
+ return 0
172
+
173
+
174
+ def build_parser() -> argparse.ArgumentParser:
175
+ parser = argparse.ArgumentParser(
176
+ prog="polyglot-python",
177
+ description="Write Python in your own language: deterministic keyword translation.",
178
+ )
179
+ parser.add_argument(
180
+ "--version", action="version", version=f"polyglot-python {__version__}"
181
+ )
182
+ subparsers = parser.add_subparsers(dest="command", required=True)
183
+
184
+ run = subparsers.add_parser("run", help="translate and execute a program")
185
+ run.add_argument("file", help="localized source file")
186
+ run.add_argument("--lang", metavar="CODE", help="locale code, e.g. tr")
187
+ run.add_argument(
188
+ "args",
189
+ nargs=argparse.REMAINDER,
190
+ help="arguments passed to the program",
191
+ )
192
+ run.set_defaults(handler=_run)
193
+
194
+ transform = subparsers.add_parser(
195
+ "translate", help="print the Python translation"
196
+ )
197
+ transform.add_argument("file", help="localized source file")
198
+ transform.add_argument("--lang", metavar="CODE", help="locale code, e.g. tr")
199
+ transform.add_argument(
200
+ "-o", "--output", metavar="PATH", help="write the translation to PATH"
201
+ )
202
+ transform.set_defaults(handler=_translate)
203
+
204
+ check = subparsers.add_parser(
205
+ "check", help="translate and compile a program without running it"
206
+ )
207
+ check.add_argument("file", help="localized source file")
208
+ check.add_argument("--lang", metavar="CODE", help="locale code, e.g. tr")
209
+ check.set_defaults(handler=_check)
210
+
211
+ listing = subparsers.add_parser("locales", help="list bundled locales")
212
+ listing.set_defaults(handler=_locales)
213
+
214
+ return parser
215
+
216
+
217
+ def main(argv: Sequence[str] | None = None) -> int:
218
+ _configure_console()
219
+ parser = build_parser()
220
+ namespace = parser.parse_args(argv)
221
+ return namespace.handler(namespace)
222
+
223
+
224
+ if __name__ == "__main__":
225
+ sys.exit(main())