1from __future__ import unicode_literals 2 3import os 4import sys 5import shutil 6import tempfile 7import textwrap 8import contextlib 9 10try: 11 from contextlib import ExitStack 12except ImportError: 13 from contextlib2 import ExitStack 14 15try: 16 import pathlib 17except ImportError: 18 import pathlib2 as pathlib 19 20 21__metaclass__ = type 22 23 24@contextlib.contextmanager 25def tempdir(): 26 tmpdir = tempfile.mkdtemp() 27 try: 28 yield pathlib.Path(tmpdir) 29 finally: 30 shutil.rmtree(tmpdir) 31 32 33@contextlib.contextmanager 34def save_cwd(): 35 orig = os.getcwd() 36 try: 37 yield 38 finally: 39 os.chdir(orig) 40 41 42@contextlib.contextmanager 43def tempdir_as_cwd(): 44 with tempdir() as tmp: 45 with save_cwd(): 46 os.chdir(str(tmp)) 47 yield tmp 48 49 50class SiteDir: 51 def setUp(self): 52 self.fixtures = ExitStack() 53 self.addCleanup(self.fixtures.close) 54 self.site_dir = self.fixtures.enter_context(tempdir()) 55 56 57class OnSysPath: 58 @staticmethod 59 @contextlib.contextmanager 60 def add_sys_path(dir): 61 sys.path[:0] = [str(dir)] 62 try: 63 yield 64 finally: 65 sys.path.remove(str(dir)) 66 67 def setUp(self): 68 super(OnSysPath, self).setUp() 69 self.fixtures.enter_context(self.add_sys_path(self.site_dir)) 70 71 72class DistInfoPkg(OnSysPath, SiteDir): 73 files = { 74 "distinfo_pkg-1.0.0.dist-info": { 75 "METADATA": """ 76 Name: distinfo-pkg 77 Author: Steven Ma 78 Version: 1.0.0 79 Requires-Dist: wheel >= 1.0 80 Requires-Dist: pytest; extra == 'test' 81 """, 82 "RECORD": "mod.py,sha256=abc,20\n", 83 "entry_points.txt": """ 84 [entries] 85 main = mod:main 86 ns:sub = mod:main 87 """ 88 }, 89 "mod.py": """ 90 def main(): 91 print("hello world") 92 """, 93 } 94 95 def setUp(self): 96 super(DistInfoPkg, self).setUp() 97 build_files(DistInfoPkg.files, self.site_dir) 98 99 100class DistInfoPkgOffPath(SiteDir): 101 def setUp(self): 102 super(DistInfoPkgOffPath, self).setUp() 103 build_files(DistInfoPkg.files, self.site_dir) 104 105 106class EggInfoPkg(OnSysPath, SiteDir): 107 files = { 108 "egginfo_pkg.egg-info": { 109 "PKG-INFO": """ 110 Name: egginfo-pkg 111 Author: Steven Ma 112 License: Unknown 113 Version: 1.0.0 114 Classifier: Intended Audience :: Developers 115 Classifier: Topic :: Software Development :: Libraries 116 """, 117 "SOURCES.txt": """ 118 mod.py 119 egginfo_pkg.egg-info/top_level.txt 120 """, 121 "entry_points.txt": """ 122 [entries] 123 main = mod:main 124 """, 125 "requires.txt": """ 126 wheel >= 1.0; python_version >= "2.7" 127 [test] 128 pytest 129 """, 130 "top_level.txt": "mod\n" 131 }, 132 "mod.py": """ 133 def main(): 134 print("hello world") 135 """, 136 } 137 138 def setUp(self): 139 super(EggInfoPkg, self).setUp() 140 build_files(EggInfoPkg.files, prefix=self.site_dir) 141 142 143class EggInfoFile(OnSysPath, SiteDir): 144 files = { 145 "egginfo_file.egg-info": """ 146 Metadata-Version: 1.0 147 Name: egginfo_file 148 Version: 0.1 149 Summary: An example package 150 Home-page: www.example.com 151 Author: Eric Haffa-Vee 152 Author-email: eric@example.coms 153 License: UNKNOWN 154 Description: UNKNOWN 155 Platform: UNKNOWN 156 """, 157 } 158 159 def setUp(self): 160 super(EggInfoFile, self).setUp() 161 build_files(EggInfoFile.files, prefix=self.site_dir) 162 163 164def build_files(file_defs, prefix=pathlib.Path()): 165 """Build a set of files/directories, as described by the 166 167 file_defs dictionary. Each key/value pair in the dictionary is 168 interpreted as a filename/contents pair. If the contents value is a 169 dictionary, a directory is created, and the dictionary interpreted 170 as the files within it, recursively. 171 172 For example: 173 174 {"README.txt": "A README file", 175 "foo": { 176 "__init__.py": "", 177 "bar": { 178 "__init__.py": "", 179 }, 180 "baz.py": "# Some code", 181 } 182 } 183 """ 184 for name, contents in file_defs.items(): 185 full_name = prefix / name 186 if isinstance(contents, dict): 187 full_name.mkdir() 188 build_files(contents, prefix=full_name) 189 else: 190 if isinstance(contents, bytes): 191 with full_name.open('wb') as f: 192 f.write(contents) 193 else: 194 with full_name.open('w') as f: 195 f.write(DALS(contents)) 196 197 198def DALS(str): 199 "Dedent and left-strip" 200 return textwrap.dedent(str).lstrip() 201