tracktolib 0.64.0__tar.gz → 0.65.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tracktolib
3
- Version: 0.64.0
3
+ Version: 0.65.1
4
4
  Summary: Utility library for python
5
5
  Keywords: utility
6
6
  Author-email: julien.brayere@tracktor.fr
7
7
  License-Expression: MIT
8
8
  Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
9
13
  Requires-Dist: fastapi>=0.103.2 ; extra == 'api'
10
14
  Requires-Dist: pydantic>=2 ; extra == 'api'
11
15
  Requires-Dist: httpx>=0.25.0 ; extra == 'http'
@@ -36,21 +40,25 @@ Description-Content-Type: text/markdown
36
40
  [![Latest PyPI version](https://img.shields.io/pypi/v/tracktolib?logo=pypi)](https://pypi.python.org/pypi/tracktolib)
37
41
  [![CircleCI](https://circleci.com/gh/Tracktor/tracktolib/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/Tracktor/tracktolib?branch=master)
38
42
 
39
- Utility library for python
43
+ Utility library for Python 3.12+
40
44
 
41
- # Installation
45
+ ## Installation
42
46
 
43
- Just run:
47
+ ```bash
48
+ uv add tracktolib
49
+ ```
50
+
51
+ With specific extras:
44
52
 
45
53
  ```bash
46
- uv add tracktolib@latest
54
+ uv add tracktolib[pg,api]
47
55
  ```
48
56
 
49
- # Utilities
57
+ ## Modules
50
58
 
51
- - **log**
59
+ ### logs
52
60
 
53
- Utility functions for logging.
61
+ Utility functions to initialize logging formatting and streams.
54
62
 
55
63
  ```python
56
64
  import logging
@@ -60,56 +68,87 @@ logger = logging.getLogger()
60
68
  formatter, stream_handler = init_logging(logger, 'json', version='0.0.1')
61
69
  ```
62
70
 
63
- - **pg**
71
+ ### pg
64
72
 
65
- Utility functions for [asyncpg](https://github.com/MagicStack/asyncpg)
73
+ Async PostgreSQL helpers using [asyncpg](https://github.com/MagicStack/asyncpg).
66
74
 
67
- - **pg-sync**
75
+ ```bash
76
+ uv add tracktolib[pg]
77
+ ```
68
78
 
69
- Utility functions based on psycopg such as `fetch_one`, `insert_many`, `fetch_count` ...
79
+ ### pg-sync
70
80
 
71
- To use the functions, create a `Connection` using psycopg: `conn = psycopg2.connect()`
81
+ Sync PostgreSQL helpers using [psycopg](https://www.psycopg.org/psycopg3/) (v3).
72
82
 
73
- *fetch_one*
83
+ ```bash
84
+ uv add tracktolib[pg-sync]
85
+ ```
74
86
 
75
87
  ```python
76
- from tracktolib.pg.pg_sync import (
77
- insert_many, fetch_one, fetch_count, fetch_all
78
- )
88
+ from psycopg import connect
89
+ from tracktolib.pg_sync import insert_many, fetch_one, fetch_count, fetch_all
90
+
91
+ conn = connect('postgresql://user:pass@localhost/db')
79
92
 
80
93
  data = [
81
94
  {'foo': 'bar', 'value': 1},
82
95
  {'foo': 'baz', 'value': 2}
83
96
  ]
84
- insert_many(conn, 'public.test', data) # Will insert the 2 dict
97
+ insert_many(conn, 'public.test', data)
98
+
85
99
  query = 'SELECT foo from public.test order by value asc'
86
- value = fetch_one(conn, query, required=True) # Will return {'foo': 'bar'}, raise an error is not found
100
+ value = fetch_one(conn, query, required=True) # {'foo': 'bar'}, raises if not found
101
+
87
102
  assert fetch_count(conn, 'public.test') == 2
103
+
88
104
  query = 'SELECT * from public.test order by value asc'
89
105
  assert fetch_all(conn, query) == data
106
+ ```
107
+
108
+ ### s3
109
+
110
+ Async S3 helpers using [aiobotocore](https://github.com/aio-libs/aiobotocore).
90
111
 
112
+ ```bash
113
+ uv add tracktolib[s3]
91
114
  ```
92
115
 
93
- - **tests**
116
+ ### s3-minio
94
117
 
95
- Utility functions for testing
118
+ S3 helpers using [minio](https://min.io/docs/minio/linux/developers/python/API.html).
96
119
 
97
- - **s3-minio**
120
+ ```bash
121
+ uv add tracktolib[s3-minio]
122
+ ```
98
123
 
99
- Utility functions for [minio](https://min.io/docs/minio/linux/developers/python/API.html)
124
+ ### http
100
125
 
101
- - **s3**
126
+ HTTP client helpers using [httpx](https://www.python-httpx.org/).
102
127
 
103
- Utility functions for [aiobotocore](https://github.com/aio-libs/aiobotocore)
128
+ ```bash
129
+ uv add tracktolib[http]
130
+ ```
104
131
 
105
- - **logs**
132
+ ### api
106
133
 
107
- Utility functions to initialize the logging formatting and streams
134
+ FastAPI utilities using [fastapi](https://fastapi.tiangolo.com/) and [pydantic](https://docs.pydantic.dev/).
108
135
 
109
- - **http**
136
+ ```bash
137
+ uv add tracktolib[api]
138
+ ```
110
139
 
111
- Utility functions using [httpx](https://www.python-httpx.org/)
140
+ ### notion
112
141
 
113
- - **api**
142
+ Notion API helpers using [niquests](https://github.com/jawah/niquests).
114
143
 
115
- Utility functions using [fastapi](https://fastapi.tiangolo.com/)
144
+ ```bash
145
+ uv add tracktolib[notion]
146
+ ```
147
+
148
+ ### tests
149
+
150
+ Testing utilities using [deepdiff](https://github.com/seperman/deepdiff).
151
+
152
+ ```bash
153
+ uv add tracktolib[tests]
154
+ ```
@@ -0,0 +1,118 @@
1
+ # Tracktolib
2
+
3
+ [![Python versions](https://img.shields.io/pypi/pyversions/tracktolib)](https://pypi.python.org/pypi/tracktolib)
4
+ [![Latest PyPI version](https://img.shields.io/pypi/v/tracktolib?logo=pypi)](https://pypi.python.org/pypi/tracktolib)
5
+ [![CircleCI](https://circleci.com/gh/Tracktor/tracktolib/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/Tracktor/tracktolib?branch=master)
6
+
7
+ Utility library for Python 3.12+
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ uv add tracktolib
13
+ ```
14
+
15
+ With specific extras:
16
+
17
+ ```bash
18
+ uv add tracktolib[pg,api]
19
+ ```
20
+
21
+ ## Modules
22
+
23
+ ### logs
24
+
25
+ Utility functions to initialize logging formatting and streams.
26
+
27
+ ```python
28
+ import logging
29
+ from tracktolib.logs import init_logging
30
+
31
+ logger = logging.getLogger()
32
+ formatter, stream_handler = init_logging(logger, 'json', version='0.0.1')
33
+ ```
34
+
35
+ ### pg
36
+
37
+ Async PostgreSQL helpers using [asyncpg](https://github.com/MagicStack/asyncpg).
38
+
39
+ ```bash
40
+ uv add tracktolib[pg]
41
+ ```
42
+
43
+ ### pg-sync
44
+
45
+ Sync PostgreSQL helpers using [psycopg](https://www.psycopg.org/psycopg3/) (v3).
46
+
47
+ ```bash
48
+ uv add tracktolib[pg-sync]
49
+ ```
50
+
51
+ ```python
52
+ from psycopg import connect
53
+ from tracktolib.pg_sync import insert_many, fetch_one, fetch_count, fetch_all
54
+
55
+ conn = connect('postgresql://user:pass@localhost/db')
56
+
57
+ data = [
58
+ {'foo': 'bar', 'value': 1},
59
+ {'foo': 'baz', 'value': 2}
60
+ ]
61
+ insert_many(conn, 'public.test', data)
62
+
63
+ query = 'SELECT foo from public.test order by value asc'
64
+ value = fetch_one(conn, query, required=True) # {'foo': 'bar'}, raises if not found
65
+
66
+ assert fetch_count(conn, 'public.test') == 2
67
+
68
+ query = 'SELECT * from public.test order by value asc'
69
+ assert fetch_all(conn, query) == data
70
+ ```
71
+
72
+ ### s3
73
+
74
+ Async S3 helpers using [aiobotocore](https://github.com/aio-libs/aiobotocore).
75
+
76
+ ```bash
77
+ uv add tracktolib[s3]
78
+ ```
79
+
80
+ ### s3-minio
81
+
82
+ S3 helpers using [minio](https://min.io/docs/minio/linux/developers/python/API.html).
83
+
84
+ ```bash
85
+ uv add tracktolib[s3-minio]
86
+ ```
87
+
88
+ ### http
89
+
90
+ HTTP client helpers using [httpx](https://www.python-httpx.org/).
91
+
92
+ ```bash
93
+ uv add tracktolib[http]
94
+ ```
95
+
96
+ ### api
97
+
98
+ FastAPI utilities using [fastapi](https://fastapi.tiangolo.com/) and [pydantic](https://docs.pydantic.dev/).
99
+
100
+ ```bash
101
+ uv add tracktolib[api]
102
+ ```
103
+
104
+ ### notion
105
+
106
+ Notion API helpers using [niquests](https://github.com/jawah/niquests).
107
+
108
+ ```bash
109
+ uv add tracktolib[notion]
110
+ ```
111
+
112
+ ### tests
113
+
114
+ Testing utilities using [deepdiff](https://github.com/seperman/deepdiff).
115
+
116
+ ```bash
117
+ uv add tracktolib[tests]
118
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tracktolib"
3
- version = "0.64.0"
3
+ version = "0.65.1"
4
4
  authors = [
5
5
  { email = "julien.brayere@tracktor.fr" }
6
6
  ]
@@ -11,7 +11,11 @@ homepage = "https://github.com/tracktor/tracktolib"
11
11
  repository = "https://github.com/tracktor/tracktolib"
12
12
  keywords = ["utility"]
13
13
  classifiers = [
14
- "Operating System :: OS Independent"
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
15
19
  ]
16
20
  include = [
17
21
  "LICENSE"
@@ -87,12 +91,12 @@ filterwarnings = [
87
91
  include = ["tracktolib", "tests"]
88
92
  exclude = []
89
93
 
90
- pythonVersion = "3.13"
94
+ pythonVersion = "3.14"
91
95
  pythonPlatform = "Linux"
92
96
 
93
97
  [tool.commitizen]
94
98
  name = "cz_conventional_commits"
95
- version = "0.64.0"
99
+ version = "0.65.1"
96
100
  tag_format = "$version"
97
101
  version_files = [
98
102
  "pyproject.toml:version"
@@ -102,7 +106,7 @@ bump_message = "release $current_version → $new_version [skip ci]"
102
106
 
103
107
  [tool.ruff]
104
108
  line-length = 120
105
- target-version = "py313"
109
+ target-version = "py314"
106
110
 
107
111
 
108
112
  [tool.ruff.lint.per-file-ignores]
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Any
2
+ from typing import Any, Literal
3
3
 
4
4
  try:
5
5
  import niquests
@@ -20,6 +20,19 @@ from .models import (
20
20
  UserListResponse,
21
21
  )
22
22
 
23
+ # API version constants
24
+ API_VERSION_2022_06_28 = "2022-06-28"
25
+ API_VERSION_2025_09_03 = "2025-09-03"
26
+ DEFAULT_API_VERSION = API_VERSION_2025_09_03
27
+
28
+ ApiVersion = Literal["2022-06-28", "2025-09-03"]
29
+
30
+
31
+ def _use_data_source_api(api_version: str) -> bool:
32
+ """Check if the API version uses data_source endpoints (2025-09-03+)."""
33
+ return api_version >= "2025-09-03"
34
+
35
+
23
36
  __all__ = (
24
37
  # Auth helpers
25
38
  "get_notion_headers",
@@ -181,6 +194,19 @@ async def fetch_page(session: niquests.AsyncSession, page_id: str) -> Page:
181
194
  return response.json() # type: ignore[return-value]
182
195
 
183
196
 
197
+ def _convert_parent_for_api_version(parent: dict[str, Any], api_version: str) -> dict[str, Any]:
198
+ """Convert parent dict between database_id and data_source_id based on API version."""
199
+ if _use_data_source_api(api_version):
200
+ # Convert database_id to data_source_id for new API
201
+ if "database_id" in parent:
202
+ return {"data_source_id": parent["database_id"]}
203
+ else:
204
+ # Convert data_source_id to database_id for old API
205
+ if "data_source_id" in parent:
206
+ return {"database_id": parent["data_source_id"]}
207
+ return parent
208
+
209
+
184
210
  async def create_page(
185
211
  session: niquests.AsyncSession,
186
212
  *,
@@ -189,10 +215,18 @@ async def create_page(
189
215
  children: list[dict[str, Any]] | None = None,
190
216
  icon: dict[str, Any] | None = None,
191
217
  cover: dict[str, Any] | None = None,
218
+ api_version: ApiVersion | None = None,
192
219
  ) -> Page:
193
- """Create a new page."""
220
+ """Create a new page.
221
+
222
+ For API version 2025-09-03+, parent should use {"data_source_id": "..."}.
223
+ For older API versions, parent should use {"database_id": "..."}.
224
+ The function will automatically convert between the two formats.
225
+ """
226
+ _api_version = api_version or session.headers.get("Notion-Version", DEFAULT_API_VERSION)
227
+ converted_parent = _convert_parent_for_api_version(parent, _api_version)
194
228
  payload: dict[str, Any] = {
195
- "parent": parent,
229
+ "parent": converted_parent,
196
230
  "properties": properties,
197
231
  }
198
232
  if children:
@@ -235,9 +269,24 @@ async def update_page(
235
269
  # Databases endpoints
236
270
 
237
271
 
238
- async def fetch_database(session: niquests.AsyncSession, database_id: str) -> Database:
239
- """Retrieve a database by ID."""
240
- response = await session.get(f"{NOTION_API_URL}/v1/databases/{database_id}")
272
+ async def fetch_database(
273
+ session: niquests.AsyncSession,
274
+ database_id: str,
275
+ *,
276
+ api_version: ApiVersion | None = None,
277
+ ) -> Database:
278
+ """Retrieve a database/data source by ID.
279
+
280
+ For API version 2025-09-03+, uses /v1/data_sources/{id} endpoint.
281
+ For older API versions, uses /v1/databases/{id} endpoint.
282
+ """
283
+ _api_version = api_version or session.headers.get("Notion-Version", DEFAULT_API_VERSION)
284
+ if _use_data_source_api(_api_version):
285
+ endpoint = f"{NOTION_API_URL}/v1/data_sources/{database_id}"
286
+ else:
287
+ endpoint = f"{NOTION_API_URL}/v1/databases/{database_id}"
288
+
289
+ response = await session.get(endpoint)
241
290
  response.raise_for_status()
242
291
  return response.json() # type: ignore[return-value]
243
292
 
@@ -250,8 +299,14 @@ async def query_database(
250
299
  sorts: list[dict[str, Any]] | None = None,
251
300
  start_cursor: str | None = None,
252
301
  page_size: int | None = None,
302
+ api_version: ApiVersion | None = None,
253
303
  ) -> PageListResponse:
254
- """Query a database."""
304
+ """Query a database/data source.
305
+
306
+ For API version 2025-09-03+, uses /v1/data_sources/{id}/query endpoint.
307
+ For older API versions, uses /v1/databases/{id}/query endpoint.
308
+ """
309
+ _api_version = api_version or session.headers.get("Notion-Version", DEFAULT_API_VERSION)
255
310
  payload: dict[str, Any] = {}
256
311
  if filter:
257
312
  payload["filter"] = filter
@@ -262,7 +317,12 @@ async def query_database(
262
317
  if page_size:
263
318
  payload["page_size"] = page_size
264
319
 
265
- response = await session.post(f"{NOTION_API_URL}/v1/databases/{database_id}/query", json=payload or None)
320
+ if _use_data_source_api(_api_version):
321
+ endpoint = f"{NOTION_API_URL}/v1/data_sources/{database_id}/query"
322
+ else:
323
+ endpoint = f"{NOTION_API_URL}/v1/databases/{database_id}/query"
324
+
325
+ response = await session.post(endpoint, json=payload or None)
266
326
  response.raise_for_status()
267
327
  return response.json() # type: ignore[return-value]
268
328
 
@@ -312,6 +372,23 @@ async def fetch_append_block_children(
312
372
  # Search endpoint
313
373
 
314
374
 
375
+ def _convert_search_filter_for_api_version(filter: dict[str, Any], api_version: str) -> dict[str, Any]:
376
+ """Convert search filter value between 'database' and 'data_source' based on API version."""
377
+ if "value" not in filter:
378
+ return filter
379
+
380
+ filter_copy = filter.copy()
381
+ if _use_data_source_api(api_version):
382
+ # Convert 'database' to 'data_source' for new API
383
+ if filter_copy.get("value") == "database":
384
+ filter_copy["value"] = "data_source"
385
+ else:
386
+ # Convert 'data_source' to 'database' for old API
387
+ if filter_copy.get("value") == "data_source":
388
+ filter_copy["value"] = "database"
389
+ return filter_copy
390
+
391
+
315
392
  async def fetch_search(
316
393
  session: niquests.AsyncSession,
317
394
  *,
@@ -320,13 +397,20 @@ async def fetch_search(
320
397
  sort: dict[str, Any] | None = None,
321
398
  start_cursor: str | None = None,
322
399
  page_size: int | None = None,
400
+ api_version: ApiVersion | None = None,
323
401
  ) -> SearchResponse:
324
- """Search pages and databases."""
402
+ """Search pages and databases/data sources.
403
+
404
+ For API version 2025-09-03+, filter value 'database' is automatically
405
+ converted to 'data_source'. For older versions, 'data_source' is
406
+ converted to 'database'.
407
+ """
408
+ _api_version = api_version or session.headers.get("Notion-Version", DEFAULT_API_VERSION)
325
409
  payload: dict[str, Any] = {}
326
410
  if query:
327
411
  payload["query"] = query
328
412
  if filter:
329
- payload["filter"] = filter
413
+ payload["filter"] = _convert_search_filter_for_api_version(filter, _api_version)
330
414
  if sort:
331
415
  payload["sort"] = sort
332
416
  if start_cursor:
@@ -126,12 +126,19 @@ class PageParent(TypedDict):
126
126
 
127
127
 
128
128
  class DatabaseParent(TypedDict):
129
- """Database parent."""
129
+ """Database parent (deprecated in API 2025-09-03, use DataSourceParent)."""
130
130
 
131
131
  type: Literal["database_id"]
132
132
  database_id: str
133
133
 
134
134
 
135
+ class DataSourceParent(TypedDict):
136
+ """Data source parent (API 2025-09-03+)."""
137
+
138
+ type: Literal["data_source_id"]
139
+ data_source_id: str
140
+
141
+
135
142
  class WorkspaceParent(TypedDict):
136
143
  """Workspace parent."""
137
144
 
@@ -146,7 +153,7 @@ class BlockParent(TypedDict):
146
153
  block_id: str
147
154
 
148
155
 
149
- Parent = PageParent | DatabaseParent | WorkspaceParent | BlockParent
156
+ Parent = PageParent | DatabaseParent | DataSourceParent | WorkspaceParent | BlockParent
150
157
 
151
158
 
152
159
  # Page types
@@ -1,83 +0,0 @@
1
- # Tracktolib
2
-
3
- [![Python versions](https://img.shields.io/pypi/pyversions/tracktolib)](https://pypi.python.org/pypi/tracktolib)
4
- [![Latest PyPI version](https://img.shields.io/pypi/v/tracktolib?logo=pypi)](https://pypi.python.org/pypi/tracktolib)
5
- [![CircleCI](https://circleci.com/gh/Tracktor/tracktolib/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/Tracktor/tracktolib?branch=master)
6
-
7
- Utility library for python
8
-
9
- # Installation
10
-
11
- Just run:
12
-
13
- ```bash
14
- uv add tracktolib@latest
15
- ```
16
-
17
- # Utilities
18
-
19
- - **log**
20
-
21
- Utility functions for logging.
22
-
23
- ```python
24
- import logging
25
- from tracktolib.logs import init_logging
26
-
27
- logger = logging.getLogger()
28
- formatter, stream_handler = init_logging(logger, 'json', version='0.0.1')
29
- ```
30
-
31
- - **pg**
32
-
33
- Utility functions for [asyncpg](https://github.com/MagicStack/asyncpg)
34
-
35
- - **pg-sync**
36
-
37
- Utility functions based on psycopg such as `fetch_one`, `insert_many`, `fetch_count` ...
38
-
39
- To use the functions, create a `Connection` using psycopg: `conn = psycopg2.connect()`
40
-
41
- *fetch_one*
42
-
43
- ```python
44
- from tracktolib.pg.pg_sync import (
45
- insert_many, fetch_one, fetch_count, fetch_all
46
- )
47
-
48
- data = [
49
- {'foo': 'bar', 'value': 1},
50
- {'foo': 'baz', 'value': 2}
51
- ]
52
- insert_many(conn, 'public.test', data) # Will insert the 2 dict
53
- query = 'SELECT foo from public.test order by value asc'
54
- value = fetch_one(conn, query, required=True) # Will return {'foo': 'bar'}, raise an error is not found
55
- assert fetch_count(conn, 'public.test') == 2
56
- query = 'SELECT * from public.test order by value asc'
57
- assert fetch_all(conn, query) == data
58
-
59
- ```
60
-
61
- - **tests**
62
-
63
- Utility functions for testing
64
-
65
- - **s3-minio**
66
-
67
- Utility functions for [minio](https://min.io/docs/minio/linux/developers/python/API.html)
68
-
69
- - **s3**
70
-
71
- Utility functions for [aiobotocore](https://github.com/aio-libs/aiobotocore)
72
-
73
- - **logs**
74
-
75
- Utility functions to initialize the logging formatting and streams
76
-
77
- - **http**
78
-
79
- Utility functions using [httpx](https://www.python-httpx.org/)
80
-
81
- - **api**
82
-
83
- Utility functions using [fastapi](https://fastapi.tiangolo.com/)