tracktolib 0.51.1__tar.gz → 0.52.0__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.
- {tracktolib-0.51.1 → tracktolib-0.52.0}/PKG-INFO +2 -1
- {tracktolib-0.51.1 → tracktolib-0.52.0}/pyproject.toml +5 -2
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/s3/s3.py +37 -7
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/utils.py +37 -8
- {tracktolib-0.51.1 → tracktolib-0.52.0}/LICENSE +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/README.md +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/__init__.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/api.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/http_utils.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/logs.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/pg/__init__.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/pg/query.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/pg/utils.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/pg_sync.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/pg_utils.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/s3/__init__.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/s3/minio.py +0 -0
- {tracktolib-0.51.1 → tracktolib-0.52.0}/tracktolib/tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tracktolib
|
|
3
|
-
Version: 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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tracktolib"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.52.0"
|
|
4
4
|
description = "Utility library for python"
|
|
5
5
|
authors = ["Julien Brayere <julien.brayere@tracktor.fr>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -56,6 +56,9 @@ psycopg = "^3.2.1"
|
|
|
56
56
|
minio = "^7.2.7"
|
|
57
57
|
pycryptodome = "^3.20.0"
|
|
58
58
|
aiobotocore = "^2.13.1"
|
|
59
|
+
deepdiff = "^8.0.1"
|
|
60
|
+
python-json-logger = "^3.2.0"
|
|
61
|
+
|
|
59
62
|
|
|
60
63
|
[build-system]
|
|
61
64
|
requires = ["poetry-core"]
|
|
@@ -81,7 +84,7 @@ pythonPlatform = "Linux"
|
|
|
81
84
|
|
|
82
85
|
[tool.commitizen]
|
|
83
86
|
name = "cz_conventional_commits"
|
|
84
|
-
version = "0.
|
|
87
|
+
version = "0.52.0"
|
|
85
88
|
tag_format = "$version"
|
|
86
89
|
version_files = [
|
|
87
90
|
"pyproject.toml:version"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import datetime as dt
|
|
1
2
|
from io import BytesIO
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
|
@@ -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(
|
|
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
|
-
|
|
22
|
-
cmd,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|