From 1e936e0b2e35cbd039dbe3095b756e417cc5017d Mon Sep 17 00:00:00 2001 From: Jerilyn Date: Thu, 22 Jan 2026 09:03:50 -0800 Subject: [PATCH 1/4] Updating bootstrap to fix windows experience --- bootstrap.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index e6edaeb..5038acb 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -100,6 +100,12 @@ from typing import Any, NamedTuple from venv import create as create_venv + +def _is_windows() -> bool: + """Determines if the current platform is Windows.""" + return sys.platform == 'win32' + + DEFAULT_DEBUG: bool = False """Enable debug output only if --debug is specified. @@ -209,17 +215,34 @@ def __str__(self) -> str: To activate the project's development virtual environment, run: - {{activate}} +{{activate}} To deactivate the virtual environment, run: - deactivate +{{deactivate}} {TOOL_USAGE_INSTRUCTIONS} """ + + # --- Confirmation prompt message --- +if _is_windows(): + PLATFORM_PROMPT_MESSAGE = """ +************************************************************ +If you are using PowerShell, the script will attempt to set +the execution policy to allow running scripts in the virtual +environment. This is required to activate the virtual +environment in PowerShell. + +The execution policy will be set only for the current user. +************************************************************ + +""" +else: + PLATFORM_PROMPT_MESSAGE = '' + CONFIRMATION_PROMPT_MESSAGE = f""" This script will create a {VENV_DIR} directory in the root of the current repository. @@ -228,7 +251,10 @@ def __str__(self) -> str: and install the project as an editable package into the virtual environment. -No changes will be made to your system install of Python. +It will also install a git pre-commit hook for the repository. + +{PLATFORM_PROMPT_MESSAGE} +No other changes will be made to your system install of Python. Continue? [y/n] """ @@ -305,6 +331,8 @@ def __str__(self) -> str: """The detected version control system in use (git, hg, or none).""" +_powershell_execution_policy_set: bool = False + def run_post_install_steps(python_exe: Path, root_path: Path, bin_dir: Path) -> None: """Runs any post-installation steps required after installing tools. @@ -338,10 +366,6 @@ def __init__(self, message: str, error_code: int = 1) -> None: self.error_code = error_code -def _is_windows() -> bool: - """Determines if the current platform is Windows.""" - return sys.platform == 'win32' - def _validate_string(value: str, name: str) -> None: """Validates that the input is a string. @@ -976,6 +1000,48 @@ def _build_install_command(base_command: list[str | Path], modules: list[Install return command +def set_powershell_execution_policy() -> None: + """Sets the PowerShell execution policy to RemoteSigned for the current user if on Windows.""" + if not _is_windows(): + return + + controlled_print('--> Configuring PowerShell execution policy...') + try: + global _powershell_execution_policy_set # pylint: disable=global-statement + + # Check current policy + check_command = ['powershell', '-Command', 'Get-ExecutionPolicy'] + result = subprocess.run(check_command, capture_output=True, text=True, check=False) + current_policy = result.stdout.strip() + + if current_policy == 'RemoteSigned': + controlled_print('PowerShell execution policy is already set to RemoteSigned.') + _powershell_execution_policy_set = True + return + + controlled_print('Setting PowerShell execution policy to RemoteSigned for the current user.') + # This command requires user confirmation if run interactively in a PS prompt, + # but it should proceed without a prompt when called via subprocess from a non-interactive script. + # Using -Force to be certain. + set_command: list[str | Path] = [ + 'powershell', + '-Command', + 'Set-ExecutionPolicy', + '-ExecutionPolicy', + 'RemoteSigned', + '-Scope', + 'CurrentUser', + '-Force', + ] + run_command(set_command) + controlled_print('PowerShell execution policy has been set to RemoteSigned.') + _powershell_execution_policy_set = True + + except (subprocess.CalledProcessError, FileNotFoundError, FatalBootstrapError) as e: + controlled_print(f'Warning: Failed to set PowerShell execution policy: {e}') + controlled_print('You may need to run the following command in PowerShell manually:') + controlled_print('Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser') + def print_instructions(template: str) -> None: """Prints instructions to the user on how to activate the virtual environment and use the installed tools. @@ -998,20 +1064,37 @@ def print_instructions(template: str) -> None: activate_script = f""" Windows Command Prompt: - {ACTIVATED_VENV_DIR}\\Scripts\\activate.bat + {ACTIVATED_VENV_DIR}\\Scripts\\activate PowerShell: - {ACTIVATED_VENV_DIR}\\Scripts\\Activate.ps1 + .\\{ACTIVATED_VENV_DIR}\\Scripts\\Activate.ps1 Note: For PowerShell, you may need to adjust your execution policy to run scripts. You can do this by running PowerShell and executing: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned """ - instructions = template.format(activate=activate_script) + deactivate_script: str = """ +To deactivate the virtual environment, run: + +Unix/macOS/Linux/fish/Windows Command Prompt/PowerShell: + + deactivate + +""" + instructions = template.format(activate=activate_script, deactivate=deactivate_script) controlled_print(instructions) + if _is_windows() and not _powershell_execution_policy_set: + controlled_print('\n' + '*' * 60) + controlled_print('Warning: Failed to set PowerShell execution policy.') + controlled_print('This may prevent you from activating the virtual environment in PowerShell.') + controlled_print('To fix this, open PowerShell and run:') + controlled_print(' Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser') + controlled_print('For more information, see the Microsoft documentation on execution policies.') + controlled_print('*') + def parse_arguments() -> argparse.Namespace: """Parses command-line arguments.""" @@ -1081,6 +1164,7 @@ def main() -> None: create_virtual_environment(venv_dir, python_exe) install_tools(python_exe, BOOTSTRAP_MODULES) install_vcs_hooks(repo_root, forced=args.force_hooks) + set_powershell_execution_policy() bin_dir = venv_dir / ('Scripts' if _is_windows() else 'bin') run_post_install_steps(python_exe=python_exe, root_path=repo_root, bin_dir=bin_dir) From 753e83cd31498856b131a1b795c1fb4d035ea5e8 Mon Sep 17 00:00:00 2001 From: Jerilyn Franz Date: Thu, 22 Jan 2026 09:34:59 -0800 Subject: [PATCH 2/4] Removing pydeps from uv.lock updating pre-commit hook --- hooks/pre-commit | 17 +++++++++-------- uv.lock | 25 ------------------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/hooks/pre-commit b/hooks/pre-commit index 8945e55..f01f5fd 100644 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -1,12 +1,8 @@ -#!/bin/sh -"true" '''\' -if command -v python3 >/dev/null 2>&1; then - exec python3 "$0" "$@" -else - exec python "$0" "$@" -fi -''' +#!/usr/bin/python # -*- coding: utf-8 -*- +"""Pre-commit hook to run tests via Tox or UV before allowing a commit.""" + +import io import os import shutil import subprocess @@ -17,6 +13,11 @@ from typing import Sequence, Union # Define types for compatibility with Python < 3.10 ProcessResult = Union[subprocess.CompletedProcess, subprocess.CalledProcessError] +# Force stdout to use UTF-8 encoding, regardless of the environment. +# This prevents UnicodeEncodeError when printing emojis on Windows. +if sys.stdout.encoding != 'utf-8': + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + def find_executable(name: str) -> Union[str, None]: """Find executable in .venv, PATH, or common user directories.""" diff --git a/uv.lock b/uv.lock index f70ec27..d05678c 100644 --- a/uv.lock +++ b/uv.lock @@ -57,7 +57,6 @@ dev = [ { name = "hatch", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "hatchling", version = "1.28.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "mypy", marker = "python_full_version >= '3.12'" }, - { name = "pydeps", marker = "python_full_version >= '3.12'" }, { name = "pytest", marker = "python_full_version >= '3.12'" }, { name = "pytest-cov", marker = "python_full_version >= '3.12'" }, { name = "ruff", marker = "python_full_version >= '3.12'" }, @@ -89,7 +88,6 @@ tools = [ { name = "hatch", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "hatchling", version = "1.27.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "hatchling", version = "1.28.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "pydeps" }, { name = "tox", version = "4.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "tox", version = "4.34.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "tox-uv", version = "1.28.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, @@ -109,7 +107,6 @@ dev = [ { name = "hatch", marker = "python_full_version >= '3.12'", specifier = ">=1.13" }, { name = "hatchling", marker = "python_full_version >= '3.12'", specifier = ">=1.27" }, { name = "mypy", marker = "python_full_version >= '3.12'", specifier = ">=1.13.1" }, - { name = "pydeps", marker = "python_full_version >= '3.12'", specifier = ">=1.23.2" }, { name = "pytest", marker = "python_full_version >= '3.12'", specifier = ">=7.4.1,<9.0.0" }, { name = "pytest-cov", marker = "python_full_version >= '3.12'", specifier = ">=4.2.1" }, { name = "ruff", marker = "python_full_version >= '3.12'", specifier = ">=0.14.11" }, @@ -138,7 +135,6 @@ test = [ tools = [ { name = "hatch", specifier = ">=1.13" }, { name = "hatchling", specifier = ">=1.27" }, - { name = "pydeps", specifier = ">=1.23.2" }, { name = "tox", specifier = ">=4.22.0" }, { name = "tox-uv", specifier = ">=1.13.1" }, { name = "uv", specifier = ">=0.9.18" }, @@ -1537,18 +1533,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] -[[package]] -name = "pydeps" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "stdlib-list" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/3e/750b00bc373fe0ed1981be89f432bb58ff45ae137d486d5d1667b5f89e76/pydeps-3.0.2.tar.gz", hash = "sha256:9e73575ddd6b5614cb2463a4ee9392817cc51002a2e46184d4fb1ffef8d15832", size = 53202, upload-time = "2026-01-06T16:52:03.1Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/c5/e3c81d05e4ab54fab5ba613c28ec91b2baee26671c5645293b1b020c13f4/pydeps-3.0.2-py3-none-any.whl", hash = "sha256:890515899ffdb017255ba035704cfc711966469ad3aa3574c5b0b13f9f676e32", size = 47765, upload-time = "2026-01-06T16:52:01.794Z" }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -1899,15 +1883,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] -[[package]] -name = "stdlib-list" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/25/f1540879c8815387980e56f973e54605bd924612399ace31487f7444171c/stdlib_list-0.12.0.tar.gz", hash = "sha256:517824f27ee89e591d8ae7c1dd9ff34f672eae50ee886ea31bb8816d77535675", size = 60923, upload-time = "2025-10-24T19:21:22.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/3d/2970b27a11ae17fb2d353e7a179763a2fe6f37d6d2a9f4d40104a2f132e9/stdlib_list-0.12.0-py3-none-any.whl", hash = "sha256:df2d11e97f53812a1756fb5510393a11e3b389ebd9239dc831c7f349957f62f2", size = 87615, upload-time = "2025-10-24T19:21:20.619Z" }, -] - [[package]] name = "tomli" version = "2.4.0" From 39b810bf9b24b0035fe53ed17ee3e8219f61753b Mon Sep 17 00:00:00 2001 From: Jerilyn Date: Thu, 22 Jan 2026 09:42:21 -0800 Subject: [PATCH 3/4] Updating docs for better windows instructions --- docs_source/contributing.rst | 4 ++-- docs_source/installation.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs_source/contributing.rst b/docs_source/contributing.rst index e70d5a9..34e6ea5 100644 --- a/docs_source/contributing.rst +++ b/docs_source/contributing.rst @@ -69,12 +69,12 @@ To contribute code or documentation updates to `autopypath`, follow these steps: .. code-block:: text :caption: Activating the virtual environment on Windows - .venv\Scripts\activate.bat + .venv\Scripts\activate .. code-block:: text :caption: Activating the virtual environment on Windows PowerShell - .venv\Scripts\Activate.ps1 + .\.venv\Scripts\Activate.ps1 6. Make your changes and test them thoroughly using the existing test suite and by adding new tests if necessary. Our goal is to maintain high code quality diff --git a/docs_source/installation.rst b/docs_source/installation.rst index 3d0134e..9571245 100644 --- a/docs_source/installation.rst +++ b/docs_source/installation.rst @@ -57,14 +57,14 @@ or on Windows use: .. code-block:: text :caption: Activating the virtual environment on Windows - .venv\Scripts\activate.bat + .venv\Scripts\activate or if using PowerShell: .. code-block:: text :caption: Activating the virtual environment on Windows PowerShell - .venv\Scripts\Activate.ps1 + .\.venv\Scripts\Activate.ps1 This will set up the development environment and install `autopypath` in editable mode. From 739c898395ab8f4c6ad30bb22168dc525d0abafe Mon Sep 17 00:00:00 2001 From: Jerilyn Franz Date: Thu, 22 Jan 2026 09:43:54 -0800 Subject: [PATCH 4/4] Working on cross-platform compatibility for pre-commit hook --- hooks/pre-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/pre-commit b/hooks/pre-commit index f01f5fd..6ad6fea 100644 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # -*- coding: utf-8 -*- """Pre-commit hook to run tests via Tox or UV before allowing a commit."""