aiteamutils 0.2.36__py3-none-any.whl → 0.2.38__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.
aiteamutils/database.py CHANGED
@@ -9,6 +9,7 @@ from sqlalchemy import or_
9
9
  from fastapi import Request, Depends
10
10
  from ulid import ULID
11
11
  from sqlalchemy.sql import Select
12
+ import logging
12
13
 
13
14
  from .exceptions import ErrorCode, CustomException
14
15
  from .base_model import Base, BaseColumn
@@ -31,18 +32,33 @@ def get_database_service() -> 'DatabaseService':
31
32
  if _database_service is None:
32
33
  raise CustomException(
33
34
  ErrorCode.DB_CONNECTION_ERROR,
34
- detail="database_service",
35
+ detail="Database service is not initialized. Call init_database_service() first.",
35
36
  source_function="get_database_service"
36
37
  )
37
38
  return _database_service
38
39
 
39
40
  async def get_db() -> AsyncGenerator[AsyncSession, None]:
40
- """데이터베이스 세션을 생성하고 반환하는 비동기 제너레이터."""
41
+ """데이터베이스 세션을 생성하고 반환하는 비동기 제너레이터.
42
+
43
+ Yields:
44
+ AsyncSession: 데이터베이스 세션
45
+
46
+ Raises:
47
+ CustomException: 세션 생성 실패 시
48
+ """
41
49
  db_service = get_database_service()
42
- async with db_service.get_session() as session:
43
- try:
50
+ try:
51
+ async with db_service.get_session() as session:
44
52
  yield session
45
- finally:
53
+ except Exception as e:
54
+ raise CustomException(
55
+ ErrorCode.DB_CONNECTION_ERROR,
56
+ detail=f"Failed to get database session: {str(e)}",
57
+ source_function="get_db",
58
+ original_error=e
59
+ )
60
+ finally:
61
+ if 'session' in locals():
46
62
  await session.close()
47
63
 
48
64
  def get_database_session(db: AsyncSession = Depends(get_db)) -> 'DatabaseService':
