sotkalib 0.0.6.post2__py3-none-any.whl → 0.1.0b2__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.
sotkalib/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- from . import config, enum, exceptions, http, log, redis, sqla
1
+ from . import config, enum, exceptions, http, log, redis, sqla, time, type
2
2
 
3
- __all__ = ["config", "enum", "exceptions", "http", "log", "redis", "sqla"]
3
+ __all__ = ["config", "enum", "exceptions", "http", "log", "redis", "sqla", "time", "type"]
@@ -0,0 +1,13 @@
1
+ from typing import Any
2
+
3
+ from sotkalib.type import Unset
4
+
5
+
6
+ def without_unset(d: dict[str, Any]) -> dict[str, Any]:
7
+ newd = {}
8
+ for k, v in d.items():
9
+ if v == Unset:
10
+ continue
11
+ newd[k] = v
12
+ return newd
13
+
sotkalib/enum/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- from .mixins import UppercaseStrEnumMixin, ValidatorStrEnumMixin
1
+ from .mixins import UppercaseMixin, ValidatorMixin, ValuesMixin
2
2
 
3
- __all__ = ["ValidatorStrEnumMixin", "UppercaseStrEnumMixin"]
3
+ __all__ = ["ValidatorMixin", "UppercaseMixin", "ValuesMixin"]
sotkalib/enum/mixins.py CHANGED
@@ -1,15 +1,15 @@
1
1
  from collections.abc import Sequence
2
- from enum import Enum
2
+ from enum import StrEnum
3
3
  from typing import Any, Literal, Self, overload
4
4
 
5
5
 
6
- class UppercaseStrEnumMixin(str, Enum):
6
+ class UppercaseMixin(StrEnum):
7
7
  @staticmethod
8
8
  def _generate_next_value_(name: str, start: int, count: int, last_values: Sequence) -> str: # noqa
9
9
  return name.upper()
10
10
 
11
11
 
12
- class ValidatorStrEnumMixin(str, Enum):
12
+ class ValidatorMixin(StrEnum):
13
13
  @classmethod
14
14
  def _normalize_value(cls, val: Any) -> str:
15
15
  if isinstance(val, (str, bytes, bytearray)):
@@ -57,3 +57,17 @@ class ValidatorStrEnumMixin(str, Enum):
57
57
  @classmethod
58
58
  def values(cls) -> Sequence[Self]:
59
59
  return list(cls)
60
+
61
+
62
+ class ValuesMixin:
63
+ @classmethod
64
+ def values_list(cls) -> list[str]:
65
+ return [v for k, v in vars(cls).items() if not k.startswith("_") and isinstance(v, str)]
66
+
67
+ @classmethod
68
+ def values_set(cls) -> set[str]:
69
+ return set(cls.values_list())
70
+
71
+ @classmethod
72
+ def names_list(cls) -> list[str]:
73
+ return [k for k, v in vars(cls).items() if not k.startswith("_") and isinstance(v, str)]
@@ -1,3 +1,4 @@
1
1
  from . import api, handlers
2
+ from .traceback import traceback_from
2
3
 
3
- __all__ = ["api", "handlers"]
4
+ __all__ = ["api", "handlers", "traceback_from"]
@@ -2,6 +2,8 @@ from collections.abc import Callable, Coroutine
2
2
  from functools import wraps
3
3
  from typing import Any
4
4
 
5
+ from sotkalib.log import get_logger
6
+
5
7
  from .args_incl_error import ArgsIncludedError
6
8
 
7
9
 
