Skip to content

Commit bae617d

Browse files
committed
Exceptions: add toggle to disable mapping Error.Number to Python exceptions
Includes new tests, and a few general formatting/docstring changes. Closes #54
1 parent 8dc4285 commit bae617d

5 files changed

Lines changed: 79 additions & 7 deletions

File tree

dss/IBus.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def ZscRefresh(self) -> bool:
5757

5858
@property
5959
def Coorddefined(self) -> bool:
60-
'''False=0 else True. Indicates whether a coordinate has been defined for this bus'''
60+
'''Indicates whether a coordinate has been defined for this bus'''
6161
return self.CheckForError(self._lib.Bus_Get_Coorddefined()) != 0
6262

6363
@property

dss/IError.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,35 @@ def ExtendedErrors(self) -> bool:
6161
off to restore the previous behavior.
6262
6363
(API Extension)
64-
6564
'''
6665
return self._lib.Error_Get_ExtendedErrors() != 0
6766

6867
@ExtendedErrors.setter
6968
def ExtendedErrors(self, Value: bool):
7069
self._lib.Error_Set_ExtendedErrors(Value)
70+
71+
@property
72+
def UseExceptions(self) -> bool:
73+
"""
74+
Controls whether the automatic error checking mechanism is enable, i.e., if
75+
the DSS engine errors (from the `Error` interface) are mapped exception when
76+
detected.
77+
78+
**When disabled, the user takes responsibility for checking for errors.**
79+
This can be done through the `Error` interface. When `Error.Number` is not
80+
zero, there should be an error message in `Error.Description`. This is compatible
81+
with the behavior on the official OpenDSS (Windows-only COM implementation) when
82+
`AllowForms` is disabled.
83+
84+
Users can also use the DSS command `Export ErrorLog` to inspect for errors.
85+
86+
**WARNING:** This is a global setting, affects all DSS instances from DSS-Python
87+
and OpenDSSDirect.py.
88+
89+
(API Extension)
90+
"""
91+
return Base._use_exceptions
92+
93+
@UseExceptions.setter
94+
def UseExceptions(self, value: bool):
95+
Base._enable_exceptions(value)

dss/IPVSystems.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def daily(self) -> str:
9292
'''
9393
Name of the dispatch shape to use for daily simulations. Must be previously
9494
defined as a Loadshape object of 24 hrs, typically. In the default dispatch
95-
mode, the PVSystem element uses this loadshape to trigger State changes.
95+
mode, the PVSystem element uses this loadshape to trigger State changes.
9696
9797
(API Extension)
9898
'''

dss/_cffi_api_util.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __str__(self):
4343
return f'(#{self.args[0]}) {self.args[1]}'
4444

4545

46-
# For backwards compatibility
46+
# For backwards compatibility, will be removed for version 1.0
4747
DssException = DSSException
4848
use_com_compat = set_case_insensitive_attributes
4949

@@ -105,6 +105,8 @@ class Base:
105105
'_errorPtr',
106106
]
107107

108+
_use_exceptions = True
109+
108110
def __init__(self, api_util):
109111
self._lib = api_util.lib
110112
self._api_util = api_util
@@ -137,9 +139,39 @@ def __init__(self, api_util):
137139
cls._dss_attributes = lowercase_map
138140

139141

142+
@staticmethod
143+
def _enable_exceptions(do_enable: bool):
144+
"""
145+
Controls whether the automatic error checking mechanism is enable, i.e., if
146+
the DSS engine errors (from the `Error` interface) are mapped exception when
147+
detected.
148+
149+
**When disabled, the user takes responsibility for checking for errors.**
150+
This can be done through the `Error` interface. When `Error.Number` is not
151+
zero, there should be an error message in `Error.Description`. This is compatible
152+
with the behavior on the official OpenDSS (Windows-only COM implementation) when
153+
`AllowForms` is disabled.
154+
155+
Users can also use the DSS command `Export ErrorLog` to inspect for errors.
156+
157+
**WARNING:** This is a global setting, affects all DSS instances from DSS-Python
158+
and OpenDSSDirect.py.
159+
"""
160+
Base._use_exceptions = bool(do_enable)
161+
140162
def _check_for_error(self, result=None):
141-
'''Checks for an OpenDSS error. Raises an exception if any, otherwise returns the `result` parameter.'''
142-
if self._errorPtr[0]:
163+
"""
164+
Checks for a DSS engine error (on the default configuration).
165+
166+
By default, raises an exception if any error is detected, otherwise returns the `result` parameter.
167+
168+
If the user disabled exceptions, any error is simply ignored. Note that, in this case, manually
169+
calling this function would have no purpose/effects.
170+
171+
Note that, **in the future**, we may try showing a popup form like the official OpenDSS does on Windows
172+
if AllowForms is True. This behavior is not very portable though and not adequate for automated scripts.
173+
"""
174+
if self._errorPtr[0] and Base._use_exceptions:
143175
error_num = self._errorPtr[0]
144176
self._errorPtr[0] = 0
145177
raise DSSException(error_num, self._get_string(self._lib.Error_Get_Description()))

tests/test_general.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def setup_function():
2929
DSS.AllowChangeDir = True
3030
DSS.COMErrorResults = True # TODO: change to False
3131
DSS.CompatFlags = 0
32-
32+
DSS.Error.UseExceptions = True
3333

3434
def test_zip_redirect():
3535
with pytest.raises(DSSException):
@@ -604,6 +604,21 @@ def test_essentials(DSS: IDSS = DSS):
604604
for expected, b in zip(['sourcebus', '1', '2', '3'], DSS.ActiveCircuit.ActiveBus):
605605
assert expected == b.Name
606606

607+
def test_exception_control(DSS: IDSS = DSS):
608+
# Default behavior
609+
assert DSS.Error.UseExceptions
610+
with pytest.raises(DSSException):
611+
DSS.Text.Command = 'this_is_an_invalid_command1'
612+
613+
# Behavior without exceptions
614+
DSS.Error.UseExceptions = False
615+
assert not DSS.Error.UseExceptions
616+
DSS.Text.Command = 'this_is_an_invalid_command2'
617+
# There should be an error code waiting for us...
618+
assert DSS.Error.Number != 0
619+
# ...but it should be gone after we read it
620+
assert DSS.Error.Number == 0
621+
607622

608623
def test_patch_comtypes():
609624
if WIN32:

0 commit comments

Comments
 (0)