geniespec 0.1.1.dev2__tar.gz → 0.1.1.dev3__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 (22) hide show
  1. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/PKG-INFO +8 -8
  2. geniespec-0.1.1.dev3/README.md +63 -0
  3. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/pyproject.toml +1 -1
  4. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/pyproject.toml.orig +1 -1
  5. geniespec-0.1.1.dev3/src/geniespec/cli.py +123 -0
  6. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/src/geniespec/genie.py +4 -3
  7. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/src/geniespec/languages.py +0 -6
  8. geniespec-0.1.1.dev2/README.md +0 -63
  9. geniespec-0.1.1.dev2/src/geniespec/cli.py +0 -85
  10. geniespec-0.1.1.dev2/src/geniespec/templates/python/pyproject.toml.j2 +0 -8
  11. geniespec-0.1.1.dev2/src/geniespec/templates/python/src/{service}/py.typed +0 -0
  12. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/src/geniespec/__init__.py +0 -0
  13. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/src/geniespec/api_schema.yaml +0 -0
  14. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/src/geniespec/renderable_types.py +0 -0
  15. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/__init__.py.j2 +0 -0
  16. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/v{version}/__init__.py.j2 +0 -0
  17. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/v{version}/api.py.j2 +0 -0
  18. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/v{version}/endpoints.py.j2 +0 -0
  19. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/v{version}/exceptions.py.j2 +0 -0
  20. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/v{version}/internal.py.j2 +0 -0
  21. {geniespec-0.1.1.dev2/src/geniespec/templates/python/src → geniespec-0.1.1.dev3/src/geniespec/templates/python-server}/{service}/v{version}/resources.py.j2 +0 -0
  22. {geniespec-0.1.1.dev2 → geniespec-0.1.1.dev3}/src/geniespec/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geniespec
3
- Version: 0.1.1.dev2
3
+ Version: 0.1.1.dev3
4
4
  Summary: Generate API code from an api.yaml spec
5
5
  License-Expression: MIT
6
6
  Requires-Dist: jinja2>=3.1.6
@@ -14,17 +14,17 @@ Description-Content-Type: text/markdown
14
14
 
15
15
  # Genie
16
16
 
17
- Genie (`geniespec` on PyPI) generates API code from an `api.yaml` spec. The output is a package you install, not code you commit.
17
+ Genie (`geniespec` on PyPI) generates API code from an `api.yaml` spec. Install it like any other tool and run it from your build scripts; the generated code is gitignored, not committed.
18
18
 
19
19
  ```
20
- uvx geniespec build --spec api.yaml [--output .genie]
21
- uvx geniespec watch --spec api.yaml [--output .genie]
20
+ geniespec build --spec api.yaml --target python-server [--output .]
21
+ geniespec watch --spec api.yaml --target python-server [--output .] [-- <command>]
22
22
  ```
23
23
 
24
- - `build` writes `<output>/python`: a project with `pyproject.toml` and `src/<service>_api/v<major>/` (`api`, `endpoints`, `resources`, `internal`, `exceptions`). Only changed files are rewritten and files it no longer generates are removed.
25
- - `watch` rebuilds whenever the spec changes. If the spec is invalid it logs the error and keeps the previous output.
26
- - Consumers depend on the output as an editable path dependency, e.g. `my-service-api = { path = ".genie/python", editable = true }` in `[tool.uv.sources]`, and import it as `my_service_api.v1`.
27
- - Generated code needs `kiba-core` (`core.api`, `core.util.typing_util`), `pydantic` and `starlette` from the consumer.
24
+ - `build` writes the target's package into the output directory, e.g. `./<service>_api/v<major>/` (`api`, `endpoints`, `resources`, `internal`, `exceptions`) for `python-server`. Only files in that package are touched: changed files are rewritten and files it no longer generates are removed.
25
+ - `watch` builds, then rebuilds whenever the spec changes. If the spec is invalid it logs the error and keeps the previous output. Given a command after `--`, it runs it once the first build is done, keeps rebuilding while it runs, exits with its exit code and stops it when watch itself is stopped, e.g. `geniespec watch --spec api.yaml --target python-server -- uvicorn app:app --reload`.
26
+ - Code run from the output directory imports the package directly, e.g. `from my_service_api.v1 import api`. Generated `python-server` code needs `kiba-core` (`core.api`, `core.util.typing_util`), `pydantic` and `starlette`.
27
+ - The bundled templates are examples of a kibalabs-style target.
28
28
 
