| """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, basestring) 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 |