AppEngine python's send_email not working anymore - google-app-engine

My appengine app has a cron job that calls a url endpoint whose handler uses mail.send_mail from google.appengine.api. This has been working fine for several months so far.
Today, the email never arrived. I wrote some test code to invoke send_mail, but the email does not get sent. I have adhered to the necesarry requirements like sending from a email address of the form anything#appname.appspotmail.com.
The function is not throwing any exception either. The appengine logs note that the url is invoked, but there is no error or exception.
What might be the problem? Thanks.
Editing to add some code as suggested. Note that to actually test this code one'd need an AppEngine App. In that case you'd need to change myApp etc. in the code below to the actual app name that is used.
Looking forward to any help/insights.
from google.appengine.api import mail
class TestEmailHandler(webapp2.RequestHandler):
def get(self):
mySender = "mySender <mySender#myApp.appspotmail.com>"
myTo = "myToAddress#example.com"
mySubject = "Test Subject"
myBody = "Test Body Text"
myHtml = "<html><body>Test body</body></html>"
try:
mail.send_mail(sender=mySender,
to=myTo,
subject=mySubject,
body=myBody,
html=myHtml)
self.response.headers['Content-Type'] = 'text/plain'
self.response.write("Sent email. Body: " + myBody)
except:
self.response.write("Exception. " + sys.exc_info()[0])
application = webapp2.WSGIApplication([
('/', MainPage),
('/test_email', TestEmailHandler)
], debug=True)
My app.yaml looks like this:
application: myApp
version: 2
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: myApp.application
inbound_services:
- mail

I think the problem's now fixed. I associated my credit card by enabling billing and now an email got sent when I tested it. Who would've thought...

Related

Why does dev_appserver.py exit without throwing errors?

I am trying to run a simple python 2 server code with AppEngine and Datastore. When I run dev_appserver.py app.yaml, the program immediately exits (without an error) after the following outputs:
/home/username/google-cloud-sdk/lib/third_party/google/auth/crypt/_cryptography_rsa.py:22: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.
import cryptography.exceptions
INFO 2022-12-20 11:59:41,931 devappserver2.py:239] Using Cloud Datastore Emulator.
We are gradually rolling out the emulator as the default datastore implementation of dev_appserver.
If broken, you can temporarily disable it by --support_datastore_emulator=False
Read the documentation: https://cloud.google.com/appengine/docs/standard/python/tools/migrate-cloud-datastore-emulator
INFO 2022-12-20 11:59:41,936 devappserver2.py:316] Skipping SDK update check.
INFO 2022-12-20 11:59:42,332 datastore_emulator.py:156] Starting Cloud Datastore emulator at: http://localhost:22325
INFO 2022-12-20 11:59:42,981 datastore_emulator.py:162] Cloud Datastore emulator responded after 0.648865 seconds
INFO 2022-12-20 11:59:42,982 <string>:384] Starting API server at: http://localhost:38915
Ideally, it should have continued by runnning the server on port 8000. Also, it works with option --support_datastore_emulator=False.
This is the code:
import webapp2
import datetime
from google.appengine.ext import db, deferred, ndb
import uuid
from base64 import b64decode, b64encode
import logging
class Email(ndb.Model):
email = ndb.StringProperty()
class DB(webapp2.RequestHandler):
def post(self):
try:
mail = Email()
mail.email = 'Test'
mail.put()
except Exception as e:
print(e)
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
return self.response.out.write(e)
def get(self):
try:
e1 = Email.query()
logging.critical('count is: %s' % e1.count)
e1k = e1.get(keys_only=True)
logging.critical('count 2 is: %s' % e1k.count)
e1 = e1.get()
key = unicode(e1.key.urlsafe())
logging.critical('This is a critical message: %s' % key)
logging.critical('This is a critical message: %s' % e1k)
e2 = ndb.Key(urlsafe=key).get()
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
return self.response.out.write(str(e2.email))
except Exception as e:
print(e)
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
return self.response.out.write(e)
app = webapp2.WSGIApplication([
('/', DB)
], debug=True)
How can I find the reason this is not working?
Edit: I figured that the dev server works and writes to a datastore even with support_datastore_emulator=False option. I am confused by this option. I also don't know where the database is stored currently.
It should be count() and not count i.e.
logging.critical('count is: %s' % e1.count())
A get returns only 1 record and so it doesn't make sense to do a count after calling a get. Besides, the count operation is a method of the query instance not the results. This means the following code is incorrect
e1k = e1.get(keys_only=True)
logging.critical('count 2 is: %s' % e1k.count)
You should replace it with
elk = e1.fetch(keys_only=True) # fetch gives an array
logging.critical('count 2 is: %s' % len(e1k))
When you first run your App, it will execute the GET part of your code and because this is the first time your App is being run, you have no record in Datastore. This means e1 = e1.get() will return None and key = unicode(e1.key.urlsafe()) will lead to an error.
You have to modify your code to first check you have a value for e1 or e2 before you attempt to use the keys.
I ran your code with dev_appserver.py and it displayed these errors for me in the logs. But I ran it with an older version of gcloud SDK (Google Cloud SDK 367.0.0). I don't know why yours exited without displaying any errors. Maybe it's due to the version...??
Separately - Don't know why you're importing db. Google moved on to ndb long ago and you don't use db in your code
The default datastore (for the older generation runtimes like Python 2) is in .config (hidden folder) > gcloud > emulators > datastore
You can also specify your own location by using the flag --datastore_path. See documentation