29
29
  # Development
30
30
 
@@ -0,0 +1,63 @@
1
+ # Genie
2
+
3
+ Genie (`geniespec` on PyPI) generates API code from an `api.yaml` spec. Install it like any other tool and run it from your build scripts; the generated code is gitignored, not committed.
4
+
5
+ ```
6
+ geniespec build --spec api.yaml --target python-server [--output .]
7
+ geniespec watch --spec api.yaml --target python-server [--output .] [-- <command>]
8
+ ```
9
+
10
+ - `build` writes the target's package into the output directory, e.g. `./<service>_api/v<major>/` (`api`, `endpoints`, `resources`, `internal`, `exceptions`) for `python-server`. Only files in that package are touched: changed files are rewritten and files it no longer generates are removed.
11
+ - `watch` builds, then rebuilds whenever the spec changes. If the spec is invalid it logs the error and keeps the previous output. Given a command after `--`, it runs it once the first build is done, keeps rebuilding while it runs, exits with its exit code and stops it when watch itself is stopped, e.g. `geniespec watch --spec api.yaml --target python-server -- uvicorn app:app --reload`.
12
+ - Code run from the output directory imports the package directly, e.g. `from my_service_api.v1 import api`. Generated `python-server` code needs `kiba-core` (`core.api`, `core.util.typing_util`), `pydantic` and `starlette`.
13
+ - The bundled templates are examples of a kibalabs-style target.
14
+
15
+ # Development
16
+
17
+ ```
18
+ make install
19
+ make lint-check type-check test
20
+ ```
21
+
22
+ Merges to `main` publish a dev version to PyPI. To release, bump `version` in `pyproject.toml` in a PR, then run the Release workflow on `main` (Actions → Release → Run workflow). It tags `vX.Y.Z` and the Publish workflow publishes it. Only the release workflow can push version tags.
23
+
24
+ # Things that need work
25
+
26
+ ## Typing of params and fields in general
27
+
28
+ Enum and OneOf not supported yet
29
+
30
+ I think we need to create an object for Type. what would it look like?
31
+
32
+
33
+ ```
34
+ class BaseFieldType:
35
+ isNullable: bool
36
+
37
+
38
+ class RawFieldType(BaseFieldType):
39
+ name: str
40
+
41
+
42
+ class ClassFieldType(BaseFieldType):
43
+ fieldType: RawFieldType
44
+
45
+
46
+ class ListFieldType:(BaseFieldType)
47
+ fieldType: RawFieldType
48
+ isNullable: bool
49
+
50
+
51
+ class MappingFieldType(BaseFieldType):
52
+ keyFieldType: RawFieldType
53
+ valueFieldType: RawFieldType
54
+
55
+
56
+ class OneOfFieldType(BaseFieldType):
57
+ fieldTypes: list[RawFieldType]
58
+
59
+
60
+ class EnumFieldType(BaseFieldType):
61
+ values: list[str]
62
+
63
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "geniespec"
3
- version = "0.1.1.dev2"
3
+ version = "0.1.1.dev3"
4
4
  description = "Generate API code from an api.yaml spec"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "geniespec"
3
- version = "0.1.1.dev2"
3
+ version = "0.1.1.dev3"
4
4
  description = "Generate API code from an api.yaml spec"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1,123 @@
1
+ import argparse
2
+ import pathlib
3
+ import signal
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ import time
8
+ import types
9
+
10
+ import yaml # type: ignore[import-untyped]
11
+ from core import logging
12
+
13
+ from geniespec.genie import TARGET_LANGUAGE_CODES
14
+ from geniespec.genie import Renderer
15
+ from geniespec.genie import SyntacticApiValidator
16
+ from geniespec.types import KibaApi
17
+
18
+ WATCH_INTERVAL_SECONDS = 0.5
19
+ COMMAND_STOP_TIMEOUT_SECONDS = 10
20
+
21
+
22
+ def _sync_directory(sourceDirectoryPath: pathlib.Path, targetDirectoryPath: pathlib.Path) -> list[pathlib.Path]:
23
+ changedPaths: list[pathlib.Path] = []
24
+ sourceRelativePaths = {path.relative_to(sourceDirectoryPath) for path in sourceDirectoryPath.rglob('*') if path.is_file()}
25
+ for relativePath in sorted(sourceRelativePaths):
26
+ content = (sourceDirectoryPath / relativePath).read_bytes()
27
+ targetPath = targetDirectoryPath / relativePath
28
+ if targetPath.is_file() and targetPath.read_bytes() == content:
29
+ continue
30
+ targetPath.parent.mkdir(parents=True, exist_ok=True)
31
+ temporaryPath = targetPath.with_name(f'.{targetPath.name}.tmp')
32
+ temporaryPath.write_bytes(content)
33
+ temporaryPath.replace(targetPath)
34
+ changedPaths.append(targetPath)
35
+ stalePaths = [path for path in targetDirectoryPath.rglob('*') if path.is_file() and '__pycache__' not in path.parts and path.relative_to(targetDirectoryPath) not in sourceRelativePaths]
36
+ for stalePath in stalePaths:
37
+ stalePath.unlink()
38
+ return changedPaths + stalePaths
39
+
40
+
41
+ def build(specPath: pathlib.Path, target: str, outputDirectoryPath: pathlib.Path) -> list[pathlib.Path]:
42
+ with open(specPath) as specFile:
43
+ apiYaml = yaml.safe_load(specFile)
44
+ SyntacticApiValidator().validate(apiYaml)
45
+ api = KibaApi(**apiYaml)
46
+ with tempfile.TemporaryDirectory() as renderDirectory:
47
+ Renderer(target=target).render_directory(api=api, outputDirectoryPath=renderDirectory)
48
+ changedPaths: list[pathlib.Path] = []
49
+ # NOTE(krishan711): only sync the packages the target generates so other files in the output directory are never touched
50
+ for packagePath in sorted(pathlib.Path(renderDirectory).iterdir()):
51
+ changedPaths += _sync_directory(sourceDirectoryPath=packagePath, targetDirectoryPath=outputDirectoryPath / packagePath.name)
52
+ return changedPaths
53
+
54
+
55
+ def _build_and_log(specPath: pathlib.Path, target: str, outputDirectoryPath: pathlib.Path) -> None:
56
+ changedPaths = build(specPath=specPath, target=target, outputDirectoryPath=outputDirectoryPath)
57
+ logging.info(f'geniespec: built {specPath} into {outputDirectoryPath} ({len(changedPaths)} files changed)')
58
+
59
+
60
+ def _stop_process(process: subprocess.Popen[bytes]) -> None:
61
+ if process.poll() is not None:
62
+ return
63
+ process.terminate()
64
+ try:
65
+ process.wait(timeout=COMMAND_STOP_TIMEOUT_SECONDS)
66
+ except subprocess.TimeoutExpired:
67
+ process.kill()
68
+ process.wait()
69
+
70
+
71
+ def watch(specPath: pathlib.Path, target: str, outputDirectoryPath: pathlib.Path, command: list[str]) -> int:
72
+ lastModifiedTime: int | None = None
73
+ process: subprocess.Popen[bytes] | None = None
74
+ try:
75
+ while True:
76
+ try:
77
+ modifiedTime: int | None = specPath.stat().st_mtime_ns
78
+ except FileNotFoundError:
79
+ modifiedTime = lastModifiedTime
80
+ if modifiedTime != lastModifiedTime:
81
+ lastModifiedTime = modifiedTime
82
+ try:
83
+ _build_and_log(specPath=specPath, target=target, outputDirectoryPath=outputDirectoryPath)
84
+ except Exception as exception: # noqa: BLE001
85
+ logging.error(f'geniespec: failed to build {specPath}, keeping the previous output: {exception}')
86
+ if command and process is None:
87
+ process = subprocess.Popen(command) # noqa: S603
88
+ if process is not None and process.poll() is not None:
89
+ return process.returncode
90
+ time.sleep(WATCH_INTERVAL_SECONDS)
91
+ finally:
92
+ if process is not None:
93
+ _stop_process(process=process)
94
+
95
+
96
+ def _exit_on_terminate(signalNumber: int, frame: types.FrameType | None) -> None: # noqa: ARG001
97
+ sys.exit(128 + signalNumber)
98
+
99
+
100
+ def main() -> None:
101
+ parser = argparse.ArgumentParser(prog='geniespec')
102
+ subparsers = parser.add_subparsers(dest='subcommand', required=True)
103
+ for subcommand in ('build', 'watch'):
104
+ subparser = subparsers.add_parser(subcommand)
105
+ subparser.add_argument('--spec', type=pathlib.Path, required=True)
106
+ subparser.add_argument('--target', choices=sorted(TARGET_LANGUAGE_CODES), required=True)
107
+ subparser.add_argument('--output', type=pathlib.Path, default=pathlib.Path())
108
+ subparsers.choices['watch'].add_argument('command', nargs=argparse.REMAINDER, help='command to run while watching, after --')
109
+ args = parser.parse_args()
110
+ logging.basicConfig(level=logging.INFO)
111
+ if args.subcommand == 'build':
112
+ _build_and_log(specPath=args.spec, target=args.target, outputDirectoryPath=args.output)
113
+ return
114
+ command = args.command[1:] if args.command[:1] == ['--'] else args.command
115
+ signal.signal(signal.SIGTERM, _exit_on_terminate)
116
+ try:
117
+ sys.exit(watch(specPath=args.spec, target=args.target, outputDirectoryPath=args.output, command=command))
118
+ except KeyboardInterrupt:
119
+ return
120
+
121
+
122
+ if __name__ == '__main__':
123
+ main()
@@ -46,6 +46,7 @@ from geniespec.types import ResponseTypes
46
46
 
47
47
  SCHEMA_FILE_PATH = pathlib.Path(__file__).parent / 'api_schema.yaml'
48
48
  TEMPLATES_DIRECTORY_PATH = pathlib.Path(__file__).parent / 'templates'
49
+ TARGET_LANGUAGE_CODES = {'python-server': 'python'}
49
50
 
50
51
 
51
52
  class SyntacticValidationException(BadRequestException):
@@ -80,9 +81,9 @@ JINJA_FUNCTIONS: dict[str, typing.Callable[[str], BaseLanguageDefinition]] = {
80
81
 
81
82
 
82
83
  class Renderer:
83
- def __init__(self) -> None:
84
- self.templateDirectoryPath = str(TEMPLATES_DIRECTORY_PATH / 'python')
85
- self.languageDefinition = get_language_definition(languageCode='python')
84
+ def __init__(self, target: str) -> None:
85
+ self.templateDirectoryPath = str(TEMPLATES_DIRECTORY_PATH / target)
86
+ self.languageDefinition = get_language_definition(languageCode=TARGET_LANGUAGE_CODES[target])
86
87
  self.jinjaEnvironment = jinja2.Environment(loader=jinja2.FileSystemLoader(self.templateDirectoryPath), autoescape=jinja2.select_autoescape(), keep_trailing_newline=True, trim_blocks=True)
87
88
  self.jinjaEnvironment.globals['autogenerated_notice'] = self.languageDefinition.AUTOGENERATED_NOTICE
88
89
 
@@ -75,9 +75,6 @@ class BaseLanguageDefinition:
75
75
  def package_name(self, api: RenderableApi) -> str:
76
76
  raise NotImplementedError
77
77
 
78
- def distribution_name(self, api: RenderableApi) -> str:
79
- raise NotImplementedError
80
-
81
78
  def validation_errors(self, api: RenderableApi) -> list[str]:
82
79
  raise NotImplementedError
83
80
 
@@ -214,9 +211,6 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
214
211
  def package_name(self, api: RenderableApi) -> str:
215
212
  return f'{self.snake_name(name=api.serviceName)}_api'
216
213
 
217
- def distribution_name(self, api: RenderableApi) -> str:
218
- return f'{self.kebab_name(name=api.serviceName)}-api'
219
-
220
214
  def internal_class_name(self, api: RenderableApi) -> str:
221
215
  return f'{self.pascal_name(name=api.serviceName)}ApiV{api.version.major}Internal'
222
216
 
@@ -1,63 +0,0 @@
1
- # Genie
2
-
3
- Genie (`geniespec` on PyPI) generates API code from an `api.yaml` spec. The output is a package you install, not code you commit.
4
-
5
- ```
6
- uvx geniespec build --spec api.yaml [--output .genie]
7
- uvx geniespec watch --spec api.yaml [--output .genie]
8
- ```
9
-
10
- - `build` writes `<output>/python`: a project with `pyproject.toml` and `src/<service>_api/v<major>/` (`api`, `endpoints`, `resources`, `internal`, `exceptions`). Only changed files are rewritten and files it no longer generates are removed.
11
- - `watch` rebuilds whenever the spec changes. If the spec is invalid it logs the error and keeps the previous output.
12
- - Consumers depend on the output as an editable path dependency, e.g. `my-service-api = { path = ".genie/python", editable = true }` in `[tool.uv.sources]`, and import it as `my_service_api.v1`.
13
- - Generated code needs `kiba-core` (`core.api`, `core.util.typing_util`), `pydantic` and `starlette` from the consumer.
14
-
15
- # Development
16
-
17
- ```
18
- make install
19
- make lint-check type-check test
20
- ```
21
-
22
- Merges to `main` publish a dev version to PyPI. To release, bump `version` in `pyproject.toml` in a PR, then run the Release workflow on `main` (Actions → Release → Run workflow). It tags `vX.Y.Z` and the Publish workflow publishes it. Only the release workflow can push version tags.
23
-
24
- # Things that need work
25
-
26
- ## Typing of params and fields in general
27
-
28
- Enum and OneOf not supported yet
29
-
30
- I think we need to create an object for Type. what would it look like?
31
-
32
-
33
- ```
34
- class BaseFieldType:
35
- isNullable: bool
36
-
37
-
38
- class RawFieldType(BaseFieldType):
39
- name: str
40
-
41
-
42
- class ClassFieldType(BaseFieldType):
43
- fieldType: RawFieldType
44
-
45
-
46
- class ListFieldType:(BaseFieldType)
47
- fieldType: RawFieldType
48
- isNullable: bool
49
-
50
-
51
- class MappingFieldType(BaseFieldType):
52
- keyFieldType: RawFieldType
53
- valueFieldType: RawFieldType
54
-
55
-
56
- class OneOfFieldType(BaseFieldType):
57
- fieldTypes: list[RawFieldType]
58
-
59
-
60
- class EnumFieldType(BaseFieldType):
61
- values: list[str]
62
-
63
- ```
@@ -1,85 +0,0 @@
1
- import argparse
2
- import pathlib
3
- import tempfile
4
- import time
5
-
6
- import yaml # type: ignore[import-untyped]
7
- from core import logging
8
-
9
- from geniespec.genie import Renderer
10
- from geniespec.genie import SyntacticApiValidator
11
- from geniespec.types import KibaApi
12
-
13
- WATCH_INTERVAL_SECONDS = 0.5
14
-
15
-
16
- def _sync_directory(sourceDirectoryPath: pathlib.Path, targetDirectoryPath: pathlib.Path) -> list[pathlib.Path]:
17
- changedPaths: list[pathlib.Path] = []
18
- sourceRelativePaths = {path.relative_to(sourceDirectoryPath) for path in sourceDirectoryPath.rglob('*') if path.is_file()}
19
- for relativePath in sorted(sourceRelativePaths):
20
- content = (sourceDirectoryPath / relativePath).read_bytes()
21
- targetPath = targetDirectoryPath / relativePath
22
- if targetPath.is_file() and targetPath.read_bytes() == content:
23
- continue
24
- targetPath.parent.mkdir(parents=True, exist_ok=True)
25
- temporaryPath = targetPath.with_name(f'.{targetPath.name}.tmp')
26
- temporaryPath.write_bytes(content)
27
- temporaryPath.replace(targetPath)
28
- changedPaths.append(targetPath)
29
- stalePaths = [path for path in targetDirectoryPath.rglob('*') if path.is_file() and '__pycache__' not in path.parts and path.relative_to(targetDirectoryPath) not in sourceRelativePaths]
30
- for stalePath in stalePaths:
31
- stalePath.unlink()
32
- return changedPaths + stalePaths
33
-
34
-
35
- def build(specPath: pathlib.Path, outputDirectoryPath: pathlib.Path) -> list[pathlib.Path]:
36
- with open(specPath) as specFile:
37
- apiYaml = yaml.safe_load(specFile)
38
- SyntacticApiValidator().validate(apiYaml)
39
- api = KibaApi(**apiYaml)
40
- with tempfile.TemporaryDirectory() as renderDirectory:
41
- Renderer().render_directory(api=api, outputDirectoryPath=renderDirectory)
42
- return _sync_directory(sourceDirectoryPath=pathlib.Path(renderDirectory), targetDirectoryPath=outputDirectoryPath / 'python')
43
-
44
-
45
- def _build_and_log(specPath: pathlib.Path, outputDirectoryPath: pathlib.Path) -> None:
46
- changedPaths = build(specPath=specPath, outputDirectoryPath=outputDirectoryPath)
47
- logging.info(f'geniespec: built {specPath} into {outputDirectoryPath} ({len(changedPaths)} files changed)')
48
-
49
-
50
- def watch(specPath: pathlib.Path, outputDirectoryPath: pathlib.Path) -> None:
51
- lastModifiedTime: int | None = None
52
- while True:
53
- try:
54
- modifiedTime: int | None = specPath.stat().st_mtime_ns
55
- except FileNotFoundError:
56
- modifiedTime = lastModifiedTime
57
- if modifiedTime != lastModifiedTime:
58
- lastModifiedTime = modifiedTime
59
- try:
60
- _build_and_log(specPath=specPath, outputDirectoryPath=outputDirectoryPath)
61
- except Exception as exception: # noqa: BLE001
62
- logging.error(f'geniespec: failed to build {specPath}, keeping the previous output: {exception}')
63
- time.sleep(WATCH_INTERVAL_SECONDS)
64
-
65
-
66
- def main() -> None:
67
- parser = argparse.ArgumentParser(prog='geniespec')
68
- subparsers = parser.add_subparsers(dest='command', required=True)
69
- for command in ('build', 'watch'):
70
- subparser = subparsers.add_parser(command)
71
- subparser.add_argument('--spec', type=pathlib.Path, required=True)
72
- subparser.add_argument('--output', type=pathlib.Path, default=pathlib.Path('.genie'))
73
- args = parser.parse_args()
74
- logging.basicConfig(level=logging.INFO)
75
- if args.command == 'build':
76
- _build_and_log(specPath=args.spec, outputDirectoryPath=args.output)
77
- return
78
- try:
79
- watch(specPath=args.spec, outputDirectoryPath=args.output)
80
- except KeyboardInterrupt:
81
- return
82
-
83
-
84
- if __name__ == '__main__':
85
- main()
@@ -1,8 +0,0 @@
1
- {{ autogenerated_notice }}
2
- [project]
3
- name = "{{ target.distribution_name(api=api) }}"
4
- version = "0.0.0"
5
-
6
- [build-system]
7
- requires = ["uv_build>=0.12,<0.13"]
8
- build-backend = "uv_build"