varcode 9.2.1__tar.gz → 9.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. {varcode-9.2.1/varcode.egg-info → varcode-9.2.2}/PKG-INFO +1 -1
  2. varcode-9.2.2/tests/test_sv_tabular_metadata.py +69 -0
  3. {varcode-9.2.1 → varcode-9.2.2}/varcode/csv_helpers.py +12 -0
  4. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_collection.py +7 -2
  5. {varcode-9.2.1 → varcode-9.2.2}/varcode/structural_variant.py +15 -3
  6. {varcode-9.2.1 → varcode-9.2.2}/varcode/variant_collection.py +9 -2
  7. varcode-9.2.2/varcode/version.py +1 -0
  8. {varcode-9.2.1 → varcode-9.2.2/varcode.egg-info}/PKG-INFO +1 -1
  9. {varcode-9.2.1 → varcode-9.2.2}/varcode.egg-info/SOURCES.txt +1 -0
  10. varcode-9.2.1/varcode/version.py +0 -1
  11. {varcode-9.2.1 → varcode-9.2.2}/LICENSE +0 -0
  12. {varcode-9.2.1 → varcode-9.2.2}/MANIFEST.in +0 -0
  13. {varcode-9.2.1 → varcode-9.2.2}/README.md +0 -0
  14. {varcode-9.2.1 → varcode-9.2.2}/pyproject.toml +0 -0
  15. {varcode-9.2.1 → varcode-9.2.2}/requirements.txt +0 -0
  16. {varcode-9.2.1 → varcode-9.2.2}/setup.cfg +0 -0
  17. {varcode-9.2.1 → varcode-9.2.2}/tests/test_annotator_divergence_scenarios.py +0 -0
  18. {varcode-9.2.1 → varcode-9.2.2}/tests/test_annotator_parity_adversarial.py +0 -0
  19. {varcode-9.2.1 → varcode-9.2.2}/tests/test_annotators.py +0 -0
  20. {varcode-9.2.1 → varcode-9.2.2}/tests/test_cli_effects.py +0 -0
  21. {varcode-9.2.1 → varcode-9.2.2}/tests/test_cli_genes.py +0 -0
  22. {varcode-9.2.1 → varcode-9.2.2}/tests/test_cli_without_pkg_resources.py +0 -0
  23. {varcode-9.2.1 → varcode-9.2.2}/tests/test_collection_filtering.py +0 -0
  24. {varcode-9.2.1 → varcode-9.2.2}/tests/test_collection_variants_attr_consistency.py +0 -0
  25. {varcode-9.2.1 → varcode-9.2.2}/tests/test_common.py +0 -0
  26. {varcode-9.2.1 → varcode-9.2.2}/tests/test_cosmic_mutations.py +0 -0
  27. {varcode-9.2.1 → varcode-9.2.2}/tests/test_cryptic_exons.py +0 -0
  28. {varcode-9.2.1 → varcode-9.2.2}/tests/test_csv_roundtrip.py +0 -0
  29. {varcode-9.2.1 → varcode-9.2.2}/tests/test_dbnsfp_validation.py +0 -0
  30. {varcode-9.2.1 → varcode-9.2.2}/tests/test_default_structural_helpers.py +0 -0
  31. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_annotation_errors.py +0 -0
  32. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_candidates.py +0 -0
  33. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_classes.py +0 -0
  34. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_collection.py +0 -0
  35. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_collection_serialization.py +0 -0
  36. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_collection_sort_order.py +0 -0
  37. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effect_hypotheses.py +0 -0
  38. {varcode-9.2.1 → varcode-9.2.2}/tests/test_effects_from_mutagenix_variants.py +0 -0
  39. {varcode-9.2.1 → varcode-9.2.2}/tests/test_exacto_fusions.py +0 -0
  40. {varcode-9.2.1 → varcode-9.2.2}/tests/test_exonic_splice_site.py +0 -0
  41. {varcode-9.2.1 → varcode-9.2.2}/tests/test_fast_path.py +0 -0
  42. {varcode-9.2.1 → varcode-9.2.2}/tests/test_frameshift_cterminus_regression.py +0 -0
  43. {varcode-9.2.1 → varcode-9.2.2}/tests/test_frameshift_helpers.py +0 -0
  44. {varcode-9.2.1 → varcode-9.2.2}/tests/test_genome_contig_validation.py +0 -0
  45. {varcode-9.2.1 → varcode-9.2.2}/tests/test_genome_sequence.py +0 -0
  46. {varcode-9.2.1 → varcode-9.2.2}/tests/test_genomic_layout.py +0 -0
  47. {varcode-9.2.1 → varcode-9.2.2}/tests/test_genotype.py +0 -0
  48. {varcode-9.2.1 → varcode-9.2.2}/tests/test_genotype_from_vcf.py +0 -0
  49. {varcode-9.2.1 → varcode-9.2.2}/tests/test_germline_context.py +0 -0
  50. {varcode-9.2.1 → varcode-9.2.2}/tests/test_germline_effects.py +0 -0
  51. {varcode-9.2.1 → varcode-9.2.2}/tests/test_haplotype_effect.py +0 -0
  52. {varcode-9.2.1 → varcode-9.2.2}/tests/test_maf.py +0 -0
  53. {varcode-9.2.1 → varcode-9.2.2}/tests/test_mitochondrial.py +0 -0
  54. {varcode-9.2.1 → varcode-9.2.2}/tests/test_mm10_klf6_frameshift.py +0 -0
  55. {varcode-9.2.1 → varcode-9.2.2}/tests/test_mouse.py +0 -0
  56. {varcode-9.2.1 → varcode-9.2.2}/tests/test_mutant_transcript.py +0 -0
  57. {varcode-9.2.1 → varcode-9.2.2}/tests/test_mutate.py +0 -0
  58. {varcode-9.2.1 → varcode-9.2.2}/tests/test_no_duplicate_variants.py +0 -0
  59. {varcode-9.2.1 → varcode-9.2.2}/tests/test_osteosarc_fusions.py +0 -0
  60. {varcode-9.2.1 → varcode-9.2.2}/tests/test_partial_annotators.py +0 -0
  61. {varcode-9.2.1 → varcode-9.2.2}/tests/test_premature_stop_short_description.py +0 -0
  62. {varcode-9.2.1 → varcode-9.2.2}/tests/test_problematic_variants.py +0 -0
  63. {varcode-9.2.1 → varcode-9.2.2}/tests/test_protein_diff_parity.py +0 -0
  64. {varcode-9.2.1 → varcode-9.2.2}/tests/test_read_phase_resolver.py +0 -0
  65. {varcode-9.2.1 → varcode-9.2.2}/tests/test_real_world_callers.py +0 -0
  66. {varcode-9.2.1 → varcode-9.2.2}/tests/test_reference.py +0 -0
  67. {varcode-9.2.1 → varcode-9.2.2}/tests/test_reference_mismatch_error.py +0 -0
  68. {varcode-9.2.1 → varcode-9.2.2}/tests/test_reference_segments.py +0 -0
  69. {varcode-9.2.1 → varcode-9.2.2}/tests/test_rna_evidence.py +0 -0
  70. {varcode-9.2.1 → varcode-9.2.2}/tests/test_rna_haplotype_sequences.py +0 -0
  71. {varcode-9.2.1 → varcode-9.2.2}/tests/test_rna_read_phasing_source.py +0 -0
  72. {varcode-9.2.1 → varcode-9.2.2}/tests/test_selenocysteine.py +0 -0
  73. {varcode-9.2.1 → varcode-9.2.2}/tests/test_silent_aa_pos.py +0 -0
  74. {varcode-9.2.1 → varcode-9.2.2}/tests/test_silent_hgvs_description.py +0 -0
  75. {varcode-9.2.1 → varcode-9.2.2}/tests/test_splice_graph.py +0 -0
  76. {varcode-9.2.1 → varcode-9.2.2}/tests/test_splice_outcomes.py +0 -0
  77. {varcode-9.2.1 → varcode-9.2.2}/tests/test_splice_site_effects.py +0 -0
  78. {varcode-9.2.1 → varcode-9.2.2}/tests/test_stop_codon_classification_bugs.py +0 -0
  79. {varcode-9.2.1 → varcode-9.2.2}/tests/test_string_helpers.py +0 -0
  80. {varcode-9.2.1 → varcode-9.2.2}/tests/test_structural_variant_annotator.py +0 -0
  81. {varcode-9.2.1 → varcode-9.2.2}/tests/test_structural_variants.py +0 -0
  82. {varcode-9.2.1 → varcode-9.2.2}/tests/test_symbolic_alleles.py +0 -0
  83. {varcode-9.2.1 → varcode-9.2.2}/tests/test_symbolic_sv_padding.py +0 -0
  84. {varcode-9.2.1 → varcode-9.2.2}/tests/test_timings.py +0 -0
  85. {varcode-9.2.1 → varcode-9.2.2}/tests/test_transcript_layout.py +0 -0
  86. {varcode-9.2.1 → varcode-9.2.2}/tests/test_transcript_model.py +0 -0
  87. {varcode-9.2.1 → varcode-9.2.2}/tests/test_transforms_left_align.py +0 -0
  88. {varcode-9.2.1 → varcode-9.2.2}/tests/test_transforms_pair_breakends.py +0 -0
  89. {varcode-9.2.1 → varcode-9.2.2}/tests/test_variant.py +0 -0
  90. {varcode-9.2.1 → varcode-9.2.2}/tests/test_variant_collection.py +0 -0
  91. {varcode-9.2.1 → varcode-9.2.2}/tests/test_vcf.py +0 -0
  92. {varcode-9.2.1 → varcode-9.2.2}/tests/test_vcf_output.py +0 -0
  93. {varcode-9.2.1 → varcode-9.2.2}/tests/test_vcf_parsing.py +0 -0
  94. {varcode-9.2.1 → varcode-9.2.2}/tests/test_vcf_phase_resolver.py +0 -0
  95. {varcode-9.2.1 → varcode-9.2.2}/tests/test_vcf_spec_examples.py +0 -0
  96. {varcode-9.2.1 → varcode-9.2.2}/varcode/__init__.py +0 -0
  97. {varcode-9.2.1 → varcode-9.2.2}/varcode/annotators/__init__.py +0 -0
  98. {varcode-9.2.1 → varcode-9.2.2}/varcode/annotators/fast.py +0 -0
  99. {varcode-9.2.1 → varcode-9.2.2}/varcode/annotators/protein_diff.py +0 -0
  100. {varcode-9.2.1 → varcode-9.2.2}/varcode/annotators/registry.py +0 -0
  101. {varcode-9.2.1 → varcode-9.2.2}/varcode/cli/__init__.py +0 -0
  102. {varcode-9.2.1 → varcode-9.2.2}/varcode/cli/effects_script.py +0 -0
  103. {varcode-9.2.1 → varcode-9.2.2}/varcode/cli/genes_script.py +0 -0
  104. {varcode-9.2.1 → varcode-9.2.2}/varcode/cli/logging.conf +0 -0
  105. {varcode-9.2.1 → varcode-9.2.2}/varcode/cli/variant_args.py +0 -0
  106. {varcode-9.2.1 → varcode-9.2.2}/varcode/cli/version_info.py +0 -0
  107. {varcode-9.2.1 → varcode-9.2.2}/varcode/common.py +0 -0
  108. {varcode-9.2.1 → varcode-9.2.2}/varcode/cryptic_exons.py +0 -0
  109. {varcode-9.2.1 → varcode-9.2.2}/varcode/effect_candidates.py +0 -0
  110. {varcode-9.2.1 → varcode-9.2.2}/varcode/effect_hypotheses.py +0 -0
  111. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/__init__.py +0 -0
  112. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/classify.py +0 -0
  113. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/codon_tables.py +0 -0
  114. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/common.py +0 -0
  115. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_classes.py +0 -0
  116. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_helpers.py +0 -0
  117. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_ordering.py +0 -0
  118. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_prediction.py +0 -0
  119. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_prediction_coding.py +0 -0
  120. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_prediction_coding_frameshift.py +0 -0
  121. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/effect_prediction_coding_in_frame.py +0 -0
  122. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/fast_path.py +0 -0
  123. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/mutate.py +0 -0
  124. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/structural.py +0 -0
  125. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/transcript_helpers.py +0 -0
  126. {varcode-9.2.1 → varcode-9.2.2}/varcode/effects/translate.py +0 -0
  127. {varcode-9.2.1 → varcode-9.2.2}/varcode/errors.py +0 -0
  128. {varcode-9.2.1 → varcode-9.2.2}/varcode/exacto.py +0 -0
  129. {varcode-9.2.1 → varcode-9.2.2}/varcode/genome.py +0 -0
  130. {varcode-9.2.1 → varcode-9.2.2}/varcode/genome_sequence.py +0 -0
  131. {varcode-9.2.1 → varcode-9.2.2}/varcode/genomic_layout.py +0 -0
  132. {varcode-9.2.1 → varcode-9.2.2}/varcode/genotype.py +0 -0
  133. {varcode-9.2.1 → varcode-9.2.2}/varcode/germline.py +0 -0
  134. {varcode-9.2.1 → varcode-9.2.2}/varcode/maf.py +0 -0
  135. {varcode-9.2.1 → varcode-9.2.2}/varcode/mutant_transcript.py +0 -0
  136. {varcode-9.2.1 → varcode-9.2.2}/varcode/nucleotides.py +0 -0
  137. {varcode-9.2.1 → varcode-9.2.2}/varcode/phasing.py +0 -0
  138. {varcode-9.2.1 → varcode-9.2.2}/varcode/realized_effects.py +0 -0
  139. {varcode-9.2.1 → varcode-9.2.2}/varcode/reference.py +0 -0
  140. {varcode-9.2.1 → varcode-9.2.2}/varcode/rna_evidence.py +0 -0
  141. {varcode-9.2.1 → varcode-9.2.2}/varcode/rna_read_phasing.py +0 -0
  142. {varcode-9.2.1 → varcode-9.2.2}/varcode/splice_graph.py +0 -0
  143. {varcode-9.2.1 → varcode-9.2.2}/varcode/splice_outcomes.py +0 -0
  144. {varcode-9.2.1 → varcode-9.2.2}/varcode/string_helpers.py +0 -0
  145. {varcode-9.2.1 → varcode-9.2.2}/varcode/sv_allele_parser.py +0 -0
  146. {varcode-9.2.1 → varcode-9.2.2}/varcode/transcript_layout.py +0 -0
  147. {varcode-9.2.1 → varcode-9.2.2}/varcode/transcript_model.py +0 -0
  148. {varcode-9.2.1 → varcode-9.2.2}/varcode/transforms/__init__.py +0 -0
  149. {varcode-9.2.1 → varcode-9.2.2}/varcode/ucsc_reference_names.py +0 -0
  150. {varcode-9.2.1 → varcode-9.2.2}/varcode/util.py +0 -0
  151. {varcode-9.2.1 → varcode-9.2.2}/varcode/variant.py +0 -0
  152. {varcode-9.2.1 → varcode-9.2.2}/varcode/vcf.py +0 -0
  153. {varcode-9.2.1 → varcode-9.2.2}/varcode/vcf_output.py +0 -0
  154. {varcode-9.2.1 → varcode-9.2.2}/varcode/vcf_parsing.py +0 -0
  155. {varcode-9.2.1 → varcode-9.2.2}/varcode.egg-info/dependency_links.txt +0 -0
  156. {varcode-9.2.1 → varcode-9.2.2}/varcode.egg-info/entry_points.txt +0 -0
  157. {varcode-9.2.1 → varcode-9.2.2}/varcode.egg-info/requires.txt +0 -0
  158. {varcode-9.2.1 → varcode-9.2.2}/varcode.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: varcode
