Skip to content

Commit 2618e5c

Browse files
committed
API util: add warn to use_com_compat to warn on wrong case of attributes.
`test_case_check.py` was added to test this.
1 parent d1f8b6c commit 2618e5c

2 files changed

Lines changed: 112 additions & 24 deletions

File tree

dss/_cffi_api_util.py

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
from __future__ import absolute_import
22
import numpy as np
3-
import os
3+
import os, warnings
44

5-
# The codec was changed to ASCII in version 0.10.0.
5+
# The codec was changed to ASCII in version 0.10.0.
66
# A rewrite of DSS C-API is expected to return UTF8Strings in the future
77
codec = 'ascii'
88

99
interface_classes = set()
1010

11-
def use_com_compat(value=True):
12-
if value:
13-
Base.__getattr__ = Base._getattr
14-
Base.__setattr__ = Base._setattr
15-
elif Base.__getattr__ == Base._getattr:
11+
warn_wrong_case = False
12+
13+
def use_com_compat(use=True, warn=False):
14+
if use:
15+
global warn_wrong_case
16+
warn_wrong_case = warn
17+
if warn_wrong_case:
18+
Base.__getattr__ = Base._getattr_case_check
19+
Base.__setattr__ = Base._setattr_case_check
20+
else:
21+
Base.__getattr__ = Base._getattr
22+
Base.__setattr__ = Base._setattr
23+
24+
elif Base.__getattr__ == Base._getattr or Base.__getattr__ == Base._getattr_case_check:
1625
del Base.__setattr__
17-
del Base.__getattr__
26+
del Base.__getattr__
1827

1928
class DssException(Exception):
2029
pass
21-
30+
2231
class Base(object):
2332
__slots__ = [
2433
'_lib',
@@ -35,7 +44,7 @@ class Base(object):
3544
'_prepare_int32_array',
3645
'_prepare_string_array',
3746
]
38-
47+
3948
def __init__(self, api_util):
4049
self._lib = api_util.lib
4150
self._api_util = api_util
@@ -50,7 +59,7 @@ def __init__(self, api_util):
5059
self._prepare_float64_array = api_util.prepare_float64_array
5160
self._prepare_int32_array = api_util.prepare_int32_array
5261
self._prepare_string_array = api_util.prepare_string_array
53-
62+
5463
cls = type(self)
5564
if cls not in interface_classes:
5665
interface_classes.add(cls)
@@ -62,26 +71,45 @@ def CheckForError(self):
6271
error_num = self._lib.Error_Get_Number()
6372
if error_num:
6473
raise DssException(error_num, self._get_string(self._lib.Error_Get_Description()))
65-
74+
6675
def _getattr(self, key):
6776
if key.startswith('_'):
6877
return object.__getattribute__(self, key)
6978

7079
key = self.__class__._dss_attributes.get(key.lower(), key)
7180
return object.__getattribute__(self, key)
7281

82+
def _getattr_case_check(self, key):
83+
if key.startswith('_'):
84+
return object.__getattribute__(self, key)
85+
86+
correct_key = self.__class__._dss_attributes.get(key.lower(), key)
87+
if key != correct_key:
88+
warnings.warn('Wrong case for getter {}.{}: {}'.format(self.__class__.__name__, correct_key, key))
89+
90+
return object.__getattribute__(self, correct_key)
91+
7392
def _setattr(self, key, value):
7493
key = self.__class__._dss_attributes.get(key.lower(), key)
7594
object.__setattr__(self, key, value)
76-
77-
95+
96+
def _setattr_case_check(self, key, value):
97+
correct_key = self.__class__._dss_attributes.get(key.lower(), key)
98+
if key != correct_key:
99+
warnings.warn('Wrong case for setter {}.{}: {}'.format(self.__class__.__name__, correct_key, key))
100+
101+
key = self.__class__._dss_attributes.get(key.lower(), key)
102+
object.__setattr__(self, key, value)
103+
104+
105+
78106
class CffiApiUtil(object):
79107
def __init__(self, ffi, lib):
80108
self.codec = codec #TODO: check which encoding FreePascal defaults to, on Linux
81109
self.ffi = ffi
82110
self.lib = lib
83111
self.init_buffers()
84-
112+
85113
def init_buffers(self):
86114
tmp_string_pointers = (self.ffi.new('char****'), self.ffi.new('int32_t**'))
87115
tmp_float64_pointers = (self.ffi.new('double***'), self.ffi.new('int32_t**'))
@@ -90,7 +118,7 @@ def init_buffers(self):
90118

