| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2020-04-15 13:55:43 -04:00
										 |  |  | import pathlib | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | import tempfile | 
					
						
							|  |  |  | import textwrap | 
					
						
							|  |  |  | import contextlib | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 14:14:21 -07:00
										 |  |  | from test.support.os_helper import FS_NONASCII | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  | from typing import Dict, Union | 
					
						
							| 
									
										
										
										
											2020-10-19 14:14:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | @contextlib.contextmanager | 
					
						
							|  |  |  | def tempdir(): | 
					
						
							|  |  |  |     tmpdir = tempfile.mkdtemp() | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         yield pathlib.Path(tmpdir) | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         shutil.rmtree(tmpdir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @contextlib.contextmanager | 
					
						
							|  |  |  | def save_cwd(): | 
					
						
							|  |  |  |     orig = os.getcwd() | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         os.chdir(orig) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @contextlib.contextmanager | 
					
						
							|  |  |  | def tempdir_as_cwd(): | 
					
						
							|  |  |  |     with tempdir() as tmp: | 
					
						
							|  |  |  |         with save_cwd(): | 
					
						
							|  |  |  |             os.chdir(str(tmp)) | 
					
						
							|  |  |  |             yield tmp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  | @contextlib.contextmanager | 
					
						
							|  |  |  | def install_finder(finder): | 
					
						
							|  |  |  |     sys.meta_path.append(finder) | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         sys.meta_path.remove(finder) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Fixtures: | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |     def setUp(self): | 
					
						
							| 
									
										
										
										
											2020-04-15 13:55:43 -04:00
										 |  |  |         self.fixtures = contextlib.ExitStack() | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         self.addCleanup(self.fixtures.close) | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SiteDir(Fixtures): | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(SiteDir, self).setUp() | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         self.site_dir = self.fixtures.enter_context(tempdir()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  | class OnSysPath(Fixtures): | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     @contextlib.contextmanager | 
					
						
							|  |  |  |     def add_sys_path(dir): | 
					
						
							|  |  |  |         sys.path[:0] = [str(dir)] | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             yield | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             sys.path.remove(str(dir)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(OnSysPath, self).setUp() | 
					
						
							|  |  |  |         self.fixtures.enter_context(self.add_sys_path(self.site_dir)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  | # Except for python/mypy#731, prefer to define | 
					
						
							|  |  |  | # FilesDef = Dict[str, Union['FilesDef', str]] | 
					
						
							|  |  |  | FilesDef = Dict[str, Union[Dict[str, Union[Dict[str, str], str]], str]] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | class DistInfoPkg(OnSysPath, SiteDir): | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     files: FilesDef = { | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         "distinfo_pkg-1.0.0.dist-info": { | 
					
						
							|  |  |  |             "METADATA": """
 | 
					
						
							|  |  |  |                 Name: distinfo-pkg | 
					
						
							|  |  |  |                 Author: Steven Ma | 
					
						
							|  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |                 Requires-Dist: wheel >= 1.0 | 
					
						
							|  |  |  |                 Requires-Dist: pytest; extra == 'test' | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |             "RECORD": "mod.py,sha256=abc,20\n", | 
					
						
							|  |  |  |             "entry_points.txt": """
 | 
					
						
							|  |  |  |                 [entries] | 
					
						
							|  |  |  |                 main = mod:main | 
					
						
							| 
									
										
										
										
											2019-07-28 14:59:24 -04:00
										 |  |  |                 ns:sub = mod:main | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |             """,
 | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         "mod.py": """
 | 
					
						
							|  |  |  |             def main(): | 
					
						
							|  |  |  |                 print("hello world") | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(DistInfoPkg, self).setUp() | 
					
						
							|  |  |  |         build_files(DistInfoPkg.files, self.site_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  | class DistInfoPkgWithDot(OnSysPath, SiteDir): | 
					
						
							|  |  |  |     files: FilesDef = { | 
					
						
							|  |  |  |         "pkg_dot-1.0.0.dist-info": { | 
					
						
							|  |  |  |             "METADATA": """
 | 
					
						
							|  |  |  |                 Name: pkg.dot | 
					
						
							|  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(DistInfoPkgWithDot, self).setUp() | 
					
						
							|  |  |  |         build_files(DistInfoPkgWithDot.files, self.site_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir): | 
					
						
							|  |  |  |     files: FilesDef = { | 
					
						
							|  |  |  |         "pkg.dot-1.0.0.dist-info": { | 
					
						
							|  |  |  |             "METADATA": """
 | 
					
						
							|  |  |  |                 Name: pkg.dot | 
					
						
							|  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "pkg.lot.egg-info": { | 
					
						
							|  |  |  |             "METADATA": """
 | 
					
						
							|  |  |  |                 Name: pkg.lot | 
					
						
							|  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(DistInfoPkgWithDotLegacy, self).setUp() | 
					
						
							|  |  |  |         build_files(DistInfoPkgWithDotLegacy.files, self.site_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | class DistInfoPkgOffPath(SiteDir): | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(DistInfoPkgOffPath, self).setUp() | 
					
						
							|  |  |  |         build_files(DistInfoPkg.files, self.site_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EggInfoPkg(OnSysPath, SiteDir): | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     files: FilesDef = { | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         "egginfo_pkg.egg-info": { | 
					
						
							|  |  |  |             "PKG-INFO": """
 | 
					
						
							|  |  |  |                 Name: egginfo-pkg | 
					
						
							|  |  |  |                 Author: Steven Ma | 
					
						
							|  |  |  |                 License: Unknown | 
					
						
							|  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |                 Classifier: Intended Audience :: Developers | 
					
						
							|  |  |  |                 Classifier: Topic :: Software Development :: Libraries | 
					
						
							|  |  |  |                 """,
 | 
					
						
							|  |  |  |             "SOURCES.txt": """
 | 
					
						
							|  |  |  |                 mod.py | 
					
						
							|  |  |  |                 egginfo_pkg.egg-info/top_level.txt | 
					
						
							|  |  |  |             """,
 | 
					
						
							|  |  |  |             "entry_points.txt": """
 | 
					
						
							|  |  |  |                 [entries] | 
					
						
							|  |  |  |                 main = mod:main | 
					
						
							|  |  |  |             """,
 | 
					
						
							|  |  |  |             "requires.txt": """
 | 
					
						
							|  |  |  |                 wheel >= 1.0; python_version >= "2.7" | 
					
						
							|  |  |  |                 [test] | 
					
						
							|  |  |  |                 pytest | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |             "top_level.txt": "mod\n", | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         "mod.py": """
 | 
					
						
							|  |  |  |             def main(): | 
					
						
							|  |  |  |                 print("hello world") | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(EggInfoPkg, self).setUp() | 
					
						
							|  |  |  |         build_files(EggInfoPkg.files, prefix=self.site_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EggInfoFile(OnSysPath, SiteDir): | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     files: FilesDef = { | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |         "egginfo_file.egg-info": """
 | 
					
						
							|  |  |  |             Metadata-Version: 1.0 | 
					
						
							|  |  |  |             Name: egginfo_file | 
					
						
							|  |  |  |             Version: 0.1 | 
					
						
							|  |  |  |             Summary: An example package | 
					
						
							|  |  |  |             Home-page: www.example.com | 
					
						
							|  |  |  |             Author: Eric Haffa-Vee | 
					
						
							|  |  |  |             Author-email: eric@example.coms | 
					
						
							|  |  |  |             License: UNKNOWN | 
					
						
							|  |  |  |             Description: UNKNOWN | 
					
						
							|  |  |  |             Platform: UNKNOWN | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         super(EggInfoFile, self).setUp() | 
					
						
							|  |  |  |         build_files(EggInfoFile.files, prefix=self.site_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  | class LocalPackage: | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     files: FilesDef = { | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  |         "setup.py": """
 | 
					
						
							|  |  |  |             import setuptools | 
					
						
							|  |  |  |             setuptools.setup(name="local-pkg", version="2.0.1") | 
					
						
							|  |  |  |             """,
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.fixtures = contextlib.ExitStack() | 
					
						
							|  |  |  |         self.addCleanup(self.fixtures.close) | 
					
						
							|  |  |  |         self.fixtures.enter_context(tempdir_as_cwd()) | 
					
						
							|  |  |  |         build_files(self.files) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-21 13:48:18 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | def build_files(file_defs, prefix=pathlib.Path()): | 
					
						
							|  |  |  |     """Build a set of files/directories, as described by the
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     file_defs dictionary.  Each key/value pair in the dictionary is | 
					
						
							|  |  |  |     interpreted as a filename/contents pair.  If the contents value is a | 
					
						
							|  |  |  |     dictionary, a directory is created, and the dictionary interpreted | 
					
						
							|  |  |  |     as the files within it, recursively. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     For example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     {"README.txt": "A README file", | 
					
						
							|  |  |  |      "foo": { | 
					
						
							|  |  |  |         "__init__.py": "", | 
					
						
							|  |  |  |         "bar": { | 
					
						
							|  |  |  |             "__init__.py": "", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "baz.py": "# Some code", | 
					
						
							|  |  |  |      } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     for name, contents in file_defs.items(): | 
					
						
							|  |  |  |         full_name = prefix / name | 
					
						
							|  |  |  |         if isinstance(contents, dict): | 
					
						
							|  |  |  |             full_name.mkdir() | 
					
						
							|  |  |  |             build_files(contents, prefix=full_name) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if isinstance(contents, bytes): | 
					
						
							|  |  |  |                 with full_name.open('wb') as f: | 
					
						
							|  |  |  |                     f.write(contents) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2021-04-05 13:11:23 +09:00
										 |  |  |                 with full_name.open('w', encoding='utf-8') as f: | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |                     f.write(DALS(contents)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-07 10:57:45 -04:00
										 |  |  | class FileBuilder: | 
					
						
							|  |  |  |     def unicode_filename(self): | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |         return FS_NONASCII or self.skip("File system does not support non-ascii.") | 
					
						
							| 
									
										
										
										
											2020-06-07 10:57:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  | def DALS(str): | 
					
						
							|  |  |  |     "Dedent and left-strip" | 
					
						
							|  |  |  |     return textwrap.dedent(str).lstrip() | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class NullFinder: | 
					
						
							|  |  |  |     def find_module(self, name): | 
					
						
							|  |  |  |         pass |