@@ -14,6 +16,7 @@ def exception_handler[**P, R](
14
16
  try:
15
17
  return func(*args, **kwargs)
16
18
  except Exception as e:
19
+ get_logger().exception("")
17
20
  raise ArgsIncludedError(*e.args, stack_depth=stack_depth) from e
18
21
 
19
22
  return wrapper
@@ -28,6 +31,7 @@ def aexception_handler[**P, R](
28
31
  try:
29
32
  return await func(*args, **kwargs)
30
33
  except Exception as e:
34
+ get_logger().exception("")
31
35
  raise ArgsIncludedError(*e.args, stack_depth=stack_depth) from e
32
36
 
33
37
  return wrapper
@@ -0,0 +1,5 @@
1
+ import traceback
2
+
3
+
4
+ def traceback_from(exc: BaseException) -> str:
5
+ return "".join(traceback.format_exception(exc))
@@ -0,0 +1,6 @@
1
+ from .concur import asyncfn, asyncfn_or_raise
2
+ from .cond import or_raise, suppress, type_or_raise
3
+
4
+ __all__ = [
5
+ "asyncfn", "asyncfn_or_raise", "or_raise", "type_or_raise", "suppress"
6
+ ]
@@ -0,0 +1,12 @@
1
+ import inspect
2
+ from collections.abc import Callable
3
+ from typing import Any
4
+
5
+
6
+ def asyncfn(fn: Callable[..., Any]) -> bool:
7
+ return inspect.iscoroutinefunction(fn)
8
+
9
+
10
+ def asyncfn_or_raise(fn: Callable[..., Any]) -> None:
11
+ if not inspect.iscoroutinefunction(fn):
12
+ raise TypeError(f"{fn} is not an async function")
sotkalib/func/cond.py ADDED
@@ -0,0 +1,40 @@
1
+ from collections.abc import Generator, Sequence
2
+ from contextlib import contextmanager
3
+ from typing import Literal
4
+ from warnings import warn
5
+
6
+
7
+ @contextmanager
8
+ def suppress(
9
+ mode: Literal["all", "exact"] = "all", excts: Sequence[type[BaseException]] | None = None
10
+ ) -> Generator[None]:
11
+ if excts is None:
12
+ if mode == "exact":
13
+ warn("mode = 'exact' and excts = None is passed to suppress, bubbling exception up")
14
+ exc_ts = ()
15
+ else:
16
+ exc_ts = excts
17
+
18
+ try:
19
+ yield None
20
+ except Exception as exc:
21
+ if mode == "all":
22
+ return
23
+
24
+ if mode == "exact" and type(exc) in exc_ts:
25
+ return
26
+
27
+ raise exc
28
+
29
+
30
+ def or_raise[T](v: T | None, msg: str = "v is None") -> T:
31
+ if v is None:
32
+ raise ValueError(msg)
33
+
34
+ return v
35
+
36
+
37
+ def type_or_raise[T](v: object, exp: type[T], msg: str | None = None) -> T:
38
+ if not isinstance(v, exp):
39
+ raise TypeError(msg or f"want {exp}, got {type(v)}")
40
+ return v
sotkalib/sqla/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
1
  from .db import Database, DatabaseSettings
2
+ from .dbm import BasicDBM
2
3
 
3
- __all__ = ("Database", "DatabaseSettings")
4
+ __all__ = ("Database", "DatabaseSettings", "BasicDBM")
sotkalib/sqla/dbm.py ADDED
@@ -0,0 +1,20 @@
1
+ from typing import Any
2
+
3
+ from sqlalchemy import inspect
4
+ from sqlalchemy.orm import DeclarativeBase
5
+
6
+
7
+ class BasicDBM(DeclarativeBase):
8
+ __abstract__ = True
9
+ __table_args__ = {"extend_existing": True}
10
+
11
+ def dict(self, **kw) -> dict[str, Any]:
12
+ result = {field.name: getattr(self, field.name) for field in self.__table__.c}
13
+ if kw is not None:
14
+ result.update(kw)
15
+ return result
16
+
17
+ def attribute_loaded(self, key: str):
18
+ if key not in (k := {c.key for c in inspect(self).mapper.all_orm_descriptors}): # type:ignore
19
+ raise KeyError(k)
20
+ return key not in inspect(self).unloaded
@@ -0,0 +1,10 @@
1
+ from datetime import UTC, datetime, timezone
2
+
3
+ __all__ = ["utcnow", "now"]
4
+
5
+ def utcnow() -> datetime:
6
+ return datetime.now(UTC)
7
+
8
+
9
+ def now(tz: timezone | None = None) -> datetime:
10
+ return datetime.now(tz)
@@ -0,0 +1,18 @@
1
+ __all__ = ["Unset", "unset"]
2
+
3
+
4
+ class _UnsetType:
5
+ __slots__ = ()
6
+
7
+ def __repr__(self) -> str:
8
+ return "Unset"
9
+
10
+ def __bool__(self) -> bool:
11
+ return False
12
+
13
+
14
+ Unset = _UnsetType()
15
+
16
+
17
+ def unset(val: object) -> bool:
18
+ return val is Unset
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sotkalib
3
- Version: 0.0.6.post2
3
+ Version: 0.1.0b2
4
4
  Summary:
5
5
  Author: alexey
6
6
  Author-email: alexey <me@pyrorhythm.dev>
@@ -0,0 +1,32 @@
1
+ sotkalib/__init__.py,sha256=3gWJxBiPbbWpSXapHWDtJBzSww9pWPJz_FgrPDQg2r4,167
2
+ sotkalib/config/__init__.py,sha256=_F7rSYgBsSxnNL1JtxrJYlw3lBXyVg0JdsOrxbWtcDA,96
3
+ sotkalib/config/field.py,sha256=vbKGAEevEmdvyw4eaZprfR2g7ZVAB-5AbYPx0f4uusc,317
4
+ sotkalib/config/struct.py,sha256=gv1jFrSRytMC6bZTUDOUQf00Zo1iKxQvuTxGv9qnyHI,5679
5
+ sotkalib/dict/__init__.py,sha256=jfE3RATv6xrpEr5GJ8aJuTfCktKbccM661TlxkGlc1c,206
6
+ sotkalib/enum/__init__.py,sha256=pxO9YipTLoKbXJTgWzCybbKm7dSvetgcQwSVOqvrq20,127
7
+ sotkalib/enum/mixins.py,sha256=3_x3x8NgNluk2Rycp4BLiVO3dQ9jqbgd_ImqKkE7HzU,1980
8
+ sotkalib/exceptions/__init__.py,sha256=jXqvK7RbP7Kw7mJt2bG9R9qDpVQmtivoN_0v0F9v3RQ,115
9
+ sotkalib/exceptions/api.py,sha256=gqx4GrHXUvKcR7tEmJpRqPbDWOT2AgKoyck8-FovQCc,1329
10
+ sotkalib/exceptions/handlers/__init__.py,sha256=Pz1akT2x3SaRsPezNPYnCoTcejxy4n4_cO4cXRJUBIk,179
11
+ sotkalib/exceptions/handlers/args_incl_error.py,sha256=rYiBximsXVw1YDUBbdsqeqsfTWxshyX4EdISXWYkPDE,533
12
+ sotkalib/exceptions/handlers/core.py,sha256=kN9W8JC6Wz8juZfQSKsq94tVOfKIqL81X9W1nXmvxxE,933
13
+ sotkalib/exceptions/traceback.py,sha256=lIcYIgbJ3SBc9rp1ZJ0WOht5YZF_QNXIwmpxkAmVfM4,115
14
+ sotkalib/func/__init__.py,sha256=j-18eXcSJ6RDqsJJE1i4rxJdbgpx-mEuJy9rY_xzLmE,187
15
+ sotkalib/func/concur.py,sha256=J75_puWR61Bom3lGsMROvfuXfJNLoOxCmvwzHogtKbk,311
16
+ sotkalib/func/cond.py,sha256=k53U74Ouc5N9iMknNxneZOsRITEyxMhwf-dIz6xV0AY,879
17
+ sotkalib/http/__init__.py,sha256=xi4qStyHRgLufX_3ziSL5_-hDlbVLzA1Eg8JtjfHMz8,421
18
+ sotkalib/http/client_session.py,sha256=u6rrxKhzewrNMUbBI0GJxI98NYXELjWcjjLqWUGpJJ8,12747
19
+ sotkalib/log/__init__.py,sha256=xrBx--c8QU5xkb3_n61LuqF8ySUaxlQkHCxHyH_D8aE,58
20
+ sotkalib/log/factory.py,sha256=BM8gTHoITCsS4b9JNGST4czpDql5mVTIPsESutO2OGQ,214
21
+ sotkalib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ sotkalib/redis/__init__.py,sha256=-0ZXD-cC-Xi6RmAvnrAgU-8Z6g4l88XmXf3kvIgBD9k,154
23
+ sotkalib/redis/client.py,sha256=W13VOlPtEb9HuDomQ9Uz7Fh3iq94mBWX6EXOeRRYtiQ,1137
24
+ sotkalib/redis/lock.py,sha256=nEZjIyXmgq3vH-Urs8qXC_N8lmXNho00SaTZ7wJIEIo,2528
25
+ sotkalib/sqla/__init__.py,sha256=-mCvR5aGpCSWimPWnGhZfziRhKMo7s_WmgS1vNRuDNQ,125
26
+ sotkalib/sqla/db.py,sha256=6ZckKQ8kmRlYrwCAzKidc_JNPwqp38tSGEy3XMGnv08,3376
27
+ sotkalib/sqla/dbm.py,sha256=Cd_2jM-mB5G6RxSu9H0f8pkvIXEjS0zaxsJqivSu29o,580
28
+ sotkalib/time/__init__.py,sha256=SIfMwGfbHGQNL0RqvUPGrXTsGKuiEk860ZpkyLmvUkk,203
29
+ sotkalib/type/__init__.py,sha256=S94n-b5jU3TB5I9a8GIVanMKqlWtfUTTTch_87xYS-U,234
30
+ sotkalib-0.1.0b2.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
31
+ sotkalib-0.1.0b2.dist-info/METADATA,sha256=as0DGRN3rdc1geFmBoH4LCppcHlLJg16d1bAP8Hi5ik,388
32
+ sotkalib-0.1.0b2.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- from .exc import APIError, ErrorSchema
@@ -1,25 +0,0 @@
1
- sotkalib/__init__.py,sha256=TDJPQ_pOk73TTDkjgNpvn4nvn3siktb1sTogogLCwa0,139
2
- sotkalib/config/__init__.py,sha256=_F7rSYgBsSxnNL1JtxrJYlw3lBXyVg0JdsOrxbWtcDA,96
3
- sotkalib/config/field.py,sha256=vbKGAEevEmdvyw4eaZprfR2g7ZVAB-5AbYPx0f4uusc,317
4
- sotkalib/config/struct.py,sha256=gv1jFrSRytMC6bZTUDOUQf00Zo1iKxQvuTxGv9qnyHI,5679
5
- sotkalib/enum/__init__.py,sha256=pKpLPm8fqHO4Et21TWIybIPRiehN1KrmxcBh6hPRsxM,127
6
- sotkalib/enum/mixins.py,sha256=CQrgKftnmZSWkNb-56Z9PZ3um0_lHGEsnEYy9GwCmhM,1611
7
- sotkalib/exceptions/__init__.py,sha256=r-DwSwJIkuQ2UGAorKvkIVv87n4Yt8H0mk_uxKcBGTw,59
8
- sotkalib/exceptions/api/__init__.py,sha256=yTbg2p5mB0-8ZHtzlLL6e0ZkC3LRUZmjmWMxU9Uh8-Q,39
9
- sotkalib/exceptions/api/exc.py,sha256=gqx4GrHXUvKcR7tEmJpRqPbDWOT2AgKoyck8-FovQCc,1329
10
- sotkalib/exceptions/handlers/__init__.py,sha256=Pz1akT2x3SaRsPezNPYnCoTcejxy4n4_cO4cXRJUBIk,179
11
- sotkalib/exceptions/handlers/args_incl_error.py,sha256=rYiBximsXVw1YDUBbdsqeqsfTWxshyX4EdISXWYkPDE,533
12
- sotkalib/exceptions/handlers/core.py,sha256=5fhusoxBhUz59TaVWobplBvD-sbkZKBnmmu-fcSyRk4,836
13
- sotkalib/http/__init__.py,sha256=xi4qStyHRgLufX_3ziSL5_-hDlbVLzA1Eg8JtjfHMz8,421
14
- sotkalib/http/client_session.py,sha256=u6rrxKhzewrNMUbBI0GJxI98NYXELjWcjjLqWUGpJJ8,12747
15
- sotkalib/log/__init__.py,sha256=xrBx--c8QU5xkb3_n61LuqF8ySUaxlQkHCxHyH_D8aE,58
16
- sotkalib/log/factory.py,sha256=BM8gTHoITCsS4b9JNGST4czpDql5mVTIPsESutO2OGQ,214
17
- sotkalib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- sotkalib/redis/__init__.py,sha256=-0ZXD-cC-Xi6RmAvnrAgU-8Z6g4l88XmXf3kvIgBD9k,154
19
- sotkalib/redis/client.py,sha256=W13VOlPtEb9HuDomQ9Uz7Fh3iq94mBWX6EXOeRRYtiQ,1137
20
- sotkalib/redis/lock.py,sha256=nEZjIyXmgq3vH-Urs8qXC_N8lmXNho00SaTZ7wJIEIo,2528
21
- sotkalib/sqla/__init__.py,sha256=n-I_hoRS-N7XN02yYCTtw6Dh4BBSQRmolS19tEB2KMM,87
22
- sotkalib/sqla/db.py,sha256=6ZckKQ8kmRlYrwCAzKidc_JNPwqp38tSGEy3XMGnv08,3376
23
- sotkalib-0.0.6.post2.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
24
- sotkalib-0.0.6.post2.dist-info/METADATA,sha256=OODLiqlrKSsBDdO9p1XyFtxh9FukqD3bFfCUez1gGhQ,392
25
- sotkalib-0.0.6.post2.dist-info/RECORD,,
File without changes