Commit 5a9eb01a authored by Wez Furlong's avatar Wez Furlong Committed by Facebook Github Bot

fix a python 2.7 vs 3 compat issue with exec

Summary:
This is a bit of a tricky issue; here's the bug in python 2.7
that prevents using the function-like syntax:
https://bugs.python.org/issue21591

The nature of this bug is that scoped exec only works with nested functions
when using the `exec` statement in 2.7.   That is a syntax error in python 3,
which only allows using the `exec` function.

To make the function syntax work in 2.7 we therefore need to avoid using
lambdas or nested functions and have to resort to using a global variable
to act as the "closure".

Refs: https://github.com/facebook/watchman/pull/639

Reviewed By: snarkmaster

Differential Revision: D9552413

fbshipit-source-id: a2cb9d325e18fb6fb06b9e284a84f0a0c80cfe7e
parent de883709
...@@ -55,4 +55,6 @@ internal continuous-integration platform using the same build-step DSL. ...@@ -55,4 +55,6 @@ internal continuous-integration platform using the same build-step DSL.
Please follow the ambient style (or PEP-8), and keep the code Python 2.6 Please follow the ambient style (or PEP-8), and keep the code Python 2.6
compatible -- since `fbcode_builder`'s only dependency is Docker, we want to compatible -- since `fbcode_builder`'s only dependency is Docker, we want to
allow building projects on even fairly ancient base systems. allow building projects on even fairly ancient base systems. We also wish
to be compatible with Python 3, and would appreciate it if you kept that
in mind while making changes also.
...@@ -38,21 +38,31 @@ def make_temp_dir(d): ...@@ -38,21 +38,31 @@ def make_temp_dir(d):
shutil.rmtree(d, ignore_errors=True) shutil.rmtree(d, ignore_errors=True)
def _inner_read_config(path):
'''
Helper to read a named config file.
The grossness with the global is a workaround for this python bug:
https://bugs.python.org/issue21591
The bug prevents us from defining either a local function or a lambda
in the scope of read_fbcode_builder_config below.
'''
global _project_dir
full_path = os.path.join(_project_dir, path)
return read_fbcode_builder_config(full_path)
def read_fbcode_builder_config(filename): def read_fbcode_builder_config(filename):
# Allow one spec to read another # Allow one spec to read another
# When doing so, treat paths as relative to the config's project directory. # When doing so, treat paths as relative to the config's project directory.
project_dir = os.path.dirname(filename) # _project_dir is a "local" for _inner_read_config; see the comments
# in that function for an explanation of the use of global.
def inner_read_config(path): global _project_dir
full_path = os.path.join(project_dir, path) _project_dir = os.path.dirname(filename)
return read_fbcode_builder_config(full_path)
scope = {'read_fbcode_builder_config': inner_read_config} scope = {'read_fbcode_builder_config': _inner_read_config}
with open(filename) as config_file: with open(filename) as config_file:
# Note that this will need to be changed to an exec() function call for code = compile(config_file.read(), filename, mode='exec')
# python 3 compatibility. Unfortunately python 2.7 does not seem to exec(code, scope)
# treat the scope correctly when using exec() function syntax here.
exec config_file.read() in scope
return scope['config'] return scope['config']
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment