Pub/Sub workshop Demo

This notebook is part of my MESS Design Workshop PubSub {DRAFT}

A complete PubSub demo in python

For instruction to run, or see it as a slideshow, see: http://mess.softwarebetermaken.nl/en/latest/SoftwareCompetence/DesignWorkShops/PubSub/demo/index.html {Draft}


Notes (in slides-mode):

  • Use the spacebar to forward to the next slide after selecting (clicking) in this frame.
  • Or, click the outer-edge to open in a new window/tab. And use 'F' for full-window mode

The Demo

Some demos in python

  1. A complete implementation
    1. A Topic class
    2. With a subscribe and a publish method
  2. Several "demo" subscribers
    1. They just print ...
      • Real functionality is left to the reader
    2. Both in function-style and in OO-style
      • And, in the mixture
  3. Use it, by publishing to all

    1. Life, interactive; in IPython (& Jupyter) notebook
    2. Revealjs slides, generated from above

    Both, the notebook & slides are downloadable

The basics: A Topic class

  • A simple Topic class
  • And a trivial function that is use to subscribe
In [1]:
class Topic():
    def __init__(self, name=None, initial_value=None):
        self.name = "" if name is None else str(name)
        self._callbacks = []
        self._value = initial_value

    def subscribe(self, callback):
        if not callback in self._callbacks:
            self._callbacks.append(callback)

    def publish(self, value, force=False):
        if force or self._value != value:
            self._call_callbacks(value)
            self._value = value

    def _call_callbacks(self, new_value):
        for cb in self._callbacks:
            cb(new_value, self)

    def __str__(self):
        return "<<%s: '%s' at 0X%x>>" % (
            self.__class__.__name__, self.name, id(self))
In [2]:
def demo(val, topic):
    print("Demo:: Topic: %s has new value: %s" %(topic, val))

Note: You can ignore the dunder methods, Topic.__init__() and Topic.__str__() for now

Demo 1: Simple use

  • Create a topic
  • Subscribe demo
  • Publish a value
In [3]:
t1=Topic("demo 1")
In [4]:
t1.subscribe(demo)
In [5]:
t1.publish("HOI")
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: HOI

Using a Topic is trivial:

Once a topic is created and a callback is registerd, that function is called whenever a new value is pubished.

topic.publish(data) kind of act as callback(data, topic), for all callbacks in a loop

1a: This pub/sub is smart

  • It will not distribute the same value twice.
  • Unless it is asked to do so (force)
In [6]:
# Only a changed value will be published
t1.publish("AGAIN")
t1.publish("AGAIN")
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: AGAIN
In [7]:
# Unless we 'force' it
t1.publish("FORCE", force=True) # This force is needed, as it isn't "again" ...
t1.publish("FORCE", force=True)
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: FORCE
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: FORCE
In [8]:
# Unless we 'force' it
t1.publish("SMART", force=False) # You see ...
t1.publish("SMART", force=True)
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: SMART
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: SMART

1b: Even more smartness

This Topic will not subscribe the same function again

In [9]:
t1.publish("I'm so smart")
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: I'm so smart
In [10]:
# `demo` is already registered above
t1.subscribe(demo)   # will silently be ingnored
t1.publish("As I call demo only once")
Demo:: Topic: <<Topic: 'demo 1' at 0X1941ca82e88>> has new value: As I call demo only once

Demo 2: Multi-use

Now with multiple subscribers

  • We create (another) Topic: t2
  • And a handfull of (again, trivial) demo-callbacs

And subscribe all callbacks to the same t2 Topic ...

Then, all are executed as when publish() is called (once).


It is so easy :-)

In [11]:
t2= Topic("demo2")
In [12]:
def demo_2(val, topic):
    print("Demo 2:: Topic: %s has new value: %s" %(topic, val))
def demo_3(val, topic):
    print("Demo 3:: Topic: %s has new value: %s" %(topic, val))
def demo_4(val, topic):
    print("Demo 4:: Topic: %s has new value: %s" %(topic, val))    
