Repository: qtile/qtile

Issue:
Python help broken on instance of InteractiveCommandClient
```python
from libqtile.ipc import find_sockfile, Client 
from libqtile.command_client import InteractiveCommandClient 
from libqtile.command_interface import IPCCommandInterface                                                                    

client = InteractiveCommandClient(IPCCommandInterface(Client(find_sockfile())))

help(client)
```
```
SelectError                               Traceback (most recent call last)
<ipython-input-4-0055de52face> in <module>
----> 1 help(client)

/usr/lib/python3.7/_sitebuiltins.py in __call__(self, *args, **kwds)
    101     def __call__(self, *args, **kwds):
    102         import pydoc
--> 103         return pydoc.help(*args, **kwds)

/usr/lib/python3.7/pydoc.py in __call__(self, request)
   1891     def __call__(self, request=_GoInteractive):
   1892         if request is not self._GoInteractive:
-> 1893             self.help(request)
   1894         else:
   1895             self.intro()

/usr/lib/python3.7/pydoc.py in help(self, request)
   1950             else: doc(str, 'Help on %s:', output=self._output)
   1951         elif isinstance(request, Helper): self()
-> 1952         else: doc(request, 'Help on %s:', output=self._output)
   1953         self.output.write('\n')
   1954 

/usr/lib/python3.7/pydoc.py in doc(thing, title, forceload, output)
   1670     try:
   1671         if output is None:
-> 1672             pager(render_doc(thing, title, forceload))
   1673         else:
   1674             output.write(render_doc(thing, title, forceload, plaintext))

/usr/lib/python3.7/pydoc.py in render_doc(thing, title, forceload, renderer)
   1645     if renderer is None:
   1646         renderer = text
-> 1647     object, name = resolve(thing, forceload)
   1648     desc = describe(object)
   1649     module = inspect.getmodule(object)

/usr/lib/python3.7/pydoc.py in resolve(thing, forceload)
   1637         return object, thing
   1638     else:
-> 1639         name = getattr(thing, '__name__', None)
   1640         return thing, name if isinstance(name, str) else None
   1641 

/media/data/dev/forks/qtile/libqtile/command_client.py in __getattr__(self, name)
    205             # we are going to resolve a command, check that the command is valid
    206             if not self._command.has_command(self._current_node, name):
--> 207                 raise SelectError("Not valid child or command", name, self._current_node.selectors)
    208             call_object = self._current_node.call(name)
    209             return self.__class__(self._command, current_node=call_object)

SelectError: Not valid child or command
```

Maybe it's not fixable without writing bad looking special cases, so feel free to close as `won't fix`.


Discussion:
2 years later and I might take a look at this one as I've been playing with command related stuff...

Resolving patch:
diff --git a/libqtile/command/client.py b/libqtile/command/client.py
--- a/libqtile/command/client.py
+++ b/libqtile/command/client.py
@@ -206,6 +206,13 @@ def __getattr__(self, name: str) -> InteractiveCommandClient:
             a command graph node (if the name is a valid child) or a command
             graph call (if the name is a valid command).
         """
+
+        # Python's help() command will try to look up __name__ and __origin__ so we
+        # need to handle these explicitly otherwise they'll result in a SelectError
+        # which help() does not expect.
+        if name in ["__name__", "__origin__"]:
+            raise AttributeError
+
         if isinstance(self._current_node, CommandGraphCall):
             raise SelectError("Cannot select children of call", name, self._current_node.selectors)
 


Test patch: