Repository: GoogleCloudPlatform/functions-framework-python (python)

Issue:
Using decorator @functions_framework.errorhandler makes the python script impossible to import
Using decorator `@functions_framework.errorhandler` makes the python script impossible to import without creating an application.
Import produces the error:
```
AttributeError: module 'functions_framework' has no attribute 'errorhandler'
```

## Example
Trying to start run 'importer.py' script produce the above error.

decorated.py
```python
from typing import Tuple

from flask.typing import ResponseValue, StatusCode

import functions_framework


@functions_framework.errorhandler
def handle_exception(ex: Exception) -> Tuple[ResponseValue[str], StatusCode]:
    return "Unhandled exception", 500
```

importer.py
```python
import decorated

if __name__ == '__main__':
    print("Imported")

```

## Possible source of the issue

The error handler decorator is registered in [`functions_framework.create_app()`](https://github.com/GoogleCloudPlatform/functions-framework-python/blob/c7f66fd5ed9856bdf4e484774742d7c158ab7b43/src/functions_framework/__init__.py#L232) and it is not available without calling this functons.

Pull request:
fix: Add a DummyErrorHandler
Fixes #136. This allows the `functions_framework.errorhandler` to be called on import outside the context of app creation.

Patch:
diff --git a/src/functions_framework/__init__.py b/src/functions_framework/__init__.py
index 36cde34..246dcbb 100644
--- a/src/functions_framework/__init__.py
+++ b/src/functions_framework/__init__.py
@@ -348,3 +348,14 @@ class LazyWSGIApp:
 
 
 app = LazyWSGIApp()
+
+
+class DummyErrorHandler:
+    def __init__(self):
+        pass
+
+    def __call__(self, *args, **kwargs):
+        return self
+
+
+errorhandler = DummyErrorHandler()


Test patch:
diff --git a/tests/test_cloudevent_functions.py b/tests/test_cloudevent_functions.py
index 061bb94..237225c 100644
--- a/tests/test_cloudevent_functions.py
+++ b/tests/test_cloudevent_functions.py
@@ -16,9 +16,9 @@ import pathlib
 
 import pytest
 
-from cloudevents.http import CloudEvent, from_http, to_binary, to_structured
+from cloudevents.http import CloudEvent, to_binary, to_structured
 
-from functions_framework import LazyWSGIApp, create_app, exceptions
+from functions_framework import create_app
 
 TEST_FUNCTIONS_DIR = pathlib.Path(__file__).resolve().parent / "test_functions"
 TEST_DATA_DIR = pathlib.Path(__file__).resolve().parent / "test_data"
diff --git a/tests/test_functions.py b/tests/test_functions.py
index ad16c15..e17df45 100644
--- a/tests/test_functions.py
+++ b/tests/test_functions.py
@@ -25,7 +25,7 @@ import pytest
 
 import functions_framework
 
-from functions_framework import LazyWSGIApp, create_app, exceptions
+from functions_framework import LazyWSGIApp, create_app, errorhandler, exceptions
 
 TEST_FUNCTIONS_DIR = pathlib.Path.cwd() / "tests" / "test_functions"
 
@@ -454,6 +454,12 @@ def test_lazy_wsgi_app(monkeypatch, target, source, signature_type):
     ]
 
 
+def test_dummy_error_handler():
+    @errorhandler("foo", bar="baz")
+    def function():
+        pass
+
+
 def test_class_in_main_is_in_right_module():
     source = TEST_FUNCTIONS_DIR / "module_is_correct" / "main.py"
     target = "function"