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.

Reply via email to