Skip to content

Commit 9af6307

Browse files
fix(utils): resolve all MyPy and Ruff errors in helpers/utils
1 parent cfb0655 commit 9af6307

4 files changed

Lines changed: 43 additions & 19 deletions

File tree

archipy/helpers/utils/app_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def setup_elastic_apm(app: FastAPI, config: BaseConfig) -> None:
166166
from elasticapm.contrib.starlette import ElasticAPM, make_apm_client
167167

168168
apm_client = make_apm_client(config.ELASTIC_APM.model_dump())
169-
app.add_middleware(ElasticAPM, client=apm_client)
169+
app.add_middleware(ElasticAPM, client=apm_client) # type: ignore[arg-type]
170170
except Exception:
171171
logging.exception("Failed to initialize Elastic APM")
172172

archipy/helpers/utils/keycloak_utils.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from typing import Any
66

77
try:
8-
import grpc
98
from grpc import ServicerContext
109
from grpc.aio import ServicerContext as AsyncServicerContext
1110

@@ -116,18 +115,21 @@ def dependency(
116115
request: Request,
117116
token: HTTPAuthorizationCredentials = Security(security),
118117
keycloak: KeycloakAdapter = Depends(cls._get_keycloak_adapter),
119-
) -> dict:
118+
) -> dict[str, Any]:
120119
if token is None:
121120
raise UnauthenticatedError(lang=lang)
122121
token_str = token.credentials # Extract the token string
123122
# Validate token
124123
if not keycloak.validate_token(token_str):
125124
token_info = keycloak.introspect_token(token_str)
126-
if not token_info.get("active", False):
125+
if not token_info or not token_info.get("active", False):
127126
raise TokenExpiredError(lang=lang)
128127

129128
# Get user info from token
130129
user_info = keycloak.get_userinfo(token_str)
130+
if not user_info:
131+
raise UnauthenticatedError(lang=lang)
132+
131133
token_info = keycloak.get_token_info(token_str)
132134

