xtce2py 0.1.0__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.
- xtce2py-0.1.0/LICENSE +21 -0
- xtce2py-0.1.0/PKG-INFO +27 -0
- xtce2py-0.1.0/README.md +4 -0
- xtce2py-0.1.0/pyproject.toml +90 -0
- xtce2py-0.1.0/src/xtce2py/__init__.py +12 -0
- xtce2py-0.1.0/src/xtce2py/_validation.py +31 -0
- xtce2py-0.1.0/src/xtce2py/config.py +88 -0
- xtce2py-0.1.0/src/xtce2py/context_models.py +76 -0
- xtce2py-0.1.0/src/xtce2py/generator.py +209 -0
- xtce2py-0.1.0/src/xtce2py/main.py +332 -0
- xtce2py-0.1.0/src/xtce2py/static/_decoding.py +90 -0
- xtce2py-0.1.0/src/xtce2py/static/setup.py +6 -0
- xtce2py-0.1.0/src/xtce2py/templates/README.md.j2 +48 -0
- xtce2py-0.1.0/src/xtce2py/templates/__init__.py.j2 +11 -0
- xtce2py-0.1.0/src/xtce2py/templates/models.py.j2 +47 -0
- xtce2py-0.1.0/src/xtce2py/templates/pyproject.toml.j2 +37 -0
- xtce2py-0.1.0/src/xtce2py/templates/simple_parser.py.j2 +0 -0
- xtce2py-0.1.0/src/xtce2py/templates/tree_parser.py.j2 +312 -0
- xtce2py-0.1.0/src/xtce2py/utils.py +78 -0
- xtce2py-0.1.0/src/xtce2py/xtce.py +142 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_1/__init__.py +281 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_1/context_models.py +4 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_1/dtc-06-11-06.xsd +3229 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_1/dtc_06_11_06.py +6589 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_1/parser.py +902 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_2/__init__.py +635 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_2/dtc-18-02-04.xsd +5918 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_2/dtc_18_02_04.py +10370 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_2/parser.py +27 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_3/__init__.py +647 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_3/dtc-25-02-18.xsd +6363 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_3/dtc_25_02_18.py +11011 -0
- xtce2py-0.1.0/src/xtce2py/xtce_1_3/parser.py +27 -0
- xtce2py-0.1.0/src/xtce2py/xtce_common/__init__.py +8 -0
- xtce2py-0.1.0/src/xtce2py/xtce_common/context_models.py +101 -0
- xtce2py-0.1.0/src/xtce2py/xtce_common/data_types.py +57 -0
- xtce2py-0.1.0/tests/test.py +16 -0
xtce2py-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 nrmiersen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
xtce2py-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xtce2py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A generator for creating a Python package from an XTCE file.
|
|
5
|
+
Author-Email: Nathan Miersen <nrmiersen@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Project-URL: Homepage, https://github.com/nrmiersen/xtce2py
|
|
10
|
+
Project-URL: Repository, https://github.com/nrmiersen/xtce2py
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: pydantic>=2.11.7
|
|
13
|
+
Requires-Dist: xsdata>=25.7
|
|
14
|
+
Requires-Dist: jinja2>=3.1.6
|
|
15
|
+
Requires-Dist: ruff>=0.12.4
|
|
16
|
+
Requires-Dist: docformatter>=1.7.7
|
|
17
|
+
Requires-Dist: typer[rich]>=0.16.0
|
|
18
|
+
Requires-Dist: setuptools>=80.9.0
|
|
19
|
+
Requires-Dist: lxml>=6.0.0
|
|
20
|
+
Requires-Dist: lxml-stubs>=0.5.1
|
|
21
|
+
Requires-Dist: pydantic-settings>=2.11.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# xtce2py
|
|
25
|
+
Generates a Python package from a source XTCE file.
|
|
26
|
+
|
|
27
|
+
**Alpha Software:** This package is currently in the alpha stage of development. The API is not stable and is subject to breaking changes in future releases. Please use with caution and report any bugs or issues you find.
|
xtce2py-0.1.0/README.md
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# xtce2py
|
|
2
|
+
Generates a Python package from a source XTCE file.
|
|
3
|
+
|
|
4
|
+
**Alpha Software:** This package is currently in the alpha stage of development. The API is not stable and is subject to breaking changes in future releases. Please use with caution and report any bugs or issues you find.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"pdm-backend",
|
|
4
|
+
]
|
|
5
|
+
build-backend = "pdm.backend"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "xtce2py"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
description = "A generator for creating a Python package from an XTCE file."
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Nathan Miersen", email = "nrmiersen@gmail.com" },
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"pydantic>=2.11.7",
|
|
20
|
+
"xsdata>=25.7",
|
|
21
|
+
"jinja2>=3.1.6",
|
|
22
|
+
"ruff>=0.12.4",
|
|
23
|
+
"docformatter>=1.7.7",
|
|
24
|
+
"typer[rich]>=0.16.0",
|
|
25
|
+
"setuptools>=80.9.0",
|
|
26
|
+
"lxml>=6.0.0",
|
|
27
|
+
"lxml-stubs>=0.5.1",
|
|
28
|
+
"pydantic-settings>=2.11.0",
|
|
29
|
+
]
|
|
30
|
+
requires-python = ">=3.11"
|
|
31
|
+
readme = "README.md"
|
|
32
|
+
license = "MIT"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
xtce2py = "xtce2py.main:app"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/nrmiersen/xtce2py"
|
|
39
|
+
Repository = "https://github.com/nrmiersen/xtce2py"
|
|
40
|
+
|
|
41
|
+
[dependency-groups]
|
|
42
|
+
dev = [
|
|
43
|
+
"xsdata[cli]>=25.7",
|
|
44
|
+
"bitstring>=4.3.1",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.pdm]
|
|
48
|
+
distribution = true
|
|
49
|
+
|
|
50
|
+
[tool.pdm.scripts]
|
|
51
|
+
xtce2py = "python -m xtce2py.main"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
xtce2py = [
|
|
55
|
+
"static/*",
|
|
56
|
+
"templates/*",
|
|
57
|
+
"xtce_1_1/*.xsd",
|
|
58
|
+
"xtce_1_2/*.xsd",
|
|
59
|
+
"xtce_1_3/*.xsd",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 88
|
|
64
|
+
exclude = [
|
|
65
|
+
"src/xtce2py/xtce_1_1/dtc_06_11_06.py",
|
|
66
|
+
"src/xtce2py/xtce_1_2/dtc_18_02_04.py",
|
|
67
|
+
"src/xtce2py/xtce_1_3/dtc_25_02_18.py",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = [
|
|
72
|
+
"D",
|
|
73
|
+
"I",
|
|
74
|
+
]
|
|
75
|
+
ignore = [
|
|
76
|
+
"D203",
|
|
77
|
+
"D213",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint.isort]
|
|
81
|
+
known-first-party = [
|
|
82
|
+
"xtce2py",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.ruff.format]
|
|
86
|
+
docstring-code-format = true
|
|
87
|
+
|
|
88
|
+
[tool.docformatter]
|
|
89
|
+
wrap-summaries = 72
|
|
90
|
+
wrap-descriptions = 72
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Validation-related data structures and exceptions."""
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ValidationSeverity(enum.Enum):
|
|
9
|
+
"""Enumeration for the severity of a validation issue."""
|
|
10
|
+
|
|
11
|
+
ERROR = "ERROR"
|
|
12
|
+
WARNING = "WARNING"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ValidationResult:
|
|
17
|
+
"""A structured container for a single validation finding."""
|
|
18
|
+
|
|
19
|
+
severity: ValidationSeverity
|
|
20
|
+
message: str
|
|
21
|
+
location: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class XtceSemanticValidationError(Exception):
|
|
25
|
+
"""Custom exception raised when fatal semantic errors are found."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, errors: List[ValidationResult]):
|
|
28
|
+
"""Initialize with a list of validation errors."""
|
|
29
|
+
self.errors = errors
|
|
30
|
+
error_list = "\n".join(f" - [in {e.location}]: {e.message}" for e in errors)
|
|
31
|
+
super().__init__(f"XTCE file has semantic validation errors:\n{error_list}")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Global configuration settings."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, field_validator
|
|
8
|
+
from pydantic_settings import (
|
|
9
|
+
BaseSettings,
|
|
10
|
+
PydanticBaseSettingsSource,
|
|
11
|
+
SettingsConfigDict,
|
|
12
|
+
TomlConfigSettingsSource,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LoggingSettings(BaseModel):
|
|
17
|
+
"""Logging configuration settings."""
|
|
18
|
+
|
|
19
|
+
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | None = None
|
|
20
|
+
file: Path | None = None
|
|
21
|
+
|
|
22
|
+
@field_validator("file", mode="before")
|
|
23
|
+
@classmethod
|
|
24
|
+
def expand_path(cls, v):
|
|
25
|
+
"""Expand user home directory and resolve path."""
|
|
26
|
+
if v is None:
|
|
27
|
+
return v
|
|
28
|
+
if isinstance(v, str):
|
|
29
|
+
# Expand ~ to user home directory
|
|
30
|
+
return Path(v).expanduser()
|
|
31
|
+
return v
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AuthorSettings(BaseModel):
|
|
35
|
+
"""Author information for output package."""
|
|
36
|
+
|
|
37
|
+
name: str = "Anonymous"
|
|
38
|
+
email: str = "user@example.com"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PackageDefaults(BaseModel):
|
|
42
|
+
"""Default values for package generation."""
|
|
43
|
+
|
|
44
|
+
suffix: str = "toolkit"
|
|
45
|
+
version: str = "1.0.0"
|
|
46
|
+
license: str = "MIT"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NamingSettings(BaseModel):
|
|
50
|
+
"""Naming conventions for generated package."""
|
|
51
|
+
|
|
52
|
+
use_existing_names: bool = True
|
|
53
|
+
acronyms: list[str] = ["CCSDS"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Settings(BaseSettings):
|
|
57
|
+
"""Settings model."""
|
|
58
|
+
|
|
59
|
+
author: AuthorSettings = AuthorSettings()
|
|
60
|
+
package_defaults: PackageDefaults = PackageDefaults()
|
|
61
|
+
naming_convention: NamingSettings = NamingSettings()
|
|
62
|
+
logging: LoggingSettings = LoggingSettings()
|
|
63
|
+
|
|
64
|
+
model_config = SettingsConfigDict(toml_file="xtce2py.toml")
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def settings_customise_sources(
|
|
68
|
+
cls,
|
|
69
|
+
settings_cls: type[BaseSettings],
|
|
70
|
+
init_settings: PydanticBaseSettingsSource,
|
|
71
|
+
env_settings: PydanticBaseSettingsSource,
|
|
72
|
+
dotenv_settings: PydanticBaseSettingsSource,
|
|
73
|
+
file_secret_settings: PydanticBaseSettingsSource,
|
|
74
|
+
) -> tuple[PydanticBaseSettingsSource, ...]:
|
|
75
|
+
"""Define the order of precedence for settings sources."""
|
|
76
|
+
return (
|
|
77
|
+
init_settings,
|
|
78
|
+
env_settings,
|
|
79
|
+
dotenv_settings,
|
|
80
|
+
file_secret_settings,
|
|
81
|
+
TomlConfigSettingsSource(settings_cls),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@functools.lru_cache(maxsize=1)
|
|
86
|
+
def get_settings() -> Settings:
|
|
87
|
+
"""Load and validate the settings, then cache the result."""
|
|
88
|
+
return Settings()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Context models for Jinja2 templates."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from xtce2py.xtce_common.context_models import ContainerDetailsContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class XtceMetadataContext:
|
|
11
|
+
"""Container for XTCE metadata information."""
|
|
12
|
+
|
|
13
|
+
name: str = "unknown"
|
|
14
|
+
description: Optional[str] = ""
|
|
15
|
+
version: Optional[str] = "1.0.0"
|
|
16
|
+
date: Optional[str] = ""
|
|
17
|
+
classification: Optional[str] = ""
|
|
18
|
+
authors: list[str] = field(default_factory=list)
|
|
19
|
+
notes: list[str] = field(default_factory=list)
|
|
20
|
+
history: list[str] = field(default_factory=list)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class PyprojectContext:
|
|
25
|
+
"""Container for the data needed by the pyproject.toml template."""
|
|
26
|
+
|
|
27
|
+
dist_name: str
|
|
28
|
+
package_name: str
|
|
29
|
+
version: str
|
|
30
|
+
metadata: XtceMetadataContext
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class ReadMeContext:
|
|
35
|
+
"""Container for the data needed by the README.md template."""
|
|
36
|
+
|
|
37
|
+
dist_name: str
|
|
38
|
+
package_name: str
|
|
39
|
+
xtce_version: str
|
|
40
|
+
metadata: XtceMetadataContext
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class InitContext:
|
|
45
|
+
"""Container for the data needed by the __init__.py template."""
|
|
46
|
+
|
|
47
|
+
metadata: XtceMetadataContext
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class ModelContext:
|
|
52
|
+
"""Container for the data needed by a model within the models.py template."""
|
|
53
|
+
|
|
54
|
+
name: str
|
|
55
|
+
parent: str = "PacketBase"
|
|
56
|
+
entries: list[dict[str, Any]] = field(default_factory=list)
|
|
57
|
+
description: Optional[str] = ""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class ModelsContext:
|
|
62
|
+
"""Container for the data needed by the models.py template."""
|
|
63
|
+
|
|
64
|
+
imports: dict[str, list[str]] = field(default_factory=dict)
|
|
65
|
+
containers: list[ModelContext] = field(default_factory=list)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class ParserContext:
|
|
70
|
+
"""Container for the data needed by the parser template."""
|
|
71
|
+
|
|
72
|
+
container_tree: dict[str, Any] = field(default_factory=dict)
|
|
73
|
+
concrete_container_names: set[str] = field(default_factory=set)
|
|
74
|
+
container_details_map: dict[str, ContainerDetailsContext] = field(
|
|
75
|
+
default_factory=dict
|
|
76
|
+
)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Contains the core logic for generating parser package files.
|
|
2
|
+
|
|
3
|
+
It includes functions for creating directory layouts, rendering Jinja2
|
|
4
|
+
templates, and formatting the final output.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib.resources as pkg_resources
|
|
8
|
+
import logging
|
|
9
|
+
import shutil
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import jinja2
|
|
14
|
+
|
|
15
|
+
from xtce2py import config, xtce_1_1, xtce_1_2, xtce_1_3
|
|
16
|
+
from xtce2py.context_models import (
|
|
17
|
+
InitContext,
|
|
18
|
+
ModelContext,
|
|
19
|
+
ModelsContext,
|
|
20
|
+
PyprojectContext,
|
|
21
|
+
ReadMeContext,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
log = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def setup_jinja_environment() -> jinja2.Environment:
|
|
28
|
+
"""Set up the Jinja2 environment for template rendering."""
|
|
29
|
+
template_dir_ref = pkg_resources.files("xtce2py").joinpath("templates")
|
|
30
|
+
|
|
31
|
+
with pkg_resources.as_file(template_dir_ref) as template_dir:
|
|
32
|
+
loader = jinja2.FileSystemLoader(template_dir)
|
|
33
|
+
|
|
34
|
+
return jinja2.Environment(loader=loader)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
TEMPLATE_ENV = setup_jinja_environment()
|
|
38
|
+
STATIC_FILE_MAP = [
|
|
39
|
+
("setup.py", "setup.py"),
|
|
40
|
+
("_decoding.py", "src/{package_name}/_decoding.py"),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def create_project_names(
|
|
45
|
+
space_system_name: str, package_name_suffix: str
|
|
46
|
+
) -> tuple[str, str]:
|
|
47
|
+
"""Create a package name based on the space system's name."""
|
|
48
|
+
dist_name = f"{space_system_name.lower().replace(' ', '-').replace('_', '-')}-{package_name_suffix}"
|
|
49
|
+
package_name = f"{space_system_name.lower().replace(' ', '_').replace('-', '_')}_{package_name_suffix}"
|
|
50
|
+
return dist_name, package_name
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_directory_structure(
|
|
54
|
+
package_dir: Path,
|
|
55
|
+
package_name: str,
|
|
56
|
+
parser: xtce_1_1.XtceParser, # | xtce_1_2.XtceParser | xtce_1_3.XtceParser,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Create the necessary directory structure for the generated package."""
|
|
59
|
+
# Delete existing output directory if it exists
|
|
60
|
+
if package_dir.exists():
|
|
61
|
+
for item in package_dir.iterdir():
|
|
62
|
+
if item.is_dir():
|
|
63
|
+
shutil.rmtree(item)
|
|
64
|
+
else:
|
|
65
|
+
item.unlink()
|
|
66
|
+
package_dir.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
(package_dir / "src").mkdir(parents=True, exist_ok=True)
|
|
68
|
+
|
|
69
|
+
# Create main package directory
|
|
70
|
+
main_package_dir = package_dir / "src" / package_name
|
|
71
|
+
main_package_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
|
|
73
|
+
# Create sub-directories for each sub-space system
|
|
74
|
+
hierarchy = parser.get_space_system_hierarchy()
|
|
75
|
+
for module_name in hierarchy["sub_systems"].keys():
|
|
76
|
+
sub_module_dir = main_package_dir / module_name
|
|
77
|
+
sub_module_dir.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
log.info(f"Generated directory structure at {package_dir}")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def copy_static_files(package_dir: Path):
|
|
83
|
+
"""Copy static files to the generated package."""
|
|
84
|
+
for src_filename, dest_template_path in STATIC_FILE_MAP:
|
|
85
|
+
dest_path = package_dir / dest_template_path.format(
|
|
86
|
+
package_name=package_dir.name
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
with pkg_resources.as_file(
|
|
90
|
+
pkg_resources.files("xtce2py").joinpath(f"static/{src_filename}")
|
|
91
|
+
) as src_path:
|
|
92
|
+
shutil.copy2(src_path, dest_path)
|
|
93
|
+
|
|
94
|
+
log.info(f"Copied static files to {package_dir}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def generate_readme(package_dir: Path, readme_context: ReadMeContext):
|
|
98
|
+
"""Generate a README.md file in the output package directory."""
|
|
99
|
+
template = TEMPLATE_ENV.get_template("README.md.j2")
|
|
100
|
+
|
|
101
|
+
generated_code = template.render(context=readme_context)
|
|
102
|
+
|
|
103
|
+
output_filename = package_dir / "README.md"
|
|
104
|
+
with open(output_filename, "w", encoding="utf-8") as f:
|
|
105
|
+
f.write(generated_code)
|
|
106
|
+
|
|
107
|
+
log.info(f"Generated {output_filename.name} at {output_filename}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def generate_pyproject_toml(
|
|
111
|
+
package_dir: Path,
|
|
112
|
+
pyproject_context: PyprojectContext,
|
|
113
|
+
):
|
|
114
|
+
"""Generate a pyproject.toml file in the output package directory."""
|
|
115
|
+
template = TEMPLATE_ENV.get_template("pyproject.toml.j2")
|
|
116
|
+
|
|
117
|
+
generated_code = template.render(context=pyproject_context)
|
|
118
|
+
|
|
119
|
+
output_filename = package_dir / "pyproject.toml"
|
|
120
|
+
with open(output_filename, "w", encoding="utf-8") as f:
|
|
121
|
+
f.write(generated_code)
|
|
122
|
+
|
|
123
|
+
log.info(f"Generated {output_filename.name} at {output_filename}")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def generate_init(
|
|
127
|
+
package_src_dir: Path,
|
|
128
|
+
init_context: InitContext,
|
|
129
|
+
parser: xtce_1_1.XtceParser, # | xtce_1_2.XtceParser | xtce_1_3.XtceParser,
|
|
130
|
+
):
|
|
131
|
+
"""Generate a __init__.py file in the output src directory."""
|
|
132
|
+
template = TEMPLATE_ENV.get_template("__init__.py.j2")
|
|
133
|
+
|
|
134
|
+
model_class_names = [
|
|
135
|
+
container.name for container in parser.generate_model_context()
|
|
136
|
+
]
|
|
137
|
+
public_api_names = ["parse_packet", "parse_packets", "PacketBase"]
|
|
138
|
+
all_public_names = sorted(model_class_names + public_api_names)
|
|
139
|
+
|
|
140
|
+
generated_code = template.render(
|
|
141
|
+
context=init_context,
|
|
142
|
+
model_class_names=model_class_names,
|
|
143
|
+
all_public_names=all_public_names,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
output_filename = package_src_dir / "__init__.py"
|
|
147
|
+
with open(output_filename, "w", encoding="utf-8") as f:
|
|
148
|
+
f.write(generated_code)
|
|
149
|
+
|
|
150
|
+
log.info(f"Generated {output_filename.name} at {output_filename}")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def generate_models(
|
|
154
|
+
package_src_dir: Path,
|
|
155
|
+
parser: xtce_1_1.XtceParser, # | xtce_1_2.XtceParser | xtce_1_3.XtceParser,
|
|
156
|
+
):
|
|
157
|
+
"""Generate models.py files for the main package and sub-systems."""
|
|
158
|
+
template = TEMPLATE_ENV.get_template("models.py.j2")
|
|
159
|
+
|
|
160
|
+
hierarchy = parser.get_space_system_hierarchy()
|
|
161
|
+
|
|
162
|
+
# Generate models for the root space system
|
|
163
|
+
root_system = hierarchy["root"]
|
|
164
|
+
models_context = ModelsContext(
|
|
165
|
+
imports={
|
|
166
|
+
"typing": ["Literal"],
|
|
167
|
+
"pydantic": ["BaseModel", "Field", "field_validator"],
|
|
168
|
+
},
|
|
169
|
+
containers=list(parser.generate_model_context()),
|
|
170
|
+
)
|
|
171
|
+
generated_code = template.render(context=models_context)
|
|
172
|
+
|
|
173
|
+
output_filename = package_src_dir / "models.py"
|
|
174
|
+
with open(output_filename, "w", encoding="utf-8") as f:
|
|
175
|
+
f.write(generated_code)
|
|
176
|
+
|
|
177
|
+
log.info(f"Generated {output_filename.name} at {output_filename}")
|
|
178
|
+
|
|
179
|
+
# Generate models for each sub-system in separate modules
|
|
180
|
+
for module_name, sub_system in hierarchy["sub_systems"].items():
|
|
181
|
+
print(f"Generating models for sub-system: {sub_system.name} -> {module_name}")
|
|
182
|
+
sub_module_dir = package_src_dir / module_name
|
|
183
|
+
# TODO: Generate models.py in sub_module_dir
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def generate_parser(
|
|
187
|
+
package_src_dir: Path,
|
|
188
|
+
parser: xtce_1_1.XtceParser, # | xtce_1_2.XtceParser | xtce_1_3.XtceParser,
|
|
189
|
+
):
|
|
190
|
+
"""Generate a parser.py file in the output src directory."""
|
|
191
|
+
if parser.is_simple_dispatcher:
|
|
192
|
+
log.info("Generating simple dispatcher parser.")
|
|
193
|
+
# template = TEMPLATE_ENV.get_template("simple_parser.py.j2") # TODO
|
|
194
|
+
template = TEMPLATE_ENV.get_template("tree_parser.py.j2")
|
|
195
|
+
else:
|
|
196
|
+
log.info("Generating tree parser.")
|
|
197
|
+
template = TEMPLATE_ENV.get_template("tree_parser.py.j2")
|
|
198
|
+
|
|
199
|
+
parser_context = parser.generate_parser_context()
|
|
200
|
+
|
|
201
|
+
generated_code = template.render(
|
|
202
|
+
context=parser_context, dispatcher_parameter=parser.dispatcher_parameter
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
output_filename = package_src_dir / "parser.py"
|
|
206
|
+
with open(output_filename, "w", encoding="utf-8") as f:
|
|
207
|
+
f.write(generated_code)
|
|
208
|
+
|
|
209
|
+
log.info(f"Generated {output_filename.name} at {output_filename}")
|