-
Notifications
You must be signed in to change notification settings - Fork 247
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21 from joerick/environment
CIBW_ENVIRONMENT Fixes #16
- Loading branch information
Showing
20 changed files
with
452 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
build_script: | ||
- pip install . | ||
- pip install -r requirements-dev.txt | ||
# the '-u' flag is required so the output is in the correct order. | ||
# See https://github.com/joerick/cibuildwheel/pull/24 for more info. | ||
- python -u ./run_tests.py | ||
- python -u ./bin/run_tests.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/bash | ||
|
||
cd "$(dirname "$0")" | ||
cd .. | ||
|
||
CIBW_PLATFORM=linux ./bin/run_test.py $1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#!/usr/bin/python | ||
|
||
from __future__ import print_function | ||
import os, sys, subprocess, shutil, json | ||
from glob import glob | ||
|
||
def single_run(test_project): | ||
# load project settings into environment | ||
env_file = os.path.join(test_project, 'environment.json') | ||
project_env = {} | ||
if os.path.exists(env_file): | ||
with open(env_file) as f: | ||
project_env = json.load(f) | ||
|
||
# run the build | ||
env = os.environ.copy() | ||
project_env = {str(k): str(v) for k, v in project_env.items()} # unicode not allowed in env | ||
env.update(project_env) | ||
print('Building %s with environment %s' % (test_project, project_env)) | ||
subprocess.check_call(['cibuildwheel', test_project], env=env) | ||
wheels = glob('wheelhouse/*.whl') | ||
print('%s built successfully. %i wheels built.' % (test_project, len(wheels))) | ||
|
||
# check some wheels were actually built | ||
assert len(wheels) >= 4 | ||
|
||
# clean up | ||
shutil.rmtree('wheelhouse') | ||
|
||
if __name__ == '__main__': | ||
import argparse | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument("test_project_dir") | ||
args = parser.parse_args() | ||
|
||
project_path = os.path.abspath(args.test_project_dir) | ||
|
||
if not os.path.exists(project_path): | ||
print('No test project not found.', file=sys.stderr) | ||
exit(2) | ||
|
||
single_run(project_path) | ||
|
||
print('Project built successfully.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#!/usr/bin/python | ||
|
||
from __future__ import print_function | ||
import os, sys, subprocess, shutil, json | ||
from glob import glob | ||
|
||
if __name__ == '__main__': | ||
# move cwd to the project root | ||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | ||
|
||
### run the unit tests | ||
|
||
subprocess.check_call(['python', '-m', 'pytest', 'unit_test']) | ||
|
||
### run the integration tests | ||
|
||
test_projects = glob('test/??_*') | ||
|
||
if len(test_projects) == 0: | ||
print('No test projects found. Aborting.', file=sys.stderr) | ||
exit(2) | ||
|
||
print('Testing projects:', test_projects) | ||
|
||
run_test_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'run_test.py') | ||
for project_path in test_projects: | ||
subprocess.check_call([sys.executable, run_test_path, project_path]) | ||
|
||
print('%d projects built successfully.' % len(test_projects)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import subprocess, shlex | ||
from collections import namedtuple | ||
import bashlex | ||
|
||
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input']) | ||
|
||
def evaluate(value, environment): | ||
if not value: | ||
# empty string evaluates to empty string | ||
# (but trips up bashlex) | ||
return '' | ||
|
||
command_node = bashlex.parsesingle(value) | ||
|
||
if len(command_node.parts) != 1: | ||
raise ValueError('"%s" has too many parts' % value) | ||
|
||
value_word_node = command_node.parts[0] | ||
|
||
return evaluate_node( | ||
value_word_node, | ||
context=NodeExecutionContext(environment=environment, input=value) | ||
) | ||
|
||
|
||
def evaluate_node(node, context): | ||
if node.kind == 'word': | ||
return evaluate_word_node(node, context=context) | ||
elif node.kind == 'commandsubstitution': | ||
return evaluate_command_node(node.command, context=context) | ||
elif node.kind == 'parameter': | ||
return evaluate_parameter_node(node, context=context) | ||
else: | ||
raise ValueError('Unsupported bash construct: "%s"' % node.word) | ||
|
||
|
||
def evaluate_word_node(node, context): | ||
word_start = node.pos[0] | ||
word_end = node.pos[1] | ||
word_string = context.input[word_start:word_end] | ||
letters = list(word_string) | ||
|
||
for part in node.parts: | ||
part_start = part.pos[0] - word_start | ||
part_end = part.pos[1] - word_start | ||
|
||
# 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, context=context) | ||
|
||
# remove the None letters and concat | ||
value = ''.join(l for l in letters if l is not None) | ||
|
||
# apply bash-like quotes/whitespace treatment | ||
return ' '.join(word.strip() for word in shlex.split(value)) | ||
|
||
|
||
def evaluate_command_node(node, context): | ||
words = [evaluate_node(part, context=context) for part in node.parts] | ||
command = ' '.join(words) | ||
return subprocess.check_output(shlex.split(command), env=context.environment) | ||
|
||
|
||
def evaluate_parameter_node(node, context): | ||
return context.environment.get(node.value, '') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
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"'] | ||
>>> split_env_items('PATH="$PATH;/opt/cibw_test_path"') | ||
['PATH="$PATH;/opt/cibw_test_path"'] | ||
>>> split_env_items('PATH2="something with spaces"') | ||
['PATH2="something with spaces"'] | ||
''' | ||
if not env_string: | ||
return [] | ||
|
||
command_node = bashlex.parsesingle(env_string) | ||
result = [] | ||
|
||
for word_node in command_node.parts: | ||
part_string = env_string[word_node.pos[0]:word_node.pos[1]] | ||
result.append(part_string) | ||
|
||
return result | ||
|
||
|
||
class EnvironmentAssignment(object): | ||
def __init__(self, assignment): | ||
name, equals, value = assignment.partition('=') | ||
if not equals: | ||
raise EnvironmentParseError(assignment) | ||
self.name = name | ||
self.value = value | ||
|
||
def evaluated_value(self, environment): | ||
'''Returns the value of this assignment, as evaluated in the environment''' | ||
return bashlex_eval.evaluate(self.value, environment=environment) | ||
|
||
def as_shell_assignment(self): | ||
return 'export %s=%s' % (self.name, self.value) | ||
|
||
def __repr__(self): | ||
return '%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] | ||
|
||
def __repr__(self): | ||
return 'ParsedEnvironment(%r)' % [repr(a) for a in self.assignments] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.