Skip to content
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

%autocall: Python 3.9 and PEP 585 #13006

Open
EhsanKia opened this issue Jun 2, 2021 · 9 comments
Open

%autocall: Python 3.9 and PEP 585 #13006

EhsanKia opened this issue Jun 2, 2021 · 9 comments
Labels

Comments

@EhsanKia
Copy link

EhsanKia commented Jun 2, 2021

PEP 585 in Python 3.9 introduces __class_getitem__, which allows for some types such as concurrent.futures.Future to have a type alias, such as:

IntFuture = concurrent.futures.Future[int]

The above works as expected in ipython 7.24.0, but if you don't assign it to a name, it throws an exception:

In [28]: IntFuture = concurrent.futures.Future[int]

In [29]: concurrent.futures.Future[int]
-------> concurrent.futures.Future([int])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-29-8b8b5de9d20e> in <module>
----> 1 concurrent.futures.Future([int])

TypeError: __init__() takes 1 positional argument but 2 were given

This seems to be a bug with ipython, as it works fine in normal python terminal.

From my understanding, this is due to a feature in ipython that lets you just write len array without parenthesis, and it'll automatically add parentheses for you. In this case, it tries to call Future([int]) as if it was an array.

EDIT: I believe the feature is called "autocall"

@Carreau
Copy link
Member

Carreau commented Jun 2, 2021

Are you sure yo don't have a config option that set autocall to 2 ?

By default IPython behaves properly:

$ ipython
Python 3.9.0 | packaged by conda-forge | (default, Oct 10 2020, 20:36:05)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.25.0.dev -- An enhanced Interactive Python. Type '?' for help.

In [1]: import concurrent

In [2]: concurrent.futures.Future[int]
Out[2]: concurrent.futures._base.Future[int]

What does get_ipython().config give you ?

You can try autocall 0, autocall 1, autocall 2 or no arguments but the default for autocall should be 1.

With autocall set to 2 even other stuff get improperly called:

In [8]: class A:
   ...:
   ...:     def __call__(self):
   ...:         print('a called')
   ...:

In [9]: a = A(); b = A()

In [10]: a.b = b

In [11]: a.b
-------> a.b()
a called

which is why it's not the default.

@EhsanKia
Copy link
Author

EhsanKia commented Jun 2, 2021

My autocall is set to 1 (default), I have not changed it.

Maybe it's fixed on 7.25 dev already?

In [5]: get_ipython().config
Out[5]: {'TerminalInteractiveShell': {'autocall': 1, 'confirm_exit': False}}

@Carreau
Copy link
Member

Carreau commented Jun 2, 2021

My bad, the default auf autocall is 0, not 1, with 1 I can indeed reproduce.

@Carreau
Copy link
Member

Carreau commented Jun 2, 2021

narrowed it down to


    handler_name = Unicode('auto')
    esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])

    def handle(self, line_info):

Which get called and decide that () should be added.

@EhsanKia
Copy link
Author

EhsanKia commented Jun 2, 2021

Awesome, thank you for looking into it. Note that this is only an issue with classes that have __class_getitem__ defined, so maybe that can be used to decide when autocall=smart?

@Carreau
Copy link
Member

Carreau commented Jun 3, 2021

Autocall is decided purely on the syntax, it does not use reflexion or inspection. So that might be hard.

@EhsanKia
Copy link
Author

EhsanKia commented Jun 4, 2021

Hmm, if it's working purely on the syntax tree, how does any getattr work? How can it tell that this is safe?

Future = {int: 1}
Future[int]

@MrMino
Copy link
Member

MrMino commented Jun 19, 2021

Reading the conversation it seems to me like this issue is specific to %autocall; correct me if I'm wrong.

@MrMino MrMino added the magics label Jun 19, 2021
@MrMino MrMino changed the title Python 3.9 and PEP 585 %autocall: Python 3.9 and PEP 585 Jun 19, 2021
@EhsanKia
Copy link
Author

It is. With autocall = 1, When doing a __getattr__ on a class object, it tries to call the class. In may have made sense in the past, but now that we have __class_getitem__, that no longer holds. Not sure what logic smart autocall uses to decide whether to call or not, but the logic needs to be extended to classes too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants