Dispatch, Don't Decorate
A typical Python web application uses decorators like this:
Web frameworks make extensive use of
Static Decoration Hinders Reuse
Python's decoration syntax is static. However, the concept of decoration is usually referred to in a dynamic context. From Wikipedia, for example, the Decorator Pattern"allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class." Interestingly, the examples are all written in Java, which is generally thought of as a static language, which unlike Python, does not allow you to define decorators statically.
Static decoration is problematic, because it reduces reuse. Consider this example:
The decorator also has the property of making it impossible
You cannot reuse
Dispatch Enables Reuse
A different approach to wrapping functionality around existing functions is with a Dispatch Table. Instead of statically binding the routes to functions, you can bind the functions in a dispatch table like this:
This simple change decouples
When you want to
use Flask, use the
This simple change also improves cohesion: Flask routing is co-located with Flask app management.
Dispatch Improves Security
You may be wondering about how to implement
dispatch with the
One problem with this way of ensure authentication is that
the default is "no auth". If you leave off the
Let's change the dispatch table a bit:
With this change, we are declaring roles required to access a particular function. Not only that, we have an overview of all the required authorizations at a glance. This can be very helpful for security audits.
When we register the endpoint, we can then do something like this:
Experienced Pythonistas reading the above example may say, "that's just another way of decorating functions!" True. I've purposefully kept out of the weeds here. Dispatches should happen without wrapping functions, since they are framework specific. In Flask, I might use a signal to lookup the authorized role(s) in the dispatch table.
Dispatch with Patterns
In general, I don't like dispatch tables for URL routing. That's a complex subject, but the more general rule is that you should use pattern-matching for dispatching when you can.
Here's a less complex problem, implementing logging with a decorator, which will demonstrate the dispatch using regular expressions:
This decorator causes a log message to be output when
Every time a request is called, we check the name of the view
function. If it matches,
There are certainly decorators that cannot be dispatched. The
I would not call these decorators according to the Decorator Pattern: to extend functionality to existing function. They are syntactic sugar to simplify idiosyncrasies of Python's method dispatch mechanism.
There are certainly other special cases, and I'll be happy to hear about them. The vast majority of uses of decorators I've seen (thus far) would be better off defined in centralized dispatchers.
Via Rob 8/24/15
Major Release Syndrome: A Case for Chronological VersioningSecurityTip: Deny by DefaultDigital Name Management: Own Your Name SpaceHow to Safely and Easily Manage PasswordsHow To Detect Phishing EmailsFacebook is a NOPServiceReview: InstacartBe Cheerful, Hopeful, GritfulDispatch, Don't DecorateVexillological Design LessonsLazy InvestingUS Government Cloud Computing Technology RoadmapBookReview: The Logic of Scientific DiscoveryBuild Identity Through Hard ChoicesBookReview: How to Fail at Almost Everything and Still Win Big: Kind of the Story of My LifePythonTip: "import from" Considered HarmfulBackpacking: Lost Creek WildernessNuclear Deterrence For Your CloudBookReview: Startup CEO: A Field Guide to Scaling Up Your
BusinessBookReview: The Structure of Scientific Revolutions (3rd Edition)The Psychology of Software TestingOwn VirtuallyBookReview: Fool's Gold: How the Bold Dream of a Small Tribe at J.P. Morgan Was Corrupted by Wall Street Greed and Unleashed a CatastropheBookReview: How to Be Alone: EssaysBookReview: Eat, Pray, Love▶ More▲ Most Recent
|back to top||© 2017 Rob Nagler||Software by bivio|