Skip to content

Commit 0496943

Browse files
committed
Merge pull request #263 from personalrobotics/bugfix/infinite_recursion
Refactored robot __getattr__ to resist infinite recursion. Resolves #262
2 parents 5aa9150 + 1dc571f commit 0496943

File tree

1 file changed

+33
-21
lines changed

1 file changed

+33
-21
lines changed

src/prpy/base/robot.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -97,41 +97,53 @@ def __dir__(self):
9797
def __getattr__(self, name):
9898
# We have to manually perform a lookup in InstanceDeduplicator because
9999
# __methods__ bypass __getattribute__.
100-
self = bind.InstanceDeduplicator.get_canonical(self)
100+
canonical = bind.InstanceDeduplicator.get_canonical(self)
101+
102+
# For special properties, we do NOT want to recursively look them up.
103+
# If they are not found on the canonical instance, stop looking.
104+
if canonical == self:
105+
if name in ['planner', 'actions', 'detector']:
106+
raise AttributeError('{0:s} is missing method "{1:s}".'
107+
.format(repr(canonical), name))
101108

102-
if (name != 'planner'
103-
and hasattr(self, 'planner')
104-
and self.planner is not None
105-
and self.planner.has_planning_method(name)):
109+
# For other methods, search the special properties for meta-methods.
110+
if (name != 'planner' and
111+
hasattr(canonical, 'planner') and
112+
canonical.planner is not None and
113+
canonical.planner.has_planning_method(name)):
114+
115+
delegate_method = getattr(canonical.planner, name)
106116

107-
delegate_method = getattr(self.planner, name)
108117
@functools.wraps(delegate_method)
109118
def wrapper_method(*args, **kw_args):
110-
return self._PlanWrapper(delegate_method, args, kw_args)
119+
return canonical._PlanWrapper(delegate_method, args, kw_args)
111120

112121
return wrapper_method
113-
elif (name != 'actions'
114-
and hasattr(self, 'actions')
115-
and self.actions is not None
116-
and self.actions.has_action(name)):
122+
elif (name != 'actions' and
123+
hasattr(canonical, 'actions') and
124+
canonical.actions is not None and
125+
canonical.actions.has_action(name)):
126+
127+
delegate_method = canonical.actions.get_action(name)
117128

118-
delegate_method = self.actions.get_action(name)
119129
@functools.wraps(delegate_method)
120130
def wrapper_method(*args, **kw_args):
121-
return delegate_method(self, *args, **kw_args)
131+
return delegate_method(canonical, *args, **kw_args)
122132
return wrapper_method
123-
elif (name != 'detector'
124-
and hasattr(self, 'detector')
125-
and self.detector is not None
126-
and self.detector.has_perception_method(name)):
127-
128-
delegate_method = getattr(self.detector, name)
133+
elif (name != 'detector' and
134+
hasattr(canonical, 'detector') and
135+
canonical.detector is not None and
136+
canonical.detector.has_perception_method(name)):
137+
138+
delegate_method = getattr(canonical.detector, name)
139+
129140
@functools.wraps(delegate_method)
130141
def wrapper_method(*args, **kw_args):
131-
return delegate_method(self, *args, **kw_args)
142+
return delegate_method(canonical, *args, **kw_args)
132143
return wrapper_method
133144

134-
raise AttributeError('{0:s} is missing method "{1:s}".'.format(repr(self), name))
145+
raise AttributeError('{0:s} is missing method "{1:s}".'
146+
.format(repr(canonical), name))
135147

136148
def CloneBindings(self, parent):
137149
self.planner = parent.planner

0 commit comments

Comments
 (0)