Re: [wxPython-users] does anyone use pubsub's arg1 messaging protocol?

44 views
Skip to first unread message

oliver

unread,
Dec 10, 2015, 10:06:41 AM12/10/15
to wxpytho...@googlegroups.com, PyPubSub

Hi devplayer, thx for the feedback and code. I will add support for annotated listeners shortly and plan on addressing the inspect function deprecation, I just have to find way to maintain compatibility with Python 2.7. And no worries, the older dists are always available (really old ones get hidden but never deleted). 

One note: your wrapper uses data as arg, this suggests that you are using arg1 prototcol. The still-supported protocol is kwargs, which means your data would be send as separate objects using keyword arguments. 

Cheers, 

Oliver

On Dec 9, 2015 5:13 PM, "Dev Player" <devp...@gmail.com> wrote:
I personally haven't  for some time. 

But since you're looking at pubsub arg/kwarg1 protocols/API, I'd like to mention a little recipe that I use to get around an exception I get from the inspect package which I think pubsub uses. I get this exception when I use listener functions with argument annotations.

Also there's a depreciation warning for Python 3.5 for the inspect package dropping getargspect() or whatever it is called, which I think is used by pubsub. I came across the notice for depreciation for Python 3.5 after I added argument annotations to my listeners.

The inspect package's depreciation warning for Python 3.5 says it might drop getargspec() and instead to use inspect.getfullargspect() or inspect.getkwargspect(). I am not sure of the function name.

My current workaround is to use a subscribe() decorator which pulls off the annotations and then re-adds them after I pub.sendMessage() in the decorator.  The added benefit is that the topic subscription can be before the listener functions def statement and it doesn't invalidate the pub.subscribe() being used elsewhere. 

With additional modification to the subscribe decorator I suspect the inspect module's use could be removed and the topic tree can be created that way too.


# py -3.4+

"""
    author: DevP...@gmail.com
    date: 2015-11-26
    license: GNU Lesser General Public License v 3.0
    """

import functools
from pubsub import pub

def subscribe(topic, publisher:"You do not have to use the default pubsub.pub instance"=None):
    """ A decorator for pubsub listeners with a function(data=None) signature. This template
        allows function argument annotations to be used with pubsub v3 without throwing an
        exception. Example usage:

        @subscribe('server')
        def audit(data:'Capture only metadata of action.'=None):
            log_action(data)

        @subscribe('server.process')
        def process(data:'State change request.'=None):
            do_stuff(data)

        pub.sendMessage('server.process', data=a_request)
        """

    def real_decorator(function):
        _annotations = function.__annotations__.copy()
        function.__annotations__.clear()
        @functools.wraps(function)
        def wrapper(data=None):
            return function(data=data)
        if publisher is None:
            from pubsub import pub
        else:
            pub = publisher
        pub.subscribe(wrapper, topic)
        function.__annotations__.update(_annotations)
        return wrapper
    return real_decorator

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

DevPlayer

unread,
Dec 11, 2015, 3:05:48 AM12/11/15
to wxPython-users, pypu...@googlegroups.com
Yes, I think. The subscribe decorator I posted above is specific to listener functions with "data=" as a keyword. So this:

        @subscribe('server')
       
def audit(data:'Capture only metadata of action.'=None):
            log_action
(data)


       
@subscribe('server.process')
       
def process(data:'State change request.'=None):
            do_stuff
(data)


        pub
.sendMessage('server.process', data=a_request)



is precisely equivalent to this:

        def audit(data=None):

            log_action
(data)
        pub
.subscribe(audit, "server")


       
def process(data=None):

            do_stuff
(data)
        pub
.subscribe(process, "server.process")

        pub
.sendMessage('server.process', data=a_request) # data= is an explicit keyword

So to use a listener with a different signature, say

def some_listener(firstname=None, lastname=None):
    do_stuff
(firstname, lastname)
pub
.subscribe(some_listener, 'database.record.add')


