__init__.py 5.3 KB

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