webapp.WSGIApplication to match all other possible paths - google-app-engine

I had gone through webapp2 Route to match all other paths
I try to
class SinkHandler(webapp2.RequestHandler):
def get(self, *args, **kwargs):
self.response.out.write('Sink')
application = webapp2.WSGIApplication([
(r'/<:.*>', SinkHandler)
], debug = True)
in order to match
http://localhost:9080/dummy
http://localhost:9080/dummy/eummy
http://localhost:9080/dummy/eummy/fummy
But it just yield 404 not found.
I can do it this way.
class SinkHandler(webapp2.RequestHandler):
def get(self, *args, **kwargs):
self.response.out.write('Sink')
application = webapp2.WSGIApplication([
(r'/(\w*)$', SinkHandler),
(r'/(\w*)/(\w*)$', SinkHandler),
(r'/(\w*)/(\w*)/(\w*)$', SinkHandler)
], debug = True)
It will able to match the above 3 type of URLs. However, if I need to support
http://localhost:9080/dummy/eummy/fummy/gummy
I need to add additional entry in WSGIApplication
Is there a smarter way, to match all other possible paths?

This should work:
application = webapp2.WSGIApplication([
(r'/.*', SinkHandler)
], debug = True)
To use the more general regexp, you need to use Route:
from webapp2 import Route
application = webapp2.WSGIApplication([
Route(r'/<:.*>', SinkHandler)
], debug = True)

Related

Google App Engine oauth2 Callback 404

with the following code I am getting a 404 resource not found error after clicking authorize button for google drive. My code is below- any ideas what I am doing incorrectly?
from __future__ import print_function
import httplib2
import os
from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from apiclient.discovery import build
from google.appengine.ext import webapp
from oauth2client.appengine import OAuth2DecoratorFromClientSecrets
import webapp2
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
except SystemExit:
flags= None
#
## If modifying these scopes, delete your previously saved credentials
## at ~/.credentials/drive-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/drive'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Drive API Quickstart'
decorator = OAuth2DecoratorFromClientSecrets( CLIENT_SECRET_FILE,SCOPES)
service = build('drive', 'v3')
class MainHandler(webapp.RequestHandler):
#decorator.oauth_required
def get(self):
# Get the authorized Http object created by the decorator.
http = decorator.http()
# Call the service using the authorized Http object.
request = service.files().list(q = "mimeType != 'application/vnd.google-apps.folder'", pageSize=1000, )
response = request.execute(http=http)
app = webapp.WSGIApplication([
('/', MainHandler),
], debug=True)
I have https://drive-156701.appspot.com/oauth2callback with and without a / at the end as redirects, which I thought where the correct redirect urls?
Thanks!!
You don't have a URL handler for /oauth2callback. You only have a url handler for /. Try:
app = webapp.WSGIApplication([
('/oauth2callback', Oauth2CallbackHandler),
('/', MainHandler),
], debug=True)
and make a Oauth2CallbackHandler class to handle the callback.
In addition to including the :
app = webapp2.WSGIApplication(
[
('/', MainHandler),
(decorator.callback_path, decorator.callback_handler()),
],
debug=True)
The #decorator.oauth_required handles the callbacks for you.
Check out this video: https://www.youtube.com/watch?v=HoUdWBzUZ-M
You need to create credentials for both localhost development and deployed.
And make sure you have checked out: https://cloud.google.com/appengine/docs/standard/python/tools/using-libraries-python-27
Your api imports must be handled like 3rd party imports, details here:
https://developers.google.com/api-client-library/python/start/installation

dynamically generating HTML divs/JS scripts with Python/GAE

I am building a webapp and I would like to be able to dynamically render out divs/scripts in my index.html given certain conditions. An abstraction of what I would like to do is the following:
class MainHandler(webapp2.RequestHandler):
def get(self, q):
script = "<script>function display_alert(){ {alert('Hello world!'}}</script> <div>hello world</div>"
if q is None:
q = 'index.html'
path = os.path.join (os.path.dirname (__file__), q)
self.response.headers ['Content-Type'] = 'text/html'
self.response.write (template.render (path, {
"script" : script
}))
def main ():
application = webapp.WSGIApplication ([('/(.*html)?', MainHandler)], debug=True)
util.run_wsgi_app (application)
if __name__ == '__main__':
main ()
The HTML is a simple index.html file that contains {{script}}. When it renders out, it looks like this:
Why isn't the HTML rendering what I gave it correctly? I tried running the "script" variable through simplejson.dumps, it didn't work either.
I believe Html is escaped by default in django templates
in django this is accomplished with the safe template filter
{{ script|safe }}
are you using django to render your templates? or a different engine?

Integrating Chat with (webapp2 + python 2.7 + Jinja2)

