-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MNT: add EntryVisitor and refactor search methods to use SearchVisitor #110
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the presence and differentiation between .visit()
and .visitEntryType()
methods is throwing me off here, left a few rather confused comments. Perhaps consolidating everything with single-dispatch will make things simpler / smoother
entry.accept(self) | ||
return | ||
|
||
def visitParameter(self, parameter: Parameter) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can possibly use functools.singledispatch
here to consolidate these methods. (as I familiarize myself more with this pattern I realize that's exactly what we should do)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually confused by the presence of these two types of visit method, really all .visit()
is doing here is calling Entry.accept()
, which ultimately calls a .visit*()
method? (In a sort of triple dispatch back and forth). Maybe all the .visit()
's are meant to be interchangeable?
yield entry | ||
visitor = SearchVisitor(self, *search_terms) | ||
root = self.root | ||
visitor.visit(root) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing this elsewhere as starting with an "accept" call, such as root.acept(visitor)
. These are functionally equivalent right?
self.path = [] | ||
self.matches = [] | ||
|
||
def _check_match(self, entry: Union[Entry, Root]) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def _check_match(self, entry: Union[Entry, Root]) -> bool: | |
def _check_match(self, entry: Union[Entry, Root]) -> None: |
Because it confused me as I was reading this.
This would be the place to expose any entry that matches, and I think it might be worth bubbling that up and making visit
yield as well. I can imagine other visitors gathering PVs or specific entries etc, but not every node we visit will yield a result (Entries that don't match). So in the end maybe only EntryVisitor.visit
returns a generator and whatever other worker methods return values as necessary?
In untested skeleton code I'm imagining here (consolidating all the visit_ methods):
def visit(self, entry: Union[Entry, Root, UUID]) -> None:
if isinstance(entry, UUID):
entry = self.backend.get_entry(entry)
self.path.append(entry)
# Perform work
result = self._check_match(entry)
if result is not None:
yield result
# visiting entry's children handled by Entry.accept() calls, previously in super().visit(entry)
# but we get to visit via an `Entry.accept()` call, so why do we call accept again here?
self.path.pop()
Excuse my general confusion here
WIP
SearchVisitor
is not a true generator, it just yields from a list to satisfy the existing generator handlingSearchVisitor
a generator without requiring allEntryVisitors
to be generators.visit
and.accept
calls to support generators for visitors that want to use themDescription
Implement
EntryVisitor
abstract class, with correspondingEntry.accept
methodsImplement
SearchVisitor
as the first concrete visitorAdd
ancestor
search option toSearchVisitor
Relates to #106
Motivation and Context
Using a visitor pattern will let us decouple
Entry
DAG traversal from specific components, such as theClient
or concrete_Backend
subclasses. This should make it easier to do things like collect and search entries, fill UUIDs, and save snapshots without having to re-implement traversal logic in multiple places.How Has This Been Tested?
Existing search tests cover the new
SearchVisitor
.Where Has This Been Documented?
Pre-merge checklist
docs/pre-release-notes.sh
and created a pre-release documentation page