Skip to content

Commit 956d914

Browse files
authored
Merge branch 'dev' into manvkaur/updateassistantdecorator
2 parents 6167c2c + 39ebfe0 commit 956d914

File tree

7 files changed

+452
-6
lines changed

7 files changed

+452
-6
lines changed

azure/functions/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from ._queue import QueueMessage
2424
from ._servicebus import ServiceBusMessage
2525
from ._sql import SqlRow, SqlRowList
26+
from ._mysql import MySqlRow, MySqlRowList
2627

2728
# Import binding implementations to register them
2829
from . import blob # NoQA
@@ -37,6 +38,7 @@
3738
from . import durable_functions # NoQA
3839
from . import sql # NoQA
3940
from . import warmup # NoQA
41+
from . import mysql # NoQA
4042

4143

4244
__all__ = (
@@ -67,6 +69,8 @@
6769
'SqlRowList',
6870
'TimerRequest',
6971
'WarmUpContext',
72+
'MySqlRow',
73+
'MySqlRowList',
7074

7175
# Middlewares
7276
'WsgiMiddleware',
@@ -98,4 +102,4 @@
98102
'BlobSource'
99103
)
100104

101-
__version__ = '1.21.0b3'
105+
__version__ = '1.21.1'

azure/functions/_mysql.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import abc
4+
import collections
5+
import json
6+
7+
8+
class BaseMySqlRow(abc.ABC):
9+
10+
@classmethod
11+
@abc.abstractmethod
12+
def from_json(cls, json_data: str) -> 'BaseMySqlRow':
13+
raise NotImplementedError
14+
15+
@classmethod
16+
@abc.abstractmethod
17+
def from_dict(cls, dct: dict) -> 'BaseMySqlRow':
18+
raise NotImplementedError
19+
20+
@abc.abstractmethod
21+
def __getitem__(self, key):
22+
raise NotImplementedError
23+
24+
@abc.abstractmethod
25+
def __setitem__(self, key, value):
26+
raise NotImplementedError
27+
28+
@abc.abstractmethod
29+
def to_json(self) -> str:
30+
raise NotImplementedError
31+
32+
33+
class BaseMySqlRowList(abc.ABC):
34+
pass
35+
36+
37+
class MySqlRow(BaseMySqlRow, collections.UserDict):
38+
"""A MySql Row.
39+
40+
MySqlRow objects are ''UserDict'' subclasses and behave like dicts.
41+
"""
42+
43+
@classmethod
44+
def from_json(cls, json_data: str) -> 'BaseMySqlRow':
45+
"""Create a MySqlRow from a JSON string."""
46+
return cls.from_dict(json.loads(json_data))
47+
48+
@classmethod
49+
def from_dict(cls, dct: dict) -> 'BaseMySqlRow':
50+
"""Create a MySqlRow from a dict object"""
51+
return cls({k: v for k, v in dct.items()})
52+
53+
def to_json(self) -> str:
54+
"""Return the JSON representation of the MySqlRow"""
55+
return json.dumps(dict(self))
56+
57+
def __getitem__(self, key):
58+
return collections.UserDict.__getitem__(self, key)
59+
60+
def __setitem__(self, key, value):
61+
return collections.UserDict.__setitem__(self, key, value)
62+
63+
def __repr__(self) -> str:
64+
return (
65+
f'<MySqlRow at 0x{id(self):0x}>'
66+
)
67+
68+
69+
class MySqlRowList(BaseMySqlRowList, collections.UserList):
70+
"A ''UserList'' subclass containing a list of :class:'~MySqlRow' objects"
71+
pass

azure/functions/decorators/blob.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(self,
1717
**kwargs):
1818
self.path = path
1919
self.connection = connection
20-
self.source = source
20+
self.source = source.value if source else None
2121
super().__init__(name=name, data_type=data_type)
2222

2323
@staticmethod

