[Distutils] Freeze and new import architecture
Fri, 18 Dec 1998 11:45:53 +1100
[Apols in advance for the length of this - but the background is
I will make no attempt to outline what the freeze tool should
ultimately be! Im not even going to outline what freeze is now! Im
just going to focus on one single requirement.
We want freeze to be capable of working with any number of "code
containers" - ie, to be able to locate code in "frozen C modules" (as
it works now), or potentially in a .zip file etc.
The justification is fairly easy to explain: Consider that you may
want to use "freeze" to distribute an application that consists of a
number of Python programs. Using freeze as it stands now, the entire
Python library is frozen into each application. Thus the result is
each program is frozen to an executable that may be megabytes in size,
even though the bulk is likely to be the identical, standard Python
Attempting to cut a long story short, Guido, Jack Jansen, myself and
Just came up with another idea for an extensible import mechanism:
sys.path not be restricted to path names. sys.path has "strings", and
an associated map of "module finders". Thus, a sys.path entry could
have a directory name (like now) or .zip file, URL, etc.
I have included below a mail from Jack Jansen on this topic. To
paraphrase Guido's response, it was: "looks good - but lets define a
Python interface so it may work with JPython"
Accordingly, I have mocked up a few .py files which start to implement
the ideas in Jack's post. However, Im now floundering a little as to
the best way to take this any further. Im really looking for further
support or critisism of the idea, and some interest from people in
helping take this further... If there is general interest, I will
post my mock-up, which allows directories and URLs to exist on
From: Jack Jansen [mailto:Jack.Jansen@cwi.nl]
Sent: Wednesday, 12 August 1998 1:11 AM
To: email@example.com; firstname.lastname@example.org; email@example.com
Subject: An import architecture
I thought I'd just mail you the results of a discussion Mark and
about the import architecture. I'm just sending it out to us four, but
we should get more people into the discussion if Guido thinks that
something worth "solving".
The problem we're looking at is that import.c and importdl.c have lots
special case code, and everytime someone comes up with a new neat way
import modules more special case code has to be added. This happened
PYC resource modules and PYD modules, and there's also importdl.c
about DLL modules for umpteen different systems. It would be nice if
an architecture in import.c that would allow the machine specific code
into (and, ultimately, Python code as well).
WHAT WE HAVE
We noticed that there are really two issues involved in importing
1. Finding the module in a specific namespace.
2. Importing a module of a specific type, once it has been found.
For 1. we currently have 5 namespaces: builtin modules, frozen
filesystem namespace, the PYC resource namespace in a file (mac-only)
PYD resource namespace in a file (again mac-only). Other namespaces
envisioned, for instance the namespace inside a squeeze .pyz archive,
web-based namespace, whatever.
The builtin and frozen namespace are currently special, in that they
occur in sys.path and are always virtually at the very front of
the mac sys.path entries can be either filenames (in which case the
modulefinders are invoked) or directories (in which case the
is invoked), on other platforms there are only directories in sys.path
Regarding 2: the finder currently returns a structure that enables the
importer to be called later on. Importers that we have are for
frozen, .py/.pyc/.pyo modules, various dll-importers all hiding behind
same interface, PYC-resource importers (mac-only) and PYD-resource
WHAT WE WANT
What we'd like I'll try to describe top-down (hopefully better to
importing a module becomes something like
for pathentry in sys.path:
finder = getfinder(pathentry)
loader = finder.find(module)
module = loader()
getfinder() is something like
if not path_to_finder.has_key(pathentry):
for f in all_finder_types:
finder = f.create_finder(pathentry)
path_to_finder[pathentry] = finder
And there would be a call whereby a finder type registers itself (adds
finder.create_finder() would examine the current path component, see
could handle it, and, if so, return a finder object that will search
component everytime it is invoked. The usual case is that getfinder
much: only the first time you see a new entry in sys.path you'll have
all the finders in turn whether they support it, ad you just remember
The loaders register themselves with the finders, passing
arguments. For instance, the .py loader registers itself with the
finder, telling it that the ".py" extension should result in a .py
being created. The unix-specific dll-loader does the same for .so
the windows-dll-loader for .dll, the mac-dll-loader for .slb etc.
The PYC-resource loader tells the mac-resource-finder to look for 'PYC
resources, the PYD-resource loader tells it to look for 'PYD '
The information that is passed from the finder to loader when it has
module is again finder-specific: the filesystem loader will probably
open file and a filename, etc.
A loader can register itself with multiple finders, assuming their
are similar. So, the .py loader could register itself not only with
filesystem finder but also with a url-based finder or something, as
that url-based finder uses the same calling convention for creating
WHAT DOES IT BUY US
A greatly simplified import.c, importdl.c split out over the various
(and with the possibility to pass machine-specific info from the find
the load phase, something that cant be done now and leads to double
various platforms) and easy extensibility.
There is the issue of performance. The description above is all
going through the Python calling sequence for all these things is
a good idea from a performance standpoint. This is however fixable.
objects involved are
- The finder "class", the thing you use to check whether a certain
component can be handled by this code
- The finder instance returned by this class
- The importer class (called by the finder objects)
- The importer instance (returned by the finder instance through
These 4 things could well be 4 specific PyObject types, with the
C-routines in the object struct. Calling these for the normal case
they're implemented in C) would be at most a single (C-) indirection
expensive than the current scheme.
Moreover, it would be easy to create a module that would implement
types as wrappers around Python code. The C-routines would then do the
PyEval_CallObject stuff to call the Python implementation. So, you'd
generality but you'd only pay for it when you put Python-handled
sys.path and actually hit those entries.
ODDS AND ENDS
A side issue: this stuff would also allow us to put the builtin and
namespace into sys.path explicitly, for instance as "__builtins__" and
"__frozen__", something I would like. The disadvantage would be that
be sure that everything on sys.path is a pathname, but the advantage
that you could, for instance create a frozen program that you could
set sys.path to ["/usr/local/FooPatches", "__frozen__",
whenever you have a patch to a single module in a frozen executable
send your clients the single .pyc file and tell them to put it in the
FooPatches directory. There'd probably have to be a bit of code that
explicitly prepends "__frozen__" and "__builtins__" to sys.path if
there already or something.
Jack Jansen | ++++ stop the execution of Mumia Abu-Jamal
Jack.Jansen@cwi.nl | ++++ if you agree copy these lines to your
http://www.cwi.nl/~jack | see