I want to use python-twitter, but extend the Status class to add a few new methods and attributes. What's the pythonic way to do this?
At the moment, I have functions which add attributes and new functionality to a Status, e.g.
process_status(status):
status.datetime = ...
status.phrase = ...
prettyprint_status(status):
# do something...
Naturally, I'd just like to add the extra methods to the Status constructor. I found a stackoverflow question discussing this which proposed making a new module, ext-twitter, which contains new implementations of each class, like follows:
# ext_twitter.py
import twitter
class Api(twitter.Api):
pass
class Status(twitter.Status):
def __init__(self, *args):
twitter.Status.__init__(self, *args)
self.args = args
self.time = parseTime(self.created_at)
self.phrase = ...
def prettyprint(self):
# something
However, this doesn't work since the Status classes are generated by the Twitter API object, and here ext-twitter.Api() calls python-twitter.Api() which has no reference to my extended Status class.
Any way to add my functionality to the python-twitter module without forking it and making my own version?
Try this:
# patch_twitter.py
import twitter
TwitterStatus = twitter.Status
class Status(TwitterStatus):
def __init__(self, *args, **kwargs):
TwitterStatus.__init__(self, *args, **kwargs)
self.args = args
self.time = parseTime(self.created_at)
self.phrase = ...
def prettyprint(self):
# something
twitter.Status = Status
# use api
This should work, at least for some values of "work". If there's anything else that captures a reference to the class object originally stored in twitter.Status, then those references won't be updated. That doesn't happen too often, but it could be a source of subtle bugs.
You'll need to import this immediately after importing twitter.py and before doing anything else.
Instead of using inheritance, you can use composition to add functionality to the Status object:
# ext_twitter.py
import twitter
class Api(twitter.Api):
def method_that_returns_status(self, *args, **kwargs):
status = super(Api, self).methot_that_returns_status(*args, **kwargs)
# wrap the original status with your custom status
return Status(status)
class Status(object):
def __init__(self, status):
self._internal = status
def __getattr__(self, attr):
return getattr(self._internal, attr)
def __setattr__(self, attr, value):
if attr == '_internal':
super(Status, self).__setattr__(attr, value)
else:
setattr(self._internal, attr, value)
def prettyprint(self):
# something
Related
I found a lot of questions regarding decorators and classes, and access to the class instance that is being decorated. As far as I could understand, the decorator generator can not access the object instance because, at that time, no object was yet created.
Nevertheless, it seems that if a decorator uses a class reference (I mean, receives cls), then I am able to access to some class field:
class Foo:
_tags = {}
#classmethod
def _tag(cls, tag_name):
def decorator(func):
def tmp(*args, **kwargs):
return func(*args, **kwargs)
cls._tags[tag_name] = func.__name__
return tmp
return decorator
This works as a charm for subclasses, where I can write:
#Foo._tag("something")
def somethingelse():
pass
But this stores the information in the father class (as expected), as we are talking about class variables.
I wonder if there is a way to have a similar behavior, but guaranteeing that each subclass has its own instance of the _tags property.
I can foresee some workarounds, like parsing the source code, or inspecting the functions, during __init__. But I was looking for a simpler way, if there is such a way.
Edit 1: to make it clearer, I want to decorate methods from the subclass. Thus, this is not a possibility:
Class Bar (Foo):
#Bar._tag("method")
def method(self):
pass
as the processor/compiler did not complete the creation of the class yet. Otherwise, the solution proposed of a two level dictionary with class/method would work good enough for me.
WORKAROUND
My decorator:
#classmethod
def TAG(cls, name):
def decorator(func):
def tmp(*args, **kwargs):
return func(*args, **kwargs)
tmp._has_alias = name
return tmp
return decorator
During __init__, I get this info, and store:
self._tags = {getattr(self, name).has_alias: name for name in dir(self) if callable(getattr(self, name)) and hasattr(getattr(self, name), "_has_alias")}
A quick workaround could be to treat _tags as a dictionary of dictionaries, with the top level key being the name of the subclass, and its associated dictionary containing the callables wrapped by its inherited version of _tag:
from collections import defaultdict
class Foo:
_tags = defaultdict(dict)
#classmethod
def _tag(cls, tag_name):
def decorator(func):
def tmp(*args, **kwargs):
return func(*args, **kwargs)
cls._tags[cls.__name__][tag_name] = func.__name__ #storing tag_name under the name of the subclass
return tmp
return decorator
Now, when creating subclasses and wrapping functions, _tags will store the values under the subclass name by which _tag was called:
class A(Foo):
pass
class B(Foo):
pass
#A._tag('test_a')
def a():
pass
#B._tag('test_b')
def b():
pass
print(dict(Foo._tags))
Output:
{'A': {'test_a': 'a'}, 'B': {'test_b': 'b'}}
I'm trying to use caching for my program, and I want to let the user choose how long the program should cache for.
class Parent:
"""The Parent Class, Do not import this."""
def __init__(self, apiKey, cacheTime=300):
self.key = apiKey
self.time = cacheTime
class invalidApiKey(Exception):
"""The invalidApiKey error."""
def __init__(self, *args):
self.message = args[0]
def __str__(self):
return "Invalid API Key (Register at https://url.com/register)- {0} ".format(
self.message
)
class Child(Parent):
#cached(cache=TTLCache(maxsize=1024, ttl=Parent.time))
async def requestStats(self):
This is what my code is like, I've tried to use global variables, env variables, my own decorator -
class Parent:
def cache(f):
def wrapper(*args):
return cached(cache=TTLCache(maxsize=1024, ttl=args[0].time))
return wrapper
class Child(Parent):
#Parent.cache
async def requestStats(self):
But I got a ValueError: a coroutine was expected, got <function cached.<locals>.decorator at 0x7f8b5d1e3c10>, I'm unsure how to proceed now.
TL:DR - I want to use a self variable from a parent class to use as a parameter for a function in my child class. (If it makes a difference, I'm using this library for caching)
I might be wrong, but it seems to me that you're overcomplicating it. You seem to want a decorating that is parametrized at instantiation and not definition. If you want to use cached, then you could do something like this:
from cachetools import TTLCache, cached
class Foo:
def __init__(self, time=600):
self.bar = cached(TTLCache(maxsize=1024, ttl=time))(self.bar)
def bar(self):
...
Here the undecorated method is created at definition, and then decorated with the respective parameters - time - at instantiation. But that's a bit awkward, and I think the better way is to use cachedmethod:
from cachetools import TTLCache, cachedmethod
class Foo:
def __init__(self, time=600):
self.cache = TTLCache(maxsize=1024, ttl=time)
#cachedmethod(lambda self: self.cache)
def bar(self):
...
Here the decoration happens at definition, but with a cache-function that refers to a cache object that is set up at instantiation - self.cache - with the individual parameteres.
But: Are you sure cachetools can handle asyncio?
I decorated some methods with #bot_thinking, which stores some information about the decorated method in the functions attribute.
One piece of information is 'class_name', but my program needs the class type as a variable, e.g. RandomBot. I would like to get this class.
Here is some sample code:
class DepthPrunedMinimaxAgent(Agent):
#bot_thinking(associated_name="minimax profondeur")
def select_move(self, game_state: GameState):
Above is the decorated part of the code.
The decorator:
functions = {}
def bot_thinking(associated_name, active=True):
def _(func):
if active:
class_name = func.__qualname__.rsplit('.')[-2]
import sys
# class_name_2=getattr(sys.modules[__name__], class_name)
# module=importlib.import_module('sources.agent')
functions[associated_name] = (associated_name, class_name,
globals()[class_name], func)
else:
functions.pop(associated_name)
return _
bot_thinking isn't a real decorator, it's a decorator factory.
From the func function, I get the class_name, but I can't use the accepted answer by #m.kocikowski, to find the correct class because this class is decorated, so it already imports the annotation module, so importing from the module of the annotation the annotated module would result in a cyclic import, which python does not seem to permit.
Do you see a method to get the class from its name?
ps:
ps:
to be clearer : the annotation part of the code need an import to the annotated classes(to retrieve the class from its name), which also need an importation of the annotation (for the annotation to work).
You can do what you want if you use a descriptor class, rather than a function, as the decorator, at least if you're using Python 3.6 or newer. That's because there's a new method added to the descriptor protocol, __set_name__. It gets called when the descriptor object is saved as a class variable. While most descriptors will use it to record the name they're being saved as, you can use it to get the class you're in.
You do need to make your decorator object wrap the real function (implementing calling and descriptor lookup methods), rather than being able to return the unmodified function you were decorating. Here's my attempt at a quick and dirty implementation. I don't really understand what you're doing with functions, so I may not have put the right data in it, but it should be close enough to get the idea across (owner is the class the method stored in).
functions = {}
def bot_thinking(associated_name, active=True):
class decorator:
def __init__(self, func):
self.func = func
def __set_name__(self, owner, name):
if active:
functions[associated_name] = (associated_name, owner.__name__,
owner, self.func)
else:
functions.pop(associated_name)
def __get__(self, obj, owner):
return self.func.__get__(obj, owner)
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
return decorator
The problem is the class hasn't been defined yet when the bot_thinking() decorator factory (and decorator itself) are executing. The only workaround I can think of would be to patch things up after the class is defined, as illustrated below:
from pprint import pprint, pformat
functions = {}
def bot_thinking(associated_name, active=True):
def _(func):
if active:
class_name = func.__qualname__.split(".")[-2]
functions[associated_name] = (associated_name, class_name, class_name, func)
else:
functions.pop(associated_name, None)
return func # Decorators must return a callable.
return _
class Agent: pass
class GameState: pass
class DepthPrunedMinimaxAgent(Agent):
#bot_thinking(associated_name="minimax profondeur")
def select_move(self, game_state: GameState):
pass
# After class is defined, update data put into functions dictionary.
for associated_name, info in functions.items():
functions[associated_name] = (info[0], info[1], globals()[info[2]], info[3])
pprint(functions)
Output:
{'minimax profondeur': ('minimax profondeur',
'DepthPrunedMinimaxAgent',
<class '__main__.DepthPrunedMinimaxAgent'>,
<function DepthPrunedMinimaxAgent.select_move at 0x00F158A0>)}
I'm building a rate-limiting decorator in flask using redis stores that will recognize different limits on different endpoints. (I realize there are a number of rate-limiting decorators out there, but my use case is different enough that it made sense to roll my own.)
Basically the issue I'm having is ensuring that the keys I store in redis are class-specific. I'm using the blueprint pattern in flask, which basically works like this:
class SomeEndpoint(MethodView):
def get(self):
# Respond to get request
def post(self):
# Respond to post request
The issue here is that I want to be able to rate limit the post method of these classes without adding any additional naming conventions. In my mind the best way to do this would be something like this:
class SomeEndpoint(MethodView):
#RateLimit # Access SomeEndpoint class name
def post(self):
# Some response
but within the decorator, only the post function is in scope. How would I get back to the SomeEndpoint class given the post function? This is the basic layout of the decorator. That might be confusing, so here's a more concrete example of the decorator.
class RateLimit(object):
"""
The base decorator for app-specific rate-limiting.
"""
def __call__(self, f):
def endpoint(*args, **kwargs):
print class_backtrack(f) # Should print SomeEnpoint
return f(*args, **kwargs)
return endpoint
basically looking for what that class_backtrack function looks like. I've looked through the inspect module, but I haven't found anything that seems to accomplish this.
You can decorate the entire class instead of just the methods:
def wrap(Class, method):
def wrapper(self, *args, **kwargs):
print Class
return method(self, *args, **kwargs)
return method.__class__(wrapper, None, Class)
def rate_limit(*methods):
def decorator(Class):
for method_name in methods:
method = getattr(Class, method_name)
setattr(Class, method_name, wrap(Class, method))
return Class
return decorator
#rate_limit('post')
class SomeEndpoint(object):
def post(self):
pass
class Subclass(SomeEndpoint):
pass
a = Subclass()
a.post()
# prints <class 'SomeEndpoint'>
I'd like to do something like this:
class Basehandler(webapp.RequestHandler):
def __init__(self):
if checkforspecialcase: #check something that always needs to be handled
return SpecialCaseHandler.get()
class NormalHandler(Basehandler):
def get(self):
print 'hello world'
return
class SpecialCaseHandler(Basehandler):
def get(self):
print 'hello special world'
return
The idea is that no matter what handler is initially called, if a certain case is met, we basically switch to another handler.
I'm pretty new to python, so I'm not sure whether what I'm trying to do is possible. Or whether this is the best approach. What I'm really trying to do is make sure to show someone the complete-your-profile page if they've started the registration process but haven't completed it... no matter what request they're making. So the "checkforspecialcase" looks at their sessions and checks for incomplete info.
To keep things DRY, use the Template Method pattern
class BaseHandler(webapp.RequestHandler):
def DoGet(self, *args):
''' defined in derived classes, actual per-handler get() logic'''
pass
def get(self, *args):
# don't get caught in endless redirects!
if specialCase and not self.request.path.startswith('/special'):
self.redirect('/special')
else:
self.DoGet(*args)
class NormalHandler(BaseHandler):
def DoGet(self, *args):
# normal stuff
class SpecialHandler(BaseHandler):
def DoGet(self, *args):
# SPECIAL stuff
WSGIApplication routes incoming requests based on the URL. For example,
application = webapp.WSGIApplication(
[('/special-case', SpecialCaseHandler)])
When checkforspecialcase passes, you can use self.redirect('/special-case').
Your Basehandler could just implement a get() that checks for the special case and either redirects or calls self.view(), and each handler could implement view() (or whatever you'd like to call it) rather than get().
I'm not really into writing a class for each of my handlers, or using inheritance so conspicuously, so I'd recommend rolling decorators like these:
routes = []
def get (route):
def makeHandler (handle, *args, **kwargs):
class Handler (webapp.RequestHandler):
def get (self, *args, **kwargs):
shouldRedirectToCompleteProfile = # do your test
if shouldRedirectToCompleteProfile:
self.redirect('/special-case')
else:
handle(self, *args, **kwargs)
routes.append((route, Handler))
return Handler
return makeHandler
def post (route):
def makeHandler (handle, *args, **kwargs):
class Handler (webapp.RequestHandler):
def post (self, *args, **kwargs):
handle(self, *args, **kwargs)
routes.append((route, Handler))
return Handler
return makeHandler
#get('/')
def home (ctx):
# <...>
#get('/whatever/(.*)/(.*)')
def whatever (ctx, whatever0, whatever1):
# <...>
#post('/submit')
def submit (ctx):
# <...>
application = webapp.WSGIApplication(routes)