This is an automated email from the ASF dual-hosted git repository.

kaxil pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new fca1c01c73e Fix Windows UnicodeDecodeError during pytest collection 
(#73536)
fca1c01c73e is described below

commit fca1c01c73ee41690d3a209e8d45335868e1fe53
Author: Divyansh Yadav <[email protected]>
AuthorDate: Fri Oct 2 04:28:04 2026 +0530

    Fix Windows UnicodeDecodeError during pytest collection (#73536)
---
 devel-common/src/tests_common/pytest_plugin.py     |  9 +++++---
 .../src/tests_common/test_utils/providers.py       |  2 +-
 scripts/ci/prek/common_prek_utils.py               | 26 ++++++++++++----------
 scripts/ci/prek/update_providers_dependencies.py   | 14 ++++++------
 4 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/devel-common/src/tests_common/pytest_plugin.py 
b/devel-common/src/tests_common/pytest_plugin.py
index bb85fe60c2d..63abaaaba18 100644
--- a/devel-common/src/tests_common/pytest_plugin.py
+++ b/devel-common/src/tests_common/pytest_plugin.py
@@ -166,7 +166,7 @@ ALL_PYPROJECT_TOML_FILES: list[Path] = []
 
 
 def get_all_provider_pyproject_toml_provider_yaml_files() -> Generator[Path, 
None, None]:
-    pyproject_toml_content = 
AIRFLOW_PYPROJECT_TOML_FILE_PATH.read_text().splitlines()
+    pyproject_toml_content = 
AIRFLOW_PYPROJECT_TOML_FILE_PATH.read_text(encoding="utf-8").splitlines()
     in_workspace = False
     for line in pyproject_toml_content:
         trimmed_line = line.strip()
@@ -198,9 +198,12 @@ if not PROVIDER_DEPENDENCIES_JSON_PATH.exists() or not 
PROVIDER_DEPENDENCIES_JSO
     subprocess.check_call(["uv", "run", 
UPDATE_PROVIDER_DEPENDENCIES_SCRIPT.as_posix()])
 else:
     calculated_provider_deps_hash = _calculate_provider_deps_hash()
-    if calculated_provider_deps_hash.strip() != 
PROVIDER_DEPENDENCIES_JSON_HASH_PATH.read_text().strip():
+    if (
+        calculated_provider_deps_hash.strip()
+        != 
PROVIDER_DEPENDENCIES_JSON_HASH_PATH.read_text(encoding="utf-8").strip()
+    ):
         subprocess.check_call(["uv", "run", 
UPDATE_PROVIDER_DEPENDENCIES_SCRIPT.as_posix()])
-        
PROVIDER_DEPENDENCIES_JSON_HASH_PATH.write_text(calculated_provider_deps_hash)
+        
PROVIDER_DEPENDENCIES_JSON_HASH_PATH.write_text(calculated_provider_deps_hash, 
encoding="utf-8")
 # End of copied code from breeze
 
 os.environ["AIRFLOW__CORE__ALLOWED_DESERIALIZATION_CLASSES"] = 
"airflow.*\nunit.*\n"
diff --git a/devel-common/src/tests_common/test_utils/providers.py 
b/devel-common/src/tests_common/test_utils/providers.py
index 37a999f3422..fc3778e6c01 100644
--- a/devel-common/src/tests_common/test_utils/providers.py
+++ b/devel-common/src/tests_common/test_utils/providers.py
@@ -78,7 +78,7 @@ def get_suspended_providers_folders() -> list[str]:
     """
     suspended_providers = []
     for provider_path in AIRFLOW_PROVIDERS_ROOT_PATH.rglob("provider.yaml"):
-        provider_yaml = yaml.safe_load(provider_path.read_text())
+        provider_yaml = 
yaml.safe_load(provider_path.read_text(encoding="utf-8"))
         if provider_yaml["state"] == "suspended":
             
suspended_providers.append(provider_path.parent.resolve().as_posix())
     return suspended_providers
diff --git a/scripts/ci/prek/common_prek_utils.py 
b/scripts/ci/prek/common_prek_utils.py
index 3a799b2dd19..b68ec286611 100644
--- a/scripts/ci/prek/common_prek_utils.py
+++ b/scripts/ci/prek/common_prek_utils.py
@@ -121,7 +121,7 @@ def run_command(*args, **kwargs) -> None:
 
 
 def read_airflow_version() -> str:
-    ast_obj = ast.parse((AIRFLOW_CORE_SOURCES_PATH / "airflow" / 
"__init__.py").read_text())
+    ast_obj = ast.parse((AIRFLOW_CORE_SOURCES_PATH / "airflow" / 
"__init__.py").read_text(encoding="utf-8"))
     for node in ast_obj.body:
         if isinstance(node, ast.Assign):
             if node.targets[0].id == "__version__":  # type: 
ignore[attr-defined]
@@ -142,7 +142,7 @@ def _read_global_constants_assignment(name: str) -> Any:
     (``NAME: type = ...``). The value must be a literal so it can be safely
     evaluated with ``ast.literal_eval``.
     """
-    tree = ast.parse(GLOBAL_CONSTANTS_PATH.read_text())
+    tree = ast.parse(GLOBAL_CONSTANTS_PATH.read_text(encoding="utf-8"))
     for node in tree.body:
         if isinstance(node, ast.Assign):
             for target in node.targets:
@@ -229,7 +229,7 @@ def insert_documentation(
     extra_information: str | None = None,
 ) -> bool:
     found = False
-    old_content = file_path.read_text()
+    old_content = file_path.read_text(encoding="utf-8")
     lines = old_content.splitlines(keepends=True)
     replacing = False
     result: list[str] = []
@@ -251,7 +251,7 @@ def insert_documentation(
         print(f"Header {header} not found in {file_path}")
         sys.exit(1)
     if new_content != old_content:
-        file_path.write_text(new_content)
+        file_path.write_text(new_content, encoding="utf-8")
         console.print(f"Updated {file_path} with {extra_information or 
'generated documentation'}")
         return True
     return False
@@ -277,7 +277,7 @@ def read_uv_required_min_version() -> tuple[str, tuple[int, 
...]]:
     Parses ``[tool.uv] required-version = ">=X.Y.Z"`` and returns ``(raw, 
tuple)``.
     We parse by regex to avoid pulling a TOML dep into every prek script.
     """
-    pyproject = (AIRFLOW_ROOT_PATH / "pyproject.toml").read_text()
+    pyproject = (AIRFLOW_ROOT_PATH / 
"pyproject.toml").read_text(encoding="utf-8")
     # Narrow to the [tool.uv] section so we don't match a different 
required-version.
     match = re.search(r"^\[tool\.uv\]\s*$(?P<body>.*?)(?=^\[|\Z)", pyproject, 
re.MULTILINE | re.DOTALL)
     if not match:
@@ -381,12 +381,14 @@ def describe_breeze_not_running_from_lock() -> str | None:
     if resolved.is_relative_to(BREEZE_LOCKED_VENV_PATH.resolve()):
         return None
     try:
-        text = Path(breeze_bin).read_text()
+        text = Path(breeze_bin).read_text(encoding="utf-8")
     except (OSError, UnicodeDecodeError):
         return None
     if BREEZE_SHIM_MARKER not in text:
         return f"`{breeze_bin}` is a legacy global install, which ignores the 
lock"
-    expected_version = _read_shim_version(SETUP_BREEZE_PATH.read_text(), 
SETUP_BREEZE_SHIM_VERSION_PREFIX)
+    expected_version = _read_shim_version(
+        SETUP_BREEZE_PATH.read_text(encoding="utf-8"), 
SETUP_BREEZE_SHIM_VERSION_PREFIX
+    )
     if expected_version is None:
         return None
     installed_version = _read_shim_version(text, BREEZE_SHIM_VERSION_PREFIX)
@@ -693,7 +695,7 @@ def get_all_provider_ids(
         if excluded_states:
             import yaml
 
-            provider_info = yaml.safe_load(provider_file.read_text())
+            provider_info = 
yaml.safe_load(provider_file.read_text(encoding="utf-8"))
             if provider_info.get("state") in excluded_states:
                 continue
         provider_id = get_provider_id_from_path(provider_file)
@@ -723,7 +725,7 @@ def get_all_provider_info_dicts() -> dict[str, dict]:
         provider_id = 
str(provider_file.parent.relative_to(AIRFLOW_PROVIDERS_ROOT_PATH)).replace(os.sep,
 ".")
         import yaml
 
-        provider_info = yaml.safe_load(provider_file.read_text())
+        provider_info = 
yaml.safe_load(provider_file.read_text(encoding="utf-8"))
         if provider_info["state"] != "suspended":
             providers[provider_id] = provider_info
     return providers
@@ -880,7 +882,7 @@ def get_imports_from_file(file_path: Path, *, 
only_top_level: bool) -> list[str]
     When only_top_level = False then returns
         ['os', 'collections.defaultdict', 'numpy', 'pandas.DataFrame', 'json', 
'pathlib.Path', 'pathlib.PurePath']
     """
-    root = ast.parse(file_path.read_text(), file_path.name)
+    root = ast.parse(file_path.read_text(encoding="utf-8"), file_path.name)
     imports: list[str] = []
 
     nodes = ast.iter_child_nodes(root) if only_top_level else ast.walk(root)
@@ -1070,11 +1072,11 @@ class AllowlistManager(abc.ABC):
         """Return mapping of ``relative_path -> allowed_count``."""
         if not self.allowlist_file.exists():
             return {}
-        return self.parse(self.allowlist_file.read_text())
+        return self.parse(self.allowlist_file.read_text(encoding="utf-8"))
 
     def save(self, counts: dict[str, int]) -> None:
         lines = [f"{rel}::{count}" for rel, count in sorted(counts.items())]
-        self.allowlist_file.write_text("\n".join(lines) + "\n")
+        self.allowlist_file.write_text("\n".join(lines) + "\n", 
encoding="utf-8")
 
     @abc.abstractmethod
     def iter_files(self) -> Iterable[Path]:
diff --git a/scripts/ci/prek/update_providers_dependencies.py 
b/scripts/ci/prek/update_providers_dependencies.py
index 41b0c4fe79f..ce0a4651a0d 100755
--- a/scripts/ci/prek/update_providers_dependencies.py
+++ b/scripts/ci/prek/update_providers_dependencies.py
@@ -70,7 +70,7 @@ def load_pyproject_toml(pyproject_toml_file_path: Path) -> 
dict[str, Any]:
         import tomllib
     except ImportError:
         import tomli as tomllib  # type: ignore[no-redef]
-    return tomllib.loads(pyproject_toml_file_path.read_text())
+    return tomllib.loads(pyproject_toml_file_path.read_text(encoding="utf-8"))
 
 
 def find_all_providers_and_provider_files():
@@ -95,7 +95,7 @@ def find_all_providers_and_provider_files():
                         f"The provider {provider_name} does not have 'src' 
folder"
                         f" in {provider_yaml_file.parent}"
                     )
-                provider_info = yaml.safe_load(provider_yaml_file.read_text())
+                provider_info = 
yaml.safe_load(provider_yaml_file.read_text(encoding="utf-8"))
                 if provider_info["state"] == "suspended":
                     suspended_paths.append(
                         
provider_yaml_file.parent.relative_to(AIRFLOW_PROVIDERS_ROOT_PATH).as_posix()
@@ -226,13 +226,13 @@ if __name__ == "__main__":
         console.print("[red]Errors found during verification. Exiting!")
         console.print()
         sys.exit(1)
-    old_dependencies = (
-        DEPENDENCIES_JSON_FILE_PATH.read_text() if 
DEPENDENCIES_JSON_FILE_PATH.exists() else "{}"
+    old_content = (
+        DEPENDENCIES_JSON_FILE_PATH.read_text(encoding="utf-8")
+        if DEPENDENCIES_JSON_FILE_PATH.exists()
+        else ""
     )
-    new_dependencies = json.dumps(unique_sorted_dependencies, indent=2) + "\n"
-    old_content = DEPENDENCIES_JSON_FILE_PATH.read_text() if 
DEPENDENCIES_JSON_FILE_PATH.exists() else ""
     new_content = json.dumps(unique_sorted_dependencies, indent=2) + "\n"
-    DEPENDENCIES_JSON_FILE_PATH.write_text(new_content)
+    DEPENDENCIES_JSON_FILE_PATH.write_text(new_content, encoding="utf-8")
     if new_content != old_content:
         console.print()
         console.print(f"Written {DEPENDENCIES_JSON_FILE_PATH}")

Reply via email to