From c9d4a095bca58a97bf1dd5bf8f27f9fbf6d88974 Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Tue, 23 Apr 2024 10:47:29 -0700 Subject: [PATCH] pcommand now sets PATH for venv (fixes sphinx docs gen) --- tools/batools/docs.py | 20 ++++++++++++++-- tools/efrotools/pcommand.py | 46 ++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/tools/batools/docs.py b/tools/batools/docs.py index 2a6c2051..8939d4e0 100755 --- a/tools/batools/docs.py +++ b/tools/batools/docs.py @@ -286,6 +286,7 @@ def _run_sphinx( index_rst.write(index_template.render(data=data)) starttime = time.monotonic() + apidoc_cmd = [ 'sphinx-apidoc', # '-f', # Force overwriting of any existing generated files. @@ -302,14 +303,28 @@ def _run_sphinx( paths['sphinx_cache_dir'], ] + # Prevents Python from writing __pycache__ dirs in our source tree + # which leads to slight annoyances. + environ = dict(os.environ, PYTHONDONTWRITEBYTECODE='1') + if generate_dummymodules_doc: subprocess.run( apidoc_cmd + [assets_dirs['dummy_modules']] + ['--private'], check=True, + env=environ, ) + if generate_tools_doc: - subprocess.run(apidoc_cmd + [assets_dirs['efro_tools']], check=True) - subprocess.run(apidoc_cmd + [assets_dirs['ba_data'], '-f'], check=True) + subprocess.run( + apidoc_cmd + [assets_dirs['efro_tools']], + check=True, + env=environ, + ) + subprocess.run( + apidoc_cmd + [assets_dirs['ba_data'], '-f'], + check=True, + env=environ, + ) # -f for regenerating index page so it contains the ba_data modules subprocess.run( @@ -324,6 +339,7 @@ def _run_sphinx( # '-Q', #quiet now ], check=True, + env=environ, ) duration = time.monotonic() - starttime diff --git a/tools/efrotools/pcommand.py b/tools/efrotools/pcommand.py index ace1aab8..1561b412 100644 --- a/tools/efrotools/pcommand.py +++ b/tools/efrotools/pcommand.py @@ -10,6 +10,7 @@ from __future__ import annotations # Note: import as little as possible here at the module level to keep # launch times fast for small snippets. +import os import sys from pathlib import Path from typing import TYPE_CHECKING @@ -39,8 +40,8 @@ _g_batch_server_mode: bool = False def pcommand_main(globs: dict[str, Any]) -> None: """Main entry point to pcommand scripts. - We simply look for all public functions and call - the one corresponding to the first passed arg. + We simply look for all public functions in the provided module globals + and call the one corresponding to the first passed arg. """ import types @@ -50,7 +51,46 @@ def pcommand_main(globs: dict[str, Any]) -> None: global _g_funcs # pylint: disable=global-statement assert _g_funcs is None - # Build our list of available funcs. + # Nowadays generated pcommand scripts run themselves using the + # project virtual environment's Python interpreter + # (.venv/bin/pythonX.Y, etc.). This nicely sets up the Python + # environment but does not touch PATH, meaning the stuff under + # .venv/bin won't get found if we do subprocess.run()/etc. + # + # One way to solve this would be to always do `source + # .venv/bin/activate` before running tools/pcommand. This sets PATH + # but also seems unwieldy and easy to forget. It's nice to be able + # to just run tools/pcommand and assume it'll do the right thing. + # + # So let's go ahead and set up PATH here so tools/pcommand by itself + # *does* do the right thing. + + abs_exe_path = Path(sys.executable).absolute() + pathparts = abs_exe_path.parts + if ( + len(pathparts) < 3 + or pathparts[-3] != '.venv' + or pathparts[-2] != 'bin' + or not pathparts[-1].startswith('python') + ): + raise RuntimeError( + 'Unexpected Python environment;' + ' we expect to be running using .venv/bin/pythonX.Y' + ) + + cur_paths_str = os.environ.get('PATH') + if cur_paths_str is None: + raise RuntimeError("'PATH' is not currently set; unexpected.") + + venv_bin_dir = str(abs_exe_path.parent) + + # Only add our entry if it's not already there; don't want PATH to + # get out of control if we're doing recursive stuff. + cur_paths = cur_paths_str.split(':') + if venv_bin_dir not in cur_paths: + os.environ['PATH'] = ':'.join([venv_bin_dir] + cur_paths) + + # Build our list of available command functions. _g_funcs = dict( ( (name, obj)