Why flask can suck
I have devided to settle on flask for my newest project on work. So far, so good.
I have worked for quite a long time happily on the project and nothing got in my way. I was literally absolutely happy. I could just work. Finally no php pain, no drupal pain, nothing.
Then I decided (I think for no reason) to upgrade to version 3 of celery. I did that and everything looked okay (I will so write tests for celery tasks in the future). After I did som developement, I finally noticed that there were no notifications sent at all. This is relevant, as all mailing stuff is handled by celery tasks.
So I checked that out and it was a complete mess. Missing contexts everywhere, nothing was working as expected. I spend two days with fixing one issue after the other. This was mainly due to the following reasons:
1. url_for and SERVER_NAME
url_for depends on the request context, or the
SERVER_NAME setting and application context. I first decided to go for the SERVER_NAME setting which worked fine in the shell (I just specifically ran the tasks). I deployed, and oh wonder, on the server everything was a mess. Apparently SERVER_NAME has the potential for totally breaking all assets and stuff, what it immediately did. So I had to write a decorator executing my tasks with request context:
1 2 3 4 5 6 7 8 9 10 class with_request_context(object): def __init__(self, f): self.f = f self.app = app self.request = '/' self.__name__ = f.__name__ + 'with_request_context' def __call__(self, *args): with self.app.test_request_context(self.request): return self.f(*args)
So, with this little decorator applied (and
@celery.task applied afterwards) it finally works. I still had to have another snippet to generate absolute urls, as I did not set
1 2 3 4 5 6 7 def hosturl(url): host = www.config['HOST'] if host.endswith('/'): host = host[0:-1] if url.startswith('/'): url = url[1:] return '/'.join([host, url])
Problem 1: Fixed.
2. Flask-Babel’s @babel.localeselector
Well, guess what, my locale selection method relied on current_user and broke completely after the upgrade. It just returned only english before (which I did not notice) but afterwards only english. The problem was, there is no chance to pass arguments to flask-babel to manually determine the locale you want. The only idea was to change the default language for each user, which is nasty. So I basically forked flask-babel functionality with some extras. The result is here:
1 2 3 4 5 6 7 8 from babel import support def man_gettext(string, domain, **variables): translations = support.Translations.load(os.path.join(www.root_path, 'translations')) return translations.dgettext(domain, string) % variables def man_lazy_gettext(string, domain, **variables): from speaklater import make_lazy_string return make_lazy_string(man_gettext, string, domain, **variables)
These are the two pedants to gettext and lazy_gettext where you can decide which locale you want. Just do not forget to include the commands with the -k switch, when you want to search for strings to be translated with pybabel.
3. Lazy strings and E-Mails
I have absolutely no idea what happened after I had figured all this out, but what I believe has happened is this: Flask-Mail and I tried simplemail, too, started to reject lazy strings. Do not ask me why, I have no idea at all. When they tried to assert, that there was a body, speaklater went into an infinite getattr loop, apparently to retrieve the actual string.
To be honest, I was completely helpless with this. I just decided to drop translated emails for now, as
gettext() did not offer any more, than a plain english string would.
If anyone has any idea or ever stumbles across the problem and solves it, please let me know. I will definitely try to work this out another time.