Skip to content
This repository has been archived by the owner on Dec 8, 2024. It is now read-only.

Enable XML leaf elements #59

Merged
merged 6 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 47 additions & 18 deletions sdRDM/generator/classrender.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def render_object(
object=object,
objects=all_objects,
small_types=small_types,
add_id_field=add_id_field,
)

validator_part = render_reference_validator(
Expand Down Expand Up @@ -136,13 +137,20 @@ def render_attribute(
"""Renders an attributeibute to code using a Jinja2 template"""

attribute = deepcopy(attribute)
template = Template(
attr_template = Template(
pkg_resources.read_text(
jinja_templates,
"attribute_template.jinja2",
)
)

leaf_template = Template(
pkg_resources.read_text(
jinja_templates,
"attribute_leaf_template.jinja2",
)
)

is_multiple = "multiple" in attribute
is_required = attribute["required"]
is_all_optional = _is_optional_single_dtype(attribute, objects, obj_name)
Expand Down Expand Up @@ -173,16 +181,29 @@ def render_attribute(
xml_alias = None
wrapped = False

return template.render(
name=attribute.pop("name"),
required=attribute.pop("required"),
dtype=combine_types(attribute.pop("type"), is_multiple, is_required),
metadata=stringize_option_values(attribute),
field_type=_get_field_type(attribute),
wrapped=wrapped,
tag=tag,
xml_alias=xml_alias,
)
if xml_alias == obj_name or tag == obj_name:
return leaf_template.render(
name=attribute.pop("name"),
required=attribute.pop("required"),
dtype=combine_types(attribute.pop("type"), is_multiple, is_required),
metadata=stringize_option_values(attribute),
field_type=_get_field_type(attribute),
wrapped=wrapped,
tag=tag,
xml_alias=xml_alias,
)

else:
return attr_template.render(
name=attribute.pop("name"),
required=attribute.pop("required"),
dtype=combine_types(attribute.pop("type"), is_multiple, is_required),
metadata=stringize_option_values(attribute),
field_type=_get_field_type(attribute),
wrapped=wrapped,
tag=tag,
xml_alias=xml_alias,
)


def _get_field_type(attribute: Dict) -> str:
Expand Down Expand Up @@ -314,7 +335,12 @@ def is_reference(key: str, option: str) -> bool:
return False


def render_add_methods(object: Dict, objects: List[Dict], small_types: Dict) -> str:
def render_add_methods(
object: Dict,
objects: List[Dict],
small_types: Dict,
add_id_field: bool,
) -> str:
"""Renders add methods fro each non-native type of an attribute"""

add_methods = []
Expand All @@ -330,12 +356,13 @@ def render_add_methods(object: Dict, objects: List[Dict], small_types: Dict) ->
for type in complex_types:
add_methods.append(
render_single_add_method(
attribute,
type,
objects,
is_single_type,
object["name"],
small_types,
attribute=attribute,
type=type,
objects=objects,
is_single_type=is_single_type,
obj_name=object["name"],
small_types=small_types,
add_id_field=add_id_field,
)
)

Expand Down Expand Up @@ -408,6 +435,7 @@ def render_single_add_method(
is_single_type: bool,
obj_name: str,
small_types: Dict,
add_id_field: bool,
) -> str:
"""Renders an add method for an attribute that occurs multiple times"""

Expand All @@ -433,6 +461,7 @@ def render_single_add_method(
cls=type,
signature=assemble_signature(type, objects, obj_name, small_types),
summary=f"This method adds an object of type '{type}' to attribute {attribute['name']}",
add_id_field=add_id_field,
)


Expand Down
4 changes: 2 additions & 2 deletions sdRDM/generator/templates/add_method_template.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
{%- for attr in signature %}
{{attr.name}}: {{attr.type}} {% if 'default' in attr%}= {{attr.default}} {% elif attr.multiple is true %}= ListPlus(){% endif %},
{%- endfor %}
id: Optional[str] = None,
{% if add_id_field %}id: Optional[str] = None,{% endif %}
**kwargs,
) -> {{ cls }}:
"""
{{summary}}

Args:
id (str): Unique identifier of the '{{ cls }}' object. Defaults to 'None'.
{% if add_id_field %}id (str): Unique identifier of the '{{ cls }}' object. Defaults to 'None'.{% endif %}
{%- for attr in signature %}
{{attr.name}} ({{ attr.dtype }}): {{ attr.description }}.{% if 'default' in attr %} Defaults to {{attr.default}}{% endif %}
{%- endfor %}
Expand Down
14 changes: 14 additions & 0 deletions sdRDM/generator/templates/attribute_leaf_template.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{name}}: {{dtype}} = Field(
{% if required is false and 'default' not in metadata.keys() and 'default_factory' not in metadata.keys() %}default=None,{% endif %}
{% if "description" in metadata %}description={{metadata["description"]}},{% endif %}
{% if "default" in metadata %}default={{metadata["default"]}},{% endif %}
{% if "default_factory" in metadata %}default_factory={{metadata["default_factory"]}},{% endif %}
json_schema_extra=dict(
{% for key, value in metadata.items() %}
{% if not key in ["default", "description", "default_factory"]%}
{{key}}={{value}},
{% endif %}
{% endfor %}
),
)

2 changes: 1 addition & 1 deletion sdRDM/generator/templates/import_template.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import sdRDM

from typing import Optional, Union, List, Dict
from uuid import uuid4
from pydantic import PrivateAttr, field_validator, model_validator
from pydantic import PrivateAttr, Field, field_validator, model_validator
from pydantic_xml import attr, element, wrapped
from lxml.etree import _Element

Expand Down
4 changes: 3 additions & 1 deletion sdRDM/markdown/objectutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ def process_option(
if option.lower().strip() == "type":
value = process_type_option(value, object_stack, external_types)
elif option.lower().strip() == "multiple" and attribute_has_default(object_stack):
del object_stack[-1]["attributes"][-1]["default"]

if "default" in object_stack[-1]["attributes"][-1]:
del object_stack[-1]["attributes"][-1]["default"]
object_stack[-1]["attributes"][-1]["default_factory"] = "ListPlus()"

object_stack[-1]["attributes"][-1][option.strip().lower()] = value
Expand Down
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ def model_all_dataset(model_all):
int_value=1,
)

leaf_element = model_all.LeafElement(
id="id",
leaf_value="I am a leaf",
some_attribute="I am an attribute",
)

dataset = model_all.Root(
id="id",
str_value="string",
Expand All @@ -67,6 +73,7 @@ def model_all_dataset(model_all):
nested_single_obj=nested,
nested_multiple_obj=[nested],
referenced_value=nested,
leaf_element=leaf_element,
)

return dataset
Expand Down
2 changes: 2 additions & 0 deletions tests/end2end/test_schemes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def test_scheme(model_all):
NoneType = type(None)
SomeEnum = model_all.enums.SomeEnum
Nested = model_all.Nested
LeafElement = model_all.LeafElement

expected_scheme = [
("id", Optional[str]),
Expand All @@ -35,6 +36,7 @@ def test_scheme(model_all):
("enum_value", Union[SomeEnum, NoneType]),
("nested_single_obj", Union[Nested, NoneType]),
("nested_multiple_obj", List[Nested]),
("leaf_element", Union[LeafElement, NoneType]),
]

given_scheme = [
Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/static/model_all.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ This model tests all methods and types that are present within the sdRDM library
- nested_multiple_obj
- Type: Nested[]
- XML: nested_multiple_obj/nested
- leaf_element
- Type: LeafElement
- XML: leaf_element

### Nested

Expand All @@ -55,6 +58,15 @@ This model tests all methods and types that are present within the sdRDM library
- int_value
- Type: integer

### LeafElement

- leaf_value
- Type: string
- XML: LeafElement
- some_attribute
- Type: string
- XML: @some_attribute

## Enumerations

### SomeEnum
Expand Down
7 changes: 6 additions & 1 deletion tests/fixtures/static/model_all_expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,10 @@
"float_value": 1.5,
"int_value": 1
}
]
],
"leaf_element": {
"id": "id",
"leaf_value": "I am a leaf",
"some_attribute": "I am an attribute"
}
}
1 change: 1 addition & 0 deletions tests/fixtures/static/model_all_expected.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@
<int_value>1</int_value>
</nested>
</nested_multiple_obj>
<leaf_element id="id" some_attribute="I am an attribute">I am a leaf</leaf_element>
</Root>
6 changes: 5 additions & 1 deletion tests/fixtures/static/model_all_expected.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ nested_multiple_obj:
- id: id
str_value: string
float_value: 1.5
int_value: 1
int_value: 1
leaf_element:
id: id
leaf_value: I am a leaf
some_attribute: I am an attribute
Loading