【问题标题】:Searching for equivalent of FileNotFoundError in Python 2在 Python 2 中搜索 FileNotFoundError 的等价物
【发布时间】:2014-02-17 11:49:00
【问题描述】:

我创建了一个名为 Options 的类。它工作正常,但不适用于 Python 2。 我希望它同时适用于 Python 2 和 3。 问题已确定:FileNotFoundError 在 Python 2 中不存在。 但是如果我使用 IOError 它在 Python 3 中不起作用

3.3 版更改:EnvironmentError、IOError、WindowsError、VMSError、socket.error、select.error 和 mmap.error 已合并到 OSError。

我该怎么办???(请不要讨论我对便携性的选择,我有理由。)

代码如下:

#!/usr/bin/python
#-*-coding:utf-8*

#option_controller.py

#Walle Cyril
#25/01/2014

import json
import os

class Options():
    """Options is a class designed to read, add and change informations in a JSON file with a dictionnary in it.

    The entire object works even if the file is missing since it re-creates it.
    If present it must respect the JSON format: e.g. keys must be strings and so on.
    If something corrupted the file, just destroy the file or call read_file method to remake it."""

    def __init__(self,directory_name="Cache",file_name="options.json",imported_default_values=None):
        #json file
        self.option_file_path=os.path.join(directory_name,file_name)
        self.directory_name=directory_name
        self.file_name=file_name
        #self.parameters_json_file={'sort_keys':True, 'indent':4, 'separators':(',',':')}
        #the default data
        if imported_default_values is None:
            DEFAULT_INDENT = 2
            self.default_values={\
                "translate_html_level": 1,\
                "indent_size":DEFAULT_INDENT,\
                "document_title":"Titre"}
        else:
            self.default_values=imported_default_values


    def read_file(self,read_this_key_only=False):
        """returns the value for the given key or a dictionary if the key is not given.

        returns None if it s impossible"""
        try:
            text_in_file=open(self.option_file_path,'r').read()
        except FileNotFoundError:#not 2.X compatible
            text_in_file=""#if the file is not there we re-make one with default values
        if text_in_file=="":#same if the file is empty
            self.__insert_all_default_values()
            text_in_file=open(self.option_file_path,'r').read()

        try:
            option_dict=json.loads(text_in_file)
        except ValueError:
            #if the json file is broken we re-make one with default values
            self.__insert_all_default_values()
            text_in_file=open(self.option_file_path,'r').read()
            option_dict=json.loads(text_in_file)

        if read_this_key_only:
            if read_this_key_only in option_dict:
                return option_dict[read_this_key_only]#
            else:
                #if the value is not there it should be written for the next time
                if read_this_key_only in self.default_values:
                    self.add_option_to_file(read_this_key_only,self.default_values[read_this_key_only])
                    return self.default_values[read_this_key_only]
                else:
                    #impossible because there is not default value so the value isn t meant to be here
                    return None
        else:
            return option_dict

    def add_option_to_file(self,key,value):#or update
        """Adds or updates an option(key and value) to the json file if the option exists in the default_values of the object."""

        option_dict=self.read_file()
        if key in self.default_values:
            option_dict[key]=value
        open(self.option_file_path,'w').write(\
            json.dumps(option_dict,sort_keys=True, indent=4, separators=(',',':')))


    def __insert_all_default_values(self):
        """Recreate json file with default values.

    called if the document is empty or non-existing or corrupted."""
        try:
            open(self.option_file_path,'w').write(\
            json.dumps(self.default_values,sort_keys=True, indent=4, separators=(',',':')))
        except FileNotFoundError:
            os.mkdir(self.directory_name)#Create the directory
            if os.path.isdir(self.directory_name):#succes
                self.__insert_all_default_values()
            else:
                print("Impossible to write in %s and file %s not found" % (os.getcwd(),self.option_file_path))


#demo
if __name__ == '__main__':

    option_file_object=Options()
    print(option_file_object.__doc__)
    print(option_file_object.read_file())
    option_file_object.add_option_to_file("","test")#this should have no effect

    option_file_object.add_option_to_file("translate_html_level","0")#this should have an effect
    print("value of translate_html_level:",option_file_object.read_file("translate_html_level"))
    print(option_file_object.read_file())

