Repository: bow/crimson (language: python)

Issue:
Crimson crashes on vep stats with no data
When an entry in the vep stats output has no data, crimson crashes while parsing the stats file.

Example data:
```
[Coding consequences]

[SIFT summary]

[PolyPhen summary]
```
Running crimson
```bash
crimson vep no-variants.vep_stats.txt
Traceback (most recent call last):
  File "/anaconda3/envs/hamlet-devel/bin/crimson", line 10, in <module>
    sys.exit(main())
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/crimson/cli.py", line 154, in vep
    payload = m_vep.parse(input)
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/crimson/vep.py", line 99, in parse
    entries = [group2entry(g) for g in contents.split("\n\n")]
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/crimson/vep.py", line 99, in <listcomp>
    entries = [group2entry(g) for g in contents.split("\n\n")]
  File "/anaconda3/envs/hamlet-devel/lib/python3.6/site-packages/crimson/vep.py", line 70, in group2entry
    raw_key, raw_value = group.split("\n", 1)
ValueError: not enough values to unpack (expected 2, got 1)
```



Discussion / hints:

Fix patch:
diff --git a/crimson/vep.py b/crimson/vep.py
index f86fb0d..93fffcf 100644
--- a/crimson/vep.py
+++ b/crimson/vep.py
@@ -67,7 +67,12 @@ def group2entry(
          [1, 40, 35, 50, ...])
 
     """
+    # If there is no data, only a header, we return an empty dictionary
+    if '\n' not in group:
+        return group[1:-1], dict()
+
     raw_key, raw_value = group.split("\n", 1)
+
     key = raw_key[1:-1]
 
     values = (line.split("\t", 1) for line in raw_value.strip().split("\n"))


Test patch:
diff --git a/tests/cases/vep_v97_with_empty.txt b/tests/cases/vep_v97_with_empty.txt
new file mode 100644
index 0000000..a4caa67
--- /dev/null
+++ b/tests/cases/vep_v97_with_empty.txt
@@ -0,0 +1,40 @@
+[VEP run statistics]
+VEP version (API)	 97 (97)
+Annotation sources	Cache: /exports/sasc-testing/fixtures/HAMLET/deps/vep/ensembl-vep/cache_dir/homo_sapiens_merged/97_GRCh38; Custom: /exports/sasc-testing/fixtures/HAMLET/deps/data/1KG.phase3.GRCh38.with_chr.vcf.gz (exact); Custom: /exports/sasc-testing/fixtures/HAMLET/deps/data/gonl.grch38.sorted.filtered.snps_indels.r5.vcf.gz (exact)
+Species	homo_sapiens
+Command line options	--allele_number --assembly GRCh38 --cache --cache_version 97 --custom /exports/sasc-testing/fixtures/HAMLET/deps/data/1KG.phase3.GRCh38.with_chr.vcf.gz,P3,vcf,exact,0,AF,AFR_AF,AMR_AF,EAS_AF,EUR_AF,SAS_AF --custom /exports/sasc-testing/fixtures/HAMLET/deps/data/gonl.grch38.sorted.filtered.snps_indels.r5.vcf.gz,GONL,vcf,exact,0,AF --dir /exports/sasc-testing/fixtures/HAMLET/deps/vep/ensembl-vep/cache_dir/ --everything --fasta /exports/sasc-testing/fixtures/HAMLET/deps/vep/Homo_sapiens.GRCh38.dna.primary_assembly.fa.gz --force_overwrite --fork 8 --input_file no-variants/snv-indels/no-variants.raw.vcf.gz --merged --offline --output_file STDOUT --stats_file no-variants/snv-indels/no-variants.vep_stats.txt --stats_text --vcf
+Start time	2020-06-11 11:36:28
+End time	2020-06-11 11:36:30
+Run time	2 seconds
+Input file	no-variants/snv-indels/no-variants.raw.vcf.gz
+Output file	STDOUT
+
+[General statistics]
+Lines of input read	1
+Variants processed	1
+Variants filtered out	0
+Novel / existing variants	-
+Overlapped genes	5
+Overlapped transcripts	12
+Overlapped regulatory features	2
+
+[Variant classes]
+SNV	1
+
+[Consequences (most severe)]
+non_coding_transcript_exon_variant	1
+
+[Consequences (all)]
+non_coding_transcript_exon_variant	3
+upstream_gene_variant	9
+TF_binding_site_variant	1
+regulatory_region_variant	2
+
+[Coding consequences]
+
+[SIFT summary]
+
+[PolyPhen summary]
+
+[Variants by chromosome]
+chr6	1
diff --git a/tests/test_vep.py b/tests/test_vep.py
index 4b5a8e7..90ba80f 100644
--- a/tests/test_vep.py
+++ b/tests/test_vep.py
@@ -32,6 +32,15 @@ def vep_v77_01():
     return result
 
 
+@pytest.fixture(scope="module")
+def vep_v97_with_empty():
+    runner = CliRunner()
+    in_file = get_test_path("vep_v97_with_empty.txt")
+    result = runner.invoke(main, ["vep", in_file])
+    result.json = json.loads(result.output)
+    return result
+
+
 def test_vep_fail_exit_code(vep_fail):
     assert vep_fail.exit_code != 0
 
@@ -55,3 +64,10 @@ def test_vep_fail_output(vep_fail):
 def test_vep_v77_01(vep_v77_01, attrs, exp):
     assert getattr_nested(vep_v77_01.json, attrs) == exp, \
         ", ".join([repr(x) for x in attrs])
+
+
+def test_vep_v97_headers(vep_v97_with_empty):
+    """ Test if the empty headers are in the output json """
+    headers = ["Coding consequences", "SIFT summary", "PolyPhen summary"]
+    for header in headers:
+        assert header in vep_v97_with_empty.json