91119
# reorder pointers so data pointers are first, count pointers last
92120
ptr_args = [
93-
ptr
121+
ptr
94122
for ptrs in zip(tmp_string_pointers, tmp_float64_pointers, tmp_int32_pointers, tmp_int8_pointers)
95123
for ptr in ptrs
96124
]
@@ -121,15 +149,15 @@ def get_float64_array(self, func, *args):
121149
def get_float64_gr_array(self):
122150
ptr, cnt = self.gr_float64_pointers
123151
return np.fromstring(self.ffi.buffer(ptr[0], cnt[0] * 8), dtype=np.float)
124-
152+
125153
def get_int32_array(self, func, *args):
126154
ptr = self.ffi.new('int32_t**')
127155
cnt = self.ffi.new('int32_t[2]')
128156
func(ptr, cnt, *args)
129157
res = np.fromstring(self.ffi.buffer(ptr[0], cnt[0] * 4), dtype=np.int32)
130158
self.lib.DSS_Dispose_PInteger(ptr)
131159
return res
132-
160+
133161
def get_int32_gr_array(self):
134162
ptr, cnt = self.gr_int32_pointers
135163
return np.fromstring(self.ffi.buffer(ptr[0], cnt[0] * 4), dtype=np.int32)
@@ -145,7 +173,7 @@ def get_int8_array(self, func, *args):
145173
def get_int8_gr_array(self):
146174
ptr, cnt = self.gr_int8_pointers
147175
return np.fromstring(self.ffi.buffer(ptr[0], cnt[0] * 1), dtype=np.int8)
148-
176+
149177
def get_string_array(self, func, *args):
150178
ptr = self.ffi.new('char***')
151179
cnt = self.ffi.new('int32_t[2]')
@@ -164,7 +192,7 @@ def get_string_array(self, func, *args):
164192

165193
self.lib.DSS_Dispose_PPAnsiChar(ptr, cnt[1])
166194
return res
167-
195+
168196
def get_string_array2(self, func, *args): # for compatibility with OpenDSSDirect.py
169197
ptr = self.ffi.new('char***')
170198
cnt = self.ffi.new('int32_t[2]')
@@ -205,7 +233,7 @@ def get_float64_array2(self, func, *args):
205233
def get_float64_gr_array2(self):
206234
ptr, cnt = self.gr_float64_pointers
207235
return self.ffi.unpack(ptr[0], cnt[0])
208-
236+
209237
def get_int32_array2(self, func, *args):
210238
ptr = self.ffi.new('int32_t**')
211239
cnt = self.ffi.new('int32_t[2]')
@@ -221,7 +249,7 @@ def get_int32_array2(self, func, *args):
221249
def get_int32_gr_array2(self):
222250
ptr, cnt = self.gr_int32_pointers
223251
return self.ffi.unpack(ptr[0], cnt[0])
224-
252+
225253
def get_int8_array2(self, func, *args):
226254
ptr = self.ffi.new('int8_t**')
227255
cnt = self.ffi.new('int32_t[2]')
@@ -233,12 +261,12 @@ def get_int8_array2(self, func, *args):
233261

234262
self.lib.DSS_Dispose_PByte(ptr)
235263
return res
236-
264+
237265
def get_int8_gr_array2(self):
238266
ptr, cnt = self.gr_int8_pointers
239267
return self.ffi.unpack(ptr[0], cnt[0])
240268

241-
269+
242270
def prepare_float64_array(self, value):
243271
if type(value) is not np.ndarray or value.dtype != np.float64:
244272
value = np.array(value, dtype=np.float64)

tests/test_case_check.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from dss import DSS, use_com_compat
2+
3+
DSS.Text.Command = 'redirect ../../electricdss-tst/Version7/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss'
4+
5+
# Test if we get errors by default
6+
got_error = False
7+
try:
8+
for l in DSS.ActiveCircuit.Loads:
9+
name = l.nAme
10+
except AttributeError:
11+
got_error = True
12+
13+
assert got_error
14+
15+
16+
# Test mixed case
17+
use_com_compat(True, False)
18+
got_error = False
19+
try:
20+
for l in DSS.ActiveCircuit.Loads:
21+
name = l.namE
22+
except AttributeError:
23+
got_error = True
24+
25+
assert not got_error
26+
27+
28+
# Test warnings
29+
use_com_compat(True, True)
30+
got_error = False
31+
try:
32+
for l in DSS.ActiveCircuit.Loads:
33+
name = l.name
34+
except AttributeError:
35+
got_error = True
36+
37+
assert not got_error
38+
39+
got_error = False
40+
try:
41+
for b in DSS.ActiveCircuit.Buses:
42+
votages = b.PUVoltages
43+
44+
except AttributeError:
45+
got_error = True
46+
47+
assert not got_error
48+
49+
50+
# Make sure we can disable the case magic
51+
use_com_compat(False, True)
52+
got_error = False
53+
try:
54+
for l in DSS.ActiveCircuit.Loads:
55+
name = l.name
56+
except AttributeError:
57+
got_error = True
58+
59+
assert got_error
60+

0 commit comments

Comments
 (0)