TravisSwicegood.com

13 May

Magic is to Python as Java is to PHP

Wow. I touched a live wire today in IRC without meaning to. I lamented over the inconsistency that Django has between it's generic views. It has to do with what the Python community calls magic and goes something like this.

Django ships with these great generic views (if you're coming from another web framework, consider their views as controllers). Working with any framework long enough, you know that there's only so much to do. Handle posts to update, grab a list of objects or a single object, pass them off to the presentation layer, hand it off to designers and call it a day. They work great.

There's a series of generic views for handling object updating and deleting. They're the django.views.generic.create_update views. Of course, you don't want just anybody updating your content, so you probably want to be able to require that users are logged in. Easy enough, add the login_required variable to the dictionary of options you specify for the view and call it a day. One line of code in the dictionary that both your add and edit rely on and you're done.

Until you get to the list of objects. It's located over in django.views.generic.list_detail.object_list and it complains rabidly when you include a login_required value. Turns out, that generic view doesn't support what seems like an obvious option to modify it's behavior. After all, you might not want lists of your objects just floating around out there. It's an inconsistency between the generic views, and annoyed me.

I mentioned something to that effect on IRC and ended up in a lengthy discussion about it. Turns out there is an acknowledged inconsistency, but the problem is that the create_update views take login_required rather than the list_detail is missing it. There's a perfectly acceptable way to fix it: decorators.

There's a problem with this, though. Decorators have multiple syntaxes. The convenient way to do them is like this:

@login_required
def some_view(request):
    ...


In a generic view where you're referencing a function that's already declared, that doesn't work. Instead, you have to declare it like this:

login_required(some_view)


Still doesn't seem horrible, but here's the catch. Everywhere else you work with URL patterns in Django, the tendency is to refer to the view as a string. Note that the parameter to login_required isn't a string. That means you have to explicitly import the views you want to use, otherwise Python has no way of knowing where that function exists.

That personally offended my sense of code aesthetics. I mentioned it and said that I would just allow login_required to be there. It's ubiquitous enough that it seems like it should be available all of the time, for every view that accepts parameters.

That was quickly shown to have errors as there were two ways to handle that:

  1. Every view everyone will ever write in every Django application everywhere must take a "login_required" argument and include the appropriate code to handle it.
  2. Anybody who wants the behavior can do "login_required(some_function)"

That's the two obvious ones. I added the third.

  1. or, the framework looks for the presence of login_required and decorates the function appropriately

And thus I touched the live wire and magic missiles started flying. If you're reading this post via Planet PHP and you've made it this far, you're undoubtedly wondering what all this Python and Django talk has to do with PHP. Well, it has to do with magic and Java.

See in PHP circles, the quickest way to end a discussion is to say that some piece of code acts or looks too much like Java. Try to code in object-oriented PHP and risk being dismissed with "PHP isn't Java". Once that's thrown into the conversation, you might as well call it quits. Java is tainted goods in PHP community. The culture is such that it's shear mention is enough to send shivers down the spine of aspiring PHP'ers.

That shiver inducing phrase in Python appears to be "magic." Be explicit, that's their motto. Doing anything without explicitly asking for it is considered "magic" and should be shunned like a leper with the plague. To be flip, you could change it around to "I'm fine, I can do it myself, leave me alone."

Me personally, I'm lazy. I like having hooks deep into a system where I can apply things at will. I think things like the user authentication system should be able to add framework-wide hooks so when a login_required is encountered in the parameters it can auto-magically make sure that the user is authenticated. I love it when a framework takes work away from me, that's why I use them in the first place.

I've always been slightly amused and distressed at the PHP community's need to shun Java. As an outsider to the other communities, I've often wondered what set them off. Now I've found at least one in Python and I'll keep my eyes open as I start my foray into Ruby to see what sets them off. Guess this just does show how similar all us geeks really are. Different words, same reactions.

Random question to my readers (all five of you): do you do Ruby? If so, what's the sacred cows there?

18 comments

I thought Ruby itself its own sacred cow?
"I think things like the user authentication system should be able to add framework-wide hooks so when a login_required is encountered in the parameters it can auto-magically make sure that the user is authenticated."

You've played with Symfony, right? (I don't remember if we had this discussion.) You have to love sfGuard and its ability to set "is_secure: on" in the config for your app/module and it does exactly as you ask: auth for any type of access. I, too, appreciate the magic that goes on behind the scenes in a framework like Symfony, that can be tweaked under the hood to take care of the autonomic parts of my app.
While I'm not usually one to shun things, I agree with the Python community in this case. "Magic" is one of the biggest causes of bugs programs; either something magic happens that programmers didn't expect, or something magic that programmers expected to happen didn't. Ask any C++ programmer about implicit conversions, and you'll see what I mean.

This is a fundamentally different issue than Java in the PHP community. I don't know what the Java hatred is based off of, but it's hatred of a single language. The "magic" thing is hatred of a concept, which can be present (and bad) in any language.
I guess I still don't get it, mainly because you've not explained your "third option" at all.

You say:

"I think things like the user authentication system should be able to add framework-wide hooks so when a login_required is encountered in the parameters it can auto-magically make sure that the user is authenticated."

But what does this mean? Does this mean if I accept a parameter of that name, the framework ought to decide, without my consent or yours, that authentication will always be required for this view? What if I want that requirement but you don't? How do you tell the framework, "I want you to make assumptions about anything that looks like this, _except_ for that one and that one and that one"? What if I'm using some other authentication system -- with its own requirements -- and your "helpful" automatic rewriting of my code decides to stick in its own bits anyway?

To compensate for these types of cases, any such system would end up looking like some sort of annotation or even -- gasp! -- decoration of the function to indicate the desired behavior. Which is, you'll note, what's already there in Django.

Which gets us back to having a simple, explicit way to write what you mean and, more importantly, to why all the Python people you know feel the way they feel about this sort of thing. We've learned, the hard way, that the "convenience" of systems that "do our work for us", often, turns out to be a blasted _in_convenience that creates even more work for us.
Having worked with all sorts of frameworks, big and small, I think that you cannot escape running into magic in any framework. It becomes a question of how much "magic" you are willing to tolerate.

Unwillingness to follow conventions a framework operates under is what causes people to hate frameworks, in my opinion.
@David: Awesome! :-)

@John: I've only briefly looked at Symfony a long time ago. At that time there where too many hoops to jump through to get it running. Just didn't fit right. Yeah, I know. I'm contradicting myself. I want a framework that does a lot for me, but not too much.

@Jake: No, its the same thing. PHP developers don't hate Java as a language, they hate it as a concept. They consider it bloated and heavyweight. Mentioning that code looks like Java isn't saying it's like the language; it's saying that it follows OO too close. Why use 50 classes to handle 50 different concerns when you can smash 'em all into 5 classes each responsible for 10 different functions.

@James: No it doesn't mean the framework should explicitly do things for me, unless I tell it to. This is a classic case of dependency injection. I should be able to inject behavior into the system easily. Ideally there'd be a mechanism for observing the views and adding behavior to them. It's not that I don't like the decorator approach, I think it's just more verbose than need-be.

@Chris: Agreed.
@Travis actually The perspective is another Java developers see PHP as the morons of the programming because PHP really is an idiotic and a hack of programming language. PHP 5.3 namespace is so lame and many flaws that language have, so the Java programmers look at PHP as template(Smarty, JSP, Cheetah) and ugly language for the web.
@login_required
def limited_object_list(*args, **kwargs):
return object_list(*args, **kwargs)
"the framework looks for the presence of login_required and decorates the function appropriately"

It's not that simple. Most (well designed) websites aren't a matter of either being logged in or not. What happens when you want to have a logged in version of a view and a non-logged in version?

I mean, I'm fairly particular about my code myself, but is it REALLY that ugly to say login_required(some_function) ?

The thing is, I'd rather do more typing and understand what's happening than save some typing and not know what my code is doing.
Probably the easiest way to get that kind of reaction out of Rubyists is to mention the speed of the MRI interpreter.

"Ruby is so slow." / "Well it's fast enough for me, so shut up!"
Seriously, fuck magic.
Shouldn't it be:

some_view = login_required(some_view)

not:

login_required(some_view)

?

I'm not sure if I'm missing something.
I love that phpers shun java and then use only java based IDEs and tools on the job, the irony!

"Any sufficiently advanced technology is indistinguishable from magic."
SeanJA: i am using the most recent zend studio for php developing, based on eclipse, yes. because it usually deserves convenient auto-completion for all standard php functions as well as everything in my workspace.

but it's always making a good job bringing the cpu load on my box up to 100% for a couple of minutes whenever i open or close a project or sometimes just save a file, making the fans cause a lot of noise and co-workers thinking that my box is about to take off or explode.

so if that's the greatness of java...
@aleks

Throw away Zend and forget about it. It won't be any faster/lighter for next couple of years considerng the eclipse development cycle which is "once a year".

Use netbeans for php and python. You will like it.
Setting off ruby users is really easy. Tell them ruby is slow, or try to do something that none of them have ever thought of, or complain about the lack of documentation on rails.. Actually, come to think of it, say *anything* bad about ruby, and you'll have a pile of 15 year old fat kids trying to play smear the queer.

I get agitated at some communities' express hatred towards magic. I love magic, so long as the magical behavior is easily overridden should I not need it. I'm lazy, and magic makes me code less. Something about writing the same code 500 times (in a large app) turns me off. If I can make some functionality magical enough that I don't have to worry about it anymore - you best believe, it's getting done!
Enjoyed your post. Who doesn't like magic?
I like magic as long as it is clearly defined and it can be easily turned off or replaced with my own desired behavior. It's funny that you mention the Java / PHP thing. I like PHP, but I've never been a huge fan of Java. Who knew I was just doing what comes naturally.

Thanks for the post. It was fun reading.

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)