Python:当文件应该与文本一起保存时,文件被保存为空,但只有在之前运行了一个不相关的方法时

Python:当文件应该与文本一起保存时,文件被保存为空,但只有在之前运行了一个不相关的方法时,python,Python,请遵守以下Python文件: # configmanager.py """ ConfigManager controls the modification and validation of config files. """ import os from ruamel import yaml from voluptuous import Schema class ConfigManager(): """ Controls all interaction with con

请遵守以下Python文件:

# configmanager.py
"""
ConfigManager controls the modification and validation of config files.
"""

import os

from ruamel import yaml
from voluptuous import Schema


class ConfigManager():
    """
    Controls all interaction with configuration files
    """

    def __init__(self):
        super().__init__()

        self.configvalidator = ConfigValidator()

    # The config directory inside users home directory.
    # Config files will be stored here.
    config_dir = os.path.expanduser('~')+'/.config/MyProject/'

    # The default config file
    config_file = config_dir+'myproject.conf'

    # The default configuration
    default_config = {
        'key1': {},
        'key2': {}
    }

    def _get_config(self):
        """
        Get the config file and return it as python dictionary.

        Will create the config directory and default config file if they
        do not exist.
        """

        # Create config directory if it does not exist
        if not os.path.exists(self.config_dir):
            os.makedirs(self.config_dir)

        # Create default config file if it does not exist
        if not os.path.isfile(self.config_file):
            config_file = open(self.config_file, 'w')
            config_file.write(yaml.dump(self.default_config))

        # Open config file, and load from YAML
        config_file = open(self.config_file, 'r')
        config = yaml.safe_load(config_file)

        # Validate config
        self.configvalidator.validate(config)

        return config

    def _save_config(self, config):
        """
        Save the config file to disk as YAML
        """

        # Open current config file
        config_file = open(self.config_file, 'w')

        # Validate new config
        # THE ERROR IS HERE
        # If this runs then the config file gets saved as an empty file.
        self.configvalidator.validate(config)

        # This shows that the 'config' variable still holds the data
        print(config)

        # This shows that yaml.dump() is working correctly
        print(yaml.dump(config))

        config_file.write(yaml.dump(config))

    def edit_config(self):
        """
        Edits the configuration file
        """

        config = self._get_config()

        # Perform manipulation on config here
        # No actual changes to the config are necessary for the bug to occur

        self._save_config(config)


class ConfigValidator():
    def __init__(self):
        super().__init__()

        # Config file schema
        # Used for validating the config file with voluptuous
        self.schema = Schema({
            'key1': {},
            'key2': {},
        })

    def validate(self, config):
        """
        Validates the data against the defined schema.
        """
        self.schema(config)

app = ConfigManager()
app.edit_config()
-

我的模块说明 这是我正在使用的一个模块,用于修改项目的配置文件。它访问
~/.config/MyProject/MyProject.conf
中以YAML格式保存的文件,并存储程序使用的各种信息。我已经删除了尽可能多的代码,只留下理解bug所必需的代码

配置管理器
ConfigManager
是包含用于操作配置文件的方法的类。这里它包含三种方法:
\u get\u config()
\u save\u config()
,和
edit\u config()
。实例化后,它将获得
ConfigValidator
(如下所述)的实例,并将其分配给
self.ConfigValidator

_获取配置
\u get\u config()
只需打开由类变量定义的文件,特别是
~/.config/MyProject/MyProject.conf
,或者使用默认值创建文件(如果该文件不存在)。该文件以YAML格式保存,因此该方法使用将其加载到python对象中,使用
self.configvalidator.validate(config)
对其进行验证,并返回该文件供其他代码段使用

_保存配置
\u save\u config()
是发生错误的地方,下面将详细介绍。它的目的是验证给定的数据,如果数据有效,则以YAML格式保存到磁盘

编辑配置 这是一个通用函数,在我的程序中,它将根据给定的参数对配置文件进行特定更改。在我的示例中,此函数仅使用
self.\u get\u config()
获取配置文件,然后使用
self.\u save\u config
保存它,而不做任何更改

配置验证器 此类用于使用验证我的配置文件。实例化后,它将创建要使用的模式,并将其分配给
self.schema
。当运行
validate
方法时,它使用
volupturous
验证给定数据

错误 观察
ConfigManager.\u save\u config()
中的行
self.configvalidator.validate(config)
。这将根据模式验证给定数据,如果未通过验证,则会引发错误

但是,在下面的
config\u file.write(yaml.dump(config))
行中,它只是将给定数据保存到一个文件中作为yaml,它将把一个空的文件保存到磁盘。(注意:文件为空,未删除)

如果我通过注释掉self.configvalidator.validate(config)禁用验证,则文件正确写入为YAML

如果运行了
self.configvalidator.validate(config)
,则配置文件保存为空文件

我的测试 从行
print(config)
中可以看出,变量
config
中的数据在用于验证后不会更改,但在保存到磁盘时,似乎
config
是一个空变量

print(yaml.dump(config))
显示
ruamel.yaml
工作正常

如果我更改
edit_-config
以将无效数据提供给
\u-save_-config
,则
self.configvalidator.validate(config)
将按预期引发错误<代码>self.configvalidator.validate(config)正在正确运行

终点 如果运行了
self.configvalidator.validate(config)
,则
config\u file.write(yaml.dump(config))
将配置文件保存为空文件,尽管变量
config
中的数据没有更改

如果
self.configvalidator.validate(config)
未运行,则
config\u file.write(yaml.dump(config))
正确保存文件

这是我的错误,对我来说毫无意义

如果您希望提供帮助,那么只要您的计算机能够访问
ruamel.yaml
voluputous
,那么
configmanager.py
应该在您的计算机上正确运行(有错误)。它将创建
~/.config/MyProject/MyProject.conf
,然后将其另存为空。保存我的示例
myproject.conf
,查看如何在运行
configmanager.py
时将其保存为空。如果再次运行
configmanager.py
,当
myproject.conf
为空时,将在
\u get\u config
中引发验证错误,这与预期一致

我被这个错误弄糊涂了,所以如果你有任何见解,我将不胜感激


干杯

我不想详细讨论这个问题,但至少有一种情况,可能不止一种情况下,您打开一个文件进行写入,写入,不要关闭它(甚至让它超出范围),然后再次打开它进行读取,这意味着您可能正在读取不完整(未刷新)的文件数据。始终使用
with
语句,尤其是当打开文件进行
写入时,因此在
with
块的末尾,您处于一种已知的、可预测的状态(写入的任何数据在磁盘上都是明确可见的)。使用
实现
with
可以解决问题,谢谢。不知道它为什么起作用,但不管怎样\_(ツ)_/''只是添加到@ShadowRanger解释:文件输入/输出操作在计算上非常昂贵。这意味着与其他操作相比,访问和写入磁盘上的文件需要相对较长的时间。在打开和写入文件时,python会缓冲您的输出,并在积累较大数据块时写入文件为节省昂贵的操作而打开。当您对文件对象调用close时,缓冲区将清空,无论它存储了多少数据。请在内容(完成)之前重新打开文件
# ~/.config/MyProject/myproject.conf
key1: {}
key2: {}