prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Need to raise custom exception
  https://github.com/networktocode/ntc-templates/blob/923f9afe679f549d14079c4ec962cc913f784550/ntc_templates/parse.py#L38
  
  Currently `parse_output` raises a generic `Exception`; this needs to be changed to a custom exception that can be properly caught by users of the function.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `ntc_templates/parse.py`
  Content:
  ```
  1 """ntc_templates.parse."""
  2 
  3 import os
  4 
  5 # Due to TextFSM library issues on Windows, it is better to not fail on import
  6 # Instead fail at runtime (i.e. if method is actually used).
  7 try:
  8     from textfsm import clitable
  9 
  10     HAS_CLITABLE = True
  11 except ImportError:
  12     HAS_CLITABLE = False
  13 
  14 
  15 def _get_template_dir():
  16     template_dir = os.environ.get("NTC_TEMPLATES_DIR")
  17     if template_dir is None:
  18         package_dir = os.path.dirname(__file__)
  19         template_dir = os.path.join(package_dir, "templates")
  20         if not os.path.isdir(template_dir):
  21             project_dir = os.path.dirname(os.path.dirname(os.path.dirname(template_dir)))
  22             template_dir = os.path.join(project_dir, "templates")
  23 
  24     return template_dir
  25 
  26 
  27 def _clitable_to_dict(cli_table):
  28     """Convert TextFSM cli_table object to list of dictionaries."""
  29     objs = []
  30     for row in cli_table:
  31         temp_dict = {}
  32         for index, element in enumerate(row):
  33             temp_dict[cli_table.header[index].lower()] = element
  34         objs.append(temp_dict)
  35 
  36     return objs
  37 
  38 
  39 def parse_output(platform=None, command=None, data=None):
  40     """Return the structured data based on the output from a network device."""
  41     if not HAS_CLITABLE:
  42         msg = """
  43 The TextFSM library is not currently supported on Windows. If you are NOT using Windows
  44 you should be able to 'pip install textfsm' to fix this issue. If you are using Windows
  45 then you will need to install the patch referenced here:
  46 
  47 https://github.com/google/textfsm/pull/82
  48 
  49 """
  50         raise ImportError(msg)
  51 
  52     template_dir = _get_template_dir()
  53     cli_table = clitable.CliTable("index", template_dir)
  54 
  55     attrs = {"Command": command, "Platform": platform}
  56     try:
  57         cli_table.ParseCmd(data, attrs)
  58         structured_data = _clitable_to_dict(cli_table)
  59     except clitable.CliTableError as err:
  60         raise Exception(  # pylint: disable=broad-exception-raised
  61             f'Unable to parse command "{command}" on platform {platform} - {str(err)}'
  62         ) from err
  63         # Invalid or Missing template
  64         # module.fail_json(msg='parsing error', error=str(e))
  65         # rather than fail, fallback to return raw text
  66         # structured_data = [data]
  67 
  68     return structured_data
  69
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/ntc_templates/parse.py b/ntc_templates/parse.py
  --- a/ntc_templates/parse.py
  +++ b/ntc_templates/parse.py
  @@ -12,6 +12,10 @@
       HAS_CLITABLE = False
   
   
  +class ParsingException(Exception):
  +    """Error that is raised when TextFSM hits an `Error` state."""
  +
  +
   def _get_template_dir():
       template_dir = os.environ.get("NTC_TEMPLATES_DIR")
       if template_dir is None:
  @@ -57,9 +61,7 @@
           cli_table.ParseCmd(data, attrs)
           structured_data = _clitable_to_dict(cli_table)
       except clitable.CliTableError as err:
  -        raise Exception(  # pylint: disable=broad-exception-raised
  -            f'Unable to parse command "{command}" on platform {platform} - {str(err)}'
  -        ) from err
  +        raise ParsingException(f'Unable to parse command "{command}" on platform {platform} - {str(err)}') from err
           # Invalid or Missing template
           # module.fail_json(msg='parsing error', error=str(e))
           # rather than fail, fallback to return raw text
  
verification_info: {"golden_diff": "diff --git a/ntc_templates/parse.py b/ntc_templates/parse.py\n--- a/ntc_templates/parse.py\n+++ b/ntc_templates/parse.py\n@@ -12,6 +12,10 @@\n     HAS_CLITABLE = False\n \n \n+class ParsingException(Exception):\n+    \"\"\"Error that is raised when TextFSM hits an `Error` state.\"\"\"\n+\n+\n def _get_template_dir():\n     template_dir = os.environ.get(\"NTC_TEMPLATES_DIR\")\n     if template_dir is None:\n@@ -57,9 +61,7 @@\n         cli_table.ParseCmd(data, attrs)\n         structured_data = _clitable_to_dict(cli_table)\n     except clitable.CliTableError as err:\n-        raise Exception(  # pylint: disable=broad-exception-raised\n-            f'Unable to parse command \"{command}\" on platform {platform} - {str(err)}'\n-        ) from err\n+        raise ParsingException(f'Unable to parse command \"{command}\" on platform {platform} - {str(err)}') from err\n         # Invalid or Missing template\n         # module.fail_json(msg='parsing error', error=str(e))\n         # rather than fail, fallback to return raw text\n", "issue": "Need to raise custom exception\nhttps://github.com/networktocode/ntc-templates/blob/923f9afe679f549d14079c4ec962cc913f784550/ntc_templates/parse.py#L38\r\n\r\nCurrently `parse_output` raises a generic `Exception`; this needs to be changed to a custom exception that can be properly caught by users of the function.\n", "before_files": [{"content": "\"\"\"ntc_templates.parse.\"\"\"\n\nimport os\n\n# Due to TextFSM library issues on Windows, it is better to not fail on import\n# Instead fail at runtime (i.e. if method is actually used).\ntry:\n    from textfsm import clitable\n\n    HAS_CLITABLE = True\nexcept ImportError:\n    HAS_CLITABLE = False\n\n\ndef _get_template_dir():\n    template_dir = os.environ.get(\"NTC_TEMPLATES_DIR\")\n    if template_dir is None:\n        package_dir = os.path.dirname(__file__)\n        template_dir = os.path.join(package_dir, \"templates\")\n        if not os.path.isdir(template_dir):\n            project_dir = os.path.dirname(os.path.dirname(os.path.dirname(template_dir)))\n            template_dir = os.path.join(project_dir, \"templates\")\n\n    return template_dir\n\n\ndef _clitable_to_dict(cli_table):\n    \"\"\"Convert TextFSM cli_table object to list of dictionaries.\"\"\"\n    objs = []\n    for row in cli_table:\n        temp_dict = {}\n        for index, element in enumerate(row):\n            temp_dict[cli_table.header[index].lower()] = element\n        objs.append(temp_dict)\n\n    return objs\n\n\ndef parse_output(platform=None, command=None, data=None):\n    \"\"\"Return the structured data based on the output from a network device.\"\"\"\n    if not HAS_CLITABLE:\n        msg = \"\"\"\nThe TextFSM library is not currently supported on Windows. If you are NOT using Windows\nyou should be able to 'pip install textfsm' to fix this issue. If you are using Windows\nthen you will need to install the patch referenced here:\n\nhttps://github.com/google/textfsm/pull/82\n\n\"\"\"\n        raise ImportError(msg)\n\n    template_dir = _get_template_dir()\n    cli_table = clitable.CliTable(\"index\", template_dir)\n\n    attrs = {\"Command\": command, \"Platform\": platform}\n    try:\n        cli_table.ParseCmd(data, attrs)\n        structured_data = _clitable_to_dict(cli_table)\n    except clitable.CliTableError as err:\n        raise Exception(  # pylint: disable=broad-exception-raised\n            f'Unable to parse command \"{command}\" on platform {platform} - {str(err)}'\n        ) from err\n        # Invalid or Missing template\n        # module.fail_json(msg='parsing error', error=str(e))\n        # rather than fail, fallback to return raw text\n        # structured_data = [data]\n\n    return structured_data\n", "path": "ntc_templates/parse.py"}], "after_files": [{"content": "\"\"\"ntc_templates.parse.\"\"\"\n\nimport os\n\n# Due to TextFSM library issues on Windows, it is better to not fail on import\n# Instead fail at runtime (i.e. if method is actually used).\ntry:\n    from textfsm import clitable\n\n    HAS_CLITABLE = True\nexcept ImportError:\n    HAS_CLITABLE = False\n\n\nclass ParsingException(Exception):\n    \"\"\"Error that is raised when TextFSM hits an `Error` state.\"\"\"\n\n\ndef _get_template_dir():\n    template_dir = os.environ.get(\"NTC_TEMPLATES_DIR\")\n    if template_dir is None:\n        package_dir = os.path.dirname(__file__)\n        template_dir = os.path.join(package_dir, \"templates\")\n        if not os.path.isdir(template_dir):\n            project_dir = os.path.dirname(os.path.dirname(os.path.dirname(template_dir)))\n            template_dir = os.path.join(project_dir, \"templates\")\n\n    return template_dir\n\n\ndef _clitable_to_dict(cli_table):\n    \"\"\"Convert TextFSM cli_table object to list of dictionaries.\"\"\"\n    objs = []\n    for row in cli_table:\n        temp_dict = {}\n        for index, element in enumerate(row):\n            temp_dict[cli_table.header[index].lower()] = element\n        objs.append(temp_dict)\n\n    return objs\n\n\ndef parse_output(platform=None, command=None, data=None):\n    \"\"\"Return the structured data based on the output from a network device.\"\"\"\n    if not HAS_CLITABLE:\n        msg = \"\"\"\nThe TextFSM library is not currently supported on Windows. If you are NOT using Windows\nyou should be able to 'pip install textfsm' to fix this issue. If you are using Windows\nthen you will need to install the patch referenced here:\n\nhttps://github.com/google/textfsm/pull/82\n\n\"\"\"\n        raise ImportError(msg)\n\n    template_dir = _get_template_dir()\n    cli_table = clitable.CliTable(\"index\", template_dir)\n\n    attrs = {\"Command\": command, \"Platform\": platform}\n    try:\n        cli_table.ParseCmd(data, attrs)\n        structured_data = _clitable_to_dict(cli_table)\n    except clitable.CliTableError as err:\n        raise ParsingException(f'Unable to parse command \"{command}\" on platform {platform} - {str(err)}') from err\n        # Invalid or Missing template\n        # module.fail_json(msg='parsing error', error=str(e))\n        # rather than fail, fallback to return raw text\n        # structured_data = [data]\n\n    return structured_data\n", "path": "ntc_templates/parse.py"}]}