Skip to content

Commit fe539bc

Browse files
feat(api): api update
1 parent bf9048d commit fe539bc

11 files changed

Lines changed: 73 additions & 22 deletions

File tree

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 101
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/datamini%2Fasktable-433d41b79e0e9c47c18aa56fbda33ce5a3645c224012d74f8a30da676905a16a.yml
3-
openapi_spec_hash: ff827305d98a6c808e7931a0298e7729
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/datamini%2Fasktable-06a1ae7f466edd478e449b5d27c152c689a75228b0a132d1fc75282b3b004b29.yml
3+
openapi_spec_hash: 38321574df76b7905271862ed1e200f0
44
config_hash: acdf4142177ed1932c2d82372693f811

src/asktable/_qs.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@
22

33
from typing import Any, List, Tuple, Union, Mapping, TypeVar
44
from urllib.parse import parse_qs, urlencode
5-
from typing_extensions import Literal, get_args
5+
from typing_extensions import get_args
66

7-
from ._types import NotGiven, not_given
7+
from ._types import NotGiven, ArrayFormat, NestedFormat, not_given
88
from ._utils import flatten
99

1010
_T = TypeVar("_T")
1111

12-
13-
ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
14-
NestedFormat = Literal["dots", "brackets"]
15-
1612
PrimitiveData = Union[str, int, float, bool, None]
1713
# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"]
1814
# https://github.com/microsoft/pyright/issues/3555

src/asktable/_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
4848
_T = TypeVar("_T")
4949

50+
ArrayFormat = Literal["comma", "repeat", "indices", "brackets"]
51+
NestedFormat = Literal["dots", "brackets"]
52+
5053

5154
# Approximates httpx internal ProxiesTypes and RequestFiles types
5255
# while adding support for `PathLike` instances

src/asktable/_utils/_utils.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
)
1818
from pathlib import Path
1919
from datetime import date, datetime
20-
from typing_extensions import TypeGuard
20+
from typing_extensions import TypeGuard, get_args
2121

2222
import sniffio
2323

24-
from .._types import Omit, NotGiven, FileTypes, HeadersLike
24+
from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike
2525

2626
_T = TypeVar("_T")
2727
_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
@@ -40,25 +40,45 @@ def extract_files(
4040
query: Mapping[str, object],
4141
*,
4242
paths: Sequence[Sequence[str]],
43+
array_format: ArrayFormat = "brackets",
4344
) -> list[tuple[str, FileTypes]]:
4445
"""Recursively extract files from the given dictionary based on specified paths.
4546
4647
A path may look like this ['foo', 'files', '<array>', 'data'].
4748
49+
``array_format`` controls how ``<array>`` segments contribute to the emitted
50+
field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and
51+
``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``).
52+
4853
Note: this mutates the given dictionary.
4954
"""
5055
files: list[tuple[str, FileTypes]] = []
5156
for path in paths:
52-
files.extend(_extract_items(query, path, index=0, flattened_key=None))
57+
files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format))
5358
return files
5459

5560

61+
def _array_suffix(array_format: ArrayFormat, array_index: int) -> str:
62+
if array_format == "brackets":
63+
return "[]"
64+
if array_format == "indices":
65+
return f"[{array_index}]"
66+
if array_format == "repeat" or array_format == "comma":
67+
# Both repeat the bare field name for each file part; there is no
68+
# meaningful way to comma-join binary parts.
69+
return ""
70+
raise NotImplementedError(
71+
f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}"
72+
)
73+
74+
5675
def _extract_items(
5776
obj: object,
5877
path: Sequence[str],
5978
*,
6079
index: int,
6180
flattened_key: str | None,
81+
array_format: ArrayFormat,
6282
) -> list[tuple[str, FileTypes]]:
6383
try:
6484
key = path[index]
@@ -75,9 +95,11 @@ def _extract_items(
7595

7696
if is_list(obj):
7797
files: list[tuple[str, FileTypes]] = []
78-
for entry in obj:
79-
assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
80-
files.append((flattened_key + "[]", cast(FileTypes, entry)))
98+
for array_index, entry in enumerate(obj):
99+
suffix = _array_suffix(array_format, array_index)
100+
emitted_key = (flattened_key + suffix) if flattened_key else suffix
101+
assert_is_file_content(entry, key=emitted_key)
102+
files.append((emitted_key, cast(FileTypes, entry)))
81103
return files
82104

83105
assert_is_file_content(obj, key=flattened_key)
@@ -106,6 +128,7 @@ def _extract_items(
106128
path,
107129
index=index,
108130
flattened_key=flattened_key,
131+
array_format=array_format,
109132
)
110133
elif is_list(obj):
111134
if key != "<array>":
@@ -117,9 +140,12 @@ def _extract_items(
117140
item,
118141
path,
119142
index=index,
120-
flattened_key=flattened_key + "[]" if flattened_key is not None else "[]",
143+
flattened_key=(
144+
(flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index)
145+
),
146+
array_format=array_format,
121147
)
122-
for item in obj
148+
for array_index, item in enumerate(obj)
123149
]
124150
)
125151