3
- Version: 9.2.1
3
+ Version: 9.2.2
4
4
  Summary: Variant annotation in Python
5
5
  Author-email: Alex Rubinsteyn <alex.rubinsteyn@unc.edu>
6
6
  Project-URL: Homepage, https://github.com/openvax/varcode
@@ -0,0 +1,69 @@
1
+ """Structural records must never masquerade as placeholder SNVs (#417)."""
2
+
3
+ import pickle
4
+
5
+ import pytest
6
+
7
+ from varcode import StructuralVariant, Variant, VariantCollection
8
+ from varcode.csv_helpers import STRUCTURAL_VARIANT_COLUMNS
9
+ from varcode.effects import EffectCollection, Intergenic
10
+
11
+
12
+ @pytest.mark.parametrize("ref", ["A", "C", "G", "T", "N"])
13
+ @pytest.mark.parametrize("kind,alt", [
14
+ ("DEL", "<DEL>"), ("DUP", "<DUP>"), ("INV", "<INV>"),
15
+ ("CNV", "<CN3>"), ("INS", "<INS:ME:ALU>"),
16
+ ("BND", "G[2:200["), ("BND", ".ACGT"), ("INS", "ACGT"),
17
+ ])
18
+ def test_actual_alleles_and_no_small_edit_flags(ref, kind, alt):
19
+ sv = StructuralVariant("1", 100, kind, end=200, ref=ref, alt=alt)
20
+ assert sv.start == sv.original_start == 100
21
+ assert sv.ref == sv.original_ref == ref
22
+ assert sv.alt == sv.original_alt == sv.symbolic_alt == alt
23
+ for name in ["is_snv", "is_indel", "is_insertion", "is_deletion",
24
+ "is_transition", "is_transversion"]:
25
+ assert getattr(sv, name) is False
26
+ assert sv.is_structural
27
+ for restored in [StructuralVariant.from_json(sv.to_json()), pickle.loads(pickle.dumps(sv))]:
28
+ assert restored == sv
29
+ assert (restored.ref, restored.alt) == (ref, alt)
30
+
31
+
32
+ def examples():
33
+ snv = Variant("1", 50, "C", "T")
34
+ sv = StructuralVariant("1", 100, "DEL", end=200, ref="T", affected_start=101,
35
+ mate_contig="2", mate_start=300)
36
+ for variant in [snv, sv]:
37
+ variant._gene_names = []
38
+ variant._gene_ids = []
39
+ return snv, sv
40
+
41
+
42
+ @pytest.mark.parametrize("effects", [False, True])
43
+ def test_mixed_tables_retain_structural_coordinates(effects, tmp_path):
44
+ snv, sv = examples()
45
+ collection = (EffectCollection([Intergenic(snv), Intergenic(sv)], sort_key=False)
46
+ if effects else VariantCollection([snv, sv]))
47
+ df = collection.to_dataframe()
48
+ assert list(df.columns) == list(collection._DATAFRAME_COLUMNS + STRUCTURAL_VARIANT_COLUMNS)
49
+ row = df.iloc[1]
50
+ assert (row["ref"], row["alt"], row["sv_type"]) == ("T", "<DEL>", "DEL")
51
+ assert (row["start"], row["end"], row["affected_start"], row["affected_end"]) == (100, 200, 101, 200)
52
+ assert (row["mate_contig"], row["mate_start"]) == ("2", 300)
53
+ if effects:
54
+ assert not row["is_snv"] and not row["is_transition"] and not row["is_transversion"]
55
+ assert df.iloc[0]["is_snv"] and df.iloc[0]["is_transition"]
56
+ path = tmp_path / "mixed.csv"
57
+ collection.to_csv(str(path))
58
+ with pytest.raises(ValueError, match="Structural rows cannot be reconstructed"):
59
+ type(collection).from_csv(str(path))
60
+
61
+
62
+ @pytest.mark.parametrize("effects", [False, True])
63
+ @pytest.mark.parametrize("empty", [False, True])
64
+ def test_point_only_and_empty_schemas_unchanged(effects, empty):
65
+ snv, _ = examples()
66
+ variants = [] if empty else [snv]
67
+ collection = (EffectCollection([Intergenic(v) for v in variants]) if effects
68
+ else VariantCollection(variants))
69
+ assert tuple(collection.to_dataframe().columns) == collection._DATAFRAME_COLUMNS
@@ -39,6 +39,18 @@ HEADER_PREFIX = "#"
39
39
  # openvax/varcode#274.