133135
# Resource-based authorization if resource type is provided
@@ -219,7 +221,7 @@ async def dependency(
219221
request: Request,
220222
token: HTTPAuthorizationCredentials = Security(security),
221223
keycloak: AsyncKeycloakAdapter = Depends(cls._get_async_keycloak_adapter),
222-
) -> dict:
224+
) -> dict[str, Any]:
223225
if token is None:
224226
raise UnauthenticatedError(lang=lang)
225227
token_str = token.credentials # Extract the token string
@@ -228,11 +230,14 @@ async def dependency(
228230
if not await keycloak.validate_token(token_str):
229231
# Handle token validation error
230232
token_info = await keycloak.introspect_token(token_str)
231-
if not token_info.get("active", False):
233+
if not token_info or not token_info.get("active", False):
232234
raise TokenExpiredError(lang=lang)
233235

234236
# Get user info from token
235237
user_info = await keycloak.get_userinfo(token_str)
238+
if not user_info:
239+
raise UnauthenticatedError(lang=lang)
240+
236241
token_info = await keycloak.get_token_info(token_str)
237242

238243
# Resource-based authorization if resource type is provided
@@ -287,6 +292,8 @@ async def dependency(
287292
# Add user info to request state
288293
request.state.user_info = user_info
289294
request.state.token_info = token_info
295+
if not user_info:
296+
raise UnauthenticatedError(lang=lang)
290297
return user_info
291298

292299
return dependency
@@ -302,11 +309,11 @@ def _extract_token_from_metadata(context: ServicerContext) -> str | None:
302309
if key in metadata:
303310
auth_value = metadata[key]
304311
if auth_value.startswith("Bearer "):
305-
return auth_value[7:]
312+
return str(auth_value[7:])
306313
elif auth_value.startswith("bearer "):
307-
return auth_value[7:]
314+
return str(auth_value[7:])
308315
else:
309-
return auth_value
316+
return str(auth_value)
310317

311318
return None
312319

@@ -356,11 +363,13 @@ def wrapper(self: object, request: object, context: ServicerContext) -> object:
356363
# 3. Validate token
357364
if not keycloak.validate_token(token_str):
358365
token_info = keycloak.introspect_token(token_str)
359-
if not token_info.get("active", False):
366+
if not token_info or not token_info.get("active", False):
360367
raise TokenExpiredError(lang=lang)
361368

362369
# 4. Get user info from token
363370
user_info = keycloak.get_userinfo(token_str)
371+
if not user_info:
372+
raise UnauthenticatedError(lang=lang)
364373

365374
# 5. Resource-based authorization if resource_attribute_name is provided
366375
if resource_attribute_name:
@@ -494,11 +503,13 @@ async def wrapper(self: object, request: object, context: AsyncServicerContext)
494503
# 3. Validate token
495504
if not await keycloak.validate_token(token_str):
496505
token_info = await keycloak.introspect_token(token_str)
497-
if not token_info.get("active", False):
506+
if not token_info or not token_info.get("active", False):
498507
raise TokenExpiredError(lang=lang)
499508

500509
# 4. Get user info from token
501510
user_info = await keycloak.get_userinfo(token_str)
511+
if not user_info:
512+
raise UnauthenticatedError(lang=lang)
502513

503514
# 5. Resource-based authorization if resource_attribute_name is provided
504515
if resource_attribute_name:
@@ -573,10 +584,12 @@ async def wrapper(self: object, request: object, context: AsyncServicerContext)
573584
except Exception as e:
574585
if isinstance(e, BaseError):
575586
await e.abort_grpc_async(context)
587+
return None # abort_grpc_async will terminate, but satisfy type checker
576588
await InternalError(
577589
lang=lang,
578590
additional_data={"original_error": str(e), "error_type": type(e).__name__},
579591
).abort_grpc_async(context)
592+
return None # abort_grpc_async will terminate, but satisfy type checker
580593

581594
finally:
582595
# Clean up auth context

archipy/helpers/utils/string_utils.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import re
2-
from re import compile
2+
from re import compile as re_compile
33

44
from archipy.helpers.utils.string_utils_constants import StringUtilsConstants
55

@@ -169,7 +169,7 @@ def mask_urls(cls, text: str, mask: str | None = None) -> str:
169169
str: The text with URLs masked.
170170
"""
171171
mask = mask or "MASK_URL"
172-
return compile(r"https?://\S+|www\.\S+").sub(f" {mask} ", text)
172+
return re_compile(r"https?://\S+|www\.\S+").sub(f" {mask} ", text)
173173

174174
@classmethod
175175
def mask_emails(cls, text: str, mask: str | None = None) -> str:
@@ -183,7 +183,7 @@ def mask_emails(cls, text: str, mask: str | None = None) -> str:
183183
str: The text with email addresses masked.
184184
"""
185185
mask = mask or "MASK_EMAIL"
186-
return compile(r"\S+@\S+\.\S+").sub(f" {mask} ", text)
186+
return re_compile(r"\S+@\S+\.\S+").sub(f" {mask} ", text)
187187

188188
@classmethod
189189
def mask_phones(cls, text: str, mask: str | None = None) -> str:
@@ -197,7 +197,7 @@ def mask_phones(cls, text: str, mask: str | None = None) -> str:
197197
str: The text with phone numbers masked.
198198
"""
199199
mask = mask or "MASK_PHONE"
200-
return compile(r"(?:\+98|0)?(?:\d{3}\s*?\d{3}\s*?\d{4})").sub(f" {mask} ", text)
200+
return re_compile(r"(?:\+98|0)?(?:\d{3}\s*?\d{3}\s*?\d{4})").sub(f" {mask} ", text)
201201

202202
@classmethod
203203
def convert_english_number_to_persian(cls, text: str) -> str:
@@ -308,7 +308,7 @@ def replace_currencies_with_mask(cls, text: str, mask: str | None = None) -> str
308308
str: The text with currency symbols and amounts masked.
309309
"""
310310
mask = mask or "MASK_CURRENCIES"
311-
currency_pattern = compile(r"(\\|zł|£|\$|₡|₦|¥|₩|₪|₫|€|₱|₲|₴|₹|﷼)+")
311+
currency_pattern = re_compile(r"(\\|zł|£|\$|₡|₦|¥|₩|₪|₫|€|₱|₲|₴|₹|﷼)+")
312312
return currency_pattern.sub(f" {mask} ", text)
313313

314314
@classmethod

archipy/helpers/utils/string_utils_constants.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
from re import compile
1+
import re
2+
from re import compile as re_compile
23

34

4-
def compile_patterns(patterns):
5-
return [(compile(pattern), repl) for pattern, repl in patterns]
5+
def compile_patterns(patterns: list[tuple[str, str]]) -> list[tuple[re.Pattern[str], str]]:
6+
"""Compile regex patterns with their replacement strings.
7+
8+
Args:
9+
patterns: List of tuples containing (pattern, replacement) pairs.
10+
11+
Returns:
12+
List of tuples containing (compiled_pattern, replacement) pairs.
13+
"""
14+
return [(re_compile(pattern), repl) for pattern, repl in patterns]
615

716

817
class StringUtilsConstants:
18+
"""Constants for string utility operations including translation tables and regex patterns."""
19+
920
arabic_vowel_translate_table = str.maketrans(
1021
dict.fromkeys(
1122
"\u064e\u064f\u0650\u0652\u0651\u0653\u064b\u064c\u0621\u064d\u0670" # Normal vowels (Fatha, Damma, Kasra, etc)

0 commit comments

Comments
 (0)