Skip to content

Commit b1df43f

Browse files
committed
Support Python's build-in module venv for virtual environments
1 parent a9cc1d8 commit b1df43f

File tree

10 files changed

+238
-23
lines changed

10 files changed

+238
-23
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Check Python venv virtual environment
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
# Only watch changes related to Python virtual environment venv
8+
paths:
9+
- 'requirements.txt'
10+
- 'scripts/activatePythonEnvironment.sh'
11+
- '.github/workflows/internal-check-python-venv-support.yml' # or when this file changed
12+
13+
jobs:
14+
check-python-venv-environment:
15+
runs-on: ubuntu-latest
16+
strategy:
17+
matrix:
18+
include:
19+
- os: ubuntu-22.04
20+
python: 3.12
21+
22+
steps:
23+
- name: Checkout GIT Repository
24+
uses: actions/checkout@v4
25+
26+
- name: (Python Setup) Use version ${{ matrix.python }} with venv environment management module
27+
uses: actions/setup-python@v5
28+
with:
29+
python-version: ${{ matrix.python }}
30+
cache: 'pip'
31+
32+
- name: Create, activate, install and check virtual environment
33+
env:
34+
USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV: "true"
35+
# 1. Run the script under test to create, activate and install the virtual environment
36+
# 2a. Run pip in dry-run mode without installing or resolving dependencies
37+
# 2b. Suppress all pip output (stderr)
38+
# 2c. Check if pip *would install* anything using grep
39+
# 2d. If there are missing dependencies and the environment is incomplete, return 1 (indicates all requirements already satisfied)
40+
run: |
41+
./scripts/activatePythonEnvironment.sh
42+
pip install --dry-run --no-deps --requirement "./requirements.txt" 2>/dev/null | grep -q "Would install" || return 1

.github/workflows/public-analyze-code-graph.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ on:
6161
required: false
6262
type: string
6363
default: 'true'
64+
use-venv_virtual_python_environment:
65+
description: >
66+
Use venv for virtual Python environments instead of Conda ("true") or not ("false", default).
67+
required: false
68+
type: string
69+
default: 'false'
6470
outputs:
6571
uploaded-analysis-results:
6672
description: >
@@ -103,6 +109,7 @@ jobs:
103109

104110
# "Setup Python" can be skipped if jupyter notebook analysis-results aren't needed
105111
- name: (Python Setup) Use version ${{ matrix.python }} with Conda package manager Miniforge
112+
if: inputs.use-venv_virtual_python_environment == 'false'
106113
id: prepare-conda-environment
107114
uses: conda-incubator/setup-miniconda@505e6394dae86d6a5c7fbb6e3fb8938e3e863830 # v3
108115
with:
@@ -112,7 +119,16 @@ jobs:
112119
environment-file: ./conda-environment.yml
113120
auto-activate-base: false
114121
show-channel-urls: true
122+
123+
- name: (Python Setup) Use version ${{ matrix.python }} with venv environment management module
124+
if: inputs.use-venv_virtual_python_environment == 'true'
125+
uses: actions/setup-python@v5
126+
with:
127+
python-version: ${{ matrix.python }}
128+
cache: 'pip'
129+
115130
- name: (Python Setup) Conda environment info
131+
if: inputs.use-venv_virtual_python_environment == 'false'
116132
shell: bash -el {0}
117133
run: |
118134
conda info
@@ -168,6 +184,7 @@ jobs:
168184
ENABLE_JUPYTER_NOTEBOOK_PDF_GENERATION: ${{ inputs.jupyter-pdf }}
169185
IMPORT_GIT_LOG_DATA_IF_SOURCE_IS_PRESENT: "" # Options: "none", "aggregated", "full". default = "plugin" or ""
170186
PREPARE_CONDA_ENVIRONMENT: "false" # Had already been done in step with id "prepare-conda-environment".
187+
USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV: ${{ inputs.use-venv_virtual_python_environment }}
171188
run: |
172189
TYPESCRIPT_SCAN_HEAP_MEMORY=${{ inputs.typescript-scan-heap-memory }} ./../../scripts/analysis/analyze.sh ${{ inputs.analysis-arguments }}
173190

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ __pycache__/
9898

9999
# Python environments
100100
.conda
101+
.venv/
102+
*.pyc
101103

