Blinker

I’m using Pelican for another blog dedicated to books–no one is perfect. For several needs–an mainly because I’m a nerd–I have developed several plugins. And I have discovered that the Pelican plugin mechanism is based on a small framework called Blinker.

Blinker provides fast & simple object-to-object and broadcast signaling for Python objects.

It provides a way to communicate between objects through signals (a kind of event). I really found this way of working handy and elegant so I decided to have a closer look and to talk a bit about it.

It can be installed using pip: $ pip install blinker To demonstrate its usage I’ve made a completely dumb example:

A number generator sending signals and methods written to listen to these signals in order to print some information.

# The only import needed
from blinker import signal

# Signals definition
number_generator_number = signal('number_generator_number', doc='Return a generated number')
number_generator_start = signal('number_generator_start', doc='The number generator has started')
number_generator_end = signal('number_generator_end', doc='The number generator has ended')

class NumberGenerator():
    """A dumb number generator"""
    
    def __init__(self, name, bound):
      self.name = name
      self.bound = bound
      self.counter = 0

    def generate(self):
        # Sending a simple signal
        number_generator_start.send(self)
        while self.counter < self.bound:
            # Sending a signal with an associated data (the number generated)
            number_generator_number.send(self, number=self.counter)
            yield self.counter
            self.counter += 1
        # Informing that the generation has ended
        number_generator_end.send(self)
    
    def __repr__(self):
        return self.name

# Connecting with a decorator a different method for the each signal
@number_generator_start.connect
def start_generation(sender):
    print('{sender} started'.format(sender=sender))

@number_generator_number.connect
def print_number(sender, number):
    print('Got [{number}] from {sender}'.format(number=number, sender=sender))

@number_generator_end.connect
def end_generation(sender):
    print('{sender} ended'.format(sender=sender))

# Creating a generator
number_generator = NumberGenerator('Simple Generator', 5)

# Summing values
print('Sum is {result}'.format(result=sum(number_generator.generate())))

And the result is.

Simple Generator started
Got [0] from Simple Generator
Got [1] from Simple Generator
Got [2] from Simple Generator
Got [3] from Simple Generator
Got [4] from Simple Generator
Simple Generator ended
Sum is 10

The main advantage is its simplicity. It permits to build features with a loose coupling between objects and to act as a kind of interface to permit further developments. For that reason it’s particularly useful if you want to offer plugin mechanism or if you build something like a parser–Pelican is the perfect example.