Skip to content

Commit a8ded3b

Browse files
committed
add pip script to generate code
1 parent 05f744e commit a8ded3b

5 files changed

Lines changed: 81 additions & 44 deletions

File tree

pyproject.toml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,26 @@ license = { text = "AGPL-3.0-only" }
66
license-files = ["LICENSE"]
77
readme = "README.md"
88
requires-python = ">=3.11"
9-
authors = [
10-
{ name = "Paul Winterstein", email = "paul.winterstein@swr.de" },
11-
]
9+
authors = [{ name = "Paul Winterstein", email = "paul.winterstein@swr.de" }]
1210
maintainers = [
1311
{ name = "SWR Media-over-IP Team", email = "moip@swr.de" },
1412
{ name = "Josia Hildebrandt", email = "manuel_josia.hildebrandt@swr.de" },
1513
]
16-
keywords = ["videoipath", "automation", "nevion", "media-over-ip", "st2110", "orchestration"]
14+
keywords = [
15+
"videoipath",
16+
"automation",
17+
"nevion",
18+
"media-over-ip",
19+
"st2110",
20+
"orchestration",
21+
]
1722
dependencies = [
1823
"requests (>=2.31.0,<3.0.0)",
1924
"pydantic (>=2.6.4,<3.0.0)",
2025
"pydantic-extra-types (>=2.6.0,<3.0.0)",
2126
"pydantic-settings (>=2.2.1,<3.0.0)",
2227
"urllib3 (>=2.2.3,<3.0.0)",
23-
"deepdiff (>=8.1.1,<9.0.0)"
28+
"deepdiff (>=8.1.1,<9.0.0)",
2429
]
2530

2631
[project.urls]
@@ -51,6 +56,8 @@ env_files = ["tests/.env.test"]
5156
[virtualenvs]
5257
in-project = true
5358

59+
[project.scripts]
60+
set-videoipath-version = "videoipath_automation_tool.scripts.generate_all:main"
5461

5562
[tool.ruff]
5663
include = ["pyproject.toml", "src/**/*.py", "tests/**/*.py"]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from videoipath_automation_tool.scripts.generate_driver_models import DEFAULT_OUTPUT_FILE, DEFAULT_SCHEMA_FILE, parser
2+
from videoipath_automation_tool.scripts.generate_driver_models import main as generate_driver_models
3+
from videoipath_automation_tool.scripts.generate_overloads import main as generate_overloads
4+
5+
6+
def main(
7+
schema_file: str = DEFAULT_SCHEMA_FILE,
8+
output_file: str = DEFAULT_OUTPUT_FILE,
9+
):
10+
generate_driver_models(schema_file, output_file)
11+
generate_overloads()
12+
13+
14+
if __name__ == "__main__":
15+
args = parser.parse_args()
16+
main(args.schema_file, args.output_file)

src/scripts/generate_driver_models.py renamed to src/videoipath_automation_tool/scripts/generate_driver_models.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,29 @@
11
import argparse
2-
import importlib.util
32
import json
3+
import os
44

5+
from videoipath_automation_tool.utils.script_utils import ROOT_DIR, load_module
56

6-
def load_pydantic_model_builder():
7-
spec = importlib.util.spec_from_file_location(
8-
"pydantic_model_builder", "src/videoipath_automation_tool/utils/pydantic_model_builder.py"
9-
)
10-
11-
if spec is None or spec.loader is None:
12-
raise ValueError("Failed to load pydantic_model_builder module")
13-
14-
module = importlib.util.module_from_spec(spec)
15-
spec.loader.exec_module(module)
16-
return module
17-
7+
DEFAULT_SCHEMA_FILE = os.path.join(ROOT_DIR, "apps", "inventory", "model", "driver_schema", "2024.3.3.json")
8+
DEFAULT_OUTPUT_FILE = os.path.join(ROOT_DIR, "apps", "inventory", "model", "drivers.py")
189

1910
parser = argparse.ArgumentParser(description="Generate Pydantic models from driver schema")
2011
parser.add_argument(
2112
"schema_file",
2213
nargs="?",
23-
default="src/videoipath_automation_tool/apps/inventory/model/driver_schema/2024.3.3.json",
14+
default=DEFAULT_SCHEMA_FILE,
2415
help="Path to the driver schema JSON file",
2516
)
2617
parser.add_argument(
2718
"output_file",
2819
nargs="?",
29-
default="src/videoipath_automation_tool/apps/inventory/model/drivers.py",
20+
default=DEFAULT_OUTPUT_FILE,
3021
help="Path where the generated Python file will be saved",
3122
)
3223

3324

3425
def _generate_driver_model(driver_schema: dict) -> str:
35-
pmb_module = load_pydantic_model_builder()
26+
pmb_module = load_module("pydantic_model_builder", os.path.join(ROOT_DIR, "utils", "pydantic_model_builder.py"))
3627
PydanticModelBuilder = pmb_module.PydanticModelBuilder
3728
PydanticModelField = pmb_module.PydanticModelField
3829

