setup3lib.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import sys
  2. from setuptools import setup as _setup
  3. py3_args = ['use_2to3', 'convert_2to3_doctests', 'use_2to3_fixers', 'test_dirs', 'test_build_dir', 'doctest_exts', 'pyversion_patching']
  4. if sys.version_info < (3,):
  5. # Remove any Python-3.x-only arguments (so they don't generate complaints
  6. # from 2.x setuptools) and then just pass through to the regular setup
  7. # routine.
  8. def setup(*args, **kwargs):
  9. for a in py3_args:
  10. if a in kwargs:
  11. del kwargs[a]
  12. return _setup(*args, **kwargs)
  13. else:
  14. import os
  15. import re
  16. import logging
  17. from setuptools import Distribution as _Distribution
  18. from distutils.core import Command
  19. from setuptools.command.build_py import Mixin2to3
  20. from distutils import dir_util, file_util, log
  21. import setuptools.command.test
  22. from pkg_resources import normalize_path
  23. try:
  24. import patch
  25. patch.logger.setLevel(logging.WARN)
  26. except ImportError:
  27. patch = None
  28. patchfile_re = re.compile(r'(.*)\.py([0-9.]+)\.patch$')
  29. def pyversion_patch(filename):
  30. '''Find the best pyversion-fixup patch for a given filename and apply
  31. it.
  32. '''
  33. dir, file = os.path.split(filename)
  34. best_ver = (0,)
  35. patchfile = None
  36. for dirfile in os.listdir(dir):
  37. m = patchfile_re.match(dirfile)
  38. if not m:
  39. continue
  40. base, ver = m.groups()
  41. if base != file:
  42. continue
  43. ver = tuple([int(v) for v in ver.split('.')])
  44. if sys.version_info >= ver and ver > best_ver:
  45. best_ver = ver
  46. patchfile = dirfile
  47. if not patchfile:
  48. return False
  49. log.info("Applying %s to %s..." % (patchfile, filename))
  50. cwd = os.getcwd()
  51. os.chdir(dir)
  52. try:
  53. p = patch.fromfile(patchfile)
  54. p.apply()
  55. finally:
  56. os.chdir(cwd)
  57. return True
  58. class Distribution (_Distribution):
  59. def __init__(self, attrs=None):
  60. self.test_dirs = []
  61. self.test_build_dir = None
  62. self.doctest_exts = ['.py', '.rst']
  63. self.pyversion_patching = False
  64. _Distribution.__init__(self, attrs)
  65. class BuildTestsCommand (Command, Mixin2to3):
  66. # Create mirror copy of tests, convert all .py files using 2to3
  67. user_options = []
  68. def initialize_options(self):
  69. self.test_base = None
  70. def finalize_options(self):
  71. test_base = self.distribution.test_build_dir
  72. if not test_base:
  73. bcmd = self.get_finalized_command('build')
  74. test_base = bcmd.build_base
  75. self.test_base = test_base
  76. def run(self):
  77. use_2to3 = getattr(self.distribution, 'use_2to3', False)
  78. test_dirs = getattr(self.distribution, 'test_dirs', [])
  79. test_base = self.test_base
  80. bpy_cmd = self.get_finalized_command("build_py")
  81. lib_base = normalize_path(bpy_cmd.build_lib)
  82. modified = []
  83. py_modified = []
  84. doc_modified = []
  85. dir_util.mkpath(test_base)
  86. for testdir in test_dirs:
  87. for srcdir, dirnames, filenames in os.walk(testdir):
  88. destdir = os.path.join(test_base, srcdir)
  89. dir_util.mkpath(destdir)
  90. for fn in filenames:
  91. if fn.startswith("."):
  92. # Skip .svn folders and such
  93. continue
  94. dstfile, copied = file_util.copy_file(
  95. os.path.join(srcdir, fn),
  96. os.path.join(destdir, fn),
  97. update=True)
  98. if copied:
  99. modified.append(dstfile)
  100. if fn.endswith('.py'):
  101. py_modified.append(dstfile)
  102. for ext in self.distribution.doctest_exts:
  103. if fn.endswith(ext):
  104. doc_modified.append(dstfile)
  105. break
  106. if use_2to3:
  107. self.run_2to3(py_modified)
  108. self.run_2to3(doc_modified, True)
  109. if self.distribution.pyversion_patching:
  110. if patch is not None:
  111. for file in modified:
  112. pyversion_patch(file)
  113. else:
  114. log.warn("Warning: pyversion_patching specified in setup config but patch module not found. Patching will not be performed.")
  115. dir_util.mkpath(lib_base)
  116. self.reinitialize_command('egg_info', egg_base=lib_base)
  117. self.run_command('egg_info')
  118. class TestCommand (setuptools.command.test.test):
  119. # Override 'test' command to make sure 'build_tests' gets run first.
  120. def run(self):
  121. self.run_command('build_tests')
  122. setuptools.command.test.test.run(self)
  123. def setup(*args, **kwargs):
  124. kwargs.setdefault('distclass', Distribution)
  125. cmdclass = kwargs.setdefault('cmdclass', {})
  126. cmdclass.setdefault('build_tests', BuildTestsCommand)
  127. cmdclass.setdefault('test', TestCommand)
  128. return _setup(*args, **kwargs)