Repository: Azure/azure-functions-python-library

Issue:
[FeatureRequest] Add EventGrid source option for blob_trigger
This is already documented [here](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob-trigger#decorators):

> - source: Sets the source of the triggering event. Use EventGrid for an Event Grid-based blob trigger, which provides much lower latency. The default is LogsAndContainerScan, which uses the standard polling mechanism to detect changes in the container.

But [blob_trigger](https://github.com/Azure/azure-functions-python-library/blob/ded84af5910aa2333568c144f8349502e4d6c338/azure/functions/decorators/function_app.py#L991) has no such argument and [BlobTrigger](https://github.com/Azure/azure-functions-python-library/blob/ded84af5910aa2333568c144f8349502e4d6c338/azure/functions/decorators/blob.py#L10) ignores the `kwargs`.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`0ca53b3`)](https://app.codecov.io/gh/Azure/azure-functions-python-library/commit/0ca53b35491ce22569f012d485c9973050ea3227?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) 92.97% compared to head [(`4fb1400`)](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) 92.98%.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##              dev     #196      +/-   ##
==========================================
+ Coverage   92.97%   92.98%   +0.01%     
==========================================
  Files          56       56              
  Lines        3187     3193       +6     
  Branches      649      649              
==========================================
+ Hits         2963     2969       +6     
  Misses        137      137              
  Partials       87       87              
```

| [Flag](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | `92.95% <100.00%> (+0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 93.11%. Comparing base [(`b85eb7a`)](https://app.codecov.io/gh/Azure/azure-functions-python-library/commit/b85eb7a7909198de93fd974dd8225aa9d9cbdbc5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) to head [(`ef5323b`)](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).

> :exclamation: Current head ef5323b differs from pull request most recent head 9b44ae5. Consider uploading reports for the commit 9b44ae5 to get more accurate results


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##              dev     #196      +/-   ##
==========================================
+ Coverage   93.10%   93.11%   +0.01%     
==========================================
  Files          56       56              
  Lines        3247     3253       +6     
  Branches      666      666              
==========================================
+ Hits         3023     3029       +6     
  Misses        137      137              
  Partials       87       87              
```

| [Flag](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure) | `93.08% <100.00%> (+0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Azure/azure-functions-python-library/pull/196?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azure).


Patch:
diff --git a/azure/functions/__init__.py b/azure/functions/__init__.py
index 4a35da6..15cf667 100644
--- a/azure/functions/__init__.py
+++ b/azure/functions/__init__.py
@@ -10,7 +10,7 @@ from .decorators import (FunctionApp, Function, Blueprint,
                          DecoratorApi, DataType, AuthLevel,
                          Cardinality, AccessRights, HttpMethod,
                          AsgiFunctionApp, WsgiFunctionApp,
-                         ExternalHttpFunctionApp)
+                         ExternalHttpFunctionApp, BlobSource)
 from ._durable_functions import OrchestrationContext, EntityContext
 from .decorators.function_app import (FunctionRegister, TriggerApi,
                                       BindingApi, SettingsApi)
@@ -94,7 +94,8 @@ __all__ = (
     'AuthLevel',
     'Cardinality',
     'AccessRights',
-    'HttpMethod'
+    'HttpMethod',
+    'BlobSource'
 )
 
 __version__ = '1.20.0b2'
diff --git a/azure/functions/decorators/__init__.py b/azure/functions/decorators/__init__.py
index 90ff9ac..be7ff99 100644
--- a/azure/functions/decorators/__init__.py
+++ b/azure/functions/decorators/__init__.py
@@ -3,7 +3,8 @@
 from .core import Cardinality, AccessRights
 from .function_app import FunctionApp, Function, DecoratorApi, DataType, \
     AuthLevel, Blueprint, ExternalHttpFunctionApp, AsgiFunctionApp, \
-    WsgiFunctionApp, FunctionRegister, TriggerApi, BindingApi, SettingsApi
+    WsgiFunctionApp, FunctionRegister, TriggerApi, BindingApi, \
+    SettingsApi, BlobSource
 from .http import HttpMethod
 
 __all__ = [
@@ -22,5 +23,6 @@ __all__ = [
     'AuthLevel',
     'Cardinality',
     'AccessRights',
-    'HttpMethod'
+    'HttpMethod',
+    'BlobSource'
 ]
diff --git a/azure/functions/decorators/blob.py b/azure/functions/decorators/blob.py
index 35d5678..0f38603 100644
--- a/azure/functions/decorators/blob.py
+++ b/azure/functions/decorators/blob.py
@@ -3,8 +3,8 @@
 from typing import Optional
 
 from azure.functions.decorators.constants import BLOB_TRIGGER, BLOB
-from azure.functions.decorators.core import Trigger, OutputBinding, DataType, \
-    InputBinding
+from azure.functions.decorators.core import BlobSource, Trigger, \
+    OutputBinding, DataType, InputBinding
 
 
 class BlobTrigger(Trigger):
@@ -12,10 +12,12 @@ class BlobTrigger(Trigger):
                  name: str,
                  path: str,
                  connection: str,
+                 source: BlobSource,
                  data_type: Optional[DataType] = None,
                  **kwargs):
         self.path = path
         self.connection = connection
+        self.source = source
         super().__init__(name=name, data_type=data_type)
 
     @staticmethod
diff --git a/azure/functions/decorators/core.py b/azure/functions/decorators/core.py
index 81e2ff9..7aa9d12 100644
--- a/azure/functions/decorators/core.py
+++ b/azure/functions/decorators/core.py
@@ -65,6 +65,14 @@ class AccessRights(StringifyEnum):
     and all related message handling. """
 
 
+class BlobSource(StringifyEnum):
+    """Source of the blob trigger."""
+    EVENT_GRID = "EventGrid"
+    """Event Grid is the source of the blob trigger."""
+    LOGS_AND_CONTAINER_SCAN = "LogsAndContainerScan"
+    """Standard polling mechanism to detect changes in the container."""
+
+
 class Binding(ABC):
     """Abstract binding class which captures common attributes and
     functions. :meth:`get_dict_repr` can auto generate the function.json for
diff --git a/azure/functions/decorators/function_app.py b/azure/functions/decorators/function_app.py
index fe17ceb..3807c67 100644
--- a/azure/functions/decorators/function_app.py
+++ b/azure/functions/decorators/function_app.py
@@ -11,7 +11,7 @@ from typing import Any, Callable, Dict, List, Optional, Union, \
 
 from azure.functions.decorators.blob import BlobTrigger, BlobInput, BlobOutput
 from azure.functions.decorators.core import Binding, Trigger, DataType, \
-    AuthLevel, SCRIPT_FILE_NAME, Cardinality, AccessRights, Setting
+    AuthLevel, SCRIPT_FILE_NAME, Cardinality, AccessRights, Setting, BlobSource
 from azure.functions.decorators.cosmosdb import CosmosDBTrigger, \
     CosmosDBOutput, CosmosDBInput, CosmosDBTriggerV3, CosmosDBInputV3, \
     CosmosDBOutputV3
@@ -1114,6 +1114,8 @@ class TriggerApi(DecoratorApi, ABC):
                      arg_name: str,
                      path: str,
                      connection: str,
+                     source: BlobSource =
+                     BlobSource.LOGS_AND_CONTAINER_SCAN,
                      data_type: Optional[DataType] = None,
                      **kwargs) -> Callable[..., Any]:
         """
@@ -1131,6 +1133,12 @@ class TriggerApi(DecoratorApi, ABC):
         :param path: The path to the blob.
         :param connection: The name of an app setting or setting collection
         that specifies how to connect to Azure Blobs.
+        :param source: Sets the source of the triggering event.
+        Use EventGrid for an Event Grid-based blob trigger,
+        which provides much lower latency.
+        The default is LogsAndContainerScan,
+        which uses the standard polling mechanism to detect changes
+        in the container.
         :param data_type: Defines how Functions runtime should treat the
         parameter value.
         :param kwargs: Keyword arguments for specifying additional binding
@@ -1147,6 +1155,7 @@ class TriggerApi(DecoratorApi, ABC):
                         name=arg_name,
                         path=path,
                         connection=connection,
+                        source=source,
                         data_type=parse_singular_param_to_enum(data_type,
                                                                DataType),
                         **kwargs))
diff --git a/docs/ProgModelSpec.pyi b/docs/ProgModelSpec.pyi
index a01d816..14fe202 100644
--- a/docs/ProgModelSpec.pyi
+++ b/docs/ProgModelSpec.pyi
@@ -4,7 +4,7 @@ from abc import ABC
 from typing import Callable, Dict, List, Optional, Union, Iterable
 
 from azure.functions import AsgiMiddleware, WsgiMiddleware
-from azure.functions.decorators.core import Binding, Trigger, DataType, \
+from azure.functions.decorators.core import Binding, BlobSource, Trigger, DataType, \
     AuthLevel, Cardinality, AccessRights, Setting
 from azure.functions.decorators.function_app import FunctionBuilder, SettingsApi
 from azure.functions.decorators.http import HttpMethod
@@ -495,6 +495,7 @@ class TriggerApi(DecoratorApi, ABC):
                      arg_name: str,
                      path: str,
                      connection: str,
+                     source: BlobSource = BlobSource.LOGS_AND_CONTAINER_SCAN,
                      data_type: Optional[DataType] = None,
                      **kwargs) -> Callable:
         """
@@ -512,6 +513,12 @@ class TriggerApi(DecoratorApi, ABC):
         :param path: The path to the blob.
         :param connection: The name of an app setting or setting collection
         that specifies how to connect to Azure Blobs.
+        :param source: Sets the source of the triggering event. 
+        Use EventGrid for an Event Grid-based blob trigger, 
+        which provides much lower latency. 
+        The default is LogsAndContainerScan, 
+        which uses the standard polling mechanism to detect changes 
+        in the container.
         :param data_type: Defines how Functions runtime should treat the
         parameter value.
         :param kwargs: Keyword arguments for specifying additional binding


Test patch:
diff --git a/tests/decorators/test_blob.py b/tests/decorators/test_blob.py
index 9d6154e..f77f731 100644
--- a/tests/decorators/test_blob.py
+++ b/tests/decorators/test_blob.py
@@ -3,14 +3,16 @@
 import unittest
 
 from azure.functions.decorators.blob import BlobTrigger, BlobOutput, BlobInput
-from azure.functions.decorators.core import BindingDirection, DataType
+from azure.functions.decorators.core import BindingDirection, BlobSource, \
+    DataType
 
 
 class TestBlob(unittest.TestCase):
-    def test_blob_trigger_valid_creation(self):
+    def test_blob_trigger_creation_with_source_as_string(self):
         trigger = BlobTrigger(name="req",
                               path="dummy_path",
                               connection="dummy_connection",
+                              source=BlobSource.EVENT_GRID,
                               data_type=DataType.UNDEFINED,
                               dummy_field="dummy")
 
@@ -22,6 +24,27 @@ class TestBlob(unittest.TestCase):
             "name": "req",
             "dataType": DataType.UNDEFINED,
             "path": "dummy_path",
+            'source': BlobSource.EVENT_GRID,
+            "connection": "dummy_connection"
+        })
+
+    def test_blob_trigger_creation_with_source_as_enum(self):
+        trigger = BlobTrigger(name="req",
+                              path="dummy_path",
+                              connection="dummy_connection",
+                              source=BlobSource.EVENT_GRID,
+                              data_type=DataType.UNDEFINED,
+                              dummy_field="dummy")
+
+        self.assertEqual(trigger.get_binding_name(), "blobTrigger")
+        self.assertEqual(trigger.get_dict_repr(), {
+            "type": "blobTrigger",
+            "direction": BindingDirection.IN,
+            'dummyField': 'dummy',
+            "name": "req",
+            "dataType": DataType.UNDEFINED,
+            "path": "dummy_path",
+            'source': BlobSource.EVENT_GRID,
             "connection": "dummy_connection"
         })
 
