Commit 2cad388
authored
feat(cli): python (#1088)
add python registration support: `t('hello, {name}', name='john',
_max_chars=10)`, also supports `declare_static()`/`declare_var()` syntax
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR adds a new `@generaltranslation/python-extractor` package and
wires it into the CLI, enabling translation string registration for
Python projects using `gt-flask` and `gt-fastapi`. The extractor uses
`tree-sitter-python` to parse `.py` files and extract `t()` / `msg()`
calls (including `declare_static()` / `declare_var()` compound
expressions) into the same `ExtractionResult` format used by the
existing React pipeline.
Key changes:
- New `packages/python-extractor` package with a full AST-based
extraction pipeline (`extractImports` → `extractCalls` →
`parseStringExpression` → `resolveFunctionVariants`).
- CLI framework detection extended to check `pyproject.toml`,
`requirements.txt`, and `setup.py` for `gt-flask` / `gt-fastapi`
dependencies.
- Shared `postProcess.ts` utilities (`calculateHashes`, `dedupeUpdates`,
`linkStaticUpdates`) extracted from the React pipeline so both JS and
Python extraction paths share the same deduplication and static-ID
linking logic.
- `PythonLibrary` type and `isPythonLibrary` guard added;
`translation/parse.ts` routes Python libraries to the new
`createPythonInlineUpdates` pipeline.
- **Issue:** In `createPythonInlineUpdates.ts`, `fs.promises.readFile`
is called outside the per-file `try/catch`, so file-read errors
(permissions, broken symlinks) will abort the entire extraction loop
rather than being collected in the `errors` array.
- **Issue:** The module-level `crossFileCache` in
`resolveFunctionVariants.ts` keys by `filePath::functionName` but does
not include calling-context import aliases; two callers with different
`declare_static` aliases for the same helper will share a stale cache
entry.
- **Minor:** `linkStaticUpdates` in `postProcess.ts` calls `.sort()` on
the mapped hash array without filtering out potential `undefined`
values.
<details open><summary><h3>Confidence Score: 3/5</h3></summary>
- Safe to merge after fixing the uncaught file-read error in the Python
extraction pipeline; the logic and cross-file cache issues are edge
cases but worth addressing.
- The core extraction logic is sound and well-tested, but the
`fs.promises.readFile` call outside the try/catch in
`createPythonInlineUpdates.ts` is a real bug that can crash the CLI on
permission errors or broken symlinks. The cross-file cache issue in
`resolveFunctionVariants.ts` is an edge case that would only surface
when two calling files use different aliases for
`declare_static`/`declare_var`. The sort-on-undefined in
`postProcess.ts` is a minor type safety concern. Overall the feature is
substantial and correct for the common case, but the file-read bug
should be fixed before shipping.
- packages/cli/src/python/parse/createPythonInlineUpdates.ts (uncaught
file-read error),
packages/python-extractor/src/resolveFunctionVariants.ts (cache key
ignores import context)
</details>
<details><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| packages/python-extractor/src/index.ts | New entry point for the
python-extractor package; orchestrates parsing, import extraction, and
call extraction cleanly; cache-clear exports are absent from this file
(noted in prior threads). |
| packages/python-extractor/src/extractCalls.ts | Extracts and validates
t()/msg() calls from a Python AST; routes compound expressions
(f-strings, binary concat, declare_static/declare_var) through
parseStringExpression; straightforward and well-covered by tests. |
| packages/python-extractor/src/parseStringExpression.ts | Core
recursive parser that converts Python string expressions into StringNode
trees; handles f-strings, binary concat, ternary, and
declare_static/declare_var calls; uses extractImports for GT-package
filtering in extractImportsFromRoot, addressing prior concerns. |
| packages/python-extractor/src/resolveFunctionVariants.ts | Resolves
helper function return variants across files; module-level
crossFileCache key (filePath::functionName) does not encode
calling-context import aliases, which could produce stale results when
two callers use different declare_static/declare_var aliases for the
same helper file. |
| packages/cli/src/python/parse/createPythonInlineUpdates.ts | Python
extraction pipeline for the CLI; fs.promises.readFile is called outside
the per-file try/catch, so file-read errors (permissions, broken
symlinks) will propagate as uncaught exceptions instead of being
collected in the errors array. |
| packages/cli/src/extraction/postProcess.ts | Shared post-processing
utilities (hashing, dedup, static-ID linking) extracted from the React
pipeline; linkStaticUpdates sorts a potentially undefined hash array
without guarding against undefined values. |
| packages/cli/src/fs/determineFramework/matchSetupPyDependency.ts |
Parses setup.py install_requires/extras_require blocks for GT
dependencies; correctly handles string boundaries and nested brackets;
escaped-backslash fix from prior thread is present. |
| packages/cli/src/translation/parse.ts | Routes Python libraries to
createPythonInlineUpdates and React/JS libraries to createInlineUpdates
using the new isPythonLibrary guard. |
</details>
</details>
<details><summary><h3>Sequence Diagram</h3></summary>
```mermaid
sequenceDiagram
participant CLI as CLI (translation/parse.ts)
participant PY as createPythonInlineUpdates
participant EXT as extractFromPythonSource
participant IMP as extractImports
participant CALLS as extractCalls
participant PSE as parseStringExpression
participant RFV as resolveFunctionVariants
participant RI as resolveImport
participant POST as postProcess
CLI->>PY: isPythonLibrary(pkg) → true
PY->>PY: matchFiles(cwd, patterns)
loop each .py file
PY->>EXT: extractFromPythonSource(code, filePath)
EXT->>IMP: extractImports(rootNode)
IMP-->>EXT: ImportAlias[]
EXT->>CALLS: extractCalls(rootNode, imports, filePath)
CALLS->>PSE: parseStringExpression(firstArg, ctx)
alt declare_static / declare_var
PSE->>RFV: resolveFunctionInCurrentFile / resolveFunctionInFile
RFV->>RI: resolveImportPath(moduleName, filePath)
RI-->>RFV: resolved file path
RFV-->>PSE: StringNode
end
PSE-->>CALLS: StringNode
CALLS-->>EXT: RawTranslationCall[]
EXT-->>PY: ExtractionResult[]
end
PY->>POST: calculateHashes(updates)
PY->>POST: dedupeUpdates(updates)
PY->>POST: linkStaticUpdates(updates)
PY-->>CLI: { updates, errors, warnings }
```
</details>
<!-- greptile_failed_comments -->
<details><summary><h3>Comments Outside Diff (1)</h3></summary>
1. `python-extractor-detailed-plan.md`, line 1
([link](https://github.com/generaltranslation/gt/blob/f98045879fbee352b039b80cbb96472e0e661862/python-extractor-detailed-plan.md#L1))
This detailed plan document is committed to the repo root and is now
stale. Its "Phase 2 (Future)" section lists `declare_static`,
`declare_var`, and `msg()` support as not yet implemented, but this PR
implements all three features.
Committed design documents tend to drift out of sync and can mislead
future contributors. Consider removing this file or relocating it to a
`docs/` or `.github/` directory if you want to preserve it as a living
design reference.
<details><summary>Prompt To Fix With AI</summary>
`````markdown
This is a comment left during a code review.
Path: python-extractor-detailed-plan.md
Line: 1
Comment:
This detailed plan document is committed to the repo root and is now
stale. Its "Phase 2 (Future)" section lists `declare_static`,
`declare_var`, and `msg()` support as not yet implemented, but this PR
implements all three features.
Committed design documents tend to drift out of sync and can mislead
future contributors. Consider removing this file or relocating it to a
`docs/` or `.github/` directory if you want to preserve it as a living
design reference.
How can I resolve this? If you propose a fix, please make it concise.
`````
</details>
</details>
<!-- /greptile_failed_comments -->
<sub>Last reviewed commit: 57ce390</sub>
> Greptile also left **3 inline comments** on this PR.
<!-- /greptile_comment -->1 parent fb656e2 commit 2cad388
File tree
74 files changed
+4867
-2059
lines changed- .changeset
- packages
- cli
- src
- cli
- config
- extraction
- __tests__
- formats/files
- __tests__
- fs
- determineFramework
- __tests__
- generated
- python/parse
- __tests__
- react/parse
- translation
- types
- __tests__
- python-extractor
- src
- __tests__
- fixtures
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
74 files changed
+4867
-2059
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| 120 | + | |
120 | 121 | | |
121 | 122 | | |
122 | 123 | | |
| |||
132 | 133 | | |
133 | 134 | | |
134 | 135 | | |
| 136 | + | |
135 | 137 | | |
136 | 138 | | |
137 | 139 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | | - | |
| 69 | + | |
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| 196 | + | |
| 197 | + | |
196 | 198 | | |
197 | 199 | | |
198 | 200 | | |
199 | 201 | | |
200 | | - | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
201 | 205 | | |
202 | 206 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
35 | 46 | | |
36 | 47 | | |
37 | 48 | | |
| |||
158 | 169 | | |
159 | 170 | | |
160 | 171 | | |
161 | | - | |
162 | | - | |
| 172 | + | |
| 173 | + | |
163 | 174 | | |
164 | 175 | | |
165 | 176 | | |
| |||
Lines changed: 111 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
Lines changed: 125 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
0 commit comments