Skip to content
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

DEV: Can now double-handle --profile #8

Merged
merged 9 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Codecov

on:
push:
branches: [ "main" ]
paths:
- '.github/**'
- 'build/**'
- 'tests/**'
- '{{cookiecutter.project_slug}}/**'
pull_request:
branches: [ "main" ]
paths:
- '.github/**'
- 'build/**'
- 'tests/**'
- '{{cookiecutter.project_slug}}/**'

permissions:
contents: read

jobs:
tests:
name: "Codecov using python ${{ matrix.python-version }} on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}

defaults:
run:
shell: bash -el {0}

strategy:
matrix:
os: [ubuntu-latest]
python-version: ["3.10"]

steps:
- uses: "actions/checkout@v3"
with:
fetch-depth: 0

# Setup env
- uses: conda-incubator/setup-miniconda@v2
with:
activate-environment: snaketool
environment-file: build/environment.yaml
python-version: ${{ matrix.python-version }}
auto-activate-base: false

- name: "Setup Snaketool on ${{ matrix.os }} for Python ${{ matrix.python-version }}"
run: |
cookiecutter --no-input ./
cd my_snaketool/
python -m pip install --upgrade pip
pip install .

- name: "Generate coverage report on ${{ matrix.os }} for Python ${{ matrix.python-version }}"
run: |
pip install pytest pytest-cov
pytest --cov=./ --cov-report=xml --cov-append

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage.xml
fail_ci_if_error: true
9 changes: 1 addition & 8 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ permissions:

jobs:
tests:
name: "Python ${{ matrix.python-version }}"
name: "Python ${{ matrix.python-version }} on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}

defaults:
Expand Down Expand Up @@ -60,10 +60,3 @@ jobs:
run: |
pip install pytest pytest-cov
pytest --cov=./ --cov-report=xml --cov-append

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage.xml
fail_ci_if_error: true
5 changes: 3 additions & 2 deletions tests/test_snaketool.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ def test_snaketool_cli(tmp_dir):
exec_command("my_snaketool -h")
exec_command("my_snaketool run -h")
exec_command("my_snaketool config -h")
exec_command("my_snaketool --version")


def test_snaketool_commands(tmp_dir):
"""test Snaketool"""
exec_command("my_snaketool run --input yeet")
exec_command("my_snaketool run --input yeet --no-use-conda ")
exec_command("my_snaketool config")
exec_command("my_snaketool citation")
exec_command("my_snaketool citation")
2 changes: 1 addition & 1 deletion {{cookiecutter.project_slug}}/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def get_data_files():
data_files=get_data_files(),
py_modules=["{{cookiecutter.project_slug}}"],
install_requires=[
"snaketool-utils>=0.0.2",
"snaketool-utils>=0.0.4",
"snakemake{{cookiecutter.snakemake_version}}",
"pyyaml{{cookiecutter.pyyaml_version}}",
"Click{{cookiecutter.click_version}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ def common_options(func):
click.option(
"--threads", help="Number of threads to use", default=1, show_default=True
),
click.option(
"--profile",
default=None,
help="Snakemake profile to use",
show_default=False,
),
click.option(
"--use-conda/--no-use-conda",
default=True,
Expand All @@ -76,7 +82,6 @@ def common_options(func):
"--snake-default",
multiple=True,
default=[
"--rerun-incomplete",
"--printshellcmds",
"--nolock",
"--show-failed-logs",
Expand All @@ -90,6 +95,11 @@ def common_options(func):
callback=default_to_output,
hidden=True,
),
click.option(
"--system-config",
default=snake_base(os.path.join("config", "config.yaml")),
hidden=True,
),
click.argument("snake_args", nargs=-1),
]
for option in reversed(options):
Expand Down Expand Up @@ -137,31 +147,29 @@ def cli():
)
@click.option("--input", "_input", help="Input file/directory", type=str, required=True)
@common_options
def run(_input, output, log, **kwargs):
def run(**kwargs):
"""Run {{cookiecutter.project_name}}"""
# Config to add or update in configfile
merge_config = {
"input": _input,
"output": output,
"log": log
"{{cookiecutter.project_slug}}": {
"args": kwargs
}
}

# run!
run_snakemake(
# Full path to Snakefile
snakefile_path=snake_base(os.path.join("workflow", "Snakefile")),
system_config=snake_base(os.path.join("config", "config.yaml")),
merge_config=merge_config,
log=log,
**kwargs
)


@click.command()
@common_options
def config(configfile, **kwargs):
def config(configfile, system_config, **kwargs):
"""Copy the system default config file"""
copy_config(configfile, system_config=snake_base(os.path.join("config", "config.yaml")))
copy_config(configfile, system_config=system_config)


@click.command()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Snakemake config
input:
output: '{{cookiecutter.project_slug}}.out/'
log: '{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.log'
# Namespaced config file example
{{cookiecutter.project_slug}}:
args: # Command line args will be (over)written here at runtime
input:
output:
# Add customisable config here
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{cookiecutter.project_slug}}:
# Config you don't want users to change
Original file line number Diff line number Diff line change
@@ -1,49 +1,24 @@
import glob

# Update default config with runtime config
configfile: os.path.join(workflow.basedir, "../", "config", "config.yaml")
configfile: os.path.join(workflow.basedir, "../", "config", "system_config.yaml")
config.update(config["{{cookiecutter.project_slug}}"]) # convenience if using namespaced config

configfile: os.path.join(workflow.basedir, '../', 'config', 'config.yaml')


# Concatenate Snakemake's own log file with the master log file
def copy_log_file():
files = glob.glob(os.path.join(".snakemake", "log", "*.snakemake.log"))
if not files:
return None
current_log = max(files, key=os.path.getmtime)
shell("cat " + current_log + " >> " + config['log'])

onsuccess:
copy_log_file()

onerror:
copy_log_file()


# Target file
outTouch = os.path.join(config['output'], config['input'])


# Mark target rules
target_rules = []
def targetRule(fn):
assert fn.__name__.startswith('__')
target_rules.append(fn.__name__[2:])
return fn
# Rules files
include: os.path.join(workflow.basedir, "rules", "preflight.smk")
include: os.path.join(workflow.basedir, "rules", "example.smk")


# Target rules
@targetRule
rule all:
input:
outTouch
targets


@targetRule
rule print_targets:
run:
print("\nTop level rules are: \n", file=sys.stderr)
print("* " + "\n* ".join(target_rules) + "\n\n", file=sys.stderr)


rule yeet:
output:
touch(outTouch)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: python
channels:
- defaults
dependencies:
- python>=3.9
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
rule example:
output:
os.path.join(dirs["results"], "example.done")
conda:
os.path.join(dirs["envs"], "example.yaml")
benchmark:
os.path.join(dirs["bench"], "example.txt")
log:
os.path.join(dirs["logs"], "example.err")
script:
os.path.join(dirs["scripts"], "example.py")
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

# Directories
dirs = {
"logs": os.path.join(config["args"]["output"], "logs"),
"bench": os.path.join(config["args"]["output"], "bench"),
"results": os.path.join(config["args"]["output"], "results"),
"envs": os.path.join(workflow.basedir, "envs"),
"scripts": os.path.join(workflow.basedir, "scripts")
}


# Targets
targets = [
os.path.join(dirs["results"], "example.done")
]


# Misc
target_rules = []


def targetRule(fn):
"""Mark rules as target rules for rule print_targets"""
assert fn.__name__.startswith("__")
target_rules.append(fn.__name__[2:])
return fn


def copy_log_file():
"""Concatenate Snakemake log to output log file"""
import glob

files = glob.glob(os.path.join(".snakemake", "log", "*.snakemake.log"))
if files:
current_log = max(files, key=os.path.getmtime)
shell("cat " + current_log + " >> " + config["args"]["log"])


onsuccess:
copy_log_file()

onerror:
copy_log_file()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

def main(**kwargs):
open(kwargs["output"], "w").close()


if __name__ == "__main__":
main(output=snakemake.output[0],)