azure/functions/mysql.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import collections.abc
5+
import json
6+
import typing
7+
8+
from azure.functions import _mysql as mysql
9+
10+
from . import meta
11+
12+
13+
class MySqlConverter(meta.InConverter, meta.OutConverter,
14+
binding='mysql'):
15+
16+
@classmethod
17+
def check_input_type_annotation(cls, pytype: type) -> bool:
18+
return issubclass(pytype, mysql.BaseMySqlRowList)
19+
20+
@classmethod
21+
def check_output_type_annotation(cls, pytype: type) -> bool:
22+
return issubclass(pytype, (mysql.BaseMySqlRowList, mysql.BaseMySqlRow))
23+
24+
@classmethod
25+
def decode(cls,
26+
data: meta.Datum,
27+
*,
28+
trigger_metadata) -> typing.Optional[mysql.MySqlRowList]:
29+
if data is None or data.type is None:
30+
return None
31+
32+
data_type = data.type
33+
34+
if data_type in ['string', 'json']:
35+
body = data.value
36+
37+
elif data_type == 'bytes':
38+
body = data.value.decode('utf-8')
39+
40+
else:
41+
raise NotImplementedError(
42+
f'Unsupported payload type: {data_type}')
43+
44+
rows = json.loads(body)
45+
if not isinstance(rows, list):
46+
rows = [rows]
47+
48+
return mysql.MySqlRowList(
49+
(None if row is None else mysql.MySqlRow.from_dict(row))
50+
for row in rows)
51+
52+
@classmethod
53+
def encode(cls, obj: typing.Any, *,
54+
expected_type: typing.Optional[type]) -> meta.Datum:
55+
if isinstance(obj, mysql.MySqlRow):
56+
data = mysql.MySqlRowList([obj])
57+
58+
elif isinstance(obj, mysql.MySqlRowList):
59+
data = obj
60+
61+
elif isinstance(obj, collections.abc.Iterable):
62+
data = mysql.MySqlRowList()
63+
64+
for row in obj:
65+
if not isinstance(row, mysql.MySqlRow):
66+
raise NotImplementedError(
67+
f'Unsupported list type: {type(obj)}, \
68+
lists must contain MySqlRow objects')
69+
else:
70+
data.append(row)
71+
72+
else:
73+
raise NotImplementedError(f'Unsupported type: {type(obj)}')
74+
75+
return meta.Datum(
76+
type='json',
77+
value=json.dumps([dict(d) for d in data])
78+
)

tests/decorators/test_blob.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def test_blob_trigger_creation_with_default_specified_source(self):
4242
"name": "req",
4343
"dataType": DataType.UNDEFINED,
4444
"path": "dummy_path",
45-
'source': BlobSource.LOGS_AND_CONTAINER_SCAN,
45+
'source': 'LogsAndContainerScan',
4646
"connection": "dummy_connection"
4747
})
4848

@@ -62,7 +62,7 @@ def test_blob_trigger_creation_with_source_as_string(self):
6262
"name": "req",
6363
"dataType": DataType.UNDEFINED,
6464
"path": "dummy_path",
65-
'source': BlobSource.EVENT_GRID,
65+
'source': 'EventGrid',
6666
"connection": "dummy_connection"
6767
})
6868

@@ -82,7 +82,7 @@ def test_blob_trigger_creation_with_source_as_enum(self):
8282
"name": "req",
8383
"dataType": DataType.UNDEFINED,
8484
"path": "dummy_path",
85-
'source': BlobSource.EVENT_GRID,
85+
'source': 'EventGrid',
8686
"connection": "dummy_connection"
8787
})
8888

tests/decorators/test_decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1628,7 +1628,7 @@ def test_blob_input_binding():
16281628
"type": BLOB_TRIGGER,
16291629
"name": "req",
16301630
"path": "dummy_path",
1631-
"source": BlobSource.EVENT_GRID,
1631+
"source": 'EventGrid',
16321632
"connection": "dummy_conn"
16331633
})
16341634

0 commit comments

Comments
 (0)