OLD | NEW |
(Empty) | |
| 1 """Extensions to the 'distutils' for large or complex distributions""" |
| 2 |
| 3 import os |
| 4 import sys |
| 5 import distutils.core |
| 6 import distutils.filelist |
| 7 from distutils.core import Command as _Command |
| 8 from distutils.util import convert_path |
| 9 from fnmatch import fnmatchcase |
| 10 |
| 11 import setuptools.version |
| 12 from setuptools.extension import Extension |
| 13 from setuptools.dist import Distribution, Feature, _get_unpatched |
| 14 from setuptools.depends import Require |
| 15 from setuptools.compat import filterfalse |
| 16 |
| 17 __all__ = [ |
| 18 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', |
| 19 'find_packages' |
| 20 ] |
| 21 |
| 22 __version__ = setuptools.version.__version__ |
| 23 |
| 24 bootstrap_install_from = None |
| 25 |
| 26 # If we run 2to3 on .py files, should we also convert docstrings? |
| 27 # Default: yes; assume that we can detect doctests reliably |
| 28 run_2to3_on_doctests = True |
| 29 # Standard package names for fixer packages |
| 30 lib2to3_fixer_packages = ['lib2to3.fixes'] |
| 31 |
| 32 |
| 33 class PackageFinder(object): |
| 34 @classmethod |
| 35 def find(cls, where='.', exclude=(), include=('*',)): |
| 36 """Return a list all Python packages found within directory 'where' |
| 37 |
| 38 'where' should be supplied as a "cross-platform" (i.e. URL-style) |
| 39 path; it will be converted to the appropriate local path syntax. |
| 40 'exclude' is a sequence of package names to exclude; '*' can be used |
| 41 as a wildcard in the names, such that 'foo.*' will exclude all |
| 42 subpackages of 'foo' (but not 'foo' itself). |
| 43 |
| 44 'include' is a sequence of package names to include. If it's |
| 45 specified, only the named packages will be included. If it's not |
| 46 specified, all found packages will be included. 'include' can contain |
| 47 shell style wildcard patterns just like 'exclude'. |
| 48 |
| 49 The list of included packages is built up first and then any |
| 50 explicitly excluded packages are removed from it. |
| 51 """ |
| 52 out = cls._find_packages_iter(convert_path(where)) |
| 53 out = cls.require_parents(out) |
| 54 includes = cls._build_filter(*include) |
| 55 excludes = cls._build_filter('ez_setup', '*__pycache__', *exclude) |
| 56 out = filter(includes, out) |
| 57 out = filterfalse(excludes, out) |
| 58 return list(out) |
| 59 |
| 60 @staticmethod |
| 61 def require_parents(packages): |
| 62 """ |
| 63 Exclude any apparent package that apparently doesn't include its |
| 64 parent. |
| 65 |
| 66 For example, exclude 'foo.bar' if 'foo' is not present. |
| 67 """ |
| 68 found = [] |
| 69 for pkg in packages: |
| 70 base, sep, child = pkg.rpartition('.') |
| 71 if base and base not in found: |
| 72 continue |
| 73 found.append(pkg) |
| 74 yield pkg |
| 75 |
| 76 @staticmethod |
| 77 def _all_dirs(base_path): |
| 78 """ |
| 79 Return all dirs in base_path, relative to base_path |
| 80 """ |
| 81 for root, dirs, files in os.walk(base_path, followlinks=True): |
| 82 for dir in dirs: |
| 83 yield os.path.relpath(os.path.join(root, dir), base_path) |
| 84 |
| 85 @classmethod |
| 86 def _find_packages_iter(cls, base_path): |
| 87 dirs = cls._all_dirs(base_path) |
| 88 suitable = filterfalse(lambda n: '.' in n, dirs) |
| 89 return ( |
| 90 path.replace(os.path.sep, '.') |
| 91 for path in suitable |
| 92 if cls._looks_like_package(os.path.join(base_path, path)) |
| 93 ) |
| 94 |
| 95 @staticmethod |
| 96 def _looks_like_package(path): |
| 97 return os.path.isfile(os.path.join(path, '__init__.py')) |
| 98 |
| 99 @staticmethod |
| 100 def _build_filter(*patterns): |
| 101 """ |
| 102 Given a list of patterns, return a callable that will be true only if |
| 103 the input matches one of the patterns. |
| 104 """ |
| 105 return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) |
| 106 |
| 107 class PEP420PackageFinder(PackageFinder): |
| 108 @staticmethod |
| 109 def _looks_like_package(path): |
| 110 return True |
| 111 |
| 112 find_packages = PackageFinder.find |
| 113 |
| 114 setup = distutils.core.setup |
| 115 |
| 116 _Command = _get_unpatched(_Command) |
| 117 |
| 118 class Command(_Command): |
| 119 __doc__ = _Command.__doc__ |
| 120 |
| 121 command_consumes_arguments = False |
| 122 |
| 123 def __init__(self, dist, **kw): |
| 124 # Add support for keyword arguments |
| 125 _Command.__init__(self,dist) |
| 126 for k,v in kw.items(): |
| 127 setattr(self,k,v) |
| 128 |
| 129 def reinitialize_command(self, command, reinit_subcommands=0, **kw): |
| 130 cmd = _Command.reinitialize_command(self, command, reinit_subcommands) |
| 131 for k,v in kw.items(): |
| 132 setattr(cmd,k,v) # update command with keywords |
| 133 return cmd |
| 134 |
| 135 distutils.core.Command = Command # we can't patch distutils.cmd, alas |
| 136 |
| 137 def findall(dir = os.curdir): |
| 138 """Find all files under 'dir' and return the list of full filenames |
| 139 (relative to 'dir'). |
| 140 """ |
| 141 all_files = [] |
| 142 for base, dirs, files in os.walk(dir, followlinks=True): |
| 143 if base==os.curdir or base.startswith(os.curdir+os.sep): |
| 144 base = base[2:] |
| 145 if base: |
| 146 files = [os.path.join(base, f) for f in files] |
| 147 all_files.extend(filter(os.path.isfile, files)) |
| 148 return all_files |
| 149 |
| 150 distutils.filelist.findall = findall # fix findall bug in distutils. |
OLD | NEW |