Skip to content

Commit

Permalink
fix: Options api rework (#671)
Browse files Browse the repository at this point in the history
Co-authored-by: Mohamed Koubaa <[email protected]>
Co-authored-by: pyansys-ci-bot <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Jan 22, 2025
1 parent 3961bd9 commit 9a5eb07
Show file tree
Hide file tree
Showing 16 changed files with 440 additions and 94 deletions.
33 changes: 16 additions & 17 deletions codegen/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@

from jinja2 import Environment, FileSystemLoader

from keyword_generation.handlers.shared_field import SharedFieldHandler
from keyword_generation.handlers.handler_base import KeywordHandler


SKIPPED_KEYWORDS = set(
[
# defined manually because of the variable length text card
Expand Down Expand Up @@ -288,6 +292,7 @@ def handle_override_field(kwd_data, settings):
if "new-name" in setting:
field["name"] = setting["new-name"]


def handle_rename_property(kwd_data, settings):
for setting in settings:
index = setting["index"]
Expand All @@ -299,19 +304,6 @@ def handle_rename_property(kwd_data, settings):
field["property_name"] = property_name


def handle_shared_field(kwd_data, settings):
for setting in settings:
fields = []
for card in kwd_data["cards"]:
for field in card["fields"]:
if field["name"] == setting["name"]:
fields.append(field)
assert len(fields) > 1
fields[0]["card_indices"] = setting["cards"]
for field in fields[1:]:
field["redundant"] = True


def handle_override_subkeyword(kwd_data, settings) -> None:
kwd_data["subkeyword"] = settings

Expand Down Expand Up @@ -351,7 +343,7 @@ def expand(card):
"rename-property": handle_rename_property,
"skip-card": handle_skipped_cards,
"duplicate-card-group": handle_duplicate_card_group,
"shared-field": handle_shared_field,
"shared-field": SharedFieldHandler(),
"override-subkeyword": handle_override_subkeyword,
}
)
Expand Down Expand Up @@ -387,7 +379,6 @@ def add_option_indices(kwd_data):
card["index"] = index
index += 1


def add_indices(kwd_data):
# handlers might point to cards by a specific index.
for index, card in enumerate(kwd_data["cards"]):
Expand Down Expand Up @@ -435,6 +426,9 @@ def after_handle(kwd_data):
do_insertions(kwd_data)
delete_marked_indices(kwd_data)
add_option_indices(kwd_data)
for handler_name, handler in HANDLERS.items():
if isinstance(handler, KeywordHandler):
handler.post_process(kwd_data)


def before_handle(kwd_data):
Expand All @@ -446,11 +440,16 @@ def handle_keyword_data(kwd_data, settings):
before_handle(kwd_data)
# we have to iterate in the order of the handlers because right now the order still matters
# right now this is only true for reorder_card
for handler_name, handler_func in HANDLERS.items():
for handler_name, handler in HANDLERS.items():
handler_settings = settings.get(handler_name)
if handler_settings == None:
continue
handler_func(kwd_data, handler_settings)
# handler can be a KeywordHandler object or a function pointer
# TODO - change all handlers to objects
if isinstance(handler, KeywordHandler):
handler.handle(kwd_data, handler_settings)
else:
handler(kwd_data, handler_settings)
after_handle(kwd_data)


Expand Down
Empty file.
15 changes: 15 additions & 0 deletions codegen/keyword_generation/handlers/handler_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import abc
import typing

class KeywordHandler(metaclass=abc.ABCMeta):
"""Abstract base class for keyword handlers."""

@abc.abstractmethod
def handle(self, kwd_data: typing.Dict[str, typing.Any], settings: typing.Dict[str, typing.Any]) -> None:
"""Transform `kwd_data` based on `settings`."""
raise NotImplementedError

@abc.abstractmethod
def post_process(self, kwd_data: typing.Dict[str, typing.Any]) -> None:
"""Run after all handlers have run."""
raise NotImplementedError
67 changes: 67 additions & 0 deletions codegen/keyword_generation/handlers/shared_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import typing

import keyword_generation.handlers.handler_base

def do_negative_shared_fields(kwd_data: typing.Dict):
negative_shared_fields = kwd_data.get("negative_shared_fields", [])
num_cards = len(kwd_data["cards"])
options = kwd_data.get("options", [])
option_cards = []
for options in kwd_data.get("options", []):
option_cards.extend(options["cards"])
for setting in negative_shared_fields:
indices = [-i for i in setting["cards"]]
fields = []
for index in indices:
if index >= num_cards:
for options in kwd_data.get("options", []):
for card in options["cards"]:
if card["index"] == index:
for field in card["fields"]:
if field["name"] == setting["name"]:
fields.append(field)
else:
assert False, "TODO - support negative indices for shared fields for non-options"
assert len(fields) > 1
if not setting["applied_card_indices"]:
fields[0]["card_indices"] = indices
for field in fields[1:]:
field["redundant"] = True


def handle_shared_field(kwd_data, settings):
# positive card indices are applied in handler
# negative card indices are marked and handled after transformations (after_handle)
for setting in settings:
setting["applied_card_indices"] = False
cards = setting["cards"]
num_positive = len([c for c in cards if c > 0])

# either or - we cannot support some positive some negative in the same setting now
assert num_positive == 0 or num_positive == len(cards)
if num_positive > 0:
fields = []
for card in kwd_data["cards"]:
for field in card["fields"]:
if field["name"] == setting["name"]:
fields.append(field)
assert len(fields) > 1
fields[0]["card_indices"] = cards
setting["applied_card_indices"] = True
for field in fields[1:]:
field["redundant"] = True
else:
if "negative_shared_fields" not in kwd_data:
kwd_data["negative_shared_fields"] = []
kwd_data["negative_shared_fields"].append(setting)


class SharedFieldHandler(keyword_generation.handlers.handler_base.KeywordHandler):

def handle(self, kwd_data: typing.Dict[str, typing.Any], settings: typing.Dict[str, typing.Any]) -> None:
"""Transform `kwd_data` based on `settings`."""
return handle_shared_field(kwd_data, settings)

def post_process(self, kwd_data: typing.Dict[str, typing.Any]) -> None:
"""Run after all handlers have run."""
return do_negative_shared_fields(kwd_data)
29 changes: 25 additions & 4 deletions codegen/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@
"generation-options": {
"add-option": [
{
"card-order": 1,
"title-order": 0,
"card-order": -1,
"title-order": 1,
"cards": [
{
"source": "kwd-data",
Expand All @@ -122,6 +122,28 @@
}
],
"option-name": "ID"
},
{
"card-order": -1,
"title-order": 1,
"cards": [
{
"source": "kwd-data",
"keyword-name": "CONSTRAINED_BEAM_IN_SOLID",
"card-index": 0
}
],
"option-name": "TITLE"
}
],
"shared-field": [
{
"name": "COUPID",
"cards": [-2, -3]
},
{
"name": "TITLE",
"cards": [-2, -3]
}
],
"skip-card": 0,
Expand All @@ -132,8 +154,7 @@
"new-name": "ncoup"
}
]
},
"comment": "TODO - the option name is either ID or TITLE, but there is no way in pydyna to have a union option"
}
},
"DEFINE_TRANSFORMATION": {
"generation-options": {
Expand Down
9 changes: 9 additions & 0 deletions codegen/templates/keyword/option_card_properties.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{% set card_loop = loop %}
{% for field in card.fields %}
{% if field.used %}
{% if not field.redundant %}
@property
def {{field.property_name}}(self) -> {%- if field.default is none %} typing.Optional[
{%- else %} {% endif %}{{field.property_type}}{%- if field.default is none %}]{%- endif %}:
Expand All @@ -17,9 +18,17 @@
if value not in [{{ field.options|join(', ')}}]:
raise Exception("""{{field.property_name}} must be one of {{openbrace}}{{ field.options|join(',')}}{{closebrace}}""")
{% endif %}
{% if field.card_indices %}
{# This is COMPLETELY wrong but it works for CONSTRAINED_BEAM_IN_SOLID. REVISIT! #}
{% for card_index in field.card_indices %}
self._cards[{{card_index}}].cards[{{card_loop.index-1}}].set_value("{{field.name}}", value)
{% endfor %}{# card_index in field.card_indices #}
{% else %}
self._cards[{{card.index}}].cards[{{card_loop.index-1}}].set_value("{{field.name}}", value)
{% endif %}{# card.indices #}

{% endif %}{# not field.readonly #}
{% endif %}{# not field.redundant #}
{% endif %}{# field.used #}
{% endfor %}{# field in card.fields #}
{% endfor %}{# card in option.cards #}
1 change: 1 addition & 0 deletions doc/changelog/671.documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fix: Options api rework
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class ConstrainedBeamInSolid(KeywordBase):
keyword = "CONSTRAINED"
subkeyword = "BEAM_IN_SOLID"
option_specs = [
OptionSpec("ID", 1, 0),
OptionSpec("ID", -1, 1),
OptionSpec("TITLE", -1, 1),
]

def __init__(self, **kwargs):
Expand Down Expand Up @@ -183,6 +184,30 @@ def __init__(self, **kwargs):
],
**kwargs
),
OptionCardSet(
option_spec = ConstrainedBeamInSolid.option_specs[1],
cards = [
Card(
[
Field(
"coupid",
int,
0,
10,
kwargs.get("coupid")
),
Field(
"title",
str,
10,
70,
kwargs.get("title")
),
],
),
],
**kwargs
),
]

@property
Expand Down Expand Up @@ -325,6 +350,7 @@ def coupid(self) -> typing.Optional[int]:
@coupid.setter
def coupid(self, value: int) -> None:
self._cards[2].cards[0].set_value("coupid", value)
self._cards[3].cards[0].set_value("coupid", value)

@property
def title(self) -> typing.Optional[str]:
Expand All @@ -335,4 +361,5 @@ def title(self) -> typing.Optional[str]:
@title.setter
def title(self, value: str) -> None:
self._cards[2].cards[0].set_value("title", value)
self._cards[3].cards[0].set_value("title", value)

Loading

0 comments on commit 9a5eb07

Please sign in to comment.