You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 4.9 KiB

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