sotkalib 0.0.6.post1__py3-none-any.whl → 0.1.0b1__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"]
@@ -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
@@ -189,9 +189,9 @@ class ClientSettings(BaseModel):
189
189
  for k, v in kws.items():
190
190
  if "." in k:
191
191
  pk, ck = k.split(".")
192
- setattr(self, pk, setattr(getattr(self, pk), ck, v))
193
-
194
- setattr(self, k, v)
192
+ setattr(getattr(self, pk), ck, v)
193
+ else:
194
+ setattr(self, k, v)
195
195
 
196
196
  return self
197
197
 
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.post1
3
+ Version: 0.1.0b1
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=5fhusoxBhUz59TaVWobplBvD-sbkZKBnmmu-fcSyRk4,836
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.0b1.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
31
+ sotkalib-0.1.0b1.dist-info/METADATA,sha256=YEheXrjYWfL8UvqnJ-Qh_Lt-rAyUNdlZ87qxkelh6JA,388
32
+ sotkalib-0.1.0b1.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=TOi_BwyYVgrvo4dmWzp6bcLwYyITV9C5ODx18uYTG_8,12757
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.post1.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
24
- sotkalib-0.0.6.post1.dist-info/METADATA,sha256=2_4eonP0GVU1z9EAdmrzG_YM-lSeWuc6n41bcKYUd9U,392
25
- sotkalib-0.0.6.post1.dist-info/RECORD,,
File without changes