csrd-utils 0.3.4__tar.gz → 0.3.6__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.
Files changed (51) hide show
  1. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/PKG-INFO +2 -1
  2. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/README.md +1 -0
  3. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/pyproject.toml +1 -1
  4. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/__main__.py +10 -3
  5. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/augmentor.py +9 -0
  6. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/generator.py +26 -10
  7. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/.gitignore +0 -0
  8. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/__init__.py +0 -0
  9. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/doctor.py +0 -0
  10. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
  11. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
  12. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/manifest.yaml +0 -0
  13. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/requirements.fragment +0 -0
  14. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
  15. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
  16. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
  17. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
  18. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
  19. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/fragments.py +0 -0
  20. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/resources.py +0 -0
  21. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
  22. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
  23. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
  24. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.dockerignore +0 -0
  25. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.env.example +0 -0
  26. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/Dockerfile +0 -0
  27. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/README.md +0 -0
  28. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/docker-compose.yml +0 -0
  29. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/requirements.txt +0 -0
  30. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/__init__.py +0 -0
  31. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/cache.py +0 -0
  32. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/__init__.py +0 -0
  33. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/upstreams.py +0 -0
  34. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/__init__.py +0 -0
  35. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/delegates.py +0 -0
  36. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/item_repository.py +0 -0
  37. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/worker_broker.py +0 -0
  38. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/__init__.py +0 -0
  39. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/logging.py +0 -0
  40. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/__init__.py +0 -0
  41. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/item_repository.py +0 -0
  42. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/settings.py +0 -0
  43. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/__init__.py +0 -0
  44. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/__init__.py +0 -0
  45. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/health_view.py +0 -0
  46. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/items_view.py +0 -0
  47. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/__init__.py +0 -0
  48. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/tasks.py +0 -0
  49. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/__init__.py +0 -0
  50. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/conftest.py +0 -0
  51. {csrd_utils-0.3.4 → csrd_utils-0.3.6}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/test_items.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csrd-utils
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: CLI utilities for csrd service generation and feature augmentation
5
5
  Project-URL: Repository, https://github.com/csrd-api/fastapi-common
6
6
  Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/utils
@@ -25,6 +25,7 @@ pip install "csrd-utils @ git+https://github.com/csrd-api/fastapi-common.git#sub
25
25
 
26
26
  ```bash
27
27
  csrd --help
28
+ csrd --version
28
29
  csrd new service # interactive prompts
29
30
  csrd new service --interactive --name items # pre-fills name prompt with 'items'
30
31
  csrd new service --name inventory --no-interactive --output .
@@ -12,6 +12,7 @@ pip install "csrd-utils @ git+https://github.com/csrd-api/fastapi-common.git#sub
12
12
 
13
13
  ```bash
14
14
  csrd --help
15
+ csrd --version
15
16
  csrd new service # interactive prompts
16
17
  csrd new service --interactive --name items # pre-fills name prompt with 'items'
17
18
  csrd new service --name inventory --no-interactive --output .
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csrd-utils"
3
- version = "0.3.4"
3
+ version = "0.3.6"
4
4
  description = "CLI utilities for csrd service generation and feature augmentation"
5
5
  license = { text = "MIT" }
6
6
  requires-python = ">=3.12"
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import argparse
6
6
  import json
7
+ from importlib.metadata import version
7
8
  from pathlib import Path
8
9
  from typing import TypedDict
9
10
 
@@ -166,10 +167,16 @@ def _collect_new_service_config(args: argparse.Namespace) -> GenerateServiceArgs
166
167
 
167
168
  def _build_parser() -> argparse.ArgumentParser:
168
169
  parser = argparse.ArgumentParser(prog="csrd", description="csrd utilities")
169
- sub = parser.add_subparsers(dest="command")
170
+ parser.add_argument(
171
+ "-v",
172
+ "--version",
173
+ action="version",
174
+ version=f"csrd-utils {version('csrd-utils')}",
175
+ )
176
+ sub = parser.add_subparsers(dest="command", required=True)
170
177
 
171
178
  feature = sub.add_parser("feature", help="Feature operations")
172
- feature_sub = feature.add_subparsers(dest="feature_command")
179
+ feature_sub = feature.add_subparsers(dest="feature_command", required=True)
173
180
 
174
181
  list_cmd = feature_sub.add_parser("list", help="List bundled features")
175
182
  list_cmd.set_defaults(plan=False)
@@ -187,7 +194,7 @@ def _build_parser() -> argparse.ArgumentParser:
187
194
  doctor.add_argument("--json", action="store_true", help="Print doctor report as JSON")
188
195
 
189
196
  new = sub.add_parser("new", help="Generate new assets")
190
- new_sub = new.add_subparsers(dest="new_command")
197
+ new_sub = new.add_subparsers(dest="new_command", required=True)
191
198
  service = new_sub.add_parser("service", help="Generate a service from bundled template")
192
199
  service.add_argument("--name", help="Service name")
193
200
  service.add_argument("--output", type=Path, default=Path.cwd(), help="Output directory")
@@ -400,6 +400,15 @@ class ServiceAugmentor:
400
400
  print("❌ Service validation failed")
401
401
  return False, []
402
402
 
403
+ # Step 2.5: Check if feature already applied (idempotency)
404
+ creates = self.manifest.get("creates", [])
405
+ if creates:
406
+ first_created_file = Path(creates[0])
407
+ full_path = self.service_root / first_created_file
408
+ if full_path.exists():
409
+ print(f"✅ Feature '{feature_name}' already applied (detected {creates[0]})")
410
+ return True, []
411
+
403
412
  # Step 3: Check requirements
404
413
  req_errors = self.validate_requirements()
405
414
  if req_errors:
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
6
+ import shutil
5
7
  from pathlib import Path
6
8
  from typing import Any
7
9
 
@@ -94,14 +96,28 @@ def generate_service(
94
96
  has_structured_logging=has_structured_logging,
95
97
  )
96
98
 
97
- with templates_path() as template_root:
98
- template = template_root / "cookiecutter-service"
99
- generated = cookiecutter(
100
- str(template),
101
- no_input=True,
102
- extra_context=context,
103
- output_dir=str(output_root),
104
- overwrite_if_exists=overwrite_if_exists,
105
- )
99
+ # Suppress bytecode generation during template rendering.
100
+ old_dontwritebytecode = os.environ.get("PYTHONDONTWRITEBYTECODE")
101
+ try:
102
+ os.environ["PYTHONDONTWRITEBYTECODE"] = "1"
103
+ with templates_path() as template_root:
104
+ template = template_root / "cookiecutter-service"
105
+ generated = cookiecutter(
106
+ str(template),
107
+ no_input=True,
108
+ extra_context=context,
109
+ output_dir=str(output_root),
110
+ overwrite_if_exists=overwrite_if_exists,
111
+ )
112
+ finally:
113
+ if old_dontwritebytecode is None:
114
+ os.environ.pop("PYTHONDONTWRITEBYTECODE", None)
115
+ else:
116
+ os.environ["PYTHONDONTWRITEBYTECODE"] = old_dontwritebytecode
117
+
118
+ # Clean up any __pycache__ that may have been created despite PYTHONDONTWRITEBYTECODE.
119
+ generated_path = Path(generated).resolve()
120
+ for pycache_dir in generated_path.rglob("__pycache__"):
121
+ shutil.rmtree(pycache_dir, ignore_errors=True)
106
122
 
107
- return Path(generated).resolve()
123
+ return generated_path
File without changes