102104
# Optuna (and other) Database data
103105
*.db

GETTING_STARTED.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ Use these optional command line options as needed:
8484
./../../scripts/analysis/analyze.sh --report Csv
8585
```
8686
87-
- Jupyter notebook reports when Python and Conda are installed (and Chromium Browser for PDF generation):
87+
- Jupyter notebook reports when Python and Conda (or venv) are installed (and Chromium Browser for PDF generation):
8888
8989
```shell
9090
./../../scripts/analysis/analyze.sh --report Jupyter
9191
```
9292
93-
- Python reports when Python and Conda are installed (without Chromium Browser for PDF generation):
93+
- Python reports when Python and Conda (or venv) are installed (without Chromium Browser for PDF generation):
9494
9595
```shell
9696
./../../scripts/analysis/analyze.sh --report Python
@@ -102,7 +102,7 @@ Use these optional command line options as needed:
102102
./../../scripts/analysis/analyze.sh --report Visualization
103103
```
104104
105-
- All reports with Python, Conda, Node.js and npm installed:
105+
- All reports with Python, Conda (or venv), Node.js and npm installed:
106106
107107
```shell
108108
./../../scripts/analysis/analyze.sh

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ Here are some fully automated graph visualizations utilizing [GraphViz](https://
8686

8787
### Additional Prerequisites for Python and Jupyter Notebooks
8888

89-
- Python is required for Jupyter Notebook reports.
90-
- A conda package manager like [Miniconda](https://docs.conda.io/projects/miniconda/en/latest) or [Anaconda](https://www.anaconda.com/download)(Recommended for Windows) is required for Jupyter Notebook reports.
89+
- Python is required for Jupyter Notebook and Python reports.
90+
- Either [Conda](https://docs.conda.io) or Python's build-in module [venv](https://docs.python.org/3/library/venv.html) a required as environment manager.
91+
- For Conda, use for example [Miniconda](https://docs.conda.io/projects/miniconda/en/latest) or [Anaconda](https://www.anaconda.com/download)(Recommended for Windows).
92+
- To use venv, no additional installation is needed. For that the environment variable `USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV` needs to be set to `'true'`.
9193
- Chromium will automatically be downloaded if needed for Jupyter Notebook PDF reports generation.
9294

9395
### Additional Prerequisites for Graph Visualization
@@ -131,6 +133,7 @@ The [Code Structure Analysis Pipeline](./.github/workflows/internal-java-code-an
131133
- [Checkout GIT Repository](https://github.com/actions/checkout)
132134
- [Setup Java](https://github.com/actions/setup-java)
133135
- [Setup Python with Conda](https://github.com/conda-incubator/setup-miniconda) package manager [Mambaforge](https://github.com/conda-forge/miniforge#mambaforge)
136+
- [Setup Python with venv](https://docs.python.org/3/library/venv.html)
134137
- Download artifacts and optionally source code that contain the code to be analyzed [scripts/downloader](./scripts/downloader)
135138
- Setup [Neo4j](https://neo4j.com) Graph Database ([analysis.sh](./scripts/analysis/analyze.sh))
136139
- Setup [jQAssistant](https://jqassistant.github.io/jqassistant/current) for Java and [Typescript](https://github.com/jqassistant-plugin/jqassistant-typescript-plugin) analysis ([analysis.sh](./scripts/analysis/analyze.sh))

requirements.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# --- Core Python version ---
2+
# NOTE: Python version must be >= 3.12 for compatibility
3+
# This should be enforced by the user/environment, not pip
4+
5+
# --- Core tools ---
6+
jupyter==1.1.*
7+
matplotlib==3.10.*
8+
nbconvert[webpdf]==7.16.*
9+
numpy==1.26.*
10+
pandas==2.2.*
11+
pip==25.0.*
12+
setuptools==75.8.* # opentsne uses sklearn.base uses joblib uses distutils missing in Python >= 12 (TODO use native openTSNE?)
13+
typing-extensions==4.12.* # Needed for opentsne and Python >= 3.12
14+
15+
# --- Visualization ---
16+
wordcloud==1.9.*
17+
monotonic==1.*
18+
plotly[kaleido]==6.2.*
19+
seaborn==0.13 # To visualize clustering results
20+
21+
# --- Machine Learning / Optimization ---
22+
scikit-learn==1.6.*
23+
optuna==4.3.*
24+
umap-learn==0.5.* # Dimensionality reduction to visualize node embeddings in 2D
25+
26+
# --- Database connector ---
27+
neo4j==5.23.*
28+
29+
# --- Native/scientific packages (may require compilation) ---
30+
# These are included but may cause install errors in pip/venv
31+
opentsne==1.0.* # Dimensionality reduction to visualize node embeddings in 2D. Might get replaced by umap.
32+
shap==0.48.* # For e.g. explaining anomaly detection results

scripts/activateCondaEnvironment.sh

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
#!/usr/bin/env bash
22

3-
# Activates the Conda (Python package manager) environment "codegraph" with all packages needed to execute the Jupyter Notebooks.
3+
# Activates the Conda (Python package manager) environment "codegraph" with all packages needed to run the included Jupyter Notebooks and Python scripts.
44

55
# Note: This script uses the conda environment defined in CODEGRAPH_CONDA_ENVIRONMENT (defaults to "codegraph").
6-
# If the environment hadn't been created yet, it will use "conda-environment.yml" from the root directory
7-
# in the same directory as the given jupyter notebook ipynb file
8-
# to create the environment.
6+
# If the environment hadn't been created yet, it will use "conda-environment.yml" from the root directory to create the environment.
97

108
# Requires operatingSystemFunctions.sh
119

1210
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
1311
set -o errexit -o pipefail
1412

13+
PREPARE_CONDA_ENVIRONMENT=${PREPARE_CONDA_ENVIRONMENT:-"true"} # Wether to prepare a Python environment with Conda if needed (default, "true") or use an already prepared Conda environment ("false")
14+
15+
if [ "${PREPARE_CONDA_ENVIRONMENT}" = "false" ]; then
16+
echo "activateCondaEnvironment: Skipping activation. An already activated environment and installed dependencies are expected (PREPARE_CONDA_ENVIRONMENT=false)."
17+
# "return" needs to be used here instead of "exit".
18+
# This script is included in another script by using "source".
19+
# "exit" would end the main script, "return" just ends this sub script.
20+
return 0
21+
fi
22+
23+
if [ "${USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV}" = "true" ]; then
24+
echo "activateCondaEnvironment: Skipping activation. venv will be used instead of conda (USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV=true)."
25+
# "return" needs to be used here instead of "exit".
26+
# This script is included in another script by using "source".
27+
# "exit" would end the main script, "return" just ends this sub script.
28+
return 0
29+
fi
30+
1531
## Get this "scripts" directory if not already set
1632
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
1733
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
@@ -37,16 +53,6 @@ echo "activateCondaEnvironment: CONDA_PREFIX=${CONDA_PREFIX}"
3753
echo "activateCondaEnvironment: Current conda environment=${CONDA_DEFAULT_ENV}"
3854
echo "activateCondaEnvironment: Target conda environment=${CODEGRAPH_CONDA_ENVIRONMENT}"
3955

40-
PREPARE_CONDA_ENVIRONMENT=${PREPARE_CONDA_ENVIRONMENT:-"true"} # Wether to prepare a Python environment with Conda if needed (default, "true") or use an already prepared Conda environment ("false")
41-
42-
if [ "${PREPARE_CONDA_ENVIRONMENT}" = "false" ]; then
43-
echo "activateCondaEnvironment: Skipping activation. ${PREPARE_CONDA_ENVIRONMENT} is set to false."
44-
# "return" needs to be used here instead of "exit".
45-
# This script is included in another script by using "source".
46-
# "exit" would end the main script, "return" just ends this sub script.
47-
return 0
48-
fi
49-
5056
# Include operation system function to for example detect Windows.
5157
source "${SCRIPTS_DIR}/operatingSystemFunctions.sh"
5258

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env bash
2+
3+
# Activates the .venv environment (Python build-in virtual environments) with all packages necessary to run the included Jupyter Notebooks and Python scripts.
4+
5+
# Note: If the environment hadn't been created yet, it will use "requirements.txt" from the root directory to create the environment.
6+
7+
# Requires operatingSystemFunctions.sh
8+
9+
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
10+
set -o errexit -o pipefail
11+
12+
USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV=${USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV:-"false"} # Use "venv" for virtual Python environments ("true") or use an already prepared (e.g. conda) environment (default, "false").
13+
14+
if [ "${USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV}" = "false" ]; then
15+
echo "activatePythonEnvironment: Skipping activation. An already activated environment and installed dependencies are expected e.g. by using conda (USE_VIRTUAL_PYTHON_ENVIRONMENT_VENV=false)."
16+
# "return" needs to be used here instead of "exit".
17+
# This script is included in another script by using "source".
18+
# "exit" would end the main script, "return" just ends this sub script.
19+
return 0
20+
fi
21+
22+
## Get this "scripts" directory if not already set
23+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
24+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
25+
# This way non-standard tools like readlink aren't needed.
26+
SCRIPTS_DIR=${SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts
27+
echo "activatePythonEnvironment: SCRIPTS_DIR=${SCRIPTS_DIR}"
28+
29+
# Get the root directory by taking the path of this script and going one directory up.
30+
ROOT_DIRECTORY=${ROOT_DIRECTORY:-"$(dirname "${SCRIPTS_DIR}")"} # Repository directory containing the Jupyter Notebooks
31+
echo "activatePythonEnvironment: ROOT_DIRECTORY=${ROOT_DIRECTORY}"
32+
33+
# Get the file name of the environment description file for the conda package and environment manager
34+
# that contains all dependencies and their versions.
35+
PYTHON_ENVIRONMENT_FILE=${PYTHON_ENVIRONMENT_FILE:-"${ROOT_DIRECTORY}/requirements.txt"} # Pip (package manager for Python) environment file path
36+
if [ ! -f "${PYTHON_ENVIRONMENT_FILE}" ] ; then
37+
echo "activatePythonEnvironment: Couldn't find environment file ${PYTHON_ENVIRONMENT_FILE}."
38+
exit_failed
39+
fi
40+
41+
deactivate_conda_if_necessary() {
42+
# Include operation system function to for example detect Windows.
43+
source "${SCRIPTS_DIR}/operatingSystemFunctions.sh"
44+
45+
# Determine the path to "conda"
46+
if [ -n "${CONDA}" ]; then
47+
if isWindows; then
48+
pathToConda="${CONDA}\\Scripts\\" # the trailing backslash character is required
49+
else
50+
pathToConda="${CONDA}/bin/" # the trailing slash character is required
51+
fi
52+
else
53+
pathToConda=""
54+
fi
55+
echo "activatePythonEnvironment: pathToConda=${pathToConda} (for deactivation)"
56+
57+
scriptExtension=$(ifWindows ".bat" "")
58+
echo "activatePythonEnvironment: scriptExtension=${scriptExtension}"
59+
60+
if "${pathToConda}conda" deactivate >/dev/null 2>&1; then
61+
# Call "deactivate" a second time to also deactivate the "base" environment
62+
"${pathToConda}conda" deactivate >/dev/null 2>&1;
63+
echo "activatePythonEnvironment: Conda deactivated"
64+
else
65+
echo "activatePythonEnvironment: Conda not found. Deactivation skipped"
66+
fi
67+
}
68+
69+
VENV_DIRECTORY=".venv"
70+
71+
# Create the virtual environment if needed
72+
if [ ! -d "${ROOT_DIRECTORY}/${VENV_DIRECTORY}" ]; then
73+
deactivate_conda_if_necessary
74+
echo "activatePythonEnvironment: Creating ${VENV_DIRECTORY} environment..."
75+
python3 -m venv "${ROOT_DIRECTORY}/${VENV_DIRECTORY}"
76+
else
77+
echo "activatePythonEnvironment: Already created ${VENV_DIRECTORY} environment."
78+
fi
79+
80+
# Activate the virtual environment if needed
81+
if [ "${VIRTUAL_ENV}" != "${ROOT_DIRECTORY}/${VENV_DIRECTORY}" ]; then
82+
echo "activatePythonEnvironment: Activate ${VENV_DIRECTORY} environment..."
83+
source "${ROOT_DIRECTORY}/${VENV_DIRECTORY}/bin/activate"
84+
else
85+
echo "activatePythonEnvironment: Already activated ${VENV_DIRECTORY} environment."
86+
fi
87+
88+
# Install the virtual environment if needed
89+
if pip install --dry-run --no-deps --requirement "${PYTHON_ENVIRONMENT_FILE}" 2>/dev/null | grep -q "Would install"; then
90+
echo "activatePythonEnvironment: Install environment dependencies..."
91+
pip install -q --requirement "${PYTHON_ENVIRONMENT_FILE}"
92+
else
93+
echo "activatePythonEnvironment: Already installed environment dependencies."
94+
fi
95+
96+
echo "activatePythonEnvironment: Python installation: $(which python)"

scripts/executeJupyterNotebook.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212

1313
# Note: This script uses the conda environment defined in CODEGRAPH_CONDA_ENVIRONMENT (defaults to "codegraph").
1414
# If the environment hadn't been created yet it will use "conda-environment.yml"
15-
# in the same directory as the given jupyter notebook ipynb file
16-
# to create the environment.
15+
# in the root directory of this repository to create the environment.
16+
17+
# Note: Besides Conda, Python's build-in virtual environment module "venv" is also supported.
18+
# If the environment hadn't been created yet it will use "requirements.txt"
19+
# in the root directory of this repository to create the environment.
1720

1821
# Requires juypter nbconvert,operatingSystemFunctions.sh
1922

@@ -85,8 +88,12 @@ if [ ! -f "${jupyter_notebook_file_path}/.env" ] ; then
8588
echo "NEO4J_INITIAL_PASSWORD=${NEO4J_INITIAL_PASSWORD}" > "${jupyter_notebook_file_path}/.env"
8689
fi
8790

88-
# Create and activate (if necessary) Conda environment as defined in environment variable CODEGRAPH_CONDA_ENVIRONMENT (default "codegraph")
89-
source "${SCRIPTS_DIR}/activateCondaEnvironment.sh"
91+
# Create and activate (if necessary) a virtual environment (Conda or venv).
92+
# For Conda, the environment name is taken from the environment variable CODEGRAPH_CONDA_ENVIRONMENT (default "codegraph")
93+
# and the dependencies are listed in the root directory file "conda-environment.yml".
94+
# For venv, the dependencies are listed in the root directory file "requirements.txt".
95+
time source "${SCRIPTS_DIR}/activateCondaEnvironment.sh"
96+
time source "${SCRIPTS_DIR}/activatePythonEnvironment.sh"
9097

9198
jupyter --version || exit 1
9299

scripts/reports/compilations/PythonReports.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,20 @@ echo "PythonReports: REPORT_COMPILATIONS_SCRIPT_DIR=${REPORT_COMPILATIONS_SCRIPT
2222
REPORTS_SCRIPT_DIR=${REPORTS_SCRIPT_DIR:-$(dirname -- "${REPORT_COMPILATIONS_SCRIPT_DIR}")}
2323
echo "PythonReports: REPORTS_SCRIPT_DIR=${REPORTS_SCRIPT_DIR}"
2424

25+
SCRIPTS_DIR=${SCRIPTS_DIR:-$(dirname -- "${REPORTS_SCRIPT_DIR}")}
26+
echo "PythonReports: SCRIPTS_DIR=${SCRIPTS_DIR}"
27+
2528
# Get the "domains" directory that contains analysis and report scripts by functionality.
2629
DOMAINS_DIRECTORY=${DOMAINS_DIRECTORY:-"${REPORTS_SCRIPT_DIR}/../../domains"}
2730
echo "PythonReports: DOMAINS_DIRECTORY=${DOMAINS_DIRECTORY}"
2831

32+
# Create and activate (if necessary) a virtual environment (Conda or venv).
33+
# For Conda, the environment name is taken from the environment variable CODEGRAPH_CONDA_ENVIRONMENT (default "codegraph")
34+
# and the dependencies are listed in the root directory file "conda-environment.yml".
35+
# For venv, the dependencies are listed in the root directory file "requirements.txt".
36+
time source "${SCRIPTS_DIR}/activateCondaEnvironment.sh"
37+
time source "${SCRIPTS_DIR}/activatePythonEnvironment.sh"
38+
2939
# Run all Python report scripts (filename ending with Csv.sh) in the REPORTS_SCRIPT_DIR and DOMAINS_DIRECTORY directories.
3040
for directory in "${REPORTS_SCRIPT_DIR}" "${DOMAINS_DIRECTORY}"; do
3141
if [ ! -d "${directory}" ]; then

0 commit comments

Comments
 (0)