Bases: RootModel[List[Result]]
flowchart TD
trestle.transforms.results.Results[Results]
click trestle.transforms.results.Results href "" "trestle.transforms.results.Results"
Transformer results as a list.
The OSCAL schema requires at least one Result (minItems: 1), enforced here via min_length=1 so that model_validate (used by oscal_read) rejects an empty list loaded from a file.
When building a Results incrementally (as all transformer implementations do), use Results.model_construct(root=[]) followed by .root.append(...). model_construct intentionally bypasses Pydantic field validation, so an empty list is allowed during construction before items are appended.
Source code in trestle/transforms/results.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 | class Results(RootModel[List[Result]]):
"""Transformer results as a list.
The OSCAL schema requires at least one Result (minItems: 1), enforced here via
``min_length=1`` so that ``model_validate`` (used by ``oscal_read``) rejects an
empty list loaded from a file.
When building a Results incrementally (as all transformer implementations do),
use ``Results.model_construct(root=[])`` followed by ``.root.append(...)``.
``model_construct`` intentionally bypasses Pydantic field validation, so an
empty list is allowed during construction before items are appended.
"""
root: List[Result] = Field(default=[], min_length=1)
def oscal_dict(self):
"""Return an 'oscal wrapped' dictionary."""
class_name = self.__class__.__name__
result = {}
# For RootModel, model_dump() returns the root value directly (a list in this case)
raw_data = self.model_dump(by_alias=True, exclude_none=True, mode='json')
result[classname_to_alias(class_name, AliasMode.JSON)] = raw_data
return result
def oscal_serialize_json_bytes(self, pretty: bool = False, wrapped: bool = True) -> bytes:
"""
Return an 'oscal wrapped' json object serialized in a compressed form as bytes.
Args:
pretty: Whether or not to pretty-print json output or have in compressed form.
wrapped: Whether to wrap in OSCAL format.
Returns:
Oscal model serialized to a json object including packaging inside of a single top level key.
"""
if wrapped:
odict = self.oscal_dict()
else:
odict = self.model_dump(by_alias=True, exclude_none=True, mode='json')
def default_encoder(obj):
if isinstance(obj, datetime.datetime):
return robust_datetime_serialization(obj)
raise TypeError(f'Type {type(obj)} not serializable')
if pretty:
return orjson.dumps(odict, default=default_encoder, option=orjson.OPT_INDENT_2)
return orjson.dumps(odict, default=default_encoder)
def oscal_serialize_json(self, pretty: bool = False, wrapped: bool = True) -> str:
"""
Return an 'oscal wrapped' json object serialized in a compressed form as string.
Args:
pretty: Whether or not to pretty-print json output or have in compressed form.
wrapped: Whether to wrap in OSCAL format.
Returns:
Oscal model serialized to a json object including packaging inside of a single top level key.
"""
return self.oscal_serialize_json_bytes(pretty, wrapped).decode(const.FILE_ENCODING)
def oscal_write(self, path: pathlib.Path) -> None:
"""
Write out a pydantic data model in an oscal friendly way.
OSCAL schema mandates that top level elements are wrapped in a singular
json/yaml field. This function handles both json and yaml output as well
as formatting of the json.
Args:
path: The output file location for the oscal object.
Raises:
err.TrestleError: If Results contains no items (OSCAL minItems: 1 violation).
err.TrestleError: If an unknown file extension is provided.
"""
if not self.root:
raise err.TrestleError('Results must contain at least one Result before writing (OSCAL minItems: 1)')
content_type = FileContentType.to_content_type(path.suffix)
if content_type == FileContentType.YAML:
with pathlib.Path(path).open('w', encoding=const.FILE_ENCODING) as write_file:
yaml = YAML(typ='safe')
yaml.dump(yaml.load(self.oscal_serialize_json()), write_file)
elif content_type == FileContentType.JSON:
with pathlib.Path(path).open('wb') as write_file:
write_file.write(self.oscal_serialize_json_bytes(pretty=True))
|
Return an 'oscal wrapped' dictionary.
Source code in trestle/transforms/results.py
| def oscal_dict(self):
"""Return an 'oscal wrapped' dictionary."""
class_name = self.__class__.__name__
result = {}
# For RootModel, model_dump() returns the root value directly (a list in this case)
raw_data = self.model_dump(by_alias=True, exclude_none=True, mode='json')
result[classname_to_alias(class_name, AliasMode.JSON)] = raw_data
return result
|
Return an 'oscal wrapped' json object serialized in a compressed form as string.
Parameters:
| Name | Type | Description | Default |
pretty | bool | Whether or not to pretty-print json output or have in compressed form. | False |
wrapped | bool | Whether to wrap in OSCAL format. | True |
Returns: Oscal model serialized to a json object including packaging inside of a single top level key.
Source code in trestle/transforms/results.py
82
83
84
85
86
87
88
89
90
91
92 | def oscal_serialize_json(self, pretty: bool = False, wrapped: bool = True) -> str:
"""
Return an 'oscal wrapped' json object serialized in a compressed form as string.
Args:
pretty: Whether or not to pretty-print json output or have in compressed form.
wrapped: Whether to wrap in OSCAL format.
Returns:
Oscal model serialized to a json object including packaging inside of a single top level key.
"""
return self.oscal_serialize_json_bytes(pretty, wrapped).decode(const.FILE_ENCODING)
|
Return an 'oscal wrapped' json object serialized in a compressed form as bytes.
Parameters:
| Name | Type | Description | Default |
pretty | bool | Whether or not to pretty-print json output or have in compressed form. | False |
wrapped | bool | Whether to wrap in OSCAL format. | True |
Returns: Oscal model serialized to a json object including packaging inside of a single top level key.
Source code in trestle/transforms/results.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 | def oscal_serialize_json_bytes(self, pretty: bool = False, wrapped: bool = True) -> bytes:
"""
Return an 'oscal wrapped' json object serialized in a compressed form as bytes.
Args:
pretty: Whether or not to pretty-print json output or have in compressed form.
wrapped: Whether to wrap in OSCAL format.
Returns:
Oscal model serialized to a json object including packaging inside of a single top level key.
"""
if wrapped:
odict = self.oscal_dict()
else:
odict = self.model_dump(by_alias=True, exclude_none=True, mode='json')
def default_encoder(obj):
if isinstance(obj, datetime.datetime):
return robust_datetime_serialization(obj)
raise TypeError(f'Type {type(obj)} not serializable')
if pretty:
return orjson.dumps(odict, default=default_encoder, option=orjson.OPT_INDENT_2)
return orjson.dumps(odict, default=default_encoder)
|
Write out a pydantic data model in an oscal friendly way.
OSCAL schema mandates that top level elements are wrapped in a singular json/yaml field. This function handles both json and yaml output as well as formatting of the json.
Parameters:
| Name | Type | Description | Default |
path | Path | The output file location for the oscal object. | required |
Raises:
| Type | Description |
TrestleError | If Results contains no items (OSCAL minItems: 1 violation). |
TrestleError | If an unknown file extension is provided. |
Source code in trestle/transforms/results.py
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 | def oscal_write(self, path: pathlib.Path) -> None:
"""
Write out a pydantic data model in an oscal friendly way.
OSCAL schema mandates that top level elements are wrapped in a singular
json/yaml field. This function handles both json and yaml output as well
as formatting of the json.
Args:
path: The output file location for the oscal object.
Raises:
err.TrestleError: If Results contains no items (OSCAL minItems: 1 violation).
err.TrestleError: If an unknown file extension is provided.
"""
if not self.root:
raise err.TrestleError('Results must contain at least one Result before writing (OSCAL minItems: 1)')
content_type = FileContentType.to_content_type(path.suffix)
if content_type == FileContentType.YAML:
with pathlib.Path(path).open('w', encoding=const.FILE_ENCODING) as write_file:
yaml = YAML(typ='safe')
yaml.dump(yaml.load(self.oscal_serialize_json()), write_file)
elif content_type == FileContentType.JSON:
with pathlib.Path(path).open('wb') as write_file:
write_file.write(self.oscal_serialize_json_bytes(pretty=True))
|