Thanks for all the replies. We really need a 'style guide' on package use, so I hope this example can be thrashed out and used to help others. We need to decide something and release next week, and also provide good docs for other users who will be new to Python. Apart from the ambiguity, which was a surprise but which we can work around, I have some general questions on the "right thing". Here are my examples: 1. use of __init__ to save users work ------------------------------------ ReportLab has a subpackage called 'platypus' which we have just reorganized and not yet released. The division into subpackages is clear - each exposes a different kind of functionality - but the division of each package into modules is based on keeping a sane length for each and not on providing a friendly user interface. reportlab pdfgen canvas.py defines class Canvas textobject.py defines class TextObject pathobject.py defines class PathObject platypus (the same problem, but 3x worse) lib (loads of modules, all independent) We reached a point where users need to import eight or ten classes to do useful work and were having to put eight or ten different import lines at the top of every script. This just feels dumb and wrong. So, the 'least messy' idea was to put __init__.py methods in each subpackage which explicitly import what they need. (We have not used "import *") ---reportlab/pdfgen/__init__.py-------- from canvas import Canvas from textobject.py import TextObject from pathobject.py impory PathObject Then users can just do: from reportlab.pdfgen import Canvas, PathObject, TextObject It seems that this (a) makes life easier for users, and (b) gives us freedom to refactor code as it grows, without changing the user interface. Is this style evil? If so, why? 2. Ambiguities running scripts within package --------------------------------------------- We have various test functions scattered throughout our distribution. A control script iterates through the lot and kicks them off before every release, executing everything with an 'if __name__=='__main__' handler. If these were standalone scripts just for testing, they could import "reportlab.platypus.tables" or whatever. But if I just want to put a test routine at the end of "reportlab.platypus.tables" then I seem destined to run into exactly the ambiguity outlined before. Should test scripts all be moved out of the package? Indeed, is Python behaving the way it ought to? 3. Preferred subpackage import technique ---------------------------------------- I have two scripts 'spam.py' and 'eggs.py' in subpackage 'A.B'. spam imports eggs. Should it say... import eggs or... import A.B.eggs? Both work sometimes, but can lead to different behavious sometimes. Which is the preferred style? Thanks, Andy Robinson
Hi Andy,
Thanks for all the replies. We really need a 'style guide' on package use,
On 'import', packages and modules: ---------------------------------- You should definitely have a look at the approach used by Greg McFarlane in his 'Pmw' package. In particular at his PmwLoader class and the Pmw.def. For my own project I've simplified and somewhat generalized his approach to dynamic and lazy importing. I posted a preliminary implemantation of this to comp.lang.python a while ago but didn't got much feedback. May be due to fact, that I had to repost the thing, because the TABs went wrong in the first try :-( [...]
Is this style evil? If so, why?
I believe it will get really hard to come up with a 'namespace style guide' which everybody can aggree on. But who cares: let's start a rant: --> IMO 'from ... import ...' is EVIL and its use should be banned! <-- Why? Because if software becomes bigger (> 100k LOC), these unqualified names definitely become a maintaince nightmare, since you didn't know what names are defined in the module you are currently staring at and what names come from the "outside world". I believe Python inherited two of its three forms of import syntax at least in part from its anchestors Modula-2 and Modula-3. My perception of this import-problem may be blurred by programming in Modula-2 in our company since 1985, where the code base now is about ~1 Mio. LOC. Some of the older code contains still modules using a lot of 'FROM foobar IMPORT ....;' lines in the header. These have usually been harder to read and understand by new employees than those having 'IMPORT foobar;' in the header and than using 'foobar.component' everywhere in the code body below. from .... import * is *EVEN MORE EVIL*. Well: at least if people use 'from' to import arbitrary objects and classes. Using 'from package.subpackage import module' is an idiom, with which I can live with comfortably.
Should test scripts all be moved out of the package?
This would defeat the use of Tim Peters wonderful 'doctest' module. I'm still thinking, 'doctest' should be made part of the standard library in order to give the publicity this elegant approach deserves. Regards, Peter
On Thu, Jun 08, 2000 at 11:35:14AM +0200, Peter Funk wrote:
...
Is this style evil? If so, why?
I believe it will get really hard to come up with a 'namespace style guide' which everybody can aggree on. But who cares: let's start a rant:
--> IMO 'from ... import ...' is EVIL and its use should be banned! <--
You are being overly strict here. I use this all the time: from module import submodule ... submodule.Class() or even from module.submodule import subsub ... subsub.Blargle() In other words, "from package import module" is a Fine Thing. It can simplify your code without hiding where the symbols come from. In general, I agree with you: importing symbols from a module into your namespace is a Bad Thing. Cheers, -g -- Greg Stein, http://www.lyra.org/
Hi Greg, [me]
--> IMO 'from ... import ...' is EVIL and its use should be banned! <--
[Greg Stein]:
You are being overly strict here. I use this all the time:
from module import submodule ... submodule.Class()
or even
from module.submodule import subsub ... subsub.Blargle()
Okay. Later in my rant I wrote: < Well: at least if people use 'from' to import arbitrary objects and < classes. Using 'from package.subpackage import module' is an idiom, < with which I can live with comfortably. [Greg Stein]:
In other words, "from package import module" is a Fine Thing. It can simplify your code without hiding where the symbols come from.
Yes, you are right. But if you put the facade pattern[*] into use within such packages, this will even simplify your live, if you later have to redesign the internal substructure of your package. [*] Design patterns and especially the "facade pattern" were AFAIK introduced in the so called "Gang of four" book: "Design Patterns - Elements of Reusable Object-Oriented Software", Addison Wesley, 1995. The somewhat similar "whole part" design pattern is discussed in the book "A System of Patterns" written by Buschmann, Meunier, Rohnert, Sommerlad, Stal; published by John Wiley & Sons, New York; 1996; ISBN 0 471 95869 7
In general, I agree with you: importing symbols from a module into your namespace is a Bad Thing.
Fine. Consensus gives a warm and fuzzy feeling. ;-) Regards, Peter
Andy wrote:
1. use of __init__ to save users work [package structure] reportlab pdfgen canvas.py defines class Canvas textobject.py defines class TextObject pathobject.py defines class PathObject platypus (the same problem, but 3x worse) lib (loads of modules, all independent)
---reportlab/pdfgen/__init__.py-------- from canvas import Canvas from textobject.py import TextObject from pathobject.py impory PathObject
I would suggest that these not use relative names. from reportlab.pdfgen.canvas import Canvas will ensure that Canvas is only found in one way (no matter where the script is, or rather, what sys.path[0] evaluates to at import time). Your other choice is to disallow any scripts within reportlab/ and deliver 220 V shocks to any user who starts Python anyplace within reportlab/ (or otherwise lets any part of sys.path evaluate to anything within reportlab/).
Then users can just do: from reportlab.pdfgen import Canvas, PathObject, TextObject
It seems that this (a) makes life easier for users, and (b) gives us freedom to refactor code as it grows, without changing the user interface.
Since you're doing this for "convenience", another option is to provide convenience methods: def newCanvas(): return reportlab.pdfgen.canvas.Canvas()
2. Ambiguities running scripts within package ---------------------------------------------
Should test scripts all be moved out of the package? Indeed, is Python behaving the way it ought to?
If you use relative imports, yes. But even so, you incur a risk in using relative imports (see above). And you only do it from laziness. So I say don't use relative imports.
3. Preferred subpackage import technique ---------------------------------------- I have two scripts 'spam.py' and 'eggs.py' in subpackage 'A.B'. spam imports eggs. Should it say... import eggs or... import A.B.eggs?
You know what I'm going to say... - Gordon
participants (4)
-
Andy Robinson
-
Gordon McMillan
-
Greg Stein
-
pf@artcom-gmbh.de