diff --git a/tests/decorators/test_decorators.py b/tests/decorators/test_decorators.py
index f31a42b..115c857 100644
--- a/tests/decorators/test_decorators.py
+++ b/tests/decorators/test_decorators.py
@@ -8,7 +8,7 @@ from azure.functions.decorators.constants import TIMER_TRIGGER, HTTP_TRIGGER, \
     BLOB_TRIGGER, EVENT_GRID_TRIGGER, EVENT_GRID, TABLE, WARMUP_TRIGGER, \
     SQL, SQL_TRIGGER, ORCHESTRATION_TRIGGER, ACTIVITY_TRIGGER, \
     ENTITY_TRIGGER, DURABLE_CLIENT
-from azure.functions.decorators.core import DataType, AuthLevel, \
+from azure.functions.decorators.core import BlobSource, DataType, AuthLevel, \
     BindingDirection, AccessRights, Cardinality
 from azure.functions.decorators.function_app import FunctionApp
 from azure.functions.decorators.http import HttpTrigger, HttpMethod
@@ -1569,6 +1569,8 @@ class TestFunctionsApp(unittest.TestCase):
                                          "type": BLOB_TRIGGER,
                                          "name": "req",
                                          "path": "dummy_path",
+                                         "source":
+                                         BlobSource.LOGS_AND_CONTAINER_SCAN,
                                          "connection": "dummy_conn"
                                      }]})
 
