Tuesday, August 29, 2006

More lame files - __init__.py and browser.py

Since having a blog gives me a license to rant...

Don't put code in __init__.py files. Imports, sys.modules mangling, deprecation warnings - all fine. But real functionality? Blah... I never look at __init__.py files and I wouldn't expect anyone else to do it in my own code. In 994 years they may even become less important.

Secondly - and relatedly, think ahead. If you have only one view now but you may need ten in a week's time, don't create a browser.py for the 'browser' module (which, by Zope 3 convention, is where your browser views and resources tend to live). First of all, you may need a template, which lives best in browser/template.pt. Secondly, most applications or products have several views, and having tons of classes defined in one ever-growing browser.py is a pain. At some point, you'll refactor them into sub-files and get into an import jungle.

Of course, if you're creating something that by definition should have only one view (i.e. a piece of infrastructure), then great - dont' clutter it up with unnecessary directories. But if you're building something at the Plone level, like a set of content types, they'll probably have more than one view, and creating a directory isn't going to kill you.

Oh - and thirdly.... we shouldn't have called the file for the @@plone view plone.py. Why? Because we now want to have namespaces like plone.portlets. That leads to things like this:
IPortletManager = sys.modules['plone.portlets.interfaces'].IPortletManager
IPortletManagerRenderer = sys.modules['plone.portlets.interfaces'].IPortletManagerRenderer
Can you figure out why this wouldn't work?
from plone.portlets.interfaces import IPortletManager
Python 2.5 is rumoured to have relative imports ala ZCML which may make this better, but it won't solve all problems.

Ah, I feel all better now.

5 comments:

Unknown said...

I have to disagree a bit here.

Though I agree that code in __init__.py is probably bad in all but the simplest cases. It is done quite often in zope 3 itself, and the new pattern in zope 3 land seems to be making private modules like _myfunctionality.py and importing the needed objects from there into __init__.py, which seems like a nice enough way to do things.

However, I would say browser.py is perfectly acceptable for some small packages (and shouldn't they all be small really), or one that's just starting out. Just like interfaces.py, it's easy to grow into a full browser package when you need it, and there are no backwards compatibility issues created by doing so.

Unknown said...

Now now, Martin. Not so fast...

I personally find nothing wrong with putting code in __init__.py. It might be considered bad style by others, but I they usually can't bring up any good points. The fact that you never look in __init__.py is definitely not a reason for not doing it. I guess you better start searching those files as well...

Also, for really small packaes that are mostly about browser things anyhoo, it makes no sense to create pointless structure by introducing a 'browser' subpackage. A browser module is totally enough, the templates can just as well live in the original package itself. NOT creating a directory won't kill you either.

The whole sibling-modules-shadow-toplevel-packages thing is an unfortunate Python wart and will really be fixed in Python 3.0 (which is not as much vaporware as you seem to imply). One can indeed criticize the plone view, but mostly because of giving a view a weirdo name like that in the first place. context/@@plone -- what does that say about the view? Nothing! context/@@ploneapi would've been a bit clearer at least. And the module could've been called ploneapi.py.

Unknown said...

Hah, it seems Alec beat me to exactly what I wanted to say. I actually started my comment 1 hour ago and got distracted by cooking :).

Martin Aspeli said...

We agree on the browser.py thing. It's just that I've seen a few where people start with a file, can't be bothered to do the svn dance, and end up putting many, unrelated views there. You probably know when you're going to have more than one or two, in which case, create the directory.

About __init__.py thing, doing a imports from private modules doesn't bother me at all (it's probably good practice, to avoid namespace pollution).

What does bother me is that the name has no meaning. The worst example is Products/Archetypes/Schema/__init__.py (not a great example, I know). There is tons of stuff in there, for little good reason.

So, if you had a browser/__init__.py with all of your code, then you might as well just have browser.py. If you have some code (except for imports/bbb/deprecation warnings) in __init__.py and some code in files with real names that have semantic meaning, not obvious to me that I should be looking in the __init__.py in the first place.

If you want to do a from foo.browser import then do the import in browser (like we do in interfaces/ for example), but keep the code in more easily managable modules with meaningful names.

At least that's what I'd do ;-)

Anonymous said...

Unlimited Earnings Potential - http://1greatfuture.com

Our company is rapidly growing and offers you an extraordinary income helping others succeed. The primary requirement is to follow up on client inquiries and point them in the right direction. It is stress free, rewarding and straightforward work.

For complete details: http://1greatfuture.com


(Please feel free to delete this post if you don't want it on your blog. Thanks for the informative blog and opportunity to post.)