Skip to content

Commit 5c77961

Browse files
fix: Fir 38125 ecosystem support for struct type for python sdk (#408)
1 parent 47cba14 commit 5c77961

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

src/firebolt/common/_types.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from decimal import Decimal
77
from enum import Enum
88
from io import StringIO
9-
from typing import Any, Dict, List, Optional, Sequence, Union
9+
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
1010

1111
from sqlparse import parse as parse_sql # type: ignore
1212
from sqlparse.sql import ( # type: ignore
@@ -234,7 +234,7 @@ def python_type(self) -> type:
234234
def split_struct_fields(raw_struct: str) -> List[str]:
235235
"""Split raw struct inner fields string into a list of field definitions.
236236
>>> split_struct_fields("field1 int, field2 struct(field1 int, field2 text)")
237-
["field1 int", "field2 struct(field1 int, field2 text)"]
237+
['field1 int', 'field2 struct(field1 int, field2 text)']
238238
"""
239239
balance = 0 # keep track of the level of nesting, and only split on level 0
240240
separator = ","
@@ -246,15 +246,38 @@ def split_struct_fields(raw_struct: str) -> List[str]:
246246
elif ch == ")":
247247
balance -= 1
248248
elif ch == separator and balance == 0:
249-
res.append(current.getvalue())
249+
res.append(current.getvalue().strip())
250250
current = StringIO()
251251
continue
252252
current.write(ch)
253253

254-
res.append(current.getvalue())
254+
res.append(current.getvalue().strip())
255255
return res
256256

257257

258+
def split_struct_field(raw_field: str) -> Tuple[str, str]:
259+
"""Split raw struct field into name and type.
260+
>>> split_struct_field("field int")
261+
('field', 'int')
262+
>>> split_struct_field("`with space` text null")
263+
('with space', 'text null')
264+
>>> split_struct_field("s struct(`a b` int)")
265+
('s', 'struct(`a b` int)')
266+
"""
267+
raw_field = raw_field.strip()
268+
second_tick = (
269+
raw_field.find("`", raw_field.find("`") + 1)
270+
if raw_field.startswith("`")
271+
else -1
272+
)
273+
name, type_ = (
274+
(raw_field[: second_tick + 1], raw_field[second_tick + 1 :])
275+
if second_tick != -1
276+
else raw_field.split(" ", 1)
277+
)
278+
return name.strip(" `"), type_.strip()
279+
280+
258281
def parse_type(raw_type: str) -> Union[type, ExtendedType]: # noqa: C901
259282
"""Parse typename provided by query metadata into Python type."""
260283
if not isinstance(raw_type, str):
@@ -276,7 +299,7 @@ def parse_type(raw_type: str) -> Union[type, ExtendedType]: # noqa: C901
276299
fields_raw = split_struct_fields(raw_type[len(STRUCT._prefix) : -1])
277300
fields = {}
278301
for f in fields_raw:
279-
name, type_ = f.strip().split(" ", 1)
302+
name, type_ = split_struct_field(f)
280303
fields[name.strip()] = parse_type(type_.strip())
281304
return STRUCT(fields)
282305
except ValueError:

tests/unit/common/test_typing_parse.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ def test_parse_type(types_map: Dict[str, type]) -> None:
3535
), "Invalid type parsing error message"
3636

3737

38+
def test_parse_struct_type_with_spaces() -> None:
39+
parsed = parse_type("struct(`a b` int, s struct(`c d` text))")
40+
assert parsed == STRUCT(
41+
{"a b": int, "s": STRUCT({"c d": str})}
42+
), f"Error parsing struct type with spaces"
43+
44+
3845
@mark.parametrize(
3946
"value,expected,error",
4047
[
@@ -363,9 +370,9 @@ def test_parse_value_struct(value, expected, type_, error) -> None:
363370
@mark.parametrize(
364371
"value,expected",
365372
[
366-
("a int, b text", ["a int", " b text"]),
367-
("a int, s struct(a int, b text)", ["a int", " s struct(a int, b text)"]),
368-
("a int, b array(struct(a int))", ["a int", " b array(struct(a int))"]),
373+
("a int, b text", ["a int", "b text"]),
374+
("a int, s struct(a int, b text)", ["a int", "s struct(a int, b text)"]),
375+
("a int, b array(struct(a int))", ["a int", "b array(struct(a int))"]),
369376
],
370377
)
371378
def test_split_struct_fields(value, expected) -> None:

0 commit comments

Comments
 (0)