Skip to content

Commit 4307c75

Browse files
guptaheenaYunchuWanggavin-aguiar
authored
feat: Add MySql Binding Support V1 (#243)
* mysql binding support * fix linting * Update __init__.py * fix indentation --------- Co-authored-by: wangbill <[email protected]> Co-authored-by: Gavin Aguiar <[email protected]>
1 parent 87cd092 commit 4307c75

File tree

4 files changed

+446
-0
lines changed

4 files changed

+446
-0
lines changed

azure/functions/__init__.py

+4
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',

azure/functions/_mysql.py

+71
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/mysql.py

+78
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+
)

0 commit comments

Comments
 (0)