Adjusted the initialization for :class:`.Enum` to minimize how often it
invokes the ``.__members__`` attribute of a given PEP-435 enumeration
object, to suit the case where this attribute is expensive to invoke, as is
the case for some popular third party enumeration libraries.
Fixes: #4758
Change-Id: Iffeb854c67393bdcb288944fc357a074e20e1325
(cherry picked from commit
2cc7308c96f5598ba0aea9a240b9a52629042d07)
--- /dev/null
+.. change::
+ :tags: bug, sql
+ :tickets: 4758
+
+ Adjusted the initialization for :class:`.Enum` to minimize how often it
+ invokes the ``.__members__`` attribute of a given PEP-435 enumeration
+ object, to suit the case where this attribute is expensive to invoke, as is
+ the case for some popular third party enumeration libraries.
+
if len(enums) == 1 and hasattr(enums[0], "__members__"):
self.enum_class = enums[0]
+ members = self.enum_class.__members__
if self.values_callable:
values = self.values_callable(self.enum_class)
else:
- values = list(self.enum_class.__members__)
- objects = [
- self.enum_class.__members__[k]
- for k in self.enum_class.__members__
- ]
+ values = list(members)
+ objects = [members[k] for k in members]
return values, objects
else:
self.enum_class = None
--- /dev/null
+from sqlalchemy import Enum
+from sqlalchemy.testing import fixtures
+from sqlalchemy.testing import profiling
+from sqlalchemy.util import classproperty
+
+
+class EnumTest(fixtures.TestBase):
+ __requires__ = ("cpython",)
+
+ def setup(self):
+ class SomeEnum(object):
+ # Implements PEP 435 in the minimal fashion needed by SQLAlchemy
+
+ _members = {}
+
+ @classproperty
+ def __members__(cls):
+ """simulate a very expensive ``__members__`` getter"""
+ for i in range(10):
+ x = {}
+ x.update({k: v for k, v in cls._members.items()}.copy())
+ return x.copy()
+
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+ self._members[name] = self
+ setattr(self.__class__, name, self)
+
+ for i in range(400):
+ SomeEnum("some%d" % i, i)
+
+ self.SomeEnum = SomeEnum
+
+ @profiling.function_call_count()
+ def test_create_enum_from_pep_435_w_expensive_members(self):
+ Enum(self.SomeEnum)
test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_cextensions 158
test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 158
+# TEST: test.aaa_profiling.test_misc.EnumTest.test_create_enum_from_pep_435_w_expensive_members
+
+test.aaa_profiling.test_misc.EnumTest.test_create_enum_from_pep_435_w_expensive_members 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 4638
+test.aaa_profiling.test_misc.EnumTest.test_create_enum_from_pep_435_w_expensive_members 3.7_sqlite_pysqlite_dbapiunicode_cextensions 942
+
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation
test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_mysql_mysqldb_dbapiunicode_cextensions 51975