pythonsnakemake

Using a function to set log file id with snakemake


I would like to use a function to set output log file names easily.

I tried the following function

# output_path=simulations/sim1/model/test.out
# idx=2   == add <x> after 2nd element
# x=logs
# returns= simulations/sim1/logs/model/test.out
def get_xpath(wildcards,x,idx):
  outbase=wildcards.output[0]
  return str('/'.join(outbase.split('/')[:idx])+"/"+x+"/"+'/'.join(outbase.split('/')[idx:]))

rule testme:
...
  log: lambda wildcards: get_xpath(wildcards,"logs",2),

But it gives the following AttributeError

'function' object has no attribute 'get_wildcard_names'

I also tried using lambda:

rule testme:
...
  log: lambda wildcards, output: output[0].split('.')[0],

Which gives the same error. I could not find any examples to achieve this in the snakemake documentation, so I am not sure if this is even possible. Any ideas?

Thanks!


Solution

  • Here is a workaround.

    You still cannot get the output file name without copying it manually, but you can use a function to make it easier to name log files by using rule names as variables. It can be improved by making use of certain wildcards or by providing a hard copy of the output filename. Though setting rule names as variables limits some functionality such as directly calling a rule by its name.

    def logfile(rule_name,wildcards=None,output=None):
        return f"{config['dataset_id']}/logs/{rule_name}.log"
    
    ruleid="get_fasta_per_locus"
    rule ruleid:
        input:
        output:
        log:
            lambda wildcards: logfile(ruleid)
    

    2024 update

    My current workaround:

    def get_output_log(wildcards, output):
            out=output[0].split('/')
            if len(out) < 4:
                    raise ValueError("Output path must have at least 4 components")
            out.insert(2, "logs")
            # create the directory if it does not exist
            out = [x.format(**wildcards) for x in out]
            os.makedirs('/'.join(out[:-1]), exist_ok=True)
            return '/'.join(out)
    

    Usage:

    rule xx:
        input: ...
        output: ...
        params:
            log=lambda wildcards, output: get_output_log(wildcards, output),
        shell:
            """
            command &> {params.log}
            """