A long overdue update for Flask-Classy has been released today. You can check out the updated docs here and find the Github repo here.

  • Breaking Changes

    • Classmethods are filtered out when determining which methods should be routeable.
  • Features

    • FlaskViews now support a “decorators” attribute which takes a list of decorators to apply to every method in that view.
    • The route_base now supports arguments.
  • Bugfixes

    • Fixed a bug that prevented Flask-Classy from generating routes for methods that had used a decorator but did not also use the @route decorator.
    • view_args dict modifications made before the FlaskView view is called are now available in the FlaskView.
  • Thanks:

    • Thanks to Max Countryman, Julien Rebetez, Philip Schleihauf, and Shuhao Wu for your contributions to this release!
Comments

Recently a bug report was filed on the Flask-Classy issue tracker at Github which caught me by surprise. This was a bug so glaring that the fact I hadn’t seen it myself was a shock, but even more shocking was that nobody else had reported it either.

The bug was simple to describe:

If you used any decorator (but didn’t use the @route decorator), Flask-Classy would not auto generate the correct route.

To be honest, when I realized the bug was related to decorators I wasn’t that surprised. I’d always known that there was something funky with them and I even hinted about that in the docs. As it’s turned out though I haven’t used decorators with FlaskViews that much and when I did I always had a @route decorator in the mix.

Fortunately the issue submitter – @shuhaowu – was awesome enough to not only submit some failing tests, but also took the time to do some research into the problem It turns out that decorators obfuscate the signature of the method or functions they are applied to.

This comes as absolutely no surprise, since decorators are essentially syntactic sugar for wrapping a function with another function. Somehow though the implications of this escaped me when I was writing earlier versions of Flask-Classy.

To illustrate the effect this has on decorated methods in a class take a look at this code snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from inspect import getmembers, getargspec
from functools import wraps

# Some super basic decorators
def std_decorator(f):
    def std_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    return std_wrapper

def wraps_decorator(f):
    @wraps(f)
    def wraps_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    return wraps_wrapper

# A simple class with example decorators used
class SomeClass(object):

    def method_one(self, x, y):
        pass

    @std_decorator
    def method_two(self, x, y):
        pass

    @wraps_decorator
    def method_three(self, x, y):
        pass

obj = SomeClass()
for name, func in getmembers(obj, predicate=inspect.ismethod):
    print
    print "Bound Name: %s" % name
    print "Func Name: %s" % func.func_name
    print "Args: %s" % getargspec(func)[0]

Which outputs the following:

Bound Name: method_one
Func Name: method_one
Args: ['self', 'x', 'y']

Bound Name: method_two
Func Name: std_wrapper
Args: []

Bound Name: method_three
Func Name: method_three
Args: []

As we can see, the first method is totally transparent. The second method though has been completely obfuscated. We’re only able to see the original method name because inspect.getmembers was kind enough to share the bound name. But the original arguments? Completely gone. The functools.wraps method at least keeps the original method name intact, but without the arguments we don’t have enough information to construct a meaningful route that will map back to this method at runtime.

