You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python API认证函数抛异常后else块仍执行的问题咨询

API认证函数异常处理问题

场景描述

我正在编写一个API认证函数,期望实现出现错误时抛出异常,函数成功执行时返回值的逻辑,代码如下:

import json
import http.client as httplib
import mimetypes
import urllib.parse as urlparse
import csv
import time
import os
import glob
import socket
import requests
from datetime import datetime
from loguru import logger

session = requests.Session()


@logger.catch
def authenticate(hostname, client_id, client_secret, username, password):
    """ Authenticate via username/password. Returns json token object.
     
    Args:
    string hostname - hostname like "myaccount.sharefile.com"
    string client_id - OAuth2 client_id key
    string client_secret - OAuth2 client_secret key
    string username - my@user.name
    string password - my password """


    uri_path = '/oauth/token'
     
    headers = {'Content-Type':'application/x-www-form-urlencoded'}
    params = {'grant_type':'password', 'client_id':client_id, 'client_secret':client_secret,
              'username':username, 'password':password}
     
    url=f"{hostname}{uri_path}"
    response = requests.post(url,params)

    if response.status_code == 200:
      token = response.json()
      return token
    else:
      raise RuntimeError(f"ShareFile API Authentication Error {response.status_code} {response.reason}")

在主逻辑中调用该函数时使用了try-except-else结构:

try:
    token = authenticate(
                      hostname = "https://sharefile.com", 
                      client_id = "id", 
                      client_secret = "secret", 
                      username = "email", 
                      password = "test password")    
except RuntimeError:
    logger.error("Authentication error!")
else:
    logger.info("Authentication successful")

遇到的问题

出现异常时,能看到RuntimeError的日志,但else块仍会执行(日志中输出“Authentication successful”)。我怀疑是authenticate()函数抛出异常时返回了None导致的,希望保留异常用于日志记录,想知道这种情况是否合理,以及对应的最佳实践。

日志文件内容如下:

2025-04-15 10:19:00 - ERROR - An error has been caught in function '<module>', process 'MainProcess' (2468551), thread 'MainThread' (140286569478016):
Traceback (most recent call last):

> File "/testing/Python/Code/test.py", line 41, in <module>
    token = sf_api.authenticate(
            │      └ <function authenticate at 0x7fcde1f3ea20>
            └ <module 'ShareFile_API' from '/testing/Python/Code/utilities/ShareFile_API.py'>

  File "/testing/Python/Code/utilities/ShareFile_API.py", line 50, in authenticate
    raise RuntimeError(f"ShareFile API Authentication Error {response.status_code} {response.reason}")

RuntimeError: ShareFile API Authentication Error 403 Forbidden
2025-04-15 13:12:11 - INFO - Authentication successful

分析与解决方案

问题原因

根本原因是@logger.catch装饰器的默认行为:当函数抛出异常时,这个装饰器会捕获异常并记录日志,然后返回None而非重新抛出异常。这导致外层的try-except块根本没捕获到异常——因为异常已经被装饰器“吃掉”,函数最终返回None,try块判定执行成功,所以触发了else分支。

解决方案

方案1:修改@logger.catch配置,让它重新抛出异常

在装饰器中添加reraise=True参数,这样装饰器记录日志后会把异常重新抛出来,让外层try-except可以正常捕获:

@logger.catch(reraise=True)
def authenticate(hostname, client_id, client_secret, username, password):
    # 函数内容保持不变

方案2:移除@logger.catch,在try-except中统一处理日志

如果不需要在函数内部记录异常,而是希望外层统一处理,可以去掉函数上的装饰器,在except块中记录详细错误信息:

# 去掉authenticate函数上的@logger.catch装饰器
def authenticate(hostname, client_id, client_secret, username, password):
    # 函数内容保持不变

# 调用时的逻辑
try:
    token = authenticate(...)
except RuntimeError as e:
    logger.error(f"Authentication error! {str(e)}")
else:
    logger.info("Authentication successful")

最佳实践

  1. 明确异常捕获层级:要么在函数内部处理异常并记录,要么将异常抛出到外层处理,避免多层捕获导致逻辑混乱。
  2. 合理使用loguru的reraise参数:如果需要在函数内自动记录异常堆栈,同时让外层处理后续逻辑,必须设置reraise=True,否则异常会被吞掉。
  3. 避免冗余日志:如果装饰器已经记录了完整的异常堆栈,外层except块只需记录简洁的错误提示,无需重复打印堆栈。
  4. 使用自定义异常类型:对于API认证这类特定场景,可以定义专属异常类(比如AuthenticationFailedError),让异常处理更精准,避免误捕获不相关的RuntimeError。

示例:自定义异常实现

class AuthenticationFailedError(Exception):
    """认证失败专属异常"""
    pass

# 在authenticate函数中抛出自定义异常
else:
    raise AuthenticationFailedError(f"ShareFile API Authentication Error {response.status_code} {response.reason}")

# 调用时捕获自定义异常
except AuthenticationFailedError as e:
    logger.error(f"Authentication failed: {str(e)}")

内容的提问来源于stack exchange,提问作者Alvin Li

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:38:09