dwyeapi 0.9.3__tar.gz → 0.9.4__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.
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/PKG-INFO +1 -1
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/pyproject.toml +1 -1
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/__init__.py +1 -1
- dwyeapi-0.9.4/src/dwyeapi/dt.py +113 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/security.py +2 -2
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi.egg-info/PKG-INFO +1 -1
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi.egg-info/SOURCES.txt +1 -0
- dwyeapi-0.9.4/tests/test_dt.py +75 -0
- dwyeapi-0.9.3/src/dwyeapi/dt.py +0 -52
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/README.md +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/setup.cfg +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/cache.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/config.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/database.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/dependencies.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/exceptions.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/health.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/logger.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/masking.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/pagination.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/__init__.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/email/__init__.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/email/base.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/email/config.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/email/factory.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/email/protocol.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/providers/email/resend.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/response.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/__init__.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/context.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/model.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/pool.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/registry.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/router.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/schema.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/service.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi/tasks/worker.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi.egg-info/dependency_links.txt +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi.egg-info/requires.txt +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/src/dwyeapi.egg-info/top_level.txt +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_cache.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_config.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_database.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_dependencies.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_exceptions.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_health.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_logger.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_masking.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_pagination.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_response.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_security.py +0 -0
- {dwyeapi-0.9.3 → dwyeapi-0.9.4}/tests/test_tasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dwyeapi
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/danweiyuancircle/dwy-ai-experience
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""全局时间工具 — 所有日期 / 时长操作的唯一入口。
|
|
2
|
+
|
|
3
|
+
业务时间统一 Asia/Shanghai, JWT 等协议场景提供 UTC 方法。
|
|
4
|
+
禁止在业务代码中直接使用 ``datetime.now()`` / ``datetime.utcnow()`` /
|
|
5
|
+
``from datetime import timedelta``,统一通过本模块访问。
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from dwyeapi import dt
|
|
10
|
+
|
|
11
|
+
dt.now() # naive datetime, 中国时间, 用于数据库
|
|
12
|
+
dt.now_str() # "2026-04-06 14:30:00"
|
|
13
|
+
dt.today() # date(2026, 4, 6)
|
|
14
|
+
dt.timestamp() # Unix 时间戳(秒)
|
|
15
|
+
dt.utc_now() # aware datetime, UTC, 用于 JWT
|
|
16
|
+
|
|
17
|
+
dt.timedelta(minutes=10) # 时长(透传 stdlib timedelta)
|
|
18
|
+
dt.after(days=1) # now + 偏移(naive 中国时间)
|
|
19
|
+
dt.before(hours=1) # now - 偏移
|
|
20
|
+
dt.utc_after(minutes=30) # utc_now + 偏移(aware UTC, JWT exp)
|
|
21
|
+
dt.utc_before(days=1) # utc_now - 偏移
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from datetime import date, datetime
|
|
25
|
+
from datetime import timedelta as _Timedelta
|
|
26
|
+
from zoneinfo import ZoneInfo
|
|
27
|
+
|
|
28
|
+
TZ = ZoneInfo("Asia/Shanghai")
|
|
29
|
+
UTC = ZoneInfo("UTC")
|
|
30
|
+
|
|
31
|
+
# 公开 re-export:业务侧用 dt.timedelta(...),不再 from datetime import timedelta
|
|
32
|
+
timedelta = _Timedelta
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ── 业务时间(Asia/Shanghai) ──────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def now() -> datetime:
|
|
39
|
+
"""当前中国时间(naive, 用于数据库存储)。"""
|
|
40
|
+
return datetime.now(TZ).replace(tzinfo=None)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def now_str(fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
|
44
|
+
"""格式化的当前中国时间字符串。"""
|
|
45
|
+
return datetime.now(TZ).strftime(fmt)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def today() -> date:
|
|
49
|
+
"""当前中国日期。"""
|
|
50
|
+
return datetime.now(TZ).date()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def timestamp() -> float:
|
|
54
|
+
"""当前 Unix 时间戳(秒)。"""
|
|
55
|
+
return datetime.now(TZ).timestamp()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def after(**kwargs: int | float) -> datetime:
|
|
59
|
+
"""当前中国时间加上偏移,返回 naive datetime(写库 / 业务截止时间)。
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
**kwargs: 同 ``datetime.timedelta`` 关键字参数
|
|
63
|
+
(``days`` / ``seconds`` / ``microseconds`` / ``milliseconds`` /
|
|
64
|
+
``minutes`` / ``hours`` / ``weeks``)。示例:``days=1, hours=2``。
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
datetime: ``now() + timedelta(**kwargs)``,无时区信息。
|
|
68
|
+
"""
|
|
69
|
+
return now() + _Timedelta(**kwargs)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def before(**kwargs: int | float) -> datetime:
|
|
73
|
+
"""当前中国时间减去偏移,返回 naive datetime。
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
**kwargs: 同 ``after``。示例:``hours=1``。
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
datetime: ``now() - timedelta(**kwargs)``,无时区信息。
|
|
80
|
+
"""
|
|
81
|
+
return now() - _Timedelta(**kwargs)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ── UTC(JWT 等协议场景) ──────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def utc_now() -> datetime:
|
|
88
|
+
"""当前 UTC 时间(aware, 用于 JWT exp 等协议字段)。"""
|
|
89
|
+
return datetime.now(UTC)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def utc_after(**kwargs: int | float) -> datetime:
|
|
93
|
+
"""当前 UTC 时间加上偏移,返回 aware datetime(JWT exp 等)。
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
**kwargs: 同 ``after``。示例:``minutes=30``。
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
datetime: ``utc_now() + timedelta(**kwargs)``,tzinfo=UTC。
|
|
100
|
+
"""
|
|
101
|
+
return utc_now() + _Timedelta(**kwargs)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def utc_before(**kwargs: int | float) -> datetime:
|
|
105
|
+
"""当前 UTC 时间减去偏移,返回 aware datetime。
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
**kwargs: 同 ``after``。示例:``days=1``。
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
datetime: ``utc_now() - timedelta(**kwargs)``,tzinfo=UTC。
|
|
112
|
+
"""
|
|
113
|
+
return utc_now() - _Timedelta(**kwargs)
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
所有函数保持无状态 — 密钥和算法由调用方以参数显式传入,方便在不同环境或密钥版本之间切换。
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from datetime import timedelta
|
|
8
7
|
from typing import Any
|
|
9
8
|
|
|
10
9
|
import bcrypt
|
|
@@ -63,7 +62,8 @@ def create_token(
|
|
|
63
62
|
编码后的 JWT 字符串。
|
|
64
63
|
"""
|
|
65
64
|
to_encode = data.copy()
|
|
66
|
-
|
|
65
|
+
# JWT exp 必须用 UTC aware;时长与当前时刻统一走 dt
|
|
66
|
+
expire = dt.utc_after(minutes=expires_minutes)
|
|
67
67
|
to_encode["exp"] = expire
|
|
68
68
|
return jwt.encode(to_encode, secret, algorithm=algorithm)
|
|
69
69
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dwyeapi
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/danweiyuancircle/dwy-ai-experience
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Tests for dwyeapi.dt — 时钟与时长统一入口。"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
|
|
5
|
+
from dwyeapi import dt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestClock:
|
|
9
|
+
def test_now_is_naive(self):
|
|
10
|
+
assert dt.now().tzinfo is None
|
|
11
|
+
|
|
12
|
+
def test_utc_now_is_aware_utc(self):
|
|
13
|
+
now = dt.utc_now()
|
|
14
|
+
assert now.tzinfo is not None
|
|
15
|
+
# 与 UTC 偏移为 0
|
|
16
|
+
assert now.utcoffset() == timedelta(0)
|
|
17
|
+
|
|
18
|
+
def test_today_is_date(self):
|
|
19
|
+
assert dt.today() == dt.now().date()
|
|
20
|
+
|
|
21
|
+
def test_timestamp_is_float(self):
|
|
22
|
+
ts = dt.timestamp()
|
|
23
|
+
assert isinstance(ts, float)
|
|
24
|
+
assert ts > 0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TestTimedelta:
|
|
28
|
+
def test_timedelta_reexport(self):
|
|
29
|
+
delta = dt.timedelta(minutes=10)
|
|
30
|
+
assert isinstance(delta, timedelta)
|
|
31
|
+
assert delta == timedelta(minutes=10)
|
|
32
|
+
|
|
33
|
+
def test_timedelta_days_and_hours(self):
|
|
34
|
+
assert dt.timedelta(days=1, hours=2) == timedelta(days=1, hours=2)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TestAfterBefore:
|
|
38
|
+
def test_after_is_naive_and_future(self):
|
|
39
|
+
base = dt.now()
|
|
40
|
+
future = dt.after(minutes=5)
|
|
41
|
+
assert future.tzinfo is None
|
|
42
|
+
assert future > base
|
|
43
|
+
# 约 5 分钟,允许毫秒级误差
|
|
44
|
+
assert abs((future - base).total_seconds() - 300) < 1
|
|
45
|
+
|
|
46
|
+
def test_before_is_naive_and_past(self):
|
|
47
|
+
base = dt.now()
|
|
48
|
+
past = dt.before(hours=1)
|
|
49
|
+
assert past.tzinfo is None
|
|
50
|
+
assert past < base
|
|
51
|
+
assert abs((base - past).total_seconds() - 3600) < 1
|
|
52
|
+
|
|
53
|
+
def test_utc_after_is_aware_and_future(self):
|
|
54
|
+
base = dt.utc_now()
|
|
55
|
+
future = dt.utc_after(minutes=30)
|
|
56
|
+
assert future.tzinfo is not None
|
|
57
|
+
assert future.utcoffset() == timedelta(0)
|
|
58
|
+
assert future > base
|
|
59
|
+
assert abs((future - base).total_seconds() - 1800) < 1
|
|
60
|
+
|
|
61
|
+
def test_utc_before_is_aware_and_past(self):
|
|
62
|
+
base = dt.utc_now()
|
|
63
|
+
past = dt.utc_before(days=1)
|
|
64
|
+
assert past.tzinfo is not None
|
|
65
|
+
assert past < base
|
|
66
|
+
assert abs((base - past).total_seconds() - 86400) < 1
|
|
67
|
+
|
|
68
|
+
def test_utc_after_compatible_with_jwt_exp(self):
|
|
69
|
+
"""utc_after 结果可直接写入 JWT exp(aware UTC)。"""
|
|
70
|
+
exp = dt.utc_after(minutes=10)
|
|
71
|
+
# python-jose 接受 datetime;这里只校验形态
|
|
72
|
+
assert isinstance(exp, datetime)
|
|
73
|
+
assert exp.tzinfo is not None
|
|
74
|
+
# 与 timezone.utc 兼容
|
|
75
|
+
assert exp.astimezone(timezone.utc).tzinfo is not None
|
dwyeapi-0.9.3/src/dwyeapi/dt.py
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"""全局时间工具 — 所有日期操作的唯一入口。
|
|
2
|
-
|
|
3
|
-
业务时间统一 Asia/Shanghai, JWT 等协议场景提供 UTC 方法。
|
|
4
|
-
禁止在业务代码中直接使用 datetime.now(), 统一通过本模块访问。
|
|
5
|
-
|
|
6
|
-
Usage::
|
|
7
|
-
|
|
8
|
-
from dwyeapi import dt
|
|
9
|
-
|
|
10
|
-
dt.now() # naive datetime, 中国时间, 用于数据库
|
|
11
|
-
dt.now_str() # "2026-04-06 14:30:00"
|
|
12
|
-
dt.today() # date(2026, 4, 6)
|
|
13
|
-
dt.timestamp() # Unix 时间戳(秒)
|
|
14
|
-
dt.utc_now() # aware datetime, UTC, 用于 JWT
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
from datetime import date, datetime
|
|
18
|
-
from zoneinfo import ZoneInfo
|
|
19
|
-
|
|
20
|
-
TZ = ZoneInfo("Asia/Shanghai")
|
|
21
|
-
UTC = ZoneInfo("UTC")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# ── 业务时间(Asia/Shanghai) ──────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def now() -> datetime:
|
|
28
|
-
"""当前中国时间(naive, 用于数据库存储)。"""
|
|
29
|
-
return datetime.now(TZ).replace(tzinfo=None)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def now_str(fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
|
33
|
-
"""格式化的当前中国时间字符串。"""
|
|
34
|
-
return datetime.now(TZ).strftime(fmt)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def today() -> date:
|
|
38
|
-
"""当前中国日期。"""
|
|
39
|
-
return datetime.now(TZ).date()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def timestamp() -> float:
|
|
43
|
-
"""当前 Unix 时间戳(秒)。"""
|
|
44
|
-
return datetime.now(TZ).timestamp()
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# ── UTC(JWT 等协议场景) ──────────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def utc_now() -> datetime:
|
|
51
|
-
"""当前 UTC 时间(aware, 用于 JWT exp 等协议字段)。"""
|
|
52
|
-
return datetime.now(UTC)
|
|
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
|
|
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
|
|
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
|
|
File without changes
|