diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2018-10-19 10:28:30 -0700 |
---|---|---|
committer | Eric V. Smith <ericvsmith@users.noreply.github.com> | 2018-10-19 13:28:30 -0400 |
commit | b9182aa7dad8991fc8768ae494b45b5f7c316aca (patch) | |
tree | 19f78dba054264b16b69d7d6ee4fc5ada3ae56e5 /Lib/dataclasses.py | |
parent | bd9c2ce7acaef45f23c2659b854fc9925096d040 (diff) | |
download | cpython-git-b9182aa7dad8991fc8768ae494b45b5f7c316aca.tar.gz |
bpo-33947: dataclasses no longer can raise RecursionError in repr (GF9916) (#9970)
The reprlib code was copied here instead of importing reprlib. I'm not sure if we really need to avoid the import, but since I expect dataclasses to be more common that reprlib, it seems wise. Plus, the code is small.
(cherry picked from commit dd13c88b5371e13fc16b84e2f9b8715d917de269)
Co-authored-by: Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి) <srinivasreddy@users.noreply.github.com>
Diffstat (limited to 'Lib/dataclasses.py')
-rw-r--r-- | Lib/dataclasses.py | 37 |
1 files changed, 31 insertions, 6 deletions
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 28e9f75127..71d9896a10 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -5,6 +5,9 @@ import types import inspect import keyword import builtins +import functools +import _thread + __all__ = ['dataclass', 'field', @@ -337,6 +340,27 @@ def _tuple_str(obj_name, fields): return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)' +# This function's logic is copied from "recursive_repr" function in +# reprlib module to avoid dependency. +def _recursive_repr(user_function): + # Decorator to make a repr function return "..." for a recursive + # call. + repr_running = set() + + @functools.wraps(user_function) + def wrapper(self): + key = id(self), _thread.get_ident() + if key in repr_running: + return '...' + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + return wrapper + + def _create_fn(name, args, body, *, globals=None, locals=None, return_type=MISSING): # Note that we mutate locals when exec() is called. Caller @@ -497,12 +521,13 @@ def _init_fn(fields, frozen, has_post_init, self_name): def _repr_fn(fields): - return _create_fn('__repr__', - ('self',), - ['return self.__class__.__qualname__ + f"(' + - ', '.join([f"{f.name}={{self.{f.name}!r}}" - for f in fields]) + - ')"']) + fn = _create_fn('__repr__', + ('self',), + ['return self.__class__.__qualname__ + f"(' + + ', '.join([f"{f.name}={{self.{f.name}!r}}" + for f in fields]) + + ')"']) + return _recursive_repr(fn) def _frozen_get_del_attr(cls, fields): |