You would have to create a new decorator:

def db_subscribe(topic, publisher:"You do not have to use the default pubsub.pub instance"=None):

   
def real_decorator(function):

        _annotations
= function.__annotations__.copy()
       
function.__annotations__.clear()
       
@functools.wraps(function)

       
def wrapper(firstname=None, lastname=None):
           
return function(firstname=firstname, lastname=lastname)

       
if publisher is None:
           
from pubsub import pub
       
else:
            pub
= publisher
        pub
.subscribe(wrapper, topic)
       
function.__annotations__.update(_annotations)
       
return wrapper
   
return real_decorator


My brain fails on how to decorate pubsub listeners with annotations and dynamically provided kwargs.

With the decorator recipe I gave this would fail:

@subscribe('log.user.deletes') # SUCCESS correct function signature
def user_deletes(data):
    do_delete
(data)


@subscribe('db.user.add')
def db_user_add(firstname, lastname):  # FAIL wrong function signature
    do_stuff
()


But this wouldn't fail. it would work.

@subscribe('log.user.deletes') # SUCCESS correct function signature
def user_deletes(data):
    do_delete
(data)


@db_subscribe('db.user.add') # different decorator
def db_user_add(firstname, lastname):  # SUCCESS correct function signature
    do_stuff
()




I blabbed on about this mostly for those who are not familiar with pubsub and partially hoping someone could figure out how to update that decorator to work with listener function signatures more dynamically like pubsub does now with pub.subscribe().


DevPlayer

unread,
Dec 11, 2015, 3:05:48 AM12/11/15
to wxPython-users, pypu...@googlegroups.com
REPOST WITH CORRECTIONS:

Yes, I think. The subscribe decorator I posted above is specific to listener functions with "data=" as a keyword. So this:

        @subscribe('server')
       
def audit(data:'Capture only metadata of action.'=None):
            log_action
(data)

       
@subscribe('server.process')
       
def process(data:'State change request.'=None):
            do_stuff
(data)

        pub
.sendMessage('server.process', data=a_request)


is precisely equivalent to this:

        def audit(data=None):
            log_action
(data)
        pub
.subscribe(audit, "server")


       
def process(data=None):
            do_stuff
(data)
        pub
.subscribe(process, "server.process")


        pub
.sendMessage('server.process', data=a_request) # data= is an explicit keyword


So to use a listener with a different signature, say

def some_listener(firstname=None, lastname=None):
    do_stuff
(firstname, lastname)
pub
.subscribe(some_listener, 'database.record.add')


You would have to create a new decorator:


def db_subscribe(topic, publisher:"You do not have to use the default pubsub.pub instance"=None):


   
def real_decorator(function):
        _annotations
= function.__annotations__.copy()
       
function.__annotations__.clear()

       
@functools.wraps(function)

       
def wrapper(firstname=None, lastname=None):
           
return function(firstname=firstname, lastname=lastname)

       
if publisher is None:
           
from pubsub import pub
       
else:
            pub
= publisher
        pub
.subscribe(wrapper, topic)
       
function.__annotations__.update(_annotations)
       
return wrapper
   
return real_decorator


My brain fails on how to decorate pubsub listeners with annotations and dynamically provided kwargs.
With the decorator recipe I gave this would fail:


@subscribe('log.user.deletes') # SUCCESS correct function signature
def user_deletes(data=None):
    do_delete
(data)

@subscribe('db.user.add')
def db_user_add(firstname=None, lastname=None):  # FAIL wrong function signature
    do_stuff
()


But this wouldn't fail. it would work.


@subscribe('log.user.deletes') # SUCCESS correct function signature
def user_deletes(data=None):

    do_delete
(data)


@db_subscribe('db.user.add') # different decorator
def db_user_add(firstname=None, lastname=None):  # SUCCESS correct function signature
    do_stuff
()

Reply all
Reply to author
Forward
0 new messages