Skip to content

Commit 5b60248

Browse files
authored
Merge pull request #1633 from kernc/fixup
Fixups
2 parents 8975c02 + 0cca105 commit 5b60248

File tree

6 files changed

+48
-16
lines changed

6 files changed

+48
-16
lines changed

Orange/canvas/application/errorreporting.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,17 @@ def handle_exception(cls, exc):
157157
stacktrace = ''.join(traceback.format_exception(etype, evalue, tb))
158158

159159
def _find_last_frame(tb):
160+
if not tb:
161+
return None
160162
while tb.tb_next:
161163
tb = tb.tb_next
162164
return tb
163165

164-
frame = _find_last_frame(tb)
165-
err_module = '{}:{}'.format(
166-
frame.tb_frame.f_globals.get('__name__', frame.tb_frame.f_code.co_filename),
167-
frame.tb_lineno)
166+
err_module, frame = None, _find_last_frame(tb)
167+
if frame:
168+
err_module = '{}:{}'.format(
169+
frame.tb_frame.f_globals.get('__name__', frame.tb_frame.f_code.co_filename),
170+
frame.tb_lineno)
168171

169172
def _find_widget_frame(tb):
170173
while tb:

Orange/data/variable.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,13 +919,21 @@ def _tzre_sub(s, _subtz=re.compile(r'([+-])(\d\d):(\d\d)$').sub):
919919
def repr_val(self, val):
920920
if isnan(val):
921921
return '?'
922+
if not self.have_date and not self.have_time:
923+
# The time is relative, unitless. The value is absolute.
924+
return str(val)
925+
926+
# If you know how to simplify this, be my guest
922927
seconds = int(val)
923928
microseconds = int(round((val - seconds) * 1e6))
924929
if val < 0:
930+
if microseconds:
931+
seconds, microseconds = seconds - 1, int(1e6) + microseconds
925932
date = datetime.fromtimestamp(0, tz=self.timezone) + timedelta(seconds=seconds)
926933
else:
927934
date = datetime.fromtimestamp(seconds, tz=self.timezone)
928935
date = str(date.replace(microsecond=microseconds))
936+
929937
if self.have_date and not self.have_time:
930938
date = date.split()[0]
931939
elif not self.have_date and self.have_time:
@@ -956,7 +964,9 @@ def parse(self, datestr):
956964
if not self._matches_iso_format(datestr):
957965
try:
958966
# If it is a number, assume it is a unix timestamp
959-
return float(datestr)
967+
value = float(datestr)
968+
self.have_date = self.have_time = 1
969+
return value
960970
except ValueError:
961971
raise ERROR
962972

Orange/tests/test_util.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import numpy as np
44

5-
from Orange.util import export_globals, flatten, deprecated, try_
5+
from Orange.util import export_globals, flatten, deprecated, try_, deepgetattr
66

77

88
SOMETHING = 0xf00babe
@@ -30,3 +30,10 @@ def test_try_(self):
3030
self.assertTrue(try_(lambda: np.ones(3).any()))
3131
self.assertFalse(try_(lambda: np.whatever()))
3232
self.assertEqual(try_(len, default=SOMETHING), SOMETHING)
33+
34+
def test_deepgetattr(self):
35+
class a:
36+
l = []
37+
self.assertTrue(deepgetattr(a, 'l.__len__.__call__'), a.l.__len__.__call__)
38+
self.assertTrue(deepgetattr(a, 'l.__nx__.__x__', 42), 42)
39+
self.assertRaises(AttributeError, lambda: deepgetattr(a, 'l.__nx__.__x__'))

Orange/tests/test_variable.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,10 +313,10 @@ class TestTimeVariable(VariableTest):
313313
('01:01', 3660, '01:01:00'),
314314
('1970-01-01 00:00:00', 0, '1970-01-01 00:00:00'),
315315
('1969-12-31 23:59:59', -1, '1969-12-31 23:59:59'),
316+
('1969-12-31 23:59:58.9', -1.1, '1969-12-31 23:59:58.900000'),
316317
('1900-01-01', -2208988800, '1900-01-01'),
317318
('nan', np.nan, '?'),
318319
('1444651991.81', 1444651991.81, '2015-10-12 12:13:11.810000'),
319-
(1444651991.81, 1444651991.81, '2015-10-12 12:13:11.810000'),
320320
]
321321

322322
def test_parse_repr(self):
@@ -356,6 +356,9 @@ def test_have_date(self):
356356
# observe have datetime
357357
self.assertEqual(var.repr_val(ts), '1970-01-01 16:20:00')
358358

359+
def test_no_date_no_time(self):
360+
self.assertEqual(TimeVariable('relative time').repr_val(1.6), '1.6')
361+
359362
def test_readwrite_timevariable(self):
360363
output_csv = StringIO()
361364
input_csv = StringIO("""\

Orange/util.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from functools import wraps
44
from inspect import isfunction
5+
from operator import attrgetter
56
from itertools import chain, count
67
from collections import OrderedDict
78
import warnings
@@ -129,6 +130,21 @@ def export_globals(globals, module_name):
129130
not getattr(v, '__name__', k).startswith('_'))] # neither marked internal
130131

131132

133+
_NOTSET = object()
134+
135+
136+
def deepgetattr(obj, attr, default=_NOTSET):
137+
"""Works exactly like getattr(), except that attr can be a nested attribute
138+
(e.g. "attr1.attr2.attr3").
139+
"""
140+
try:
141+
return attrgetter(attr)(obj)
142+
except AttributeError:
143+
if default is _NOTSET:
144+
raise
145+
return default
146+
147+
132148
def color_to_hex(color):
133149
return "#{:02X}{:02X}{:02X}".format(*color)
134150

Orange/widgets/utils/__init__.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from functools import reduce
21
from Orange.data.variable import TimeVariable
2+
from Orange.util import deepgetattr
33

44

55
def vartype(var):
@@ -22,14 +22,7 @@ def progress_bar_milestones(count, iterations=100):
2222
def getdeepattr(obj, attr, *arg, **kwarg):
2323
if isinstance(obj, dict):
2424
return obj.get(attr)
25-
try:
26-
return reduce(getattr, attr.split("."), obj)
27-
except AttributeError:
28-
if arg:
29-
return arg[0]
30-
if kwarg:
31-
return kwarg["default"]
32-
raise
25+
return deepgetattr(obj, attr, *arg, **kwarg)
3326

3427

3528
def to_html(str):

0 commit comments

Comments
 (0)