Compare commits
17 Commits
Author | SHA1 | Date |
---|---|---|
Charles Reid | f9cc1d93f2 | 5 years ago |
Charles Reid | 09417f7c23 | 5 years ago |
Charles Reid | cf4145aee5 | 5 years ago |
Charles Reid | 98ef3fa49a | 5 years ago |
Charles Reid | 4b69492d40 | 5 years ago |
Charles Reid | c8e1760470 | 5 years ago |
Charles Reid | 861dc16a64 | 5 years ago |
Charles Reid | fc0479ff77 | 5 years ago |
Charles Reid | dae3b1dd45 | 5 years ago |
Charles Reid | 88cd2f71a6 | 5 years ago |
Charles Reid | 6c29958c13 | 5 years ago |
Charles Reid | 682f760c10 | 5 years ago |
Charles Reid | 742f24d0e5 | 5 years ago |
Charles Reid | abd4a07dec | 5 years ago |
Charles Reid | 48f4d30903 | 5 years ago |
Charles Reid | a908ddf8c2 | 5 years ago |
Charles Reid | 71f48e0f5e | 5 years ago |
17 changed files with 327 additions and 107 deletions
@ -0,0 +1,13 @@ |
|||||||
|
# https://docs.travis-ci.com/user/languages/python/ |
||||||
|
language: python |
||||||
|
python: |
||||||
|
- "3.5" |
||||||
|
- "3.6" |
||||||
|
- "3.7-dev" |
||||||
|
# command to install dependencies |
||||||
|
install: |
||||||
|
- pip install -r requirements.txt |
||||||
|
- python setup.py build install |
||||||
|
# command to run tests |
||||||
|
script: |
||||||
|
- pytest |
@ -0,0 +1,19 @@ |
|||||||
|
Copyright (c) 2019 Charles Reid |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,103 @@ |
|||||||
|
""" |
||||||
|
Command line interface driver for snakemake workflows |
||||||
|
""" |
||||||
|
import argparse |
||||||
|
import os.path |
||||||
|
import snakemake |
||||||
|
import sys |
||||||
|
import pprint |
||||||
|
import json |
||||||
|
|
||||||
|
from . import _program |
||||||
|
|
||||||
|
|
||||||
|
thisdir = os.path.abspath(os.path.dirname(__file__)) |
||||||
|
parentdir = os.path.join(thisdir,'..') |
||||||
|
cwd = os.getcwd() |
||||||
|
|
||||||
|
def main(sysargs = sys.argv[1:]): |
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(prog = _program, description='bananas: run snakemake workflows', usage='''bananas <workflow> <parameters> [<target>] |
||||||
|
|
||||||
|
bananas: run snakemake workflows, using the given workflow name & parameters file. |
||||||
|
|
||||||
|
''') |
||||||
|
|
||||||
|
parser.add_argument('workflowfile') |
||||||
|
parser.add_argument('paramsfile') |
||||||
|
parser.add_argument('-n', '--dry-run', action='store_true') |
||||||
|
parser.add_argument('-f', '--force', action='store_true') |
||||||
|
args = parser.parse_args(sysargs) |
||||||
|
|
||||||
|
# first, find the Snakefile |
||||||
|
snakefile_this = os.path.join(thisdir,"Snakefile") |
||||||
|
snakefile_parent = os.path.join(parentdir,"Snakefile") |
||||||
|
if os.path.exists(snakefile_this): |
||||||
|
snakefile = snakefile_this |
||||||
|
elif os.path.exists(snakefile_parent): |
||||||
|
snakefile = snakefile_parent |
||||||
|
else: |
||||||
|
msg = 'Error: cannot find Snakefile at any of the following locations:\n' |
||||||
|
msg += '{}\n'.format(snakefile_this) |
||||||
|
msg += '{}\n'.format(snakefile_parent) |
||||||
|
sys.stderr.write(msg) |
||||||
|
sys.exit(-1) |
||||||
|
|
||||||
|
# next, find the workflow config file |
||||||
|
workflowfile = None |
||||||
|
w1 = os.path.join(cwd,args.workflowfile) |
||||||
|
w2 = os.path.join(cwd,args.workflowfile+'.json') |
||||||
|
if os.path.exists(w1) and not os.path.isdir(w1): |
||||||
|
workflowfile = w1 |
||||||
|
elif os.path.exists(w2) and not os.path.isdir(w2): |
||||||
|
workflowfile = w2 |
||||||
|
|
||||||
|
if not workflowfile: |
||||||
|
msg = 'Error: cannot find workflowfile {} or {} '.format(w1,w2) |
||||||
|
msg += 'in directory {}\n'.format(cwd) |
||||||
|
sys.stderr.write(msg) |
||||||
|
sys.exit(-1) |
||||||
|
|
||||||
|
# next, find the workflow params file |
||||||
|
paramsfile = None |
||||||
|
p1 = os.path.join(cwd,args.paramsfile) |
||||||
|
p2 = os.path.join(cwd,args.paramsfile+'.json') |
||||||
|
if os.path.exists(p1) and not os.path.isdir(p1): |
||||||
|
paramsfile = p1 |
||||||
|
elif os.path.exists(p2) and not os.path.isdir(p2): |
||||||
|
paramsfile = p2 |
||||||
|
|
||||||
|
if not paramsfile: |
||||||
|
msg = 'Error: cannot find paramsfile {} or {} '.format(p1,p2) |
||||||
|
msg += 'in directory {}\n'.format(cwd) |
||||||
|
sys.stderr.write(msg) |
||||||
|
sys.exit(-1) |
||||||
|
|
||||||
|
with open(workflowfile, 'rt') as fp: |
||||||
|
workflow_info = json.load(fp) |
||||||
|
|
||||||
|
target = workflow_info['workflow_target'] |
||||||
|
config = dict() |
||||||
|
|
||||||
|
print('--------') |
||||||
|
print('details!') |
||||||
|
print('\tsnakefile: {}'.format(snakefile)) |
||||||
|
print('\tconfig: {}'.format(workflowfile)) |
||||||
|
print('\tparams: {}'.format(paramsfile)) |
||||||
|
print('\ttarget: {}'.format(target)) |
||||||
|
print('--------') |
||||||
|
|
||||||
|
# run bananas!! |
||||||
|
status = snakemake.snakemake(snakefile, configfile=paramsfile, |
||||||
|
targets=[target], printshellcmds=True, |
||||||
|
dryrun=args.dry_run, forceall=args.force, |
||||||
|
config=config) |
||||||
|
|
||||||
|
if status: # translate "success" into shell exit code of 0 |
||||||
|
return 0 |
||||||
|
return 1 |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
||||||
|
|
@ -0,0 +1,17 @@ |
|||||||
|
appdirs==1.4.3 |
||||||
|
certifi==2018.11.29 |
||||||
|
chardet==3.0.4 |
||||||
|
ConfigArgParse==0.14.0 |
||||||
|
datrie==0.7.1 |
||||||
|
docutils==0.14 |
||||||
|
gitdb2==2.0.5 |
||||||
|
GitPython==2.1.11 |
||||||
|
idna==2.8 |
||||||
|
jsonschema==2.6.0 |
||||||
|
PyYAML==3.13 |
||||||
|
ratelimiter==1.2.0.post0 |
||||||
|
requests==2.21.0 |
||||||
|
smmap2==2.0.5 |
||||||
|
snakemake==5.4.0 |
||||||
|
urllib3==1.24.1 |
||||||
|
wrapt==1.11.0 |
@ -1,91 +0,0 @@ |
|||||||
#! /usr/bin/env python |
|
||||||
""" |
|
||||||
Execution script for snakemake workflows. |
|
||||||
""" |
|
||||||
import argparse |
|
||||||
import os.path |
|
||||||
import snakemake |
|
||||||
import sys |
|
||||||
import pprint |
|
||||||
import json |
|
||||||
|
|
||||||
|
|
||||||
thisdir = os.path.abspath(os.path.dirname(__file__)) |
|
||||||
|
|
||||||
|
|
||||||
def main(args): |
|
||||||
# first, find the Snakefile |
|
||||||
snakefile = os.path.join(thisdir, 'Snakefile') |
|
||||||
if not os.path.exists(snakefile): |
|
||||||
sys.stderr.write('Error: cannot find Snakefile at {}\n'.format(snakefile)) |
|
||||||
sys.exit(-1) |
|
||||||
|
|
||||||
# next, find the workflow config file |
|
||||||
workflowfile = None |
|
||||||
if os.path.exists(args.workflowfile) and not os.path.isdir(args.workflowfile): |
|
||||||
workflowfile = args.workflowfile |
|
||||||
else: |
|
||||||
for suffix in ('', '.json'): |
|
||||||
tryfile = os.path.join(thisdir, args.workflowfile + suffix) |
|
||||||
if os.path.exists(tryfile) and not os.path.isdir(tryfile): |
|
||||||
sys.stderr.write('Found workflowfile at {}\n'.format(tryfile)) |
|
||||||
workflowfile = tryfile |
|
||||||
break |
|
||||||
|
|
||||||
if not workflowfile: |
|
||||||
sys.stderr.write('Error: cannot find workflowfile {}\n'.format(args.workflowfile)) |
|
||||||
sys.exit(-1) |
|
||||||
|
|
||||||
# next, find the workflow params file |
|
||||||
paramsfile = None |
|
||||||
if os.path.exists(args.paramsfile) and not os.path.isdir(args.paramsfile): |
|
||||||
paramsfile = args.paramsfile |
|
||||||
else: |
|
||||||
for suffix in ('', '.json'): |
|
||||||
tryfile = os.path.join(thisdir, args.paramsfile + suffix) |
|
||||||
if os.path.exists(tryfile) and not os.path.isdir(tryfile): |
|
||||||
sys.stderr.write('Found paramsfile at {}\n'.format(tryfile)) |
|
||||||
paramsfile = tryfile |
|
||||||
break |
|
||||||
|
|
||||||
if not paramsfile: |
|
||||||
sys.stderr.write('Error: cannot find paramsfile {}\n'.format(args.paramsfile)) |
|
||||||
sys.exit(-1) |
|
||||||
|
|
||||||
with open(workflowfile, 'rt') as fp: |
|
||||||
workflow_info = json.load(fp) |
|
||||||
|
|
||||||
target = workflow_info['workflow_target'] |
|
||||||
config = dict() |
|
||||||
|
|
||||||
print('--------') |
|
||||||
print('details!') |
|
||||||
print('\tsnakefile: {}'.format(snakefile)) |
|
||||||
print('\tconfig: {}'.format(workflowfile)) |
|
||||||
print('\tparams: {}'.format(paramsfile)) |
|
||||||
print('\ttarget: {}'.format(target)) |
|
||||||
print('--------') |
|
||||||
|
|
||||||
# run!! |
|
||||||
status = snakemake.snakemake(snakefile, configfile=paramsfile, |
|
||||||
targets=[target], printshellcmds=True, |
|
||||||
dryrun=args.dry_run, config=config) |
|
||||||
|
|
||||||
if status: # translate "success" into shell exit code of 0 |
|
||||||
return 0 |
|
||||||
return 1 |
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
parser = argparse.ArgumentParser(description='run snakemake workflows', usage='''run <workflow> <parameters> [<target>] |
|
||||||
|
|
||||||
Run snakemake workflows, using the given workflow name & parameters file. |
|
||||||
|
|
||||||
''') |
|
||||||
|
|
||||||
parser.add_argument('workflowfile') |
|
||||||
parser.add_argument('paramsfile') |
|
||||||
parser.add_argument('-n', '--dry-run', action='store_true') |
|
||||||
args = parser.parse_args() |
|
||||||
|
|
||||||
sys.exit(main(args)) |
|
@ -0,0 +1,29 @@ |
|||||||
|
from setuptools import setup, find_packages |
||||||
|
import glob |
||||||
|
import os |
||||||
|
|
||||||
|
with open('requirements.txt') as f: |
||||||
|
required = [x for x in f.read().splitlines() if not x.startswith("#")] |
||||||
|
|
||||||
|
# Note: the _program variable is set in __init__.py. |
||||||
|
# it determines the name of the package/final command line tool. |
||||||
|
from cli import __version__, _program |
||||||
|
|
||||||
|
setup(name='bananas', |
||||||
|
version=__version__, |
||||||
|
packages=['cli'], |
||||||
|
test_suite='pytest.collector', |
||||||
|
tests_require=['pytest'], |
||||||
|
description='bananas command line interface', |
||||||
|
url='https://charlesreid1.github.io/2019-snakemake-cli', |
||||||
|
author='@charlesreid1', |
||||||
|
author_email='cmreid@ucdavis.edu', |
||||||
|
license='MIT', |
||||||
|
entry_points=""" |
||||||
|
[console_scripts] |
||||||
|
{program} = cli.command:main |
||||||
|
""".format(program = _program), |
||||||
|
install_requires=required, |
||||||
|
include_package_data=True, |
||||||
|
keywords=[], |
||||||
|
zip_safe=False) |
@ -0,0 +1,76 @@ |
|||||||
|
from unittest import TestCase |
||||||
|
from subprocess import call, Popen, PIPE |
||||||
|
import os |
||||||
|
import shutil, tempfile |
||||||
|
from os.path import isdir, join |
||||||
|
|
||||||
|
|
||||||
|
""" |
||||||
|
test banana |
||||||
|
|
||||||
|
this test will run bananas with the test |
||||||
|
config and params provided in the test dir. |
||||||
|
|
||||||
|
this test will also show how to run tests where |
||||||
|
failure is expected (i.e., checking that we handle |
||||||
|
invalid parameters). |
||||||
|
|
||||||
|
each test has a unittest TestCase defined. |
||||||
|
pytest will automatically find these tests. |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
class TestBananas(TestCase): |
||||||
|
""" |
||||||
|
simple bananas test class |
||||||
|
|
||||||
|
This uses the subprocess PIPE var |
||||||
|
to capture system input and output, |
||||||
|
since we are running bananas from the |
||||||
|
command line directly using subprocess. |
||||||
|
""" |
||||||
|
@classmethod |
||||||
|
def setUpClass(self): |
||||||
|
""" |
||||||
|
set up a bananas workflow test. |
||||||
|
|
||||||
|
we are using the existing test/ dir |
||||||
|
as our working dir, so no setup to do. |
||||||
|
|
||||||
|
if we were expecting the user to provide |
||||||
|
a Snakefile, this is where we would set |
||||||
|
up a test Snakefile. |
||||||
|
""" |
||||||
|
pass |
||||||
|
|
||||||
|
def test_hello(self): |
||||||
|
""" |
||||||
|
test hello workflow |
||||||
|
""" |
||||||
|
command_prefix = ['bananas','workflow-hello'] |
||||||
|
|
||||||
|
params = ['params-amy','params-beth'] |
||||||
|
|
||||||
|
pwd = os.path.abspath(os.path.dirname(__file__)) |
||||||
|
|
||||||
|
for param in params: |
||||||
|
|
||||||
|
command = command_prefix + [param] |
||||||
|
|
||||||
|
p = Popen(command, cwd=pwd, stdout=PIPE, stderr=PIPE).communicate() |
||||||
|
p_out = p[0].decode('utf-8').strip() |
||||||
|
p_err = p[1].decode('utf-8').strip() |
||||||
|
|
||||||
|
self.assertIn('details',p_out) |
||||||
|
|
||||||
|
# clean up |
||||||
|
call(['rm','-f','hello.txt']) |
||||||
|
|
||||||
|
|
||||||
|
@classmethod |
||||||
|
def tearDownClass(self): |
||||||
|
""" |
||||||
|
clean up after the tests |
||||||
|
""" |
||||||
|
pass |
||||||
|
|
Loading…
Reference in new issue