Google AppEngine: Python 2.7: How to split request handler up into several files?

I have an application in which I would like to move some of the request handlers into separate files. I've reduced the problem to this simple app which demonstrates it.
If I browse the main page, e.g.:
http://localhost:12082/
I see
'Hello World...'
displayed
if I try to go to the init page, e.g.:
http://localhost:12082/init/
I see
'404 Not Found'
The resource could not be found.
and in the log I see this:
(init2 message is never logged, the helper method is never called.)
What am I doing wrong?
Thank you for any clues
log contents:
...
INFO 2014-01-15 20:58:23,384 admin_server.py:117] Starting admin server at: http://localhost:8005
INFO 2014-01-16 04:58:34,398 initter.py:6] init1
INFO 2014-01-16 04:58:34,398 initter.py:17] init3
INFO 2014-01-15 20:58:34,421 module.py:617] default: "GET / HTTP/1.1" 200 14
INFO 2014-01-15 20:58:34,473 module.py:617] default: "GET / HTTP/1.1" 200 14
INFO 2014-01-15 20:58:39,555 module.py:617] default: "GET /init/ HTTP/1.1" 404 154
app.yaml:
application: helloworld
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: helloworld.application
helloworld.py
import webapp2
import initter
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write('Hello World...')
application = webapp2.WSGIApplication([
('/', MainPage),
('/init', initter.LoadPeople),
], debug=True)
initter.py
import webapp2
import logging
logging.getLogger().setLevel(logging.DEBUG)
logging.info("init1")
class LoadPeople(webapp2.RequestHandler):
def get(self):
logging.info("init2")
names = [ 'Joe', 'Jill', 'George', 'John', 'Dave' ]
for name in names:
logging.debug("name is %s", str(name))
logging.info("init3")
http handler expressions could be tricky. There is a difference between '/init' and '/init/' (with an extra slash at the end). Try changing your WSGI app initialization to this:
application = webapp2.WSGIApplication([
('/', MainPage),
('/init*', initter.LoadPeople),
], debug=True)
This will also take care of any query-strings you provide. Example: "....localhost/init?someKey=someValue"

How to understand chats.html v.s ('/getchats', ChatsRequestHandler)]

I try to understand the different between:
The class ChatsRequestHandler generate a template with the name chats.html
template = self.generate('chats.html', template_values)
In the application view its is named getchats:
application = webapp.WSGIApplication(
[('/', MainRequestHandler),
('/getchats', ChatsRequestHandler)],
The same occurs to me at edit_user.html v.s ('/edituser', EditUserProfileHandler)
How is it that the application knows that the getchats is connected to the chats.html aldo they have not the same name? I would expect that it should be the same name chats.html and ('/chats', ChatsRequestHandler).
The flow of your request goes something like this.
App Engine looks up your app.yaml file. It should contain an entry that says /getchats should be handled by application in somefile.py.
It then goes to this "application view" and matches it to a Webapp Route. In this case, that route is ('/getchats', ChatsRequestHandler).
Then it calls get or post on ChatRequestHandler, passing it the request and response objects.
The output of that is sent back to the user's browser.
You are free to implement ChatRequestHandler as you'd like. In this case you're doing so by reading in a template named chats.html, populating it with some values, and then outputting it.
So the application knows that getchats is connected to ChatRequestHandler. The name of chats.html is pretty arbitrary - the ChatReqeustHandler has to know it, but that is all. You could rename it.
Thanks for helping me:
The example a came up with comes from codenvy.com as a examples app.
1 App Engine looks up your app.yaml file. It should contain an entry that says /getchats should be handled by application in somefile.py.
Here is the app.yaml file of this application
application: 3kus-apps
version: 1
runtime: python
api_version: 1
handlers:
- url: /css
static_dir: css
- url: /js
static_dir: js
- url: /.*
script: devchat.py
So as you can see it contain's no entry that says /getchats should be handled by application in somefile.py.
What i found there is a util.js file witch has a function updateChat(). function updateChat() {downloadUrl(getRandomUrl("/getchats"), "GET", null, onChatsReturned);}.
However, I would like to know - under (1) how this should be handled by a somefile.py.

dev_appserver: where do static routes get routed?

Could anyone point to roughly where in the python sdk code static routes get loaded into or accessed by http_server. This is to debug a failure to load static images. In eclipse I can see the static routes loading into var appinfo from the yaml file, and later can see the dynamic routes being checked during a request, but having trouble following the in-between steps.
Thanks
Update 11/30
Previously tried variations on the yaml, path, etc that were suggested in some docs and postings.
Here is one of them. In this case there is no 404 error, but image doesn't load and Firebug reports "Failed to the load the given URL".
app.yaml
application: crazywidget2
version: 1
runtime: python27
api_version: 1
threadsafe: false
handlers:
- url: /images
static_dir: /images
secure: always
-url: /.*
script: crazywidget2.py
secure: always
libraries:
- name: jinja2
version: latest
index.html
...
<img src="/images/xyz.gif" alt="XYZ illustration" />
...
crazywidget2.py
...
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render({}))
...
...
app = webapp2.WSGIApplication([('/script_send', ScriptSend),
('/resetkey', ResetKey),
('/admin', Admin),
('/start', Start),
('/', MainPage)],
debug=True)
def main():
app.run()
if __name__=='__main__':
main()
Update 12/3
Turns out that in the above case it works if the static_dir is relative, "images" instead of "/images". In the absolute case it tries to open that path as is. Maybe some other variations would work as well.
Here are three relevant code pointers (all in google/appengine/tools/dev_appserver.py):
class FileDispatcher
CreateURLMatcherFromMaps()
DevAppServerRequestHandler._Dispatch()
I would assume though, there are easier ways to debug your problem. If you would post your app.yaml and the path you access and the response you get, people here could start to help you.
Just a hunch: does it work if you add a slash to the end of /images in your handlers? Try replacing /images with /images/ in both places that you use it in app.yaml.

