Print python-socketio events infos using decorators - python

I would like to improve the socketio.event decorator to make it print the event fired and its parameters.
I have a Manager class which has a self.sio: socketio.Server attribute. I try to define a new decorator as a Manager method such as it returns a function decorated by self.sio.eventand which also prints its data. I have tried this solution, but it does not work :
def event(self, func):
#self.sio.event
def wrapper(*args, **kwargs):
print(f'[{func.__name__}] : {args} {kwargs}')
func(*args, **kwargs)
return wrapper
Any recommendation ?

I think something like this should work for you:
def event(self, func):
def wrapper(*args, **kwargs):
print(f'[{func.__name__}] : {args} {kwargs}')
return func(*args, **kwargs)
return self.sio.on(func.__name__, wrapper)
You can't really use the #sio.event on the wrapper, because then the event that will be configured is going to be named wrapper. My solution uses the #sio.on decorator, which accepts the event name explicitly.

Related

How create decorator inside a class?

I want create a simple decorator function that get in input a func e add some methode before.
Here an example:
class A:
def beforeWriteOracle(func):
def wrapper(self, func):
self.dbOracle.truncateTable(self.oracle_final_table)
func(self.mylist)
return wrapper
#beforeWriteOracle
def writeDataToOracle(self, writeDataToOracleRequestList):
return self.executeMethod(self.writeDataToOracleImpl, writeDataToOracleRequestList, threaded, False)
self.writeDataToOracle(self, writeDataList)
but i have error:
"beforeWriteOracle() missing 1 required positional argument: 'func'"
How use correctly decorator for my case?
thanks
You don't need to (or want to) pass func as an argument to the wrapper; the wrapper should take the same arguments that function you are decorating takes, since it's going to "become" that function.
func itself is available as a non-local variable inside wrapper, which
is a closure: it retains the value passed to beforeWriteOracle even after beforeWriteOracle exits.
def beforeWriteOracle(func):
def wrapper(self, *args, **kwargs):
self.dbOracle.truncateTable(self.oracle_final_table)
func(self, *args, **kwargs)
return wrapper
#beforeWriteOracle
def writeDataToOracle(self, writeDataToOracleRequestList):
return self.executeMethod(self.writeDataToOracleImpl, writeDataToOracleRequestList, threaded, False)

How to decorate a method that belongs to another class in my current class?

We have an internal library that returns a kafka consumer object. What I want to do is implement a decorator which uses this object to start the consumer (the method to start the consumer also comes from that internal library). The aim is to import this decorator and use it for any method which needs the consumer to be started before execution. Here's a snippet from a file say, utils.py:
from internal_library import KafkaMsgConsumer
class KafkaMessageConsumer(object):
def __init__(self, topic_name, kafka_host, kafka_port='some_port'):
self.topic_name = topic_name
self.kafka_host = kafka_host
self.kafka_port = kafka_port
self.consumer = KafkaMsgConsumer(kafka_host_name=(str(self.kafka_host) + ":" + str(self.kafka_port)),
topic_name=self.topic_name)
def consumer_required(self):
def decorator():
consumer = self.consumer.start_consumer()
return consumer
return decorator
Then there's my main script where I want to use this decorator. Contents from script.py:
mes = KafkaMessageConsumer(topic_name='some_topic', kafka_host='some_host',
kafka_port='some_port')
#mes.consumer_required()
def post_act():
''' some processing goes here before which I require the consumer to be started'''
import functools
def consumer_required(self):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
self.consumer.start_consumer()
return func(*args, **kwargs)
return wrapper
return decorator
Your decorator needs to accept the decorated function as an argument and you need to return a corresponding wrapper:
from functools import wraps
def consumer_required(self):
def decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
self.consumer.start_consumer()
return func(*args, **kwargs)
return wrapper
return decorator
Alternatively you can also inject the consumer into the decorated function via
kwargs.update(consumer=self.consumer.start_consumer())

Wrapping a decorator, with arguments

