1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
Index: fenics-dolfinx/python/dolfinx/__init__.py
===================================================================
--- fenics-dolfinx.orig/python/dolfinx/__init__.py 2025-11-12 15:50:15.708582994 +0100
+++ fenics-dolfinx/python/dolfinx/__init__.py 2025-11-12 15:50:15.700715425 +0100
@@ -27,6 +27,29 @@
default_scalar_type = _np.float64
default_real_type = _np.float64
+import pusimp
+
+def pip_uninstall_call(executable: str, dependency_pypi_name: str, dependency_actual_path: str) -> str:
+ """Report to the user how to uninstall a dependency with pip."""
+ output = f"{executable} -m pip uninstall --break-system-packages {dependency_pypi_name}"
+ if dependency_actual_path.startswith("/usr"):
+ return f"sudo {output}"
+ else:
+ return output
+
+
+pusimp.prevent_user_site_imports(
+ "dolfinx", "apt", "https://fenicsproject.discourse.group/",
+ "/usr/lib/python3/dist-packages",
+ ["basix", "ffcx", "ufl"],
+ ["fenics-basix", "fenics-ffcx", "fenics-ufl"],
+ [False, False, False],
+ ["", "", ""],
+ pip_uninstall_call
+)
+
+del pip_uninstall_call, pusimp
+
from dolfinx import common
from dolfinx import cpp as _cpp
from dolfinx import fem, geometry, graph, io, jit, la, log, mesh, nls, plot
Index: fenics-dolfinx/python/test/debian-only/test_pusimp.py
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ fenics-dolfinx/python/test/debian-only/test_pusimp.py 2025-11-12 15:50:15.701779222 +0100
@@ -0,0 +1,91 @@
+# Copyright (C) 2024 by the pusimp authors
+#
+# This file is part of pusimp for FEniCSx.
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+"""Test DOLFINx patches."""
+
+import os
+import typing
+
+import pytest
+import requests
+
+from pusimp.utils import (
+ assert_package_import_errors_with_local_packages,
+ assert_package_import_errors_with_broken_non_optional_packages,
+ assert_package_import_success_with_allowed_local_packages,
+ assert_package_import_success_without_local_packages
+)
+
+try:
+ response = requests.get("https://www.debian.org", timeout=5)
+except requests.ConnectionError:
+ has_internet = False
+else:
+ has_internet = True
+
+if os.getenv("PUSIMP_EXPECT_DOLFINX_IN_CWD") is None:
+ dolfinx_python_prefix = "/usr/lib/petsc/lib/python3/dist-packages/dolfinx"
+else:
+ dolfinx_python_prefix = os.path.join(os.getcwd(), "dolfinx")
+
+
+def pip_install_call(executable: str, dependency_pypi_name: str) -> str:
+ """Command to be run to install a dependency with pip."""
+ return (
+ f"{executable} -m pip install --ignore-installed --break-system-packages {dependency_pypi_name}"
+ # debian does not ship numpy 2.0 yet, and a pip install-ed numpy 2.0 would break binary compatibility
+ + " 'numpy<2.0'"
+ )
+
+
+def pip_uninstall_call(executable: str, dependency_pypi_name: str, dependency_actual_path: str) -> str:
+ """Command to be run to uninstall a dependency with pip."""
+ return f"{executable} -m pip uninstall --break-system-packages {dependency_pypi_name}"
+
+
+def test_dolfinx_import_success_without_local_packages() -> None:
+ """Test that dolfinx imports correctly without any extra local packages."""
+ assert_package_import_success_without_local_packages(
+ "dolfinx", os.path.join(dolfinx_python_prefix, "__init__.py")
+ )
+
+
+@pytest.mark.skipif(not has_internet, reason="Requires downloading from pypi.org")
+@pytest.mark.parametrize("dependencies_import_name,dependencies_pypi_name,dependencies_extra_error_message", [
+ (["ufl"], ["fenics-ufl"], []),
+ (["basix", "ufl"], ["fenics-basix", "fenics-ufl"], [])
+])
+def test_dolfinx_import_errors_with_local_packages(
+ dependencies_import_name: typing.List[str], dependencies_pypi_name: typing.List[str],
+ dependencies_extra_error_message: typing.List[str]
+) -> None:
+ """Test that dolfinx fails to import with extra local packages."""
+ assert_package_import_errors_with_local_packages(
+ "dolfinx", dependencies_import_name, dependencies_pypi_name, dependencies_extra_error_message,
+ pip_install_call, pip_uninstall_call
+ )
+
+
+@pytest.mark.skipif(not has_internet, reason="Requires downloading from pypi.org")
+@pytest.mark.parametrize("dependencies_import_name,dependencies_pypi_name", [
+ (["ufl"], ["fenics-ufl"])
+])
+def test_dolfinx_import_success_with_allowed_local_packages(
+ dependencies_import_name: typing.List[str], dependencies_pypi_name: typing.List[str]
+) -> None:
+ """Test that dolfinx imports correctly even with extra local packages when asked to allow user-site imports."""
+ assert_package_import_success_with_allowed_local_packages(
+ "dolfinx", os.path.join(dolfinx_python_prefix, "__init__.py"),
+ dependencies_import_name, dependencies_pypi_name, pip_install_call
+ )
+
+
+@pytest.mark.parametrize("dependencies_import_name", [
+ ["ufl"],
+ ["ffcx"]
+])
+def test_dolfinx_import_errors_with_broken_non_optional_packages(dependencies_import_name: typing.List[str]) -> None:
+ """Test that dolfinx fails to import with broken local packages."""
+ assert_package_import_errors_with_broken_non_optional_packages("dolfinx", dependencies_import_name, [False])
|