summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Holth <dholth@fastmail.fm>2016-06-06 00:23:12 (GMT)
committerDaniel Holth <dholth@fastmail.fm>2016-06-06 00:23:12 (GMT)
commite4b799e8dc348159c5f2d70bdf49819c4d8270bc (patch)
treef8cd8e050e2aa4ccd886ab7a05cceb5e20c6a0f6
parentaa5a0468340338cc3423a06d6d55e1b52a713bc5 (diff)
downloadSCons-e4b799e8dc348159c5f2d70bdf49819c4d8270bc.zip
SCons-e4b799e8dc348159c5f2d70bdf49819c4d8270bc.tar.gz
SCons-e4b799e8dc348159c5f2d70bdf49819c4d8270bc.tar.bz2
avoid using __slots__ on Node and Executor (but only on PyPy)
-rw-r--r--src/engine/SCons/Executor.py6
-rw-r--r--src/engine/SCons/Node/__init__.py5
-rw-r--r--src/engine/SCons/compat/__init__.py47
3 files changed, 54 insertions, 4 deletions
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index 7107fde..dd5088d 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -35,7 +35,7 @@ import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Memoize
-
+from SCons.compat import with_metaclass, NoSlotsPyPy
class Batch(object):
"""Remembers exact association between targets
@@ -154,7 +154,7 @@ _execute_str_map = {0 : execute_null_str,
1 : execute_actions_str}
-class Executor(object):
+class Executor(object, with_metaclass(NoSlotsPyPy)):
"""A class for controlling instances of executing an action.
This largely exists to hold a single association of an action,
@@ -580,7 +580,7 @@ def get_NullEnvironment():
nullenv = NullEnvironment()
return nullenv
-class Null(object):
+class Null(object, with_metaclass(NoSlotsPyPy)):
"""A null Executor, with a null build Environment, that does
nothing when the rest of the methods call it.
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 1a76b60..e714172 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -57,6 +57,8 @@ import SCons.Util
from SCons.Debug import Trace
+from SCons.compat import with_metaclass, NoSlotsPyPy
+
print_duplicate = 0
def classname(obj):
@@ -489,7 +491,8 @@ class BuildInfoBase(object):
if key not in ('__weakref__',):
setattr(self, key, value)
-class Node(object):
+
+class Node(object, with_metaclass(NoSlotsPyPy)):
"""The base Node class, for entities that we know how to
build, or use to build other Nodes.
"""
diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py
index 0ddbdd5..285c4ac 100644
--- a/src/engine/SCons/compat/__init__.py
+++ b/src/engine/SCons/compat/__init__.py
@@ -63,6 +63,8 @@ import os
import sys
import imp # Use the "imp" module to protect imports from fixers.
+PYPY = hasattr(sys, 'pypy_translation_info')
+
def import_as(module, name):
"""
Imports the specified module (from our local directory) as the
@@ -135,6 +137,51 @@ except AttributeError:
del _UserString
+def with_metaclass(meta, *bases):
+ """
+ Function from jinja2/_compat.py. License: BSD.
+
+ Use it like this::
+
+ class BaseForm(object):
+ pass
+
+ class FormType(type):
+ pass
+
+ class Form(with_metaclass(FormType, BaseForm)):
+ pass
+
+ This requires a bit of explanation: the basic idea is to make a
+ dummy metaclass for one level of class instantiation that replaces
+ itself with the actual metaclass. Because of internal type checks
+ we also need to make sure that we downgrade the custom metaclass
+ for one level to something closer to type (that's why __call__ and
+ __init__ comes back from type etc.).
+
+ This has the advantage over six.with_metaclass of not introducing
+ dummy classes into the final MRO.
+ """
+ class metaclass(meta):
+ __call__ = type.__call__
+ __init__ = type.__init__
+ def __new__(cls, name, this_bases, d):
+ if this_bases is None:
+ return type.__new__(cls, name, (), d)
+ return meta(name, bases, d)
+ return metaclass('temporary_class', None, {})
+
+
+class NoSlotsPyPy(type):
+ """
+ Workaround for PyPy not working well with __slots__ and __class__ assignment.
+ """
+ def __new__(meta, name, bases, dct):
+ if PYPY and '__slots__' in dct:
+ dct['__slats__'] = dct.pop('__slots__')
+ return super(NoSlotsPyPy, meta).__new__(meta, name, bases, dct)
+
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil