Setuptools, __init__ and __main__

Ben Finney ben+python at benfinney.id.au
Fri Feb 6 17:56:20 EST 2015


Rob Gaddi <rgaddi at technologyhighland.invalid> writes:

> So, I'll take the app I'm working on right now as an example. I've got
> 348 lines in my __init__.py, and another 200 in another library.
> Little baby program.

By “another library”, I think you mean “another module”. I'll assume
that in the rest of this response.

> My library code isn't in __init__.py (or previously in __main__)
> because I'm trying to dodge recursive imports; it's there because the
> code is short enough and tightly-coupled enough that I didn't see
> upside in splitting any of it out to a separate file.

Sure, there's no need to make more modules if the code is small and
coherent.

But you describe it as “tightly-coupled”; that's a warning sign already.
How do you automate tests of its individual units? The code should be in
separate functions with tightly-defined scope and narrow, well-specified
interfaces.

> Under other circumstances when the app has become substantial, the
> bulk of the application code winds up in, e.g. mainwindow.py, and the
> __main__ does nothing but call mainwindow.main().

Great! So you already have an entry point for Setuptools: the entry
point is something like "fooapp.mainwindow:main".

> The advice to reduce the entry point code (be it __init__ or __main__)
> to a small stub and move the bulk of the code to another [module]
> doesn't change the underlying situation. Setuptools seems to require
> that the application be a "package", i.e. a thing that has an
> __init__.py. But the python application start mechanism seems to be
> steering me to using a __main__.

It's not Setuptools per se, it's Python's import mechanism. It is a
deliberate design decision that direct import of a module makes that
module blind to its location in the package hierarchy.

That's a design decision I deplore, because it makes something that
should be easy (write a command directly into a file and invoke that
file as an executable program) tortuously difficult when the program
comprises several modules.

> I found a recommendation at https://chriswarrick.com/blog/2014/09/15/
> python-apps-the-right-way-entry_points-and-scripts/ that seems to say 
> that I should get around this by simply having an empty __init__.

More importantly, the core tip there is that you can write your entry
point as "fooapp.__main__:main".

> I guess I can move the program to foobar.py, have an empty __init__, a
> stub __main__ that just calls foobar.main(), and an entry-point of
> foobar.foobar:main . It just feels like a bit of a heavy solution to
> have two unnecessary files.

Well, they're small but not unnecessary. They're explicitly telling
Python the meaning of a particular directory, rather than leaving it to
guess.

The quirks of Python's import system, and especially the implications
for trying to just get ‘./fooapp’ working, are covered in a “here's some
traps to avoid” article by Nick Coghlan
<URL:http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html>.

I really wish Python had a better solution for this, but it doesn't.
This is one of the few downsides of Python's design IMO, and we just
need to work with it.

-- 
 \      “I find the whole business of religion profoundly interesting. |
  `\     But it does mystify me that otherwise intelligent people take |
_o__)                                    it seriously.” —Douglas Adams |
Ben Finney




More information about the Python-list mailing list