csrd-utils 0.3.5__tar.gz → 0.3.7__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.5 → csrd_utils-0.3.7}/PKG-INFO +1 -1
  2. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/pyproject.toml +1 -1
  3. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/__main__.py +3 -3
  4. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/augmentor.py +9 -0
  5. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/generator.py +26 -10
  6. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/.gitignore +0 -0
  7. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/README.md +0 -0
  8. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/__init__.py +0 -0
  9. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/doctor.py +0 -0
  10. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
  11. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
  12. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/manifest.yaml +0 -0
  13. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/requirements.fragment +0 -0
  14. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
  15. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
  16. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
  17. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
  18. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
  19. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/fragments.py +0 -0
  20. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/resources.py +0 -0
  21. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
  22. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
  23. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
  24. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.dockerignore +0 -0
  25. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.env.example +0 -0
  26. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/Dockerfile +0 -0
  27. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/README.md +0 -0
  28. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/docker-compose.yml +0 -0
  29. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/requirements.txt +0 -0
  30. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/__init__.py +0 -0
  31. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/cache.py +0 -0
  32. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/__init__.py +0 -0
  33. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/upstreams.py +0 -0
  34. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/__init__.py +0 -0
  35. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/delegates.py +0 -0
  36. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/item_repository.py +0 -0
  37. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/worker_broker.py +0 -0
  38. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/__init__.py +0 -0
  39. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/logging.py +0 -0
  40. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/__init__.py +0 -0
  41. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/item_repository.py +0 -0
  42. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/settings.py +0 -0
  43. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/__init__.py +0 -0
  44. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/__init__.py +0 -0
  45. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/health_view.py +0 -0
  46. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/items_view.py +0 -0
  47. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/__init__.py +0 -0
  48. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/tasks.py +0 -0
  49. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/__init__.py +0 -0
  50. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/conftest.py +0 -0
  51. {csrd_utils-0.3.5 → csrd_utils-0.3.7}/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.5
3
+ Version: 0.3.7
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csrd-utils"
3
- version = "0.3.5"
3
+ version = "0.3.7"
4
4
  description = "CLI utilities for csrd service generation and feature augmentation"
5
5
  license = { text = "MIT" }
6
6
  requires-python = ">=3.12"
@@ -173,10 +173,10 @@ def _build_parser() -> argparse.ArgumentParser:
173
173
  action="version",
174
174
  version=f"csrd-utils {version('csrd-utils')}",
175
175
  )
176
- sub = parser.add_subparsers(dest="command")
176
+ sub = parser.add_subparsers(dest="command", required=True)
177
177
 
178
178
  feature = sub.add_parser("feature", help="Feature operations")
179
- feature_sub = feature.add_subparsers(dest="feature_command")
179
+ feature_sub = feature.add_subparsers(dest="feature_command", required=True)
180
180
 
181
181
  list_cmd = feature_sub.add_parser("list", help="List bundled features")
182
182
  list_cmd.set_defaults(plan=False)
@@ -194,7 +194,7 @@ def _build_parser() -> argparse.ArgumentParser:
194
194
  doctor.add_argument("--json", action="store_true", help="Print doctor report as JSON")
195
195
 
196
196
  new = sub.add_parser("new", help="Generate new assets")
197
- new_sub = new.add_subparsers(dest="new_command")
197
+ new_sub = new.add_subparsers(dest="new_command", required=True)
198
198
  service = new_sub.add_parser("service", help="Generate a service from bundled template")
199
199
  service.add_argument("--name", help="Service name")
200
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
File without changes