2

I was trying to change the os.environ dict to simulate a logged in user on Google App Engine, as documented at https://stackoverflow.com/a/6230083/1241454.

from google.appengine.api import users
import webapp2
import os

class TestPage(webapp2.RequestHandler):
    def get(self):
        os.environ['USER_EMAIL'] = 'a@b.c'
        user = users.get_current_user()
        self.response.out.write(user.email())

This doesn't work. get_current_user() returns None in the above example for me, at least when running on the dev server. I get the same result when using testbed.setup_env() rather than directly editing os.environ. However, the below does work:

from google.appengine.api import users
import webapp2
import os

class TestPage(webapp2.RequestHandler):
    def get(self):
        os.environ['USER_EMAIL'] = 'a@b.c'
        reload(users)
        user = users.get_current_user()
        self.response.out.write(user.email())

The only change was reloading the users module after changing os.environ. Is this expected behavior, or is something wrong with my App Engine set up? My understanding is that Python / App Engine should load only one copy of the os module loaded into the system, not two.

Any ideas? This is very confusing to me.

Community
  • 1
  • 1
Andrew F
  • 595
  • 1
  • 3
  • 16

2 Answers2

4

You're almost there. What you need is to pass overwrite=True to testbed.setup_env().

I'd normally create a "helper" method, e.g. login_user, something like this:

def login_user(self, email, user_id, is_admin=False):
    self.testbed.setup_env(user_email=email or '', overwrite=True)
    self.testbed.setup_env(user_id=str(user_id) or '', overwrite=True)
    self.testbed.setup_env(user_is_admin='1' if is_admin else '0', overwrite=True)

def logout_user(self):
    self.login_user(None, None)

Also works for OAuth (in case you need that too):

def login_user(self, email, user_id, is_admin=False):
    self.testbed.setup_env(oauth_error_code='', overwrite=True)
    self.testbed.setup_env(oauth_email=email, overwrite=True)
    self.testbed.setup_env(oauth_user_id=str(user_id) or '', overwrite=True)
    self.testbed.setup_env(oauth_auth_domain='example.com', overwrite=True)
    self.testbed.setup_env(oauth_is_admin='1' if is_admin else '0', overwrite=True)
alex
  • 2,450
  • 16
  • 22
  • Good point on the overwrite=True. But even with that change, I still need to reload the module in order for it to take effect. – Andrew F Apr 06 '12 at 06:28
  • Andrew, where exactly are you checking for get_current_user()? I mean, at which state of your app. The code above works for when you're unittesting your app, not with dev_appserver.py – alex Apr 06 '12 at 06:32
  • I'm running dev_appserver.py. I guess that's the root cause of the issue. – Andrew F Apr 06 '12 at 07:35
1

To clarify, os.environ is the WSGI environment (or CGI for Python 2.5) that is being passed to your WSGI-compatible framework (in your case, webapp2) for the request that you are processing - so just one environ per request.

You can see an example of what all the WSGI values are by visiting http://foo-shop.appspot.com - this is a simple WSGI app on Python 2.7 that shows all WSGI values. And all the USER_* values seem to be specific to using Google accounts for auth. There is no mention of them at http://www.wsgi.org/en/latest/definitions.html.

At https://developers.google.com/appengine/docs/python/tools/localunittesting#Changing_the_Default_Environment_Variables, the docs recommend using self.setup_env() to go changing environ variables specifically for testing...and I think testing only. That's not something that an application should be doing in a non-testing circumstance.

timbo
  • 13,244
  • 8
  • 51
  • 71
  • Yeah, that's probably what's going on. I was trying to run test code directly off the actual development server, but I guess the sandboxing places limits on modifying the environ. – Andrew F Apr 06 '12 at 06:34
  • To help clarify a bit more, your app (as specified in app.yaml) is a WSGI-compliant application that is run by a WSGI-compliant server (GAE). In order to allow your app to make decisions based on the environment the request was made in, os.environ is the method for passing environment information. When running dev_appserver.py (or production), it doesn't make sense to change os.environ. I hope that helps :-) – timbo Apr 06 '12 at 07:00
  • ..although it is conceivable you may want to intercept/alter the environment for some sort of middleware purpose, I don't think that what you are trying to achieve here. – timbo Apr 06 '12 at 07:08
  • Yeah, that helps. The issue was that I had tests that failed during unittesting (using nose_gae) but the underlying code ran just fine when I ran it on dev_appserver. So I tried to call the test directly from a dev_appserver request to see what was going on, and ended up stuck here. – Andrew F Apr 06 '12 at 07:38
  • I guess I'll post a separate question about why my nose_gae tests are failing at some point, but at least this bit makes sense now. – Andrew F Apr 06 '12 at 07:40