Closed spham-amzn closed 6 months ago
Currently the Python 3rd Party package is downloaded and extracted into $O3DE_HOME/Python/runtime/$PYTHON_PACKAGE_NAME. The location will change to $LY_3RDPARTY_PATH (inside of the $HOME/.o3de/%USERPROFILE%.o3de folder)
The variable placeholder of $O3DE_HOME
is ambiguous in meaning to a reader. It could mean the the root of the O3DE user directory inside the user home directory (i.e ~/.o3de
).
However in the case that it is being used in, the meaning is the O3DE engine root.
Check if the $PYTHON_VENV_PATH path exists. If it exists, check the following scenarios:
Check if the expected Python binaries (by platform) exists. Check if the expected pyvenv.cfg exists. Check if a 3rd Party hash marker file .hash exists. If the .hash exists, check if it matches the package hash of the 3rd Party Python package the virtual environment is meant for. If one or more of the above check fails, then clear out the folder.
I think there should be a CMake option to choose to not clear out the 3rd Party folder if the hash file doesn't match the 3rd Party Package hash file.
Instead a FATAL_ERROR could occur in that case.
Perhaps an option such as, O3DE_ERROR_ON_PYTHON_HASH_MISMATCH
This RFC should mention that the o3de CLI package is also pip installed locally and that would be needed to allow methods such as import o3de
to still function: https://github.com/o3de/o3de/blob/development/cmake/LYPython.cmake#L304
Check if the $PYTHON_VENV_PATH path exists. If it exists, check the following scenarios:
Check if the expected Python binaries (by platform) exists. Check if the expected pyvenv.cfg exists. Check if a 3rd Party hash marker file .hash exists. If the .hash exists, check if it matches the package hash of the 3rd Party Python package the virtual environment is meant for. If one or more of the above check fails, then clear out the folder.
I think there should be a CMake option to choose to not clear out the 3rd Party folder if the hash file doesn't match the 3rd Party Package hash file. Instead a FATAL_ERROR could occur in that case. Perhaps an option such as,
O3DE_ERROR_ON_PYTHON_HASH_MISMATCH
Pip Install of the o3de CLI python package
This RFC should mention that the o3de CLI package is also pip installed locally and that would be needed to allow methods such as
import o3de
to still function: https://github.com/o3de/o3de/blob/development/cmake/LYPython.cmake#L304
Actually, I think by default it should report the detailed error, which will also suggest to re-run the a 'Force Update' type of argument if ran through get_python
, or set a O3DE_RESET_ENV_ON_PYTHON_UPDATES
(which is a variable that is set from the get_python flow)
We should keep this open for comment for the next two weeks until December 20th
Do you think we could/should move the $O3DE/python
folder? Could we move what remains into $O3DE/scripts
or $O3DE/Tools/python
? Any idea why it was a root level folder initially?
There would be a concern of breaking backwards compatibility for anyone who had scripts that referenced $O3DE/Python/Python.cmd or Python.sh
but I like the idea of making the O3DE root as simple/efficient as possible.
Right now, the venv
folder that is created is meant for the engine. New projects will share the same virtual environment as the engine. How important is it to have a venv
at the project level as well? That will at least protect individual projects from engine-wide python environment or librarie updates? At it fits the pattern for virtual environments better.
Feature has been implemented and merged in https://github.com/o3de/o3de/commit/cd07167884f06e7813a2a5f7175c45360a56cef8
Summary:
The download and installation of Python should be moved out of the engine and into a package outside of the engine root, and subsequent uses of Python should be done from a virtual environments instead of directly in the downloaded package.
What is the relevance of this feature?
Moving Python out of the engine path and into the
LY_3RDPARTY_PATH
with the other 3rd Party packages will provide the following benefits:Python virtual environments provides a mechanism to isolate different packages and libraries for a specific application from other applications. Since virtual environments creates a small layer on top of the main Python package, the overhead is minimal. Switching O3DE to use Python virtual environments will add the following benefits:
Feature design description:
This feature is a change to how O3DE uses Python, and the updates will have minimal impacts on development workflows that do not involve Python directly.
Technical design description:
Python 3rd Party Package A new revision of the Python 3rd Party package is not necessary. The Python virtual environment module is part of the standard Python library. The pip modules are already part of the package as well. The handling of the 3rd Party Package will change in the following ways:
<Engine Root>/Python/runtime/$PYTHON_PACKAGE_NAME
where<Engine Root>
is the location of the engine (either from github or an installer package). The location will change to$LY_3RDPARTY_PATH
(inside of the$HOME/.o3de
/%USERPROFILE%\.o3de
folder)downloaded_packages
folder, and the package will be validated against its hash during every cmake generation run.Bootstrap Process
The initial bootstrap process for Python will be updated to include the creation of the Python virtual environment, and the subsequent O3DE specific installation of modules will use the
venv
instead of direct Python calls in the 3rd Party Package. The target location of the virtual environment will be unique to the engine path from where the bootstrap process is occuring. O3DE will use the full absolute path of the current engine to generate a reasonable unique identifier. This path will be deterministic based on the engine path.Below is the current bootstrap flow for O3DE:
$O3DE/Python/runtime
if needed.cmake
project generation calls. Prevent normal 3rd party package hash validation. This is due to the fact that the subsequent calls topip
into the 3rd Party package will alter the package contents, and thus the package hash will be different.pip install
using the O3DE Python script under$O3DE/Python
to perform the installation of the following:$O3DE/Tools/LyTestTools
The updated bootstrap flow for O3DE will be:
Download and unpack the Python 3rd Party Package into
$LY_3RDPARTY_PATH
if needed.Perform the standard package validation against the package hash.
Calculate the full path (
$PYTHON_VENV_PATH
) to the Python virtual environment based on the absolute path of the engine. The full path will be:$HOME/.o3de/Python/$ENGINE_ID/
where$ENGINE_ID
will be the first 8 hexadecimal digits of the SHA-1 hash of the absolute path of the current engine.Check if the
$PYTHON_VENV_PATH
path exists. If it exists, check the following scenarios:pyvenv.cfg
exists..hash
exists..hash
exists, check if it matches the package hash of the 3rd Party Python package the virtual environment is meant for. If one or more of the above check fails, then check against a cmake variableO3DE_ERROR_ON_PYTHON_HASH_MISMATCH
to determine if we want to clear out the venv and regenerate it, or just report a fatal error with instructions on how to re-generate the venv.If
$PYTHON_VENV_PATH
does not exist, or it was cleared out by the above validation checks, then perform the creation of the virtual environment based on the following command ($PYTHON_EXECUTABLE
will be the Python executable inside the Python 3rd Party package, and its sub folder is platform specific. )$PYTHON_EXECUTABLE -m venv $PYTHON_VENV_PATH
Once the environment is initialized to$PYTHON_VENV_PATH
, write the package hash for the Python 3rd Party package to the folder to.hash
.Perform
pip install
using the O3DE Python script under$O3DE/Python
to perform the installation of the following:$O3DE/Tools/LyTestTools
Python PAL-ification
The current Python bootstrap script does not employ the Platform Abstraction Layer (PAL) pattern for the Python packages since it is used in both generation and script modes. In script mode, the main LYPython.cmake does not have access to many of the PAL variables that is available during the generation mode, so it currently does a manual check against the current platform (and architecture) to determine the package name, hash, etc.
In order to refactor the script to follow the PAL pattern, the PAL variables that are needed will instead be initialized by the current
get_python.*
scripts instead. get_Python.bat is guaranteed to run on Windows only, so the windows specific PAL variables can be hardcoded in that script. get_Python.sh is valid for both Linux and Mac, so platform detection (as well as architecture detection for Linux) will be handled there. Since this is a BASH script, it can detect the platform by using the$OSTYPE
environment variable.These
get_Python.*
scripts subsequently calls the get_python.cmake, which provides the necessaryPAL*
related variables needed and passes it to LYPython.cmake to run through the same bootstrap process as a cmake project generation workflow.The platform specific information for the Python package will be moved to PAL'ified files
cmake/3rdParty/Platform/{$PLATFORM_NAME}/Python_{$PLATFORM_LOWER}.cmake
.Python Script Updates
Since PAL-ification is handled in the either the
get_Python.*
scripts or part of the cmake generation workflow, the location of the Python virtual environment for the engine will be set to a known location based on the current engine rather than trying to detect the platform and the location of the actual Python 3rd Party package. The current Python.cmd/Python.sh will updated to generate the deterministic path to the Python virtual environment$PYTHON_VENV_PATH
by performing the same logic employed by the bootstrap workflow.Instead of running
Python
from the 3rd Party package, it will instead run through the execution flow for virtual environments:activate
script within thevenv
to setup the proper environmentPython
executable within thevenv
deactivate
script (Windows) to restore the environmentThe
activate
/deactivate
is necessary to set up and tear down the virtual environment. (Only activate is needed on BASH since it is running in its own shell)Embedded Python Updates
The targets that depend on the Pybind 3rd Party Package (Project Manager and the Editor Python Bindings Gem) will also need to update its environment to use the virtual environment. The
pyvenv.cfg
file is only used when running the Python interpreter that is inside the virtual environment. With embedded Python, however, we will need to read in this file and set thePYTHON_HOME
to the 3rd Party Python library. In addition to initializing the Python interpreter from pybind, we will need to scan the virtual environment's site-packages to look for*.egg-link
files. These files tell the Python interpreter where to look for additional modules in other folders. Pybind11 has trouble interpreting these files, so we will need to work around this issue by scanning and manually adding the paths that are contained in theseegg-link
files into the `$PYTHON_PATH`` environment.What are the advantages of the feature?
.gitignore
file.Python/runtime
folder, and then packages it in the SNAP container. The disadvantages of this are:PYTHONPATH
injection when running Python. For instance, the ROS ecosystem installs its own Python and injects ROS-specific Python packages intoPYTHONPATH
.What are the disadvantages of the feature?
How will this be implemented or integrated into the O3DE environment?
The implementation is described in the technical design description above. For source versions of the engine, the updated scripts will perform the bootstrap process as needed to setup the Python environment properly. The legacy Python runtime will still exist in the engine path and may be removed manually.
Are there any alternatives to this feature?
Use alternatives to Python Virtual Environments:
Python Environments was chosen because it is provided by default by Python is does not require additional packages/dependencies.
How will users learn this feature?
The bootstrap process will occur automatically, and all Python calls in O3DE currently are wrapped with O3DE python script files already. Users will learn of this update through the release notes and impactful change messaging from O3DE.
Are there any open questions?
What happens to the virtual environment when there is an update to the 3rd Party Python package? (ie security update) The workflow attempts to remedy this scenario by keeping the package hash within the generated virtual environment folder. If there a detected change in the hash, the bootstrap process will wipe out the virtual environment contents and force a generation of a new virtual environment based on the updated 3rd Party Python.
Why is the virtual environment stored in the
.o3de
folder? We need a location that is outside of the engine path, and the.o3de
folder already manages all the 3rd Party packages. Alternatively the location could be stored in the$HOME/O3DE
folder instead, but since it has a dependency on a specific 3rd Party Python package, it made more sense to place it there.Will this solve individual projects from injecting python libraries globally through pip-install? The virtual environment scope is still at the engine level, not the project level, so this will not solve that issue. It does protect the 3rd Party Python site packages however since the project-injected python libraries are installed into the virtual environment, and not the 3rd Party Python packages.