Python如何单独捕获Errno13权限拒绝异常
问题原因
两个核心问题导致你无法单独捕获errno13权限错误:
- 异常捕获顺序错误:
PermissionError是OSError(Windows平台中WindowsError是OSError的别名)的子类,如果你把父类异常的except块写在子类前面,子类异常会被父类块提前拦截,永远走不到后面的PermissionError处理分支。 - 捕获的异常类型不匹配:
shutil.copytree复制目录时默认不会直接抛出原始的PermissionError/WindowsError,而是会收集整个复制过程中遇到的所有错误,最终抛出shutil.Error。你看到的包含三元路径元组的输出,就是shutil.Error的标准字符串格式,这个异常本身没有winerror属性,所以你在WindowsError块里判断winerror自然匹配不到errno13的权限错误。
正确实现方案
你可以根据需求二选一:
方案1:直接捕获原始权限错误(推荐)
给shutil.copytree传入错误回调,让它遇到错误时直接抛出原始异常,再按「子类在前、父类在后」的顺序捕获即可:
import shutil import errno def throw_original_error(func, path, exc_info): # 直接抛出捕获到的原始异常,不做收集 raise exc_info[1] try: print('Copying files...') shutil.copytree( 'C:/Users/' + userName + '/AppData/Roaming/Test/test1/', 'C:/Users/' + userName + '/Desktop/test', onerror=throw_original_error ) print('Success!') # 子类异常放最前面,优先匹配 except PermissionError as e: if e.errno == errno.EACCES: # errno.EACCES 就是数值13 print('You do not have the required privileges to make changes to this file.') print('One of the following solutions may work:') print(' >The file might be open in another location, close the file and try again.') print(' >Try to run the program as administrator.') except WindowsError as e: if e.winerror == 2 or e.winerror == 3: print('Requested file does not exist.') else: print('Unexpected Windows exception occurred. Please copy the following exception and send it to test at test@test.com') print(e) quit() except Exception as e: print('Unexpected exception occured. Please copy the following exception and send it to test at test@test.com') print(e) quit()
方案2:保留默认错误收集逻辑,解析shutil.Error
如果你需要保留copytree收集所有错误的特性,可以直接捕获shutil.Error,遍历它携带的错误列表判断是否存在权限错误:
import shutil import errno try: print('Copying files...') shutil.copytree( 'C:/Users/' + userName + '/AppData/Roaming/Test/test1/', 'C:/Users/' + userName + '/Desktop/test' ) print('Success!') except shutil.Error as e: # e.args[0] 是所有错误的列表,每个元素格式为(源路径, 目标路径, 异常信息) has_permission_denied = False for src, dst, err in e.args[0]: # 匹配Python3环境下的PermissionError实例 if isinstance(err, PermissionError) and err.errno == errno.EACCES: has_permission_denied = True break # 兼容Python2环境下错误信息为字符串的情况 if isinstance(err, str) and 'Errno 13' in err: has_permission_denied = True break if has_permission_denied: print('You do not have the required privileges to make changes to this file.') print('One of the following solutions may work:') print(' >The file might be open in another location, close the file and try again.') print(' >Try to run the program as administrator.') else: print('Unexpected exception occurred. Please copy the following exception and send it to test at test@test.com') print(e) quit() except WindowsError as e: if e.winerror == 2 or e.winerror == 3: print('Requested file does not exist.') else: print('Unexpected Windows exception occurred. Please copy the following exception and send it to test at test@test.com') print(e) quit() except Exception as e: print('Unexpected exception occured. Please copy the following exception and send it to test at test@test.com') print(e) quit()
注意事项
- 所有异常捕获必须严格遵循子类在前、父类在后的顺序,否则子类异常永远会被上层父类捕获块拦截。
- Windows平台下
winerror=5(访问拒绝)本身就属于PermissionError范畴,和errno13是同类型权限错误,不需要单独写分支判断,统一在PermissionError块处理即可。 - 你最初代码里的
except WindowsError, e是Python2专属语法,在Python3环境下会直接抛出语法错误,需要改成except WindowsError as e的写法。
内容的提问来源于stack exchange,提问作者Jatinder Brar
相关产品推荐
相关产品推荐

