From 9a0bd42d6cd991bcbcadee879fa65dee8f0cff2c Mon Sep 17 00:00:00 2001 From: Talm28 <100356396+Talm28@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:51:03 +0300 Subject: [PATCH 1/3] Add IFERROR function --- xlcalculator/xlfunctions/logical.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xlcalculator/xlfunctions/logical.py b/xlcalculator/xlfunctions/logical.py index 10a9f95..609ed5a 100644 --- a/xlcalculator/xlfunctions/logical.py +++ b/xlcalculator/xlfunctions/logical.py @@ -101,3 +101,15 @@ def TRUE() -> func_xltypes.XlBoolean: true-function-7652c6e3-8987-48d0-97cd-ef223246b3fb """ return True + +@xl.register() +@xl.validate_args +def IFERROR(value: func_xltypes.XlExpr, + value_if_error: func_xltypes.XlAnything) -> func_xltypes.XlAnything: + """Evaluate value, return value if has no error, otherwise return value_if_error. + + https://support.microsoft.com/en-au/office/iferror-function-c526fd07-caeb-47b8-8bb6-63f3e417f611 + """ + if isinstance(value(), xlerrors.ExcelError): + return value_if_error + return value() From ee18e34dcf1212cf2e3fb31b77792baddf62f536 Mon Sep 17 00:00:00 2001 From: Talm28 <100356396+Talm28@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:52:03 +0300 Subject: [PATCH 2/3] Add IFERROR tests --- tests/resources/IFERROR.xlsx | Bin 0 -> 6384 bytes tests/xlfunctions/test_logical.py | 7 ++++ tests/xlfunctions_vs_excel/iferror_test.py | 37 +++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 tests/resources/IFERROR.xlsx create mode 100644 tests/xlfunctions_vs_excel/iferror_test.py diff --git a/tests/resources/IFERROR.xlsx b/tests/resources/IFERROR.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..10eb19f72133727333d68a2880a63f78b41c1a5b GIT binary patch literal 6384 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 GZ~q61W>{PR literal 0 HcmV?d00001 diff --git a/tests/xlfunctions/test_logical.py b/tests/xlfunctions/test_logical.py index 085f55c..fb64327 100644 --- a/tests/xlfunctions/test_logical.py +++ b/tests/xlfunctions/test_logical.py @@ -52,3 +52,10 @@ def test_NOT_with_direct_values(self): def test_TRUE(self): self.assertTrue(logical.TRUE()) + + def test_IFERROR(self): + self.assertEqual(logical.IFERROR(10/2,0),5) # Check if value is OK + self.assertEqual(logical.IFERROR(10/0,0),0) # Check if value cause error + self.assertEqual(logical.IFERROR(10/0,"ERROR"),"ERROR") # Check returning string + self.assertEqual(logical.IFERROR(logical.IFERROR(10/0,0) + 1,0),1) # Check nested IFERROR + diff --git a/tests/xlfunctions_vs_excel/iferror_test.py b/tests/xlfunctions_vs_excel/iferror_test.py new file mode 100644 index 0000000..9713a8b --- /dev/null +++ b/tests/xlfunctions_vs_excel/iferror_test.py @@ -0,0 +1,37 @@ +from .. import testing + +from xlcalculator.xlfunctions import xlerrors + + +class SqrtTest(testing.FunctionalTestCase): + filename = "IFERROR.xlsx" + + def test_evaluation_C1(self): + self.assertEqual( + self.evaluator.get_cell_value('Sheet1!C1!'), + self.evaluator.evaluate('Sheet1!C1') + ) + + def test_evaluation_C2(self): + self.assertEqual( + self.evaluator.get_cell_value('Sheet1!C2!'), + self.evaluator.evaluate('Sheet1!C2') + ) + + def test_evaluation_C3(self): + self.assertEqual( + self.evaluator.get_cell_value('Sheet1!C3!'), + self.evaluator.evaluate('Sheet1!C3') + ) + + def test_evaluation_C4(self): + self.assertEqual( + self.evaluator.get_cell_value('Sheet1!C4!'), + self.evaluator.evaluate('Sheet1!C4') + ) + + def test_evaluation_C5(self): + self.assertEqual( + self.evaluator.get_cell_value('Sheet1!C5!'), + self.evaluator.evaluate('Sheet1!C5') + ) From f75bb8d80f3dafd933732192c40b3978d84cbfd2 Mon Sep 17 00:00:00 2001 From: Talm28 <100356396+Talm28@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:57:10 +0300 Subject: [PATCH 3/3] Update README for IFERROR function --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 836058f..d2a771a 100644 --- a/README.rst +++ b/README.rst @@ -361,7 +361,7 @@ Logical +-----------------+--------------+-------+----------+-------+ | IF | * | * | * | | +-----------------+--------------+-------+----------+-------+ - | IFERROR | | * | * | * | + | IFERROR | * | * | * | * | +-----------------+--------------+-------+----------+-------+ | IFS | | * | | | +-----------------+--------------+-------+----------+-------+