diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 78dbcf0a7..cba3ef671 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,7 @@ Change Log [upcoming release] - 2026-..-.. ------------------------------- +- [FIXED] restored ``OpenDSSDirect.py`` to the ``all``/``dev`` extras so the OpenDSS converter is exercised (and its coverage reported) in CI again; it was dropped in #3062 because installing it alongside ``pytest~=9.1`` crashed the pytest process on Windows. Root cause (see `dss-extensions/OpenDSSDirect.py#148 `_): pytest enables Python's ``faulthandler`` by default, which intercepts a first-chance Windows structured exception that OpenDSSDirect.py's native backend raises -- and normally handles itself -- during import, and misreports it as fatal. Bracketing the import with ``faulthandler.disable()``/``.enable()`` avoids the false crash while leaving ``faulthandler`` protecting the rest of the test run; the converter now runs on Windows instead of merely skipping there. - [ADDED] OpenDSS converter: series (bus-to-bus) ``Reactor`` elements are now imported as a fixed-impedance ``line``, the pattern some feeder libraries (e.g. EPRI's Ckt5/Ckt7) use to model the substation's Thevenin-equivalent source impedance instead of a ``Transformer``. [3.5.4] - 2026-07-08 diff --git a/pandapower/converter/opendss/from_dss.py b/pandapower/converter/opendss/from_dss.py index ffea7d18e..eb5136182 100644 --- a/pandapower/converter/opendss/from_dss.py +++ b/pandapower/converter/opendss/from_dss.py @@ -19,6 +19,7 @@ # The circuit is read through the ``OpenDSSDirect.py`` API (lazy-imported), not a # text parser, so every OpenDSS-supported master file is understood. +import faulthandler import logging import math from dataclasses import dataclass, field @@ -28,7 +29,22 @@ import pandapower as pp try: - import opendssdirect as dss + # On Windows, OpenDSSDirect.py's native backend (dss_python_backend) raises an + # internal, first-chance structured exception during its own startup -- one its + # Free Pascal runtime normally handles and silences itself. If a Windows crash + # handler is already installed (as Python's faulthandler does, and pytest enables + # faulthandler by default), it intercepts that exception first and misreports it + # as fatal, killing the whole process instead of a clean ImportError. Disable + # faulthandler for just this import, then restore it -- it's still needed to + # catch genuine crashes elsewhere. See + # https://github.com/dss-extensions/OpenDSSDirect.py/issues/148 + faulthandler_was_enabled = faulthandler.is_enabled() + faulthandler.disable() + try: + import opendssdirect as dss + finally: + if faulthandler_was_enabled: + faulthandler.enable() opendssdirect_imported = True except ImportError: diff --git a/pandapower/test/converter/test_from_opendss.py b/pandapower/test/converter/test_from_opendss.py index a930a1b1a..5f9449aa5 100644 --- a/pandapower/test/converter/test_from_opendss.py +++ b/pandapower/test/converter/test_from_opendss.py @@ -12,13 +12,27 @@ import promises for symmetric feeders. """ +import faulthandler + import numpy as np import pytest import pandapower as pp -# OpenDSSDirect.py is an optional dependency; skip the whole module without it. -pytest.importorskip("opendssdirect") +# pytest enables faulthandler by default, which on Windows intercepts a first-chance +# structured exception that OpenDSSDirect.py's native backend raises (and normally +# handles itself) during import -- see pandapower/converter/opendss/from_dss.py for +# the full explanation and https://github.com/dss-extensions/OpenDSSDirect.py/issues/148. +# Disable faulthandler for this pre-flight import check the same way, so it doesn't +# crash here before from_dss's own guarded import ever runs. +faulthandler_was_enabled = faulthandler.is_enabled() +faulthandler.disable() +try: + # OpenDSSDirect.py is an optional dependency; skip the whole module without it. + pytest.importorskip("opendssdirect") +finally: + if faulthandler_was_enabled: + faulthandler.enable() from pandapower.converter.opendss import from_opendss diff --git a/pyproject.toml b/pyproject.toml index 8b0e59c3b..490a7b038 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,7 +106,7 @@ typing = [ ] #all = ["pandapower[plotting,performance,fileio,converter,pgm,control,helm,opendss]"] -all = ["pandapower[plotting,performance,fileio,converter,pgm,control]"] +all = ["pandapower[plotting,performance,fileio,converter,pgm,control,opendss]"] dev = ["pandapower[all,docs,test,typing,pandamodels,tutorials]"] # "shapely", "pyproj", "Pyogrio" are dependencies of geopandas and should be already available ("Fiona" got dropped) # "hashlib", "zlib", "base64" produce install problems, so they are not included