test_find_packages.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. """Tests for setuptools.find_packages()."""
  2. import os
  3. import sys
  4. import shutil
  5. import tempfile
  6. import platform
  7. import pytest
  8. import setuptools
  9. from setuptools import find_packages
  10. find_420_packages = setuptools.PEP420PackageFinder.find
  11. # modeled after CPython's test.support.can_symlink
  12. def can_symlink():
  13. TESTFN = tempfile.mktemp()
  14. symlink_path = TESTFN + "can_symlink"
  15. try:
  16. os.symlink(TESTFN, symlink_path)
  17. can = True
  18. except (OSError, NotImplementedError, AttributeError):
  19. can = False
  20. else:
  21. os.remove(symlink_path)
  22. globals().update(can_symlink=lambda: can)
  23. return can
  24. def has_symlink():
  25. bad_symlink = (
  26. # Windows symlink directory detection is broken on Python 3.2
  27. platform.system() == 'Windows' and sys.version_info[:2] == (3,2)
  28. )
  29. return can_symlink() and not bad_symlink
  30. class TestFindPackages:
  31. def setup_method(self, method):
  32. self.dist_dir = tempfile.mkdtemp()
  33. self._make_pkg_structure()
  34. def teardown_method(self, method):
  35. shutil.rmtree(self.dist_dir)
  36. def _make_pkg_structure(self):
  37. """Make basic package structure.
  38. dist/
  39. docs/
  40. conf.py
  41. pkg/
  42. __pycache__/
  43. nspkg/
  44. mod.py
  45. subpkg/
  46. assets/
  47. asset
  48. __init__.py
  49. setup.py
  50. """
  51. self.docs_dir = self._mkdir('docs', self.dist_dir)
  52. self._touch('conf.py', self.docs_dir)
  53. self.pkg_dir = self._mkdir('pkg', self.dist_dir)
  54. self._mkdir('__pycache__', self.pkg_dir)
  55. self.ns_pkg_dir = self._mkdir('nspkg', self.pkg_dir)
  56. self._touch('mod.py', self.ns_pkg_dir)
  57. self.sub_pkg_dir = self._mkdir('subpkg', self.pkg_dir)
  58. self.asset_dir = self._mkdir('assets', self.sub_pkg_dir)
  59. self._touch('asset', self.asset_dir)
  60. self._touch('__init__.py', self.sub_pkg_dir)
  61. self._touch('setup.py', self.dist_dir)
  62. def _mkdir(self, path, parent_dir=None):
  63. if parent_dir:
  64. path = os.path.join(parent_dir, path)
  65. os.mkdir(path)
  66. return path
  67. def _touch(self, path, dir_=None):
  68. if dir_:
  69. path = os.path.join(dir_, path)
  70. fp = open(path, 'w')
  71. fp.close()
  72. return path
  73. def test_regular_package(self):
  74. self._touch('__init__.py', self.pkg_dir)
  75. packages = find_packages(self.dist_dir)
  76. assert packages == ['pkg', 'pkg.subpkg']
  77. def test_exclude(self):
  78. self._touch('__init__.py', self.pkg_dir)
  79. packages = find_packages(self.dist_dir, exclude=('pkg.*',))
  80. assert packages == ['pkg']
  81. def test_include_excludes_other(self):
  82. """
  83. If include is specified, other packages should be excluded.
  84. """
  85. self._touch('__init__.py', self.pkg_dir)
  86. alt_dir = self._mkdir('other_pkg', self.dist_dir)
  87. self._touch('__init__.py', alt_dir)
  88. packages = find_packages(self.dist_dir, include=['other_pkg'])
  89. assert packages == ['other_pkg']
  90. def test_dir_with_dot_is_skipped(self):
  91. shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets'))
  92. data_dir = self._mkdir('some.data', self.pkg_dir)
  93. self._touch('__init__.py', data_dir)
  94. self._touch('file.dat', data_dir)
  95. packages = find_packages(self.dist_dir)
  96. assert 'pkg.some.data' not in packages
  97. def test_dir_with_packages_in_subdir_is_excluded(self):
  98. """
  99. Ensure that a package in a non-package such as build/pkg/__init__.py
  100. is excluded.
  101. """
  102. build_dir = self._mkdir('build', self.dist_dir)
  103. build_pkg_dir = self._mkdir('pkg', build_dir)
  104. self._touch('__init__.py', build_pkg_dir)
  105. packages = find_packages(self.dist_dir)
  106. assert 'build.pkg' not in packages
  107. @pytest.mark.skipif(not has_symlink(), reason='Symlink support required')
  108. def test_symlinked_packages_are_included(self):
  109. """
  110. A symbolically-linked directory should be treated like any other
  111. directory when matched as a package.
  112. Create a link from lpkg -> pkg.
  113. """
  114. self._touch('__init__.py', self.pkg_dir)
  115. linked_pkg = os.path.join(self.dist_dir, 'lpkg')
  116. os.symlink('pkg', linked_pkg)
  117. assert os.path.isdir(linked_pkg)
  118. packages = find_packages(self.dist_dir)
  119. assert 'lpkg' in packages
  120. def _assert_packages(self, actual, expected):
  121. assert set(actual) == set(expected)
  122. def test_pep420_ns_package(self):
  123. packages = find_420_packages(
  124. self.dist_dir, include=['pkg*'], exclude=['pkg.subpkg.assets'])
  125. self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg'])
  126. def test_pep420_ns_package_no_includes(self):
  127. packages = find_420_packages(
  128. self.dist_dir, exclude=['pkg.subpkg.assets'])
  129. self._assert_packages(packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg'])
  130. def test_pep420_ns_package_no_includes_or_excludes(self):
  131. packages = find_420_packages(self.dist_dir)
  132. expected = [
  133. 'docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets']
  134. self._assert_packages(packages, expected)
  135. def test_regular_package_with_nested_pep420_ns_packages(self):
  136. self._touch('__init__.py', self.pkg_dir)
  137. packages = find_420_packages(
  138. self.dist_dir, exclude=['docs', 'pkg.subpkg.assets'])
  139. self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg'])
  140. def test_pep420_ns_package_no_non_package_dirs(self):
  141. shutil.rmtree(self.docs_dir)
  142. shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets'))
  143. packages = find_420_packages(self.dist_dir)
  144. self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg'])