40
40
  CONTIG_COLUMN_ALIASES = ("contig", "chr")
41
41
 
42
+ STRUCTURAL_VARIANT_COLUMNS = (
43
+ "sv_type", "end", "mate_contig", "mate_start", "affected_start", "affected_end",
44
+ )
45
+
46
+
47
+ def reject_structural_csv(df):
48
+ """CSV summaries do not encode enough state to restore structural effects."""
49
+ if "sv_type" in df and df["sv_type"].fillna("").ne("").any():
50
+ raise ValueError(
51
+ "Structural rows cannot be reconstructed from CSV summaries. "
52
+ "Retain the original VCF/JSON variants and RNA evidence instead.")
53
+
42
54
 
43
55
  def resolve_contig_column(columns):
44
56
  """Return the first of :data:`CONTIG_COLUMN_ALIASES` that appears in
@@ -17,7 +17,9 @@ from sercol import Collection
17
17
 
18
18
  from ..csv_helpers import (
19
19
  CONTIG_COLUMN_ALIASES,
20
+ STRUCTURAL_VARIANT_COLUMNS,
20
21
  read_metadata_header,
22
+ reject_structural_csv,
21
23
  resolve_contig_column,
22
24
  warn_on_version_drift,
23
25
  write_metadata_header,
@@ -344,6 +346,8 @@ class EffectCollection(Collection):
344
346
 
345
347
  def to_dataframe(self):
346
348
  """Build a dataframe from the effect collection."""
349
+ structural_columns = STRUCTURAL_VARIANT_COLUMNS if any(
350
+ getattr(e.variant, "is_structural", False) for e in self) else ()
347
351
  # list of properties to extract from Variant objects if they're
348
352
  # not None
349
353
  variant_properties = [
@@ -354,7 +358,7 @@ class EffectCollection(Collection):
354
358
  "is_snv",
355
359
  "is_transversion",
356
360
  "is_transition"
357
- ]
361
+ ] + list(structural_columns)
358
362
 
359
363
  def row_from_effect(effect):
360
364
  row = OrderedDict()
@@ -375,7 +379,7 @@ class EffectCollection(Collection):
375
379
  # CSV round-trip doesn't have to special-case len == 0.
376
380
  return pd.DataFrame.from_records(
377
381
  [row_from_effect(effect) for effect in self],
378
- columns=self._DATAFRAME_COLUMNS,
382
+ columns=self._DATAFRAME_COLUMNS + structural_columns,
379
383
  )
380
384
 
381
385
  def to_csv(self, path, include_header=True):
@@ -485,6 +489,7 @@ class EffectCollection(Collection):
485
489
  # dtype is a no-op for whichever column is absent.
486
490
  df = pd.read_csv(
487
491
  path, comment="#", dtype={"chr": str, "contig": str})
492
+ reject_structural_csv(df)
488
493
  contig_col = resolve_contig_column(df.columns)
489
494
  if contig_col is None:
490
495
  raise ValueError(
@@ -242,6 +242,15 @@ class StructuralVariant(Variant):
242
242
  event coordinates include a retained padding base.
243
243
  """