@@ -921,14 +937,40 @@ def init_database_service(
921
937
 
922
938
  Returns:
923
939
  DatabaseService: 초기화된 데이터베이스 서비스 인스턴스
940
+
941
+ Raises:
942
+ CustomException: 데이터베이스 초기화 실패 시
924
943
  """
925
- global _database_service
926
- _database_service = DatabaseService(
927
- db_url=db_url,
928
- db_echo=db_echo,
929
- db_pool_size=db_pool_size,
930
- db_max_overflow=db_max_overflow,
931
- db_pool_timeout=db_pool_timeout,
932
- db_pool_recycle=db_pool_recycle
933
- )
934
- return _database_service
944
+ try:
945
+ global _database_service
946
+ if _database_service is not None:
947
+ logging.info("Database service already initialized")
948
+ return _database_service
949
+
950
+ logging.info(f"Initializing database service with URL: {db_url}")
951
+ _database_service = DatabaseService(
952
+ db_url=db_url,
953
+ db_echo=db_echo,
954
+ db_pool_size=db_pool_size,
955
+ db_max_overflow=db_max_overflow,
956
+ db_pool_timeout=db_pool_timeout,
957
+ db_pool_recycle=db_pool_recycle
958
+ )
959
+
960
+ if not _database_service.engine:
961
+ raise CustomException(
962
+ ErrorCode.DB_CONNECTION_ERROR,
963
+ detail="Database engine initialization failed",
964
+ source_function="init_database_service"
965
+ )
966
+
967
+ logging.info("Database service initialized successfully")
968
+ return _database_service
969
+ except Exception as e:
970
+ logging.error(f"Failed to initialize database service: {str(e)}")
971
+ raise CustomException(
972
+ ErrorCode.DB_CONNECTION_ERROR,
973
+ detail=f"Failed to initialize database service: {str(e)}",
974
+ source_function="init_database_service",
975
+ original_error=e
976
+ )
@@ -67,11 +67,22 @@ def get_service(name: str):
67
67
 
68
68
  Returns:
69
69
  Callable: 서비스 인스턴스를 반환하는 의존성 함수
70
+
71
+ Raises:
72
+ CustomException: 서비스 생성 실패 시
70
73
  """
71
74
  def _get_service(db_service: DatabaseService = Depends(get_database_service)):
72
- repository_class, service_class = service_registry.get(name)
73
- repository = repository_class(db_service)
74
- return service_class(repository, db_service)
75
+ try:
76
+ repository_class, service_class = service_registry.get(name)
77
+ repository = repository_class(db_service)
78
+ return service_class(repository)
79
+ except Exception as e:
80
+ raise CustomException(
81
+ ErrorCode.SERVICE_NOT_REGISTERED,
82
+ detail=f"Failed to create service {name}: {str(e)}",
83
+ source_function="dependencies.get_service",
84
+ original_error=e
85
+ )
75
86
  return _get_service
76
87
 
77
88
  oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/token")
aiteamutils/security.py CHANGED
@@ -15,6 +15,9 @@ from .config import get_settings
15
15
 
16
16
  pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
17
17
 
18
+ # 전역 rate limit 상태 저장
19
+ _rate_limits: Dict[str, Dict[str, Any]] = {}
20
+
18
21
  class RateLimitExceeded(CustomException):
19
22
  """Rate limit 초과 예외."""
20
23
 
@@ -200,14 +203,10 @@ def rate_limit(
200
203
  key_func: Optional[Callable] = None
201
204
  ):
202
205
  """Rate limiting 데코레이터."""
203
- rate_limits: Dict[str, Dict[str, Any]] = {}
204
-
205
206
  def decorator(func: Callable) -> Callable:
206
207
  @wraps(func)
207
208
  async def wrapper(*args, **kwargs):
208
209
  logging.info(f"[rate_limit] Starting rate limit check for {func.__name__}")
209
- logging.info(f"[rate_limit] Args: {args}")
210
- logging.info(f"[rate_limit] Kwargs: {kwargs}")
211
210
 
212
211
  # Request 객체 찾기
213
212
  request = None
@@ -240,16 +239,16 @@ def rate_limit(
240
239
  now = datetime.now(UTC)
241
240
 
242
241
  # 현재 rate limit 정보 가져오기
243
- rate_info = rate_limits.get(rate_limit_key)
242
+ rate_info = _rate_limits.get(rate_limit_key)
244
243
  logging.info(f"[rate_limit] Current rate info: {rate_info}")
245
244
 
246
245
  if rate_info is None or (now - rate_info["start_time"]).total_seconds() >= window_seconds:
247
246
  # 새로운 rate limit 설정
248
- rate_limits[rate_limit_key] = {
247
+ _rate_limits[rate_limit_key] = {
249
248
  "count": 1,
250
249
  "start_time": now
251
250
  }
252
- logging.info(f"[rate_limit] Created new rate limit: {rate_limits[rate_limit_key]}")
251
+ logging.info(f"[rate_limit] Created new rate limit: {_rate_limits[rate_limit_key]}")
253
252
  else:
254
253
  # 기존 rate limit 업데이트
255
254
  if rate_info["count"] >= max_requests:
@@ -268,14 +267,6 @@ def rate_limit(
268
267
 
269
268
  try:
270
269
  logging.info(f"[rate_limit] Executing original function: {func.__name__}")
271
- # db_service가 있는지 확인
272
- for arg in args:
273
- if hasattr(arg, 'db_service'):
274
- logging.info(f"[rate_limit] Found db_service in args: {arg.db_service}")
275
- for value in kwargs.values():
276
- if hasattr(value, 'db_service'):
277
- logging.info(f"[rate_limit] Found db_service in kwargs: {value.db_service}")
278
-
279
270
  result = await func(*args, **kwargs)
280
271
  logging.info("[rate_limit] Function executed successfully")
281
272
  return result
@@ -290,7 +281,7 @@ def rate_limit(
290
281
  source_function=func.__name__,
291
282
  original_error=e
292
283
  )
293
-
284
+
294
285
  return wrapper
295
286
  return decorator
296
287
 
aiteamutils/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """버전 정보"""
2
- __version__ = "0.2.36"
2
+ __version__ = "0.2.38"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiteamutils
3
- Version: 0.2.36
3
+ Version: 0.2.38
4
4
  Summary: AI Team Utilities
5
5
  Project-URL: Homepage, https://github.com/yourusername/aiteamutils
6
6
  Project-URL: Issues, https://github.com/yourusername/aiteamutils/issues
@@ -4,13 +4,13 @@ aiteamutils/base_repository.py,sha256=qdwQ7Sj2fUqxpDg6cWM48n_QbwPK_VUlG9zTSem8iC
4
4
  aiteamutils/base_service.py,sha256=E4dHGE0DvhmRyFplh46SwKJOSF_nUL7OAsCkf_ZJF_8,24733
5
5
  aiteamutils/cache.py,sha256=tr0Yn8VPYA9QHiKCUzciVlQ2J1RAwNo2K9lGMH4rY3s,1334
6
6
  aiteamutils/config.py,sha256=kFKMeIx1KcuEwwx4VjZdCgoTOHCkG3ySYVJ0G6cvMoA,2849
7
- aiteamutils/database.py,sha256=CaH73g8PPNcmLCz4Xr0DBtNSrLcpRlyt0F1zO5Tigfo,33811
8
- aiteamutils/dependencies.py,sha256=GgN8sFM7qGNmOfpLaFrhuHOgPlBVXWrEgxv29jxjEgI,4226
7
+ aiteamutils/database.py,sha256=ATFiwG_o6o1eeUa0VVQ_2E_ExC2Yg-Ui_eA62SL6vQk,35344
8
+ aiteamutils/dependencies.py,sha256=ShbGAWAEfZCe_8tfWW8D5zm7JA_OkcGEXX7sJr0xqfI,4605
9
9
  aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
10
10
  aiteamutils/exceptions.py,sha256=YV-ISya4wQlHk4twvGo16I5r8h22-tXpn9wa-b3WwDM,15231
11
- aiteamutils/security.py,sha256=U6EjpruMK6PB_gfUsX1kbrnv5pdkrg-vbJ266HDmelU,16563
11
+ aiteamutils/security.py,sha256=9gvEqDtE3RJaoCWqELPCjkg-IsSqZVrpMP6XPZaodWU,16024
12
12
  aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
13
- aiteamutils/version.py,sha256=xIPwqXH-2Fd8zt8Gh1JwJx_ISWM3WKZzxCLLIcTS6JU,42
14
- aiteamutils-0.2.36.dist-info/METADATA,sha256=0StxHP94IIzkjMUHtwHz2vJ-n9lFKhhMh3aSax2LZ3g,1718
15
- aiteamutils-0.2.36.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- aiteamutils-0.2.36.dist-info/RECORD,,
13
+ aiteamutils/version.py,sha256=ALyTk2taueqibsmGUnkLfV7gD0h49jnn_qqd8wQoe7M,42
14
+ aiteamutils-0.2.38.dist-info/METADATA,sha256=YuD1Vi-w0zGdm0tK3YdTBcpuBOVjdO1nlR3x1Onq45A,1718
15
+ aiteamutils-0.2.38.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ aiteamutils-0.2.38.dist-info/RECORD,,