-
Notifications
You must be signed in to change notification settings - Fork 247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CIBW_ENVIRONMENT #21
Merged
Merged
CIBW_ENVIRONMENT #21
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
38f89e9
Added test for environment and a script for local running
joerick 6aba5f8
Add a Linux implementation of environment
joerick 573098f
Mac/Windows stubs and test case for environment inheritance
joerick fd385a9
Add some tests for the environment functionality
joerick 5ae4da6
Add environment code satisfying unit test
joerick 9091533
Run the unit tests in CI
joerick fcb681d
Use parse_environment in the main code
joerick f64cb35
Merge remote-tracking branch 'origin/master' into environment
joerick f56ddda
Make shell evals work and add tests for that
joerick 7229dde
Windows uses a different pathsep so have to use that in the test
joerick 60ed370
Semicolons need quoting
joerick a7c7a58
Reducing code duplication in bin/run_test.py and bin/run_tests.py
YannickJadoul 3188c97
Fixing 'ValueError: Attempted relative import in non-package'
YannickJadoul 93d2a59
Making call from run_tests to run_test subprocess.check_call
YannickJadoul be474fd
Fix string splitting for the env string
joerick b18684a
Merge branch 'environment' of github.com:joerick/cibuildwheel into en…
joerick 34e8ee0
Fix empty env string case
joerick 7c41632
Add CIBW_ENVIRONMENT to README and reorder options
joerick 523dfc0
Fix some linter warnings
joerick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,48 @@ | ||
#!/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) | ||
|
||
# move cwd to the project root | ||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | ||
|
||
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,30 @@ | ||
#!/usr/bin/python | ||
|
||
from __future__ import print_function | ||
import os, sys, subprocess, shutil, json | ||
from glob import glob | ||
|
||
import run_test | ||
|
||
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) | ||
|
||
for project_path in test_projects: | ||
run_test.single_run(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,66 @@ | ||
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 | ||
|
||
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
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 |
---|---|---|
@@ -0,0 +1,2 @@ | ||
-e . | ||
pytest |
This file was deleted.
Oops, something went wrong.
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,4 @@ | ||
{ | ||
"CIBW_ENVIRONMENT": "CIBW_TEST_VAR=\"a b c\" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3=\"$(echo 'test string 3')\" PATH=$PATH:/opt/cibw_test_path", | ||
"CIBW_ENVIRONMENT_WINDOWS": "CIBW_TEST_VAR=\"a b c\" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3=\"$(echo 'test string 3')\" PATH=\"$PATH;/opt/cibw_test_path\"" | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if there is a better way of doing this 'import from the same folder'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, there isn't. Also since these are utility scripts I'd prefer using the shell interface instead, something like
subprocess.check_call([sys.executable, 'bin/run_test.py', arg1, arg2])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There you go, then. I'd chosen for this such that the whole argument parsing (and extra output 'Project built successfully.') was not executed every time.
Have a look, maybe the
if __name__ == '__main__':
construction was also unnecessary, then.