So what’s left? How can we get the argspec of the base method that’s been decorated? Fortunately for us python’s reflection capabilities are more than enough to get us there. Here’s a function that can get the base method’s argspec from most decorated methods. I’m certain there must be a case where this will not work, but for all the cases I’ve tried it works fine. (Remember, this only works for methods of a class, not plain functions.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def get_true_argspec(method):
    """Drills through layers of decorators attempting to locate 
       the actual argspec for the method.
    """

    argspec = inspect.getargspec(method)
    args = argspec[0]
    if args and args[0] == 'self':
        return argspec
    if hasattr(method, '__func__'):
        method = method.__func__
    if not hasattr(method, 'func_closure') or method.func_closure is None:
        raise Exception("No closure for method.")

    method = method.func_closure[0].cell_contents
    return get_true_argspec(method)

Now, I haven’t run any speed tests on this, but I presume by it’s very nature that it’s going to perform much slower than a typical inspect.getargspec (if for no other reason than the fact that it makes that exact call one or more times). Fortunately in the case of Flask-Classy, the price is only paid during application loading and won’t have any impact on an actively running application.

Comments

It seems so tempting, the allure of that instant gratification, the sweet smell of data persistence made simple. You start by browsing some blogs, maybe reading some Stack Overflow questions and before you know it, you’ve become smitten with that one tool that is going to save your soul (and your time).

Object Relational Mappers aren’t a new phenomenon, and by george they are incredibly useful tools. But like most brilliant promises ORM frameworks come with a bitter dark side: You may not have to write your own queries but you better be sure you understand deeply how that framework works.

When will I learn?

In my years as a software developer, I’ve used more than a few ORMs. From enterprisey configuration heavy systems like Hibernate and Entity Framework to the smaller agile ones like Rails’ ActiveRecord, Django’s ORM and SQLAlchemy. I’ve even used some of the NoSQL ones for my favorite document oriented database. It seems without fail that when I require even the most trivial of data persistence features I’m inclined to avoid going it alone and instead reach for a pre-baked off the shelf ORM framework and move on to the parts of my project that are more interesting to me.

And every time the same thing happens.

Eventually the system gets bigger, and the design goals of the ORM framework developer start to conflict with the objectives of my application. Every time.

Let me show you what I mean. I’ll use psuedo-code here as not to taint the conversation about particulars of any specific ORM.

obj = Object.get_by_id(id)
obj.view_count += 1
obj.save()

Let’s look at that save() method invocation for a moment. The problem comes when you compare what you as a developer thinks is a reasonable way for the ORM framework to handle this task to what the framework designer thought was best for his or her particular needs. What I thought this line of code would do was:

  1. Find dirty attributes (like view_count)
  2. Build a query that updates that attribute in the persistent store
  3. Execute that query

It turns out that what save() actually did was more along the lines of:

  1. Build a query to update every field in the persistent store with the current values of the object.
  2. Execute that query.
  3. Syncrhonously and recursively do an individual select on every single related entity, pulling the entire object graph into memory one object at a time.
  4. Synchonously and recursively persist each individual object in the graph, even though they have not been modified.

It was quite a shock when I realized that simply incrementing the view_count attribute of the object led to an expensive and time consuming cascading update that frustrated, then later infurated me.

It turns out the right way to do this would be to have just used an update method that was included with the ORM framework and all would have been well, but for some reason the fact that the ORM provided me the tools to make it behave reasonably didn’t do much to alevate my renewed distrust of ORM in general. The fact that I didnt anticipate that the save method would cascade over the entire object graph meant that there was either something wrong with me, or something wrong with the framework, and any of my friends will tell you I don’t easily assign blame to myself (even though that’s usually where it belongs). In the end though, I believe I came to the correct conclusion.

It’s not the (or any) ORM framework’s fault.

Inherently ORM frameworks are a general solution that solve the general problem of persistence management. And generally, they do this pretty well. The problems begin to show up later when the needs of an application become more specific. The tools will usually bend to serve those specific needs fairly well, but the ownus is on the developer to make sure they are well-educated in the ways of that particular framework so that they can use these specific-case features effectively.

And therein lies the problem. You’ve saved yourself the time and energy of managing data access so that you can use that time and energy to manage your data access. While a good working knowlege of the persistent store is a requirement wether you use an ORM or not, a working knowlege of your ORM solution is only needed if you’re using one. By subtracting it from the picture your mental overhead is therefore decreased.

ORM is not the devil.

I’m sure it sounds like I’m villifying ORM frameworks and to be honest there is a part of me that certainly feels like it’s appropriate after the week I’ve had. But the truth is ORM frameworks make up a vital and important part of the development lifecycle for all kinds of apps from little web toys to monster enterprise applications. The key is knowing when and how to use them in your development, and most importantly what role they should play.

ORM Should be a Bridge, not the Foundation

I’m doing something here that I hate. I’m using metaphor to describe a technical term and I apologize. But let’s run through this excercise anyway.

Treat ORM like a bridge. Use it to get your application off the ground, to get it running. ORM is a great tool because it makes it possible to get up and running faster and with less code. Bridges help us to cross the gap. But remember: When bridges can’t handle the traffic, they get replaced with something better. Make sure your bridge can be replaced when the time comes.

Will I use ORM on my next application? If it requires persistence, most assuredly I will. But I’ll be mindful to keep an eye on what the framework does and correct any issues before they reveal themselves at some other inopportune moment.