zs-yaml 0.3.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.
- zs_yaml-0.3.0/LICENSE +29 -0
- zs_yaml-0.3.0/MANIFEST.in +26 -0
- zs_yaml-0.3.0/PKG-INFO +174 -0
- zs_yaml-0.3.0/README.md +158 -0
- zs_yaml-0.3.0/VERSION +1 -0
- zs_yaml-0.3.0/doc/zs-yaml.png +0 -0
- zs_yaml-0.3.0/doc/zs-yaml.svg +90 -0
- zs_yaml-0.3.0/requirements.txt +2 -0
- zs_yaml-0.3.0/setup.cfg +4 -0
- zs_yaml-0.3.0/setup.py +78 -0
- zs_yaml-0.3.0/zs_yaml/__init__.py +6 -0
- zs_yaml-0.3.0/zs_yaml/_version.py +2 -0
- zs_yaml-0.3.0/zs_yaml/built_in_transformations.py +75 -0
- zs_yaml-0.3.0/zs_yaml/convert.py +164 -0
- zs_yaml-0.3.0/zs_yaml/main.py +119 -0
- zs_yaml-0.3.0/zs_yaml/yaml_transformer.py +112 -0
- zs_yaml-0.3.0/zs_yaml.egg-info/PKG-INFO +174 -0
- zs_yaml-0.3.0/zs_yaml.egg-info/SOURCES.txt +20 -0
- zs_yaml-0.3.0/zs_yaml.egg-info/dependency_links.txt +1 -0
- zs_yaml-0.3.0/zs_yaml.egg-info/entry_points.txt +2 -0
- zs_yaml-0.3.0/zs_yaml.egg-info/requires.txt +2 -0
- zs_yaml-0.3.0/zs_yaml.egg-info/top_level.txt +1 -0
zs_yaml-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, NDS e.V. Association
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Explicitly include specific files from the root directory
|
|
2
|
+
include README.md
|
|
3
|
+
include requirements.txt
|
|
4
|
+
include LICENSE
|
|
5
|
+
|
|
6
|
+
# Include all Python files in the package directory and subdirectories
|
|
7
|
+
recursive-include zs_yaml *.py
|
|
8
|
+
|
|
9
|
+
# Include all documentation resources
|
|
10
|
+
recursive-include docs *
|
|
11
|
+
|
|
12
|
+
# Exclude cache directories
|
|
13
|
+
prune **/__pycache__
|
|
14
|
+
prune **/.cache
|
|
15
|
+
|
|
16
|
+
# Exclude version control directories
|
|
17
|
+
prune .git
|
|
18
|
+
|
|
19
|
+
# Exclude test directories
|
|
20
|
+
prune **/tests
|
|
21
|
+
|
|
22
|
+
# Exclude development and IDE-specific files
|
|
23
|
+
global-exclude .DS_Store
|
|
24
|
+
global-exclude *.swp
|
|
25
|
+
global-exclude .vscode
|
|
26
|
+
global-exclude .idea
|
zs_yaml-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: zs-yaml
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A package that allows using yaml to define zserio objects.
|
|
5
|
+
Home-page: https://github.com/ndsev/zs_yaml
|
|
6
|
+
Author: Navigation Data Standard e.V.
|
|
7
|
+
Author-email: support@nds-association.org
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: PyYAML~=6.0
|
|
15
|
+
Requires-Dist: zserio~=2.12
|
|
16
|
+
|
|
17
|
+
# `zs-yaml`
|
|
18
|
+
|
|
19
|
+
*Your Easy Path to zserio Test Data*
|
|
20
|
+
|
|
21
|
+
<img src="doc/zs-yaml.png" alt="" height="100">
|
|
22
|
+
|
|
23
|
+
**zs-yaml** adds YAML as a text format to be used with zserio in order to improve the user experience when manually creating or analyzing test data for zserio.
|
|
24
|
+
|
|
25
|
+
- **YAML Format**: Uses YAML's human-readable format instead of JSON as the primary format for editing, making data handling and transformation more intuitive and less cluttered.
|
|
26
|
+
- **Metadata Inclusion**: Automatically includes metadata in the YAML, eliminating the need for users to manually identify the correct type when importing JSON data, ensuring seamless (de-)serialization.
|
|
27
|
+
- **Custom Transformations**: Allows for hooking in custom transformations so that users can work with familiar formats (e.g., dates or coordinate representations) instead of thinking in unfamiliar formats.
|
|
28
|
+
|
|
29
|
+
## Example
|
|
30
|
+
|
|
31
|
+
### Zserio Schema Module Creation
|
|
32
|
+
|
|
33
|
+
1. **Create the Zserio schema**:
|
|
34
|
+
- Create a file named `person.zs` with the following content:
|
|
35
|
+
|
|
36
|
+
```zserio
|
|
37
|
+
package person;
|
|
38
|
+
|
|
39
|
+
struct Person
|
|
40
|
+
{
|
|
41
|
+
string name;
|
|
42
|
+
string birthdate;
|
|
43
|
+
string birth_location;
|
|
44
|
+
string current_job;
|
|
45
|
+
int32 income;
|
|
46
|
+
RoleExperience experience[];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
struct RoleExperience
|
|
50
|
+
{
|
|
51
|
+
string role;
|
|
52
|
+
int32 years;
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. **Compile the Zserio schema and generate Python APIs**:
|
|
57
|
+
- Run the following command to compile the schema and generate Python sources:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# Generated sources needs type infos so that 'json <-> bin'
|
|
61
|
+
# conversion works, which is utilized by zs-yaml
|
|
62
|
+
zserio person.zs -withTypeInfoCode -python zs_gen_api
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
3. **Ensure that the schema modules are available in the Python environment**:
|
|
66
|
+
- Export the `PYTHONPATH` to include the directory containing the generated Python sources:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
export PYTHONPATH="zs_gen_api"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Use zs-yaml to create data
|
|
73
|
+
|
|
74
|
+
Using **zs-yaml**, you can define the same data in a more human-readable YAML format and include necessary metadata along with a custom transformation for the birthdate:
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
# 1) Metadata is used to specify the type needed for
|
|
78
|
+
# (de-)serialization and custom transform functions
|
|
79
|
+
# 2) User are free to use their preferred date format
|
|
80
|
+
# for the birth data as the a normalization function
|
|
81
|
+
# get invoked.
|
|
82
|
+
# 3) Yaml allows avoiding clutter and adding comments
|
|
83
|
+
# like this one :)
|
|
84
|
+
|
|
85
|
+
_meta:
|
|
86
|
+
schema_module: person.api
|
|
87
|
+
schema_type: Person
|
|
88
|
+
transformation_module: "./transformations.py"
|
|
89
|
+
|
|
90
|
+
name: John Doe
|
|
91
|
+
|
|
92
|
+
birthdate:
|
|
93
|
+
_f: normalize_date
|
|
94
|
+
_a: "01/01/1990"
|
|
95
|
+
birth_location: Springfield, USA
|
|
96
|
+
|
|
97
|
+
current_job: Software Engineer
|
|
98
|
+
income: 75000
|
|
99
|
+
|
|
100
|
+
experience:
|
|
101
|
+
- role: Intern
|
|
102
|
+
years: 1
|
|
103
|
+
- role: Junior Developer
|
|
104
|
+
years: 2
|
|
105
|
+
- role: Senior Developer
|
|
106
|
+
years: 3
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Example Transformation Module (transformations.py)
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
def normalize_date(date_str):
|
|
113
|
+
from datetime import datetime
|
|
114
|
+
for fmt in ("%Y-%m-%d", "%d/%m/%Y", "%m/%d/%Y"):
|
|
115
|
+
try:
|
|
116
|
+
return datetime.strptime(date_str, fmt).strftime("%Y-%m-%d")
|
|
117
|
+
except ValueError:
|
|
118
|
+
pass
|
|
119
|
+
raise ValueError(f"Date {date_str} is not in a recognized format")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Creating the Binary Representation
|
|
123
|
+
|
|
124
|
+
After you have installed `zs-yaml`, call `zs-yaml` to convert your YAML file to a binary representation:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
# Install zs-yaml if not already installed.
|
|
128
|
+
|
|
129
|
+
# Create the binary representation from the YAML file
|
|
130
|
+
zs-yaml person.yaml person.bin
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Installation
|
|
134
|
+
|
|
135
|
+
Currently, the Python package is only available at test.pypi. Once the official release is available,
|
|
136
|
+
you can install the package without specifying an index URL.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
python -m pip install --upgrade zs-yaml
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Usage
|
|
143
|
+
|
|
144
|
+
The main entry point for the application is `main.py`. It accepts arguments for specifying the input and output file paths. You can run the application as follows:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
zs-yaml input.yaml output.bin
|
|
148
|
+
zs-yaml input.bin output.yaml
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Notes
|
|
152
|
+
|
|
153
|
+
- You have to use the exact same order of field in the yaml as defined by the zserio schema, because zserio expects this
|
|
154
|
+
- When converting from binary to YAML, the target YAML file must already exist and contain the necessary metadata.
|
|
155
|
+
- The minimal metadata content in the target YAML file should be:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
_meta:
|
|
159
|
+
schema_module: <module_name>
|
|
160
|
+
schema_type: <type_name>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Future Considerations
|
|
164
|
+
|
|
165
|
+
In the future, we may consider using YAML tags for a more integrated approach to handling transformations directly within the YAML files, simplifying syntax and enhancing readability.
|
|
166
|
+
|
|
167
|
+
## Project Structure
|
|
168
|
+
|
|
169
|
+
- `setup.py`: Script for setting up the project and installing dependencies.
|
|
170
|
+
- `zs_yaml/`: Directory containing the actual `zs-yaml` implementation.
|
|
171
|
+
|
|
172
|
+
## Reference
|
|
173
|
+
|
|
174
|
+
This project references the zserio serialization framework. For more information about zserio, visit the [zserio GitHub project](https://github.com/ndsev/zserio).
|
zs_yaml-0.3.0/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# `zs-yaml`
|
|
2
|
+
|
|
3
|
+
*Your Easy Path to zserio Test Data*
|
|
4
|
+
|
|
5
|
+
<img src="doc/zs-yaml.png" alt="" height="100">
|
|
6
|
+
|
|
7
|
+
**zs-yaml** adds YAML as a text format to be used with zserio in order to improve the user experience when manually creating or analyzing test data for zserio.
|
|
8
|
+
|
|
9
|
+
- **YAML Format**: Uses YAML's human-readable format instead of JSON as the primary format for editing, making data handling and transformation more intuitive and less cluttered.
|
|
10
|
+
- **Metadata Inclusion**: Automatically includes metadata in the YAML, eliminating the need for users to manually identify the correct type when importing JSON data, ensuring seamless (de-)serialization.
|
|
11
|
+
- **Custom Transformations**: Allows for hooking in custom transformations so that users can work with familiar formats (e.g., dates or coordinate representations) instead of thinking in unfamiliar formats.
|
|
12
|
+
|
|
13
|
+
## Example
|
|
14
|
+
|
|
15
|
+
### Zserio Schema Module Creation
|
|
16
|
+
|
|
17
|
+
1. **Create the Zserio schema**:
|
|
18
|
+
- Create a file named `person.zs` with the following content:
|
|
19
|
+
|
|
20
|
+
```zserio
|
|
21
|
+
package person;
|
|
22
|
+
|
|
23
|
+
struct Person
|
|
24
|
+
{
|
|
25
|
+
string name;
|
|
26
|
+
string birthdate;
|
|
27
|
+
string birth_location;
|
|
28
|
+
string current_job;
|
|
29
|
+
int32 income;
|
|
30
|
+
RoleExperience experience[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
struct RoleExperience
|
|
34
|
+
{
|
|
35
|
+
string role;
|
|
36
|
+
int32 years;
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
2. **Compile the Zserio schema and generate Python APIs**:
|
|
41
|
+
- Run the following command to compile the schema and generate Python sources:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
# Generated sources needs type infos so that 'json <-> bin'
|
|
45
|
+
# conversion works, which is utilized by zs-yaml
|
|
46
|
+
zserio person.zs -withTypeInfoCode -python zs_gen_api
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3. **Ensure that the schema modules are available in the Python environment**:
|
|
50
|
+
- Export the `PYTHONPATH` to include the directory containing the generated Python sources:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
export PYTHONPATH="zs_gen_api"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Use zs-yaml to create data
|
|
57
|
+
|
|
58
|
+
Using **zs-yaml**, you can define the same data in a more human-readable YAML format and include necessary metadata along with a custom transformation for the birthdate:
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
# 1) Metadata is used to specify the type needed for
|
|
62
|
+
# (de-)serialization and custom transform functions
|
|
63
|
+
# 2) User are free to use their preferred date format
|
|
64
|
+
# for the birth data as the a normalization function
|
|
65
|
+
# get invoked.
|
|
66
|
+
# 3) Yaml allows avoiding clutter and adding comments
|
|
67
|
+
# like this one :)
|
|
68
|
+
|
|
69
|
+
_meta:
|
|
70
|
+
schema_module: person.api
|
|
71
|
+
schema_type: Person
|
|
72
|
+
transformation_module: "./transformations.py"
|
|
73
|
+
|
|
74
|
+
name: John Doe
|
|
75
|
+
|
|
76
|
+
birthdate:
|
|
77
|
+
_f: normalize_date
|
|
78
|
+
_a: "01/01/1990"
|
|
79
|
+
birth_location: Springfield, USA
|
|
80
|
+
|
|
81
|
+
current_job: Software Engineer
|
|
82
|
+
income: 75000
|
|
83
|
+
|
|
84
|
+
experience:
|
|
85
|
+
- role: Intern
|
|
86
|
+
years: 1
|
|
87
|
+
- role: Junior Developer
|
|
88
|
+
years: 2
|
|
89
|
+
- role: Senior Developer
|
|
90
|
+
years: 3
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Example Transformation Module (transformations.py)
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
def normalize_date(date_str):
|
|
97
|
+
from datetime import datetime
|
|
98
|
+
for fmt in ("%Y-%m-%d", "%d/%m/%Y", "%m/%d/%Y"):
|
|
99
|
+
try:
|
|
100
|
+
return datetime.strptime(date_str, fmt).strftime("%Y-%m-%d")
|
|
101
|
+
except ValueError:
|
|
102
|
+
pass
|
|
103
|
+
raise ValueError(f"Date {date_str} is not in a recognized format")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Creating the Binary Representation
|
|
107
|
+
|
|
108
|
+
After you have installed `zs-yaml`, call `zs-yaml` to convert your YAML file to a binary representation:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
# Install zs-yaml if not already installed.
|
|
112
|
+
|
|
113
|
+
# Create the binary representation from the YAML file
|
|
114
|
+
zs-yaml person.yaml person.bin
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Installation
|
|
118
|
+
|
|
119
|
+
Currently, the Python package is only available at test.pypi. Once the official release is available,
|
|
120
|
+
you can install the package without specifying an index URL.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
python -m pip install --upgrade zs-yaml
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Usage
|
|
127
|
+
|
|
128
|
+
The main entry point for the application is `main.py`. It accepts arguments for specifying the input and output file paths. You can run the application as follows:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
zs-yaml input.yaml output.bin
|
|
132
|
+
zs-yaml input.bin output.yaml
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Notes
|
|
136
|
+
|
|
137
|
+
- You have to use the exact same order of field in the yaml as defined by the zserio schema, because zserio expects this
|
|
138
|
+
- When converting from binary to YAML, the target YAML file must already exist and contain the necessary metadata.
|
|
139
|
+
- The minimal metadata content in the target YAML file should be:
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
_meta:
|
|
143
|
+
schema_module: <module_name>
|
|
144
|
+
schema_type: <type_name>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Future Considerations
|
|
148
|
+
|
|
149
|
+
In the future, we may consider using YAML tags for a more integrated approach to handling transformations directly within the YAML files, simplifying syntax and enhancing readability.
|
|
150
|
+
|
|
151
|
+
## Project Structure
|
|
152
|
+
|
|
153
|
+
- `setup.py`: Script for setting up the project and installing dependencies.
|
|
154
|
+
- `zs_yaml/`: Directory containing the actual `zs-yaml` implementation.
|
|
155
|
+
|
|
156
|
+
## Reference
|
|
157
|
+
|
|
158
|
+
This project references the zserio serialization framework. For more information about zserio, visit the [zserio GitHub project](https://github.com/ndsev/zserio).
|
zs_yaml-0.3.0/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
Binary file
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
viewBox="0 0 76.940711 22.655753"
|
|
4
|
+
id="zserio-logo"
|
|
5
|
+
version="1.1"
|
|
6
|
+
sodipodi:docname="zs-yaml.svg"
|
|
7
|
+
width="76.940712"
|
|
8
|
+
height="22.655754"
|
|
9
|
+
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
10
|
+
xml:space="preserve"
|
|
11
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
12
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
16
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
|
17
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
|
|
18
|
+
id="metadata21"><rdf:RDF><cc:Work
|
|
19
|
+
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
20
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
|
21
|
+
id="defs19" /><sodipodi:namedview
|
|
22
|
+
pagecolor="#ffffff"
|
|
23
|
+
bordercolor="#666666"
|
|
24
|
+
borderopacity="1"
|
|
25
|
+
objecttolerance="10"
|
|
26
|
+
gridtolerance="10"
|
|
27
|
+
guidetolerance="10"
|
|
28
|
+
inkscape:pageopacity="0"
|
|
29
|
+
inkscape:pageshadow="2"
|
|
30
|
+
inkscape:window-width="1920"
|
|
31
|
+
inkscape:window-height="935"
|
|
32
|
+
id="namedview17"
|
|
33
|
+
showgrid="false"
|
|
34
|
+
inkscape:zoom="5.6462916"
|
|
35
|
+
inkscape:cx="29.134167"
|
|
36
|
+
inkscape:cy="0.79698329"
|
|
37
|
+
inkscape:window-x="0"
|
|
38
|
+
inkscape:window-y="32"
|
|
39
|
+
inkscape:window-maximized="1"
|
|
40
|
+
inkscape:showpageshadow="2"
|
|
41
|
+
inkscape:pagecheckerboard="0"
|
|
42
|
+
inkscape:deskcolor="#d1d1d1"
|
|
43
|
+
inkscape:current-layer="zserio-logo" /><rect
|
|
44
|
+
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.444658"
|
|
45
|
+
id="rect3"
|
|
46
|
+
width="76.940712"
|
|
47
|
+
height="22.655754"
|
|
48
|
+
x="0"
|
|
49
|
+
y="0" /><g
|
|
50
|
+
id="zserio-logo-serio"
|
|
51
|
+
fill="currentColor"
|
|
52
|
+
transform="matrix(0.96869663,0,0,0.96869663,5.4172856,2.76989)"><path
|
|
53
|
+
d="m 21.733415,11.886839 q 0,1.508249 -1.251904,2.474278 -1.242093,0.966048 -3.400868,0.966048 -1.222375,0 -2.24756,-0.285874 -1.015376,-0.295725 -1.705339,-0.640746 v -2.079999 h 0.09857 q 0.877324,0.660455 1.951776,1.054813 1.074434,0.38445 2.06028,0.38445 1.222375,0 1.912339,-0.394309 0.689963,-0.394309 0.690033,-1.242094 0,-0.650605 -0.37459,-0.985767 -0.37459,-0.335162 -1.439184,-0.571739 -0.394309,-0.08872 -1.035094,-0.207009 -0.630886,-0.118294 -1.153309,-0.256297 -1.449092,-0.384449 -2.06028,-1.1237795 -0.601318,-0.74918 -0.601318,-1.8335618 0,-0.6801737 0.276016,-1.2815312 0.285874,-0.6013179 0.857614,-1.0744331 0.552031,-0.4633054 1.399746,-0.7294612 0.857614,-0.2760152 1.912338,-0.2760152 0.985757,0 1.991214,0.2464369 1.015375,0.2365873 1.68562,0.5815989 V 6.593252 h -0.09857 Q 20.491193,6.0707998 19.475886,5.7159287 18.460511,5.3511981 17.484672,5.3511981 q -1.015375,0 -1.715248,0.394309 -0.699893,0.3844494 -0.699893,1.1533088 0,0.6801737 0.423878,1.0251853 0.414018,0.3450117 1.340589,0.5618801 0.512592,0.1182937 1.143499,0.2365873 0.640745,0.1182937 1.064623,0.2168685 1.291341,0.2957242 1.991214,1.0153751 0.699893,0.7294608 0.699893,1.9320578 z"
|
|
54
|
+
inkscape:connector-curvature="0"
|
|
55
|
+
id="path5" /></g><path
|
|
56
|
+
id="zserio-logo-z"
|
|
57
|
+
d="M 16.939948,17.513033 H 6.5404496 V 15.235944 L 12.376159,9.0354854 H 6.7770766 V 6.442894 H 16.831742 v 2.2376514 l -5.747023,6.1905486 h 5.855427 z"
|
|
58
|
+
inkscape:connector-curvature="0"
|
|
59
|
+
style="fill:#ff0000" /><g
|
|
60
|
+
id="g2"
|
|
61
|
+
transform="matrix(0.08129594,0,0,0.08129594,82.698086,11.100326)"><g
|
|
62
|
+
id="g1"
|
|
63
|
+
transform="matrix(1.3333333,0,0,1.3333333,-558.97901,-69.293599)"><polygon
|
|
64
|
+
points="1,109 73,1 258,1 258,19 318,109 "
|
|
65
|
+
style="fill:#ffffff;stroke:#000000;stroke-width:1"
|
|
66
|
+
id="polygon1" /><polygon
|
|
67
|
+
points="1,109 37,55 1,1 25,1 49,37 73,1 97,1 25,109 "
|
|
68
|
+
style="fill:#000000;stroke:#000000;stroke-width:1"
|
|
69
|
+
id="polygon2" /><polygon
|
|
70
|
+
points="127,1 103,1 31,109 55,109 103,37 103,109 127,109 "
|
|
71
|
+
style="fill:#000000;stroke:#000000;stroke-width:1"
|
|
72
|
+
id="polygon3" /><polygon
|
|
73
|
+
points="228,1 198,1 180,37 156,1 132,1 132,109 156,109 156,37 180,73 204,37 204,109 228,109 "
|
|
74
|
+
style="fill:#000000;stroke:#000000;stroke-width:1"
|
|
75
|
+
id="polygon4" /><polygon
|
|
76
|
+
points="318,109 302,85 258,85 258,1 234,1 234,109 "
|
|
77
|
+
style="fill:#000000;stroke:#000000;stroke-width:1"
|
|
78
|
+
id="polygon5" /></g></g><rect
|
|
79
|
+
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.784084"
|
|
80
|
+
id="rect2"
|
|
81
|
+
width="6.5856104"
|
|
82
|
+
height="1.8180964"
|
|
83
|
+
x="29.616592"
|
|
84
|
+
y="10.931295" /><rect
|
|
85
|
+
style="fill:none;fill-opacity:1;stroke:#311620;stroke-width:0.721635;stroke-opacity:0.642554"
|
|
86
|
+
id="rect4"
|
|
87
|
+
width="73.180183"
|
|
88
|
+
height="19.131836"
|
|
89
|
+
x="2.0607479"
|
|
90
|
+
y="1.8520763" /></svg>
|
zs_yaml-0.3.0/setup.cfg
ADDED
zs_yaml-0.3.0/setup.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from setuptools import setup, find_packages
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Utility function to read file content into a string
|
|
7
|
+
def read(fname):
|
|
8
|
+
return open(os.path.join(os.path.dirname(__file__), fname),
|
|
9
|
+
'r', encoding='utf-8').read()
|
|
10
|
+
|
|
11
|
+
# DOCUMENTATION
|
|
12
|
+
def read_for_pypi(fname):
|
|
13
|
+
"""Read a file and replace relative image URLs with absolute URLs."""
|
|
14
|
+
content = read(fname)
|
|
15
|
+
# Replace relative image URLs with absolute URLs
|
|
16
|
+
# Adjust the base URL to where your images are actually hosted
|
|
17
|
+
base_url = "https://raw.githubusercontent.com/ndsev/zs_yaml/main/"
|
|
18
|
+
content = content.replace("](images/", f"]({base_url}images/")
|
|
19
|
+
return content
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# REQUIREMENTS
|
|
23
|
+
def read_requirements(fname):
|
|
24
|
+
return [line.strip() for line in read(fname).splitlines() if line.strip() and not line.startswith('#')]
|
|
25
|
+
|
|
26
|
+
# VERSION INFO
|
|
27
|
+
def get_version_info():
|
|
28
|
+
version_file = os.path.join(os.path.dirname(__file__), 'zs_yaml', '_version.py')
|
|
29
|
+
version_info = {}
|
|
30
|
+
with open(version_file, 'r', encoding='utf-8') as f:
|
|
31
|
+
exec(f.read(), version_info)
|
|
32
|
+
|
|
33
|
+
# Get the Git commit ID if possible
|
|
34
|
+
try:
|
|
35
|
+
commit_id = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('ascii').strip()
|
|
36
|
+
version_info['__commit_id__'] = commit_id
|
|
37
|
+
except:
|
|
38
|
+
pass # If Git is not available or not a Git repository, use the default "unknown"
|
|
39
|
+
|
|
40
|
+
return version_info
|
|
41
|
+
|
|
42
|
+
version_info = get_version_info()
|
|
43
|
+
|
|
44
|
+
# Write the updated version info back to the file
|
|
45
|
+
with open(os.path.join('zs_yaml', '_version.py'), 'w', encoding='utf-8') as f:
|
|
46
|
+
f.write(f"__version__ = '{version_info['__version__']}'\n")
|
|
47
|
+
f.write(f"__commit_id__ = '{version_info['__commit_id__']}'\n")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
setup(
|
|
51
|
+
name='zs-yaml',
|
|
52
|
+
version=version_info['__version__'],
|
|
53
|
+
author="Navigation Data Standard e.V.",
|
|
54
|
+
author_email="support@nds-association.org",
|
|
55
|
+
description='A package that allows using yaml to define zserio objects.',
|
|
56
|
+
long_description=read_for_pypi('README.md'),
|
|
57
|
+
long_description_content_type="text/markdown",
|
|
58
|
+
url="https://github.com/ndsev/zs_yaml",
|
|
59
|
+
packages=find_packages(),
|
|
60
|
+
install_requires=read_requirements('requirements.txt'),
|
|
61
|
+
classifiers=[
|
|
62
|
+
"Programming Language :: Python :: 3",
|
|
63
|
+
"License :: OSI Approved :: MIT License",
|
|
64
|
+
"Operating System :: OS Independent",
|
|
65
|
+
],
|
|
66
|
+
python_requires='>=3.8',
|
|
67
|
+
entry_points={
|
|
68
|
+
'console_scripts': [
|
|
69
|
+
'zs-yaml=zs_yaml.main:main'
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
include_package_data=True,
|
|
73
|
+
package_data={
|
|
74
|
+
'zs_yaml': ['images/*'],
|
|
75
|
+
},
|
|
76
|
+
ext_modules=[],
|
|
77
|
+
cmdclass={}
|
|
78
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import importlib
|
|
3
|
+
import zserio
|
|
4
|
+
import yaml
|
|
5
|
+
import copy
|
|
6
|
+
|
|
7
|
+
def insert_yaml_as_extern(transformer, file, template_args=None):
|
|
8
|
+
"""
|
|
9
|
+
Include external YAML by transforming it to JSON and using zserio.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
transformer (YamlTransformer): The transformer instance.
|
|
13
|
+
file (str): Path to the external YAML file.
|
|
14
|
+
template_args (dict, optional): A dictionary of template arguments for placeholder replacement.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
dict: A dictionary containing the binary data and its bit size.
|
|
18
|
+
"""
|
|
19
|
+
abs_path = transformer.resolve_path(file)
|
|
20
|
+
external_transformer = transformer.__class__(abs_path, template_args, initial_transformations=transformer.transformations)
|
|
21
|
+
processed_data = external_transformer.transform()
|
|
22
|
+
meta = external_transformer.get_meta()
|
|
23
|
+
|
|
24
|
+
schema_module = meta.get('schema_module')
|
|
25
|
+
schema_type = meta.get('schema_type')
|
|
26
|
+
|
|
27
|
+
if not schema_module or not schema_type:
|
|
28
|
+
raise ValueError(f"Error: schema_module and schema_type must be specified in the _meta section of the YAML file: {file}")
|
|
29
|
+
|
|
30
|
+
json_string = json.dumps(processed_data)
|
|
31
|
+
|
|
32
|
+
# Convert JSON to binary using zserio
|
|
33
|
+
module = importlib.import_module(schema_module)
|
|
34
|
+
ImportedType = getattr(module, schema_type)
|
|
35
|
+
zserio_object = zserio.from_json_string(ImportedType, json_string)
|
|
36
|
+
writer = zserio.BitStreamWriter()
|
|
37
|
+
zserio_object.write(writer)
|
|
38
|
+
bits = zserio.BitBuffer(writer.byte_array, writer.bitposition)
|
|
39
|
+
|
|
40
|
+
# Encode the binary data
|
|
41
|
+
encoded_bytes = list(bits.buffer)
|
|
42
|
+
data = {"buffer": encoded_bytes, "bitSize": bits.bitsize}
|
|
43
|
+
|
|
44
|
+
return data
|
|
45
|
+
|
|
46
|
+
def insert_yaml(transformer, file, template_args=None):
|
|
47
|
+
"""
|
|
48
|
+
Insert YAML content directly without converting to binary.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
transformer (YamlTransformer): The transformer instance.
|
|
52
|
+
file (str): Path to the external YAML file.
|
|
53
|
+
template_args (dict, optional): A dictionary of template arguments for placeholder replacement.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
dict: The processed YAML data.
|
|
57
|
+
"""
|
|
58
|
+
abs_path = transformer.resolve_path(file)
|
|
59
|
+
external_transformer = transformer.__class__(abs_path, template_args, initial_transformations=transformer.transformations)
|
|
60
|
+
processed_data = external_transformer.transform()
|
|
61
|
+
return processed_data
|
|
62
|
+
|
|
63
|
+
def repeat_node(transformer, node, count):
|
|
64
|
+
"""
|
|
65
|
+
Repeat a specific node n times and return as a list.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
transformer (YamlTransformer): The transformer instance.
|
|
69
|
+
node: The node to be repeated.
|
|
70
|
+
count (int): The number of times to repeat the node.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
list: A list containing the repeated node.
|
|
74
|
+
"""
|
|
75
|
+
return [copy.deepcopy(node) for _ in range(count)]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the logic to cover the following conversions:
|
|
3
|
+
- yaml to bin: Converts a YAML file to a binary file using zserio serialization.
|
|
4
|
+
Custom transformations can be applied to the YAML data before conversion.
|
|
5
|
+
- bin to yaml: Converts a binary file to a YAML file using zserio deserialization.
|
|
6
|
+
The conversion requires the schema module and type information to be
|
|
7
|
+
specified in the _meta section of the YAML file.
|
|
8
|
+
- json to yaml: Converts a JSON file to a YAML file. This is a plain conversion without
|
|
9
|
+
applying any transformations.
|
|
10
|
+
- yaml to json: Converts a YAML file to a JSON file. Custom transformations can be applied
|
|
11
|
+
to the YAML data before conversion.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
import json
|
|
16
|
+
import importlib
|
|
17
|
+
import zserio
|
|
18
|
+
import tempfile
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
from .yaml_transformer import YamlTransformer
|
|
22
|
+
|
|
23
|
+
def _json_to_zs_bin(json_input_path, bin_output_path, module_name, type_name):
|
|
24
|
+
"""
|
|
25
|
+
Converts a JSON file to a binary file using Zserio serialization.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
json_input_path (str): Path to the input JSON file.
|
|
29
|
+
bin_output_path (str): Path to the output binary file.
|
|
30
|
+
module_name (str): Name of the Zserio module containing the schema.
|
|
31
|
+
type_name (str): Name of the Zserio type to be used for serialization.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If the specified Zserio type is not found in the module.
|
|
35
|
+
"""
|
|
36
|
+
module = importlib.import_module(module_name)
|
|
37
|
+
ImportedType = getattr(module, type_name)
|
|
38
|
+
if ImportedType is None:
|
|
39
|
+
raise ValueError(f"Type {type_name} not found in module {module_name}")
|
|
40
|
+
zserio_object = zserio.from_json_file(ImportedType, json_input_path)
|
|
41
|
+
zserio.serialize_to_file(zserio_object, bin_output_path)
|
|
42
|
+
|
|
43
|
+
def yaml_to_yaml(yaml_input_path, yaml_output_path=None):
|
|
44
|
+
"""
|
|
45
|
+
Applies all transformations and template substitution to
|
|
46
|
+
the input YAML to come up with the output YAML.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
yaml_input_path (str): Path to the input YAML file.
|
|
50
|
+
yaml_output_path (str): Path to the output YAML file.
|
|
51
|
+
"""
|
|
52
|
+
transformer = YamlTransformer(yaml_input_path)
|
|
53
|
+
transformed_data = transformer.transform()
|
|
54
|
+
meta = transformer.get_meta()
|
|
55
|
+
|
|
56
|
+
if yaml_output_path:
|
|
57
|
+
output_data = {'_meta': meta}
|
|
58
|
+
output_data.update(transformed_data)
|
|
59
|
+
with open(yaml_output_path, 'w') as yaml_file:
|
|
60
|
+
yaml.dump(output_data, yaml_file, default_flow_style=False, sort_keys=False)
|
|
61
|
+
|
|
62
|
+
return transformed_data, meta
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def yaml_to_json(yaml_input_path, json_output_path):
|
|
66
|
+
"""
|
|
67
|
+
Converts a YAML file to a JSON file.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
yaml_input_path (str): Path to the input YAML file.
|
|
71
|
+
json_output_path (str): Path to the output JSON file.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
dict: The _meta section from the YAML file, if present.
|
|
75
|
+
"""
|
|
76
|
+
transformed_data, meta = yaml_to_yaml(yaml_input_path)
|
|
77
|
+
|
|
78
|
+
with open(json_output_path, 'w') as json_file:
|
|
79
|
+
json.dump(transformed_data, json_file, indent=2)
|
|
80
|
+
|
|
81
|
+
return meta
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def json_to_yaml(json_input_path, yaml_output_path):
|
|
85
|
+
"""
|
|
86
|
+
Converts a JSON file to a YAML file. This is a plain conversion without applying
|
|
87
|
+
any transformations.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
json_input_path (str): Path to the input JSON file.
|
|
91
|
+
yaml_output_path (str): Path to the output YAML file.
|
|
92
|
+
"""
|
|
93
|
+
with open(json_input_path, 'r') as json_file:
|
|
94
|
+
data = json.load(json_file)
|
|
95
|
+
with open(yaml_output_path, 'w') as yaml_file:
|
|
96
|
+
yaml.safe_dump(data, yaml_file, default_flow_style=False, sort_keys=False)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def yaml_to_bin(yaml_input_path, bin_output_path):
|
|
100
|
+
"""
|
|
101
|
+
Converts a YAML file to a binary file using Zserio serialization.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
yaml_input_path (str): Path to the input YAML file.
|
|
105
|
+
bin_output_path (str): Path to the output binary file.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
ValueError: If schema_module and schema_type are not specified in the _meta
|
|
109
|
+
section of the YAML file.
|
|
110
|
+
"""
|
|
111
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as temp_json_file:
|
|
112
|
+
temp_json_path = temp_json_file.name
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
meta = yaml_to_json(yaml_input_path, temp_json_path)
|
|
116
|
+
|
|
117
|
+
schema_module = meta.get('schema_module')
|
|
118
|
+
schema_type = meta.get('schema_type')
|
|
119
|
+
|
|
120
|
+
if not schema_module or not schema_type:
|
|
121
|
+
raise ValueError("Error: schema_module and schema_type must be specified in the _meta section for binary output")
|
|
122
|
+
|
|
123
|
+
_json_to_zs_bin(temp_json_path, bin_output_path, schema_module, schema_type)
|
|
124
|
+
finally:
|
|
125
|
+
os.remove(temp_json_path)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def bin_to_yaml(bin_input_path, yaml_output_path):
|
|
129
|
+
"""
|
|
130
|
+
Converts a binary file to a YAML file using Zserio deserialization.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
bin_input_path (str): Path to the input binary file.
|
|
134
|
+
yaml_output_path (str): Path to the output YAML file.
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
ValueError: If schema_module and schema_type are not specified in the _meta
|
|
138
|
+
section of the YAML file, or if the specified Zserio type is not found
|
|
139
|
+
in the module.
|
|
140
|
+
"""
|
|
141
|
+
with open(yaml_output_path, 'r') as yaml_file:
|
|
142
|
+
meta = yaml.safe_load(yaml_file)
|
|
143
|
+
|
|
144
|
+
schema_module = meta.get('_meta', {}).get('schema_module')
|
|
145
|
+
schema_type = meta.get('_meta', {}).get('schema_type')
|
|
146
|
+
if not schema_module or not schema_type:
|
|
147
|
+
raise ValueError("Error: schema_module and schema_type must be specified in the _meta section of the YAML file")
|
|
148
|
+
|
|
149
|
+
module = importlib.import_module(schema_module)
|
|
150
|
+
ImportedType = getattr(module, schema_type)
|
|
151
|
+
if ImportedType is None:
|
|
152
|
+
raise ValueError(f"Type {schema_type} not found in module {schema_module}")
|
|
153
|
+
|
|
154
|
+
zserio_object = zserio.deserialize_from_file(ImportedType, bin_input_path)
|
|
155
|
+
json_data = zserio.to_json_string(zserio_object)
|
|
156
|
+
|
|
157
|
+
data = json.loads(json_data)
|
|
158
|
+
|
|
159
|
+
# Create a new dictionary to ensure _meta comes first
|
|
160
|
+
final_data = {'_meta': meta['_meta']}
|
|
161
|
+
final_data.update(data)
|
|
162
|
+
|
|
163
|
+
with open(yaml_output_path, 'w') as yaml_file:
|
|
164
|
+
yaml.safe_dump(final_data, yaml_file, default_flow_style=False, sort_keys=False)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import traceback
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
from zs_yaml.convert import (
|
|
6
|
+
yaml_to_bin,
|
|
7
|
+
bin_to_yaml,
|
|
8
|
+
yaml_to_json,
|
|
9
|
+
json_to_yaml,
|
|
10
|
+
yaml_to_yaml
|
|
11
|
+
)
|
|
12
|
+
from zs_yaml import get_version_info
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
def parse_arguments():
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
18
|
+
|
|
19
|
+
description=
|
|
20
|
+
f'%(prog)s {get_version_info()}\n\n'
|
|
21
|
+
'Converts between YAML, JSON, and binary formats. '
|
|
22
|
+
'To convert from binary to YAML, the target YAML file must already exist with metadata. '
|
|
23
|
+
'The metadata is required to identify the correct Python type for deserialization.\n\n'
|
|
24
|
+
'The minimal metadata content in the target YAML file should be:\n'
|
|
25
|
+
' _meta:\n'
|
|
26
|
+
' schema_module: <module_name>\n'
|
|
27
|
+
' schema_type: <type_name>',
|
|
28
|
+
|
|
29
|
+
usage='%(prog)s <input_path> [output_path]\n\n'
|
|
30
|
+
'Example usage:\n'
|
|
31
|
+
' %(prog)s input.yaml output.bin\n'
|
|
32
|
+
' %(prog)s input.bin output.yaml\n'
|
|
33
|
+
' %(prog)s input.yaml output.json\n'
|
|
34
|
+
' %(prog)s input.json output.yaml\n'
|
|
35
|
+
' %(prog)s input.yaml (output will be inferred as binary if not specified)'
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument('input_path', type=str, help='Path to the input file (YAML, JSON, or binary)')
|
|
38
|
+
parser.add_argument('output_path', type=str, nargs='?', help='Path to the output file (YAML, JSON, or binary)')
|
|
39
|
+
parser.add_argument('--version', action='version', version=f'%(prog)s {get_version_info()}')
|
|
40
|
+
|
|
41
|
+
if len(sys.argv) < 2:
|
|
42
|
+
parser.print_help(sys.stderr)
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
|
|
45
|
+
return parser.parse_args()
|
|
46
|
+
|
|
47
|
+
def process_yaml_input(input_path, output_path):
|
|
48
|
+
if not output_path:
|
|
49
|
+
output_extension = '.bin'
|
|
50
|
+
output_path = os.path.splitext(input_path)[0] + output_extension
|
|
51
|
+
else:
|
|
52
|
+
output_extension = os.path.splitext(output_path)[1].lower()
|
|
53
|
+
|
|
54
|
+
if output_extension == '.bin' or output_extension == '':
|
|
55
|
+
yaml_to_bin(input_path, output_path)
|
|
56
|
+
elif output_extension == '.json':
|
|
57
|
+
yaml_to_json(input_path, output_path)
|
|
58
|
+
elif output_extension == '.yaml':
|
|
59
|
+
yaml_to_yaml(input_path, output_path)
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError("Unsupported output file extension for YAML input")
|
|
62
|
+
|
|
63
|
+
return output_path
|
|
64
|
+
|
|
65
|
+
def process_binary_input(input_path, output_path):
|
|
66
|
+
if not output_path:
|
|
67
|
+
raise ValueError("Output path must be specified for binary input")
|
|
68
|
+
bin_to_yaml(input_path, output_path)
|
|
69
|
+
return output_path
|
|
70
|
+
|
|
71
|
+
def process_json_input(input_path, output_path):
|
|
72
|
+
if not output_path:
|
|
73
|
+
output_extension = '.yaml'
|
|
74
|
+
output_path = os.path.splitext(input_path)[0] + output_extension
|
|
75
|
+
else:
|
|
76
|
+
output_extension = os.path.splitext(output_path)[1].lower()
|
|
77
|
+
|
|
78
|
+
if output_extension == '.yaml':
|
|
79
|
+
json_to_yaml(input_path, output_path)
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError("Unsupported output file extension for JSON input")
|
|
82
|
+
|
|
83
|
+
return output_path
|
|
84
|
+
|
|
85
|
+
def get_file_size(file_path):
|
|
86
|
+
return os.path.getsize(file_path)
|
|
87
|
+
|
|
88
|
+
def print_summary(start_time, output_path):
|
|
89
|
+
end_time = time.time()
|
|
90
|
+
execution_time = end_time - start_time
|
|
91
|
+
output_size = get_file_size(output_path)
|
|
92
|
+
print(f"Generated {output_path} (size: {output_size/1024.0:.0f}KB) in {execution_time:.2f} seconds.")
|
|
93
|
+
|
|
94
|
+
def main():
|
|
95
|
+
args = parse_arguments()
|
|
96
|
+
|
|
97
|
+
input_extension = os.path.splitext(args.input_path)[1].lower()
|
|
98
|
+
|
|
99
|
+
start_time = time.time()
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
if input_extension == '.yaml':
|
|
103
|
+
output_path = process_yaml_input(args.input_path, args.output_path)
|
|
104
|
+
elif input_extension == '.bin' or input_extension == '':
|
|
105
|
+
output_path = process_binary_input(args.input_path, args.output_path)
|
|
106
|
+
elif input_extension == '.json':
|
|
107
|
+
output_path = process_json_input(args.input_path, args.output_path)
|
|
108
|
+
else:
|
|
109
|
+
raise ValueError("Unsupported input file extension")
|
|
110
|
+
|
|
111
|
+
print_summary(start_time, output_path)
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"Error processing file: {e}")
|
|
115
|
+
traceback.print_exc()
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
main()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import importlib.util
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import yaml
|
|
6
|
+
import zs_yaml.built_in_transformations
|
|
7
|
+
from string import Template
|
|
8
|
+
|
|
9
|
+
class YamlTransformer:
|
|
10
|
+
"""
|
|
11
|
+
Encapsulates the transformation of yaml data using
|
|
12
|
+
invocations of registered transformation functions.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# Class-level cache for loaded modules
|
|
16
|
+
_loaded_modules = {}
|
|
17
|
+
|
|
18
|
+
def __init__(self, yaml_file_path, template_args=None, initial_transformations=None):
|
|
19
|
+
self.yaml_file_path = yaml_file_path
|
|
20
|
+
self.yaml_dir = os.path.dirname(os.path.abspath(yaml_file_path))
|
|
21
|
+
self.transformations = initial_transformations or {}
|
|
22
|
+
self._load_functions(zs_yaml.built_in_transformations)
|
|
23
|
+
self.meta = {}
|
|
24
|
+
self.template_args = template_args
|
|
25
|
+
self.yaml_data = None
|
|
26
|
+
|
|
27
|
+
def resolve_path(self, path):
|
|
28
|
+
if os.path.isabs(path):
|
|
29
|
+
return path
|
|
30
|
+
return os.path.normpath(os.path.join(self.yaml_dir, path))
|
|
31
|
+
|
|
32
|
+
def transform(self):
|
|
33
|
+
if self.yaml_data is None:
|
|
34
|
+
self._load_yaml()
|
|
35
|
+
|
|
36
|
+
transformation_module = self.meta.get('transformation_module')
|
|
37
|
+
|
|
38
|
+
if transformation_module:
|
|
39
|
+
self._load_functions(transformation_module)
|
|
40
|
+
|
|
41
|
+
return self._process(self.yaml_data)
|
|
42
|
+
|
|
43
|
+
def to_json(self):
|
|
44
|
+
processed_data = self.transform()
|
|
45
|
+
return json.dumps(processed_data, indent=2)
|
|
46
|
+
|
|
47
|
+
def get_meta(self):
|
|
48
|
+
if self.yaml_data is None:
|
|
49
|
+
self._load_yaml()
|
|
50
|
+
return self.meta
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def _load_module_from_file(cls, module_name, file_path):
|
|
54
|
+
abs_path = os.path.abspath(file_path)
|
|
55
|
+
if abs_path in cls._loaded_modules:
|
|
56
|
+
return cls._loaded_modules[abs_path]
|
|
57
|
+
|
|
58
|
+
spec = importlib.util.spec_from_file_location(module_name, abs_path)
|
|
59
|
+
module = importlib.util.module_from_spec(spec)
|
|
60
|
+
spec.loader.exec_module(module)
|
|
61
|
+
cls._loaded_modules[abs_path] = module
|
|
62
|
+
return module
|
|
63
|
+
|
|
64
|
+
def _load_functions(self, transformation_module):
|
|
65
|
+
if isinstance(transformation_module, str):
|
|
66
|
+
if transformation_module.endswith('.py'):
|
|
67
|
+
abs_path = self.resolve_path(transformation_module)
|
|
68
|
+
transformation_module = self._load_module_from_file('custom_transformations', abs_path)
|
|
69
|
+
else:
|
|
70
|
+
transformation_module = importlib.import_module(transformation_module)
|
|
71
|
+
|
|
72
|
+
for name, func in vars(transformation_module).items():
|
|
73
|
+
if callable(func):
|
|
74
|
+
self._register_function(name, func)
|
|
75
|
+
|
|
76
|
+
def _register_function(self, name, func):
|
|
77
|
+
if name in self.transformations:
|
|
78
|
+
if self.transformations[name] != func:
|
|
79
|
+
raise ValueError(f"Attempting to register a different function with an existing name: {name}")
|
|
80
|
+
else:
|
|
81
|
+
self.transformations[name] = func
|
|
82
|
+
|
|
83
|
+
def _get_function(self, name):
|
|
84
|
+
return self.transformations.get(name)
|
|
85
|
+
|
|
86
|
+
def _process(self, data):
|
|
87
|
+
if isinstance(data, list):
|
|
88
|
+
return [self._process(item) for item in data]
|
|
89
|
+
elif isinstance(data, dict):
|
|
90
|
+
if '_f' in data and '_a' in data:
|
|
91
|
+
func = self._get_function(data['_f'])
|
|
92
|
+
args = data['_a']
|
|
93
|
+
if func and callable(func):
|
|
94
|
+
if isinstance(args, dict):
|
|
95
|
+
return func(self, **args)
|
|
96
|
+
else:
|
|
97
|
+
return func(self, args)
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError(f"Function {data['_f']} not found or is not callable")
|
|
100
|
+
else:
|
|
101
|
+
return {key: self._process(value) for key, value in data.items()}
|
|
102
|
+
return data
|
|
103
|
+
|
|
104
|
+
def _load_yaml(self):
|
|
105
|
+
with open(self.yaml_file_path, 'r') as yaml_file:
|
|
106
|
+
content = yaml_file.read()
|
|
107
|
+
|
|
108
|
+
if self.template_args:
|
|
109
|
+
content = Template(content).safe_substitute(self.template_args)
|
|
110
|
+
|
|
111
|
+
self.yaml_data = yaml.safe_load(content)
|
|
112
|
+
self.meta = self.yaml_data.pop('_meta', {})
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: zs-yaml
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A package that allows using yaml to define zserio objects.
|
|
5
|
+
Home-page: https://github.com/ndsev/zs_yaml
|
|
6
|
+
Author: Navigation Data Standard e.V.
|
|
7
|
+
Author-email: support@nds-association.org
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: PyYAML~=6.0
|
|
15
|
+
Requires-Dist: zserio~=2.12
|
|
16
|
+
|
|
17
|
+
# `zs-yaml`
|
|
18
|
+
|
|
19
|
+
*Your Easy Path to zserio Test Data*
|
|
20
|
+
|
|
21
|
+
<img src="doc/zs-yaml.png" alt="" height="100">
|
|
22
|
+
|
|
23
|
+
**zs-yaml** adds YAML as a text format to be used with zserio in order to improve the user experience when manually creating or analyzing test data for zserio.
|
|
24
|
+
|
|
25
|
+
- **YAML Format**: Uses YAML's human-readable format instead of JSON as the primary format for editing, making data handling and transformation more intuitive and less cluttered.
|
|
26
|
+
- **Metadata Inclusion**: Automatically includes metadata in the YAML, eliminating the need for users to manually identify the correct type when importing JSON data, ensuring seamless (de-)serialization.
|
|
27
|
+
- **Custom Transformations**: Allows for hooking in custom transformations so that users can work with familiar formats (e.g., dates or coordinate representations) instead of thinking in unfamiliar formats.
|
|
28
|
+
|
|
29
|
+
## Example
|
|
30
|
+
|
|
31
|
+
### Zserio Schema Module Creation
|
|
32
|
+
|
|
33
|
+
1. **Create the Zserio schema**:
|
|
34
|
+
- Create a file named `person.zs` with the following content:
|
|
35
|
+
|
|
36
|
+
```zserio
|
|
37
|
+
package person;
|
|
38
|
+
|
|
39
|
+
struct Person
|
|
40
|
+
{
|
|
41
|
+
string name;
|
|
42
|
+
string birthdate;
|
|
43
|
+
string birth_location;
|
|
44
|
+
string current_job;
|
|
45
|
+
int32 income;
|
|
46
|
+
RoleExperience experience[];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
struct RoleExperience
|
|
50
|
+
{
|
|
51
|
+
string role;
|
|
52
|
+
int32 years;
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. **Compile the Zserio schema and generate Python APIs**:
|
|
57
|
+
- Run the following command to compile the schema and generate Python sources:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# Generated sources needs type infos so that 'json <-> bin'
|
|
61
|
+
# conversion works, which is utilized by zs-yaml
|
|
62
|
+
zserio person.zs -withTypeInfoCode -python zs_gen_api
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
3. **Ensure that the schema modules are available in the Python environment**:
|
|
66
|
+
- Export the `PYTHONPATH` to include the directory containing the generated Python sources:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
export PYTHONPATH="zs_gen_api"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Use zs-yaml to create data
|
|
73
|
+
|
|
74
|
+
Using **zs-yaml**, you can define the same data in a more human-readable YAML format and include necessary metadata along with a custom transformation for the birthdate:
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
# 1) Metadata is used to specify the type needed for
|
|
78
|
+
# (de-)serialization and custom transform functions
|
|
79
|
+
# 2) User are free to use their preferred date format
|
|
80
|
+
# for the birth data as the a normalization function
|
|
81
|
+
# get invoked.
|
|
82
|
+
# 3) Yaml allows avoiding clutter and adding comments
|
|
83
|
+
# like this one :)
|
|
84
|
+
|
|
85
|
+
_meta:
|
|
86
|
+
schema_module: person.api
|
|
87
|
+
schema_type: Person
|
|
88
|
+
transformation_module: "./transformations.py"
|
|
89
|
+
|
|
90
|
+
name: John Doe
|
|
91
|
+
|
|
92
|
+
birthdate:
|
|
93
|
+
_f: normalize_date
|
|
94
|
+
_a: "01/01/1990"
|
|
95
|
+
birth_location: Springfield, USA
|
|
96
|
+
|
|
97
|
+
current_job: Software Engineer
|
|
98
|
+
income: 75000
|
|
99
|
+
|
|
100
|
+
experience:
|
|
101
|
+
- role: Intern
|
|
102
|
+
years: 1
|
|
103
|
+
- role: Junior Developer
|
|
104
|
+
years: 2
|
|
105
|
+
- role: Senior Developer
|
|
106
|
+
years: 3
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Example Transformation Module (transformations.py)
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
def normalize_date(date_str):
|
|
113
|
+
from datetime import datetime
|
|
114
|
+
for fmt in ("%Y-%m-%d", "%d/%m/%Y", "%m/%d/%Y"):
|
|
115
|
+
try:
|
|
116
|
+
return datetime.strptime(date_str, fmt).strftime("%Y-%m-%d")
|
|
117
|
+
except ValueError:
|
|
118
|
+
pass
|
|
119
|
+
raise ValueError(f"Date {date_str} is not in a recognized format")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Creating the Binary Representation
|
|
123
|
+
|
|
124
|
+
After you have installed `zs-yaml`, call `zs-yaml` to convert your YAML file to a binary representation:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
# Install zs-yaml if not already installed.
|
|
128
|
+
|
|
129
|
+
# Create the binary representation from the YAML file
|
|
130
|
+
zs-yaml person.yaml person.bin
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Installation
|
|
134
|
+
|
|
135
|
+
Currently, the Python package is only available at test.pypi. Once the official release is available,
|
|
136
|
+
you can install the package without specifying an index URL.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
python -m pip install --upgrade zs-yaml
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Usage
|
|
143
|
+
|
|
144
|
+
The main entry point for the application is `main.py`. It accepts arguments for specifying the input and output file paths. You can run the application as follows:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
zs-yaml input.yaml output.bin
|
|
148
|
+
zs-yaml input.bin output.yaml
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Notes
|
|
152
|
+
|
|
153
|
+
- You have to use the exact same order of field in the yaml as defined by the zserio schema, because zserio expects this
|
|
154
|
+
- When converting from binary to YAML, the target YAML file must already exist and contain the necessary metadata.
|
|
155
|
+
- The minimal metadata content in the target YAML file should be:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
_meta:
|
|
159
|
+
schema_module: <module_name>
|
|
160
|
+
schema_type: <type_name>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Future Considerations
|
|
164
|
+
|
|
165
|
+
In the future, we may consider using YAML tags for a more integrated approach to handling transformations directly within the YAML files, simplifying syntax and enhancing readability.
|
|
166
|
+
|
|
167
|
+
## Project Structure
|
|
168
|
+
|
|
169
|
+
- `setup.py`: Script for setting up the project and installing dependencies.
|
|
170
|
+
- `zs_yaml/`: Directory containing the actual `zs-yaml` implementation.
|
|
171
|
+
|
|
172
|
+
## Reference
|
|
173
|
+
|
|
174
|
+
This project references the zserio serialization framework. For more information about zserio, visit the [zserio GitHub project](https://github.com/ndsev/zserio).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
VERSION
|
|
5
|
+
requirements.txt
|
|
6
|
+
setup.py
|
|
7
|
+
doc/zs-yaml.png
|
|
8
|
+
doc/zs-yaml.svg
|
|
9
|
+
zs_yaml/__init__.py
|
|
10
|
+
zs_yaml/_version.py
|
|
11
|
+
zs_yaml/built_in_transformations.py
|
|
12
|
+
zs_yaml/convert.py
|
|
13
|
+
zs_yaml/main.py
|
|
14
|
+
zs_yaml/yaml_transformer.py
|
|
15
|
+
zs_yaml.egg-info/PKG-INFO
|
|
16
|
+
zs_yaml.egg-info/SOURCES.txt
|
|
17
|
+
zs_yaml.egg-info/dependency_links.txt
|
|
18
|
+
zs_yaml.egg-info/entry_points.txt
|
|
19
|
+
zs_yaml.egg-info/requires.txt
|
|
20
|
+
zs_yaml.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zs_yaml
|