Example #1
0
    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.")
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
 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))
Example #5
0
    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)