Skip to content

[ports] Add default values to PortInformation - #510

Merged
sea-bass merged 4 commits into
develfrom
default-port-values
Aug 26, 2026
Merged

sea-bass merged 4 commits into
develfrom
default-port-values

Conversation

@sea-bass

Copy link
Copy Markdown
Member

Adds a default_value attribute to the PortInformation dataclass, which is a convenient extension that allows specifying default input/output port values right in the port declarations.

Closes #489

@laibazahid26 laibazahid26 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Two small questions left inline, both about the default value edge cases rather than the design itself.

Comment thread tests/test_ports.py
Comment on lines +314 to +315
self.assertEqual(cons.get_input("items"), [1, 2])
self.assertEqual(DefaultingConsumer.input_ports()["items"].default_value, [1, 2])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: input_ports() rebuilds the dict and the PortInformation objects on every call, so lines 314 and 315 each get a fresh [1, 2]. I think both assertions would pass with the copy.deepcopy removed from get_input().

Is a port declaration ever shared across instances, or is it always reconstructed? If it can be shared, keeping the dict at the class level in DefaultingConsumer would make this test actually exercise the copy. If not, the deepcopy may not be necessary at all.

@sea-bass sea-bass Aug 24, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a great question -- in this particular case you're right that the deepcopy is unnecessary, but I didn't realize that this is set up so the dictionary keeps getting recreated. I would actually prefer having the deepcopies of the port values, but turning these methods into class attributes instead.

  class MyBehaviour(PortsMixin, py_trees.behaviour.Behaviour):
      INPUT_PORTS = {"input": PortInformation(data_type=str, required=True)}
      OUTPUT_PORTS = {"output": PortInformation(data_type=str, required=True)}

@JenniferBuehler what do you think? It's a breaking change but prevents unnecessary dictionary recreations every time we try to get the input port information.

Definitely for a follow-on PR though!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Update: Did this in #511 and it works fine.

Comment thread tests/test_ports.py
"""An explicit default= argument wins over the one declared on the port."""
cons = DefaultingConsumer("cons")
cons.setup_ports()
self.assertEqual(cons.get_input("input", default="call_site"), "call_site")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: get_input() uses None as the sentinel for “no argument default.” As a result, a falsy default falls through to the declared value. For example, get_input("input", default="") returns the declared value rather than "".

ports.rst says that “a call-site default still wins,” which is maybe stronger than the current implementation? Is that intentional? A module level _UNSET sentinel would allow False, 0, and "" to be used as explicit defaults. Either way, it could be useful for this test to include a falsy default case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I checked this and this is (thankfully) not the case, as all the checks are explicitly done with is not None rather than assuming falsiness.

For example, if we modify this exact like to do:

        self.assertEqual(cons.get_input("input", default=""), "")

it actually succeeds as expected, and does not fall back to the declared value.

@sea-bass
sea-bass merged commit 161309a into devel Aug 26, 2026
4 checks passed
@sea-bass
sea-bass deleted the default-port-values branch August 26, 2026 11:59
@sea-bass sea-bass linked an issue Aug 28, 2026 that may be closed by this pull request
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.

Add port default values Consider adding default value option when registering blackboard keys

2 participants