Source code for pypet.slots

"""Module containing the superclass having slots"""

__author__ = 'Robert Meyer'


import pypet.compat as compat


[docs]def get_all_slots(cls): """Iterates through a class' (`cls`) mro to get all slots as a set.""" slots_iterator = (getattr(c, '__slots__', ()) for c in cls.__mro__) # `__slots__` might only be a single string, # so we need to put the strings into a tuple. slots_converted = ((slots,) if isinstance(slots, compat.base_type) else slots for slots in slots_iterator) all_slots = set() all_slots.update(*slots_converted) return all_slots
[docs]def add_metaclass(metaclass): """Adds a metaclass to a given class. This decorator is used instead of `__metaclass__` to allow for Python 2 and 3 compatibility. Inspired by the *six* module: (https://bitbucket.org/gutworth/six/src/784c6a213c4527ea18f86a800f51bf16bc1df5bc/six.py?at=default) For example: .. code-block:: python @add_metaclass(MyMetaClass) class MyClass(object): pass is equivalent to .. code-block:: python class MyClass(object): __metaclass__ = MyMetaClass in Python 2 or .. code-block:: python class MyClass(object, metaclass=MyMetaClass) pass in Python 3. """ def wrapper(cls): cls_dict = cls.__dict__.copy() slots = cls_dict.get('__slots__', None) if slots is not None: if isinstance(slots, compat.base_type): slots = (slots,) for slot in slots: cls_dict.pop(slot) cls_dict.pop('__dict__', None) cls_dict.pop('__weakref__', None) return metaclass(cls.__name__, cls.__bases__, cls_dict) return wrapper
[docs]class MetaSlotMachine(type): """Meta-class that adds the attribute `__all_slots__` to a class. `__all_slots__` is a set that contains all unique slots of a class, including the ones that are inherited from parents. """ def __init__(cls, name, bases, dictionary): super(MetaSlotMachine, cls).__init__(name, bases, dictionary) cls.__all_slots__ = get_all_slots(cls)
@add_metaclass(MetaSlotMachine)
[docs]class HasSlots(object): """Top-class that allows mixing of classes with and without slots. Takes care that instances can still be pickled with the lowest protocol. Moreover, provides a generic `__dir__` method that lists all slots. """ __slots__ = ('__weakref__',) def __getstate__(self): if hasattr(self, '__dict__'): # We don't require that all sub-classes also define slots, # so they may provide a dictionary statedict = self.__dict__.copy() else: statedict = {} # Get all slots of potential parent classes for slot in self.__all_slots__: try: value = getattr(self, slot) statedict[slot] = value except AttributeError: pass # Pop slots that cannot or should not be pickled statedict.pop('__dict__', None) statedict.pop('__weakref__', None) return statedict
[docs] def __setstate__(self, state): """Recalls state for items with slots""" for key in state: setattr(self, key, state[key])
[docs] def __dir__(self): """Includes all slots in the `dir` method""" result = set() result.update(dir(self.__class__), self.__all_slots__) if hasattr(self, '__dict__'): result.update(compat.iterkeys(self.__dict__)) return list(result)