tracktolib 0.51.0__py3-none-any.whl → 0.52.0__py3-none-any.whl

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.
tracktolib/pg/query.py CHANGED
@@ -206,10 +206,6 @@ def get_update_fields(
206
206
  _merge_keys = set(merge_keys or [])
207
207
  _ignore_keys = ignore_keys or []
208
208
 
209
- _invalid_merge_keys = _merge_keys - set(keys)
210
- if _invalid_merge_keys:
211
- raise ValueError(f"Merge keys not in keys found: {_invalid_merge_keys}")
212
-
213
209
  for k in keys:
214
210
  v = item[k]
215
211
  if k in _ignore_keys:
@@ -397,8 +393,6 @@ async def update_one(
397
393
  query = PGUpdateQuery(
398
394
  table=table, items=[item], start_from=start_from, where_keys=keys, where=where, merge_keys=merge_keys
399
395
  )
400
- print(query.query)
401
- print(query.values)
402
396
  await conn.execute(query.query, *args, *query.values)
403
397
 
404
398
 
tracktolib/s3/s3.py CHANGED
@@ -1,7 +1,7 @@
1
+ import datetime as dt
1
2
  from io import BytesIO
2
3
  from pathlib import Path
3
- import datetime as dt
4
- from typing import TypedDict, Literal
4
+ from typing import TypedDict, Literal, Callable
5
5
 
6
6
  try:
7
7
  from aiobotocore.client import AioBaseClient
@@ -36,18 +36,48 @@ async def upload_file(
36
36
  return resp
37
37
 
38
38
 
39
- async def download_file(client: AioBaseClient, bucket: str, path: str) -> BytesIO | None:
39
+ type ContentLength = int
40
+ type ChunkSize = int
41
+ type OnUpdateDownload = Callable[[ChunkSize], None]
42
+ type OnStartDownload = Callable[[ContentLength], None]
43
+
44
+
45
+ async def download_file(
46
+ client: AioBaseClient,
47
+ bucket: str,
48
+ path: str,
49
+ *,
50
+ chunk_size: int = -1,
51
+ on_start: OnStartDownload | None = None,
52
+ on_update: OnUpdateDownload | None = None,
53
+ ) -> BytesIO | None:
40
54
  """
41
- Loads a file from a s3 bucket
55
+ Loads a file from a s3 bucket.
56
+ If chunk_size is -1, the file will be loaded in one go
57
+ otherwise, the file will be loaded in chunks of size `chunk_size`.
58
+ When downloading in chunked, you can specify an `on_start` and `on_update`
59
+ callback to get the total size of the file and the size of each chunk downloaded respectively.
42
60
  """
43
61
  try:
44
62
  resp = await client.get_object(Bucket=bucket, Key=path) # type: ignore
45
63
  except client.exceptions.NoSuchKey:
46
- _file = None
47
- else:
48
- async with resp["Body"] as stream:
64
+ return None
65
+
66
+ if on_start is not None:
67
+ on_start(resp["ContentLength"])
68
+
69
+ async with resp["Body"] as stream:
70
+ if chunk_size == -1:
49
71
  _data = await stream.read()
50
72
  _file = BytesIO(_data)
73
+ else:
74
+ chunks = []
75
+ while chunk := await stream.content.read(chunk_size):
76
+ chunks.append(chunk)
77
+ if on_update is not None:
78
+ on_update(len(chunk))
79
+ _file = BytesIO(b"".join(chunks)) if chunks else None
80
+
51
81
  return _file
52
82
 
53
83
 
tracktolib/utils.py CHANGED
@@ -10,20 +10,49 @@ import subprocess
10
10
  from decimal import Decimal
11
11
  from ipaddress import IPv4Address, IPv6Address
12
12
  from pathlib import Path
13
- from typing import Iterable, TypeVar, Iterator, Literal, overload, Any
13
+ from typing import Iterable, TypeVar, Iterator, Literal, overload, Any, Callable
14
+
14
15
 
15
16
  T = TypeVar("T")
16
17
 
18
+ type OnCmdUpdate = Callable[[str], None]
19
+ type OnCmdDone = Callable[[str, str, int], None]
20
+
17
21
 
18
- def exec_cmd(cmd: str | list[str], *, encoding: str = "utf-8", env: dict | None = None) -> str:
22
+ def exec_cmd(
23
+ cmd: str | list[str],
24
+ *,
25
+ env: dict | None = None,
26
+ on_update: OnCmdUpdate | None = None,
27
+ on_done: OnCmdDone | None = None,
28
+ **kwargs,
29
+ ) -> str:
19
30
  default_shell = os.getenv("SHELL", "/bin/bash")
20
31
 
21
- stdout, stderr = subprocess.Popen(
22
- cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable=default_shell, env=env
23
- ).communicate()
24
- if stderr:
25
- raise Exception(stderr.decode(encoding))
26
- return stdout.decode(encoding)
32
+ process = subprocess.Popen(
33
+ cmd,
34
+ shell=True,
35
+ stdout=subprocess.PIPE,
36
+ stderr=subprocess.PIPE,
37
+ executable=default_shell,
38
+ env=env,
39
+ text=True,
40
+ **kwargs,
41
+ )
42
+
43
+ if on_update is not None:
44
+ for line in process.stderr or []:
45
+ on_update(line)
46
+
47
+ stdout, stderr = process.communicate()
48
+ exit_code = process.wait()
49
+
50
+ if on_done is not None:
51
+ on_done(stdout, stderr, exit_code)
52
+
53
+ if exit_code != 0:
54
+ raise Exception(stderr)
55
+ return stdout
27
56
 
28
57
 
29
58
  async def aexec_cmd(cmd: str | list[str], *, encoding: str = "utf-8", env: dict | None = None) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tracktolib
3
- Version: 0.51.0
3
+ Version: 0.52.0
4
4
  Summary: Utility library for python
5
5
  Home-page: https://github.com/tracktor/tracktolib
6
6
  License: MIT
@@ -12,6 +12,7 @@ Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
15
16
  Provides-Extra: api
16
17
  Provides-Extra: http
17
18
  Provides-Extra: logs
@@ -4,16 +4,16 @@ tracktolib/api.py,sha256=xArVgRj_g7bw2tEDbjC9qs9f55b9X0-kJoc8-s1rsYo,9616
4
4
  tracktolib/http_utils.py,sha256=c10JGmHaBw3VSDMYhz2dvVw2lo4PUAq1xMub74I7xDc,2625
5
5
  tracktolib/logs.py,sha256=M5RZ8OYKgLEBJeC1AwUCSRbFAH09hwdTjjs-AQ9QGg8,2204
6
6
  tracktolib/pg/__init__.py,sha256=j67e3B3gBbCHLD20QBybptmNdbbVMzNhZE6XjIPuKVo,349
7
- tracktolib/pg/query.py,sha256=XX5eD02TRWRIiBVj0ENooQ893aycan2fJ55_1J207Rw,14711
7
+ tracktolib/pg/query.py,sha256=MBYOTL9aLnYRBhaLy8-P6uGNXOmyluejNyUdt3Gfx5M,14504
8
8
  tracktolib/pg/utils.py,sha256=cL24KEt4SWJQ7LJPzaO3c8Xg0ZLmjhn22DtTWg86nwc,6324
9
9
  tracktolib/pg_sync.py,sha256=z6EB0hELq6t129gnVnX0rNXv2McZXxI8khy-TEG65Ag,5165
10
10
  tracktolib/pg_utils.py,sha256=VXPpy1jGq6aCgTlfFJDIrq6JDujR83JN5ZRiCi8Lx4E,2582
11
11
  tracktolib/s3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  tracktolib/s3/minio.py,sha256=wMEjkSes9Fp39fD17IctALpD6zB2xwDRQEmO7Vzan3g,1387
13
- tracktolib/s3/s3.py,sha256=d0Q63Zb62ef4jAt05zQwpgYvAoxHC9kSQuHzzK90VvE,4825
13
+ tracktolib/s3/s3.py,sha256=I2fT3C9pIm9pM0V96O_wbYg_bPJX35ryzx05G3EgZus,5816
14
14
  tracktolib/tests.py,sha256=Pbc4yGQrIWFLBEgX-kDBxhMCqH-bfOJtkH2agvaM6ZQ,382
15
- tracktolib/utils.py,sha256=jwLww8bqDu8zEip9uN4yW0lE5_YMWfrAHYtagr8sYOA,5295
16
- tracktolib-0.51.0.dist-info/LICENSE,sha256=uUanH0X7SeZEPdsRTHegMSMTiIHMurt9H0jSwEwKE1Y,1081
17
- tracktolib-0.51.0.dist-info/METADATA,sha256=v2MsoN7EIuclo58jhPp40YJespn9Oy4rYKM8kg7VRsQ,3641
18
- tracktolib-0.51.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
19
- tracktolib-0.51.0.dist-info/RECORD,,
15
+ tracktolib/utils.py,sha256=ysTBF9V35fVXQVBPk0kfE_84SGRxzrayqmg9RbtoJq4,5761
16
+ tracktolib-0.52.0.dist-info/LICENSE,sha256=uUanH0X7SeZEPdsRTHegMSMTiIHMurt9H0jSwEwKE1Y,1081
17
+ tracktolib-0.52.0.dist-info/METADATA,sha256=peqROd6d1t-a0mXJFJjgqy0mc7trrpLb8-l4nYANf5s,3692
18
+ tracktolib-0.52.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
19
+ tracktolib-0.52.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any