I'm new to python and GAE and I thought python will act as any other OO language, but apparently not. How does __init__(self): function gives me different results in the following code?
class BaseHandler(webapp.RequestHandler):
#property
def current_user(self):
if not hasattr(self, "_current_user"):
self._current_user = None
cookie = facebook.get_user_from_cookie(self.request.cookies, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET)
user = User.get_by_key_name(cookie["uid"])
return self._current_user
class SubmitHandler(BaseHandler):
template_values = dict(facebook_app_id=FACEBOOK_APP_ID)
def __init__(self):
#throws error : AttributeError: 'SubmitHandler' object has no attribute 'request'
self.template_values['current_user'] = self.current_user
def get(self):
#this one function is error free
self.template_values['current_user'] = self.current_user
How do I access the class' parent property?
If you look at your SubmitHandler class you'll notice that it indeed does not have a request attribute -- at least, none you set, and none you give the parent class a chance to set. Perhaps what you need to do is call the parentclass __init__ method before you try to access self.current_user.
As a side note, you should realize that the template_values dict you define inside the SubmitHandler class there is a class attribute, and thus shared between all instances of the class. Since you assign it something instance-specific in your __init__, you probably mean for it to be an instance attribute instead. Assign it to self.template_values in your __init__ method.
There's nothing particularly different about Python's object inheritance.
By defining __init__, you have told Python that this is all that needs to be done to initialize the object. You're therefore denying it the chance to run the superclass's initialization code. You need to call super:
def __init__(self, *args, **kwargs):
super(SubmitHandler, self).__init__(*args, **kwargs)
self.template_values['current_user'] = self.current_user
This might however not solve your problem - you're failing to take into account the possibility that self.request is initialized at another point in the program, which is why it works by the time get is called.
self.request and self.response are not set by the class constructor in webapp. They're set later, when the framework calls the handler's initialize method. You can override this, just make sure you call the parent class's initialize before doing anything else.
Related
Background
The Django LiveServerTestCase class has a live_server_url method with a #classproperty decorator. (django.utils.functional.classproperty.) The class starts its test server before any tests run, and thus knows the test server's URL before any tests run.
I have a similar MyTestCase class that has a live_server_url #property. It starts a new test server before each test, and thus its live_server_url property can't be a #classproperty because it doesn't know its port until it is instantiated.
To make the API for both consistent, so that all the test utility functions etc. in the code base can be used with both classes, the tests could be written to never reference live_server_url in setUpClass(), before all the tests run. But this would slow down many tests.
Instead, I want MyTestCase.live_server_url to raise a helpful error if it is referenced from the class object rather than an instance object.
Since MyTestCase.live_server_url is a #property, MyTestCase().live_server_url returns a string, but MyTestCase.live_server_url returns a property object. This causes cryptic errors like "TypeError: unsupported operand type(s) for +: 'property' and 'str'".
Actual question
If I could define a #classproperty and #property on the same class, then I would define a MyTestCase.live_server_url #classproperty that raises an error with a helpful message like "live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class".
But when you define 2 methods with the same name in a Python class then the earlier one gets discarded, so I can't do that.
How can I make the behavior of MyTestCase.live_server_url different depending on whether it is called on the class or on an instance?
One option might be to use a metaclass. (Example)
But since I read that metaclasses should usually be avoided, here is what I did:
from django.utils.functional import classproperty
class MyTestCase(TransactionTestCase):
def __getattribute__(self, attr: str):
if attr == 'live_server_url':
return "http://%s:%s" % (self.host, self._port)
return super().__getattribute__(attr)
#classproperty
def live_server_url(self):
raise ValueError('live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class')
MyTestCase().live_server_url calls __getattribute__().
MyTestCase.live_server_url calls the #classproperty definition of live_server_url.
Just subclass property and implement __get__ to raise on error if it is not accessed through an instance :
class OnlyInstanceProperty(property):
def __get__(self, obj, objtype=None):
if obj is None:
raise ValueError('live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class')
return super().__get__(obj, objtype)
Perhaps you can come up with a better name for it. But in any case, you can then use it like so:
class MyTestClass:
def __init__(self):
self.host = "foo"
self._port = "bar"
#OnlyInstanceProperty
def live_server_url(self):
return "http://%s:%s" % (self.host, self._port)
Then:
print(MyTestClass.live_server_url) # http://foo:bar
print(MyTestClass.live_server_url) # ValueError: live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class
And it will work like any other property, so you can use live_server_url.setter and live_server_url.deleter
If you are unfamiliar with __get__, read this HOWTO and it should tell you all you need to know about descriptors, which are an intermediate/advanced Python technique. The descriptor protocol is behind a lot of seemingly magical behavior, and that HOWTO shows you how various things like classmethod, staticmethod, property could be implemented in pure python using the descriptor protocol.
Note, you don't have to inherit from property, a simple bespoke (albeit tightly coupled) approach could be something like:
class LiveServerUrl:
def __get__(self, obj, objtype=None):
if obj is None:
raise ValueError('live_server_url can only be called on an instance of MyTestCase, not on the MyTestCase class')
return "http://%s:%s" % (obj.host, obj._port)
class MyTestClass:
live_server_url = LiveServerUrl()
def __init__(self):
self.host = "foo"
self._port = "bar"
I am working with locust and I am working in mimicking the behavior of a user. However I am getting trouble accessing the parent class variable. Any idea how I can pass it?
class User(TaskSet):
some_user = ''
def on_start(self):
self.get_user()
def get_user(self):
some_user = self.client.get...#gets user
#task
class UpdatingUser(TaskSet):
def updating(self):
path = "/posts/" + User.some_user
By the time I get to User.some_user I never have the user.
You've not provided all of the code, but the problem may be that get_user() is setting some_user as an instance attribute somewhere, as in self.some_user = foo.
This will only set some_user for that specific instance of User however (so for Bob, Lisa, Beto, User53, etc.), but not for the User class itself. When accessing some_user with self, as in self.some_user, you set it for the specific instance that's executing those statements, not the class. In updating() you're accessing the class attribute User.some_user, not a specific instance attribute like usr53.some_user. In order to update the class attribute, invariant by default for all instances of User, you ought to be setting it with User.some_user = foo in get_user().
Right now in path = "/posts/" + User.some_user, it's trying to access the class attribute which may never have been set. Because nested classes like UpdatingUser can't access the instances of the nesting class (User) that they're called from, UpdatingUser won't be able to access any some_user set with self or any other instance attributes of User. So the solution would be to have get_user() set the class attribute instead of the instance attribute as described in the previous paragraph.
This answer is a bit late but, if anyone has this issue, the TaskSet has a parent property, which can be used to access the parent's instance variables. The following is what I used for a basic one-time login:
class UserBehaviour(TaskSet):
def on_start(self):
self.token = self.login()
self.headers = {'Authorization': 'Bearer ' + self.token}
def login(self):
with self.client.post("/login", catch_response = True) as response:
return response.json()['token']
#task
class UserTask1(TaskSet):
#task
def get_data(self):
self.client.get("/data", headers = self.parent.headers)
class WebsiteUser(HttpLocust):
task_set = UserBehaviour
I have subclassed django-haystack's FacetedSearchView, and want to have an action take place when a specific GET parameter is present in the view's URL. I know exactly how to do this if I had a function-based view, but am at a complete loss when using a subclass of a subclass.
The request variable should be available somehow. I assume I need to call it up from some kind of super() call, maybe in an __init__() override? I've tried the following:
def __init__(self, *args, **kwargs):
super(MySearch, self).__init__(*args, **kwargs)
if self.request.GET.get("date_facet", ""):
do_something()
But it tells me that the returned request object is None, though by the URL it clearly shouldn't be.
Any thoughts?
Turns out, the request variable is accessible, it just has to be through self, which means you can only access it through a function common to the parent class. __init__ wasn't working, because request isn't defined until later in the parent class. To get things to work I ended up overwriting the get_results() class (originally just return self.form.search()), in a way similar to the following:
class Foo(ParentClass):
def get_results(self):
if 'date_facet' in self.request.GET:
year = int(self.request.GET['date_facet'])
return self.form.search().filter(some_filter_function)
return self.form.search()
I have an Abstract Base Class and subclasses defined as follows (Python 2.7):
import abc
import MyDatabaseModule
class _DbObject(object):
__metaclass__ = abc.ABCMeta
def _GetObjectType(self):
raise NotImplementedError, "Please override in the derived class"
ObjectType = abc.abstractproperty(_GetObjectType, None)
class Entry(_DbObject):
_objectTypeID = 'ENTRY'
def _GetObjectType(self):
return MyDatabaseModule.DoesSomethingWith(self._objectTypeID)
ObjectType = property(_GetObjectType, None)
This works fine, meaning that the base class _DbObject cannot be instantiated because it has only an abstract version of the property getter method.
try:
dbObject = _DbObject()
print "dbObject.ObjectType: " + dbObject.ObjectType
except Exception, err:
print 'ERROR:', str(err)
Now I can do:
entry = Entry()
print entry.ObjectType
to get access to the ObjectType property. However, what I would like to be able to do is just:
print Entry.ObjectType
However, wherever I try to insert #classmethod, I get the error classmethod object is not callabale.
So, the magic for the way "property" works in Python is implemented using the descriptor protocol - property itself if a powerful built-in that provides a descriptor that works well for instances, not classes as you had seen.
So, you need a "class property" - the property built-in can't give you that, but the descriptor protocol can. What the descriptor protocol says is that whenever an attribute is retrieved from the class, if it is an object with a __get__ method, that method is called with "self, instance, owner" - and if it is retrieved from the class, instead of from an instance, the "instance" parameter is set to None.
BTW, as stated by #Constantinius, this does not have to do with the ABC's at all, just with you wanting a "class property".
class classproperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func(owner)
class Entry(_DbObject):
_objectTypeID = 'ENTRY'
def _GetObjectType(cls):
return MyDatabaseModule.DoesSomethingWith(cls._objectTypeID)
ObjectType = classproperty(_GetObjectType, None)
The problem is not your ABC but the simple fact, that there is no such thing as a classproperty in python, you have to create it on your own. Actually there is a good question + answer on SO about that. It actually should be no problem using it with your ABC aswell.
I am not very experienced with class inheritance. Please help me and have a look at the code below:
class Handle(STAFHandle):
def __init__(self, HandleName):
handle = STAFHandle.__init__(self, HandleName)
self.initLogger(handle)
def initLogger(self, handle):
self.logger = Logging(handle, 'Test')
handle = Handle('test')
handle.logger.info('test')
it says submit method is not defined:
result = handle.submit(system, service, logRequest)
AttributeError: 'NoneType' object has no attribute 'submit'
but if I change it to:
class Handle(STAFHandle):
def __init__(self, HandleName):
handle = STAFHandle.__init__(self, HandleName)
def initLogger(self, handle):
self.logger = Logging(handle, 'Test')
handle = Handle('test')
handle.initLogger(handle)
handle.logger.info('test')
it works. Why there is a difference? Thanks a lot!!
Cheers,
Zhe
STAFHandle.__init__ returns None. You probably want:
class Handle(STAFHandle):
def __init__(self, handle_name):
super(Handle, self).__init__(handle_name)
self.initLogger()
def initLogger(self):
self.logger = Logging(self, 'Test')
handle = Handle('test')
Remember that __init__ methods take as their first argument an object, and modify that object. So when you call super(Handle, self).__init__(handleName) you are changing the properties of self instead of returning a new object. The difference between your two examples is that the variable handle in the two calls to initLogger refers to different things.
Notice that I have replaced the explicit STAFHandle.__init__ call with a super proxy; this is equivalent in this case but allows for more flexibility, since you can now change the inheritance of the class without breaking its __init__.
I've also changed HandleName to handle_name to conform with Python conventions (CamelCase refers to classes).