Add environment code satisfying unit test
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
import subprocess, shlex
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate_node(node, environment):
|
||||||
|
if node.kind == 'word':
|
||||||
|
return evaluate_word_node(node, environment=environment)
|
||||||
|
elif node.kind == 'commandsubstitution':
|
||||||
|
return evaluate_command_node(node.command, environment=environment)
|
||||||
|
elif node.kind == 'parameter':
|
||||||
|
return evaluate_parameter_node(node, environment=environment)
|
||||||
|
else:
|
||||||
|
raise ValueError('Unsupported bash construct: "%s"' % node.word)
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate_word_node(node, environment):
|
||||||
|
letters = list(node.word)
|
||||||
|
|
||||||
|
for part in node.parts:
|
||||||
|
part_start = part.pos[0]
|
||||||
|
part_end = part.pos[1]
|
||||||
|
|
||||||
|
# Set all the characters in the part to None
|
||||||
|
for i in range(part_start, part_end):
|
||||||
|
letters[i] = None
|
||||||
|
|
||||||
|
letters[part_start] = evaluate_node(part, environment=environment)
|
||||||
|
|
||||||
|
# remove the None letters and concat
|
||||||
|
return ''.join(l for l in letters if l is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate_command_node(node, environment):
|
||||||
|
words = [evaluate_node(part, environment=environment) for part in node.parts]
|
||||||
|
command = ' '.join(words)
|
||||||
|
return subprocess.check_output(shlex.split(command), env=environment)
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate_parameter_node(node, environment):
|
||||||
|
return environment.get(node.value, '')
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import bashlex
|
||||||
|
from . import bashlex_eval
|
||||||
|
|
||||||
|
|
||||||
|
class EnvironmentParseError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def parse_environment(env_string):
|
||||||
|
env_items = split_env_items(env_string)
|
||||||
|
assignments = [EnvironmentAssignment(item) for item in env_items]
|
||||||
|
return ParsedEnvironment(assignments=assignments)
|
||||||
|
|
||||||
|
|
||||||
|
def split_env_items(env_string):
|
||||||
|
'''Splits space-separated variable assignments into a list of individual assignments.
|
||||||
|
|
||||||
|
>>> split_env_items('VAR=abc')
|
||||||
|
['VAR=abc']
|
||||||
|
>>> split_env_items('VAR="a string" THING=3')
|
||||||
|
['VAR="a string"', 'THING=3']
|
||||||
|
>>> split_env_items('VAR="a string" THING=\\'single "quotes"\\'')
|
||||||
|
['VAR="a string"', 'THING=\\'single "quotes"\\'']
|
||||||
|
>>> split_env_items('VAR="dont \\\\"forget\\\\" about backslashes"')
|
||||||
|
['VAR="dont \\\\"forget\\\\" about backslashes"']
|
||||||
|
'''
|
||||||
|
return list(bashlex.split(env_string))
|
||||||
|
|
||||||
|
|
||||||
|
class EnvironmentAssignment(object):
|
||||||
|
def __init__(self, assignment):
|
||||||
|
name, equals, value = assignment.partition('=')
|
||||||
|
if not equals:
|
||||||
|
raise EnvironmentParseError(assignment)
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
if value:
|
||||||
|
command_node = bashlex.parsesingle(value)
|
||||||
|
|
||||||
|
if len(command_node.parts) != 1:
|
||||||
|
raise ValueError('"%s" has too many parts' % value)
|
||||||
|
|
||||||
|
self.value_word_node = command_node.parts[0]
|
||||||
|
else:
|
||||||
|
self.value_word_node = None
|
||||||
|
|
||||||
|
def evaluated_value(self, environment):
|
||||||
|
'''Returns the value of this assignment, as evaluated in the environment'''
|
||||||
|
if self.value_word_node:
|
||||||
|
return bashlex_eval.evaluate_node(self.value_word_node, environment=environment)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def as_shell_assignment(self):
|
||||||
|
return 'export %s=%s' % (self.name, self.value)
|
||||||
|
|
||||||
|
|
||||||
|
class ParsedEnvironment(object):
|
||||||
|
def __init__(self, assignments):
|
||||||
|
self.assignments = assignments
|
||||||
|
|
||||||
|
def as_dictionary(self, prev_environment):
|
||||||
|
environment = prev_environment.copy()
|
||||||
|
|
||||||
|
for assignment in self.assignments:
|
||||||
|
value = assignment.evaluated_value(environment=environment)
|
||||||
|
environment[assignment.name] = value
|
||||||
|
|
||||||
|
return environment
|
||||||
|
|
||||||
|
def as_shell_commands(self):
|
||||||
|
return [a.as_shell_assignment() for a in self.assignments]
|
||||||
@@ -9,6 +9,7 @@ except ImportError:
|
|||||||
setup(
|
setup(
|
||||||
name='cibuildwheel',
|
name='cibuildwheel',
|
||||||
version='0.4.0',
|
version='0.4.0',
|
||||||
|
install_requires=['bashlex'],
|
||||||
description="Build Python wheels on CI with minimal configuration.",
|
description="Build Python wheels on CI with minimal configuration.",
|
||||||
long_description='For readme please see http://github.com/joerick/cibuildwheel',
|
long_description='For readme please see http://github.com/joerick/cibuildwheel',
|
||||||
author="Joe Rickerby",
|
author="Joe Rickerby",
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ def test_basic_parsing():
|
|||||||
environment_recipe = parse_environment('VAR=1 VBR=2')
|
environment_recipe = parse_environment('VAR=1 VBR=2')
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(
|
environment_dict = environment_recipe.as_dictionary(
|
||||||
prev_environment={},
|
prev_environment={}
|
||||||
shell=lambda cmd: '')
|
)
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'VAR': '1', 'VBR': '2'}
|
assert environment_dict == {'VAR': '1', 'VBR': '2'}
|
||||||
@@ -16,20 +16,31 @@ def test_quotes():
|
|||||||
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
|
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(
|
environment_dict = environment_recipe.as_dictionary(
|
||||||
prev_environment={},
|
prev_environment={}
|
||||||
shell=lambda cmd: '')
|
)
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
|
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
|
||||||
assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export ABR=vbr']
|
assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export VBR=\'vbr\'']
|
||||||
|
|
||||||
def test_inheritance():
|
def test_inheritance():
|
||||||
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
|
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(
|
environment_dict = environment_recipe.as_dictionary(
|
||||||
prev_environment={'PATH': '/usr/bin'},
|
prev_environment={'PATH': '/usr/bin'}
|
||||||
shell=lambda cmd: '')
|
)
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
|
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
|
||||||
assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
|
assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
|
||||||
|
|
||||||
|
def test_empty_var():
|
||||||
|
environment_recipe = parse_environment('CFLAGS=')
|
||||||
|
|
||||||
|
environment_dict = environment_recipe.as_dictionary(
|
||||||
|
prev_environment={'CFLAGS': '-Wall'}
|
||||||
|
)
|
||||||
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
|
assert environment_dict == {'CFLAGS': ''}
|
||||||
|
assert environment_cmds == ['export CFLAGS=']
|
||||||
|
|||||||
Reference in New Issue
Block a user