【问题讨论】:

    标签: python python-3.x python-2.7 file exception-handling


    【解决方案1】:

    您可以使用基类异常EnvironmentError 并使用“errno”属性来确定引发了哪个异常:

    from __future__ import print_function
    
    import os
    import errno
    
    try:
        open('no file of this name')   # generate 'file not found error'
    except EnvironmentError as e:      # OSError or IOError...
        print(os.strerror(e.errno))  
    

    或者直接用同样的方式使用 IOError:

    try:
        open('/Users/test/Documents/test')   # will be a permission error
    except IOError as e:
        print(os.strerror(e.errno))  
    

    这适用于 Python 2 或 Python 3。

    注意不要直接与数值比较,因为它们can be different在不同的平台上。相反,请使用Python's standard library errno module 中的命名常量,这将为运行时平台使用正确的值。

    【讨论】:

    • 我建议使用EnvironmentError 而不是IOError。原因如下:在 Python2.7 上,一些操作(例如 os.remove)引发 WindowsErrorOSError 的类型(不是 IOError)。在 Python 2.7 中,OSErrorIOError 都继承自 EnvironmentError,在 Python 3.3+ 中和在 Python 3.3+ 中,EnvironmentErrorOSError 的别名。在 Python 3 中,FileNotFoundError 继承自 OSError(又名EnvironmentError)。所以,EnvironmentError 应该涵盖所有基础。使用EnvironmentError
    【解决方案2】:

    如果FileNotFoundError 不存在,请定义它:

    try:
        FileNotFoundError
    except NameError:
        FileNotFoundError = IOError
    

    现在您可以在 Python 2 中捕获 FileNotFoundError,因为它实际上是 IOError

    不过要小心,IOError 有其他含义。特别是,任何消息都应该说“无法读取文件”而不是“找不到文件”。

    【讨论】:

    • 不错的解决方案。因为我使用six,所以我做了:if six.PY2: FileNotFoundError = IOError
    • 如果您只想排除“找不到文件”错误,请参阅我的解决方案stackoverflow.com/a/55755432/7830612
    • os.remove("fake_file") 在 Python 2.7.17 中引发 WindowsError,它继承自 OSError,而不是 IOError
    【解决方案3】:

    对于它的价值,尽管IOErrorPython 3's official document 中几乎没有提及,甚至在its official Exception hierarchy 中也没有出现,但它仍然存在,它是 Python 3 中FileNotFoundError 的父类。请参阅python3 -c "print(isinstance(FileNotFoundError(), IOError))" 给你一个True。因此,您可以在技术上以这种方式编写代码,这适用于 Python 2 和 Python 3。

    try: 
        content = open("somefile.txt").read()
    except IOError:  # Works in both Python 2 & 3
        print("Oops, we can not read this file")
    

    在许多情况下它可能“足够好”。虽然一般来说,不建议依赖未记录的行为。所以,我并不是真的建议这种方法。我个人使用Kindall's answer

    【讨论】:

    • IOerror 并不理想,因为os.listdir() 在 Python 2 上引发了 OSError,但在 Python 3 上引发了 FileNotFoundError
    【解决方案4】:

    除了FileNotFoundError 之外的 Python 2 / 3 兼容方式是这样的:

    import errno
    
    try:
        with open('some_file_that_does_not_exist', 'r'):
            pass
    except EnvironmentError as e:
        if e.errno != errno.ENOENT:
            raise
    

    其他答案很接近,但如果错误编号不匹配,请不要重新提出。

    在大多数情况下使用IOError 很好,但出于某种原因os.listdir() 和朋友提出OSError 而不是在Python 2 上。因为IOError 继承自OSError,所以总是抓住@ 很好987654328@并检查错误号。

    编辑:上一句仅在 Python 3 上成立。为了交叉兼容,请改为捕获 EnvironmentError 并检查错误号。

    【讨论】:

    • IOError 是否继承自 OSError?它不适用于 macOS 上的 Python 2.7.16。相反,它们都继承自EnvironmentError
    • @Jason R. Coombs 我刚刚在 Python 2.7.10 上再次测试了这个,看起来你是对的。我已经更新了答案。
    猜你喜欢
    • 1970-01-01
    • 2013-12-13
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 2018-09-06
    相关资源
    最近更新 更多