python3-commons 0.5.14__tar.gz → 0.5.16__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.

Potentially problematic release.


This version of python3-commons might be problematic. Click here for more details.

Files changed (46) hide show
  1. {python3_commons-0.5.14/src/python3_commons.egg-info → python3_commons-0.5.16}/PKG-INFO +3 -3
  2. {python3_commons-0.5.14 → python3_commons-0.5.16}/requirements.txt +2 -2
  3. {python3_commons-0.5.14 → python3_commons-0.5.16}/setup.cfg +3 -3
  4. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/audit.py +29 -30
  5. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/helpers.py +17 -1
  6. {python3_commons-0.5.14 → python3_commons-0.5.16/src/python3_commons.egg-info}/PKG-INFO +3 -3
  7. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/SOURCES.txt +1 -0
  8. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/requires.txt +2 -2
  9. {python3_commons-0.5.14 → python3_commons-0.5.16}/tests/conftest.py +10 -0
  10. python3_commons-0.5.16/tests/test_audit.py +10 -0
  11. {python3_commons-0.5.14 → python3_commons-0.5.16}/.coveragerc +0 -0
  12. {python3_commons-0.5.14 → python3_commons-0.5.16}/.github/workflows/python-publish.yaml +0 -0
  13. {python3_commons-0.5.14 → python3_commons-0.5.16}/.gitignore +0 -0
  14. {python3_commons-0.5.14 → python3_commons-0.5.16}/AUTHORS.rst +0 -0
  15. {python3_commons-0.5.14 → python3_commons-0.5.16}/CHANGELOG.rst +0 -0
  16. {python3_commons-0.5.14 → python3_commons-0.5.16}/LICENSE +0 -0
  17. {python3_commons-0.5.14 → python3_commons-0.5.16}/README.md +0 -0
  18. {python3_commons-0.5.14 → python3_commons-0.5.16}/README.rst +0 -0
  19. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/Makefile +0 -0
  20. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/_static/.gitignore +0 -0
  21. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/authors.rst +0 -0
  22. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/changelog.rst +0 -0
  23. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/conf.py +0 -0
  24. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/index.rst +0 -0
  25. {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/license.rst +0 -0
  26. {python3_commons-0.5.14 → python3_commons-0.5.16}/pyproject.toml +0 -0
  27. {python3_commons-0.5.14 → python3_commons-0.5.16}/requirements_dev.txt +0 -0
  28. {python3_commons-0.5.14 → python3_commons-0.5.16}/requirements_test.txt +0 -0
  29. {python3_commons-0.5.14 → python3_commons-0.5.16}/setup.py +0 -0
  30. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/__init__.py +0 -0
  31. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/conf.py +0 -0
  32. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/db.py +0 -0
  33. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/fs.py +0 -0
  34. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/logging/__init__.py +0 -0
  35. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/logging/filters.py +0 -0
  36. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/logging/formatter.py +0 -0
  37. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/object_storage.py +0 -0
  38. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/__init__.py +0 -0
  39. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/json.py +0 -0
  40. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/msgpack.py +0 -0
  41. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/msgspec.py +0 -0
  42. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/dependency_links.txt +0 -0
  43. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/not-zip-safe +0 -0
  44. {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/top_level.txt +0 -0
  45. {python3_commons-0.5.14 → python3_commons-0.5.16}/tests/test_msgpack.py +0 -0
  46. {python3_commons-0.5.14 → python3_commons-0.5.16}/tests/test_msgspec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python3-commons
3
- Version: 0.5.14
3
+ Version: 0.5.16
4
4
  Summary: Re-usable Python3 code
5
5
  Home-page: https://github.com/kamikaze/python3-commons
6
6
  Author: Oleg Korsak
@@ -19,8 +19,8 @@ Requires-Dist: lxml==5.2.2
19
19
  Requires-Dist: minio==7.2.7
20
20
  Requires-Dist: msgpack==1.0.8
21
21
  Requires-Dist: msgspec==0.18.6
22
- Requires-Dist: pydantic[email]==2.7.2
23
- Requires-Dist: pydantic-settings==2.2.1
22
+ Requires-Dist: pydantic[email]==2.8.2
23
+ Requires-Dist: pydantic-settings==2.3.4
24
24
  Requires-Dist: zeep==4.2.1
25
25
  Provides-Extra: testing
26
26
  Requires-Dist: pytest; extra == "testing"
@@ -3,6 +3,6 @@ lxml==5.2.2
3
3
  minio==7.2.7
4
4
  msgpack==1.0.8
5
5
  msgspec==0.18.6
6
- pydantic[email]==2.7.2
7
- pydantic-settings==2.2.1
6
+ pydantic[email]==2.8.2
7
+ pydantic-settings==2.3.4
8
8
  zeep==4.2.1
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = python3-commons
3
- version = 0.5.14
3
+ version = 0.5.16
4
4
  description = Re-usable Python3 code
5
5
  author = Oleg Korsak
6
6
  author_email = kamikaze.is.waiting.you@gmail.com
@@ -28,8 +28,8 @@ install_requires =
28
28
  minio==7.2.7
29
29
  msgpack==1.0.8
30
30
  msgspec==0.18.6
31
- pydantic[email]==2.7.2
32
- pydantic-settings==2.2.1
31
+ pydantic[email]==2.8.2
32
+ pydantic-settings==2.3.4
33
33
  zeep==4.2.1
34
34
  python_requires = >=3.12
35
35
 
@@ -3,8 +3,7 @@ import io
3
3
  import logging
4
4
  import tarfile
5
5
  from datetime import datetime, timedelta, UTC
6
- from io import BytesIO
7
- from typing import Generator
6
+ from typing import Generator, Iterable
8
7
  from uuid import uuid4
9
8
 
10
9
  from lxml import etree
@@ -19,7 +18,7 @@ from python3_commons.object_storage import get_s3_client
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
20
 
22
- class BytesIOStream(io.BytesIO):
21
+ class GeneratedStream(io.BytesIO):
23
22
  def __init__(self, generator: Generator[bytes, None, None], *args, **kwargs):
24
23
  super().__init__(*args, **kwargs)
25
24
  self.generator = generator
@@ -54,33 +53,30 @@ class BytesIOStream(io.BytesIO):
54
53
  return True
55
54
 
56
55
 
57
- def generate_archive(bucket_name: str, date_path: str, chunk_size: int = 4096) -> Generator[bytes, None, None]:
56
+ def generate_archive(objects: Iterable[tuple[str, datetime, bytes]],
57
+ chunk_size: int = 4096) -> Generator[bytes, None, None]:
58
58
  buffer = io.BytesIO()
59
59
 
60
- with tarfile.open(fileobj=buffer, mode='w|bz2') as archive:
61
- objects = object_storage.get_objects(bucket_name, date_path, recursive=True)
60
+ with tarfile.open(fileobj=buffer, mode='w') as archive:
61
+ for name, last_modified, content in objects:
62
+ logger.info(f'Adding {name} to archive')
63
+ info = tarfile.TarInfo(name)
64
+ info.size = len(content)
65
+ info.mtime = last_modified.timestamp()
66
+ archive.addfile(info, io.BytesIO(content))
62
67
 
63
- if objects:
64
- logger.info(f'Compacting files in: {date_path}')
68
+ buffer.seek(0)
65
69
 
66
- for name, last_modified, content in objects:
67
- logger.info(f'Adding {name} to archive')
68
- info = tarfile.TarInfo(name)
69
- info.size = len(content)
70
- info.mtime = last_modified.timestamp()
71
- archive.addfile(info, io.BytesIO(content))
72
- buffer.seek(0)
70
+ while True:
71
+ chunk = buffer.read(chunk_size)
73
72
 
74
- while True:
75
- chunk = buffer.read(chunk_size)
73
+ if not chunk:
74
+ break
76
75
 
77
- if not chunk:
78
- break
76
+ yield chunk
79
77
 
80
- yield chunk
81
-
82
- buffer.seek(0)
83
- buffer.truncate(0)
78
+ buffer.seek(0)
79
+ buffer.truncate(0)
84
80
 
85
81
 
86
82
  def write_audit_data_sync(settings: S3Settings, key: str, data: bytes):
@@ -110,15 +106,18 @@ async def archive_audit_data(root_path: str = 'audit'):
110
106
  bucket_name = s3_settings.s3_bucket
111
107
  date_path = object_storage.get_absolute_path(f'{root_path}/{year}/{month:02}/{day:02}')
112
108
 
113
- generator = generate_archive(bucket_name, date_path, chunk_size=5*1024*1024)
114
- archive_stream = BytesIOStream(generator)
109
+ if objects := object_storage.get_objects(bucket_name, date_path, recursive=True):
110
+ logger.info(f'Compacting files in: {date_path}')
111
+
112
+ generator = generate_archive(objects, chunk_size=5*1024*1024)
113
+ archive_stream = GeneratedStream(generator)
115
114
 
116
- archive_path = object_storage.get_absolute_path(f'audit/.archive/{year}_{month:02}_{day:02}.tar.bz2')
117
- object_storage.put_object(bucket_name, archive_path, archive_stream, -1, part_size=5*1024*1024)
115
+ archive_path = object_storage.get_absolute_path(f'audit/.archive/{year}_{month:02}_{day:02}.tar.bz2')
116
+ object_storage.put_object(bucket_name, archive_path, archive_stream, -1, part_size=5*1024*1024)
118
117
 
119
- if errors := object_storage.remove_objects(bucket_name, date_path):
120
- for error in errors:
121
- logger.error(f'Failed to delete object in {bucket_name=}: {error}')
118
+ if errors := object_storage.remove_objects(bucket_name, date_path):
119
+ for error in errors:
120
+ logger.error(f'Failed to delete object in {bucket_name=}: {error}')
122
121
 
123
122
 
124
123
  class ZeepAuditPlugin(Plugin):
@@ -1,8 +1,9 @@
1
1
  import datetime
2
2
  import logging
3
+ import shlex
3
4
 
4
5
  from decimal import Decimal, ROUND_HALF_UP
5
-
6
+ from typing import Mapping
6
7
 
7
8
  logger = logging.getLogger(__name__)
8
9
 
@@ -47,3 +48,18 @@ def round_decimal(value: Decimal, decimal_places=2, rounding_mode=ROUND_HALF_UP)
47
48
  return value.quantize(Decimal(10) ** -decimal_places, rounding=rounding_mode)
48
49
  except AttributeError:
49
50
  return value
51
+
52
+
53
+ def request_to_curl(url: str, method: str, headers: Mapping, body: bytes | None = None) -> str:
54
+ curl_cmd = ['curl', '-i', '-X', method, shlex.quote(url)]
55
+
56
+ for key, value in headers.items():
57
+ header_line = f'{key}: {value}'
58
+ curl_cmd.append('-H')
59
+ curl_cmd.append(shlex.quote(header_line))
60
+
61
+ if body is not None:
62
+ curl_cmd.append('--data')
63
+ curl_cmd.append(shlex.quote(body.decode('utf-8')))
64
+
65
+ return ' '.join(curl_cmd)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python3-commons
3
- Version: 0.5.14
3
+ Version: 0.5.16
4
4
  Summary: Re-usable Python3 code
5
5
  Home-page: https://github.com/kamikaze/python3-commons
6
6
  Author: Oleg Korsak
@@ -19,8 +19,8 @@ Requires-Dist: lxml==5.2.2
19
19
  Requires-Dist: minio==7.2.7
20
20
  Requires-Dist: msgpack==1.0.8
21
21
  Requires-Dist: msgspec==0.18.6
22
- Requires-Dist: pydantic[email]==2.7.2
23
- Requires-Dist: pydantic-settings==2.2.1
22
+ Requires-Dist: pydantic[email]==2.8.2
23
+ Requires-Dist: pydantic-settings==2.3.4
24
24
  Requires-Dist: zeep==4.2.1
25
25
  Provides-Extra: testing
26
26
  Requires-Dist: pytest; extra == "testing"
@@ -40,5 +40,6 @@ src/python3_commons/serializers/json.py
40
40
  src/python3_commons/serializers/msgpack.py
41
41
  src/python3_commons/serializers/msgspec.py
42
42
  tests/conftest.py
43
+ tests/test_audit.py
43
44
  tests/test_msgpack.py
44
45
  tests/test_msgspec.py
@@ -3,8 +3,8 @@ lxml==5.2.2
3
3
  minio==7.2.7
4
4
  msgpack==1.0.8
5
5
  msgspec==0.18.6
6
- pydantic[email]==2.7.2
7
- pydantic-settings==2.2.1
6
+ pydantic[email]==2.8.2
7
+ pydantic-settings==2.3.4
8
8
  zeep==4.2.1
9
9
 
10
10
  [testing]
@@ -59,3 +59,13 @@ def data_struct():
59
59
  e=date(2023, 7, 24),
60
60
  f=Decimal('1.23')
61
61
  )
62
+
63
+
64
+ @pytest.fixture
65
+ def s3_file_objects() -> tuple:
66
+ return (
67
+ ('file_a.txt', datetime(2024, 1, 1), b'ABCDE', ),
68
+ ('file_b.txt', datetime(2024, 1, 2), b'FGHIJ', ),
69
+ ('file_c.txt', datetime(2024, 1, 3), b'KLMNO', ),
70
+ ('file_d.txt', datetime(2024, 1, 4), b'PQRST', ),
71
+ )
@@ -0,0 +1,10 @@
1
+ from python3_commons.audit import GeneratedStream, generate_archive
2
+
3
+
4
+ def test_generated_stream(s3_file_objects):
5
+ expected_data = b''
6
+ generator = generate_archive(s3_file_objects, chunk_size=5 * 1024 * 1024)
7
+ archive_stream = GeneratedStream(generator)
8
+ archived_data = archive_stream.read()
9
+
10
+ assert archived_data == expected_data