From ba022184cfdc2c8557f9776b39c00e483cd56fce Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 16:29:52 +0100 Subject: [PATCH 1/8] morecoverage --- packtivity/handlers/environment_handlers.py | 7 +- packtivity/handlers/execution_handlers.py | 110 +++++++++++++------- packtivity/typedleafs.py | 30 ++++-- tests/conftest.py | 8 ++ 4 files changed, 106 insertions(+), 49 deletions(-) diff --git a/packtivity/handlers/environment_handlers.py b/packtivity/handlers/environment_handlers.py index 7265245..25e77db 100644 --- a/packtivity/handlers/environment_handlers.py +++ b/packtivity/handlers/environment_handlers.py @@ -4,9 +4,14 @@ @environment('docker-encapsulated') def docker(environment,parameters,state): + + jsonpars = parameters.json() + for p,v in parameters.leafs(): + p.set(jsonpars, v) + for i,x in enumerate(environment['par_mounts']): script = x.pop('jqscript') - x['mountcontent'] = jq.jq(script).transform(parameters, text_output = True) + x['mountcontent'] = jq.jq(script).transform(jsonpars, text_output = True) if environment['workdir'] is not None: environment['workdir'] = state.contextualize_value(environment['workdir']) diff --git a/packtivity/handlers/execution_handlers.py b/packtivity/handlers/execution_handlers.py index b191076..6537d82 100644 --- a/packtivity/handlers/execution_handlers.py +++ b/packtivity/handlers/execution_handlers.py @@ -25,11 +25,22 @@ def sourcepath(path): def state_context_to_mounts(state): readwrites = state.readwrite readonlies = state.readonly - mounts = '' + + mounts = [] for rw in readwrites: - mounts += '-v {}:{}:rw'.format(sourcepath(os.path.abspath(rw)),rw) + mounts.append({ + 'type': 'bind', + 'source': sourcepath(os.path.abspath(rw)), + 'destination': rw, + 'readonly': False + }) for ro in readonlies: - mounts += ' -v {}:{}:ro'.format(sourcepath(ro),ro) + mounts.append({ + 'type': 'bind', + 'source': sourcepath(os.path.abspath(ro)), + 'destination': ro, + 'readonly': False + }) return mounts def prepare_par_mounts(parmounts,state): @@ -39,10 +50,12 @@ def prepare_par_mounts(parmounts,state): with open(parmountfile,'w') as f: f.write(x['mountcontent']) - mounts.append(' -v {}:{}'.format( - sourcepath(os.path.abspath(parmountfile)), - x['mountpath'] - )) + mounts.append({ + 'type': 'bind', + 'source': sourcepath(os.path.abspath(parmountfile)), + 'destination': x['mountpath'], + 'readonly': False + }) return mounts @@ -51,13 +64,26 @@ def cvmfs_from_volume_plugin(cvmfs_repos = None): cvmfs_repos = yaml.load(os.environ.get('PACKTIVITY_CVMFS_REPOS','null')) if not cvmfs_repos: cvmfs_repos = ['atlas.cern.ch','atlas-condb.cern.ch','sft.cern.ch'] - command_line = ' --security-opt label:disable' + + options = '--security-opt label:disable' + mounts = [] for repo in cvmfs_repos: - command_line += ' --volume-driver cvmfs -v {cvmfs_repo}:/cvmfs/{cvmfs_repo}'.format(cvmfs_repo = repo) - return command_line + mounts.append({ + 'type': 'volume', + 'source': repo, + 'destination': '/cvmfs/{}'.format(repo), + 'readonly': False + }) + + return options, mounts def cvmfs_from_external_mount(): - return ' -v {}:/cvmfs'.format(os.environ.get('PACKTIVITY_CVMFS_LOCATION','/cvmfs')) + return '', [{ + 'type': 'volume', + 'source': os.environ.get('PACKTIVITY_CVMFS_LOCATION','/cvmfs'), + 'destination': '/cvmfs', + 'readonly': False + }] def cvmfs_mount(): cvmfs_source = os.environ.get('PACKTIVITY_CVMFS_SOURCE','external') @@ -69,10 +95,12 @@ def cvmfs_mount(): raise RuntimeError('unknown CVMFS location requested') def auth_mount(): - if 'PACKTIVITY_AUTH_LOCATION' not in os.environ: - return ' -v /home/recast/recast_auth:/recast_auth' - else: - return ' -v {}:/recast_auth'.format(os.environ['PACKTIVITY_AUTH_LOCATION']) + return [{ + 'type': 'bind', + 'source': os.environ.get('PACKTIVITY_AUTH_LOCATION','/home/recast/recast_auth'), + 'destination': '/recast_auth', + 'readonly': False + }] def resource_mounts(state,environment,log): report = '''\n\ @@ -91,15 +119,16 @@ def resource_mounts(state,environment,log): do_auth = ('GRIDProxy' in environment['resources']) or ('KRB5Auth' in environment['resources']) log.debug('do_auth: %s do_cvmfs: %s',do_auth,do_cvmfs) + options, mounts = '', [] - resource_mounts = '' if do_cvmfs: - resource_mounts+=cvmfs_mount() - + cvfms_options, cvmfs_mounts = cvmfs_mount() + options += cvfms_options + mounts += cvmfs_mounts if do_auth: - resource_mounts+=auth_mount() + mounts += auth_mount() - return resource_mounts + return options, mounts def docker_execution_cmdline(state,environment,log,metadata,stdin,cmd_argv): quoted_string = ' '.join(map(pipes.quote,cmd_argv)) @@ -119,23 +148,31 @@ def docker_execution_cmdline(state,environment,log,metadata,stdin,cmd_argv): # volume mounts (resources, parameter mounts and state mounts) state_mounts = state_context_to_mounts(state) - rsrcs_mounts = resource_mounts(state,environment,log) - par_mounts = ' '.join(prepare_par_mounts(environment['par_mounts'], state)) + par_mounts = prepare_par_mounts(environment['par_mounts'], state) + rsrcs_opts, rsrcs_mounts = resource_mounts(state,environment,log) + + + mount_args = '' + for s in state_mounts + par_mounts + rsrcs_mounts: + mount_args += ' -v {source}:{destination}:{mode}'.format( + source = s['source'], + destination = s['destination'], + mode = 'ro' if s['readonly'] else 'rw' + ) - return 'docker run --rm {stdin} {cid} {workdir} {custom} {state_mounts} {rsrcs} {par_mounts} {img}:{tag} {command}'.format( + return 'docker run --rm {stdin} {cid} {workdir} {custom} {mount_args} {rsrcs_opts} {img}:{tag} {command}'.format( stdin = '-i' if stdin else '', cid = cid_file, workdir = workdir_flag, custom = custom_mod, - state_mounts = state_mounts, - rsrcs = rsrcs_mounts, - par_mounts = par_mounts, + mount_args = mount_args, + rsrcs_opts = rsrcs_opts, img = image, tag = imagetag, command = quoted_string ) -def run_docker_with_script(state,environment,job,log): +def run_docker_with_script(environment,job,log): script = job['script'] interpreter = job['interpreter'] @@ -147,19 +184,19 @@ def run_docker_with_script(state,environment,job,log): indocker = interpreter envmod = 'source {} && '.format(environment['envscript']) if environment['envscript'] else '' in_docker_cmd = envmod+indocker - return in_docker_cmd, script + return ['sh', '-c', in_docker_cmd], script -def run_docker_with_oneliner(state,environment,command,log): +def run_docker_with_oneliner(environment,job,log): log.debug('''\n\ -------------- running one liner in container. command: {command} -------------- - '''.format(command = command)) + '''.format(command = job['command'])) - envmod = 'source {} &&'.format(environment['envscript']) if environment['envscript'] else '' - in_docker_cmd = '{envmodifier} {command}'.format(envmodifier = envmod, command = command) - return in_docker_cmd, None + envmod = 'source {} && '.format(environment['envscript']) if environment['envscript'] else '' + in_docker_cmd = envmod + job['command'] + return ['sh', '-c', in_docker_cmd], None def execute_docker(metadata,state,log,docker_run_cmd_str,stdin_content = None): log.debug('container execution command: \n%s',docker_run_cmd_str) @@ -247,16 +284,13 @@ def docker_enc_handler(environment,state,job,metadata): with logutils.setup_logging_topic(metadata,state,'step',return_logger = True) as log: if 'command' in job: stdin = False - in_docker_cmd, stdin = run_docker_with_oneliner(state,environment,job['command'],log) + container_argv, container_stdin = run_docker_with_oneliner(environment,job,log) elif 'script' in job: stdin = True - in_docker_cmd, stdin = run_docker_with_script(state,environment,job,log) + container_argv, container_stdin = run_docker_with_script(environment,job,log) else: raise RuntimeError('do not know yet how to run this...') - container_argv = ['sh', '-c', in_docker_cmd] - container_stdin = stdin - cmdline = docker_execution_cmdline( state,environment,log,metadata, stdin = stdin, diff --git a/packtivity/typedleafs.py b/packtivity/typedleafs.py index a92ee4b..9620d6d 100644 --- a/packtivity/typedleafs.py +++ b/packtivity/typedleafs.py @@ -6,16 +6,23 @@ import base64 import importlib import collections +from six import string_types class LeafModel(object): def __init__(self, spec): self.datamodel = spec or {'keyword': None, 'types': {}} self._types2str, self._str2types = {}, {} - for name,module_class in self.datamodel['types'].items(): - m, c = module_class.split(':') - c = getattr(importlib.import_module(m),c) - self._types2str[c] = name - self._str2types[name] = c + for name,class_def in self.datamodel['types'].items(): + if type(class_def)==type: + self._types2str[class_def] = name + self._str2types[name] = class_def + elif isinstance(class_def, string_types): + m, c = class_def.split(':') + c = getattr(importlib.import_module(m),c) + self._types2str[c] = name + self._str2types[name] = c + else: + raise RuntimeError('not sure how to interpret type def %s',class_def) self.keyword = self.datamodel['keyword'] self.leaf_magic = '___leaf___' @@ -95,7 +102,7 @@ def _jsonable(self, value): @classmethod def fromJSON(cls, data, deserialization_opts): - return cls(data, deserialization_opts['leafmodel'], deserialization_opts['idleafs']) + return cls(data, deserialization_opts.get('leafmodel',None), deserialization_opts.get('idleafs',False)) def _load_from_string(self,jsonstring, typed = True, idleafs = False): if typed: @@ -121,20 +128,23 @@ def copy(self): return TypedLeafs(copy.deepcopy(self.typed()), self.leafmodel) def asrefs(self): - data = copy.deepcopy(self.typed()) + data = copy.deepcopy(self.json()) for p, v in self.leafs(): p.set(data, p) return data ### QUERY methods def resolve_ref(self, reference): - return reference.resolve(self.typed()) + return reference.get(self.typed()) def jsonpointer(self,pointer_str): return jsonpointer.JsonPointer(pointer_str).resolve( self.typed() ) - def jsonpath(self,jsonpath_expression): - return jsonpath_rw.parse(jsonpath_expression).find( self.typed() )[0].value + def jsonpath(self,jsonpath_expression, multiple_output = False): + if not multiple_output: + return jsonpath_rw.parse(jsonpath_expression).find( self.typed() )[0].value + else: + return [x.value for x in jsonpath_rw.parse(jsonpath_expression).find( self.typed())] def jq(self,jq_program, *args, **kwargs): return TypedLeafs(jq.jq(jq_program).transform(self.typed(idleafs = True), *args, **kwargs), self.leafmodel, idleafs = True) diff --git a/tests/conftest.py b/tests/conftest.py index 64a231c..f9cb91f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,6 +45,14 @@ def dockeproc_script_pack(tmpdir): def docker_touchfile_workdir(tmpdir): return packtivity.pack_object.fromspec('tests/testspecs/environment_tests/touchfile_docker_inworkdir.yml') +@pytest.fixture() +def docker_env_resources(tmpdir): + return packtivity.pack_object.fromspec('tests/testspecs/environment_tests/resources_docker.yml') + +@pytest.fixture() +def docker_env_parmounts(tmpdir): + return packtivity.pack_object.fromspec('tests/testspecs/environment_tests/resources_parmounts.yml') + @pytest.fixture() def fromjq_pub_default(tmpdir): return packtivity.pack_object.fromspec('tests/testspecs/publisher_tests/fromjq-pub-default.yml') From 990a8ad2a52316b173d1c248316383707fc8061a Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 16:40:19 +0100 Subject: [PATCH 2/8] morecoverage --- tests/test_environments.py | 16 +++ tests/test_typedleafs.py | 105 ++++++++++++++++++ .../environment_tests/resources_docker.yml | 16 +++ .../environment_tests/resources_parmounts.yml | 16 +++ 4 files changed, 153 insertions(+) create mode 100644 tests/test_environments.py create mode 100644 tests/test_typedleafs.py create mode 100644 tests/testspecs/environment_tests/resources_docker.yml create mode 100644 tests/testspecs/environment_tests/resources_parmounts.yml diff --git a/tests/test_environments.py b/tests/test_environments.py new file mode 100644 index 0000000..73c0269 --- /dev/null +++ b/tests/test_environments.py @@ -0,0 +1,16 @@ +from packtivity.handlers.publisher_handlers import handlers +from packtivity.typedleafs import TypedLeafs + +from packtivity.handlers.environment_handlers import handlers +from packtivity.syncbackends import finalize_inputs + +import logging +import monkeypatch + +def test_docker_parmounts(tmpdir,basic_localfs_state, docker_env_parmounts): + state = basic_localfs_state + environment = docker_env_parmounts.spec['environment'] + + parameters, state = finalize_inputs(TypedLeafs({'outputfile': '{workdir}/hello.txt'}), state) + env = handlers[environment['environment_type']]['default'](environment,parameters,state) + assert env['par_mounts'][0]['mountcontent'] == '"{}"'.format(parameters['outputfile']) diff --git a/tests/test_typedleafs.py b/tests/test_typedleafs.py new file mode 100644 index 0000000..19e2170 --- /dev/null +++ b/tests/test_typedleafs.py @@ -0,0 +1,105 @@ +from packtivity.typedleafs import TypedLeafs +class MyClass(object): + def __init__(self, first_attr, second_attr): + self.first_attr = first_attr + self.second_attr = second_attr + + @classmethod + def fromJSON(cls, data): + return cls(**data) + + def json(self): + return { + 'first_attr': self.first_attr, + 'second_attr': self.second_attr + } + +datamodel = {'keyword': '$type', 'types': {'MyClass': MyClass}} + +simple_data = { + 'hello': { + '$type': 'MyClass', + 'first_attr': 'hello', + 'second_attr': 'world' + } +} + +nested_data = { + 'list_of_things': [{ + '$type': 'MyClass', + 'first_attr': 'hello', + 'second_attr': 'world' + }, + { + '$type': 'MyClass', + 'first_attr': 'hello', + 'second_attr': 'world' + }], + 'single_thing': { + '$type': 'MyClass', + 'first_attr': 'hello', + 'second_attr': 'world' + } +} + +def test_init(): + tl = TypedLeafs(simple_data,datamodel) + assert type(tl['hello']) == MyClass + assert tl['hello'].first_attr == 'hello' + assert tl['hello'].second_attr == 'world' + assert tl.json() == simple_data + + tl = TypedLeafs.fromJSON(simple_data, deserialization_opts = {'leafmodel': datamodel}) + assert tl.json() == simple_data + +def test_deepnest(): + tl = TypedLeafs(nested_data,datamodel) + paths = [p.path for p,v in tl.leafs()] + assert set(paths) == set(['/list_of_things/0','/list_of_things/1','/single_thing']) + + +def test_jq(): + tl = TypedLeafs(nested_data,datamodel) + assert tl.jq('.list_of_things[]',multiple_output = True)[0].json() == tl['list_of_things'][0].json() + assert tl.jq('.list_of_things[]',multiple_output = True)[1].json() == tl['list_of_things'][1].json() + + assert tl.jq('[.list_of_things[]]').json() == nested_data['list_of_things'] + +def test_jsonpath(): + tl = TypedLeafs(nested_data,datamodel) + + assert tl.jsonpath('single_thing').json() == tl['single_thing'].json() + assert tl.jsonpath('list_of_things[*]', multiple_output = True)[0].json() == tl['list_of_things'][0].json() + +def test_jsonpointer(): + tl = TypedLeafs(nested_data,datamodel) + for p, v in tl.leafs(): + try: + assert tl.jsonpointer(p.path).json() == v.json() + except AttributeError: + assert tl.jsonpointer(p.path) == v + + +def test_refs(): + import jq + refs = TypedLeafs(nested_data,datamodel).asrefs() + assert refs['list_of_things'][0].path == '/list_of_things/0' + + import jsonpointer + jp = jsonpointer.JsonPointer('/list_of_things/0') + + tl = TypedLeafs(nested_data,datamodel) + assert tl.resolve_ref(jp).json() == tl['list_of_things'][0].json() + + +def test_modify(): + import jq + tl = TypedLeafs(nested_data,datamodel) + tlnew = TypedLeafs({'$type': 'MyClass', 'second_attr':'newsecond', 'first_attr': 'newfirst'}, datamodel) + + tl['single_thing'] = tlnew.typed() + assert type(tlnew.typed()) == MyClass + assert tl['single_thing'].json() == tlnew.typed().json() + + item = tl.pop('single_thing') + assert item.json() == tlnew.typed().json() diff --git a/tests/testspecs/environment_tests/resources_docker.yml b/tests/testspecs/environment_tests/resources_docker.yml new file mode 100644 index 0000000..c82b3ab --- /dev/null +++ b/tests/testspecs/environment_tests/resources_docker.yml @@ -0,0 +1,16 @@ +process: + process_type: 'string-interpolated-cmd' + cmd: 'echo Hello World > {outputfile}; touch inworkdir.txt' +publisher: + publisher_type: 'interpolated-pub' + glob: True + relative_paths: True + publish: + main_output: '{outputfile}' + inworkdir: inworkdir.txt +environment: + environment_type: 'docker-encapsulated' + image: atlas/slc6-atlasos + resources: + - CVMFS + - GRIDProxy diff --git a/tests/testspecs/environment_tests/resources_parmounts.yml b/tests/testspecs/environment_tests/resources_parmounts.yml new file mode 100644 index 0000000..799f6b1 --- /dev/null +++ b/tests/testspecs/environment_tests/resources_parmounts.yml @@ -0,0 +1,16 @@ +process: + process_type: 'string-interpolated-cmd' + cmd: 'echo Hello World > {outputfile}; touch inworkdir.txt' +publisher: + publisher_type: 'interpolated-pub' + glob: True + relative_paths: True + publish: + main_output: '{outputfile}' + inworkdir: inworkdir.txt +environment: + environment_type: 'docker-encapsulated' + image: atlas/slc6-atlasos + par_mounts: + - mountpath: /parmounts/outputfile + jqscript: .outputfile From 07e44ff099417def192bc35fdeb9c4185ad19aa5 Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 16:48:17 +0100 Subject: [PATCH 3/8] morecoverage --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index f49d305..0fd09b9 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,7 @@ 'pyyaml', 'click', 'glob2', + 'monkeypatch', 'jsonpointer', 'jsonpath-rw', 'jq', From 47a0b019300534ab6e657409a97bf0e051f0e0d7 Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 16:51:23 +0100 Subject: [PATCH 4/8] morecoverage --- tests/test_executions.py | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/test_executions.py diff --git a/tests/test_executions.py b/tests/test_executions.py new file mode 100644 index 0000000..a4e018b --- /dev/null +++ b/tests/test_executions.py @@ -0,0 +1,69 @@ +from packtivity.handlers.publisher_handlers import handlers +from packtivity.typedleafs import TypedLeafs + +from packtivity.handlers.execution_handlers import run_docker_with_oneliner, docker_execution_cmdline +import logging +import monkeypatch + +def test_docker_cvmfs(tmpdir,basic_localfs_state, docker_env_resources, monkeypatch): + state = basic_localfs_state + environment = docker_env_resources.spec['environment'] + log = logging.getLogger('test') + job = {'command': 'echo hello world'} + container_argv, stdin = run_docker_with_oneliner(environment,job,log) + + assert container_argv == ['sh','-c','echo hello world'] + assert stdin == None + + cmdline = docker_execution_cmdline( + state,environment,log,{'name':'myname'}, + stdin = stdin, + cmd_argv = container_argv + ) + assert '-v /cvmfs:/cvmfs' in cmdline + + monkeypatch.setenv('PACKTIVITY_CVMFS_LOCATION','/here/cvmfs') + cmdline = docker_execution_cmdline( + state,environment,log,{'name':'myname'}, + stdin = stdin, + cmd_argv = container_argv + ) + assert '-v /here/cvmfs:/cvmfs' in cmdline + + monkeypatch.setenv('PACKTIVITY_CVMFS_SOURCE','voldriver') + cmdline = docker_execution_cmdline( + state,environment,log,{'name':'myname'}, + stdin = stdin, + cmd_argv = container_argv + ) + + assert '-v atlas-condb.cern.ch:/cvmfs/atlas-condb.cern.ch:rw' in cmdline + assert '-v sft.cern.ch:/cvmfs/sft.cern.ch:rw' in cmdline + assert '-v atlas.cern.ch:/cvmfs/atlas.cern.ch:rw' in cmdline + assert '--security-opt label:disable' in cmdline + + +def test_docker_auth(tmpdir,basic_localfs_state, docker_env_resources, monkeypatch): + state = basic_localfs_state + environment = docker_env_resources.spec['environment'] + log = logging.getLogger('test') + job = {'command': 'echo hello world'} + container_argv, stdin = run_docker_with_oneliner(environment,job,log) + + assert container_argv == ['sh','-c','echo hello world'] + assert stdin == None + + cmdline = docker_execution_cmdline( + state,environment,log,{'name':'myname'}, + stdin = stdin, + cmd_argv = container_argv + ) + assert '-v /home/recast/recast_auth:/recast_auth:rw' in cmdline + + monkeypatch.setenv('PACKTIVITY_AUTH_LOCATION','/here') + cmdline = docker_execution_cmdline( + state,environment,log,{'name':'myname'}, + stdin = stdin, + cmd_argv = container_argv + ) + assert '-v /here:/recast_auth:rw' in cmdline From 03c7834a0d6dbbd0edad255ff09cec75ee61f9b4 Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 17:19:56 +0100 Subject: [PATCH 5/8] morecoverage --- tests/test_executions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_executions.py b/tests/test_executions.py index a4e018b..5a253ab 100644 --- a/tests/test_executions.py +++ b/tests/test_executions.py @@ -3,7 +3,6 @@ from packtivity.handlers.execution_handlers import run_docker_with_oneliner, docker_execution_cmdline import logging -import monkeypatch def test_docker_cvmfs(tmpdir,basic_localfs_state, docker_env_resources, monkeypatch): state = basic_localfs_state From 83aac293d7e5283b72c9e77ffe82dcaab22c4079 Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 17:43:00 +0100 Subject: [PATCH 6/8] no monkeypatch module --- setup.py | 1 - tests/test_environments.py | 1 - 2 files changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 0fd09b9..f49d305 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,6 @@ 'pyyaml', 'click', 'glob2', - 'monkeypatch', 'jsonpointer', 'jsonpath-rw', 'jq', diff --git a/tests/test_environments.py b/tests/test_environments.py index 73c0269..4cfbf6a 100644 --- a/tests/test_environments.py +++ b/tests/test_environments.py @@ -5,7 +5,6 @@ from packtivity.syncbackends import finalize_inputs import logging -import monkeypatch def test_docker_parmounts(tmpdir,basic_localfs_state, docker_env_parmounts): state = basic_localfs_state From 590bbb16cfbb515c1b26c97610eb1510b1d68aa1 Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 18:02:45 +0100 Subject: [PATCH 7/8] indentation --- tests/test_environments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_environments.py b/tests/test_environments.py index 4cfbf6a..d42d013 100644 --- a/tests/test_environments.py +++ b/tests/test_environments.py @@ -11,5 +11,5 @@ def test_docker_parmounts(tmpdir,basic_localfs_state, docker_env_parmounts): environment = docker_env_parmounts.spec['environment'] parameters, state = finalize_inputs(TypedLeafs({'outputfile': '{workdir}/hello.txt'}), state) - env = handlers[environment['environment_type']]['default'](environment,parameters,state) + env = handlers[environment['environment_type']]['default'](environment,parameters,state) assert env['par_mounts'][0]['mountcontent'] == '"{}"'.format(parameters['outputfile']) From a916114865eaaf1b3c44f91d51b9b28622474038 Mon Sep 17 00:00:00 2001 From: lukasheinrich Date: Tue, 16 Jan 2018 18:18:36 +0100 Subject: [PATCH 8/8] indentation --- packtivity/typedleafs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packtivity/typedleafs.py b/packtivity/typedleafs.py index 9620d6d..bc50aba 100644 --- a/packtivity/typedleafs.py +++ b/packtivity/typedleafs.py @@ -27,11 +27,11 @@ def __init__(self, spec): self.leaf_magic = '___leaf___' def leaf_encode(self,obj): - return self.leaf_magic + base64.b64encode(json.dumps(self.dumper(obj))) + return self.leaf_magic + base64.b64encode(json.dumps(self.dumper(obj)).encode('utf-8')) @staticmethod def leaf_decode(str): - return json.loads(base64.b64decode(str)) + return json.loads(base64.b64decode(str).decode('utf-8')) def loader(self, spec, idleafs): if not self.keyword: return spec