From ce1b9155328ade04b2b405969f32af7033569649 Mon Sep 17 00:00:00 2001 From: Tin Tvrtkovic Date: Wed, 23 Jun 2021 02:04:15 +0200 Subject: [PATCH 1/3] Rework linecache handling --- src/attr/_make.py | 55 ++++++++++++++++++------------------------- tests/test_dunders.py | 28 +++++++++++++++++++--- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/attr/_make.py b/src/attr/_make.py index 658eb047b..51183f87a 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -329,16 +329,25 @@ def _make_method(name, script, filename, globs=None): if globs is None: globs = {} - _compile_and_eval(script, globs, locs, filename) - # In order of debuggers like PDB being able to step through the code, # we add a fake linecache entry. - linecache.cache[filename] = ( - len(script), - None, - script.splitlines(True), - filename, - ) + count = 1 + base_filename = filename + while True: + linecache_tuple = ( + len(script), + None, + script.splitlines(True), + filename, + ) + old_val = linecache.cache.setdefault(filename, linecache_tuple) + if old_val == linecache_tuple: + break + else: + filename = "{}-{}>".format(base_filename[:-1], count) + count += 1 + + _compile_and_eval(script, globs, locs, filename) return locs[name] @@ -1632,30 +1641,12 @@ def _generate_unique_filename(cls, func_name): """ Create a "filename" suitable for a function being generated. """ - unique_id = uuid.uuid4() - extra = "" - count = 1 - - while True: - unique_filename = "".format( - func_name, - cls.__module__, - getattr(cls, "__qualname__", cls.__name__), - extra, - ) - # To handle concurrency we essentially "reserve" our spot in - # the linecache with a dummy line. The caller can then - # set this value correctly. - cache_line = (1, None, (str(unique_id),), unique_filename) - if ( - linecache.cache.setdefault(unique_filename, cache_line) - == cache_line - ): - return unique_filename - - # Looks like this spot is taken. Try again. - count += 1 - extra = "-{0}".format(count) + unique_filename = "".format( + func_name, + cls.__module__, + getattr(cls, "__qualname__", cls.__name__), + ) + return unique_filename def _make_hash(cls, attrs, frozen, cache_hash): diff --git a/tests/test_dunders.py b/tests/test_dunders.py index ba8f3ce89..3a8300399 100644 --- a/tests/test_dunders.py +++ b/tests/test_dunders.py @@ -936,6 +936,16 @@ class C(object): pass +CopyC = C + + +@attr.s(hash=True, order=True) +class C(object): + """A different class, to generate different methods.""" + + a = attr.ib() + + class TestFilenames(object): def test_filenames(self): """ @@ -953,15 +963,27 @@ def test_filenames(self): OriginalC.__hash__.__code__.co_filename == "" ) + assert ( + CopyC.__init__.__code__.co_filename + == "" + ) + assert ( + CopyC.__eq__.__code__.co_filename + == "" + ) + assert ( + CopyC.__hash__.__code__.co_filename + == "" + ) assert ( C.__init__.__code__.co_filename - == "" + == "" ) assert ( C.__eq__.__code__.co_filename - == "" + == "" ) assert ( C.__hash__.__code__.co_filename - == "" + == "" ) From 3a7c0372c6e79cbc511d3b28700e86661c9e8ef9 Mon Sep 17 00:00:00 2001 From: Tin Tvrtkovic Date: Wed, 23 Jun 2021 02:22:04 +0200 Subject: [PATCH 2/3] lint --- src/attr/_make.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/attr/_make.py b/src/attr/_make.py index 51183f87a..95a37ea64 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -5,7 +5,6 @@ import linecache import sys import threading -import uuid import warnings from operator import itemgetter From bd5e5ec5f4f4a934724fac5123698fd32d573f5d Mon Sep 17 00:00:00 2001 From: Tin Tvrtkovic Date: Wed, 30 Jun 2021 01:13:36 +0200 Subject: [PATCH 3/3] Add changelog --- changelog.d/828.changes.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/828.changes.rst diff --git a/changelog.d/828.changes.rst b/changelog.d/828.changes.rst new file mode 100644 index 000000000..b4a5454c8 --- /dev/null +++ b/changelog.d/828.changes.rst @@ -0,0 +1 @@ +Generated source code is now cached more efficiently for identical classes.