I'm trying to replace the marshal_with decorator from flask-restful with a decorator that does something before calling marshal_with. My approach is to try to implement a new decorator that wraps marshal_with.
My code looks like:
from flask.ext.restful import marshal_with as restful_marshal_with
def marshal_with(fields, envelope=None):
def wrapper(f):
print("Do something with fields and envelope")
#wraps(f)
def inner(*args, **kwargs):
restful_marshal_with(f(*args, **kwargs))
return inner
return wrapper
Unfortunately this seems to break things... no error messages but my API returns a null response when it shouldn't be. Any insights on what I'm doing wrong?
I don't know the specifics of marshal_with, but it's entirely possible to use multiple decorators on a single function. For instance:
def decorator_one(func):
def inner(*args, **kwargs):
print("I'm decorator one")
func(*args, **kwargs)
return inner
def decorator_two(text):
def wrapper(func):
def inner(*args, **kwargs):
print(text)
func(*args, **kwargs)
return inner
return wrapper
#decorator_one
#decorator_two("I'm decorator two")
def some_function(a, b):
print(a, b, a+b)
some_function(4, 7)
The output this gives is:
I'm decorator one
I'm decorator two
4 7 11
You can modify this little script by adding print statements after each inner function call to see the exact flow control between each decorator as well.
I was doing a couple things wrong here, first, failing to return the output of restful_marshal_with as jonrsharpe pointed out, secondly, failing to understand a decorator written as a class instead of a function, and how to properly pass values to it. The correct code ended up being:
def marshal_with(fields, envelope=None):
def wrapper(f):
print("Do something with fields and envelope")
#wraps(f)
def inner(*args, **kwargs):
rmw = restful_marshal_with(fields, envelope)
return rmw(f)(*args, **kwargs)
return inner
return wrapper
As you can see, in addition to not returning rmw(), I needed to properly initialize the request_marshal_with class before calling it. Finally, it is important to remember that decorators return functions, therefore the arguments of the original function should be passed to the return value of rmw(f), hence the statement return rmw(f)(*args, **kwargs). This is perhaps more apparent if you take a look at the flask_restful.marshal_with code here.

Why are these decorators called from outermost to innermost, not the other way round?

I have the following method inside a tornado.websocket.WebSocketHandler subclass:
#authenticate_user_async
#gen.coroutine
def on_message(self, data):
"""
Callback when new message received via the socket.
"""
# do stuff...
The #authenticate_user_async decorator is below:
def authenticate_user_async(f):
"""
Authentication decorator based on http://tornadogists.org/5251927/
Authenticates the user's credentials and sets self.current_user if valid
"""
#functools.wraps(f)
#gen.coroutine
def wrapper(self, *args, **kwargs):
# do stuff...
f(self, *args, **kwargs)
return wrapper
I thought that the innermost decorator was called first, so would have expected to need them to be given:
#gen.coroutine
#authenticate_user_async
def on_message(self, data):
...
but only the first version works and the one above is never called if the yield keyword is inside the on_message() method.
Why is it necessary to use the first version and not the second?
(I think I gave you this bug in my answer to your previous question, sorry.)
The line in authenticate_user_async where it calls f needs to be changed. Change this:
f(self, *args, **kwargs)
To this:
yield f(self, *args, **kwargs)
Now swap the order of decoration for on_message:
#authenticate_user_async
#gen.coroutine
def on_message(self, data):
In authenticate_user_async, "f" is your on_message. These two fixes ensure that "f" is a coroutine and that it gets run to completion.

No Argument Decorator Decorator?

I wrote a decorator that looks like this
def login_required_message(*args, **kwargs):
kwargs.setdefault('message', "You must be logged in to do that.")
return _user_passes_test_message(lambda u: u.is_authenticated(), *args, **kwargs)
But when I try to use it without the () at the end, it fails, unless I rewrite it like this:
def login_required_message(function=None, *args, **kwargs):
kwargs.setdefault('message', "You must be logged in to do that.")
decorator = _user_passes_test_message(lambda u: u.is_authenticated(), *args, **kwargs)
if function: return decorator(function)
else: return decorator
Then the () is optional. So, how do I encapsulate this "optional" functionality into a decorator so that I can decorate my decorators to allow no arguments?
I actually recently wrote a blog post about this - at least, I think it addresses what you're trying to do. For posterity, here's what I came up with:
def opt_arguments(func):
def meta_wrapper(*args, **kwargs):
if len(args) == 1 and callable(args[0]):
return func(args[0])
else:
def meta_func(inner_func):
return func(inner_func, *args, **kwargs)
return meta_func
return meta_wrapper
By the way, in the process of trying to figure out how to do it, I came to the conclusion that this is one of those things that is almost always more complication than necessary ;-)
This answer is inspired by recipe 9.6 of the Python Cookbook
def mydecorator_with_opt_args(func=None, arg1="arg1-default", arg2="arg2-default"):
if func is None:
return partial(mydecorator_with_opt_args, arg1=arg1, arg2=arg2)
#wraps(func)
def wrapper(*args, **kwargs):
print(f"Do something with {arg1} and {arg2}")
return func(*args, **kwargs)
return wrapper
To use it :
#mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set")
def passer():
pass
passer()
will output
"Do something with arg1-set and arg2-set"
#mydecorator_with_opt_args
def passer2():
pass
passer2()
will output
"Do something with arg1-default and arg2-default"
To understand what happens if you specify no args (passer2):
Putting a decorator is the same as writing passer2 = mydecorator_with_opt_args(passer2) which means the if func is None is ignored and you apply the decorator with the default arguments
However if you specify args (passer), Putting a decorator is the same as writing passer = mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set")(passer)
mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set") returns partial(mydecorator_with_opt_args, arg1="arg1-set", arg2="arg2-set") and then partial(mydecorator_with_opt_args, arg1="arg1-set", arg2="arg2-set")(passer) is equivalent to mydecorator_with_opt_args(func=passer, arg1="arg1-set", arg2="arg2-set")
Note that you need to specify the keyword arguments.

Categories