mirror of
https://github.com/langgenius/dify.git
synced 2026-08-15 04:59:46 +08:00
fix(api): chain original exception in 13 except blocks per PEP 3134 (#40738)
Co-authored-by: Harsh Kashyap <Harsh23Kashyap@users.noreply.github.com>
This commit is contained in:
parent
c9c7fb9962
commit
8238684334
@ -96,8 +96,8 @@ class PartnerTenants(Resource):
|
||||
try:
|
||||
click_id = req_data.click_id
|
||||
decoded_partner_key = base64.b64decode(partner_key).decode("utf-8")
|
||||
except Exception:
|
||||
raise BadRequest("Invalid partner_key")
|
||||
except Exception as e:
|
||||
raise BadRequest("Invalid partner_key") from e
|
||||
|
||||
if not click_id or not decoded_partner_key or not current_user.id:
|
||||
raise BadRequest("Invalid partner information")
|
||||
|
||||
@ -302,8 +302,8 @@ class SwitchWorkspaceApi(Resource):
|
||||
# Check whether the tenant_id belongs to the current account.
|
||||
try:
|
||||
TenantService.switch_tenant(current_user, args.tenant_id, session=session)
|
||||
except Exception:
|
||||
raise AccountNotLinkTenantError("Account not link tenant")
|
||||
except Exception as e:
|
||||
raise AccountNotLinkTenantError("Account not link tenant") from e
|
||||
|
||||
new_tenant = TenantService.get_tenant_by_id(args.tenant_id, session=session)
|
||||
if new_tenant is None:
|
||||
|
||||
@ -67,8 +67,8 @@ class ToolFileApi(Resource):
|
||||
except NotFound:
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
raise UnsupportedFileTypeError()
|
||||
except Exception as e:
|
||||
raise UnsupportedFileTypeError() from e
|
||||
|
||||
mime_type = tool_file.mime_type
|
||||
filename = tool_file.filename
|
||||
|
||||
@ -84,8 +84,8 @@ def get_user(tenant_id: str, user_id: str | None) -> EndUser:
|
||||
session.flush()
|
||||
session.refresh(user_model)
|
||||
|
||||
except Exception:
|
||||
raise ValueError("user not found")
|
||||
except Exception as e:
|
||||
raise ValueError("user not found") from e
|
||||
|
||||
return user_model
|
||||
|
||||
@ -131,13 +131,13 @@ def plugin_data[**P, R](
|
||||
def decorated_view(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
try:
|
||||
data = request.get_json()
|
||||
except Exception:
|
||||
raise ValueError("invalid json")
|
||||
except Exception as e:
|
||||
raise ValueError("invalid json") from e
|
||||
|
||||
try:
|
||||
payload = payload_type.model_validate(data)
|
||||
except Exception as e:
|
||||
raise ValueError(f"invalid payload: {str(e)}")
|
||||
raise ValueError(f"invalid payload: {str(e)}") from e
|
||||
|
||||
kwargs["payload"] = payload
|
||||
return view_func(*args, **kwargs)
|
||||
|
||||
@ -270,8 +270,8 @@ class PluginAppBackwardsInvocation(BaseBackwardsInvocation):
|
||||
app = session.scalar(select(App).where(App.id == app_id, App.tenant_id == tenant_id).limit(1))
|
||||
if app:
|
||||
session.expunge(app)
|
||||
except Exception:
|
||||
raise ValueError("app not found")
|
||||
except Exception as e:
|
||||
raise ValueError("app not found") from e
|
||||
|
||||
if not app:
|
||||
raise ValueError("app not found")
|
||||
|
||||
@ -241,8 +241,10 @@ def cast_parameter_value(typ: StrEnum, value: Any, /):
|
||||
return str(value)
|
||||
except ValueError:
|
||||
raise
|
||||
except Exception:
|
||||
raise ValueError(f"The tool parameter value {repr(value)} is not in correct type of {as_normal_type(typ)}.")
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
f"The tool parameter value {repr(value)} is not in correct type of {as_normal_type(typ)}."
|
||||
) from e
|
||||
|
||||
|
||||
def init_frontend_parameter(rule: PluginParameter, type: StrEnum, value: Any):
|
||||
|
||||
@ -276,19 +276,19 @@ class BasePluginClient:
|
||||
json_response = transformer(json_response)
|
||||
# https://stackoverflow.com/questions/59634937/variable-foo-class-is-not-valid-as-type-but-why
|
||||
rep = PluginDaemonBasicResponse[type_].model_validate(json_response) # type: ignore
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
msg = (
|
||||
f"Failed to parse response from plugin daemon to PluginDaemonBasicResponse [{str(type_.__name__)}],"
|
||||
f" url: {path}"
|
||||
)
|
||||
logger.exception(msg)
|
||||
raise ValueError(msg)
|
||||
raise ValueError(msg) from e
|
||||
|
||||
if rep.code != 0:
|
||||
try:
|
||||
error = PluginDaemonError.model_validate(json.loads(rep.message))
|
||||
except Exception:
|
||||
raise ValueError(f"{rep.message}, code: {rep.code}")
|
||||
except Exception as e:
|
||||
raise ValueError(f"{rep.message}, code: {rep.code}") from e
|
||||
|
||||
self._handle_plugin_daemon_error(error.error_type, error.message)
|
||||
if rep.data is None:
|
||||
|
||||
@ -19,5 +19,5 @@ class StructuredChatOutputParser:
|
||||
return ReactAction(response["action"], response.get("action_input", {}), text)
|
||||
else:
|
||||
return ReactFinish({"output": text}, text)
|
||||
except Exception:
|
||||
raise ValueError(f"Could not parse LLM output: {text}")
|
||||
except Exception as e:
|
||||
raise ValueError(f"Could not parse LLM output: {text}") from e
|
||||
|
||||
@ -238,9 +238,9 @@ class ApiToolManageService:
|
||||
|
||||
# try to parse schema, avoid SSRF attack
|
||||
ApiToolManageService.parser_api_schema(schema)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.exception("parse api schema error")
|
||||
raise ValueError("invalid schema, please check the url you provided")
|
||||
raise ValueError("invalid schema, please check the url you provided") from e
|
||||
|
||||
return {"schema": schema}
|
||||
|
||||
@ -453,8 +453,8 @@ class ApiToolManageService:
|
||||
|
||||
try:
|
||||
tool_bundles, _ = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema)
|
||||
except Exception:
|
||||
raise ValueError("invalid schema")
|
||||
except Exception as e:
|
||||
raise ValueError("invalid schema") from e
|
||||
|
||||
# get tool bundle
|
||||
tool_bundle = next(filter(lambda tb: tb.operation_id == tool_name, tool_bundles), None)
|
||||
|
||||
@ -219,8 +219,11 @@ class TestGetUser:
|
||||
|
||||
event.listen(sqlite_plugin_engine, "before_cursor_execute", _raise_database_error)
|
||||
try:
|
||||
with app.app_context(), pytest.raises(ValueError, match="user not found"):
|
||||
with app.app_context(), pytest.raises(ValueError, match="user not found") as exc_info:
|
||||
get_user("tenant123", "user123")
|
||||
# PEP 3134: the re-raised ValueError must chain the underlying database error.
|
||||
assert isinstance(exc_info.value.__cause__, RuntimeError)
|
||||
assert str(exc_info.value.__cause__) == "Database error"
|
||||
finally:
|
||||
event.remove(sqlite_plugin_engine, "before_cursor_execute", _raise_database_error)
|
||||
|
||||
@ -351,8 +354,11 @@ class TestPluginData:
|
||||
|
||||
# Act & Assert - Malformed JSON triggers ValueError
|
||||
with app.test_request_context(data="not valid json", content_type="application/json"):
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
protected_view()
|
||||
# PEP 3134: the re-raised ValueError must chain the underlying JSON parse failure.
|
||||
assert exc_info.value.__cause__ is not None
|
||||
assert isinstance(exc_info.value.__cause__, Exception)
|
||||
|
||||
def test_should_raise_error_on_invalid_payload(self, app: Flask):
|
||||
"""Test that ValueError is raised when payload validation fails"""
|
||||
@ -369,8 +375,11 @@ class TestPluginData:
|
||||
|
||||
# Act & Assert
|
||||
with app.test_request_context(json={"data": "test"}):
|
||||
with pytest.raises(ValueError, match="invalid payload"):
|
||||
with pytest.raises(ValueError, match="invalid payload") as exc_info:
|
||||
protected_view()
|
||||
# PEP 3134: the re-raised ValueError must chain the original Exception from model_validate.
|
||||
assert exc_info.value.__cause__ is not None
|
||||
assert str(exc_info.value.__cause__) == "Validation failed"
|
||||
|
||||
def test_should_work_as_parameterized_decorator(self, app: Flask):
|
||||
"""Test that decorator works when used with parentheses"""
|
||||
|
||||
@ -244,9 +244,15 @@ class TestPluginParameterEntities:
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=r"The tool parameter value <.*_BadString object at .* is not in correct type of string\.",
|
||||
):
|
||||
) as exc_info:
|
||||
cast_parameter_value(PluginParameterType.STRING, _BadString())
|
||||
|
||||
# PEP 3134: the re-raised ValueError must chain the underlying RuntimeError
|
||||
# so the original traceback is preserved for debugging.
|
||||
assert exc_info.value.__cause__ is not None
|
||||
assert isinstance(exc_info.value.__cause__, RuntimeError)
|
||||
assert str(exc_info.value.__cause__) == "boom"
|
||||
|
||||
def test_init_frontend_parameter(self):
|
||||
rule = PluginParameter(
|
||||
name="choice",
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from core.rag.retrieval.output_parser.react_output import ReactAction, ReactFinish
|
||||
@ -65,5 +67,10 @@ class TestStructuredChatOutputParser:
|
||||
parser = StructuredChatOutputParser()
|
||||
text = 'Action:\n```json\n{"action":"search","action_input": }\n```'
|
||||
|
||||
with pytest.raises(ValueError, match="Could not parse LLM output"):
|
||||
with pytest.raises(ValueError, match="Could not parse LLM output") as exc_info:
|
||||
parser.parse(text)
|
||||
|
||||
# PEP 3134: the re-raised ValueError must chain the underlying parse failure
|
||||
# (json.JSONDecodeError on the malformed JSON) so the original traceback is preserved.
|
||||
assert exc_info.value.__cause__ is not None
|
||||
assert isinstance(exc_info.value.__cause__, json.JSONDecodeError)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user