OLD | NEW |
(Empty) | |
| 1 """Extensions to the 'distutils' for large or complex distributions""" |
| 2 |
| 3 import os |
| 4 import functools |
| 5 import distutils.core |
| 6 import distutils.filelist |
| 7 from distutils.util import convert_path |
| 8 from fnmatch import fnmatchcase |
| 9 |
| 10 from six.moves import filter, map |
| 11 |
| 12 import setuptools.version |
| 13 from setuptools.extension import Extension |
| 14 from setuptools.dist import Distribution, Feature |
| 15 from setuptools.depends import Require |
| 16 from . import monkey |
| 17 |
| 18 __all__ = [ |
| 19 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', |
| 20 'find_packages', |
| 21 ] |
| 22 |
| 23 __version__ = setuptools.version.__version__ |
| 24 |
| 25 bootstrap_install_from = None |
| 26 |
| 27 # If we run 2to3 on .py files, should we also convert docstrings? |
| 28 # Default: yes; assume that we can detect doctests reliably |
| 29 run_2to3_on_doctests = True |
| 30 # Standard package names for fixer packages |
| 31 lib2to3_fixer_packages = ['lib2to3.fixes'] |
| 32 |
| 33 |
| 34 class PackageFinder(object): |
| 35 """ |
| 36 Generate a list of all Python packages found within a directory |
| 37 """ |
| 38 |
| 39 @classmethod |
| 40 def find(cls, where='.', exclude=(), include=('*',)): |
| 41 """Return a list all Python packages found within directory 'where' |
| 42 |
| 43 'where' is the root directory which will be searched for packages. It |
| 44 should be supplied as a "cross-platform" (i.e. URL-style) path; it will |
| 45 be converted to the appropriate local path syntax. |
| 46 |
| 47 'exclude' is a sequence of package names to exclude; '*' can be used |
| 48 as a wildcard in the names, such that 'foo.*' will exclude all |
| 49 subpackages of 'foo' (but not 'foo' itself). |
| 50 |
| 51 'include' is a sequence of package names to include. If it's |
| 52 specified, only the named packages will be included. If it's not |
| 53 specified, all found packages will be included. 'include' can contain |
| 54 shell style wildcard patterns just like 'exclude'. |
| 55 """ |
| 56 |
| 57 return list(cls._find_packages_iter( |
| 58 convert_path(where), |
| 59 cls._build_filter('ez_setup', '*__pycache__', *exclude), |
| 60 cls._build_filter(*include))) |
| 61 |
| 62 @classmethod |
| 63 def _find_packages_iter(cls, where, exclude, include): |
| 64 """ |
| 65 All the packages found in 'where' that pass the 'include' filter, but |
| 66 not the 'exclude' filter. |
| 67 """ |
| 68 for root, dirs, files in os.walk(where, followlinks=True): |
| 69 # Copy dirs to iterate over it, then empty dirs. |
| 70 all_dirs = dirs[:] |
| 71 dirs[:] = [] |
| 72 |
| 73 for dir in all_dirs: |
| 74 full_path = os.path.join(root, dir) |
| 75 rel_path = os.path.relpath(full_path, where) |
| 76 package = rel_path.replace(os.path.sep, '.') |
| 77 |
| 78 # Skip directory trees that are not valid packages |
| 79 if ('.' in dir or not cls._looks_like_package(full_path)): |
| 80 continue |
| 81 |
| 82 # Should this package be included? |
| 83 if include(package) and not exclude(package): |
| 84 yield package |
| 85 |
| 86 # Keep searching subdirectories, as there may be more packages |
| 87 # down there, even if the parent was excluded. |
| 88 dirs.append(dir) |
| 89 |
| 90 @staticmethod |
| 91 def _looks_like_package(path): |
| 92 """Does a directory look like a package?""" |
| 93 return os.path.isfile(os.path.join(path, '__init__.py')) |
| 94 |
| 95 @staticmethod |
| 96 def _build_filter(*patterns): |
| 97 """ |
| 98 Given a list of patterns, return a callable that will be true only if |
| 99 the input matches at least one of the patterns. |
| 100 """ |
| 101 return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) |
| 102 |
| 103 |
| 104 class PEP420PackageFinder(PackageFinder): |
| 105 @staticmethod |
| 106 def _looks_like_package(path): |
| 107 return True |
| 108 |
| 109 |
| 110 find_packages = PackageFinder.find |
| 111 |
| 112 setup = distutils.core.setup |
| 113 |
| 114 _Command = monkey.get_unpatched(distutils.core.Command) |
| 115 |
| 116 |
| 117 class Command(_Command): |
| 118 __doc__ = _Command.__doc__ |
| 119 |
| 120 command_consumes_arguments = False |
| 121 |
| 122 def __init__(self, dist, **kw): |
| 123 """ |
| 124 Construct the command for dist, updating |
| 125 vars(self) with any keyword parameters. |
| 126 """ |
| 127 _Command.__init__(self, dist) |
| 128 vars(self).update(kw) |
| 129 |
| 130 def reinitialize_command(self, command, reinit_subcommands=0, **kw): |
| 131 cmd = _Command.reinitialize_command(self, command, reinit_subcommands) |
| 132 vars(cmd).update(kw) |
| 133 return cmd |
| 134 |
| 135 |
| 136 def _find_all_simple(path): |
| 137 """ |
| 138 Find all files under 'path' |
| 139 """ |
| 140 results = ( |
| 141 os.path.join(base, file) |
| 142 for base, dirs, files in os.walk(path, followlinks=True) |
| 143 for file in files |
| 144 ) |
| 145 return filter(os.path.isfile, results) |
| 146 |
| 147 |
| 148 def findall(dir=os.curdir): |
| 149 """ |
| 150 Find all files under 'dir' and return the list of full filenames. |
| 151 Unless dir is '.', return full filenames with dir prepended. |
| 152 """ |
| 153 files = _find_all_simple(dir) |
| 154 if dir == os.curdir: |
| 155 make_rel = functools.partial(os.path.relpath, start=dir) |
| 156 files = map(make_rel, files) |
| 157 return list(files) |
| 158 |
| 159 |
| 160 monkey.patch_all() |
OLD | NEW |