In [13]:
t2.subscribe(demo)
t2.subscribe(demo_2)
t2.subscribe(demo_3)
t2.subscribe(demo_4)
In [14]:
t2.publish("ALL")
Demo:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: ALL
Demo 2:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: ALL
Demo 3:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: ALL
Demo 4:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: ALL
In [15]:
t2.publish("again")
Demo:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: again
Demo 2:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: again
Demo 3:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: again
Demo 4:: Topic: <<Topic: 'demo2' at 0X1941ca89848>> has new value: again

Surely, we can add more subscribers, and publish more often ...

Demo 3: OO subscribers

You can also subscribe methods, when you prefer an OO style. This works the same as with functions: just register the method.

  • We will define a class with a demo-callback
  • And register it to a new Topic: t3

Notes:

  • Python will automatically remember the object (self).
  • Here, we use a trick (self._no) to show (which of the) many instances are used.
In [16]:
class Demo:
    _stat_count=0
    def __init__(self):
        self._no = Demo._stat_count # make them unique
        Demo._stat_count+=1

    def demo(self, val, topic):
        print("%s got '%s' from topic %s" %(
        self, val, topic))
        
    def __str__(self):
        return "<<%s: ._no=%d at 0X%x>>" % (
            self.__class__.__name__, self._no, id(self))

Note: Again, you may ignore the dunder methods, Demo.__init__() and ``Demo.__str__()

In [17]:
o = Demo()
In [18]:
t3 = Topic("OO Demo")
In [19]:
t3.subscribe(o.demo)
In [20]:
t3.publish("class")
<<Demo: ._no=0 at 0X1941cadb588>> got 'class' from topic <<Topic: 'OO Demo' at 0X1941cad8348>>

Demo4: Mix and Match

You can mix-&-match all the several uses:

  • Classic functions and objects
  • Register many callbacks
    • In this demo, we use a loop to quckly define many callback.
  • Subscribe after a publication

4.1 many subscribers

In [21]:
t4 = Topic("four-10-plys")
t4.subscribe(demo)
for x in range(10): # Quicly create/subscribe 10 Demo-instances
    t4.subscribe(Demo().demo)
t4.subscribe(demo_4)
t4.subscribe(demo_3)
t4.subscribe(demo_2)
In [22]:
t4.publish('Yes')
Demo:: Topic: <<Topic: 'four-10-plys' at 0X1941ca98e48>> has new value: Yes
<<Demo: ._no=1 at 0X1941ca71e08>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=2 at 0X1941ca71dc8>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=3 at 0X1941ca82488>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=4 at 0X1941ca82988>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=5 at 0X1941ca82288>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=6 at 0X1941cabf748>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=7 at 0X1941cabf608>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=8 at 0X1941cabf388>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=9 at 0X1941ca82108>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
<<Demo: ._no=10 at 0X1941cabf048>> got 'Yes' from topic <<Topic: 'four-10-plys' at 0X1941ca98e48>>
Demo 4:: Topic: <<Topic: 'four-10-plys' at 0X1941ca98e48>> has new value: Yes
Demo 3:: Topic: <<Topic: 'four-10-plys' at 0X1941ca98e48>> has new value: Yes
Demo 2:: Topic: <<Topic: 'four-10-plys' at 0X1941ca98e48>> has new value: Yes

4.2 : Subscribe after 1st publish

In [23]:
t42 = Topic()
t42.subscribe(demo)
t42.publish("fist")
Demo:: Topic: <<Topic: '' at 0X1941cabf4c8>> has new value: fist
In [24]:
# Now add subscribe another callback
t42. subscribe(demo_2)
In [25]:
t42.publish("more and more")
Demo:: Topic: <<Topic: '' at 0X1941cabf4c8>> has new value: more and more
Demo 2:: Topic: <<Topic: '' at 0X1941cabf4c8>> has new value: more and more

Back to the main presentation

From WikiMedia

In [26]:
import datetime, pytz
print("These slides are generated at:", datetime.datetime.now(pytz.timezone('Europe/Amsterdam')))
#EoF
These slides are generated at: 2020-03-24 10:24:17.362544+01:00