From: Thomas Walker Lynch Date: Wed, 14 Oct 2026 12:17:47 +0000 (+0000) Subject: handles broken Python better X-Git-Url: https://git.reasoningtechnology.com/?a=commitdiff_plain;h=refs%2Fheads%2Fcore-developer_branch;p=Harmony handles broken Python better --- diff --git a/setup b/setup index 8ab56c8..1992fea 100755 --- a/setup +++ b/setup @@ -17,8 +17,22 @@ def main(): env = os.environ.copy() + # A role shell started inside another role shell inherits the outer + # project's PATH, REPO_HOME and functions, and a loop through setup would + # start shells without end. RT_SETUP_DEPTH counts the role shells this + # process is inside; any nesting is refused. + depth = env.get("RT_SETUP_DEPTH", "0") + depth = int(depth) if depth.isdigit() else 0 + if depth > 0: + print(f"setup: already inside a role shell ({env.get('PROJECT', '?')} {env.get('ROLE', '?')}).") + print("setup: type 'exit' to leave it, then run setup again.") + sys.exit(1) + env["RT_SETUP_DEPTH"] = str(depth + 1) + # Python-domain logic: Variables and Paths - repo_home = env.get("REPO_HOME", os.getcwd()) + # The project is the directory holding this file, whatever REPO_HOME the + # calling shell carries; an inherited REPO_HOME may name another project. + repo_home = os.path.dirname(os.path.realpath(__file__)) role = args.role role_home = os.path.join(repo_home, role) tool_dir = os.path.join(role_home, "tool") @@ -31,7 +45,7 @@ def main(): print(f"ROLE_HOME {role_home}") # Python-domain logic: Directory validation - if not os.path.isdir(role): + if not os.path.isdir(role_home): print(f"setup: failed to locate directory for role '{role}'") sys.exit(1) @@ -61,6 +75,7 @@ def main(): with os.fdopen(fd, "w") as f: f.write("\n".join(bash_logic) + "\n") + os.chdir(repo_home) os.execvpe("bash", ["bash", "--rcfile", rcfile_path], env) if __name__ == "__main__": diff --git a/shared/project-dependency/Python.init b/shared/project-dependency/Python.init index eddbaad..f0b108d 100644 --- a/shared/project-dependency/Python.init +++ b/shared/project-dependency/Python.init @@ -3,14 +3,23 @@ # Python.init # -------------------------------------------------------------------------------- -# Ensure the local Python build is prioritized in the PATH. -# The user must establish the Python-3.12.3 symlink for this to activate. +# Put the project's Python build at the front of PATH, but only when it runs. +# +# Every '#!/usr/bin/env python3' script the person runs, their own tools +# included, takes the first python3 on PATH. A build that cannot start, for +# example one that lost its libpython after its directory moved, must not be +# put there, or all of those scripts fail with it. The test runs the +# interpreter once with an empty program. -if [[ -d "$REPO_HOME/shared/project-dependency/Python-3.12.3/bin" ]]; then - PATH="$REPO_HOME/shared/project-dependency/Python-3.12.3/bin:$PATH" -else - # Optional: Issue a quiet warning for the administrator role +python_dp="$REPO_HOME/shared/project-dependency/Python-3.12.3" +if [[ ! -e "$python_dp" ]]; then if [[ "$ROLE" == "administrator" ]]; then - echo "Notice: Local Python-3.12.3 bin not found. Using system Python." + echo "Python.init: no $python_dp; using the system python3" fi +elif "$python_dp/bin/python3" -c '' 2>/dev/null; then + PATH="$python_dp/bin:$PATH" +else + echo "Python.init: $python_dp/bin/python3 does not run; using the system python3" >&2 + echo "Python.init: run it directly to see why, and see Python.org beside this file" >&2 fi +unset python_dp diff --git a/shared/project-dependency/Python.org b/shared/project-dependency/Python.org index a5ba50c..7d2b4e7 100644 --- a/shared/project-dependency/Python.org +++ b/shared/project-dependency/Python.org @@ -1,9 +1,22 @@ #+TITLE: Python Installation Guide #+AUTHOR: Project Administrator -This project requires a dedicated Python environment. The configuration plugin (`shared/project-dependency/Python.init`) was written targeting Python 3.12.3, though the architecture does not rely on any known version-dependent features. +This project requires a dedicated Python environment. The configuration plugin (`shared/project-dependency/Python.init`) was written targeting Python 3.12.3, though the architecture does not rely on any known version-dependent features. -* 1. Download and Extract +The build below can be moved. Nothing in it records the absolute directory it was built in where the interpreter depends on it, so the directory can move with a home directory, or to another machine, and keep working. + +* 1. Install the development libraries + +Python's configure step looks for the development files of each optional library, and silently leaves out any module whose library it does not find. A build without sqlite3-devel, for example, has no sqlite3 module, and the gap shows only when a program imports it. Install these first. The names are for openSUSE; zypper search finds the name of one that differs. + +#+begin_src bash +sudo zypper install gcc make sqlite3-devel libopenssl-devel libffi-devel zlib-devel \ + readline-devel ncurses-devel xz-devel libbz2-devel gdbm-devel tk-devel libuuid-devel +#+end_src + +tk-devel gives the tkinter module, which the window programs use. + +* 2. Download and Extract Download the Python source code and extract it as a sibling to the main project directory. Assuming you are at the top level of your project workspace: #+begin_src bash @@ -13,26 +26,40 @@ tar -xzf Python-3.12.3.tgz cd Python-3.12.3 #+end_src -* 2. Build the Environment -Compile the Python binaries in place. Setting the installation prefix to the current directory isolates the build, and enabling shared libraries ensures compatibility with C extensions. +* 3. Build the Environment +Compile the Python binaries in place. Setting the installation prefix to the current directory isolates the build. #+begin_src bash -./configure --prefix="$(pwd)" --enable-optimizations --enable-shared -make -j8 +./configure --prefix="$(pwd)" --enable-optimizations +make -j8 2>&1 | tee build.log make install #+end_src -* 3. Link the Project +The build is static: the interpreter is one program, with no separate libpython3.12.so to find when it starts. A build with --enable-shared records the absolute path of its library directory in the program, and stops working when the directory moves. C extension modules, such as those pip installs, work with the static build. -Python-3.12.3 is linked to a Harmony derived project by default. +Near the end of build.log, make lists any optional modules it could not build, under "The necessary bits to build these optional modules were not found". The list should be empty; if it is not, install the missing development library and build again. -See administrator/document/how-to_add-project-dependency.html for details about linking a project, if you want to check the setup. +* 4. Check the build -If you are installing a different version, then at least the Python-3.12.3 version project linking is there as a good example. +#+begin_src bash +bin/python3 -c 'import sys ,ssl ,sqlite3 ,ctypes ,lzma ,bz2 ,readline ,tkinter; print(sys.prefix)' +ldd bin/python3 | grep libpython # prints nothing: no shared libpython +#+end_src -Project linking is not difficult, make a sym link to `project/`, and then add an init file, as described in administrator/document/how-to_add-project-dependency.html. The 'project' in the link is literal. +* 5. Use pip through the interpreter +The scripts make install writes into bin/, such as pip3, start with a #! line naming the absolute path of the interpreter, so they break when the directory moves. Run pip as a module instead, which uses whichever interpreter runs it: +#+begin_src bash +python3 -m pip install +#+end_src +* 6. Link the Project +Python-3.12.3 is linked to a Harmony derived project by default. +See administrator/document/how-to_add-project-dependency.html for details about linking a project, if you want to check the setup. + +If you are installing a different version, then at least the Python-3.12.3 version project linking is there as a good example. + +Project linking is not difficult, make a sym link to `project/`, and then add an init file, as described in administrator/document/how-to_add-project-dependency.html. The 'project' in the link is literal. diff --git a/shared/tool/setup b/shared/tool/setup index a7a66aa..7dc24d3 100644 --- a/shared/tool/setup +++ b/shared/tool/setup @@ -74,8 +74,19 @@ umask 0077 PATH="$REPO_HOME/shared/authored:$PATH" # Remove duplicates, keeping the first occurrence - clean_path() { - PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//') + # Done with bash built-ins only. A pipeline through awk and sed left PATH + # empty whenever either program failed to run. + clean_path(){ + local entry result="" + local -A seen=() + local -a part + IFS=: read -r -a part <<< "$PATH" + for entry in "${part[@]}"; do + [[ -z "$entry" || -n "${seen[$entry]:-}" ]] && continue + seen[$entry]=1 + result="${result:+$result:}$entry" + done + PATH="$result" } clean_path