Skip to content

Target from parent#75

Merged
bandophahita merged 20 commits into
ScreenPyHQ:trunkfrom
bandophahita:target_from_parent
Jun 26, 2026
Merged

Target from parent#75
bandophahita merged 20 commits into
ScreenPyHQ:trunkfrom
bandophahita:target_from_parent

Conversation

@bandophahita

@bandophahita bandophahita commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Adding support in Target to find_elements within a found element.
Addresses #74

@bandophahita bandophahita requested a review from perrygoy June 16, 2026 21:15
@bandophahita

Copy link
Copy Markdown
Contributor Author

I haven't added documentation yet. wanted your feedback before I did.

@bandophahita

Copy link
Copy Markdown
Contributor Author

I added some basic documentation that needs some refining.

@bandophahita

Copy link
Copy Markdown
Contributor Author

Well, I see a problem with the current design. Target.inside() mutates the original target

Take the following example::

actor.shall(See(Element(FIRST_NAME.inside(SELECTED)), Visible()))
actor.shall(See(Element(FIRST_NAME), Visible()))

FIRST_NAME is mutated by calling inside on the first line causing the second line to be the exact same.

@perrygoy perrygoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I had a bunch of thoughts, polish opportunities, and quibbles to talk through!

Comment thread screenpy_selenium/target.py
Comment thread docs/targets.rst
Comment thread docs/targets.rst Outdated
Comment thread docs/targets.rst
| Websert clicks on the "Sign In" button.


Target in Target

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

polish: This won't affect the output, but i've been trying to write these .rst files following the Semantic Linebreak conventions, since it makes changes to these docs much easier to review in PRs. I won't say i've done a great job of it (i think i might have been a bit overzealous in the linebreaks in the past), but it's more than just a character limit.

Can you give these another pass and try to follow that convention?

@bandophahita bandophahita Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am 100% terrible at figuring out where to break up the lines. I thought I was doing that. Better to just let me know where they should break.

Comment thread docs/targets.rst Outdated
Comment thread docs/targets.rst Outdated
Comment thread docs/targets.rst
Comment thread docs/targets.rst Outdated
Comment thread screenpy_selenium/target.py Outdated
raise TargetingError(msg) from e

def inside(self, parent_target: Target) -> Self:
"""Create a new Target and set the parent target for where search should start.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

quibble: I think this might be getting too into the sauce for the first line. Maybe just say "Set the containing parent element for this Target" on the first line, then mention that we create and return a new Target.

We may also want to add an example of creating a parent-contained-Target in the docstring for Target above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've updated the text... see if that works better.

msg = f"{e} raised while trying to find {self}."
raise TargetingError(msg) from e

def inside(self, parent_target: Target) -> Self:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

issue: This is not the correct typing for a method that returns a new object. Is there a generic "whatever class i am" return type, so we can support Target subclasses?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe that technically it is correct even when it's not the same instance. I had the same thought as you. Self feels like an instance of, but iirc it's not limited to that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Using mypy and reveal_type shows that inside returns the subclass rather than the base class.

class SubTarget(Target):
    pass

t1 = Target("t1").located((By.ID, "t1"))
st = SubTarget("s1").located((By.ID, "s1")).inside(t1)
reveal_type(st)

Revealed type is "test_target.SubTarget" (232:16)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Man, you're totally right! The docs confirm it: https://mypy.readthedocs.io/en/stable/more_types.html#precise-typing-of-alternative-constructors

I thought Self was very specifically "i return my self" but clearly it's a little more flexible than that. That's some good learnin', thanks for pushing back!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(I was going to be very curious about why our mypy check wasn't yelling about it, i suppose that probably should have been my clue that i had some more learning to do.)

@perrygoy perrygoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you nailed the Semantic Linebreak approach, thanks for the quick rework! This looks good to me now. Nice add 👍

@bandophahita bandophahita merged commit 2194bc2 into ScreenPyHQ:trunk Jun 26, 2026
12 checks passed
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