Skip to content

Prevent self-recursion on name fields implicitly#4

Merged
Poofjunior merged 6 commits into
mainfrom
feat-avoid-self-recursion
Aug 1, 2025
Merged

Prevent self-recursion on name fields implicitly#4
Poofjunior merged 6 commits into
mainfrom
feat-avoid-self-recursion

Conversation

@Poofjunior

@Poofjunior Poofjunior commented Jul 24, 2025

Copy link
Copy Markdown
Collaborator

Fixes #3

Added some syntactic sugar to prevent an instance from trying to recursively stuff an instance of itself into itself.

Previously, you had to mark the value to be skipped:

reaction_vessel:  # <-- Same as "name" kwarg
    class: brainwasher.devices.vessels.ReactionVessel
    skip_kwds: [name]
    kwds:
        name: reaction_vessel
        max_volume_ul: 10000

Now, DeviceSpinner can tell that you're not trying to instantiate an object by putting an instance of itself into itsellf recursively, so it skips args/kwarg-values that match the instance. (I can't think of any use case where this is desireable so we'll explicitly not-support it unless we have a good reason otherwise down the road.)

Now you can do this without recursing forever:

reaction_vessel:
    class: brainwasher.devices.vessels.ReactionVessel
    kwds:
        name: reaction_vessel
        max_volume_ul: 10000

Finally, because this packages supports creating devices using __init__ and also any function that returns an object, I added a to_list(*elements) function so that we can define an unbounded list in yamls. This is the only literal that needs to be handled this way.

@Poofjunior Poofjunior changed the title Prevent self-recursion on name fields Prevent self-recursion on name fields implicitly Jul 24, 2025
@Poofjunior Poofjunior requested a review from waltermwaniki July 24, 2025 15:23

@waltermwaniki waltermwaniki left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also can't imagine a situation where one would want to inject an object into itself (not sure if this is even possible) - this update is very welcome I believe.
Perhaps unrelated to this request though, I think we should avoid using python reserved names/core module names. Maybe consider renaming the "class" attribute in config to something else like "target" or "driver". And renaming the device_spinner.builtins module to something else...

@Poofjunior

Copy link
Copy Markdown
Collaborator Author

@waltermwaniki I'm open to changing it.

The original (and still valid!) syntax is:

module: <module path>
class: <class name>
args: ...
kwds: ...

I like this syntax because it's consistent with Python. The module field is actually a module, and the class field is actually a class. In python, the above is equivalent to: from <module> import <class>.

What's shown in the first comment is the shortened syntax, which is a concatenation of <module> + '.' + <name> to minimize the number of lines in the yaml file. We're calling that class right now which is technically incorrect. It's more of a class_path. This is the name I like the least. Is this the part you're calling out specifically?

If we want to rename these, here are some nuances to consider:

  1. The name of the "concatenated" field: of <module> + '.' + <name>. (Maybe classpath?)
  2. The case where we don't have a class but actually a function that returns a class instance. Currently, we use factory instead of class in that case.

For the "builtins", maybe we could call it factory_functions or factories? This was a case where I needed to generate a list with *args where the list elements will get replaced with instances. (It turns out Python doesn't a great syntax option for this.) LMK!

@waltermwaniki

Copy link
Copy Markdown
Collaborator

@Poofjunior
I think factory_functions if a fitting name. Alternatively, factory_utils...

I like using the term classpath since it is not reserved in python - and keeping factory for the case where a function is used.

I can also see a case where we only have the factory key. it shouldn't matter if it is a class or a function since they're all callables. I haven't used the function factory approach so I might be wrong.

@Poofjunior

Copy link
Copy Markdown
Collaborator Author

@waltermwaniki I renamed the builtins module to factory_utils. For the name shadowing, let's punt that discussion to #5 and address it there since it's a bit out of scope for this issue.

@patricklatimer

Copy link
Copy Markdown

Self-recursion fix looks great! For the to_list addition, I believe that it's necessary, but could you add an example or something to document the kind of error you might see if you just use builtins.list naively? This seems like the kind of thing a user would run into and want to look for a clear example of the error case in the docs.

@Poofjunior

Copy link
Copy Markdown
Collaborator Author

@patricklatimer I added the "gotcha" to the readme, along with a bit more documentation. (Docs are still a TODO.)

@Poofjunior Poofjunior merged commit 10f5a36 into main Aug 1, 2025
0 of 3 checks passed
@Poofjunior Poofjunior deleted the feat-avoid-self-recursion branch August 1, 2025 22:58
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.

By default, skip arg/kwarg-values whose values match the current object's instance name

3 participants