@@ -127,9 +118,11 @@ def format_value(value: str | int | float) -> str:
127118
return field["_schema"]["type"], None
128119

129120

130-
if __name__ == "__main__":
131-
args = parser.parse_args()
132-
schema = json.load(open(args.schema_file))
121+
def main(
122+
schema_file: str = DEFAULT_SCHEMA_FILE,
123+
output_file: str = DEFAULT_OUTPUT_FILE,
124+
):
125+
schema = json.load(open(schema_file))
133126

134127
drivers = schema["data"]["status"]["system"]["drivers"]["_items"]
135128
driver_models = "\n\n".join([_generate_driver_model(driver) for driver in drivers])
@@ -141,7 +134,7 @@ def format_value(value: str | int | float) -> str:
141134
142135
# Notes:
143136
# - The name of the custom settings model follows the naming convention: CustomSettings_<driver_organization>_<driver_name>_<driver_version> => "." and "-" are replaced by "_"!
144-
# - {args.schema_file} is used as reference to define the custom settings model!
137+
# - {schema_file} is used as reference to define the custom settings model!
145138
# - The "driver_id" attribute is necessary for the discriminator, which is used to determine the correct model for the custom settings in DeviceConfiguration!
146139
# - The "alias" attribute is used to map the attribute to the correct key (with driver organization & name) in the JSON payload for the API!
147140
# - "DriverLiteral" is used to provide a list of all possible drivers in the IDEs IntelliSense!
@@ -167,6 +160,11 @@ class DriverCustomSettings(ABC, BaseModel, validate_assignment=True): ...
167160
"""
168161
print("Drivers generated successfully!")
169162

170-
with open(args.output_file, "w") as f:
163+
with open(output_file, "w") as f:
171164
f.write(code)
172-
print(f"Updated {args.output_file}")
165+
print(f"Updated {output_file}")
166+
167+
168+
if __name__ == "__main__":
169+
args = parser.parse_args()
170+
main(args.schema_file, args.output_file)

src/scripts/generate_overloads.py renamed to src/videoipath_automation_tool/scripts/generate_overloads.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
1-
import importlib.util
1+
import os
22
import re
33
from typing import Callable
44

5+
from videoipath_automation_tool.utils.script_utils import ROOT_DIR, load_module
56

6-
def load_driver_settings():
7-
spec = importlib.util.spec_from_file_location(
8-
"drivers_module", "src/videoipath_automation_tool/apps/inventory/model/drivers.py"
9-
)
10-
11-
if spec is None or spec.loader is None:
12-
raise ValueError("Failed to load drivers module")
13-
14-
module = importlib.util.module_from_spec(spec)
15-
spec.loader.exec_module(module)
16-
return getattr(module, "DRIVER_ID_TO_CUSTOM_SETTINGS", {})
17-
18-
19-
DRIVER_ID_TO_CUSTOM_SETTINGS = load_driver_settings()
7+
DRIVERS_MODULE = load_module(
8+
"drivers_module",
9+
os.path.join(ROOT_DIR, "apps", "inventory", "model", "drivers.py"),
10+
)
11+
DRIVER_ID_TO_CUSTOM_SETTINGS = DRIVERS_MODULE.DRIVER_ID_TO_CUSTOM_SETTINGS
2012

2113

2214
def generate_create_device_overloads() -> str:
@@ -44,7 +36,7 @@ def generate_get_device_overloads() -> str:
4436

4537

4638
def generate_overloads(method: str, generate_overloads: Callable) -> None:
47-
FILE_PATH = f"src/videoipath_automation_tool/apps/inventory/app/{method}.py"
39+
FILE_PATH = os.path.join(ROOT_DIR, "apps", "inventory", "app", f"{method}.py")
4840

4941
with open(FILE_PATH, "r") as f:
5042
content = f.read()
@@ -70,11 +62,15 @@ def generate_overloads(method: str, generate_overloads: Callable) -> None:
7062
print(f"Updated overloads in {FILE_PATH} ✅")
7163

7264

73-
if __name__ == "__main__":
65+
def main():
7466
overloaded_methods = {
7567
"create_device": generate_create_device_overloads,
7668
"create_device_from_discovered_device": generate_create_device_from_discovered_device_overloads,
7769
"get_device": generate_get_device_overloads,
7870
}
7971
for method, generator in overloaded_methods.items():
8072
generate_overloads(method, generator)
73+
74+
75+
if __name__ == "__main__":
76+
main()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import importlib.util
2+
import os
3+
from types import ModuleType
4+
5+
UTILS_DIR = os.path.dirname(os.path.abspath(__file__))
6+
ROOT_DIR = os.path.dirname(UTILS_DIR)
7+
8+
9+
def load_module(module_name: str, file_path: str) -> ModuleType:
10+
spec = importlib.util.spec_from_file_location(
11+
module_name,
12+
file_path,
13+
)
14+
15+
if spec is None or spec.loader is None:
16+
raise ValueError("Failed to load drivers module")
17+
18+
module = importlib.util.module_from_spec(spec)
19+
spec.loader.exec_module(module)
20+
return module

0 commit comments

Comments
 (0)