App Engine 401/403 Status Codes Not Working on WebApp2

I am trying to raise a 401/403 status for when a user attempts to access something they don't have privileges to. I used the Webapp2 Exceptions example which generates the proper error codes for 404/500 "natural" events. Such as going to http://localhost:8080/nourl generates the proper 404 and messing up code generates a 500. But when I use the method such as below to set the code using self.error(XXX) I see the code in the console but it does not show up in the browser. EG If you leave self.error() empty it generates the proper 500 code. If you use self.error(500) the console outputs:
INFO 2012-05-09 18:25:29,549 dev_appserver.py:2891] "GET / HTTP/1.1" 500 -
But the browser is completely blank. Below is an example app that exhibits this behaviour. Simply change the self.error() line to the desired code and run. The expected result would be that it generates the proper response to the browser based on the code supplied, not just when a "natural" event occurs such as 404.
main.py
import webapp2
import wsgiref.handlers
import logging
from google.appengine.api import users
class HomeHandler(webapp2.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
self.response.out.write("Hi :1", user.nickname)
else:
self.error(401)
app = webapp2.WSGIApplication([
(r'/', HomeHandler),
], debug=True)
def handle_401(request, response, exception):
logging.exception(exception)
response.write("401 Error")
response.set_status(401)
def handle_403(request, response, exception):
logging.exception(exception)
response.write("403 Error")
response.set_status(403)
def handle_404(request, response, exception):
logging.exception(exception)
response.write("404 Error")
response.set_status(404)
def handle_500(request, response, exception):
logging.exception(exception)
response.write("500 Error")
response.set_status(500)
app.error_handlers[401] = handle_401
app.error_handlers[403] = handle_403
app.error_handlers[404] = handle_404
app.error_handlers[500] = handle_500
# Run the application
def main():
app.run()
app.yaml
application: 401test
version: 1
runtime: python27
api_version: 1
threadsafe: yes
libraries:
- name: webapp2
version: latest
handlers:
- url: /.*
script: main.app
you are using py27 with threadsafe environment.
in app.yaml you set script: main.app so the code after app is defined is not executed.
didn't test it but this should work:
# create a bare app
bare_app = webapp2.WSGIApplication(debug=True)
#define the error handlers
def handle_401(request, response, exception):
logging.exception(exception)
response.write("401 Error")
response.set_status(401)
def handle_403(request, response, exception):
logging.exception(exception)
response.write("403 Error")
response.set_status(403)
def handle_404(request, response, exception):
logging.exception(exception)
response.write("404 Error")
response.set_status(404)
def handle_500(request, response, exception):
logging.exception(exception)
response.write("500 Error")
response.set_status(500)
# add the error handlers
bare_app.error_handlers[401] = handle_401
bare_app.error_handlers[403] = handle_403
bare_app.error_handlers[404] = handle_404
bare_app.error_handlers[500] = handle_500
bare_app.router.add((r'/', HomeHandler))
app = bare_app
edit:
use self.abort() instead of self.error()
http://webapp-improved.appspot.com/guide/exceptions.html#abort
you can see the difference in the source code:
http://code.google.com/p/webapp-improved/source/browse/webapp2.py#574
while self.error() sets the status code but clears the response, self.abort() executes the function that takes care of the error handling.

Resources