Metadata-Version: 1.2
Name: compose_struct
Version: 0.3
Summary: yet another namedtuple alternative
Home-page: https://github.com/ninjaaron/compose-struct
License: UNKNOWN
Description: compose
        =======
        yet another namedtuple alternative for Python
        
        ``compose.struct`` is something like an alternative to namedtuple
        attrs_ and now dataclasses_ in Python 3.7.
        
        .. _attrs: https://github.com/python-attrs/attrs
        .. _dataclasses: https://docs.python.org/3/library/dataclasses.html
        
        to create a new struct, you simply:
        
        .. code:: Python
        
          @compose.struct
          class Foo:
              bar = ...
              baz = 'spam'
        
        This generate a class like this:
        
        .. code:: Python
        
          class Foo:
              __slots__ = 'bar', 'baz'
        
              def __init__(self, bar, baz='spam'):
                  self.bar = bar
                  self.baz = spam
        
        You can, naturally, implement any other methods you wish.
        
        How's this different from attrs_ and dataclasses_? A few ways. Aside
        from the use of ellipsis to create positional parameters, another that
        can be seen here is that everything is based on ``__slots__``, which
        means your attribute lookup will be faster and your instances more
        compact in memory. attrs_ allows you to use slots, but ``struct``
        defaults to using slots. This means that attributes cannot be
        dynamically created. If a class needs private attributes, you may
        define additional slots with the usual method of defining
        ``__slots__`` inside the class body.
        
        Another important distinction is ``compose.struct`` doesn't define a
        bunch of random dunder methods. You get your ``__init__`` method and
        your ``__repr__`` and that's it. It is the opinion of the author that
        sticking all attributes in a tuple and comparing them usually is *not*
        what you want when defining a new type. However, it is still easy to get
        more dunder methods, as you will see in the following section.
        
        Interfaces
        ----------
        Perhaps the most significant difference our structs and alternatives is
        that we emphasize composition over inheritance. A ``struct`` isn't even
        able to inherit! It's an outrage! What about interfaces!? What about
        polymorphism!? Well, what ``compose`` provides is a simple way to
        generate pass-through methods to attributes.
        
        .. code:: Python
        
          from compose import struct, Provider
        
          @struct
          class ListWrapper:
              self.data = Provider('__getitem__', '__iter__')
              self.metadata = None
        
        
        So this will generate pass-through methods for ``__getitem__`` and
        ``__iter__`` to the ``data`` attribute. Certain python keywords and
        operators can be used as shorthand for adding dunder methods as well.
        
        .. code:: Python
        
          @struct
          class ListWrapper:
              self.data = Provider('[]', 'for')
              self.metadata = None
        
        Here, ``[]`` is shorthand for item access and implements
        ``__getitem__``, ``__setitem__`` and ``__delitem__``. ``for`` implements
        the ``__iter__`` method. A full list of these abbreviations can be found
        below in the `Pre-Defined Interfaces`_ section.
        
        Going even deeper, interfaces can be specified as classes. Wrapper
        methods will be created for any method attached to a class which is
        given as an argument to Provides. The following code is more or less
        equivalent to subclassing ``collections.UserList``, but no inheritance
        is used.
        
        .. code:: Python
        
          from collections import abc
        
          @struct
          class ListWrapper:
              self.data = Provider(abc.MutableSequence)
              self.metadata = None
        
        An instances of this class tested with ``isinstance(instance,
        abc.MutableSequence)`` will return ``True`` because wrapper methods
        have been generated on ``self.data`` for all the methods
        ``abc.MutableSequence``. Note that ``abc.MutableSequence`` does not
        actually provide all of the methods a real list does. If you want ALL
        of them, you can use ``Provides(list)``.
        
        Note that you cannot implicitly make pass-through methods for
        ``__setattr__`` and ``__getattribute__``, since they have some rather
        strange behaviors. You can, however, pass them explicitly to
        ``Provider`` to force the issue. In the case of ``__setattr__``, This
        invokes special behavior. See `__setattr__ hacks`_ for
        details.
        
        All methods defined with a provider can be overridden in the body of the
        class as desired. Methods can also be overridden by other providers.
        It's first-come, first-serve in that case. The Provider you want to
        define the methods has to be placed *above* any other interfaces that
        implement the same method.
        
        You can use ``@struct(frozen=True)`` to make your class more-or-less
        immutable after it initializes. It will raise an exception if you try
        to change it using the normal means.
        
        If you need a ``struct`` to look like a child of another class, I
        suggest using the abc_ module to define abstract classes. This allows
        classes to look like children for the purposes of type-checking, but
        without actually using inheritance.
        
        .. _abc: https://docs.python.org/3/library/abc.html
        
        Caveats
        -------
        This library is still very new. As of this moment, type
        annotations have not been implemented. ``*args``
        and ``**kwargs`` haven't been implemented either. Both of those things
        are planned. args/kwargs have a higher priority and should be available
        soon.
        
        Also be aware that this library uses code generation at class-creation
        time. The intent is to optimize performance of instances at the cost
        of slowing class creation. If you're dynamically creating huge numbers
        of classes, using ``compose.struct`` might be a bad idea. FYI,
        ``namedtuple`` does the same. I haven't looke at the source for attrs_
        too much, but I did see some strings with sourcecode there as well.
        
        Pre-Defined Interfaces
        ----------------------
        This is the code that implements the expansion of interface
        abbreviations for dunder methods. Any key in the ``interfaces``
        dictionary may be used to implement the corresponding dunder methods on
        an attribute with the ``Provides()`` constructor.
        
        .. code:: Python
        
          interfaces = {
              '+': 'add radd',
              '-': 'sub rsub',
              '*': 'mul rmul',
              '@': 'matmul rmatmul',
              '/': 'truediv rtruediv',
              '//': 'floordiv rfloordiv',
              '%': 'mod rmod',
              '**': 'pow rpow',
              '<<': 'lshift rlshift',
              '>>': 'rshift rrshift',
              '&': 'and rand',
              '^': 'xor rxor',
              '|': 'or ror',
              '~': 'invert',
              '==': 'eq',
              '!=': 'ne',
              '>': 'gt',
              '<': 'lt',
              '>=': 'ge',
              '<=': 'le',
              '()': 'call',
              '[]': 'getitem setitem delitem',
              '.': 'get set delete set_name',
              'in': 'contains',
              'for': 'iter',
              'with': 'enter exit',
              'del': 'del',
              'await': 'await'
          }
          interfaces = {k: ['__%s__' % n for n in v.split()]
                        for k, v in interfaces.items()}
        
        __setattr__ hacks
        -----------------
         If you choose to create an attribute
        wrapper for ``__setattr__``, the default will look like this so you
        won't hit a recursion error while accessing pre-defined attributes:
        
        .. code:: Python
        
            def __setattr__(self, attribute, value):
                if attr in self.__slots__:
                    object.__setattr__(self, attribute, value)
                else:
                    setattr(self.{wrapped_attribute}, attribute, value)
        
        If you want to override ``__setattr__`` with a more, eh, "exotic"
        method, you may want to build your struct with the ``escape_setattr``
        argument.
        
        .. code:: Python
        
            @struct(escape_setattr=True)
            class Foo:
                 bar = ...
                 baz = ...
        
             def __setattr__(self, attribute, value):
                  setattr(self.bar, attribute, value)
        
        This allows attributes to be set when the object is initialized, but
        will use your method at all other times, *including in other methods,
        which may break your stuff*. Definiting a ``__setattr__`` method like
        this together with the default ``__getattr__`` wrapper will cause a
        recursion error durring initialization of you don't use
        ``escape_setattr``.
        
Platform: UNKNOWN
Requires-Python: >=3.5
