Skip to content

Conversation

uriyyo
Copy link
Member

@uriyyo uriyyo commented Jul 27, 2021

@uriyyo
Copy link
Member Author

uriyyo commented Jul 27, 2021

@Fidget-Spinner Could you please review this PR?

Copy link
Member

@Fidget-Spinner Fidget-Spinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this mostly looks good. Could you please change your PR title to Refactor usage... instead of Reduce usage...? I thought you'd found some clever hack to avoid sys._getframe outright at first 😉 .

BTW, after ae0a2b7, avoiding sys._getframe could yield us very good performance gains. So there's some incentive to that, though I don't know how we can get rid of it.

Lib/typing.py Outdated
if '.' in name:
name = name.rpartition('.')[-1]
self.__name__ = name
self.__module__ = _callee(default='typing')
Copy link
Member

@Fidget-Spinner Fidget-Spinner Jul 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. Why don't we need this anymore? I thought the new default is __main__ which is different?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we should do it like it did at other classes:

def_mod = _callee()
if def_mod != 'typing':
    self.__module__ = def_mod

This line was written by me and it can lead to pickling errors cause in case if name is not present it will set module name to typing and typing module can not contains this NewType.

What is your opinion regarding that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow interesting! Are you able to to reproduce this with a small peice of code? If you can, we should backport this specific change (and you should open a separate PR for this, tied to the pickling/serialization issue). We can't backport the current PR entirely because it is refactoring, not bugfix.

Copy link
Member Author

@uriyyo uriyyo Jul 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code to snippet to reproduce this issue:

import pickle
from typing import NewType

del __name__

MyNewType = NewType("MyNewType", int)

pickle.dumps(MyNewType)
Traceback (most recent call last):
  File "...", line 9, in <module>
    pickle.dumps(MyNewType)
    ^^^^^^^^^^^^^^^^^^^^^^^
_pickle.PicklingError: Can't pickle typing.MyNewType: attribute lookup MyNewType on typing failed

I am not sure that it's okay to delete __name__ attribute of module)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Please create a separate PR just for this and link to https://bugs.python.org/issue44353.

Copy link
Member Author

@uriyyo uriyyo Jul 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New issue has been created.

link to issue - https://bugs.python.org/issue44761
lint to PR - #27406

@Fidget-Spinner
Copy link
Member

Hmm, also I can't verify this from the devguide, but I had the impression that refactoring can usually skip news if it has zero effect on the end user. I'll wait and see what others say.

@uriyyo uriyyo changed the title bpo-44747: Reduce usage of sys._getframe at typing module bpo-44747: Refactor usage of sys._getframe at typing module Jul 28, 2021
uriyyo and others added 3 commits July 28, 2021 12:35
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
@Fidget-Spinner
Copy link
Member

Fidget-Spinner commented Jul 28, 2021

LGTM. Lets wait for your other PR to land first. BTW minor nit and feel free to ignore it: (see #27387 (comment))

@ambv
Copy link
Contributor

ambv commented Jul 28, 2021

I find this goes somewhat outside of an acceptable refactor for backporting to 3.10 and 3.9 due to changes to _allow_reckless_class_checks.

@uriyyo
Copy link
Member Author

uriyyo commented Jul 30, 2021

@Fidget-Spinner @ambv How do you think typing._callee implementation should look like? Is current implementation to complex? I want to hear yours opinion regarding that to make implementation better.

@ambv
Copy link
Contributor

ambv commented Jul 30, 2021

The implementation of _callee() is close to what we need. Its contract is essentially to return the caller without ever raising exceptions. I would simplify:

  • if _getframe() is supported, then you will never get None back because default is '__main__' and __name__ is only ever explicitly set to None during interpreter shutdown;
  • therefore, we can remove the unsupported= kwarg and just return None in this case.

In this case usage of _callee() could use the result as long as it is not None.

@ambv
Copy link
Contributor

ambv commented Jul 30, 2021

Oh, by the way, it just appeared to me. The function is called wrong.

callee - n. The person who is called by the caller (on the telephone)

The function should be called _caller, not _callee.

@uriyyo
Copy link
Member Author

uriyyo commented Jul 30, 2021

if _getframe() is supported, then you will never get None back because default is 'main' and name is only ever explicitly set to None during interpreter shutdown;

Looks like we can explicitly set __name__ to None and in such case _caller will return None because __name__ will be at module namespace.

Code to show this behavior:

ns = {}

exec("""
import typing

__name__ = None

T = typing.TypeVar("T")
P = typing.ParamSpec("P")
NT = typing.NewType("NT", int)
""", ns)

for o in (ns["T"], ns["P"], ns["NT"]):
    print(o, o.__module__)
~T None
~P None
None.NT None

I don't know if it's valid case when someone explicitly set __name__ value. Should we cover this case or can we skip it?

@ambv
Copy link
Contributor

ambv commented Jul 30, 2021

If the user explicitly sets __name__ to an invalid value of None, types declared in such module will have __module__ set to None as well. Other weird things will happen too like the inability to use logging filtering, and so on.

The only time __name__ is set to None is during interpreter shutdown. We don't care about this use case since by that time no new typing.* objects are being created.

@uriyyo uriyyo requested a review from ambv July 30, 2021 12:25
@ambv ambv merged commit ea4673e into python:main Jul 30, 2021
@bedevere-bot
Copy link

@ambv: Please replace # with GH- in the commit message next time. Thanks!

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

Successfully merging this pull request may close these issues.

5 participants