Repository: sanders41/sas7bdat-converter

Issue:
Continue Processing on Exception
It would be good to add an option to continue processing files if a file conversion raises an exception. There could be a continue_on_exception parameter, defaulted to False, that when set to true would log the file name that raised an exception and continue with the other files.


Discussion:

Resolving patch:
diff --git a/sas7bdat_converter/converter.py b/sas7bdat_converter/converter.py
--- a/sas7bdat_converter/converter.py
+++ b/sas7bdat_converter/converter.py
@@ -1,4 +1,5 @@
 import csv
+import logging
 from pathlib import Path
 from typing import Dict, List, Optional, Tuple, Union
 from xml.sax.saxutils import escape
@@ -6,13 +7,19 @@
 import numpy as np
 import pandas as pd
 
+logging.basicConfig(format="%(asctime)s: %(levelname)s: %(message)s")
+logging.root.setLevel(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
 __file_dict_required_keys = [
     "sas7bdat_file",
     "export_file",
 ]
 
 
-def batch_to_csv(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
+def batch_to_csv(
+    file_dicts: List[Dict[str, Union[str, Path]]], continue_on_error: bool = False,
+) -> None:
     """
     Converts a batch of sas7bdat files to csv files.
 
@@ -22,16 +29,26 @@ def batch_to_csv(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
                     and 'export_file' containing the path and name of the export csv). 
                     Example: file_dict = [{'sas7bdat_file': 'sas_file1.sas7bdat', 'export_file': 'converted_file1.csv'},
                                           {'sas7bdat_file': 'sas_file2.sas7bdat', 'export_file': 'converted_file2.csv'}]
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                    a file conversion error instead of raising an exception. Default = False
     """
     for file_dict in file_dicts:
         _rise_on_invalid_file_dict(file_dict)
 
         sas7bdat = _format_path(file_dict["sas7bdat_file"])
         export = _format_path(file_dict["export_file"])
-        to_csv(sas7bdat_file=sas7bdat, export_file=export)
+        try:
+            to_csv(sas7bdat_file=sas7bdat, export_file=export)
+        except:
+            if continue_on_error:
+                logger.info(f"Error converting {sas7bdat}")
+            else:
+                raise
 
 
-def batch_to_excel(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
+def batch_to_excel(
+    file_dicts: List[Dict[str, Union[str, Path]]], continue_on_error: bool = False,
+) -> None:
     """
     Converts a batch of sas7bdat files to xlsx files.
 
@@ -41,16 +58,26 @@ def batch_to_excel(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
                     and 'export_file' containing the path and name of the export xlsx). 
                     Example: file_dict = [{'sas7bdat_file': 'sas_file1.sas7bdat', 'export_file': 'converted_file1.xlsx'},
                                           {'sas7bdat_file': 'sas_file2.sas7bdat', 'export_file': 'converted_file2.xlxs'}]
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                    a file conversion error instead of raising an exception. Default = False
     """
     for file_dict in file_dicts:
         _rise_on_invalid_file_dict(file_dict)
 
         sas7bdat = _format_path(file_dict["sas7bdat_file"])
         export = _format_path(file_dict["export_file"])
-        to_excel(sas7bdat_file=sas7bdat, export_file=export)
+        try:
+            to_excel(sas7bdat_file=sas7bdat, export_file=export)
+        except:
+            if continue_on_error:
+                logger.info(f"Error converting {sas7bdat}")
+            else:
+                raise
 
 
-def batch_to_json(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
+def batch_to_json(
+    file_dicts: List[Dict[str, Union[str, Path]]], continue_on_error: bool = False,
+) -> None:
     """
     Converts a batch of sas7bdat files to json files.
 
@@ -60,16 +87,26 @@ def batch_to_json(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
                     and 'export_file' containing the path and name of the export json). 
                     Example: file_dict = [{'sas7bdat_file': 'sas_file1.sas7bdat', 'export_file': 'converted_file1.json'},
                                           {'sas7bdat_file': 'sas_file2.sas7bdat', 'export_file': 'converted_file2.json'}]
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                    a file conversion error instead of raising an exception. Default = False
     """
     for file_dict in file_dicts:
         _rise_on_invalid_file_dict(file_dict)
 
         sas7bdat = _format_path(file_dict["sas7bdat_file"])
         export = _format_path(file_dict["export_file"])
-        to_json(sas7bdat_file=sas7bdat, export_file=export)
+        try:
+            to_json(sas7bdat_file=sas7bdat, export_file=export)
+        except:
+            if continue_on_error:
+                logger.info(f"Error converting {sas7bdat}")
+            else:
+                raise
 
 
-def batch_to_xml(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
+def batch_to_xml(
+    file_dicts: List[Dict[str, Union[str, Path]]], continue_on_error: bool = False,
+) -> None:
     """
     Converts a batch of sas7bdat files to xml files.
 
@@ -93,6 +130,8 @@ def batch_to_xml(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
                                             'export_file': 'converted_file2.xml',
                                             'root_node': 'another_root',
                                             'first_node': 'another_first'}]
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                    a file conversion error instead of raising an exception. Default = False
     """
     optional_keys = [
         "root_node",
@@ -128,22 +167,32 @@ def batch_to_xml(file_dicts: List[Dict[str, Union[str, Path]]]) -> None:
         if "first_node" in file_dict:
             first_node = file_dict["first_node"]
 
-        if root_node and first_node:
-            to_xml(
-                sas7bdat_file=sas7bdat,
-                export_file=export,
-                root_node=str(root_node),
-                first_node=str(first_node),
-            )
-        elif root_node:
-            to_xml(sas7bdat_file=sas7bdat, export_file=export, root_node=str(root_node))
-        elif first_node:
-            to_xml(sas7bdat_file=sas7bdat, export_file=export, first_node=str(first_node))
-        else:
-            to_xml(sas7bdat_file=sas7bdat, export_file=export)
+        try:
+            if root_node and first_node:
+                to_xml(
+                    sas7bdat_file=sas7bdat,
+                    export_file=export,
+                    root_node=str(root_node),
+                    first_node=str(first_node),
+                )
+            elif root_node:
+                to_xml(sas7bdat_file=sas7bdat, export_file=export, root_node=str(root_node))
+            elif first_node:
+                to_xml(sas7bdat_file=sas7bdat, export_file=export, first_node=str(first_node))
+            else:
+                to_xml(sas7bdat_file=sas7bdat, export_file=export)
+        except:
+            if continue_on_error:
+                logger.info(f"Error converting {sas7bdat}")
+            else:
+                raise
 
 
-def dir_to_csv(dir_path: Union[str, Path], export_path: Optional[Union[str, Path]] = None) -> None:
+def dir_to_csv(
+    dir_path: Union[str, Path],
+    export_path: Optional[Union[str, Path]] = None,
+    continue_on_error: bool = False,
+) -> None:
     """
     Converts all sas7bdat files in a directory into csv files.
 
@@ -152,13 +201,17 @@ def dir_to_csv(dir_path: Union[str, Path], export_path: Optional[Union[str, Path
                 for conversion.
         export_path (optional): If used this can specify a new directory to create
                 the converted files into. If not supplied then the files will be
-                created into the same directory as dir_path.
+                created into the same directory as dir_path. Default = None
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                a file conversion error instead of raising an exception. Default = False
     """
-    _walk_dir("csv", dir_path, export_path)
+    _walk_dir("csv", dir_path, continue_on_error, export_path)
 
 
 def dir_to_excel(
-    dir_path: Union[str, Path], export_path: Optional[Union[str, Path]] = None
+    dir_path: Union[str, Path],
+    export_path: Optional[Union[str, Path]] = None,
+    continue_on_error: bool = False,
 ) -> None:
     """
     Converts all sas7bdat files in a directory into xlsx files.
@@ -168,12 +221,18 @@ def dir_to_excel(
                 for conversion.
         export_path (optional): If used this can specify a new directory to create
                 the converted files into. If not supplied then the files will be
-                created into the same directory as dir_path.
+                created into the same directory as dir_path. Default = None
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                a file conversion error instead of raising an exception. Default = False
     """
-    _walk_dir("xlsx", dir_path, export_path)
+    _walk_dir("xlsx", dir_path, continue_on_error, export_path)
 
 
-def dir_to_json(dir_path: Union[str, Path], export_path: Optional[Union[str, Path]] = None) -> None:
+def dir_to_json(
+    dir_path: Union[str, Path],
+    export_path: Optional[Union[str, Path]] = None,
+    continue_on_error: bool = False,
+) -> None:
     """
     Converts all sas7bdat files in a directory into json files.
 
@@ -182,12 +241,18 @@ def dir_to_json(dir_path: Union[str, Path], export_path: Optional[Union[str, Pat
                 for conversion.
         export_path (optional): If used this can specify a new directory to create
                 the converted files into. If not supplied then the files will be
-                created into the same directory as dir_path.
+                created into the same directory as dir_path. Default = None
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                a file conversion error instead of raising an exception. Default = False
     """
-    _walk_dir("json", dir_path, export_path)
+    _walk_dir("json", dir_path, continue_on_error, export_path)
 
 
-def dir_to_xml(dir_path: Union[str, Path], export_path: Optional[Union[str, Path]] = None) -> None:
+def dir_to_xml(
+    dir_path: Union[str, Path],
+    export_path: Optional[Union[str, Path]] = None,
+    continue_on_error: bool = False,
+) -> None:
     """
     Converts all sas7bdat files in a directory into xml files.
 
@@ -196,9 +261,11 @@ def dir_to_xml(dir_path: Union[str, Path], export_path: Optional[Union[str, Path
                 for conversion.
         export_path (optional): If used this can specify a new directory to create
                 the converted files into. If not supplied then the files will be
-                created into the same directory as dir_path.
+                created into the same directory as dir_path. Default = None
+        continue_on_error: If set to true processing of files in a batch will continue if there is 
+                a file conversion error instead of raising an exception. Default = False
     """
-    _walk_dir("xml", dir_path, export_path)
+    _walk_dir("xml", dir_path, continue_on_error, export_path)
 
 
 def to_csv(sas7bdat_file: Union[str, Path], export_file: Union[str, Path]) -> None:
@@ -364,7 +431,10 @@ def _rise_on_invalid_file_dict(file_dict: Dict[str, Union[str, Path]]) -> None:
 
 
 def _walk_dir(
-    file_type: str, dir_path: Union[str, Path], export_path: Optional[Union[str, Path]] = None,
+    file_type: str,
+    dir_path: Union[str, Path],
+    continue_on_error: bool,
+    export_path: Optional[Union[str, Path]] = None,
 ) -> None:
     path = dir_path if isinstance(dir_path, Path) else Path(dir_path)
     for file_name in path.iterdir():
@@ -377,11 +447,17 @@ def _walk_dir(
 
             sas7bdat_file = path.joinpath(file_name)
 
-            if file_type == "csv":
-                to_csv(str(sas7bdat_file), str(export_file))
-            elif file_type == "xlsx":
-                to_excel(str(sas7bdat_file), str(export_file))
-            elif file_type == "json":
-                to_json(str(sas7bdat_file), str(export_file))
-            elif file_type == "xml":
-                to_xml(str(sas7bdat_file), str(export_file))
+            try:
+                if file_type == "csv":
+                    to_csv(str(sas7bdat_file), str(export_file))
+                elif file_type == "xlsx":
+                    to_excel(str(sas7bdat_file), str(export_file))
+                elif file_type == "json":
+                    to_json(str(sas7bdat_file), str(export_file))
+                elif file_type == "xml":
+                    to_xml(str(sas7bdat_file), str(export_file))
+            except:
+                if continue_on_error:
+                    logger.info(f"Error converting {sas7bdat_file}")
+                else:
+                    raise
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
 
 setup(
     name="sas7bdat_converter",
-    version="0.4.0",
+    version="0.5.0",
     author="Paul Sanders",
     author_email="psanders1@gmail.com",
     license="Apache 2.0",
@@ -15,7 +15,7 @@
     long_description=README,
     long_description_content_type="text/markdown",
     url="https://github.com/sanders41/sas7bdat_converter",
-    download_url="https://github.com/sanders41/sas7bdat_converter/archive/v0.4.0.tar.gz",
+    download_url="https://github.com/sanders41/sas7bdat_converter/archive/v0.5.0.tar.gz",
     packages=["sas7bdat_converter"],
     install_requires=["pandas>=0.25.3", "XlsxWriter>=1.2.6",],
     classifiers=[


Test patch:
diff --git a/tests/assets/bad_sas_files/bad_sas_file.sas7bdat b/tests/assets/bad_sas_files/bad_sas_file.sas7bdat
new file mode 100644
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -28,3 +28,8 @@ def sas_file_3():
 @pytest.fixture(scope="session")
 def sas7bdat_dir():
     return SAS7BDAT_DIR
+
+
+@pytest.fixture(scope="session")
+def bad_sas_file():
+    return Path().absolute().joinpath("tests/assets/bad_sas_files/bad_sas_file.sas7bdat")
diff --git a/tests/test_sas7bdat_converter.py b/tests/test_sas7bdat_converter.py
--- a/tests/test_sas7bdat_converter.py
+++ b/tests/test_sas7bdat_converter.py
@@ -7,6 +7,7 @@
 import pytest
 
 import sas7bdat_converter.converter as converter
+from tests.conftest import bad_sas_file
 
 current_dir = Path().absolute()
 
@@ -53,6 +54,38 @@ def test_batch_to_csv_str(tmp_path, sas_file_1, sas_file_2, sas_file_3):
     assert files_created
 
 
+def test_batch_to_csv_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.csv")
+    converted_file = tmp_path.joinpath("file1.csv")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    converter.batch_to_csv(file_dict, continue_on_error=True)
+
+    assert converted_file.is_file()
+    assert "Error converting" in caplog.text
+
+
+def test_batch_to_csv_no_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.csv")
+    converted_file = tmp_path.joinpath("file1.csv")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    with pytest.raises(Exception) as execinfo:
+        converter.batch_to_csv(file_dict, continue_on_error=False)
+
+    assert execinfo.value
+
+
 file_dicts = [
     [{"bad_key": "test.sas7bdat", "export_file": "test.csv"}],
     [{"sas7bdat_file": "test.sas7bdat", "bad_key": "test.csv"}],
@@ -108,6 +141,38 @@ def test_batch_to_excel_str(tmp_path, sas_file_1, sas_file_2, sas_file_3):
     assert files_created
 
 
+def test_batch_to_excel_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.xlsx")
+    converted_file = tmp_path.joinpath("file1.xlsx")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    converter.batch_to_excel(file_dict, continue_on_error=True)
+
+    assert converted_file.is_file()
+    assert "Error converting" in caplog.text
+
+
+def test_batch_to_excel_no_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.xlsx")
+    converted_file = tmp_path.joinpath("file1.xlsx")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    with pytest.raises(FileNotFoundError) as execinfo:
+        converter.batch_to_excel(file_dict, continue_on_error=False)
+
+    assert execinfo.value
+
+
 file_dicts = [
     [{"bad_key": "test.sas7bdat", "export_file": "test.xlsx"}],
     [{"sas7bdat_file": "test.sas7bdat", "bad_key": "test.xlsx"}],
@@ -163,6 +228,38 @@ def test_batch_to_json_path_str(tmp_path, sas_file_1, sas_file_2, sas_file_3):
     assert files_created
 
 
+def test_batch_to_json_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.json")
+    converted_file = tmp_path.joinpath("file1.json")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    converter.batch_to_json(file_dict, continue_on_error=True)
+
+    assert converted_file.is_file()
+    assert "Error converting" in caplog.text
+
+
+def test_batch_to_json_no_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.json")
+    converted_file = tmp_path.joinpath("file1.json")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    with pytest.raises(Exception) as execinfo:
+        converter.batch_to_json(file_dict, continue_on_error=False)
+
+    assert execinfo.value
+
+
 file_dicts = [
     [{"bad_key": "test.sas7bdat", "export_file": "test.json"}],
     [{"sas7bdat_file": "test.sas7bdat", "bad_key": "test.json"}],
@@ -344,6 +441,38 @@ def test_batch_to_xml_str(tmp_path, sas_file_1, sas_file_2, sas_file_3, optional
     assert files_created
 
 
+def test_batch_to_xml_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.xml")
+    converted_file = tmp_path.joinpath("file1.xml")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    converter.batch_to_xml(file_dict, continue_on_error=True)
+
+    assert converted_file.is_file()
+    assert "Error converting" in caplog.text
+
+
+def test_batch_to_xml_no_continue(tmp_path, caplog, sas_file_1):
+    bad_sas_file = tmp_path.joinpath("bad_file.sas7bdat")
+    bad_converted_file = tmp_path.joinpath("bad_file.xml")
+    converted_file = tmp_path.joinpath("file1.xml")
+
+    file_dict = [
+        {"sas7bdat_file": bad_sas_file, "export_file": bad_converted_file},
+        {"sas7bdat_file": sas_file_1, "export_file": converted_file},
+    ]
+
+    with pytest.raises(Exception) as execinfo:
+        converter.batch_to_xml(file_dict, continue_on_error=False)
+
+    assert execinfo.value
+
+
 file_dicts = [
     [{"bad_key": "test.sas7bdat", "export_file": "test.xml"}],
     [{"sas7bdat_file": "test.sas7bdat", "bad_key": "test.xml"}],
@@ -399,6 +528,33 @@ def test_dir_to_csv_same_dir_str(tmpdir, sas7bdat_dir):
     assert sas_counter == convert_counter
 
 
+def test_dir_to_csv_continue(tmp_path, caplog, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+
+    converter.dir_to_csv(tmp_path, continue_on_error=True)
+    sas_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".sas7bdat"]) - 1
+    convert_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".csv"])
+
+    assert sas_counter == convert_counter
+    assert "Error converting" in caplog.text
+
+
+def test_dir_to_csv_no_continue(tmp_path, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+    with pytest.raises(Exception) as execinfo:
+        converter.dir_to_csv(tmp_path, continue_on_error=False)
+
+    assert execinfo.value
+
+
 def test_dir_to_csv_different_dir_path(tmp_path, sas7bdat_dir):
     converter.dir_to_csv(dir_path=sas7bdat_dir, export_path=tmp_path)
     sas_counter = len([name for name in sas7bdat_dir.iterdir() if name.suffix == ".sas7bdat"])
@@ -455,6 +611,33 @@ def test_dir_to_excel_different_dir_str(tmpdir, sas7bdat_dir):
     assert sas_counter == convert_counter
 
 
+def test_dir_to_excel_continue(tmp_path, caplog, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+
+    converter.dir_to_excel(tmp_path, continue_on_error=True)
+    sas_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".sas7bdat"]) - 1
+    convert_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".xlsx"])
+
+    assert sas_counter == convert_counter
+    assert "Error converting" in caplog.text
+
+
+def test_dir_to_excel_no_continue(tmp_path, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+    with pytest.raises(Exception) as execinfo:
+        converter.dir_to_excel(tmp_path, continue_on_error=False)
+
+    assert execinfo.value
+
+
 def test_dir_to_json_same_dir_path(tmp_path, sas7bdat_dir):
     sas_files = [x for x in sas7bdat_dir.iterdir()]
     for sas_file in sas_files:
@@ -495,6 +678,33 @@ def test_dir_to_json_different_dir_str(tmpdir, sas7bdat_dir):
     assert sas_counter == convert_counter
 
 
+def test_dir_to_json_continue(tmp_path, caplog, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+
+    converter.dir_to_json(tmp_path, continue_on_error=True)
+    sas_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".sas7bdat"]) - 1
+    convert_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".json"])
+
+    assert sas_counter == convert_counter
+    assert "Error converting" in caplog.text
+
+
+def test_dir_to_json_no_continue(tmp_path, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+    with pytest.raises(Exception) as execinfo:
+        converter.dir_to_json(tmp_path, continue_on_error=False)
+
+    assert execinfo.value
+
+
 def test_dir_to_xml_same_dir_path(tmp_path, sas7bdat_dir):
     sas_files = [x for x in sas7bdat_dir.iterdir()]
     for sas_file in sas_files:
@@ -535,6 +745,33 @@ def test_dir_to_xml_different_dir_str(tmpdir, sas7bdat_dir):
     assert sas_counter == convert_counter
 
 
+def test_dir_to_xml_continue(tmp_path, caplog, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+
+    converter.dir_to_xml(tmp_path, continue_on_error=True)
+    sas_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".sas7bdat"]) - 1
+    convert_counter = len([name for name in tmp_path.iterdir() if name.suffix == ".xml"])
+
+    assert sas_counter == convert_counter
+    assert "Error converting" in caplog.text
+
+
+def test_dir_to_xml_no_continue(tmp_path, sas7bdat_dir, bad_sas_file):
+    sas_files = [str(x) for x in sas7bdat_dir.iterdir()]
+    for sas_file in sas_files:
+        shutil.copy(sas_file, str(tmp_path))
+
+    shutil.copy(bad_sas_file, str(tmp_path))
+    with pytest.raises(Exception) as execinfo:
+        converter.dir_to_xml(tmp_path, continue_on_error=False)
+
+    assert execinfo.value
+
+
 exception_data = [
     (
         "sas7bdat conversion error - Valid extension for to_csv conversion is: .csv",
@@ -766,7 +1003,7 @@ def test_walk_dir(tmpdir, sas7bdat_dir, file_type):
     for sas_file in sas_files:
         shutil.copy(sas_file, tmpdir)
 
-    converter._walk_dir(file_type, tmpdir)
+    converter._walk_dir(file_type, tmpdir, False)
     sas_counter = len([name for name in Path(tmpdir).iterdir() if name.suffix == ".sas7bdat"])
     convert_counter = len(
         [name for name in Path(tmpdir).iterdir() if name.suffix == f".{file_type}"]