I'm trying to add chat to my site and am integrating some code with my existing code. The chat app works fine on its own when it's all set up in the original main.app file. But when I try to move that same code to a handlers.py file and then setup up routes in routes.py I get errors saying template variables are undefined. Are the two different codes conflicting in the way they render templates? They seem to be using webapp2 differently, i.e. my code renders templates like this:
self.render_template('secure_zone.html', **params)
And the chat app like this:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
Are both acceptable?
Here's my handlers.py file:
Routes are setup in routes.py and added in main.py
"""
import httpagentparser
from boilerplate import models
from boilerplate.lib.basehandler import BaseHandler
from boilerplate.lib.basehandler import user_required
class SecureRequestHandler(BaseHandler):
"""
Only accessible to users that are logged in
"""
#user_required
def get(self, **kwargs):
user_session = self.user
user_session_object = self.auth.store.get_session(self.request)
user_info = models.User.get_by_id(long( self.user_id ))
user_info_object = self.auth.store.user_model.get_by_auth_token(
user_session['user_id'], user_session['token'])
try:
params = {
"user_session" : user_session,
"user_session_object" : user_session_object,
"user_info" : user_info,
"user_info_object" : user_info_object,
"userinfo_logout-url" : self.auth_config['logout_url'],
}
return self.render_template('secure_zone.html', **params)
except (AttributeError, KeyError), e:
return "Secure zone error:" + " %s." % e
Here's the main.py file for the chat app:
import os
import hashlib
import urllib
import logging
import re
import json
import webapp2
import jinja2
from google.appengine.api import channel as channel_api # 'channel' is kind of ambiguous in context
from google.appengine.ext import db
from google.appengine.api import memcache
# This section will eventually get moved to a Handler class
template_dir = os.path.join(
os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(
loader = jinja2.FileSystemLoader(template_dir),
autoescape = True)
def render_str(template, **params):
'''Returns a string of the rendered template'''
t = jinja_env.get_template(template)
return t.render(params)
def render(template, **kw):
'''Render using the template and parameters'''
return(render_str(template, **kw))
# End Handler
class Main(webapp2.RequestHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
def post(self):
'''Displays chat UI'''
username = self.request.get('username')
channelname = self.request.get('channel')
usernameerror = ""
if not username:
usernameerror="Please enter a username"
elif not re.compile(r'^[a-zA-Z0-9_-]{3,20}$').match(username):
usernameerror = "Username must consist of 3-20 alphanumeric characters."
elif get_user(username):
usernameerror="Username already in use"
channelerror = ""
if channelname and not re.compile(r'^#[\w]{3,20}$').match(channelname):
channelerror="Channel must consist of 3-20 alpha_numeric characters and start with a #"
if len(usernameerror+channelerror) > 0:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
app = webapp2.WSGIApplication([
('/', Main),
('/communication', Communication),
('/_ah/channel/connected/?', Connect),
('/_ah/channel/disconnected/?', Disconnect)
], debug=True)
The specific error you posted in the comments "Error: 'Main' object has no attribute 'render_template'" is because in your Main handler, you try to return self.render_template. You should be just calling the function like this:
render_template("main.html", channel="#udacity")
Please note that I did not check the rest of your code, so if you run into any other issues, please post the specific errors you get.
It is because your webapp2.RequestHandler do not have the corresponding function "render_template"
you can use a BaseHandler with render_template function added to achieve the template rendering
from google.appengine.ext.webapp import template
class BaseHandler(webapp2.RequestHandler):
def render_template(self, filename, **template_args):
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
self.response.write(template.render(path, template_args))
class Main(BaseHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
ref: http://blog.notdot.net/2011/11/Migrating-to-Python-2-7-part-2-Webapp-and-templates
I just started using webapp2 + python 2.7 + Jinja2 few days, and that is the same problem I encountered.
Hope this solution can help you ;)

User Accounts with (Python 2.7 + Webapp2 + Google App Engine + Jinja + NDB + WTForms)

I have been racking my brain for weeks trying to get this authentication module to work under the webapp2 framework using Jinja, NDB and WTForms. I've looked online everywhere and have reached my breaking point and need to reach out to somebody for help. I've been following this tutorial (which is pretty thorough but I think leaves out some important details, especially for a newbie like me):
User Accounts with Webapp2 + Google App Engine
As far as I know I have everything setup correctly but I continually get a 404 - resources can not be found. What could I be doing wrong? Here is my code...
handlers.py:
import webapp2
import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb
import webapp2_extras.appengine.auth.models as auth_models
from google.appengine.api import users
from webapp2_extras import sessions, auth # we'll use auth later on
from webapp2_extras.auth import InvalidAuthIdError
from webapp2_extras.auth import InvalidPasswordError
from wtforms import Form, TextField, PasswordField, validators
from webapp2_extras.appengine.users import login_required
def jinja2_factory(app):
j = jinja2.Jinja2(app)
j.environment.filters.update({
# Set filters. (http://tinyurl.com/jinja2-factory)
# ...
})
j.environment.globals.update({
# Set global variables.
'uri_for': webapp2.uri_for,
# ...
})
return j
def login_required(handler):
def check_login(self, *args, **kwargs):
if not self.user:
return self.redirect_to('login')
else:
return handler(self, *args, **kwargs)
return check_login
class UserAwareHandler(webapp2.RequestHandler):
def dispatch(self):
try:
super(UserAwareHandler, self).dispatch()
finally:
# Save the session after each request
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session_store(self):
return sessions.get_store(request=self.request)
#webapp2.cached_property
def session(self):
return self.session_store.get_session(backend="datastore")
#webapp2.cached_property
def auth(self):
return auth.get_auth(request=self.request)
#webapp2.cached_property
def user(self):
user = self.auth.get_user_by_session()
return user
#webapp2.cached_property
def user_model(self):
user_model, timestamp = self.auth.store.user_model.get_by_auth_token(
self.user['user_id'],
self.user['token']) if self.user else (None, None)
return user_model
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(factory=jinja2_factory, app=self.app)
def render_response(self, _template, **context):
ctx = {'user': self.user_model}
ctx.update(context)
rv = self.jinja2.render_template(_template, **ctx)
self.response.write(rv)
class SignupForm(Form):
email = TextField('Email',
[validators.Required(),
validators.Email()])
password = PasswordField('Password',
[validators.Required(),
validators.EqualTo('confirm_password',
message="Passwords must match.")])
password_confirm = PasswordField('Confirm Password',
[validators.Required()])
class SignupHandler(UserAwareHandler):
#Serves up a signup form, creates new users
def get(self):
self.render_response("templates/signup.html", form=SignupForm())
def post(self):
form = SignupForm(self.request.POST)
error = None
if form.validate():
success, info = self.auth.store.user_model.create_user(
"auth:" + form.email.data,
unique_properties=['email'],
email= form.password.data,
password_raw= form.password.data)
if success:
self.auth.get_user_by_password("auth:"+form.email.data,
form.password.data)
return self.redirect_to("index")
else:
error = "That email is already in use." if 'email'\
in user else "Something has gone horrible wrong."
self.render_response("templates/signup.html", form=form, error=error)
class LoginForm(Form):
email = TextField('Email',
[validators.Required(), validators.Email()])
password = PasswordField('Password',
[validators.Required()])
class LoginHandler(UserAwareHandler):
def get(self):
self.render_response("templates/index.html", form=LoginForm())
def post(self):
form = LoginForm(self.request.POST)
error = None
if form.validate():
try:
self.auth.get_user_by_password(
"auth:"+form.email.data,
form.password.data)
return self.redirect_to('secure')
except (auth.InvalidAuthIdError, auth.InvalidPasswordError):
error = "Invalid Email / Password"
self.render_response("templates/login.html", form=form, error=error)
class LogoutHandler(UserAwareHandler):
#Destroy the user session and return them to the login screen.
#login_required
def get(self):
self.auth.unset_session()
self.redirect_to('login')
class IndexHandler(UserAwareHandler):
def get(self):
ctx = {
'title1': "ALAZA",
'title2': "HOA",
'slogan': "A communication tool for the Alazan HOA.",
'message1': """
<p>The whole idea here is to show how to set up a simple static web site
on Google App Engine. I want to create an easy way to host your modest web
site on App Engine. My approach is dead simple. All I use is some boilerplate
code almost anyone can follow. You can have multiple pages and use template
variable features that are part of App Engine's WebApp Framework. Most modest
web sites don't do much more than this. Your certainly free to expand on what
you find here.</p>""",
}
self.render_response('templates/index.html', **ctx)
main.py:
import webapp2
import config
import routes
import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb
import webapp2_extras.appengine.auth.models as auth_models
class AwesomeUser(auth_models.User):
email = ndb.StringProperty()
webapp2_config = {}
webapp2_config['webapp2_extras.sessions'] = {
'secret_key': 'othello',}
webapp2_config['webapp2_extras.auth'] = {
'user_model': AwesomeUser,}
app = webapp2.WSGIApplication(config=config.webapp2_config)
routes.add_routes(app)
routes.py:
import handlers
import webapp2
from webapp2_extras.routes import RedirectRoute
# Using redirect route instead of simple routes since it supports strict_slash
# Simple route: http://webapp-improved.appspot.com/guide/routing.html#simple-routes
# RedirectRoute: http://webapp-improved.appspot.com/api/webapp2_extras/routes.html#webapp2_extras.routes.RedirectRoute
_routes = [
RedirectRoute('templates/login.html', handlers.LoginHandler, name='login'),
RedirectRoute('/templates/logout.html', handlers.LogoutHandler, name='logout'),
RedirectRoute('/templates/index.html', handlers.IndexHandler, name='index'),
RedirectRoute('/templates/signup.html', handlers.SignupHandler, name='signup'),]
def get_routes():
return _routes
def add_routes(app):
if app.debug:
secure_scheme = 'http'
for r in _routes:
app.router.add(r)
app.yaml:
application: alazan-hoa
version: main
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /js
static_dir: js
- url: /images
static_dir: images
- url: /css
static_dir: css
- url: /.*
script: main.app
libraries:
- name: webapp2
version: "2.5.1"
- name: jinja2
version: "2.6"
builtins:
- appstats: on
In your routes.py file, in the RedirectRoute you specify html files, while you should specify paths that are mapped to handler methods and inside them render the html templates.
For example, in your routes.py file you should have something like this:
_routes = [
RedirectRoute('/login', handlers.LoginHandler, name='login'),
RedirectRoute('/logout', handlers.LogoutHandler, name='logout'),
RedirectRoute('/index', handlers.IndexHandler, name='index'),
RedirectRoute('/signup', handlers.SignupHandler, name='signup')
]
You can take a look at Google App Engine Boilerplate for an introduction to Google App Engine development and best practices around it.
Hope this helps!

How do I fix this encoding error in Google App Engine

I'm new to Google App Engine and Django forms. I'm getting an encoding error when I try to run the follow code:
import webapp2
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from django import newforms as forms
from google.appengine.ext.db import djangoforms
import os
import re
import fix_path
import config
import static
def slugify(s):
return re.sub('[^a-zA-Z0-9-]+', '-', s).strip('-')
def format_post_path(post, num):
slug = slugify(post.title)
if num > 0:
slug += "-" + str(num)
return config.post_path_format % {
'slug': slug,
'year': post.published.year,
'month': post.published.month,
'day': post.published.day,
}
def render_template(template_name, template_vals=None, theme=None):
template_path = os.path.join("themes", theme or config.theme, template_name)
return template.render(template_path, template_vals or {})
class BlogPost(db.Model):
# The url path to the blog post. Posts have a path if they are published.
path = db.StringProperty()
title = db.StringProperty(required=True, Indexed=False)
body = db.TextProperty(required=True)
published = db.DateTimeProperty(auto_now_add=True)
updated = db.DateTimeProperty(auto_now=True)
def render(self):
template_vals = {
'config': config,
'post': self,
}
return render_template("post.html", template_vals)
def publish(self):
rendered = self.render()
if not self.path:
num = 0
content = None
while not content:
path = format_post_path(self, num)
content = static.add(path, rendered, "text/html")
num += 1
self.path = path
self.put()
else:
static.set(self.path, rendered, "text/html")
class PostForm(djangoforms.ModelForm):
class Meta:
model = BlogPost
exclude = ['path', 'published', 'update']
class PostHandler(webapp2.RequestHandler)
def render_to_response(self, template_name, template_vals=None, theme=None):
template_name = os.path.join("admin", template_name)
self.response.out.write(render_template(template_name, template_vals, theme))
def render_form(self, form):
self.render_to_response("edit.html", {'form': form})
def get(self):
self.render_form(PostForm())
def post(self)
form = PostForm(date=self.request.POST)
if form.is_valid():
post = form.save(commit=False)
post.publish()
self.render_to_response("published.html", {'post': post})
else:
self.render_form(form)
app = webapp2.WSGIApplication([('/admin/newpost', PostHandler)],
debug=True)
Here is the traceback.
ERROR 2012-05-08 10:35:03,609 cgi.py:121] Traceback (most recent call last):
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver_import_hook.py", line 676, in Decorate
return func(self, *args, **kwargs)
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver_import_hook.py", line 1911, in get_code
source_code.decode(encoding)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 0: ordinal not in range(128)
INFO 2012-05-08 10:35:03,627 dev_appserver.py:2891] "GET /admin/newpost HTTP/1.1" 500 -
The traceback doesn't give me enough information to fix the problem. Any ideas?
When I had this problem with appengine I could clean all my data to unicode / utf-8 and then it worked.
Some of your data is not unicode / utf-8. I've had this problem on several occassions with GAE and it's always the data and not the source code that is not unicode in some way.
If you inspect your data and/or source formatting and set everything to utf-8 / unicode it will work. It might seem that you've already set everything to utf-8, but if you're still getting this error it means some string or the like is is some other charset than utf-8.

Resources