snakemake icon indicating copy to clipboard operation
snakemake copied to clipboard

🐛 Nested config parsed as string when using --config

Open Wytamma opened this issue 2 years ago • 2 comments

If nested config is passed to Snakemake via the --config flag all values are interpreted as strings.

$ snakemake --config test={'test_bool': True, 'int_test': 1}
 'test': {'test_bool': 'True', 'int_test': '1'}  # Note strings

This is because the yaml parser is using the BaseLoader which does not convert YAML scalars to their corresponding Python types (instead returning them as strings).

If I update the yaml parser to use SafeLoader then the expected behaviour is achieved.

- yaml_base_load = lambda s: yaml.load(s, Loader=yaml.loader.B̶a̶s̶e̶L̶o̶a̶d̶e̶r̶)
+ yaml_base_load = lambda s: yaml.load(s, Loader=yaml.loader.SafeLoader)
$ snakemake --config test={'test_bool': True, 'int_test': 1}
 'test': {'test_bool': True, 'int_test': 1}  # Note correct types

Should I submit a PR to fix this issue?

Wytamma avatar Jul 30 '23 10:07 Wytamma

SafeLoader DateTime auto-conversion should potentially be switched switched-off as per https://github.com/snakemake/snakemake/pull/709#issuecomment-730336048

Wytamma avatar Jan 30 '24 22:01 Wytamma

Just ran into this issue! Here is a safe loader I have used in the past which does not convert datetime strings to datetime objects

import datetime
import yaml

class SafeLoaderTimestamp(yaml.SafeLoader):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.add_constructor("tag:yaml.org,2002:timestamp", self.construct_yaml_str)
    
     @staticmethod
     def construct_yaml_str(loader, node):
         return loader.construct_scalar(node)

data = yaml.load('{date: 2019-01-01, number: 1.01}', Loader=SafeLoaderTimestamp)
assert isinstance(data["date"], str)
assert isinstance(data["number"], float)

data = yaml.load('{date: 2019-01-01, number: 1.01}', Loader=yaml.SafeLoader)
assert isinstance(data["date"], datetime.date)
assert isinstance(data["number"], float)

data = yaml.load('{date: 2019-01-01, number: 1.01}', Loader=yaml.BaseLoader)
assert isinstance(data["date"], str)
assert isinstance(data["number"], str)

ollie-bell avatar Jul 30 '24 11:07 ollie-bell