src/asktable/resources/datasources/datasources.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def create(
131131
"gaussdbdws",
132132
"bitable",
133133
"dap",
134+
"duckdb",
134135
],
135136
access_config: Optional[datasource_create_params.AccessConfig] | Omit = omit,
136137
name: Optional[str] | Omit = omit,
@@ -249,6 +250,7 @@ def update(
249250
"gaussdbdws",
250251
"bitable",
251252
"dap",
253+
"duckdb",
252254
]
253255
]
254256
| Omit = omit,
@@ -657,6 +659,7 @@ async def create(
657659
"gaussdbdws",
658660
"bitable",
659661
"dap",
662+
"duckdb",
660663
],
661664
access_config: Optional[datasource_create_params.AccessConfig] | Omit = omit,
662665
name: Optional[str] | Omit = omit,
@@ -775,6 +778,7 @@ async def update(
775778
"gaussdbdws",
776779
"bitable",
777780
"dap",
781+
"duckdb",
778782
]
779783
]
780784
| Omit = omit,

src/asktable/types/datasource.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Datasource(BaseModel):
5050
"gaussdbdws",
5151
"bitable",
5252
"dap",
53+
"duckdb",
5354
]
5455
"""数据源引擎"""
5556

src/asktable/types/datasource_create_params.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class DatasourceCreateParams(TypedDict, total=False):
5151
"gaussdbdws",
5252
"bitable",
5353
"dap",
54+
"duckdb",
5455
]
5556
]
5657
"""数据源引擎"""

src/asktable/types/datasource_retrieve_response.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class DatasourceRetrieveResponse(BaseModel):
105105
"gaussdbdws",
106106
"bitable",
107107
"dap",
108+
"duckdb",
108109
]
109110
"""数据源引擎"""
110111

src/asktable/types/datasource_update_params.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class DatasourceUpdateParams(TypedDict, total=False):
5858
"gaussdbdws",
5959
"bitable",
6060
"dap",
61+
"duckdb",
6162
]
6263
]
6364
"""数据源引擎"""

tests/test_extract_files.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from asktable._types import FileTypes
7+
from asktable._types import FileTypes, ArrayFormat
88
from asktable._utils import extract_files
99

1010

@@ -37,10 +37,7 @@ def test_multiple_files() -> None:
3737

3838
def test_top_level_file_array() -> None:
3939
query = {"files": [b"file one", b"file two"], "title": "hello"}
40-
assert extract_files(query, paths=[["files", "<array>"]]) == [
41-
("files[]", b"file one"),
42-
("files[]", b"file two"),
43-
]
40+
assert extract_files(query, paths=[["files", "<array>"]]) == [("files[]", b"file one"), ("files[]", b"file two")]
4441
assert query == {"title": "hello"}
4542

4643

@@ -71,3 +68,24 @@ def test_ignores_incorrect_paths(
7168
expected: list[tuple[str, FileTypes]],
7269
) -> None:
7370
assert extract_files(query, paths=paths) == expected
71+
72+
73+
@pytest.mark.parametrize(
74+
"array_format,expected_top_level,expected_nested",
75+
[
76+
("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]),
77+
("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]),
78+
("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]),
79+
("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]),
80+
],
81+
)
82+
def test_array_format_controls_file_field_names(
83+
array_format: ArrayFormat,
84+
expected_top_level: list[tuple[str, FileTypes]],
85+
expected_nested: list[tuple[str, FileTypes]],
86+
) -> None:
87+
top_level = {"files": [b"a", b"b"]}
88+
assert extract_files(top_level, paths=[["files", "<array>"]], array_format=array_format) == expected_top_level
89+
90+
nested = {"items": [{"file": b"a"}, {"file": b"b"}]}
91+
assert extract_files(nested, paths=[["items", "<array>", "file"]], array_format=array_format) == expected_nested

0 commit comments

Comments
 (0)