pystdoc 0.7.1__tar.gz → 0.7.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.
- {pystdoc-0.7.1/src/pystdoc.egg-info → pystdoc-0.7.2}/PKG-INFO +4 -4
- {pystdoc-0.7.1 → pystdoc-0.7.2}/README.md +3 -3
- {pystdoc-0.7.1 → pystdoc-0.7.2}/pyproject.toml +1 -1
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/__init__.py +1 -1
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/cli.py +8 -5
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/config.py +66 -1
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/doc_writer.py +21 -2
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/mcp_server.py +5 -3
- {pystdoc-0.7.1 → pystdoc-0.7.2/src/pystdoc.egg-info}/PKG-INFO +4 -4
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc.egg-info/SOURCES.txt +1 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_cli.py +40 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_config.py +39 -0
- pystdoc-0.7.2/tests/test_pep8.py +64 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/LICENSE +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/setup.cfg +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/__main__.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/__init__.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/base.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/clang_adapter.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/generic_adapter.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/java_adapter.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/kotlin_adapter.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/adapters/python_adapter.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/cache.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/call_graph.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/compilation_db.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/db.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/design_engine.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/engine.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/hasher.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/interrupt.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/llm_client.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/parser_clang.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/parser_generic.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/parser_java.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/parser_kotlin.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/parser_python.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/perf.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/progress.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/query.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/report_engine.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/scanner.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/symbols.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/ui_detector.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc/watcher.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc.egg-info/dependency_links.txt +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc.egg-info/entry_points.txt +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc.egg-info/requires.txt +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/src/pystdoc.egg-info/top_level.txt +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_adapters.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_coverage_boost.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_design_engine_full.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_designgen.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_docgen.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_engine.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_engine_full.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_final_100.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_interrupt.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_live_server_100.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_llm_client.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_mcp_server.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_parser_generic.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_parser_java.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_parser_kotlin.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_perf.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_progress.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_query.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_reportgen.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_ui_detector.py +0 -0
- {pystdoc-0.7.1 → pystdoc-0.7.2}/tests/test_watcher.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pystdoc
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Python Structural & Topological Documentation Engine (pystdoc): High-precision, LLM-powered hierarchical codebase and architecture documentation generator.
|
|
5
5
|
Author: tab4moji
|
|
6
6
|
License: MIT
|
|
@@ -94,7 +94,7 @@ your_project/
|
|
|
94
94
|
- Instant 360-degree dependency tracing: inspect where a symbol is called from (*Inbound Callers / References*) and what it uses (*Outbound Dependencies*) before making breaking code changes or deletions.
|
|
95
95
|
6. **Standard LLM Options & Multi-Language Support**:
|
|
96
96
|
- Works with **Ollama, LiteRT-LM, vLLM, and OpenAI API**.
|
|
97
|
-
- Supports `--host`, `--model`, `--token` / `--api-key`, `--context-size`, and `--language` (e.g. `
|
|
97
|
+
- Supports `--host`, `--model`, `--token` / `--api-key`, `--context-size`, and `--language` (defaults to terminal language, e.g. `Japanese`, `English`, `日本語`).
|
|
98
98
|
|
|
99
99
|
---
|
|
100
100
|
|
|
@@ -225,7 +225,7 @@ pystdoc sync --dir ./target_project/ --host 192.168.0.11:11434 --model gemma4-26
|
|
|
225
225
|
2. **Environment Variables** (`LLM_HOST`, `LLM_MODEL`, `OPENAI_API_KEY`, etc.)
|
|
226
226
|
3. **Project Config** (`<project>/.pystdoc.json`)
|
|
227
227
|
4. **User Config** (`~/.config/pystdoc/pystdoc.json`)
|
|
228
|
-
5. **Builtin Defaults** (`http://127.0.0.1:11434`, `gemma4-26b-a4b`, `English`)
|
|
228
|
+
5. **Builtin Defaults** (`http://127.0.0.1:11434`, `gemma4-26b-a4b`, Terminal language / `English`)
|
|
229
229
|
|
|
230
230
|
---
|
|
231
231
|
|
|
@@ -273,7 +273,7 @@ pystdoc sync --dir ./target_project/ --host 192.168.0.11:11434 --model gemma4-26
|
|
|
273
273
|
| :--- | :--- | :--- | :--- |
|
|
274
274
|
| `--dir` | | `./` | Target project directory path |
|
|
275
275
|
| `--fast` | | `false` | Fast bottom-up sync only (runs docgen symbol indexing, skips designgen and reportgen) |
|
|
276
|
-
| `--language` | `-l` |
|
|
276
|
+
| `--language` | `-l` | Terminal locale | Output documentation language (defaults to terminal language; e.g. `Japanese`, `English`, `日本語`) |
|
|
277
277
|
| `--host` | `-H`, `--base-url` | `http://127.0.0.1:11434` | LLM server host endpoint URL |
|
|
278
278
|
| `--model` | `-m`, `LLM_MODEL` | `gemma4-26b-a4b` | LLM model identifier |
|
|
279
279
|
| `--token` | `--api-key`, `OPENAI_API_KEY` | `None` | API Bearer token |
|
|
@@ -65,7 +65,7 @@ your_project/
|
|
|
65
65
|
- Instant 360-degree dependency tracing: inspect where a symbol is called from (*Inbound Callers / References*) and what it uses (*Outbound Dependencies*) before making breaking code changes or deletions.
|
|
66
66
|
6. **Standard LLM Options & Multi-Language Support**:
|
|
67
67
|
- Works with **Ollama, LiteRT-LM, vLLM, and OpenAI API**.
|
|
68
|
-
- Supports `--host`, `--model`, `--token` / `--api-key`, `--context-size`, and `--language` (e.g. `
|
|
68
|
+
- Supports `--host`, `--model`, `--token` / `--api-key`, `--context-size`, and `--language` (defaults to terminal language, e.g. `Japanese`, `English`, `日本語`).
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
@@ -196,7 +196,7 @@ pystdoc sync --dir ./target_project/ --host 192.168.0.11:11434 --model gemma4-26
|
|
|
196
196
|
2. **Environment Variables** (`LLM_HOST`, `LLM_MODEL`, `OPENAI_API_KEY`, etc.)
|
|
197
197
|
3. **Project Config** (`<project>/.pystdoc.json`)
|
|
198
198
|
4. **User Config** (`~/.config/pystdoc/pystdoc.json`)
|
|
199
|
-
5. **Builtin Defaults** (`http://127.0.0.1:11434`, `gemma4-26b-a4b`, `English`)
|
|
199
|
+
5. **Builtin Defaults** (`http://127.0.0.1:11434`, `gemma4-26b-a4b`, Terminal language / `English`)
|
|
200
200
|
|
|
201
201
|
---
|
|
202
202
|
|
|
@@ -244,7 +244,7 @@ pystdoc sync --dir ./target_project/ --host 192.168.0.11:11434 --model gemma4-26
|
|
|
244
244
|
| :--- | :--- | :--- | :--- |
|
|
245
245
|
| `--dir` | | `./` | Target project directory path |
|
|
246
246
|
| `--fast` | | `false` | Fast bottom-up sync only (runs docgen symbol indexing, skips designgen and reportgen) |
|
|
247
|
-
| `--language` | `-l` |
|
|
247
|
+
| `--language` | `-l` | Terminal locale | Output documentation language (defaults to terminal language; e.g. `Japanese`, `English`, `日本語`) |
|
|
248
248
|
| `--host` | `-H`, `--base-url` | `http://127.0.0.1:11434` | LLM server host endpoint URL |
|
|
249
249
|
| `--model` | `-m`, `LLM_MODEL` | `gemma4-26b-a4b` | LLM model identifier |
|
|
250
250
|
| `--token` | `--api-key`, `OPENAI_API_KEY` | `None` | API Bearer token |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pystdoc"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.2"
|
|
8
8
|
description = "Python Structural & Topological Documentation Engine (pystdoc): High-precision, LLM-powered hierarchical codebase and architecture documentation generator."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import List, Optional, Tuple
|
|
7
7
|
|
|
8
8
|
import pystdoc
|
|
9
|
-
from pystdoc.config import load_config
|
|
9
|
+
from pystdoc.config import detect_terminal_language, load_config
|
|
10
10
|
from pystdoc.db import DocgenDB
|
|
11
11
|
from pystdoc.design_engine import run_design_generation
|
|
12
12
|
from pystdoc.engine import run_docgen
|
|
@@ -108,7 +108,7 @@ def docgen_main(argv: Optional[List[str]] = None) -> None:
|
|
|
108
108
|
model = args.model or cfg.get("model", "gemma4-26b-a4b")
|
|
109
109
|
token = args.token or args.api_key or cfg.get("token")
|
|
110
110
|
ctx_size = args.context_size or cfg.get("context_size", 16384)
|
|
111
|
-
lang = args.language or cfg.get("language"
|
|
111
|
+
lang = args.language or cfg.get("language") or detect_terminal_language()
|
|
112
112
|
concurrency = args.concurrency or cfg.get("concurrency", 1)
|
|
113
113
|
allow_fallback = (
|
|
114
114
|
args.allow_fallback
|
|
@@ -214,7 +214,7 @@ def designgen_main(argv: Optional[List[str]] = None) -> None:
|
|
|
214
214
|
model = args.model or cfg.get("model", "gemma4-26b-a4b")
|
|
215
215
|
token = args.token or args.api_key or cfg.get("token")
|
|
216
216
|
ctx_size = args.context_size or cfg.get("context_size", 16384)
|
|
217
|
-
lang = args.language or cfg.get("language"
|
|
217
|
+
lang = args.language or cfg.get("language") or detect_terminal_language()
|
|
218
218
|
allow_fallback = (
|
|
219
219
|
args.allow_fallback
|
|
220
220
|
if args.allow_fallback is not None
|
|
@@ -298,7 +298,7 @@ def run_watch(
|
|
|
298
298
|
token: Optional[str] = None,
|
|
299
299
|
context_size: int = 16384,
|
|
300
300
|
concurrency: int = 1,
|
|
301
|
-
language: str =
|
|
301
|
+
language: Optional[str] = None,
|
|
302
302
|
allow_fallback: bool = False,
|
|
303
303
|
compile_commands: Optional[str] = None,
|
|
304
304
|
interval: float = 1.0,
|
|
@@ -307,6 +307,9 @@ def run_watch(
|
|
|
307
307
|
"""Run continuous watcher with auto-sync on code change."""
|
|
308
308
|
from pystdoc.watcher import DNotifyWatcher
|
|
309
309
|
|
|
310
|
+
if language is None:
|
|
311
|
+
language = detect_terminal_language()
|
|
312
|
+
|
|
310
313
|
def _sync_action() -> None:
|
|
311
314
|
ret_docgen = run_docgen(
|
|
312
315
|
target_dir=target_dir,
|
|
@@ -649,7 +652,7 @@ def reportgen_main(argv: Optional[List[str]] = None) -> None:
|
|
|
649
652
|
model = args.model or cfg.get("model", "gemma4-26b-a4b")
|
|
650
653
|
token = args.token or args.api_key or cfg.get("token")
|
|
651
654
|
ctx_size = args.context_size or cfg.get("context_size", 16384)
|
|
652
|
-
lang = args.language or cfg.get("language"
|
|
655
|
+
lang = args.language or cfg.get("language") or detect_terminal_language()
|
|
653
656
|
concurrency = args.concurrency or cfg.get("concurrency", 1)
|
|
654
657
|
allow_fallback = (
|
|
655
658
|
args.allow_fallback
|
|
@@ -17,6 +17,71 @@ DEFAULT_CONFIG: Dict[str, Any] = {
|
|
|
17
17
|
"allow_fallback": False,
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
LANGUAGE_CODE_MAP: Dict[str, str] = {
|
|
21
|
+
"ja": "Japanese",
|
|
22
|
+
"en": "English",
|
|
23
|
+
"zh": "Chinese",
|
|
24
|
+
"es": "Spanish",
|
|
25
|
+
"fr": "French",
|
|
26
|
+
"de": "German",
|
|
27
|
+
"ko": "Korean",
|
|
28
|
+
"it": "Italian",
|
|
29
|
+
"ru": "Russian",
|
|
30
|
+
"pt": "Portuguese",
|
|
31
|
+
"nl": "Dutch",
|
|
32
|
+
"pl": "Polish",
|
|
33
|
+
"sv": "Swedish",
|
|
34
|
+
"da": "Danish",
|
|
35
|
+
"fi": "Finnish",
|
|
36
|
+
"no": "Norwegian",
|
|
37
|
+
"tr": "Turkish",
|
|
38
|
+
"ar": "Arabic",
|
|
39
|
+
"vi": "Vietnamese",
|
|
40
|
+
"th": "Thai",
|
|
41
|
+
"id": "Indonesian",
|
|
42
|
+
"hi": "Hindi",
|
|
43
|
+
"uk": "Ukrainian",
|
|
44
|
+
"cs": "Czech",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def detect_terminal_language() -> str:
|
|
49
|
+
"""Detect terminal/system language from locale environment variables."""
|
|
50
|
+
for env_var in ("LC_ALL", "LC_MESSAGES", "LANG", "LANGUAGE"):
|
|
51
|
+
val = os.environ.get(env_var)
|
|
52
|
+
if val:
|
|
53
|
+
val = val.strip()
|
|
54
|
+
if not val:
|
|
55
|
+
continue
|
|
56
|
+
if val.lower() in ("c", "posix", "c.utf-8", "c.utf8"):
|
|
57
|
+
return "English"
|
|
58
|
+
first_part = val.split(":")[0].strip()
|
|
59
|
+
lang_code = first_part.split(".")[0].split("@")[0].strip()
|
|
60
|
+
if not lang_code:
|
|
61
|
+
return "English"
|
|
62
|
+
l_lower = lang_code.lower()
|
|
63
|
+
if l_lower in ("japanese", "ja", "jp", "日本語"):
|
|
64
|
+
return "Japanese"
|
|
65
|
+
elif l_lower in ("english", "en"):
|
|
66
|
+
return "English"
|
|
67
|
+
elif l_lower in ("chinese", "zh"):
|
|
68
|
+
return "Chinese"
|
|
69
|
+
elif l_lower in ("spanish", "es"):
|
|
70
|
+
return "Spanish"
|
|
71
|
+
elif l_lower in ("french", "fr"):
|
|
72
|
+
return "French"
|
|
73
|
+
elif l_lower in ("german", "de"):
|
|
74
|
+
return "German"
|
|
75
|
+
elif l_lower in ("korean", "ko"):
|
|
76
|
+
return "Korean"
|
|
77
|
+
|
|
78
|
+
primary = l_lower.split("_")[0].split("-")[0]
|
|
79
|
+
if primary in LANGUAGE_CODE_MAP:
|
|
80
|
+
return LANGUAGE_CODE_MAP[primary]
|
|
81
|
+
return lang_code.capitalize()
|
|
82
|
+
|
|
83
|
+
return "English"
|
|
84
|
+
|
|
20
85
|
|
|
21
86
|
def get_user_config_path() -> Path:
|
|
22
87
|
"""Return user configuration path ~/.config/pystdoc/pystdoc.json."""
|
|
@@ -51,6 +116,7 @@ def load_config(target_dir: Optional[Path] = None) -> Dict[str, Any]:
|
|
|
51
116
|
Defaults < ~/.config/pystdoc/pystdoc.json < ./.pystdoc.json < Env vars
|
|
52
117
|
"""
|
|
53
118
|
config = dict(DEFAULT_CONFIG)
|
|
119
|
+
config["language"] = detect_terminal_language()
|
|
54
120
|
|
|
55
121
|
# 1. User config (~/.config/pystdoc/pystdoc.json)
|
|
56
122
|
user_cfg_path = get_user_config_path()
|
|
@@ -89,7 +155,6 @@ def load_config(target_dir: Optional[Path] = None) -> Dict[str, Any]:
|
|
|
89
155
|
env_lang = (
|
|
90
156
|
os.environ.get("DOCGEN_LANG")
|
|
91
157
|
or os.environ.get("PYSTDOC_LANG")
|
|
92
|
-
or os.environ.get("LANGUAGE")
|
|
93
158
|
)
|
|
94
159
|
if env_lang:
|
|
95
160
|
config["language"] = env_lang
|
|
@@ -34,7 +34,12 @@ def normalize_language(lang: Optional[str]) -> str:
|
|
|
34
34
|
"""Normalize language string into standard representation."""
|
|
35
35
|
if not lang:
|
|
36
36
|
return "English"
|
|
37
|
-
|
|
37
|
+
l_strip = lang.strip()
|
|
38
|
+
if not l_strip:
|
|
39
|
+
return "English"
|
|
40
|
+
l_lower = l_strip.lower()
|
|
41
|
+
if l_lower in ("c", "posix", "c.utf-8", "c.utf8"):
|
|
42
|
+
return "English"
|
|
38
43
|
if l_lower in ("japanese", "ja", "jp", "日本語"):
|
|
39
44
|
return "Japanese"
|
|
40
45
|
elif l_lower in ("english", "en"):
|
|
@@ -43,7 +48,21 @@ def normalize_language(lang: Optional[str]) -> str:
|
|
|
43
48
|
return "Chinese"
|
|
44
49
|
elif l_lower in ("spanish", "es"):
|
|
45
50
|
return "Spanish"
|
|
46
|
-
|
|
51
|
+
elif l_lower in ("french", "fr"):
|
|
52
|
+
return "French"
|
|
53
|
+
elif l_lower in ("german", "de"):
|
|
54
|
+
return "German"
|
|
55
|
+
elif l_lower in ("korean", "ko"):
|
|
56
|
+
return "Korean"
|
|
57
|
+
|
|
58
|
+
first_part = l_lower.split(":")[0].strip()
|
|
59
|
+
lang_tag = first_part.split(".")[0].split("@")[0].strip()
|
|
60
|
+
primary = lang_tag.split("_")[0].split("-")[0]
|
|
61
|
+
from pystdoc.config import LANGUAGE_CODE_MAP
|
|
62
|
+
|
|
63
|
+
if primary in LANGUAGE_CODE_MAP:
|
|
64
|
+
return LANGUAGE_CODE_MAP[primary]
|
|
65
|
+
return l_strip.capitalize()
|
|
47
66
|
|
|
48
67
|
|
|
49
68
|
def get_code_language(extension: str) -> str:
|
|
@@ -13,7 +13,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
13
13
|
except (ImportError, ModuleNotFoundError):
|
|
14
14
|
FastMCP = None # type: ignore
|
|
15
15
|
|
|
16
|
-
from pystdoc.config import load_config
|
|
16
|
+
from pystdoc.config import detect_terminal_language, load_config
|
|
17
17
|
from pystdoc.db import DocgenDB
|
|
18
18
|
from pystdoc.design_engine import run_design_generation
|
|
19
19
|
from pystdoc.engine import run_docgen
|
|
@@ -53,7 +53,9 @@ class MCPWatcherManager:
|
|
|
53
53
|
def _on_change() -> None:
|
|
54
54
|
try:
|
|
55
55
|
cfg = load_config(target_dir)
|
|
56
|
-
lang =
|
|
56
|
+
lang = (
|
|
57
|
+
cfg.get("language") or detect_terminal_language()
|
|
58
|
+
)
|
|
57
59
|
host = cfg.get("host")
|
|
58
60
|
model = cfg.get("model")
|
|
59
61
|
token = cfg.get("token")
|
|
@@ -576,7 +578,7 @@ def create_mcp_server(
|
|
|
576
578
|
cfg = load_config(target_dir)
|
|
577
579
|
|
|
578
580
|
use_llm = not no_llm if no_llm is not None else True
|
|
579
|
-
lang = language or cfg.get("language"
|
|
581
|
+
lang = language or cfg.get("language") or detect_terminal_language()
|
|
580
582
|
host = cfg.get("host")
|
|
581
583
|
model = cfg.get("model")
|
|
582
584
|
token = cfg.get("token")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pystdoc
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Python Structural & Topological Documentation Engine (pystdoc): High-precision, LLM-powered hierarchical codebase and architecture documentation generator.
|
|
5
5
|
Author: tab4moji
|
|
6
6
|
License: MIT
|
|
@@ -94,7 +94,7 @@ your_project/
|
|
|
94
94
|
- Instant 360-degree dependency tracing: inspect where a symbol is called from (*Inbound Callers / References*) and what it uses (*Outbound Dependencies*) before making breaking code changes or deletions.
|
|
95
95
|
6. **Standard LLM Options & Multi-Language Support**:
|
|
96
96
|
- Works with **Ollama, LiteRT-LM, vLLM, and OpenAI API**.
|
|
97
|
-
- Supports `--host`, `--model`, `--token` / `--api-key`, `--context-size`, and `--language` (e.g. `
|
|
97
|
+
- Supports `--host`, `--model`, `--token` / `--api-key`, `--context-size`, and `--language` (defaults to terminal language, e.g. `Japanese`, `English`, `日本語`).
|
|
98
98
|
|
|
99
99
|
---
|
|
100
100
|
|
|
@@ -225,7 +225,7 @@ pystdoc sync --dir ./target_project/ --host 192.168.0.11:11434 --model gemma4-26
|
|
|
225
225
|
2. **Environment Variables** (`LLM_HOST`, `LLM_MODEL`, `OPENAI_API_KEY`, etc.)
|
|
226
226
|
3. **Project Config** (`<project>/.pystdoc.json`)
|
|
227
227
|
4. **User Config** (`~/.config/pystdoc/pystdoc.json`)
|
|
228
|
-
5. **Builtin Defaults** (`http://127.0.0.1:11434`, `gemma4-26b-a4b`, `English`)
|
|
228
|
+
5. **Builtin Defaults** (`http://127.0.0.1:11434`, `gemma4-26b-a4b`, Terminal language / `English`)
|
|
229
229
|
|
|
230
230
|
---
|
|
231
231
|
|
|
@@ -273,7 +273,7 @@ pystdoc sync --dir ./target_project/ --host 192.168.0.11:11434 --model gemma4-26
|
|
|
273
273
|
| :--- | :--- | :--- | :--- |
|
|
274
274
|
| `--dir` | | `./` | Target project directory path |
|
|
275
275
|
| `--fast` | | `false` | Fast bottom-up sync only (runs docgen symbol indexing, skips designgen and reportgen) |
|
|
276
|
-
| `--language` | `-l` |
|
|
276
|
+
| `--language` | `-l` | Terminal locale | Output documentation language (defaults to terminal language; e.g. `Japanese`, `English`, `日本語`) |
|
|
277
277
|
| `--host` | `-H`, `--base-url` | `http://127.0.0.1:11434` | LLM server host endpoint URL |
|
|
278
278
|
| `--model` | `-m`, `LLM_MODEL` | `gemma4-26b-a4b` | LLM model identifier |
|
|
279
279
|
| `--token` | `--api-key`, `OPENAI_API_KEY` | `None` | API Bearer token |
|
|
@@ -473,6 +473,46 @@ class TestCLI(unittest.TestCase):
|
|
|
473
473
|
self.assertEqual(cm.exception.code, 0)
|
|
474
474
|
mock_start.assert_called_once()
|
|
475
475
|
|
|
476
|
+
def test_cli_default_language_from_terminal(self):
|
|
477
|
+
# When --language is NOT specified, terminal language is used
|
|
478
|
+
test_args = [
|
|
479
|
+
"docgen",
|
|
480
|
+
"--dir", str(self.test_dir),
|
|
481
|
+
"--no-llm",
|
|
482
|
+
"--allow-fallback",
|
|
483
|
+
]
|
|
484
|
+
with patch.dict("os.environ", {"LANG": "ja_JP.UTF-8"}, clear=False):
|
|
485
|
+
with patch(
|
|
486
|
+
"pystdoc.cli.run_docgen", return_value=0
|
|
487
|
+
) as mock_docgen:
|
|
488
|
+
with patch("sys.argv", test_args):
|
|
489
|
+
with self.assertRaises(SystemExit) as cm:
|
|
490
|
+
docgen_main()
|
|
491
|
+
self.assertEqual(cm.exception.code, 0)
|
|
492
|
+
mock_docgen.assert_called_once()
|
|
493
|
+
_, kwargs = mock_docgen.call_args
|
|
494
|
+
self.assertEqual(kwargs.get("language"), "Japanese")
|
|
495
|
+
|
|
496
|
+
# When --language IS specified, the explicit argument should be used
|
|
497
|
+
test_args_explicit = [
|
|
498
|
+
"docgen",
|
|
499
|
+
"--dir", str(self.test_dir),
|
|
500
|
+
"--no-llm",
|
|
501
|
+
"--allow-fallback",
|
|
502
|
+
"--language", "French",
|
|
503
|
+
]
|
|
504
|
+
with patch.dict("os.environ", {"LANG": "ja_JP.UTF-8"}, clear=False):
|
|
505
|
+
with patch(
|
|
506
|
+
"pystdoc.cli.run_docgen", return_value=0
|
|
507
|
+
) as mock_docgen:
|
|
508
|
+
with patch("sys.argv", test_args_explicit):
|
|
509
|
+
with self.assertRaises(SystemExit) as cm:
|
|
510
|
+
docgen_main()
|
|
511
|
+
self.assertEqual(cm.exception.code, 0)
|
|
512
|
+
mock_docgen.assert_called_once()
|
|
513
|
+
_, kwargs = mock_docgen.call_args
|
|
514
|
+
self.assertEqual(kwargs.get("language"), "French")
|
|
515
|
+
|
|
476
516
|
|
|
477
517
|
if __name__ == "__main__":
|
|
478
518
|
unittest.main()
|
|
@@ -9,6 +9,7 @@ from unittest.mock import patch
|
|
|
9
9
|
|
|
10
10
|
from pystdoc.config import (
|
|
11
11
|
_read_json_file,
|
|
12
|
+
detect_terminal_language,
|
|
12
13
|
get_user_config_path,
|
|
13
14
|
load_config,
|
|
14
15
|
)
|
|
@@ -154,6 +155,44 @@ class TestConfig(unittest.TestCase):
|
|
|
154
155
|
p, Path.home() / ".config" / "pystdoc" / "pystdoc.json"
|
|
155
156
|
)
|
|
156
157
|
|
|
158
|
+
def test_detect_terminal_language_japanese(self):
|
|
159
|
+
with patch.dict(os.environ, {"LANG": "ja_JP.UTF-8"}, clear=True):
|
|
160
|
+
self.assertEqual(detect_terminal_language(), "Japanese")
|
|
161
|
+
|
|
162
|
+
def test_detect_terminal_language_priority(self):
|
|
163
|
+
# LC_ALL > LC_MESSAGES > LANG > LANGUAGE
|
|
164
|
+
env = {
|
|
165
|
+
"LANG": "ja_JP.UTF-8",
|
|
166
|
+
"LC_MESSAGES": "fr_FR.UTF-8",
|
|
167
|
+
"LC_ALL": "de_DE.UTF-8",
|
|
168
|
+
}
|
|
169
|
+
with patch.dict(os.environ, env, clear=True):
|
|
170
|
+
self.assertEqual(detect_terminal_language(), "German")
|
|
171
|
+
|
|
172
|
+
env2 = {
|
|
173
|
+
"LANG": "ja_JP.UTF-8",
|
|
174
|
+
"LC_MESSAGES": "fr_FR.UTF-8",
|
|
175
|
+
}
|
|
176
|
+
with patch.dict(os.environ, env2, clear=True):
|
|
177
|
+
self.assertEqual(detect_terminal_language(), "French")
|
|
178
|
+
|
|
179
|
+
def test_detect_terminal_language_c_posix(self):
|
|
180
|
+
with patch.dict(os.environ, {"LANG": "C"}, clear=True):
|
|
181
|
+
self.assertEqual(detect_terminal_language(), "English")
|
|
182
|
+
with patch.dict(os.environ, {"LANG": "POSIX"}, clear=True):
|
|
183
|
+
self.assertEqual(detect_terminal_language(), "English")
|
|
184
|
+
|
|
185
|
+
def test_load_config_defaults_to_terminal_language(self):
|
|
186
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
187
|
+
tmp_path = Path(tmpdir)
|
|
188
|
+
with patch.dict(os.environ, {"LANG": "ja_JP.UTF-8"}, clear=True):
|
|
189
|
+
with patch(
|
|
190
|
+
"pystdoc.config.get_user_config_path",
|
|
191
|
+
return_value=tmp_path / "not_found.json",
|
|
192
|
+
):
|
|
193
|
+
cfg = load_config(tmp_path)
|
|
194
|
+
self.assertEqual(cfg["language"], "Japanese")
|
|
195
|
+
|
|
157
196
|
|
|
158
197
|
if __name__ == "__main__":
|
|
159
198
|
unittest.main()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Unit test for PEP8 compliance using flake8 / pycodestyle."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import unittest
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestPEP8(unittest.TestCase):
|
|
10
|
+
"""Ensure all python source files adhere to PEP8 guidelines."""
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def setUpClass(cls):
|
|
14
|
+
cls.pystdoc_dir = Path(__file__).resolve().parent.parent
|
|
15
|
+
cls.src_dir = cls.pystdoc_dir / "src"
|
|
16
|
+
cls.tests_dir = cls.pystdoc_dir / "tests"
|
|
17
|
+
|
|
18
|
+
cls.linter_cmd = None
|
|
19
|
+
if shutil.which("flake8"):
|
|
20
|
+
cls.linter_cmd = ["flake8"]
|
|
21
|
+
elif shutil.which("pycodestyle"):
|
|
22
|
+
cls.linter_cmd = ["pycodestyle"]
|
|
23
|
+
else:
|
|
24
|
+
for candidate in (
|
|
25
|
+
Path.home() / ".local" / "bin" / "flake8",
|
|
26
|
+
Path.home() / ".local" / "bin" / "pycodestyle",
|
|
27
|
+
Path("/usr/local/bin/flake8"),
|
|
28
|
+
Path("/usr/bin/flake8"),
|
|
29
|
+
):
|
|
30
|
+
if candidate.exists() and candidate.is_file():
|
|
31
|
+
cls.linter_cmd = [str(candidate)]
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
def _run_linter(self, target_dir: Path):
|
|
35
|
+
if not self.linter_cmd:
|
|
36
|
+
self.skipTest(
|
|
37
|
+
"Neither flake8 nor pycodestyle found in environment."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
cmd = self.linter_cmd + [str(target_dir)]
|
|
41
|
+
result = subprocess.run(
|
|
42
|
+
cmd,
|
|
43
|
+
stdout=subprocess.PIPE,
|
|
44
|
+
stderr=subprocess.PIPE,
|
|
45
|
+
text=True,
|
|
46
|
+
cwd=str(self.pystdoc_dir),
|
|
47
|
+
)
|
|
48
|
+
self.assertEqual(
|
|
49
|
+
result.returncode,
|
|
50
|
+
0,
|
|
51
|
+
f"PEP8 violations detected in {target_dir}:\n{result.stdout}",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def test_src_pep8_compliance(self):
|
|
55
|
+
"""Verify all files under src/ pass PEP8 compliance."""
|
|
56
|
+
self._run_linter(self.src_dir)
|
|
57
|
+
|
|
58
|
+
def test_tests_pep8_compliance(self):
|
|
59
|
+
"""Verify all test files under tests/ pass PEP8 compliance."""
|
|
60
|
+
self._run_linter(self.tests_dir)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|