79 lines
3.0 KiB
Python
79 lines
3.0 KiB
Python
|
"""Utilities to support packages."""
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
|
||
|
def extend_path(path, name):
|
||
|
"""Extend a package's path.
|
||
|
|
||
|
Intended use is to place the following code in a package's __init__.py:
|
||
|
|
||
|
from pkgutil import extend_path
|
||
|
__path__ = extend_path(__path__, __name__)
|
||
|
|
||
|
This will add to the package's __path__ all subdirectories of
|
||
|
directories on sys.path named after the package. This is useful
|
||
|
if one wants to distribute different parts of a single logical
|
||
|
package as multiple directories.
|
||
|
|
||
|
It also looks for *.pkg files beginning where * matches the name
|
||
|
argument. This feature is similar to *.pth files (see site.py),
|
||
|
except that it doesn't special-case lines starting with 'import'.
|
||
|
A *.pkg file is trusted at face value: apart from checking for
|
||
|
duplicates, all entries found in a *.pkg file are added to the
|
||
|
path, regardless of whether they are exist the filesystem. (This
|
||
|
is a feature.)
|
||
|
|
||
|
If the input path is not a list (as is the case for frozen
|
||
|
packages) it is returned unchanged. The input path is not
|
||
|
modified; an extended copy is returned. Items are only appended
|
||
|
to the copy at the end.
|
||
|
|
||
|
It is assumed that sys.path is a sequence. Items of sys.path that
|
||
|
are not (unicode or 8-bit) strings referring to existing
|
||
|
directories are ignored. Unicode items of sys.path that cause
|
||
|
errors when used as filenames may cause this function to raise an
|
||
|
exception (in line with os.path.isdir() behavior).
|
||
|
"""
|
||
|
|
||
|
if not isinstance(path, list):
|
||
|
# This could happen e.g. when this is called from inside a
|
||
|
# frozen package. Return the path unchanged in that case.
|
||
|
return path
|
||
|
|
||
|
pname = os.path.join(*name.split('.')) # Reconstitute as relative path
|
||
|
# Just in case os.extsep != '.'
|
||
|
sname = os.extsep.join(name.split('.'))
|
||
|
sname_pkg = sname + os.extsep + "pkg"
|
||
|
init_py = "__init__" + os.extsep + "py"
|
||
|
|
||
|
path = path[:] # Start with a copy of the existing path
|
||
|
|
||
|
for dir in sys.path:
|
||
|
if not isinstance(dir, (str, unicode)) or not os.path.isdir(dir):
|
||
|
continue
|
||
|
subdir = os.path.join(dir, pname)
|
||
|
# XXX This may still add duplicate entries to path on
|
||
|
# case-insensitive filesystems
|
||
|
initfile = os.path.join(subdir, init_py)
|
||
|
if subdir not in path and os.path.isfile(initfile):
|
||
|
path.append(subdir)
|
||
|
# XXX Is this the right thing for subpackages like zope.app?
|
||
|
# It looks for a file named "zope.app.pkg"
|
||
|
pkgfile = os.path.join(dir, sname_pkg)
|
||
|
if os.path.isfile(pkgfile):
|
||
|
try:
|
||
|
f = open(pkgfile)
|
||
|
except IOError, msg:
|
||
|
sys.stderr.write("Can't open %s: %s\n" %
|
||
|
(pkgfile, msg))
|
||
|
else:
|
||
|
for line in f:
|
||
|
line = line.rstrip('\n')
|
||
|
if not line or line.startswith('#'):
|
||
|
continue
|
||
|
path.append(line) # Don't check for existence!
|
||
|
f.close()
|
||
|
|
||
|
return path
|