python3-commons 0.0.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.
Potentially problematic release.
This version of python3-commons might be problematic. Click here for more details.
- python3_commons/__init__.py +7 -0
- python3_commons/api_client.py +120 -0
- python3_commons/audit.py +196 -0
- python3_commons/auth.py +82 -0
- python3_commons/cache.py +261 -0
- python3_commons/conf.py +52 -0
- python3_commons/db/__init__.py +83 -0
- python3_commons/db/helpers.py +62 -0
- python3_commons/db/models/__init__.py +2 -0
- python3_commons/db/models/auth.py +35 -0
- python3_commons/db/models/common.py +39 -0
- python3_commons/db/models/rbac.py +91 -0
- python3_commons/fs.py +10 -0
- python3_commons/helpers.py +108 -0
- python3_commons/logging/__init__.py +0 -0
- python3_commons/logging/filters.py +10 -0
- python3_commons/logging/formatters.py +25 -0
- python3_commons/object_storage.py +127 -0
- python3_commons/permissions.py +48 -0
- python3_commons/serializers/__init__.py +0 -0
- python3_commons/serializers/json.py +26 -0
- python3_commons/serializers/msgpack.py +50 -0
- python3_commons/serializers/msgspec.py +73 -0
- python3_commons-0.0.0.dist-info/METADATA +34 -0
- python3_commons-0.0.0.dist-info/RECORD +29 -0
- python3_commons-0.0.0.dist-info/WHEEL +5 -0
- python3_commons-0.0.0.dist-info/licenses/AUTHORS.rst +5 -0
- python3_commons-0.0.0.dist-info/licenses/LICENSE +604 -0
- python3_commons-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
import sqlalchemy as sa
|
|
5
|
+
from sqlalchemy import and_, exists, func
|
|
6
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
7
|
+
|
|
8
|
+
from python3_commons.db.models import RBACApiKeyRole, RBACPermission, RBACRolePermission, RBACUserRole
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def has_api_key_permission(session: AsyncSession, api_key_uid: UUID, permission: str) -> bool:
|
|
14
|
+
query = sa.select(
|
|
15
|
+
exists().where(
|
|
16
|
+
and_(
|
|
17
|
+
RBACApiKeyRole.api_key_uid == api_key_uid,
|
|
18
|
+
(RBACApiKeyRole.expires_at.is_(None) | (RBACApiKeyRole.expires_at > func.now())),
|
|
19
|
+
RBACApiKeyRole.role_uid == RBACRolePermission.role_uid,
|
|
20
|
+
RBACRolePermission.permission_uid == RBACPermission.uid,
|
|
21
|
+
RBACPermission.name == permission,
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
cursor = await session.execute(query)
|
|
27
|
+
result = cursor.scalar()
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def has_user_permission(session: AsyncSession, user_id: UUID, permission: str) -> bool:
|
|
33
|
+
query = sa.select(
|
|
34
|
+
exists().where(
|
|
35
|
+
and_(
|
|
36
|
+
RBACUserRole.user_id == user_id,
|
|
37
|
+
(RBACUserRole.expires_at.is_(None) | (RBACUserRole.expires_at > func.now())),
|
|
38
|
+
RBACUserRole.role_uid == RBACRolePermission.role_uid,
|
|
39
|
+
RBACRolePermission.permission_uid == RBACPermission.uid,
|
|
40
|
+
RBACPermission.name == permission,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
cursor = await session.execute(query)
|
|
46
|
+
result = cursor.scalar()
|
|
47
|
+
|
|
48
|
+
return result
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import dataclasses
|
|
3
|
+
import json
|
|
4
|
+
from datetime import date, datetime
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from socket import socket
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CustomJSONEncoder(json.JSONEncoder):
|
|
11
|
+
def default(self, o) -> Any:
|
|
12
|
+
try:
|
|
13
|
+
return super(CustomJSONEncoder, self).default(o)
|
|
14
|
+
except TypeError:
|
|
15
|
+
if isinstance(o, datetime):
|
|
16
|
+
return o.isoformat()
|
|
17
|
+
elif isinstance(o, date):
|
|
18
|
+
return o.isoformat()
|
|
19
|
+
elif isinstance(o, bytes):
|
|
20
|
+
return base64.b64encode(o).decode('ascii')
|
|
21
|
+
elif dataclasses.is_dataclass(o):
|
|
22
|
+
return dataclasses.asdict(o)
|
|
23
|
+
elif isinstance(o, (Decimal, socket, type, Exception)):
|
|
24
|
+
return str(o)
|
|
25
|
+
|
|
26
|
+
return type(o).__name__
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from datetime import date, datetime
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
|
|
7
|
+
import msgpack
|
|
8
|
+
from msgpack import ExtType
|
|
9
|
+
|
|
10
|
+
from python3_commons.serializers.json import CustomJSONEncoder
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def msgpack_encoder(obj):
|
|
16
|
+
if isinstance(obj, Decimal):
|
|
17
|
+
return ExtType(1, str(obj).encode())
|
|
18
|
+
elif isinstance(obj, datetime):
|
|
19
|
+
return ExtType(2, obj.isoformat().encode())
|
|
20
|
+
elif isinstance(obj, date):
|
|
21
|
+
return ExtType(3, obj.isoformat().encode())
|
|
22
|
+
elif dataclasses.is_dataclass(obj):
|
|
23
|
+
return ExtType(4, json.dumps(dataclasses.asdict(obj), cls=CustomJSONEncoder).encode())
|
|
24
|
+
|
|
25
|
+
return f'no encoder for {obj}'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def msgpack_decoder(code, data):
|
|
29
|
+
if code == 1:
|
|
30
|
+
return Decimal(data.decode())
|
|
31
|
+
elif code == 2:
|
|
32
|
+
return datetime.fromisoformat(data.decode())
|
|
33
|
+
elif code == 3:
|
|
34
|
+
return date.fromisoformat(data.decode())
|
|
35
|
+
elif code == 4:
|
|
36
|
+
return json.loads(data)
|
|
37
|
+
|
|
38
|
+
return f'no decoder for type {code}'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def serialize_msgpack(data) -> bytes:
|
|
42
|
+
result = msgpack.packb(data, default=msgpack_encoder)
|
|
43
|
+
|
|
44
|
+
return result
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def deserialize_msgpack(data: bytes):
|
|
48
|
+
result = msgpack.unpackb(data, ext_hook=msgpack_decoder)
|
|
49
|
+
|
|
50
|
+
return result
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import struct
|
|
5
|
+
from _decimal import Decimal
|
|
6
|
+
from datetime import date, datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from msgspec import msgpack
|
|
10
|
+
from msgspec.msgpack import Ext, encode
|
|
11
|
+
|
|
12
|
+
from python3_commons.serializers.json import CustomJSONEncoder
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def enc_hook(obj: Any) -> Any:
|
|
18
|
+
if isinstance(obj, Decimal):
|
|
19
|
+
return Ext(1, struct.pack('b', str(obj).encode()))
|
|
20
|
+
elif isinstance(obj, datetime):
|
|
21
|
+
return Ext(2, struct.pack('b', obj.isoformat().encode()))
|
|
22
|
+
elif isinstance(obj, date):
|
|
23
|
+
return Ext(3, struct.pack('b', obj.isoformat().encode()))
|
|
24
|
+
elif dataclasses.is_dataclass(obj):
|
|
25
|
+
return Ext(4, struct.pack('b', json.dumps(dataclasses.asdict(obj), cls=CustomJSONEncoder).encode()))
|
|
26
|
+
|
|
27
|
+
raise NotImplementedError(f'Objects of type {type(obj)} are not supported')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ext_hook(code: int, data: memoryview) -> Any:
|
|
31
|
+
if code == 1:
|
|
32
|
+
return Decimal(data.tobytes().decode())
|
|
33
|
+
elif code == 2:
|
|
34
|
+
return datetime.fromisoformat(data.tobytes().decode())
|
|
35
|
+
elif code == 3:
|
|
36
|
+
return date.fromisoformat(data.tobytes().decode())
|
|
37
|
+
elif code == 4:
|
|
38
|
+
return json.loads(data.tobytes())
|
|
39
|
+
|
|
40
|
+
raise NotImplementedError(f'Extension type code {code} is not supported')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
MSGPACK_ENCODER = msgpack.Encoder(enc_hook=enc_hook)
|
|
44
|
+
MSGPACK_DECODER = msgpack.Decoder(ext_hook=ext_hook)
|
|
45
|
+
MSGPACK_DECODER_NATIVE = msgpack.Decoder()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def serialize_msgpack_native(data) -> bytes:
|
|
49
|
+
return encode(data)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def deserialize_msgpack_native(data: bytes, data_type=None):
|
|
53
|
+
if data_type:
|
|
54
|
+
result = msgpack.decode(data, type=data_type)
|
|
55
|
+
else:
|
|
56
|
+
result = MSGPACK_DECODER_NATIVE.decode(data)
|
|
57
|
+
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def serialize_msgpack(data) -> bytes:
|
|
62
|
+
result = MSGPACK_ENCODER.encode(data)
|
|
63
|
+
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def deserialize_msgpack(data: bytes, data_type=None):
|
|
68
|
+
if data_type:
|
|
69
|
+
result = msgpack.decode(data, type=data_type)
|
|
70
|
+
else:
|
|
71
|
+
result = MSGPACK_DECODER.decode(data)
|
|
72
|
+
|
|
73
|
+
return result
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python3-commons
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Re-usable Python3 code
|
|
5
|
+
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
|
+
License: gpl-3
|
|
7
|
+
Project-URL: Homepage, https://github.com/kamikaze/python3-commons
|
|
8
|
+
Project-URL: Documentation, https://github.com/kamikaze/python3-commons/wiki
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Requires-Python: ==3.13.*
|
|
12
|
+
Description-Content-Type: text/x-rst
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
License-File: AUTHORS.rst
|
|
15
|
+
Requires-Dist: aiohttp[speedups]~=3.11.16
|
|
16
|
+
Requires-Dist: asyncpg~=0.30.0
|
|
17
|
+
Requires-Dist: fastapi-users-db-sqlalchemy~=7.0.0
|
|
18
|
+
Requires-Dist: fastapi-users[sqlalchemy]~=14.0.1
|
|
19
|
+
Requires-Dist: lxml~=5.4.0
|
|
20
|
+
Requires-Dist: minio~=7.2.15
|
|
21
|
+
Requires-Dist: msgpack~=1.1.0
|
|
22
|
+
Requires-Dist: msgspec~=0.19.0
|
|
23
|
+
Requires-Dist: pydantic[email]~=2.11.3
|
|
24
|
+
Requires-Dist: pydantic-settings~=2.9.1
|
|
25
|
+
Requires-Dist: python-jose==3.4.0
|
|
26
|
+
Requires-Dist: SQLAlchemy[asyncio]~=2.0.40
|
|
27
|
+
Requires-Dist: valkey[libvalkey]~=6.1.0
|
|
28
|
+
Requires-Dist: zeep~=4.3.1
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
Re-usable Python3 code
|
|
32
|
+
======================
|
|
33
|
+
|
|
34
|
+
Some description here
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,189
|
|
2
|
+
python3_commons/api_client.py,sha256=LT7_YmnYVHK2ucKxIhUJCZrmxgfy-lfOxx08-R0WvW0,4505
|
|
3
|
+
python3_commons/audit.py,sha256=osx2ywZXf-V0zOkrhlNgSyzCBvojXQwSYBQ4-ze1xiM,6249
|
|
4
|
+
python3_commons/auth.py,sha256=X8mo7L0dhy1EwzHjOkjBAPvHCCqeTS6yhkcVfVi89cU,2459
|
|
5
|
+
python3_commons/cache.py,sha256=lf27LTD4Z9Iqi5GaK8jH8UC0cL9sHH8wicZ88YDp6Mg,7725
|
|
6
|
+
python3_commons/conf.py,sha256=1VebgIPy195dX4mHDSsDRt-8rE1WrloxKGR-dP0PzBw,1466
|
|
7
|
+
python3_commons/fs.py,sha256=wfLjybXndwLqNlOxTpm_HRJnuTcC4wbrHEOaEeCo9Wc,337
|
|
8
|
+
python3_commons/helpers.py,sha256=ygnTv3KYoiibOFIi99-g8EXaETKHLt5i3jvykGrv6aE,3079
|
|
9
|
+
python3_commons/object_storage.py,sha256=nQsXca0zzzeSY35qhnjE6pLfkLuxn7jDul0-hw0jizE,3985
|
|
10
|
+
python3_commons/permissions.py,sha256=bhjTp-tq-oaTGFMHNnSBlcVX5XQCTL0nWcu6SdPEAB4,1555
|
|
11
|
+
python3_commons/db/__init__.py,sha256=ONlvuAYEagLeSdU9oX02JBpECvWdqNx0OoR9BMCvAIQ,2741
|
|
12
|
+
python3_commons/db/helpers.py,sha256=PY0h08aLiGx-J54wmP3GHPCgGCcLd60rayAUnR3aWdI,1742
|
|
13
|
+
python3_commons/db/models/__init__.py,sha256=Utr5AJf1FwcrxNtdesgjq92WMK4zpK4VL_8z1JEkJw0,185
|
|
14
|
+
python3_commons/db/models/auth.py,sha256=dmyD3BX7LVBgKiepPN-bxlY6J3PhcmUfVdQwhNR45fU,1187
|
|
15
|
+
python3_commons/db/models/common.py,sha256=8JUDUBJLf59xLNhF57zZBgcgrmvcWAYXGYO4P-LKzHY,1512
|
|
16
|
+
python3_commons/db/models/rbac.py,sha256=7NNTUbS8whuPUHpm4oba_UWDdNiJlHrm8HBO7oGtk64,3185
|
|
17
|
+
python3_commons/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
python3_commons/logging/filters.py,sha256=fuyjXZAUm-i2MNrxvFYag8F8Rr27x8W8MdV3ke6miSs,175
|
|
19
|
+
python3_commons/logging/formatters.py,sha256=p2AtZD4Axp3Em0e9gWzW8U_yOR5entD7xn7Edvc-IuM,719
|
|
20
|
+
python3_commons/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
python3_commons/serializers/json.py,sha256=91UaXLGKGj0yPyrnuMeNrkG2GuPUgcgAsmIokUgEwpU,808
|
|
22
|
+
python3_commons/serializers/msgpack.py,sha256=WrvaPE187shSK8zkH4UHHMimEZNMv9RaDSwsBE2HlCw,1269
|
|
23
|
+
python3_commons/serializers/msgspec.py,sha256=5SS_wQXcrThPmCWjI7k1a_cHWLz46Jzn3pit89SYJGY,2038
|
|
24
|
+
python3_commons-0.0.0.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
|
|
25
|
+
python3_commons-0.0.0.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
|
|
26
|
+
python3_commons-0.0.0.dist-info/METADATA,sha256=RuVBD9YvT9Q0aRW7aEUDoYEpnXzUeqsgLu83xG0PKck,1113
|
|
27
|
+
python3_commons-0.0.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
28
|
+
python3_commons-0.0.0.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
|
|
29
|
+
python3_commons-0.0.0.dist-info/RECORD,,
|