python-infrakit-dev 0.1.0__py3-none-any.whl
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.
- infrakit/__init__.py +0 -0
- infrakit/cli/__init__.py +1 -0
- infrakit/cli/commands/__init__.py +1 -0
- infrakit/cli/commands/deps.py +530 -0
- infrakit/cli/commands/init.py +129 -0
- infrakit/cli/commands/llm.py +295 -0
- infrakit/cli/commands/logger.py +160 -0
- infrakit/cli/commands/module.py +342 -0
- infrakit/cli/commands/time.py +81 -0
- infrakit/cli/main.py +65 -0
- infrakit/core/__init__.py +0 -0
- infrakit/core/config/__init__.py +0 -0
- infrakit/core/config/converter.py +480 -0
- infrakit/core/config/exporter.py +304 -0
- infrakit/core/config/loader.py +713 -0
- infrakit/core/config/validator.py +389 -0
- infrakit/core/logger/__init__.py +21 -0
- infrakit/core/logger/formatters.py +143 -0
- infrakit/core/logger/handlers.py +322 -0
- infrakit/core/logger/retention.py +176 -0
- infrakit/core/logger/setup.py +314 -0
- infrakit/deps/__init__.py +239 -0
- infrakit/deps/clean.py +141 -0
- infrakit/deps/depfile.py +405 -0
- infrakit/deps/health.py +357 -0
- infrakit/deps/optimizer.py +642 -0
- infrakit/deps/scanner.py +550 -0
- infrakit/llm/__init__.py +35 -0
- infrakit/llm/batch.py +165 -0
- infrakit/llm/client.py +575 -0
- infrakit/llm/key_manager.py +728 -0
- infrakit/llm/llm_readme.md +306 -0
- infrakit/llm/models.py +148 -0
- infrakit/llm/providers/__init__.py +5 -0
- infrakit/llm/providers/base.py +112 -0
- infrakit/llm/providers/gemini.py +164 -0
- infrakit/llm/providers/openai.py +168 -0
- infrakit/llm/rate_limiter.py +54 -0
- infrakit/scaffolder/__init__.py +31 -0
- infrakit/scaffolder/ai.py +508 -0
- infrakit/scaffolder/backend.py +555 -0
- infrakit/scaffolder/cli_tool.py +386 -0
- infrakit/scaffolder/generator.py +338 -0
- infrakit/scaffolder/pipeline.py +562 -0
- infrakit/scaffolder/registry.py +121 -0
- infrakit/time/__init__.py +60 -0
- infrakit/time/profiler.py +511 -0
- python_infrakit_dev-0.1.0.dist-info/METADATA +124 -0
- python_infrakit_dev-0.1.0.dist-info/RECORD +51 -0
- python_infrakit_dev-0.1.0.dist-info/WHEEL +4 -0
- python_infrakit_dev-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"""
|
|
2
|
+
infrakit.scaffolder.templates.cli_tool
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Scaffold a distributable Typer CLI tool.
|
|
5
|
+
|
|
6
|
+
Layout
|
|
7
|
+
------
|
|
8
|
+
<project>/
|
|
9
|
+
├── src/
|
|
10
|
+
│ └── <project>/ # importable package (same name as project)
|
|
11
|
+
│ ├── __init__.py
|
|
12
|
+
│ ├── cli/
|
|
13
|
+
│ │ ├── __init__.py
|
|
14
|
+
│ │ ├── main.py # root Typer app + entry point
|
|
15
|
+
│ │ └── commands/
|
|
16
|
+
│ │ ├── __init__.py
|
|
17
|
+
│ │ └── run.py # starter "run" command group
|
|
18
|
+
│ └── core.py # library logic (CLI-agnostic)
|
|
19
|
+
├── utils/
|
|
20
|
+
│ ├── __init__.py
|
|
21
|
+
│ ├── logger.py
|
|
22
|
+
│ └── llm.py # optional — for AI-powered CLI tools
|
|
23
|
+
├── tests/
|
|
24
|
+
│ ├── __init__.py
|
|
25
|
+
│ └── test_cli.py
|
|
26
|
+
├── logs/
|
|
27
|
+
├── pyproject.toml / requirements.txt # entry_points wired up
|
|
28
|
+
├── config.{env|yaml|json}
|
|
29
|
+
├── README.md
|
|
30
|
+
└── .gitignore
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
from infrakit.scaffolder.generator import (
|
|
38
|
+
ScaffoldResult,
|
|
39
|
+
_mkdir,
|
|
40
|
+
_write,
|
|
41
|
+
_config_content,
|
|
42
|
+
_gitignore,
|
|
43
|
+
_logger_util,
|
|
44
|
+
_src_init,
|
|
45
|
+
_tests_init,
|
|
46
|
+
)
|
|
47
|
+
from infrakit.scaffolder.ai import _llm_util
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ── template content ──────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _cli_main(project_name: str, version: str) -> str:
|
|
54
|
+
cmd = project_name.replace("_", "-")
|
|
55
|
+
return f'''\
|
|
56
|
+
"""
|
|
57
|
+
{project_name}.cli.main
|
|
58
|
+
{"~" * (len(project_name) + 10)}
|
|
59
|
+
Root Typer application.
|
|
60
|
+
|
|
61
|
+
Entry point: ``{cmd}`` (wired via pyproject.toml).
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
import typer
|
|
65
|
+
|
|
66
|
+
from {project_name}.cli.commands import run as run_cmd
|
|
67
|
+
from utils.logger import get_logger
|
|
68
|
+
|
|
69
|
+
log = get_logger(__name__)
|
|
70
|
+
|
|
71
|
+
app = typer.Typer(
|
|
72
|
+
name="{cmd}",
|
|
73
|
+
help="{project_name.replace("_", " ").title()} CLI.",
|
|
74
|
+
no_args_is_help=True,
|
|
75
|
+
rich_markup_mode="markdown",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# ── register command groups ───────────────────────────────────────────────────
|
|
79
|
+
app.add_typer(run_cmd.app, name="run")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def version_callback(value: bool) -> None:
|
|
83
|
+
if value:
|
|
84
|
+
from {project_name} import __version__
|
|
85
|
+
typer.echo(f"{cmd} {{__version__}}")
|
|
86
|
+
raise typer.Exit()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.callback()
|
|
90
|
+
def main(
|
|
91
|
+
version: bool = typer.Option(
|
|
92
|
+
False, "--version", "-v",
|
|
93
|
+
callback=version_callback,
|
|
94
|
+
is_eager=True,
|
|
95
|
+
help="Show version and exit.",
|
|
96
|
+
),
|
|
97
|
+
) -> None:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def cli() -> None:
|
|
102
|
+
"""Entry point called by the ``{cmd}`` script."""
|
|
103
|
+
app()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
cli()
|
|
108
|
+
'''
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _cli_commands_init() -> str:
|
|
112
|
+
return '"""CLI command modules."""\n'
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _cli_run_command(project_name: str) -> str:
|
|
116
|
+
return f'''\
|
|
117
|
+
"""
|
|
118
|
+
{project_name}.cli.commands.run
|
|
119
|
+
{"~" * (len(project_name) + 20)}
|
|
120
|
+
Example "run" command group. Replace with your own commands.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
import typer
|
|
124
|
+
from typing_extensions import Annotated
|
|
125
|
+
|
|
126
|
+
from {project_name}.core import do_something
|
|
127
|
+
from utils.logger import get_logger
|
|
128
|
+
|
|
129
|
+
log = get_logger(__name__)
|
|
130
|
+
|
|
131
|
+
app = typer.Typer(
|
|
132
|
+
name="run",
|
|
133
|
+
help="Run project operations.",
|
|
134
|
+
no_args_is_help=True,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@app.command("hello")
|
|
139
|
+
def hello(
|
|
140
|
+
name: Annotated[str, typer.Argument(help="Name to greet.")] = "World",
|
|
141
|
+
verbose: Annotated[bool, typer.Option("--verbose", "-v")] = False,
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Say hello."""
|
|
144
|
+
result = do_something(name)
|
|
145
|
+
if verbose:
|
|
146
|
+
log.info("hello: %s -> %s", name, result)
|
|
147
|
+
typer.echo(result)
|
|
148
|
+
'''
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _core(project_name: str) -> str:
|
|
152
|
+
return f'''\
|
|
153
|
+
"""
|
|
154
|
+
{project_name}.core
|
|
155
|
+
{"~" * (len(project_name) + 6)}
|
|
156
|
+
Library logic — no CLI dependencies here.
|
|
157
|
+
|
|
158
|
+
Keeping business logic separate from CLI code means it can be imported,
|
|
159
|
+
tested, and reused without invoking Typer.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def do_something(name: str) -> str:
|
|
164
|
+
"""Placeholder — replace with real logic."""
|
|
165
|
+
return f"Hello, {{name}}!"
|
|
166
|
+
'''
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _test_cli(project_name: str) -> str:
|
|
170
|
+
cmd = project_name.replace("_", "-")
|
|
171
|
+
return f'''\
|
|
172
|
+
"""tests.test_cli — basic CLI smoke tests."""
|
|
173
|
+
|
|
174
|
+
from typer.testing import CliRunner
|
|
175
|
+
|
|
176
|
+
from {project_name}.cli.main import app
|
|
177
|
+
|
|
178
|
+
runner = CliRunner()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_version():
|
|
182
|
+
result = runner.invoke(app, ["--version"])
|
|
183
|
+
assert result.exit_code == 0
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_hello_default():
|
|
187
|
+
result = runner.invoke(app, ["run", "hello"])
|
|
188
|
+
assert result.exit_code == 0
|
|
189
|
+
assert "Hello" in result.output
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_hello_name():
|
|
193
|
+
result = runner.invoke(app, ["run", "hello", "Alice"])
|
|
194
|
+
assert result.exit_code == 0
|
|
195
|
+
assert "Alice" in result.output
|
|
196
|
+
'''
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _cli_pyproject(
|
|
200
|
+
project_name: str, version: str, description: str, author: str, include_llm: bool
|
|
201
|
+
) -> str:
|
|
202
|
+
author_line = f' "{author}",' if author else ' # "Your Name <you@example.com>",'
|
|
203
|
+
cmd = project_name.replace("_", "-")
|
|
204
|
+
llm_deps = """\
|
|
205
|
+
"openai",
|
|
206
|
+
"google-generativeai",
|
|
207
|
+
"tqdm",
|
|
208
|
+
""" if include_llm else ""
|
|
209
|
+
return f"""\
|
|
210
|
+
[project]
|
|
211
|
+
name = "{project_name}"
|
|
212
|
+
version = "{version}"
|
|
213
|
+
description = "{description}"
|
|
214
|
+
readme = "README.md"
|
|
215
|
+
requires-python = ">=3.10"
|
|
216
|
+
authors = [
|
|
217
|
+
{author_line}
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
dependencies = [
|
|
221
|
+
"infrakit",
|
|
222
|
+
"typer[all]",
|
|
223
|
+
"pydantic>=2.0",
|
|
224
|
+
{llm_deps}]
|
|
225
|
+
|
|
226
|
+
[project.optional-dependencies]
|
|
227
|
+
dev = [
|
|
228
|
+
"pytest",
|
|
229
|
+
"pytest-cov",
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
[project.scripts]
|
|
233
|
+
{cmd} = "{project_name}.cli.main:cli"
|
|
234
|
+
|
|
235
|
+
[tool.setuptools.packages.find]
|
|
236
|
+
where = ["src"]
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _cli_readme(project_name: str, description: str, include_llm: bool) -> str:
|
|
241
|
+
title = project_name.replace("-", " ").replace("_", " ").title()
|
|
242
|
+
desc_line = f"\n{description}\n" if description else ""
|
|
243
|
+
cmd = project_name.replace("_", "-")
|
|
244
|
+
llm_note = (
|
|
245
|
+
"\nThis tool includes `utils/llm.py` — set `OPENAI_API_KEY` or "
|
|
246
|
+
"`GEMINI_API_KEY` in your environment to use LLM features.\n"
|
|
247
|
+
) if include_llm else ""
|
|
248
|
+
return f"""\
|
|
249
|
+
# {title}
|
|
250
|
+
{desc_line}{llm_note}
|
|
251
|
+
## Installation
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
pip install -e .
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Usage
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
{cmd} --help
|
|
261
|
+
{cmd} --version
|
|
262
|
+
{cmd} run hello
|
|
263
|
+
{cmd} run hello Alice --verbose
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Structure
|
|
267
|
+
|
|
268
|
+
| Path | Purpose |
|
|
269
|
+
|---|---|
|
|
270
|
+
| `src/{project_name}/cli/main.py` | Root Typer app + entry point |
|
|
271
|
+
| `src/{project_name}/cli/commands/` | One file per command group |
|
|
272
|
+
| `src/{project_name}/core.py` | Business logic (no CLI deps) |
|
|
273
|
+
| `utils/logger.py` | Logger singleton |
|
|
274
|
+
{"| `utils/llm.py` | LLM client singleton |" if include_llm else ""}
|
|
275
|
+
|
|
276
|
+
## Development
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pip install -e ".[dev]"
|
|
280
|
+
pytest
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Adding a new command group
|
|
284
|
+
|
|
285
|
+
1. Create `src/{project_name}/cli/commands/mygroup.py` with a `app = typer.Typer(...)`.
|
|
286
|
+
2. Register it in `main.py`: `app.add_typer(mygroup.app, name="mygroup")`.
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _cli_gitignore() -> str:
|
|
291
|
+
return _gitignore() + """\
|
|
292
|
+
# Keys
|
|
293
|
+
.env
|
|
294
|
+
keys.json
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# ── public API ────────────────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def scaffold_cli_tool(
|
|
302
|
+
project_dir: Path,
|
|
303
|
+
*,
|
|
304
|
+
version: str = "0.1.0",
|
|
305
|
+
description: str = "",
|
|
306
|
+
author: str = "",
|
|
307
|
+
config_fmt: str = "env",
|
|
308
|
+
deps: str = "toml",
|
|
309
|
+
include_llm: bool = False,
|
|
310
|
+
) -> ScaffoldResult:
|
|
311
|
+
"""
|
|
312
|
+
Scaffold a distributable Typer CLI tool under ``project_dir``.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
project_dir:
|
|
317
|
+
Root directory for the project.
|
|
318
|
+
version:
|
|
319
|
+
Starting version string.
|
|
320
|
+
description:
|
|
321
|
+
Short project description.
|
|
322
|
+
author:
|
|
323
|
+
Author string.
|
|
324
|
+
config_fmt:
|
|
325
|
+
Config file format — ``"env"``, ``"yaml"``, or ``"json"``.
|
|
326
|
+
deps:
|
|
327
|
+
``"toml"`` or ``"requirements"``.
|
|
328
|
+
include_llm:
|
|
329
|
+
Whether to include ``utils/llm.py`` for AI-powered CLI commands.
|
|
330
|
+
Defaults to False for pure CLI tools.
|
|
331
|
+
"""
|
|
332
|
+
result = ScaffoldResult(project_dir=project_dir)
|
|
333
|
+
project_name = project_dir.name
|
|
334
|
+
|
|
335
|
+
pkg_dir = project_dir / "src" / project_name
|
|
336
|
+
|
|
337
|
+
# ── directories ───────────────────────────────────────────────────────────
|
|
338
|
+
_mkdir(result, project_dir)
|
|
339
|
+
_mkdir(result, pkg_dir)
|
|
340
|
+
_mkdir(result, pkg_dir / "cli")
|
|
341
|
+
_mkdir(result, pkg_dir / "cli" / "commands")
|
|
342
|
+
_mkdir(result, project_dir / "utils")
|
|
343
|
+
_mkdir(result, project_dir / "tests")
|
|
344
|
+
_mkdir(result, project_dir / "logs")
|
|
345
|
+
|
|
346
|
+
# ── package ───────────────────────────────────────────────────────────────
|
|
347
|
+
_write(result, pkg_dir / "__init__.py", _src_init(version))
|
|
348
|
+
_write(result, pkg_dir / "core.py", _core(project_name))
|
|
349
|
+
|
|
350
|
+
# ── cli ───────────────────────────────────────────────────────────────────
|
|
351
|
+
_write(result, pkg_dir / "cli" / "__init__.py", '"""CLI package."""\n')
|
|
352
|
+
_write(result, pkg_dir / "cli" / "main.py", _cli_main(project_name, version))
|
|
353
|
+
_write(result, pkg_dir / "cli" / "commands" / "__init__.py",
|
|
354
|
+
_cli_commands_init())
|
|
355
|
+
_write(result, pkg_dir / "cli" / "commands" / "run.py",
|
|
356
|
+
_cli_run_command(project_name))
|
|
357
|
+
|
|
358
|
+
# ── utils ─────────────────────────────────────────────────────────────────
|
|
359
|
+
_write(result, project_dir / "utils" / "__init__.py", '"""Shared utilities."""\n')
|
|
360
|
+
_write(result, project_dir / "utils" / "logger.py", _logger_util())
|
|
361
|
+
if include_llm:
|
|
362
|
+
_write(result, project_dir / "utils" / "llm.py", _llm_util(project_name))
|
|
363
|
+
|
|
364
|
+
# ── tests ─────────────────────────────────────────────────────────────────
|
|
365
|
+
_write(result, project_dir / "tests" / "__init__.py", _tests_init())
|
|
366
|
+
_write(result, project_dir / "tests" / "test_cli.py", _test_cli(project_name))
|
|
367
|
+
|
|
368
|
+
# ── config ────────────────────────────────────────────────────────────────
|
|
369
|
+
cfg_name, cfg_content = _config_content(config_fmt)
|
|
370
|
+
_write(result, project_dir / cfg_name, cfg_content)
|
|
371
|
+
|
|
372
|
+
# ── dependency file ───────────────────────────────────────────────────────
|
|
373
|
+
if deps == "requirements":
|
|
374
|
+
from infrakit.scaffolder.generator import _requirements_txt
|
|
375
|
+
_write(result, project_dir / "requirements.txt",
|
|
376
|
+
_requirements_txt(project_name))
|
|
377
|
+
else:
|
|
378
|
+
_write(result, project_dir / "pyproject.toml",
|
|
379
|
+
_cli_pyproject(project_name, version, description, author, include_llm))
|
|
380
|
+
|
|
381
|
+
# ── repo files ────────────────────────────────────────────────────────────
|
|
382
|
+
_write(result, project_dir / "README.md",
|
|
383
|
+
_cli_readme(project_name, description, include_llm))
|
|
384
|
+
_write(result, project_dir / ".gitignore", _cli_gitignore())
|
|
385
|
+
|
|
386
|
+
return result
|