Commit 92a61578 authored by PJ Eby's avatar PJ Eby

Support namespace packages in conjunction with system packagers, by omitting

the installation of any ``__init__.py`` files for namespace packages, and
adding a special ``.pth`` file to create a working package in ``sys.modules``.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4043119
parent 0b0d41cd
......@@ -1225,21 +1225,18 @@ packages), in a normal Python package layout. These ``__init__.py`` files
This code ensures that the namespace package machinery is operating and that
the current package is registered as a namespace package.
You can include other code and data in a namespace package's ``__init__.py``,
but it's generally not a great idea because the loading order of each
project's namespace packages is not guaranteed, and thus the actual contents
of the parent package at runtime may vary from one machine to another. While
it's true that you won't have such conflicts if only one project defines the
contents of a particular namespace package's ``__init__.py``, it's less error-
prone to just leave ``__init__.py`` empty except for the declaration line.
(Note that this non-deterministic ordering also means that you must include
the declaration line in the ``__init__.py`` of *every* project that has
contents for the namespace package in question, in order to ensure that the
namespace will be declared regardless of which project's copy of
``__init__.py`` is loaded first. If the first loaded ``__init__.py`` doesn't
declare it, it will never *be* declared, because no other copies will ever be
loaded!)
You must NOT include any other code and data in a namespace package's
``__init__.py``. Even though it may appear to work during development, or when
projects are installed as ``.egg`` files, it will not work when the projects
are installed using "system" packaging tools -- in such cases the
``__init__.py`` files will not be installed, let alone executed.
You must include the ``declare_namespace()`` line in the ``__init__.py`` of
*every* project that has contents for the namespace package in question, in
order to ensure that the namespace will be declared regardless of which
project's copy of ``__init__.py`` is loaded first. If the first loaded
``__init__.py`` doesn't declare it, it will never *be* declared, because no
other copies will ever be loaded!)
TRANSITIONAL NOTE
......@@ -2352,6 +2349,11 @@ Release Notes/Change History
----------------------------
0.6a11
* Support namespace packages in conjunction with system packagers, by omitting
the installation of any ``__init__.py`` files for namespace packages, and
adding a special ``.pth`` file to create a working package in
``sys.modules``.
* Made ``--single-version-externally-managed`` automatic when ``--root`` is
used, so that most system packagers won't require special support for
setuptools.
......
......@@ -1288,6 +1288,8 @@ def get_exe_prefixes(exe_filename):
break
if len(parts)<>2 or not name.endswith('.pth'):
continue
if name.endswith('-nspkg.pth'):
continue
if parts[0] in ('PURELIB','PLATLIB'):
pth = z.read(name).strip()
prefixes[0] = ('PURELIB/%s/' % pth), ''
......@@ -1308,8 +1310,6 @@ def parse_requirement_arg(spec):
)
class PthDistributions(Environment):
"""A .pth file with Distribution paths in it"""
......
......@@ -37,7 +37,7 @@ class install_egg_info(Command):
self.execute(self.copytree, (),
"Copying %s to %s" % (self.source, self.target)
)
self.install_namespaces()
def get_outputs(self):
return self.outputs
......@@ -58,25 +58,25 @@ class install_egg_info(Command):
unpack_archive(self.source, self.target, skimmer)
def install_namespaces(self):
nsp = (self.distribution.namespace_packages or [])[:]
if not nsp: return
nsp.sort() # set up shorter names first
filename,ext = os.path.splitext(self.target)
filename += '-nspkg.pth'; self.outputs.append(filename)
log.info("Installing %s",filename)
if not self.dry_run:
f = open(filename,'wb')
for pkg in nsp:
pth = tuple(pkg.split('.'))
f.write(
"import sys,new; "
"m = sys.modules.setdefault(%(pkg)r,new.module(%(pkg)r)); "
"p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
"*%(pth)r); "
"mp = m.__path__ = getattr(m,'__path__',[]); "
"(p not in mp) and mp.append(p)\n"
% locals()
)
f.close()
from distutils.command.install_lib import install_lib as _install_lib
import os
class install_lib(_install_lib):
"""Don't add compiled flags to filenames of non-Python files"""
......@@ -15,7 +16,6 @@ class install_lib(_install_lib):
return bytecode_files
def run(self):
self.build()
outfiles = self.install()
......@@ -23,3 +23,60 @@ class install_lib(_install_lib):
# always compile, in case we have any extension stubs to deal with
self.byte_compile(outfiles)
def get_exclusions(self):
exclude = {}
nsp = self.distribution.namespace_packages
if (nsp and self.get_finalized_command('install')
.single_version_externally_managed
):
for pkg in nsp:
parts = pkg.split('.')
while parts:
pkgdir = os.path.join(self.install_dir, *parts)
for f in '__init__.py', '__init__.pyc', '__init__.pyo':
exclude[os.path.join(pkgdir,f)] = 1
parts.pop()
return exclude
def copy_tree(
self, infile, outfile,
preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
):
assert preserve_mode and preserve_times and not preserve_symlinks
exclude = self.get_exclusions()
if not exclude:
return _install_lib.copy_tree(self, infile, outfile)
# Exclude namespace package __init__.py* files from the output
from setuptools.archive_util import unpack_directory
from distutils import log
outfiles = []
def pf(src, dst):
if dst in exclude:
log.warn("Skipping installation of %s (namespace package)",dst)
return False
log.info("copying %s -> %s", src, os.path.dirname(dst))
outfiles.append(dst)
return dst
unpack_directory(infile, outfile, pf)
return outfiles
def get_outputs(self):
outputs = _install_lib.get_outputs(self)
exclude = self.get_exclusions()
if exclude:
return [f for f in outputs if f not in exclude]
return outputs
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment