Skip to content

Commit 461db36

Browse files
authored
Maintain datatype retrieve rows (#2163)
1 parent 9cd62cb commit 461db36

6 files changed

Lines changed: 57 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ Changes are grouped as follows
2121
### Fixed
2222
- Fixes type annotations for Functions API. Adds new `FunctionHandle` type for annotating function handles.
2323

24+
## [7.75.2] - 2025-06-05
25+
### Fixed
26+
- The `client.raw.rows.retrieve_dataframe` method now has a new parameter `infer_dtypes` that allows
27+
you to not infer the data types of column types in the returning dataframe.
28+
2429
## [7.75.1] - 2025-05-15
2530
### Fixed
2631
- Fixes missing `instance_id` field in `Document` class returned from `client.documents.list()` and `client.documents.search()`.

cognite/client/_api/raw.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ def retrieve_dataframe(
717717
limit: int | None = DEFAULT_LIMIT_READ,
718718
partitions: int | None = None,
719719
last_updated_time_in_index: bool = False,
720+
infer_dtypes: bool = True,
720721
) -> pd.DataFrame:
721722
"""`Retrieve rows in a table as a pandas dataframe. <https://developer.cognite.com/api#tag/Raw/operation/getRows>`_
722723
@@ -734,6 +735,7 @@ def retrieve_dataframe(
734735
(will be capped at this value). To prevent unexpected problems and maximize read throughput, check out
735736
`concurrency limits in the API documentation. <https://developer.cognite.com/api#tag/Raw/#section/Request-and-concurrency-limits>`_
736737
last_updated_time_in_index (bool): Use a MultiIndex with row keys and last_updated_time as index.
738+
infer_dtypes (bool): If True, pandas will try to infer dtypes of the columns. Defaults to True.
737739
738740
Returns:
739741
pd.DataFrame: The requested rows in a pandas dataframe.
@@ -756,7 +758,7 @@ def retrieve_dataframe(
756758
else:
757759
idx = [r.key for r in rows]
758760
cols = [r.columns for r in rows]
759-
return pd.DataFrame(cols, index=idx)
761+
return pd.DataFrame(cols, index=idx, dtype=object if not infer_dtypes else None)
760762

761763
def _get_parallel_cursors(
762764
self,

cognite/client/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import annotations
22

3-
__version__ = "7.75.1"
3+
__version__ = "7.75.2"
44

55
__api_subversion__ = "20230101"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "cognite-sdk"
3-
version = "7.75.1"
3+
version = "7.75.2"
44

55
description = "Cognite Python SDK"
66
readme = "README.md"

tests/tests_unit/test_api/test_raw.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44
import pytest
55

6+
from cognite.client import CogniteClient
67
from cognite.client._api.raw import RawRowsAPI
78
from cognite.client.data_classes import Database, DatabaseList, Row, RowList, RowWrite, RowWriteList, Table, TableList
89
from cognite.client.exceptions import CogniteAPIError
9-
from tests.utils import jsgz_load
10+
from tests.utils import assert_all_value_types_equal, jsgz_load
1011

1112

1213
@pytest.fixture
@@ -81,6 +82,29 @@ def mock_retrieve_raw_rows_response_two_rows(rsps, cognite_client):
8182
yield rsps
8283

8384

85+
@pytest.fixture
86+
def integer_rows_response() -> dict:
87+
return {
88+
"items": [
89+
{"key": "row1", "columns": {"c1": 1, "c2": 4.0}, "lastUpdatedTime": 0},
90+
{"key": "row2", "columns": {"c1": 2, "c2": 3}, "lastUpdatedTime": 1},
91+
{"key": "row3", "columns": {"c1": 3, "c2": 1}, "lastUpdatedTime": 2},
92+
{"key": "row4", "columns": {"c1": None, "c2": 0.1}, "lastUpdatedTime": 3},
93+
]
94+
}
95+
96+
97+
@pytest.fixture
98+
def mock_retrieve_integer_rows(rsps, integer_rows_response: dict, cognite_client: CogniteClient):
99+
rsps.add(
100+
rsps.GET,
101+
cognite_client.raw._get_base_url_with_base_path() + "/raw/dbs/db1/tables/table1/rows",
102+
status=200,
103+
json=integer_rows_response,
104+
)
105+
yield rsps
106+
107+
84108
@pytest.fixture
85109
def mock_retrieve_raw_rows_response_one_row(rsps, cognite_client):
86110
response_body = {"items": [{"key": "row1", "columns": {"c1": 1, "c2": "2"}, "lastUpdatedTime": 0}]}
@@ -378,6 +402,19 @@ def test_retrieve_dataframe_two_rows(self, cognite_client, mock_retrieve_raw_row
378402
pd.Timestamp(1, unit="ms"),
379403
]
380404

405+
def test_retrieve_dataframe_integers(
406+
self,
407+
cognite_client: CogniteClient,
408+
mock_retrieve_integer_rows,
409+
integer_rows_response: dict,
410+
) -> None:
411+
result = cognite_client.raw.rows.retrieve_dataframe(db_name="db1", table_name="table1", infer_dtypes=False)
412+
413+
actual = result.to_dict(orient="index")
414+
expected = {row["key"]: row["columns"] for row in integer_rows_response["items"]}
415+
assert_all_value_types_equal(actual, expected)
416+
assert actual == expected
417+
381418

382419
@pytest.mark.parametrize("raw_cls", (Row, RowWrite))
383420
def test_raw_row__direct_column_access(raw_cls):

tests/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,3 +630,12 @@ def get_or_raise(obj: T | None) -> T:
630630
if obj is None:
631631
raise ValueError("Object is None")
632632
return obj
633+
634+
635+
def assert_all_value_types_equal(d1: dict, d2: dict) -> None:
636+
assert d1.keys() == d2.keys()
637+
for k in d1.keys():
638+
v1, v2 = d1[k], d2[k]
639+
assert type(v1) is type(v2), f"Type mismatch for key '{k}': {type(v1)} != {type(v2)}"
640+
if isinstance(v1, dict) and isinstance(v2, dict):
641+
assert_all_value_types_equal(v1, v2)

0 commit comments

Comments
 (0)