270 lines
6.6 KiB
Python
270 lines
6.6 KiB
Python
import os
|
|
import sys
|
|
import shutil
|
|
import pathlib
|
|
import tempfile
|
|
import textwrap
|
|
import contextlib
|
|
|
|
from test.support.os_helper import FS_NONASCII
|
|
from typing import Dict, Union
|
|
|
|
|
|
@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
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def install_finder(finder):
|
|
sys.meta_path.append(finder)
|
|
try:
|
|
yield
|
|
finally:
|
|
sys.meta_path.remove(finder)
|
|
|
|
|
|
class Fixtures:
|
|
def setUp(self):
|
|
self.fixtures = contextlib.ExitStack()
|
|
self.addCleanup(self.fixtures.close)
|
|
|
|
|
|
class SiteDir(Fixtures):
|
|
def setUp(self):
|
|
super(SiteDir, self).setUp()
|
|
self.site_dir = self.fixtures.enter_context(tempdir())
|
|
|
|
|
|
class OnSysPath(Fixtures):
|
|
@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))
|
|
|
|
|
|
# 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]]
|
|
|
|
|
|
class DistInfoPkg(OnSysPath, SiteDir):
|
|
files: FilesDef = {
|
|
"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
|
|
ns:sub = mod:main
|
|
""",
|
|
},
|
|
"mod.py": """
|
|
def main():
|
|
print("hello world")
|
|
""",
|
|
}
|
|
|
|
def setUp(self):
|
|
super(DistInfoPkg, self).setUp()
|
|
build_files(DistInfoPkg.files, self.site_dir)
|
|
|
|
|
|
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)
|
|
|
|
|
|
class DistInfoPkgOffPath(SiteDir):
|
|
def setUp(self):
|
|
super(DistInfoPkgOffPath, self).setUp()
|
|
build_files(DistInfoPkg.files, self.site_dir)
|
|
|
|
|
|
class EggInfoPkg(OnSysPath, SiteDir):
|
|
files: FilesDef = {
|
|
"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
|
|
""",
|
|
"top_level.txt": "mod\n",
|
|
},
|
|
"mod.py": """
|
|
def main():
|
|
print("hello world")
|
|
""",
|
|
}
|
|
|
|
def setUp(self):
|
|
super(EggInfoPkg, self).setUp()
|
|
build_files(EggInfoPkg.files, prefix=self.site_dir)
|
|
|
|
|
|
class EggInfoFile(OnSysPath, SiteDir):
|
|
files: FilesDef = {
|
|
"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
|
|
""",
|
|
}
|
|
|
|
def setUp(self):
|
|
super(EggInfoFile, self).setUp()
|
|
build_files(EggInfoFile.files, prefix=self.site_dir)
|
|
|
|
|
|
class LocalPackage:
|
|
files: FilesDef = {
|
|
"setup.py": """
|
|
import setuptools
|
|
setuptools.setup(name="local-pkg", version="2.0.1")
|
|
""",
|
|
}
|
|
|
|
def setUp(self):
|
|
self.fixtures = contextlib.ExitStack()
|
|
self.addCleanup(self.fixtures.close)
|
|
self.fixtures.enter_context(tempdir_as_cwd())
|
|
build_files(self.files)
|
|
|
|
|
|
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:
|
|
with full_name.open('w') as f:
|
|
f.write(DALS(contents))
|
|
|
|
|
|
class FileBuilder:
|
|
def unicode_filename(self):
|
|
return FS_NONASCII or self.skip("File system does not support non-ascii.")
|
|
|
|
|
|
def DALS(str):
|
|
"Dedent and left-strip"
|
|
return textwrap.dedent(str).lstrip()
|
|
|
|
|
|
class NullFinder:
|
|
def find_module(self, name):
|
|
pass
|