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.
Files changed (51) hide show
  1. infrakit/__init__.py +0 -0
  2. infrakit/cli/__init__.py +1 -0
  3. infrakit/cli/commands/__init__.py +1 -0
  4. infrakit/cli/commands/deps.py +530 -0
  5. infrakit/cli/commands/init.py +129 -0
  6. infrakit/cli/commands/llm.py +295 -0
  7. infrakit/cli/commands/logger.py +160 -0
  8. infrakit/cli/commands/module.py +342 -0
  9. infrakit/cli/commands/time.py +81 -0
  10. infrakit/cli/main.py +65 -0
  11. infrakit/core/__init__.py +0 -0
  12. infrakit/core/config/__init__.py +0 -0
  13. infrakit/core/config/converter.py +480 -0
  14. infrakit/core/config/exporter.py +304 -0
  15. infrakit/core/config/loader.py +713 -0
  16. infrakit/core/config/validator.py +389 -0
  17. infrakit/core/logger/__init__.py +21 -0
  18. infrakit/core/logger/formatters.py +143 -0
  19. infrakit/core/logger/handlers.py +322 -0
  20. infrakit/core/logger/retention.py +176 -0
  21. infrakit/core/logger/setup.py +314 -0
  22. infrakit/deps/__init__.py +239 -0
  23. infrakit/deps/clean.py +141 -0
  24. infrakit/deps/depfile.py +405 -0
  25. infrakit/deps/health.py +357 -0
  26. infrakit/deps/optimizer.py +642 -0
  27. infrakit/deps/scanner.py +550 -0
  28. infrakit/llm/__init__.py +35 -0
  29. infrakit/llm/batch.py +165 -0
  30. infrakit/llm/client.py +575 -0
  31. infrakit/llm/key_manager.py +728 -0
  32. infrakit/llm/llm_readme.md +306 -0
  33. infrakit/llm/models.py +148 -0
  34. infrakit/llm/providers/__init__.py +5 -0
  35. infrakit/llm/providers/base.py +112 -0
  36. infrakit/llm/providers/gemini.py +164 -0
  37. infrakit/llm/providers/openai.py +168 -0
  38. infrakit/llm/rate_limiter.py +54 -0
  39. infrakit/scaffolder/__init__.py +31 -0
  40. infrakit/scaffolder/ai.py +508 -0
  41. infrakit/scaffolder/backend.py +555 -0
  42. infrakit/scaffolder/cli_tool.py +386 -0
  43. infrakit/scaffolder/generator.py +338 -0
  44. infrakit/scaffolder/pipeline.py +562 -0
  45. infrakit/scaffolder/registry.py +121 -0
  46. infrakit/time/__init__.py +60 -0
  47. infrakit/time/profiler.py +511 -0
  48. python_infrakit_dev-0.1.0.dist-info/METADATA +124 -0
  49. python_infrakit_dev-0.1.0.dist-info/RECORD +51 -0
  50. python_infrakit_dev-0.1.0.dist-info/WHEEL +4 -0
  51. 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