summaryrefslogtreecommitdiff
path: root/Lib/dataclasses.py
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2018-10-19 10:28:30 -0700
committerEric V. Smith <ericvsmith@users.noreply.github.com>2018-10-19 13:28:30 -0400
commitb9182aa7dad8991fc8768ae494b45b5f7c316aca (patch)
tree19f78dba054264b16b69d7d6ee4fc5ada3ae56e5 /Lib/dataclasses.py
parentbd9c2ce7acaef45f23c2659b854fc9925096d040 (diff)
downloadcpython-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.py37
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):