blob: 42373711c06bcc982906175fc5e569b726e8c44e [file] [log] [blame]
Guido van Rossuma4deda02002-12-23 16:30:00 +00001"""Utilities to support packages."""
2
3import os
4import sys
5
6def extend_path(path, name):
7 """Extend a package's path.
8
9 Intended use is to place the following code in a package's __init__.py:
10
11 from pkgutil import extend_path
12 __path__ = extend_path(__path__, __name__)
13
14 This will add to the package's __path__ all subdirectories of
15 directories on sys.path named after the package. This is useful
16 if one wants to distribute different parts of a single logical
17 package as multiple directories.
18
19 It also looks for *.pkg files beginning where * matches the name
20 argument. This feature is similar to *.pth files (see site.py),
21 except that it doesn't special-case lines starting with 'import'.
22 A *.pkg file is trusted at face value: apart from checking for
23 duplicates, all entries found in a *.pkg file are added to the
24 path, regardless of whether they are exist the filesystem. (This
25 is a feature.)
26
27 If the input path is not a list (as is the case for frozen
28 packages) it is returned unchanged. The input path is not
29 modified; an extended copy is returned. Items are only appended
30 to the copy at the end.
31
32 It is assumed that sys.path is a sequence. Items of sys.path that
33 are not (unicode or 8-bit) strings referring to existing
34 directories are ignored. Unicode items of sys.path that cause
35 errors when used as filenames may cause this function to raise an
36 exception (in line with os.path.isdir() behavior).
37 """
38
39 if not isinstance(path, list):
40 # This could happen e.g. when this is called from inside a
41 # frozen package. Return the path unchanged in that case.
42 return path
43
44 pname = os.path.join(*name.split('.')) # Reconstitute as relative path
45 # Just in case os.extsep != '.'
46 sname = os.extsep.join(name.split('.'))
47 sname_pkg = sname + os.extsep + "pkg"
48 init_py = "__init__" + os.extsep + "py"
49
50 path = path[:] # Start with a copy of the existing path
51
52 for dir in sys.path:
53 if not isinstance(dir, (str, unicode)) or not os.path.isdir(dir):
54 continue
55 subdir = os.path.join(dir, pname)
56 # XXX This may still add duplicate entries to path on
57 # case-insensitive filesystems
58 initfile = os.path.join(subdir, init_py)
59 if subdir not in path and os.path.isfile(initfile):
60 path.append(subdir)
61 # XXX Is this the right thing for subpackages like zope.app?
62 # It looks for a file named "zope.app.pkg"
63 pkgfile = os.path.join(dir, sname_pkg)
64 if os.path.isfile(pkgfile):
65 try:
66 f = open(pkgfile)
67 except IOError, msg:
68 sys.stderr.write("Can't open %s: %s\n" %
69 (pkgfile, msg))
70 else:
71 for line in f:
72 line = line.rstrip('\n')
73 if not line or line.startswith('#'):
74 continue
75 path.append(line) # Don't check for existence!
76 f.close()
77
78 return path