From 147458b658987084ac7488bb8b824ca650ed4b66 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Sun, 20 Sep 2026 19:50:56 +0800 Subject: [PATCH] Fix Windows script path normalization --- custom_components/pyscript/__init__.py | 11 ++++++----- tests/test_init.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/custom_components/pyscript/__init__.py b/custom_components/pyscript/__init__.py index 590e3d0..59d8cdd 100644 --- a/custom_components/pyscript/__init__.py +++ b/custom_components/pyscript/__init__.py @@ -74,6 +74,11 @@ CONFIG_SCHEMA = vol.Schema({DOMAIN: PYSCRIPT_SCHEMA}, extra=vol.ALLOW_EXTRA) +def _relative_script_path(path: str, root: str) -> str: + """Return a slash-separated path relative to the pyscript root.""" + return os.path.relpath(path, root).replace(os.sep, "/") + + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Component setup, run import config flow for each entry in config.""" await restore_state(hass) @@ -528,11 +533,7 @@ def glob_read_files( for path, match, check_config, autoload in load_paths: for this_path in sorted(glob.glob(os.path.join(pyscript_dir, path, match), recursive=True)): rel_import_path = None - rel_path = this_path - if rel_path.startswith(pyscript_dir): - rel_path = rel_path[len(pyscript_dir) :] - if rel_path.startswith("/"): - rel_path = rel_path[1:] + rel_path = _relative_script_path(this_path, pyscript_dir) if rel_path[0] == "#" or rel_path.find("/#") >= 0: # skip "commented" files and directories continue diff --git a/tests/test_init.py b/tests/test_init.py index 47995dd..41ebf75 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -3,11 +3,13 @@ from ast import literal_eval import asyncio from datetime import datetime as dt +import ntpath import pathlib from unittest.mock import mock_open, patch import pytest +import custom_components.pyscript as pyscript_component from custom_components.pyscript import trigger from custom_components.pyscript.const import DOMAIN from custom_components.pyscript.event import Event @@ -70,6 +72,19 @@ async def wait_until_done(notify_q): return await asyncio.wait_for(notify_q.get(), timeout=4) +def test_relative_script_path_normalizes_windows_paths(monkeypatch): + """Script paths should derive portable context names on Windows.""" + monkeypatch.setattr(pyscript_component.os.path, "relpath", ntpath.relpath) + monkeypatch.setattr(pyscript_component.os, "sep", "\\") + + assert ( + pyscript_component._relative_script_path( # pylint: disable=protected-access + r"C:\config\pyscript\hello.py", r"C:\config\pyscript" + ) + == "hello.py" + ) + + @pytest.mark.asyncio async def test_setup_makedirs_on_no_dir(hass, caplog): """Test setup calls os.makedirs when no dir found."""