244
244
 
245
+ # These flags describe literal small edits, not event classes. Consumers
246
+ # must use is_structural/sv_type instead of interpreting symbolic alleles.
247
+ is_snv = False
248
+ is_indel = False
249
+ is_insertion = False
250
+ is_deletion = False
251
+ is_transition = False
252
+ is_transversion = False
253
+
245
254
  __slots__ = (
246
255
  "sv_type",
247
256
  "mate_contig",
@@ -288,14 +297,14 @@ class StructuralVariant(Variant):
288
297
 
289
298
  # Initialize the base Variant with a placeholder ref/alt so the
290
299
  # nucleotide-normalization path doesn't reject <DEL>-style
291
- # symbolic alleles. The original symbolic ALT is preserved in
292
- # ``_sv_alt``; :attr:`alt` returns it via a property override.
300
+ # symbolic alleles. Restore the actual record fields below; placeholder
301
+ # normalization must not leak into public alleles or exports (#417).
293
302
  Variant.__init__(
294
303
  self,
295
304
  contig=contig,
296
305
  start=start,
297
306
  ref=ref if ref else "N",
298
- alt="A" if ref == "N" else "A", # placeholder, overridden below
307
+ alt="A",
299
308
  genome=genome,
300
309
  ensembl=ensembl,
301
310
  allow_extended_nucleotides=True,
@@ -325,6 +334,9 @@ class StructuralVariant(Variant):
325
334
  # Preserve the original symbolic ALT string so round-tripping
326
335
  # and downstream consumers see what the VCF said.
327
336
  self._sv_alt = alt if alt is not None else "<%s>" % sv_type
337
+ self.ref = self.original_ref
338
+ self.alt = self.original_alt = self._sv_alt
339
+ self.start = self.original_start = int(start)
328
340
 
329
341
  # Caches, like Variant's overlapping-gene / transcript caches:
330
342
  # the junction list derived from the fields above, and results
@@ -19,7 +19,9 @@ from .effects import EffectCollection
19
19
  from .common import memoize
20
20
  from .csv_helpers import (
21
21
  CONTIG_COLUMN_ALIASES,
22
+ STRUCTURAL_VARIANT_COLUMNS,
22
23
  read_metadata_header,
24
+ reject_structural_csv,
23
25
  resolve_contig_column,
24
26
  warn_on_version_drift,
25
27
  write_metadata_header,
@@ -434,8 +436,10 @@ class VariantCollection(Collection):
434
436
 
435
437
  def to_dataframe(self):
436
438
  """Build a DataFrame from this variant collection."""
439
+ structural_columns = STRUCTURAL_VARIANT_COLUMNS if any(
440
+ getattr(v, "is_structural", False) for v in self) else ()
437
441
  def row_from_variant(variant):
438
- return OrderedDict([
442
+ row = OrderedDict([
439
443
  ("chr", variant.contig),
440
444
  ("start", variant.original_start),
441
445
  ("ref", variant.original_ref),
@@ -443,11 +447,13 @@ class VariantCollection(Collection):
443
447
  ("gene_name", ";".join(variant.gene_names)),
444
448
  ("gene_id", ";".join(variant.gene_ids))
445
449
  ])
450
+ row.update((name, getattr(variant, name, None)) for name in structural_columns)
451
+ return row
446
452
  rows = [row_from_variant(v) for v in self]
447
453
  # Always return a DataFrame with the expected columns, even
448
454
  # when empty, so downstream code (CSV round-trip, joins) doesn't
449
455
  # have to special-case len == 0.
450
- return pd.DataFrame.from_records(rows, columns=self._DATAFRAME_COLUMNS)
456
+ return pd.DataFrame.from_records(rows, columns=self._DATAFRAME_COLUMNS + structural_columns)
451
457
 
452
458
  def to_csv(self, path, include_header=True):
453
459
  """Write this collection to CSV.
@@ -541,6 +547,7 @@ class VariantCollection(Collection):
541
547
  # dtype is a no-op for whichever column is absent.
542
548
  df = pd.read_csv(
543
549
  path, comment="#", dtype={"chr": str, "contig": str})
550
+ reject_structural_csv(df)
544
551
  contig_col = resolve_contig_column(df.columns)
545
552
  if contig_col is None:
546
553
  raise ValueError(
@@ -0,0 +1 @@
1
+ __version__ = "9.2.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: varcode
3
- Version: 9.2.1
3
+ Version: 9.2.2
4
4
  Summary: Variant annotation in Python
5
5
  Author-email: Alex Rubinsteyn <alex.rubinsteyn@unc.edu>
6
6
  Project-URL: Homepage, https://github.com/openvax/varcode
@@ -68,6 +68,7 @@ tests/test_stop_codon_classification_bugs.py
68
68
  tests/test_string_helpers.py
69
69
  tests/test_structural_variant_annotator.py
70
70
  tests/test_structural_variants.py
71
+ tests/test_sv_tabular_metadata.py
71
72
  tests/test_symbolic_alleles.py
72
73
  tests/test_symbolic_sv_padding.py
73
74
  tests/test_timings.py
@@ -1 +0,0 @@
1
- __version__ = "9.2.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes