def test_render_truncate_and_sanitize(self): calls = [] retval = ProcessResult(pd.DataFrame({'A': [1]})) retval.truncate_in_place_if_too_big = lambda: calls.append('truncate') retval.sanitize_in_place = lambda: calls.append('sanitize') lm = LoadedModule('int', '1', False, render_impl=lambda _a, _b: retval) with self.assertLogs(): lm.render(MockParams(), pd.DataFrame(), fetch_result=None) self.assertEqual(calls, ['truncate', 'sanitize'])
def test_render_truncate(self): calls = [] retval = ProcessResult(pd.DataFrame({"A": [1]})) retval.truncate_in_place_if_too_big = lambda: calls.append("truncate") lm = LoadedModule( "int", "1", ParamDType.Dict({}), render_impl=lambda _a, _b: retval ) with self.assertLogs(): lm.render(ProcessResult(), {}, tab_name="x", fetch_result=None) self.assertEqual(calls, ["truncate"])
def test_render_truncate(self): calls = [] retval = ProcessResult(pd.DataFrame({'A': [1]})) retval.truncate_in_place_if_too_big = lambda: calls.append('truncate') lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=lambda _a, _b: retval) with self.assertLogs(): lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) self.assertEqual(calls, ['truncate'])
def test_render_with_tab_name(self): passed_tab_name = None def render(table, params, *, tab_name): nonlocal passed_tab_name passed_tab_name = tab_name in_result = ProcessResult(pd.DataFrame({'A': [0]})) lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(): lm.render(in_result, {}, 'Tab X', None) self.assertEqual(passed_tab_name, 'Tab X')
def test_render_dynamic_default(self): lm = LoadedModule('int', '1', True) with self.assertLogs(): result = lm.render(MockParams(), pd.DataFrame({'A': [1]}), fetch_result=None) self.assertEqual(result, ProcessResult(pd.DataFrame({'A': [1]})))
def test_render_dynamic_default(self): lm = LoadedModule('int', '1', ParamDType.Dict({})) with self.assertLogs(): result = lm.render(ProcessResult(pd.DataFrame({'A': [1]})), {}, tab_name='x', fetch_result=None) self.assertEqual(result, ProcessResult(pd.DataFrame({'A': [1]})))
def test_render_with_input_columns(self): passed_columns = [] def render(table, params, *, input_columns, **kwargs): nonlocal passed_columns passed_columns = input_columns return pd.DataFrame({"A": [2]}) in_result = ProcessResult(pd.DataFrame({"A": [0]})) params = {"foo": "bar"} lm = LoadedModule("int", "1", ParamDType.Dict({}), render_impl=render) with self.assertLogs(): lm.render(in_result, params, tab_name="x", fetch_result=None) self.assertEqual(len(passed_columns), 1) self.assertEqual(passed_columns["A"].name, "A") self.assertEqual(passed_columns["A"].type, "number")
def test_render_with_input_columns(self): passed_columns = [] def render(table, params, *, input_columns, **kwargs): nonlocal passed_columns passed_columns = input_columns return pd.DataFrame({'A': [2]}) in_result = ProcessResult(pd.DataFrame({'A': [0]})) params = {'foo': 'bar'} lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(): lm.render(in_result, params, tab_name='x', fetch_result=None) self.assertEqual(len(passed_columns), 1) self.assertEqual(passed_columns['A'].name, 'A') self.assertEqual(passed_columns['A'].type, 'number')
def test_render_dynamic_default(self): lm = LoadedModule("int", "1", ParamDType.Dict({})) with self.assertLogs(): result = lm.render( ProcessResult(pd.DataFrame({"A": [1]})), {}, tab_name="x", fetch_result=None, ) self.assertEqual(result, ProcessResult(pd.DataFrame({"A": [1]})))
def test_render_use_input_columns_as_try_fallback_columns(self): def render(table, params): return pd.DataFrame({'A': [1]}) lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) column = Column('A', ColumnType.NUMBER('{:,d}')) with self.assertLogs(): result = lm.render(ProcessResult(pd.DataFrame({'A': [1]}), columns=[column]), {}, tab_name='x', fetch_result=None) self.assertEqual(result.columns, [column])
def test_render_format_columns(self): # More of an integration test.... def render(table, params): return { "dataframe": pd.DataFrame({"A": [1]}), "column_formats": {"A": "{:,d}"}, } lm = LoadedModule("int", "1", ParamDType.Dict({}), render_impl=render) with self.assertLogs(): result = lm.render(ProcessResult(), {}, tab_name="x", fetch_result=None) self.assertEqual(result.columns, [Column("A", ColumnType.NUMBER("{:,d}"))])
def test_render_cannot_coerce_output(self): """Log and display error to user when module output is invalid.""" def render(table, params, **kwargs): return {'foo': 'bar'} # not a valid retval lm = LoadedModule('int', '1', render_impl=render) with self.assertLogs(level=logging.ERROR): result = lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) _, lineno = inspect.getsourcelines(render) self.assertRegex(result.error, (r'ValueError: ProcessResult input must only contain ' r'\{dataframe, error, json, quick_fixes\} '))
def test_render_use_input_columns_as_try_fallback_columns(self): def render(table, params): return pd.DataFrame({"A": [1]}) lm = LoadedModule("int", "1", ParamDType.Dict({}), render_impl=render) column = Column("A", ColumnType.NUMBER("{:,d}")) with self.assertLogs(): result = lm.render( ProcessResult(pd.DataFrame({"A": [1]}), columns=[column]), {}, tab_name="x", fetch_result=None, ) self.assertEqual(result.columns, [column])
def test_render_invalid_return_dict_is_error(self): def render(table, params): return {'table': pd.DataFrame({'A': [1]})} # should be 'dataframe' lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(): result = lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) self.assertRegex( result.error, ('Something unexpected happened. We have been notified and are ' 'working to fix it. If this persists, contact us. Error code: ' 'ProcessResult input must only contain {dataframe, error, json, ' 'quick_fixes, column_formats}'))
def test_render_static_exception(self): class Ick(Exception): pass def render(params, table, **kwargs): raise Ick('Oops') lm = LoadedModule('int', '1', False, render_impl=render) with self.assertLogs(): result = lm.render(MockParams(), pd.DataFrame(), fetch_result=None) _, lineno = inspect.getsourcelines(render) self.assertEqual( result, ProcessResult(error=( f'Ick: Oops at line {lineno + 1} of test_LoadedModule.py')))
def test_render_cannot_coerce_output(self): """Log and display error to user when module output is invalid.""" def render(table, params, **kwargs): return {'foo': 'bar'} # not a valid retval lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(level=logging.ERROR): result = lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) _, lineno = inspect.getsourcelines(render) self.assertRegex( result.error, ('Something unexpected happened. We have been notified and are ' 'working to fix it. If this persists, contact us. Error code: ' 'ProcessResult input must only contain {dataframe, error, json, ' 'quick_fixes, column_formats}'))
def test_render_format_columns(self): # More of an integration test.... def render(table, params): return { 'dataframe': pd.DataFrame({'A': [1]}), 'column_formats': { 'A': '{:,d}' }, } lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(): result = lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) self.assertEqual(result.columns, [ Column('A', ColumnType.NUMBER('{:,d}')), ])
def test_render_exception(self): class Ick(Exception): pass def render(table, params, **kwargs): raise Ick('Oops') lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(level=logging.ERROR): result = lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) _, lineno = inspect.getsourcelines(render) self.assertEqual( result, ProcessResult(error=( f'Ick: Oops at line {lineno + 1} of test_LoadedModule.py')))
def test_render_dynamic_with_no_kwargs(self): args = None def render(table, params): nonlocal args args = (table, params) return ProcessResult(pd.DataFrame({'A': [1]})) in_table = pd.DataFrame({'A': [0]}) params = MockParams(foo='bar') expected = ProcessResult(pd.DataFrame({'A': [1]})) lm = LoadedModule('int', '1', True, render_impl=render) with self.assertLogs(): result = lm.render(params, in_table, fetch_result=None) self.assertIs(args[0], in_table) self.assertEqual(args[1], {'foo': 'bar'}) self.assertEqual(len(args), 2) self.assertEqual(result, expected)
def test_render_invalid_retval(self): def render(table, params): return pd.DataFrame({'A': [True, False]}) # we don't support bool lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(level=logging.ERROR) as cm: result = lm.render(ProcessResult(), {}, tab_name='x', fetch_result=None) # Should log an exception, which will email us self.assertRegex(cm.output[0], r'Exception coercing int\.render output') self.assertRegex(cm.output[0], r"unsupported dtype dtype\('bool'\)") # Should inform the user, who can follow up with the dev self.assertEqual( result, ProcessResult(error=( 'Something unexpected happened. We have been notified and are ' 'working to fix it. If this persists, contact us. Error code: ' "unsupported dtype dtype('bool') in column 'A'")))
def test_render_static_with_fetch_result(self): args = None def render(params, table, *, fetch_result, **kwargs): nonlocal args args = (params, table, fetch_result, kwargs) return ProcessResult(pd.DataFrame({'A': [2]})) in_table = pd.DataFrame({'A': [0]}) params = MockParams(foo='bar') fetch_result = ProcessResult(pd.DataFrame({'A': [1]})) expected = ProcessResult(pd.DataFrame({'A': [2]})) lm = LoadedModule('int', '1', False, render_impl=render) with self.assertLogs(): result = lm.render(params, in_table, fetch_result=fetch_result) self.assertIs(args[0], params) self.assertIs(args[1], in_table) self.assertIs(args[2], fetch_result) self.assertEqual(args[3], {}) self.assertEqual(result, expected)
def test_render_with_no_kwargs(self): args = None def render(table, params): nonlocal args args = (table, params) return pd.DataFrame({"A": [1]}) in_table = pd.DataFrame({"A": [0]}) params = {"foo": "bar"} expected = ProcessResult(pd.DataFrame({"A": [1]})) lm = LoadedModule("int", "1", ParamDType.Dict({}), render_impl=render) with self.assertLogs(): result = lm.render( ProcessResult(in_table), params, tab_name="x", fetch_result=None ) self.assertIs(args[0], in_table) self.assertIs(args[1], params) self.assertEqual(len(args), 2) self.assertEqual(result, expected)
def test_render_dynamic_exception(self): class Ick(Exception): pass def render(table, params, **kwargs): # Pre # am # bl # e # # The user never wrote the top 6 lines of code raise Ick('Oops') lm = LoadedModule('int', '1', True, render_impl=render) with self.assertLogs(): result = lm.render(MockParams(), pd.DataFrame(), fetch_result=None) _, lineno = inspect.getsourcelines(render) self.assertEqual( result, ProcessResult(error=( f'Ick: Oops at line {lineno + 1} of test_LoadedModule.py')))
def test_render_with_fetch_result(self): args = None def render(table, params, *, fetch_result): nonlocal args args = (table, params, fetch_result) return pd.DataFrame({'A': [2]}) in_table = pd.DataFrame({'A': [0]}) params = {'foo': 'bar'} fetch_result = ProcessResult(pd.DataFrame({'A': [1]})) expected = ProcessResult(pd.DataFrame({'A': [2]})) lm = LoadedModule('int', '1', ParamDType.Dict({}), render_impl=render) with self.assertLogs(): result = lm.render(ProcessResult(in_table), params, tab_name='x', fetch_result=fetch_result) self.assertIs(args[0], in_table) self.assertIs(args[1], params) self.assertIs(args[2], fetch_result) self.assertEqual(result, expected)