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