@@ -1593,6 +1595,7 @@ class TestFunctionsApp(unittest.TestCase):
             "type": BLOB_TRIGGER,
             "name": "req",
             "path": "dummy_path",
+            "source": BlobSource.LOGS_AND_CONTAINER_SCAN,
             "connection": "dummy_conn"
         })
 
@@ -1601,6 +1604,7 @@ class TestFunctionsApp(unittest.TestCase):
 
         @app.blob_trigger(arg_name="req", path="dummy_path",
                           data_type=DataType.STRING,
+                          source=BlobSource.EVENT_GRID,
                           connection="dummy_conn")
         @app.blob_input(arg_name="file", path="dummy_in_path",
                         connection="dummy_in_conn",
@@ -1622,6 +1626,7 @@ class TestFunctionsApp(unittest.TestCase):
             "type": BLOB_TRIGGER,
             "name": "req",
             "path": "dummy_path",
+            "source": BlobSource.EVENT_GRID,
             "connection": "dummy_conn"
         })
 
@@ -1660,6 +1665,7 @@ class TestFunctionsApp(unittest.TestCase):
             "type": BLOB_TRIGGER,
             "name": "req",
             "path": "dummy_path",
+            "source": BlobSource.LOGS_AND_CONTAINER_SCAN,
             "connection": "dummy_conn"
         })