def test_hash_dir(self): with temporary_dir() as root: root1_path = Path(root) root1_path.joinpath("a").write_text("jake jones") root1_path.joinpath("b").write_text("jane george") hash1 = hash_dir(root1_path) with temporary_dir() as root: root2_path = Path(root) root2_path.joinpath("a").write_text("jake jones") root2_path.joinpath("b").write_text("jane george") hash2 = hash_dir(root2_path) self.assertNotEqual( root1_path, root2_path, "The path of the directory being hashed should not factor into the hash.", ) self.assertEqual(hash1, hash2) with temporary_dir() as root: root_path = Path(root) root_path.joinpath("a1").write_text("jake jones") root_path.joinpath("b").write_text("jane george") hash3 = hash_dir(root_path) self.assertNotEqual(hash1, hash3, "File names should be included in the hash.") with temporary_dir() as root: root_path = Path(root) root_path.joinpath("a").write_text("jake jones") root_path.joinpath("b").write_text("jane george") root_path.joinpath("c").mkdir() hash4 = hash_dir(root_path) self.assertNotEqual(hash1, hash4, "Directory names should be included in the hash.") with temporary_dir() as root: root_path = Path(root) root_path.joinpath("a").write_text("jake jones II") root_path.joinpath("b").write_text("jane george") hash5 = hash_dir(root_path) self.assertNotEqual(hash1, hash5, "File content should be included in the hash.")
def _get_mypy_pex(self, py3_interpreter: PythonInterpreter, *extra_pexes: PEX) -> PEX: mypy_version = self._mypy_subsystem.options.version extras_hash = hash_utils.hash_all( hash_utils.hash_dir(Path(extra_pex.path())) for extra_pex in extra_pexes) path = Path(self.workdir, str(py3_interpreter.identity), f"{mypy_version}-{extras_hash}") pex_dir = str(path) if not path.is_dir(): mypy_requirement_pex = self.resolve_requirement_strings( py3_interpreter, [mypy_version]) pex_info = PexInfo.default() pex_info.entry_point = "pants_mypy_launcher" with self.merged_pex( path=pex_dir, pex_info=pex_info, interpreter=py3_interpreter, pexes=[mypy_requirement_pex, *extra_pexes], ) as builder: with temporary_file(binary_mode=False) as exe_fp: # MyPy searches for types for a package in packages containing a `py.types` marker file # or else in a sibling `<package>-stubs` package as per PEP-0561. Going further than that # PEP, MyPy restricts its search to `site-packages`. Since PEX deliberately isolates # itself from `site-packages` as part of its raison d'etre, we monkey-patch # `site.getsitepackages` to look inside the scrubbed PEX sys.path before handing off to # `mypy`. # # See: # https://mypy.readthedocs.io/en/stable/installed_packages.html#installed-packages # https://www.python.org/dev/peps/pep-0561/#stub-only-packages exe_fp.write( dedent(""" import runpy import site import sys site.getsitepackages = lambda: sys.path[:] runpy.run_module('mypy', run_name='__main__') """)) exe_fp.flush() builder.set_executable( filename=exe_fp.name, env_filename=f"{pex_info.entry_point}.py") builder.freeze(bytecode_compile=False) return PEX(pex_dir, py3_interpreter)
def _get_mypy_pex(self, py3_interpreter: PythonInterpreter, *extra_pexes: PEX) -> PEX: mypy_version = self._mypy_subsystem.options.version extras_hash = hash_utils.hash_all( hash_utils.hash_dir(Path(extra_pex.path())) for extra_pex in extra_pexes ) path = Path(self.workdir, str(py3_interpreter.identity), f"{mypy_version}-{extras_hash}") pex_dir = str(path) if not path.is_dir(): mypy_requirement_pex = self.resolve_requirement_strings(py3_interpreter, [mypy_version]) pex_info = PexInfo.default() pex_info.entry_point = "pants_mypy_launcher" with self.merged_pex( path=pex_dir, pex_info=pex_info, interpreter=py3_interpreter, pexes=[mypy_requirement_pex, *extra_pexes], ) as builder: with temporary_file(binary_mode=False) as exe_fp: # MyPy searches for types for a package in packages containing a `py.types` # marker file or else in a sibling `<package>-stubs` package as per PEP-0561. # Going further than that PEP, MyPy restricts its search to `site-packages`. # Since PEX deliberately isolates itself from `site-packages` as part of its # raison d'etre, we monkey-patch `site.getsitepackages` to look inside the # scrubbed PEX sys.path before handing off to `mypy`. # # As a complication, MyPy does its own validation to ensure packages aren't # both available in site-packages and on the PYTHONPATH. As such, we elide all # PYTHONPATH entries from artificial site-packages we set up since MyPy will # manually scan PYTHONPATH outside this PEX to find packages. # # See: # https://mypy.readthedocs.io/en/stable/installed_packages.html#installed-packages # https://www.python.org/dev/peps/pep-0561/#stub-only-packages exe_fp.write( dedent( """ import os import runpy import site import sys PYTHONPATH = frozenset( os.path.realpath(p) for p in os.environ.get('PYTHONPATH', '').split(os.pathsep) ) site.getsitepackages = lambda: [ p for p in sys.path if os.path.realpath(p) not in PYTHONPATH ] runpy.run_module('mypy', run_name='__main__') """ ) ) exe_fp.flush() builder.set_executable( filename=exe_fp.name, env_filename=f"{pex_info.entry_point}.py" ) builder.freeze(bytecode_compile=False) return PEX(pex_dir, py3_interpreter)
def test_hash_dir_invalid(self): with temporary_file_path() as path: with self.assertRaises(TypeError): hash_dir(path) with self.assertRaises(ValueError): hash_dir(Path(path))
def _get_mypy_pex(self, py3_interpreter: PythonInterpreter, *extra_pexes: PEX) -> PEX: def get_mypy_version() -> str: task_version_configured = not self.get_options().is_default( 'version') subsystem_version_configured = not self._mypy_subsystem.get_options( ).is_default('version') if task_version_configured and subsystem_version_configured: raise ValueError( "Conflicting options for the MyPy version used. You used the new, preferred " "`--mypy-version`, but also used the deprecated `--lint-mypy-version`.\nPlease use " "only one of these (preferably `--mypy-version`).") if task_version_configured: return f"mypy=={self.get_options().version}" return cast(str, self._mypy_subsystem.get_options().version) mypy_version = get_mypy_version() extras_hash = hash_utils.hash_all( hash_utils.hash_dir(Path(extra_pex.path())) for extra_pex in extra_pexes) path = Path(self.workdir, str(py3_interpreter.identity), f'{mypy_version}-{extras_hash}') pex_dir = str(path) if not path.is_dir(): mypy_requirement_pex = self.resolve_requirement_strings( py3_interpreter, [mypy_version]) pex_info = PexInfo.default() pex_info.entry_point = 'pants_mypy_launcher' with self.merged_pex(path=pex_dir, pex_info=pex_info, interpreter=py3_interpreter, pexes=[mypy_requirement_pex, *extra_pexes]) as builder: with temporary_file(binary_mode=False) as exe_fp: # MyPy searches for types for a package in packages containing a `py.types` marker file # or else in a sibling `<package>-stubs` package as per PEP-0561. Going further than that # PEP, MyPy restricts its search to `site-packages`. Since PEX deliberately isolates # itself from `site-packages` as part of its raison d'etre, we monkey-patch # `site.getsitepackages` to look inside the scrubbed PEX sys.path before handing off to # `mypy`. # # See: # https://mypy.readthedocs.io/en/stable/installed_packages.html#installed-packages # https://www.python.org/dev/peps/pep-0561/#stub-only-packages exe_fp.write( dedent(""" import runpy import site import sys site.getsitepackages = lambda: sys.path[:] runpy.run_module('mypy', run_name='__main__') """)) exe_fp.flush() builder.set_executable( filename=exe_fp.name, env_filename=f'{pex_info.entry_point}.py') builder.freeze(bytecode_compile=False) return PEX(pex_dir, py3_interpreter)