pex-tool / pex

A tool for generating .pex (Python EXecutable) files, lock files and venvs.
https://docs.pex-tool.org/
Apache License 2.0
2.52k stars 258 forks source link

Missing `py.typed` #2323

Closed NiklasRosenstein closed 7 months ago

NiklasRosenstein commented 8 months ago

Hi! I'm using the PEX class in my package that is linted with Mypy (for it's PEX.resolve() method). It seems pex is typed, but is missing the py.typed file and Mypy complains about missing type hints for the package.

jsirois commented 8 months ago

Pex is only supported as a binary, not a library. Can you explain your use case?

NiklasRosenstein commented 8 months ago

Ah! I've been using PEX.resolve() to find all the console_scripts entry points and export them as shell wrappers after building the PEX, to allow the PEX to subprocess.run() console_scripts from other requirements.

I'm using the CLI to build the PEX, but have not found a way to enable this use case with the PEX cli.

        console_scripts = _get_console_scripts(PEX(str(self.output_file.get())))
        if console_scripts:
            console_scripts_dir.mkdir(parents=True, exist_ok=True)
            self.logger.info("Exporting %d console_scripts in PEX to %s", len(console_scripts), console_scripts_dir)
            for script in console_scripts:
                file = console_scripts_dir / script
                file.write_text(
                    f'#!/bin/sh\nPEX_SCRIPT={script} PATH="{console_scripts_dir.absolute()}:$PATH" '
                    f'"{self.output_file.get().absolute()}" "$@"\n'
                    "exit $?\n"
                )
                file.chmod(0o777)
        else:
            self.logger.info("note: PEX has no console_scripts")

# ...

def _get_console_scripts(pex: PEX) -> set[str]:
    """Return all entry points registered under `console_script` for this PEX."""

    result = set()
    for dist in pex.resolve():
        result.update(dist.get_entry_map().get("console_scripts", {}).keys())
    return result

There is the --venv option and PEX_VENV=true PEX_VENV_BIN_PATH=prepend variables, but I could not find a way to "embed" those variables in the PEX (--inject-var doesn't seem to work with these PEX_ variables).

jsirois commented 8 months ago

You found the right answer, build the PEX with --venv prepend. Or maybe you missed that --venv optionally takes {prepend,append} args?

NiklasRosenstein commented 8 months ago

Oh, I did miss that! I'll try this right away, thank you. Are there any performance implications for enabling the --venv mode?

NiklasRosenstein commented 8 months ago

It works, unfortunately it has one drawback in my particular case in that the python executable also resolve to that of the environment that the PEX is unpacked into. So when I run Mypy as a PEX, and pass --python-executable python, it doesn't find any type hints for the project I actually want to type-check.

I've tried passing shutil.which("python") to --python-executable to get the full path to the Python interpreter that I actually want it to use. On my system that resolves to a Pyenv shim though, which later on when Mypy accesses it still resolves to the Python of the PEX's unpacked venv. 🤦

jsirois commented 8 months ago

@NiklasRosenstein if you can provide a working example using Pip / venv, I'm sure I can translate that to a working Pex example for you.

jsirois commented 7 months ago

@NiklasRosenstein I'll close this as an answered question unless you can provide an example set up repro steps that lead to a situation the CLI can't handle but your existing API use can.

jsirois commented 7 months ago

Alright @NiklasRosenstein you've been silent; so I'll close this as answered, but please speak up if you do find a situation the CLI can't handle that you expect it could / should / the (unsupported) APIs do.