From 38af139bd6302e935dd324acf969d7c92ed36a49 Mon Sep 17 00:00:00 2001 From: Jeremy Fleischman Date: Fri, 21 Sep 2018 11:13:32 -0700 Subject: [PATCH] Change write_table to atomically write to the parsetab file. The previous implementation had issues with multiple python processes simultaneously invoking `.yacc(...)` with the same arugments. One of them could be midway through generating a .py file that the other process would attempt to import and then crash with a `SyntaxError`. This diff avoids that problem by instead writing to a tempfile and then atomically renaming it. This means processes starting up at the same time will both do the same work, but that's a lot better than having one of them crash! `flanker` had this issue reported to them in https://github.com/mailgun/flanker/issues/168 and they attempted to work around this by committing the parsetab files that ply generates (https://github.com/mailgun/flanker/pull/188), but this doesn't help if the user is running a different version of ply than the version that flanker generated their parsetab files with (because ply will go ahead and regenerate those files). --- ply/yacc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ply/yacc.py b/ply/yacc.py index 88188a1..523c363 100644 --- a/ply/yacc.py +++ b/ply/yacc.py @@ -65,6 +65,7 @@ import os.path import inspect import warnings +import tempfile __version__ = '3.11' __tabversion__ = '3.10' @@ -2731,7 +2732,7 @@ def write_table(self, tabmodule, outputdir='', signature=''): basemodulename = tabmodule.split('.')[-1] filename = os.path.join(outputdir, basemodulename) + '.py' try: - f = open(filename, 'w') + f = tempfile.NamedTemporaryFile('wt', delete=False) f.write(''' # %s @@ -2836,6 +2837,7 @@ def write_table(self, tabmodule, outputdir='', signature=''): f.write(' (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len)) f.write(']\n') f.close() + os.rename(f.name, filename) except IOError as e: raise