I agree with what Nils said (what he said is what I was trying to get at
when I said what my first preferred option would be). But after looking
some things up it seems like modifying __dir__ is actually the
recommended way of implementing this! I was surprised to learn this.
Below you can find a minimal example that shows the difference. Both
classes, `A` and `B` have a method `bad`, but in `A` this method is
visible to ipython's tab-completion, in `A` it is not.
When I try your example in IPython `bad` is not visible to
tab-completion for instances of `A` or instances of `B`. It is visible
for `A` itself, but methods are normally called on instances, not
classes so that's fine. When I try it with a regular Python prompt then
it is as you said. So modifying __dir__ is sufficient for the IPython
interface, and I'm happy to sacrifice alias handling for tab complete
behaviour in a regular (not IPython) terminal if it means much simpler
code. I'm not sure how many people use Sage in an interactive
non-IPython terminal anyway.
The IPython docs do explicitly say that __dir__ is the correct way to
control what shows up in tab autocomplete:
https://ipython.readthedocs.io/en/stable/config/integrating.html#tab-completion
Moreover the Python docs say this about dir:
https://docs.python.org/3/library/functions.html#dir
Because |dir()|
<https://docs.python.org/3/library/functions.html#dir> is supplied
primarily as a convenience for use at an interactive prompt, it
tries to supply an interesting set of names more than it tries to
supply a rigorously or consistently defined set of names, and its
detailed behavior may change across releases. For example, metaclass
attributes are not in the result list when the argument is a class.
So it seems that changing __dir__ is actually the correct way to
implement this. I am surprised, I would have expected it to be via an
IPython-specific interface. And given how the Python docs describe dir,
it also seems like other introspection tools shouldn't be relying on it.
Of course we'd need to try it to see for sure. Sphinx is probably the
most likely thing to be relying on __dir__, so looking at how the
generated docs turn out (especially with aliases) would be a good sanity
check that nothing broke. Worst-case, since this wouldn't change any
Sage functionality (so no deprecation policies apply), we could always
revert the change if we find out later that it broke some obscure but
important thing.
If we want this to apply to all Sage objects (I see no reason not to),
then implementing this behaviour in __dir__ for `SageObject` seems
appropriate.
Even if we want to hide all aliases: I don't know whether there is any
way to distinguish an alias from a method defined as usual in python -
how would you?
I did this in https://github.com/sagemath/sage/pull/40753 using the
Sphinx API, but it can be done in pure Python. There are probably some
edge cases the following code doesn't account for (lambdas, public
aliases of private functions), but it illustrates the idea.
class A:
def foo(self):
return 5
bar = foo
a = A()
a.__getattribute__('foo').__name__ == 'foo' # True
a.__getattribute__('bar').__name__ == 'bar' # False, because
a.__getattribute__('bar').__name__ is 'foo'
So you can distinguish an alias like this. (Both 'foo' and 'bar' are in
`a.__dir__()` by default, so iterate over each `s` in the super
`__dir__` and check if `self.__getattribute__(s).__name__ == s`.)
And if you only want to exclude some aliases, you're working with
strings so you can just write code to do whatever string processing you
need to only apply it for some aliases. (So for your example, check if
the alias is from num_xxx to n_xxx). Is there any reason not to filter
out all public aliases of public methods though? The only thing I can
think of is if the original non-alias name is something unexpected, but
in that case we could just swap what's the original and what's the
alias. Maybe there could be a few special cases we want to include both
for (I don't think we are consistent about which of sqrt or square_root
is the alias, maybe we include both in __dir__ until we refactor things
to make one of them consistently the original).
Also, both Python and IPython exclude private (_foo) methods from the
tab complete unless you try to tab complete on something starting with
an underscore, I think this behaviour is fine and should be left alone.
I'd only filter out public aliases of public methods.
--
You received this message because you are subscribed to the Google Groups
"sage-devel" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/sage-devel/af3de8c2-b036-4355-9d71-66191ce8b09e%40ucalgary.ca.