输出相同时不覆盖文件的简单方法 我在Python中有一个C++代码生成器,它生成了许多源文件。大多数情况下,只有一个文件会更改,但由于生成器会重新生成所有文件,因此它们都会重新生成。有没有办法让Python不覆盖文件,或者让cmak使用校验和来查看需要重建的内容,而不仅仅是使用文件日期

输出相同时不覆盖文件的简单方法 我在Python中有一个C++代码生成器,它生成了许多源文件。大多数情况下,只有一个文件会更改,但由于生成器会重新生成所有文件,因此它们都会重新生成。有没有办法让Python不覆盖文件,或者让cmak使用校验和来查看需要重建的内容,而不仅仅是使用文件日期,python,cmake,Python,Cmake,我在想,在Python中这样做很容易:如果我可以替换 with open('blah', 'w') as f: 为此: with open_but_only_overwrite_if_total_output_is_different('blah', 'w') as f: 实现这一点的好方法是什么?我建议您编写自己的类似文件的对象,如下所示: \uuuu输入\uuuu:创建临时文件 \uuuu退出\uuuu:将临时文件的内容与旧文件(如果存在)进行比较(如果不相同),然后用临时文件替换旧文

我在想,在Python中这样做很容易:如果我可以替换

with open('blah', 'w') as f:
为此:

with open_but_only_overwrite_if_total_output_is_different('blah', 'w') as f:

实现这一点的好方法是什么?

我建议您编写自己的类似文件的对象,如下所示:

  • \uuuu输入\uuuu
    :创建临时文件
  • \uuuu退出\uuuu
    :将临时文件的内容与旧文件(如果存在)进行比较(如果不相同),然后用临时文件替换旧文件

这篇文章对于理解
with
语句非常有帮助:

最简单的方法是在Python中完全按照cmake所做的操作:让生成器检查输入是否比输出新,并且仅在输入比输出新时生成

以下是我用于类似内容的片段:

if (os.path.exists(output) and
    os.path.getmtime(source) <= os.path.getmtime(output)):
    print "Generated output %s is up-to-date." % output
    return
if(os.path.exists)(输出)和
getmtime(源)使用filecmp-

将新文件写入tmp目录,并与工作目录进行比较
,并传输更改过的文件。然后删除tmp。

结合、和的代码和思想:

一些小的补充(希望是改进):

  • shutil.copyfile
    仅将
    tempname
    的内容复制到
    文件名
    ,同时保留元数据,如文件权限和文件 所有权
  • filecmp.cmp
    检查文件的大小 如果大小不匹配,则返回
    False
    。这可能是个不错的选择 如果文件很大,并且其中一个文件附加了内容,则加速 结束。它还一次读取和比较字节, 而不是一次行。
    bufsize
    通常比 行,这将导致更少的读取

您是在询问cmake或Python是否可以确定在Python脚本中生成代码之前是否应该覆盖文件?否,他是在询问是否可以避免在这种情况下更改文件的修改日期,因为构建系统(cmake)如果修改日期更改,将重新生成文件,使生成时间变长。没有任何迹象表明他正在使用本地文件系统上的文件作为生成器的输入。实际上,它可以是任何内容。生成器是一个程序,因此在运行时很难知道哪些输出文件将受到我对p的更改的影响在这种情况下,要么按照@gecco的答案实现一些东西。另一个选项,让生成器写入StringIO.StringIO。然后编写一个名为
write\u的快速函数,如果不同(StringIO,filename)
啊,谢谢!就在我完成gecco解决方案的编码时……取决于。它在unix系统上运行得最好(与胜利相反)。试试看。如果
filecmp
接受文件句柄而不是文件名,这将是最好的解决方案。但是,+1,谢谢你提醒我。嗯。NamedTemporaryFile会给你一个名称来使用。我认为这应该适用于filecmp。我很好奇。我有时间的时候会尝试。是的,你是对的。我必须设置delete设置为False,并在关闭文件后将临时文件名传递给filecmp。这是一个不错的解决方案,但不必关闭并重新打开该文件会很好。解决方案不错。要用临时文件替换旧文件,是否可以以某种方式将旧文件指向临时文件?而不是读取临时文件并将其写入哦,老人file@robertking是的:嗯……我不太清楚为什么,但是当我使用777权限在/tmp/test.dat上测试此代码时,另一个用户拥有,
shutil.copyfile
成功,但是
shutil.move
会导致操作错误。
shutil.move
调用
shutil.copystat
,试图更改atime和mtime。这就是如果您不拥有该文件,则不允许使用。因此,调用
os.utime
失败。
shutil.copyfile
不会更改元数据,因此它可以工作。
的出色使用产生了
contextlib.contextmanager
!太棒了!
import contextlib
@contextlib.contextmanager
def write_on_change(filename):
    with tempfile.NamedTemporaryFile(delete=False) as f:
        yield f
        tempname = f.name
    try:
        overwrite = not filecmp.cmp(tempname,filename)
    except (OSError,IOError):
        overwrite = True
    if overwrite:
        shutil.copyfile(tempname,filename)
    os.unlink(tempname)