-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Using __getattr__ breaks python-fire #149
Comments
TL;DR
The ProblemThe error occurs in The problem lies in the following snippet (spanning from lines 329-335) from the function in the try:
for base in object.__bases__:
for k, v in base.__dict__.items():
if isinstance(v, types.DynamicClassAttribute):
names.append(k)
except AttributeError:
pass The erorr occurs on line 2 above (line 330 from the actual code): This line was actually meant to get members of the parent classes or base classes of the current class. If the object variable happens to be a Python Object, as in the case of The problem in this particular case is that since the Possible Solutions
|
@borislopezaraoz you could try out the second solution till this is fixed. |
@dbieber The first solution seems kind of a hack to me, but if it's cool with the maintainers of the project, I'll raise a PR to fix this. |
I think there may be a simpler solution involving removing the line We really just want to check for the existence of two members ( @meshde wdyt? |
Makes sense. We could simply change lines L562 - L571 in members = dict(inspect.getmembers(component))
arg = args[0]
arg_names = [
arg,
arg.replace('-', '_'), # treat '-' as '_'.
]
for arg_name in arg_names:
if arg_name in members:
return members[arg_name], [arg], args[1:] to members = dir(component)
arg = args[0]
arg_names = [
arg,
arg.replace('-', '_'), # treat '-' as '_'.
]
for arg_name in arg_names:
if arg_name in members:
return getattr(component, arg_name), [arg], args[1:] I don't think this change should break anything, because |
Thanks @meshde, I can confirm that your second solution works on python 3. In the end I went with a different approach: https://stackoverflow.com/a/534597/1096370 For other use cases when the "missing methods" are more dynamic (not known in advance) it would be nice to have that fix in place. |
This still breaks in 0.2.1 (the latest version on pypi) - for my use case, I don't know which method is being called in advance and I'm lazy-loading modules accordingly, a la: def __getattr__(self, handle: str) -> AbstractSubSystem:
"""Hook from the command line."""
target: ImportTarget = self._APP_DICT[handle] # "handle" ends up being "__bases__" among other things
subsystem: Type[AbstractSubSystem] = getattr(
import_module(target.package), target.callable
) it looks like line 635 in members = dict(inspect.getmembers(component)) which tells me that the change that you guys talked about here never made it into PyPI. @meshde @dbieber any recommendations on what to do here? Is there a new release coming soon? Or should I try to find another workaround (since raising AttributeError doesn't actually solve the problem)? |
@Datamance I have raised a PR to fix this, I seem to have missed doing so earlier. In the meantime you could use the hack mentioned in the comments. def __getattr__(self, name):
if name == '__bases__':
raise AttributeError
def _missing(*args, **kwargs):
print("A missing method was called.")
print("The object was %r, the method was %r. " % (self, name))
print("It was called with %r and %r as arguments" % (args, kwargs))
return _missing You mention
Are you facing issues with this hack? |
@meshde correct - even with that check, it'll break on |
Actually, before it breaks on
|
Now that I'm looking closer, I'm looking at your proposed fix @meshde and realizing it won't work for my use case: members = dir(component)
arg = args[0]
arg_names = [
arg,
arg.replace('-', '_'), # treat '-' as '_'.
]
for arg_name in arg_names:
if arg_name in members: # <<<<<< ------- <<<<<< [THIS LINE RIGHT HERE]
return getattr(component, arg_name), [arg], args[1:] The check I've pointed to above precludes the use of If I have def __getattr__(self, handle: str) -> AbstractSubSystem:
"""Hook from the command line."""
target: ImportTarget = self._APP_DICT[handle]
subsystem: Type[AbstractSubSystem] = getattr(
import_module(target.package), target.callable
)
self._subsystem = subsystem.from_config(self._config)
return self._subsystem then those objects will definitionally never appear in This brings my next question to bear - why check for membership at all - why not just use for arg_name in arg_names:
sub_component = getattr(component, arg_name, None)
if sub_component:
return sub_component, arg, args[1:] |
This (admittedly hacky) metaprogramming way of implementing missing methods on the fly breaks python-fire.
Ref: https://stackoverflow.com/a/6955825/1096370
This "ghost methods" works normally when called from other python module.
When trying to call it with fire I get this:
The text was updated successfully, but these errors were encountered: