Skip to content

Onboard hyper into rush-py#96

Open
ryanswrt wants to merge 7 commits into
mainfrom
hyper-onboard-clean
Open

Onboard hyper into rush-py#96
ryanswrt wants to merge 7 commits into
mainfrom
hyper-onboard-clean

Conversation

@ryanswrt
Copy link
Copy Markdown
Contributor

Automated rush-py onboarding for hyper.

Module source: https://github.com/talo/tengu-hyper
Deploy paths:

  • hyper_minimize_sumo: github:talo/tengu-hyper/f2906f085c7a3a07104a98a263dc9876f08ee4e7#hyper_minimize_sumo
  • hyper_run_sumo: github:talo/tengu-hyper/f2906f085c7a3a07104a98a263dc9876f08ee4e7#hyper_run_sumo
  • hyper_solvate_sumo: github:talo/tengu-hyper/f2906f085c7a3a07104a98a263dc9876f08ee4e7#hyper_solvate_sumo

@github-actions
Copy link
Copy Markdown
Contributor

📖 Docs preview: https://talo.github.io/rush-py/pr-96/

@ryanswrt ryanswrt requested a review from seankhl March 31, 2026 10:25
Copy link
Copy Markdown
Contributor

@seankhl seankhl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standardize naming. Since this is a Python-submodule modules (as it should be, modeled after exess where there are multiple entrypoints to the same program), it should be like:

./src/rush/hyper/
  __init__.py
  _common.py (fine)
  _optimize.py (unless minimization is distinct; we use optimize for exess and nnxtb)
  _solvate.py
  _simulate.py

Names should be the verb of the module, should be clear, should not be prefixed with the program name or suffixed with sumo, etc.

  • Explode configs into the module function parameters: the top-level fields should be parameters, and if they themselves are objects, that's fine. So, there should be no generic Config type; if classes are needed for nested config values, don't call them Config, call them something meaningful based on what they configure
  • In general, classes should be named without the module name prefixed, and "rex"/"sumo"/"tengu"/etc. should never be part of the name
  • For Python-submodule modules, the individual entrypoints' classes should either be generically-named (if there's a "default" one) or all the classes should be uniformly prefixed with a meaningful name like OptimizationInput, OptimizationRun, OptimizationResultRef, OptimizationResult, OptimizationResultPaths, etc.
  • "Run" should not be part of any of the type names either
  • The module function itself should be named whatever you used for the prefix, or the verb-form of it if the case may be e.g. optimize. Redundant naming like adding hyper to everything is unpythonic, namespaces and classes are widely used to scope names like that.
  • I very deliberately chose not to use Output in any of the class names; it's too similar to Result and not clear enough.

For batched runs, I think the best thing to do is to make ResultRef, Result, and ResultPaths the "single" version of the output, and have the module function return Run[Iterator[RunResultRef]]. I think that keep things sane and auto3d should be changed to work that way too. Then for fetch and save, we don't wanna support the error types as input. The user can easily filter them out, and it bloats the implementations. We don't have to do this for this PR, I'm just making a note here when thinking about the design.

I think there's probably more, but this is a start.

Comment thread src/rush/hyper/_common.py
stage: ErrorStage
category: ErrorCategory
message: str
input_index: int
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably redundant and a bit sloppy? Maybe OK and doesn't hurt? 🤷

Comment thread src/rush/hyper/_common.py
input_index: int

@classmethod
def from_raw_output(cls, raw: Any) -> "ItemError":
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def from_raw_output(cls, raw: Any) -> "ItemError":
def from_raw_output(cls, raw: Any) -> Self:

Comment thread src/rush/hyper/_common.py
Comment on lines +44 to +45
def _is_result_type(raw: Any) -> bool:
return isinstance(raw, dict) and len(raw) == 1 and ("Ok" in raw or "Err" in raw)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably extract the inner function in runs.py and just reuse it.

Comment thread src/rush/hyper/_common.py
Comment on lines +91 to +100
def _upload_json_object(input_object: JsonObjectInput) -> RushObject:
match input_object:
case RushObject():
return input_object
case Path() | str() | dict():
return RushObject.from_dict(upload_object(input_object))
case _:
raise TypeError(
"Expected Path | str | RushObject | dict input for Hyper JSON object"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm I guess this should just be how rush.object.upload_object works...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we should have upload_json and upload_bin as class methods on RushObject.

Comment thread src/rush/hyper/_common.py
Comment on lines +134 to +145
def _to_rex_json_obj(obj: RushObject) -> str:
return (
'VirtualObject { path = "'
+ str(obj.path)
+ '", format = ObjectFormat::json, size = 0 }'
)


def _format_rex_list(items: list[str]) -> str:
if not items:
return "[]"
return "[\n " + ",\n ".join(items) + "\n ]"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could go in _rex.py probs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it should probably be _to_rex_obj and it should use the format in obj.

Comment on lines +155 to +161
def _save_run_artifact(obj: RushObject, ext: str, label: str) -> Path:
out_path = obj.save(ext=ext)
if obj.format.lower() == "json":
payload = _fetch_run_artifact_bytes(obj, label)
with out_path.open("wb") as out_file:
out_file.write(payload)
return out_path
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit like slop. Why is it saving and then opening it and writing to it again?



@dataclass(frozen=True)
class RunOutput:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class RunOutput:
class Result:

Should match auto3d design



@dataclass(frozen=True)
class RunOutputPaths:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class RunOutputPaths:
class ResultPaths:



@dataclass(frozen=True)
class TRCBatchResultRef:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably belongs in _common.py.

Then we could just create a TypeAlias to ResultRef if it's used as exactly a specific module's result reference.

)


def hyper_run_sumo(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def hyper_run_sumo(
def simulate(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants