skip to navigation
skip to content

Not Logged In

glue.py 0.5.1

The agnostic model/view toolshed

glue.py is a single lightweight utility module expanding Python with new
powerful idioms for accessors, callsets, weak methods, enums, singletons and
observable primitives that can easily be integrated with existing projects. It
is particularly useful for writing model/view oriented applications.

Installation
============

You'll require Python 2.7, Python 3+ or PyPy 1.9 to use glue.py. Earlier
versions are not supported.

glue.py can easily be installed from the Python package repository using
distutils or pip:

    pip install glue.py

If you plan to make amendments to the codebase, or you're interested in the
latest development release, it is best to check out the source tree and
install glue.py in development mode:

    python setup.py develop

Usage
=====

glue.py provides a larger set of interoperating utility classes and functions,
of which only the most important ones will be described here in detail. It's
not a bad idea to just browse through the source and look what else is there
besides what's covered in this introduction.

Generated Accessors
-------------------

While Python supports accessors through properties, providing regulated access
to class variables has been traditionally cumbersome and repetitive, even though
the syntax has somewhat improved with recent versions. Consider this classical
approach to property access:

    from types import NoneType

    class Sponge(object):
        pass

    class Pineapple(object):
        on_owner_changed = None

        def __init__(self):
            self.__owner = None

        def get_owner(self):
            return self.__owner
        def set_owner(self, value):
            if value == self.__owner:
                return
            assert isinstance(value, (Sponge, NoneType))
            self.__owner = value
            if self.on_owner_changed:
                self.on_owner_changed(self)
        owner = property(get_owner, set_owner,
            doc = "the owner of this Pineapple")

apart from preventing a rewrite, the setter also provides an optional hook that
can be triggered when the value changes. Notice how many times the word "owner"
has to be repeated in one form or the other in order to expose this relatively
simple attribute. Until Python 2.7, most people adapted this handy recipe to
cut down on repetition:

         def owner():
            doc = """the owner of this pineapple"""
            def fget(self):
                return self.__owner
            def fset(self, value):
                if value == self.__owner:
                    return
                assert isinstance(value, (Sponge, NoneType))
                self.__owner = value
                if self.on_owner_changed:
                    self.on_owner_changed(self)
            return property(**locals())
        owner = owner()

That's slightly better, but not perfect, and also a little convoluted. These
days, this approach is the status quo:

        @property
        def owner(self):
            """The owner of this pineapple"""
            return self.__owner
        @owner.setter
        def owner(self, value):
            if value == self.__owner:
                return
            assert isinstance(value, (Sponge, NoneType))
            self.__owner = value
            if self.on_owner_changed:
                self.on_owner_changed(self)

it seems like we're mostly shuffling details around... all three solutions
vary around 12 lines, and with a bunch of these, classes may seem a lot larger
than they are. This is how glue.py does it:

    from glue import (lazydecls, defproperty, autoinit, callset)
    from types import NoneType

    class Sponge(object):
        pass

    @lazydecls
    class Pineapple(object):
        on_owner_changed = callset()

        owner = defproperty(
            default = None, types=(Sponge, NoneType), hook = on_owner_changed,
            doc = "The owner of this pineapple")

        def __init__(self):
            autoinit()

...case closed. For more information, try `help(glue.defproperty)`.

Callable Sets
-------------

You may have noticed that the `Pineapple` class exposes the `on_owner_changed`
callback as a `callset`, which is just a short way of instantiating a
`CallableSet`. Callable sets are a fast way to create observables:

    from glue import callset

    def func1(value):
        print("func1 called with value",value)
        return value+10

    def func2(value):
        print("func2 called with value",value)
        return value+20

    def func3(value):
        print("func3 called with value",value)
        return value+30

Trying this on the command line:

    >>> funcs = callset([func1,func2,func3])
    >>> print(max(funcs(10)))
    func1 called with value 10
    func2 called with value 10
    func3 called with value 10
    40

In the pineapple example, this is how a view would typically subscribe to
events sent by the `Pineapple` class:

    @lazydecls
    class Squid(object):
        neighbor_house = defproperty()
        annoying_coworkers = defproperty(default = set)

        def __init__(self, **kwargs):
            # auto-assign matching keyword args
            autoinit(**kwargs)
            # track if the owner of the neighbor house changes by registering
            # Squid's callback. The callback will be weakly proxied, and
            # automatically unregisters when this Squid instance is no longer
            # referenced.
            self.neighbor_house.on_owner_changed.addweak(self.someone_moved_in)

        def someone_moved_in(self, event):
            # since the on_owner_changed callset is global to Pineapple, Squid
            # would be notified of any owner changes in Pineapples, so filter
            # for the right one.
            if not (event.instance is self.neighbor_house):
                return
            # check if the new tenant is one of our annoying coworkers
            if event.value in self.annoying_coworkers:
                # state opinion on the situation
                print("Meh.")


Trying this on the command line:

    >>> house = Pineapple()
    >>> bob = Sponge()
    >>> squidward = Squid(neighbor_house = house)
    >>> squidward.annoying_coworkers.add(bob)
    >>> house.owner = bob
    Meh.

And that's the gist of it.

Weak Callable Proxies
---------------------

TODO

Observable Dicts, Lists and Sets
--------------------------------

TODO

Enums
-----

TODO

Singletons
----------

TODO
 
  • Downloads (All Versions):
  • 0 downloads in the last day
  • 0 downloads in the last week
  • 0 downloads in the last month