diff --git a/.env b/.env deleted file mode 100644 index 580b14f..0000000 --- a/.env +++ /dev/null @@ -1,2 +0,0 @@ -# Adding multiple relative paths for shared modules. -PYTHONPATH=../shared/python;../../shared/python; diff --git a/.gitignore b/.gitignore index 946b612..44dfdf0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,9 +20,8 @@ labs-in-progress/ # Misc *.log -# Exclude sensitive or generated files (except for root .env for Python) +# Exclude sensitive or generated files .env -!/.env # Coverage data and reports .coverage diff --git a/.vscode/settings.json b/.vscode/settings.json index 3779d1e..87f626a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,21 +1,23 @@ { - "python.analysis.extraPaths": [ - "${workspaceFolder}/shared/python" - ], - "python.analysis.completeFunctionParens": true, + "plantuml.diagramsRoot": "diagrams/src", + "plantuml.exportFormat": "svg", + "plantuml.exportOutDir": "diagrams/out", + "plantuml.java": "C:\\Program Files\\OpenJDK\\jdk-22.0.2\\bin\\java.exe", + "plantuml.render": "Local", "python.analysis.autoIndent": true, + "python.analysis.completeFunctionParens": true, "python.analysis.diagnosticSeverityOverrides": { "reportDuplicateImport": "warning", "reportUndefinedVariable": "information", "reportUnusedVariable": "information" }, + "python.analysis.extraPaths": [ + "${workspaceFolder}/shared/python" + ], + "python.defaultInterpreterPath": "./venv/bin/python", "python.envFile": "${workspaceFolder}/.env", - "plantuml.render": "Local", - "plantuml.exportFormat": "svg", + "python.terminal.activateEnvironment": true, "terminal.integrated.env.windows": { "PATH": "${env:PATH}" - }, - "plantuml.java": "C:\\Program Files\\OpenJDK\\jdk-22.0.2\\bin\\java.exe", - "plantuml.diagramsRoot": "diagrams/src", - "plantuml.exportOutDir": "diagrams/out" + } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..8767d1c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Setup Python Environment", + "type": "shell", + "command": "${config:python.pythonPath}", + "args": [ + "setup/setup_python_path.py", + "--generate-env" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": [], + "detail": "Configure PYTHONPATH for cross-platform compatibility" + } + ] +} diff --git a/README.md b/README.md index 4bd66c6..abc8384 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,14 @@ These prerequisites apply broadly across all infrastructure and samples. If ther Run through the following steps to create a Python virtual environment before doing anything else: 1. Open VS Code. -1. Invoke the _Command Palette_ via the _View_ menu or a shortcut (on Windows: Ctrl + Shift + P). +1. Invoke the _Command Palette_ via the _View_ menu or a shortcut (on Windows: Ctrl + Shift + P, on Mac: CMD + Shift + P). 1. Select _Python: Create Environment_. 1. Select _Venv_ as we want a local virtual environment. 1. Select the desired, installed Python version. -1. Check _requirements.txt_ to install the Python dependencies we need for this repo, then press _OK_. The install may take a few minutes. You can check on progress in the _OUTPUT_ window. +1. Check _requirements.txt_ to install the Python dependencies we need for this repo, then press _OK_. The install may take a few minutes. You can check on progress in the _OUTPUT_ window (select `Python`). 1. Verify the virtual environment is set up. You should see a new _.venv_ directory with a _pyveng.cfg_ file and the Python version you selected earlier. +1. Set up the project environment by running `python setup/setup_python_path.py --generate-env` to configure the Python path. + a. If for some reason the `python` command is not found, please try adding your virtual environment's `bin` or `Scripts` directory to your system's PATH variable. An example command to do this for a virtual environment named `venv` would be to run `source .venv/bin/activate` The first time you run a Jupyter notebook, you'll be asked to install the Jupyter kernel package (ipykernel). @@ -174,4 +176,4 @@ The APIM team maintains an [APIM policy snippets repo](https://github.com/Azure/ This project has its roots in work done by [Alex Vieira](https://github.com/vieiraae) on the excellent Azure API Management [AI Gateway](https://github.com/Azure-Samples/AI-Gateway) GitHub repository. Much of the structure is similar and its reuse resulted in significant time savings. Thank you, Alex! -Furthermore, [Houssem Dellai](https://github.com/HoussemDellai) was instrumental in setting up a working Front Door to API Management [private connectivity lab](https://github.com/Azure-Samples/AI-Gateway/tree/main/labs/private-connectivity). This created a working baseline for one of this repository's infrastructures. Thank you, Houssem! +Furthermore, [Houssem Dellai](https://github.com/HoussemDellai) was instrumental in setting up a working Front Door to API Management [private connectivity lab](https://github.com/Azure-Samples/AI-Gateway/tree/main/labs/private-connectivity). This created a working baseline for one of this repository's infrastructures. Thank you, Houssem! diff --git a/setup/README.md b/setup/README.md new file mode 100644 index 0000000..0afcb07 --- /dev/null +++ b/setup/README.md @@ -0,0 +1,15 @@ +# Python Environment Setup + +Configures cross-platform PYTHONPATH for APIM Samples. + +## Usage + +```shell +python setup/setup_python_path.py --generate-env +``` + +This script auto-detects the project root and generates a `.env` file that VS Code uses for Python path configuration. If for some reason the `python` command is not found, please try adding your virtual environment's `bin` or `Scripts` directory to your system's PATH variable. An example command to do this for a virtual environment named `venv` is: + +```shell +source .venv/bin/activate +``` diff --git a/setup/setup_python_path.py b/setup/setup_python_path.py new file mode 100644 index 0000000..7b4f5ce --- /dev/null +++ b/setup/setup_python_path.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +""" +Cross-platform PYTHONPATH setup for APIM Samples. + +This script automatically detects the project root and configures PYTHONPATH +to include shared Python modules. Cross-platform compatibility is achieved by: +- Using pathlib.Path for all file operations (handles Windows/Unix path separators) +- Using absolute paths (eliminates relative path issues across platforms) +- Using UTF-8 encoding explicitly (ensures consistent file encoding) +- Using Python's sys.path for runtime PYTHONPATH configuration +""" + +import sys +from pathlib import Path # Cross-platform path handling (Windows: \, Unix: /) + + +def get_project_root() -> Path: + """ + Get the absolute path to the project root directory. + + Cross-platform strategy: + - Uses pathlib.Path for consistent path operations across OS + - Searches upward from script location to find project indicators + - Returns absolute paths that work on Windows, macOS, and Linux + + Returns: + Path: Absolute path to project root directory + """ + + # Start from script's parent directory (since we're in setup/ folder) + # Path(__file__).resolve() gives absolute path, .parent.parent goes up two levels + start_path = Path(__file__).resolve().parent.parent + + # Project root indicators - files that should exist at project root + # These help identify the correct directory regardless of where script is run + indicators = ['README.md', 'requirements.txt', 'bicepconfig.json'] + current_path = start_path + + # Walk up the directory tree until we find all indicators or reach filesystem root + while current_path != current_path.parent: # Stop at filesystem root + # Check if all indicator files exist in current directory + if all((current_path / indicator).exists() for indicator in indicators): + return current_path + current_path = current_path.parent + + # Fallback: if indicators not found, assume parent of script directory is project root + # This handles cases where the project structure might be different + return Path(__file__).resolve().parent.parent + + +def setup_python_path() -> None: + """ + Add shared Python modules to PYTHONPATH for runtime import resolution. + + This modifies sys.path in the current Python session to enable imports + from the shared/python directory. Cross-platform compatibility: + - Uses pathlib for path construction (handles OS-specific separators) + - Converts to string only when needed for sys.path compatibility + - Uses sys.path.insert(0, ...) to prioritize our modules + """ + + project_root = get_project_root() + # Use pathlib's / operator for cross-platform path joining + shared_python_path = project_root / 'shared' / 'python' + + if shared_python_path.exists(): + # Convert Path object to string for sys.path compatibility + shared_path_str = str(shared_python_path) + + # Check if path is already in sys.path to avoid duplicates + if shared_path_str not in sys.path: + # Insert at beginning to prioritize our modules over system modules + sys.path.insert(0, shared_path_str) + print(f"Added to PYTHONPATH: {shared_path_str}") + + +def generate_env_file() -> None: + """ + Generate .env file with cross-platform absolute paths for VS Code integration. + Creates a .env file that VS Code's Python extension reads to configure + the Python environment. Cross-platform features: + - Uses absolute paths (no relative path issues) + - Explicit UTF-8 encoding (consistent across platforms) + - pathlib handles path separators automatically (\\ on Windows, / on Unix) + - Works with VS Code's python.envFile setting + """ + + project_root = get_project_root() + shared_python_path = project_root / 'shared' / 'python' + + # Create .env file content with absolute paths + # These paths will be automatically correct for the current platform + env_content = f"""# Auto-generated PYTHONPATH for VS Code - Run 'python setup/setup_python_path.py' to regenerate +PROJECT_ROOT={project_root} +PYTHONPATH={shared_python_path} +""" + + env_file_path = project_root / '.env' + + # Use explicit UTF-8 encoding for cross-platform text file compatibility + # This ensures the file reads correctly on all operating systems + with open(env_file_path, 'w', encoding='utf-8') as f: + f.write(env_content) + + print() + print(f"Generated .env file : {env_file_path}") + print(f"PROJECT_ROOT : {project_root}") + print(f"PYTHONPATH : {shared_python_path}\n") + print("All done!\n") + + +# Script entry point - handles command-line arguments +if __name__ == "__main__": + # Check for --generate-env flag to create .env file for VS Code + if len(sys.argv) > 1 and sys.argv[1] == "--generate-env": + generate_env_file() + else: + # Default behavior: modify current session's PYTHONPATH + setup_python_path()