Skip to content

Add a namespace module. #832

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
CHANGES
=======

v28.7.0
-------

* Moved much of the namespace package handling functionality
into a separate module for re-use in something like #789.

v28.6.1
-------

Expand Down
61 changes: 2 additions & 59 deletions setuptools/command/install_egg_info.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from distutils import log, dir_util
import os

from setuptools.extern.six.moves import map

from setuptools import Command
from setuptools import namespaces
from setuptools.archive_util import unpack_archive
import pkg_resources


class install_egg_info(Command):
class install_egg_info(namespaces.Installer, Command):
"""Install an .egg-info directory for the package"""

description = "Install an .egg-info directory for the package"
Expand Down Expand Up @@ -61,59 +60,3 @@ def skimmer(src, dst):
return dst

unpack_archive(self.source, self.target, skimmer)

def install_namespaces(self):
nsp = self._get_all_ns_packages()
if not nsp:
return
filename, ext = os.path.splitext(self.target)
filename += '-nspkg.pth'
self.outputs.append(filename)
log.info("Installing %s", filename)
lines = map(self._gen_nspkg_line, nsp)

if self.dry_run:
# always generate the lines, even in dry run
list(lines)
return

with open(filename, 'wt') as f:
f.writelines(lines)

_nspkg_tmpl = (
"import sys, types, os",
"pep420 = sys.version_info > (3, 3)",
"p = os.path.join(sys._getframe(1).f_locals['sitedir'], *%(pth)r)",
"ie = os.path.exists(os.path.join(p,'__init__.py'))",
"m = not ie and not pep420 and "
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))",
"mp = (m or []) and m.__dict__.setdefault('__path__',[])",
"(p not in mp) and mp.append(p)",
)
"lines for the namespace installer"

_nspkg_tmpl_multi = (
'm and setattr(sys.modules[%(parent)r], %(child)r, m)',
)
"additional line(s) when a parent package is indicated"

@classmethod
def _gen_nspkg_line(cls, pkg):
# ensure pkg is not a unicode string under Python 2.7
pkg = str(pkg)
pth = tuple(pkg.split('.'))
tmpl_lines = cls._nspkg_tmpl
parent, sep, child = pkg.rpartition('.')
if parent:
tmpl_lines += cls._nspkg_tmpl_multi
return ';'.join(tmpl_lines) % locals() + '\n'

def _get_all_ns_packages(self):
"""Return sorted list of all package namespaces"""
nsp = set()
for pkg in self.distribution.namespace_packages or []:
pkg = pkg.split('.')
while pkg:
nsp.add('.'.join(pkg))
pkg.pop()
return sorted(nsp)
93 changes: 93 additions & 0 deletions setuptools/namespaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os
from distutils import log
import itertools

from setuptools.extern.six.moves import map


flatten = itertools.chain.from_iterable


class Installer:

nspkg_ext = '-nspkg.pth'

def install_namespaces(self):
nsp = self._get_all_ns_packages()
if not nsp:
return
filename, ext = os.path.splitext(self._get_target())
filename += self.nspkg_ext
self.outputs.append(filename)
log.info("Installing %s", filename)
lines = map(self._gen_nspkg_line, nsp)

if self.dry_run:
# always generate the lines, even in dry run
list(lines)
return

with open(filename, 'wt') as f:
f.writelines(lines)

def _get_target(self):
return self.target

_nspkg_tmpl = (
"import sys, types, os",
"pep420 = sys.version_info > (3, 3)",
"p = os.path.join(%(root)s, *%(pth)r)",
"ie = os.path.exists(os.path.join(p,'__init__.py'))",
"m = not ie and not pep420 and "
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))",
"mp = (m or []) and m.__dict__.setdefault('__path__',[])",
"(p not in mp) and mp.append(p)",
)
"lines for the namespace installer"

_nspkg_tmpl_multi = (
'm and setattr(sys.modules[%(parent)r], %(child)r, m)',
)
"additional line(s) when a parent package is indicated"

def _get_root(self):
return "sys._getframe(1).f_locals['sitedir']"

def _gen_nspkg_line(self, pkg):
# ensure pkg is not a unicode string under Python 2.7
pkg = str(pkg)
pth = tuple(pkg.split('.'))
root = self._get_root()
tmpl_lines = self._nspkg_tmpl
parent, sep, child = pkg.rpartition('.')
if parent:
tmpl_lines += self._nspkg_tmpl_multi
return ';'.join(tmpl_lines) % locals() + '\n'

def _get_all_ns_packages(self):
"""Return sorted list of all package namespaces"""
pkgs = self.distribution.namespace_packages or []
return sorted(flatten(map(self._pkg_names, pkgs)))

@staticmethod
def _pkg_names(pkg):
"""
Given a namespace package, yield the components of that
package.

>>> names = Installer._pkg_names('a.b.c')
>>> set(names) == set(['a', 'a.b', 'a.b.c'])
True
"""
parts = pkg.split('.')
while parts:
yield '.'.join(parts)
parts.pop()


class DevelopInstaller(Installer):
def _get_root(self):
return repr(str(self.egg_path))

def _get_target(self):
return self.egg_link