RNApolis 0.10.3__py3-none-any.whl → 0.10.5__py3-none-any.whl

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.
rnapolis/adapter.py CHANGED
@@ -317,10 +317,14 @@ def parse_dssr_output(
317
317
  if nt1 is not None and nt2 is not None:
318
318
  stackings.append(Stacking(nt1, nt2, None))
319
319
 
320
- return BaseInteractions(base_pairs, stackings, [], [], [])
320
+ return BaseInteractions.from_structure3d(
321
+ structure3d, base_pairs, stackings, [], [], []
322
+ )
321
323
 
322
324
 
323
- def parse_maxit_output(file_paths: List[str]) -> BaseInteractions:
325
+ def parse_maxit_output(
326
+ file_paths: List[str], structure3d: Structure3D
327
+ ) -> BaseInteractions:
324
328
  """
325
329
  Parse MAXIT output files and convert to BaseInteractions.
326
330
 
@@ -448,10 +452,14 @@ def parse_maxit_output(file_paths: List[str]) -> BaseInteractions:
448
452
  except Exception as e:
449
453
  logging.warning(f"Error processing MAXIT file {cif_file}: {e}", exc_info=True)
450
454
 
451
- return BaseInteractions(all_base_pairs, [], [], [], all_other_interactions)
455
+ return BaseInteractions.from_structure3d(
456
+ structure3d, all_base_pairs, [], [], [], all_other_interactions
457
+ )
452
458
 
453
459
 
454
- def parse_bpnet_output(file_paths: List[str]) -> BaseInteractions:
460
+ def parse_bpnet_output(
461
+ file_paths: List[str], structure3d: Structure3D
462
+ ) -> BaseInteractions:
455
463
  """
456
464
  Parse BPNet output files and convert to BaseInteractions.
457
465
 
@@ -649,7 +657,8 @@ def parse_bpnet_output(file_paths: List[str]) -> BaseInteractions:
649
657
  f"Error processing BPNet rob file {rob_file}: {e}", exc_info=True
650
658
  )
651
659
 
652
- return BaseInteractions(
660
+ return BaseInteractions.from_structure3d(
661
+ structure3d,
653
662
  base_pairs,
654
663
  stackings,
655
664
  base_ribose_interactions,
@@ -986,7 +995,8 @@ def parse_rnaview_output(
986
995
  except Exception as e:
987
996
  logging.warning(f"Error processing RNAView file {out_file}: {e}", exc_info=True)
988
997
 
989
- return BaseInteractions(
998
+ return BaseInteractions.from_structure3d(
999
+ structure3d,
990
1000
  base_pairs,
991
1001
  stackings,
992
1002
  base_ribose_interactions,
@@ -1077,7 +1087,7 @@ def parse_barnaba_output(
1077
1087
  f"Residue name mismatch for {residue_info}: expected {name}, found {residue.auth.name}"
1078
1088
  )
1079
1089
 
1080
- return residue
1090
+ return Residue(residue.label, residue.auth)
1081
1091
 
1082
1092
  base_pairs: List[BasePair] = []
1083
1093
  stackings: List[Stacking] = []
@@ -1142,7 +1152,9 @@ def parse_barnaba_output(
1142
1152
  f"Unknown barnaba stacking topology: {interaction_str}"
1143
1153
  )
1144
1154
 
1145
- return BaseInteractions(base_pairs, stackings, [], [], other_interactions)
1155
+ return BaseInteractions.from_structure3d(
1156
+ structure3d, base_pairs, stackings, [], [], other_interactions
1157
+ )
1146
1158
 
1147
1159
 
1148
1160
  def parse_external_output(
@@ -1160,13 +1172,13 @@ def parse_external_output(
1160
1172
  BaseInteractions object containing the interactions found by the external tool
1161
1173
  """
1162
1174
  if tool == ExternalTool.FR3D:
1163
- return parse_fr3d_output(file_paths)
1175
+ return parse_fr3d_output(file_paths, structure3d)
1164
1176
  elif tool == ExternalTool.DSSR:
1165
1177
  return parse_dssr_output(file_paths, structure3d)
1166
1178
  elif tool == ExternalTool.MAXIT:
1167
- return parse_maxit_output(file_paths)
1179
+ return parse_maxit_output(file_paths, structure3d)
1168
1180
  elif tool == ExternalTool.BPNET:
1169
- return parse_bpnet_output(file_paths)
1181
+ return parse_bpnet_output(file_paths, structure3d)
1170
1182
  elif tool == ExternalTool.RNAVIEW:
1171
1183
  return parse_rnaview_output(file_paths, structure3d)
1172
1184
  elif tool == ExternalTool.BARNABA:
@@ -1175,7 +1187,9 @@ def parse_external_output(
1175
1187
  raise ValueError(f"Unsupported external tool: {tool}")
1176
1188
 
1177
1189
 
1178
- def parse_fr3d_output(file_paths: List[str]) -> BaseInteractions:
1190
+ def parse_fr3d_output(
1191
+ file_paths: List[str], structure3d: Structure3D
1192
+ ) -> BaseInteractions:
1179
1193
  """
1180
1194
  Parse FR3D output files and convert to BaseInteractions.
1181
1195
 
@@ -1208,7 +1222,8 @@ def parse_fr3d_output(file_paths: List[str]) -> BaseInteractions:
1208
1222
  _process_interaction_line(line, interactions_data)
1209
1223
 
1210
1224
  # Return a BaseInteractions object with all the processed interactions
1211
- return BaseInteractions(
1225
+ return BaseInteractions.from_structure3d(
1226
+ structure3d,
1212
1227
  interactions_data["base_pairs"],
1213
1228
  interactions_data["stackings"],
1214
1229
  interactions_data["base_ribose_interactions"],
rnapolis/annotator.py CHANGED
@@ -85,15 +85,6 @@ def detect_cis_trans(residue_i: Residue3D, residue_j: Residue3D) -> Optional[str
85
85
  return "c" if -90.0 < torsion < 90.0 else "t"
86
86
 
87
87
 
88
- def detect_saenger(
89
- residue_i: Residue3D, residue_j: Residue3D, lw: LeontisWesthof
90
- ) -> Optional[Saenger]:
91
- key = (f"{residue_i.one_letter_name}{residue_j.one_letter_name}", lw.value)
92
- if key in Saenger.table():
93
- return Saenger[Saenger.table()[key]]
94
- return None
95
-
96
-
97
88
  def detect_bph_br_classification(
98
89
  donor_residue: Residue3D, donor: Atom, acceptor: Atom
99
90
  ) -> Optional[int]:
@@ -367,7 +358,9 @@ def find_pairs(
367
358
  Residue(residue_i.label, residue_i.auth),
368
359
  Residue(residue_j.label, residue_j.auth),
369
360
  lw,
370
- detect_saenger(residue_i, residue_j, lw),
361
+ Saenger.from_leontis_westhof(
362
+ residue_i.one_letter_name, residue_j.one_letter_name, lw
363
+ ),
371
364
  )
372
365
  )
373
366
 
@@ -483,7 +476,9 @@ def extract_base_interactions(
483
476
  ) -> BaseInteractions:
484
477
  base_pairs, base_phosphate, base_ribose = find_pairs(tertiary_structure, model)
485
478
  stackings = find_stackings(tertiary_structure, model)
486
- return BaseInteractions(base_pairs, stackings, base_ribose, base_phosphate, [])
479
+ return BaseInteractions.from_structure3d(
480
+ tertiary_structure, base_pairs, stackings, base_ribose, base_phosphate, []
481
+ )
487
482
 
488
483
 
489
484
  def generate_pymol_script(mapping: Mapping2D3D, stems: List[Stem]) -> str:
@@ -688,91 +683,6 @@ def add_common_output_arguments(parser: argparse.ArgumentParser):
688
683
  )
689
684
 
690
685
 
691
- def unify_structure_data(structure2d: Structure2D, mapping: Mapping2D3D) -> Structure2D:
692
- """
693
- Unify structure data by:
694
- 1. Adding missing Saenger classifications to base pairs
695
- 2. Filling in empty residue labels from Structure3D
696
- """
697
- # Create a mapping from residue to residue3d for label filling
698
- residue_to_residue3d = {}
699
- for residue3d in mapping.structure3d.residues:
700
- residue_key = Residue(residue3d.label, residue3d.auth)
701
- residue_to_residue3d[residue_key] = residue3d
702
-
703
- def fill_residue_label(residue: Residue) -> Residue:
704
- """Fill empty label from Structure3D if available."""
705
- if residue.label is not None:
706
- return residue
707
-
708
- # Try to find matching residue3d by auth
709
- for residue3d in mapping.structure3d.residues:
710
- if residue.auth == residue3d.auth:
711
- return Residue(residue3d.label, residue.auth)
712
-
713
- return residue
714
-
715
- # Process base pairs
716
- unified_base_pairs = []
717
- for base_pair in structure2d.base_pairs:
718
- # Fill in missing labels
719
- nt1 = fill_residue_label(base_pair.nt1)
720
- nt2 = fill_residue_label(base_pair.nt2)
721
-
722
- # Detect missing Saenger classification
723
- saenger = base_pair.saenger
724
- if saenger is None:
725
- # Find corresponding 3D residues for Saenger detection
726
- residue3d_1 = residue_to_residue3d.get(Residue(nt1.label, nt1.auth))
727
- residue3d_2 = residue_to_residue3d.get(Residue(nt2.label, nt2.auth))
728
-
729
- if residue3d_1 is not None and residue3d_2 is not None:
730
- saenger = detect_saenger(residue3d_1, residue3d_2, base_pair.lw)
731
-
732
- unified_base_pairs.append(BasePair(nt1, nt2, base_pair.lw, saenger))
733
-
734
- # Process other interaction types (fill labels only)
735
- unified_stackings = []
736
- for stacking in structure2d.stackings:
737
- nt1 = fill_residue_label(stacking.nt1)
738
- nt2 = fill_residue_label(stacking.nt2)
739
- unified_stackings.append(Stacking(nt1, nt2, stacking.topology))
740
-
741
- unified_base_ribose = []
742
- for base_ribose in structure2d.base_ribose_interactions:
743
- nt1 = fill_residue_label(base_ribose.nt1)
744
- nt2 = fill_residue_label(base_ribose.nt2)
745
- unified_base_ribose.append(BaseRibose(nt1, nt2, base_ribose.br))
746
-
747
- unified_base_phosphate = []
748
- for base_phosphate in structure2d.base_phosphate_interactions:
749
- nt1 = fill_residue_label(base_phosphate.nt1)
750
- nt2 = fill_residue_label(base_phosphate.nt2)
751
- unified_base_phosphate.append(BasePhosphate(nt1, nt2, base_phosphate.bph))
752
-
753
- unified_other = []
754
- for other in structure2d.other_interactions:
755
- nt1 = fill_residue_label(other.nt1)
756
- nt2 = fill_residue_label(other.nt2)
757
- unified_other.append(OtherInteraction(nt1, nt2))
758
-
759
- # Create new Structure2D with unified data
760
- unified_base_interactions = BaseInteractions(
761
- unified_base_pairs,
762
- unified_stackings,
763
- unified_base_ribose,
764
- unified_base_phosphate,
765
- unified_other,
766
- )
767
-
768
- # Recreate Structure2D with unified interactions
769
- unified_structure2d, _ = mapping.structure3d.extract_secondary_structure(
770
- unified_base_interactions, False
771
- )
772
-
773
- return unified_structure2d
774
-
775
-
776
686
  def handle_output_arguments(
777
687
  args: argparse.Namespace,
778
688
  structure2d: Structure2D,
@@ -780,34 +690,31 @@ def handle_output_arguments(
780
690
  input_filename: str,
781
691
  ):
782
692
  """Handles writing output based on provided arguments."""
783
- # Unify the structure data before processing outputs
784
- unified_structure2d = unify_structure_data(structure2d, mapping)
785
-
786
693
  input_basename = os.path.basename(input_filename)
787
694
  if args.csv:
788
- write_csv(args.csv, unified_structure2d)
695
+ write_csv(args.csv, structure2d)
789
696
 
790
697
  if args.json:
791
- write_json(args.json, unified_structure2d)
698
+ write_json(args.json, structure2d)
792
699
 
793
700
  if args.bpseq:
794
- write_bpseq(args.bpseq, unified_structure2d.bpseq)
701
+ write_bpseq(args.bpseq, structure2d.bpseq)
795
702
 
796
703
  if args.extended:
797
- print(unified_structure2d.extended_dot_bracket)
704
+ print(structure2d.extended_dot_bracket)
798
705
  else:
799
- print(unified_structure2d.dot_bracket)
706
+ print(structure2d.dot_bracket)
800
707
 
801
708
  if args.dot:
802
- print(BpSeq.from_string(unified_structure2d.bpseq).graphviz)
709
+ print(BpSeq.from_string(structure2d.bpseq).graphviz)
803
710
 
804
711
  if args.pml:
805
- pml_script = generate_pymol_script(mapping, unified_structure2d.stems)
712
+ pml_script = generate_pymol_script(mapping, structure2d.stems)
806
713
  with open(args.pml, "w") as f:
807
714
  f.write(pml_script)
808
715
 
809
716
  if args.inter_stem_csv:
810
- if unified_structure2d.inter_stem_parameters:
717
+ if structure2d.inter_stem_parameters:
811
718
  # Convert list of dataclasses to list of dicts
812
719
  params_list = [
813
720
  {
@@ -820,7 +727,7 @@ def handle_output_arguments(
820
727
  "min_endpoint_distance_pdf": p.min_endpoint_distance_pdf,
821
728
  "coaxial_probability": p.coaxial_probability,
822
729
  }
823
- for p in unified_structure2d.interStemParameters
730
+ for p in structure2d.interStemParameters
824
731
  ]
825
732
  df = pd.DataFrame(params_list)
826
733
  df["input_basename"] = input_basename
@@ -838,9 +745,9 @@ def handle_output_arguments(
838
745
  # pd.DataFrame(columns=['input_basename', 'stem1_idx', ...]).to_csv(args.inter_stem_csv, index=False)
839
746
 
840
747
  if args.stems_csv:
841
- if unified_structure2d.stems:
748
+ if structure2d.stems:
842
749
  stems_data = []
843
- for i, stem in enumerate(unified_structure2d.stems):
750
+ for i, stem in enumerate(structure2d.stems):
844
751
  try:
845
752
  res5p_first = mapping.bpseq_index_to_residue_map.get(
846
753
  stem.strand5p.first
rnapolis/common.py CHANGED
@@ -5,7 +5,7 @@ import re
5
5
  import string
6
6
  from collections import defaultdict
7
7
  from collections.abc import Sequence
8
- from dataclasses import dataclass
8
+ from dataclasses import InitVar, dataclass
9
9
  from enum import Enum
10
10
  from functools import cache, cached_property, total_ordering
11
11
  from typing import Dict, List, Optional, Tuple
@@ -152,6 +152,18 @@ class Saenger(Enum):
152
152
  ("TG", "cWW"): "XXVIII",
153
153
  }
154
154
 
155
+ @classmethod
156
+ def from_leontis_westhof(
157
+ cls,
158
+ residue_i_one_letter_name: str,
159
+ residue_j_one_letter_name: str,
160
+ lw: LeontisWesthof,
161
+ ) -> Optional["Saenger"]:
162
+ key = (f"{residue_i_one_letter_name}{residue_j_one_letter_name}", lw.value)
163
+ if key in Saenger.table():
164
+ return Saenger[Saenger.table()[key]]
165
+ return None
166
+
155
167
  @property
156
168
  def is_canonical(self) -> bool:
157
169
  return self == Saenger.XIX or self == Saenger.XX or self == Saenger.XXVIII
@@ -1062,6 +1074,91 @@ class BaseInteractions:
1062
1074
  base_phosphate_interactions: List[BasePhosphate]
1063
1075
  other_interactions: List[OtherInteraction]
1064
1076
 
1077
+ @classmethod
1078
+ def from_structure3d(
1079
+ cls,
1080
+ structure3d: "Structure3D",
1081
+ base_pairs: List[BasePair],
1082
+ stackings: List[Stacking],
1083
+ base_ribose_interactions: List[BaseRibose],
1084
+ base_phosphate_interactions: List[BasePhosphate],
1085
+ other_interactions: List[OtherInteraction],
1086
+ ) -> "BaseInteractions":
1087
+ auth2residue3d = {}
1088
+ auth2label = {}
1089
+ label2auth = {}
1090
+
1091
+ for residue3d in structure3d.residues:
1092
+ auth2residue3d[residue3d.auth] = residue3d
1093
+ auth2label[residue3d.auth] = residue3d.label
1094
+ label2auth[residue3d.label] = residue3d.auth
1095
+
1096
+ def unify_nt(nt: Residue) -> Residue:
1097
+ if nt.auth is not None and nt.label is not None:
1098
+ return nt
1099
+ if nt.auth is not None:
1100
+ return Residue(label=auth2label.get(nt.auth, None), auth=nt.auth)
1101
+ if nt.label is not None:
1102
+ return Residue(label=nt.label, auth=label2auth.get(nt.label, None))
1103
+ return nt
1104
+
1105
+ base_pairs_new = []
1106
+ for base_pair in base_pairs:
1107
+ nt1 = unify_nt(base_pair.nt1)
1108
+ nt2 = unify_nt(base_pair.nt2)
1109
+ saenger = base_pair.saenger or Saenger.from_leontis_westhof(
1110
+ auth2residue3d[nt1.auth].one_letter_name,
1111
+ auth2residue3d[nt2.auth].one_letter_name,
1112
+ base_pair.lw,
1113
+ )
1114
+ if (
1115
+ nt1 != base_pair.nt1
1116
+ or nt2 != base_pair.nt2
1117
+ or saenger != base_pair.saenger
1118
+ ):
1119
+ base_pair = BasePair(nt1=nt1, nt2=nt2, lw=base_pair.lw, saenger=saenger)
1120
+ base_pairs_new.append(base_pair)
1121
+
1122
+ stackings_new = []
1123
+ for stacking in stackings:
1124
+ nt1 = unify_nt(stacking.nt1)
1125
+ nt2 = unify_nt(stacking.nt2)
1126
+ if nt1 != stacking.nt1 or nt2 != stacking.nt2:
1127
+ stacking = Stacking(nt1=nt1, nt2=nt2, topology=stacking.topology)
1128
+ stackings_new.append(stacking)
1129
+
1130
+ base_ribose_interactions_new = []
1131
+ for base_ribose in base_ribose_interactions:
1132
+ nt1 = unify_nt(base_ribose.nt1)
1133
+ nt2 = unify_nt(base_ribose.nt2)
1134
+ if nt1 != base_ribose.nt1 or nt2 != base_ribose.nt2:
1135
+ base_ribose = BaseRibose(nt1=nt1, nt2=nt2, br=base_ribose.br)
1136
+ base_ribose_interactions_new.append(base_ribose)
1137
+
1138
+ base_phosphate_interactions_new = []
1139
+ for base_phosphate in base_phosphate_interactions:
1140
+ nt1 = unify_nt(base_phosphate.nt1)
1141
+ nt2 = unify_nt(base_phosphate.nt2)
1142
+ if nt1 != base_phosphate.nt1 or nt2 != base_phosphate.nt2:
1143
+ base_phosphate = BasePhosphate(nt1=nt1, nt2=nt2, bph=base_phosphate.bph)
1144
+ base_phosphate_interactions_new.append(base_phosphate)
1145
+
1146
+ other_interactions_new = []
1147
+ for other_interaction in other_interactions:
1148
+ nt1 = unify_nt(other_interaction.nt1)
1149
+ nt2 = unify_nt(other_interaction.nt2)
1150
+ if nt1 != other_interaction.nt1 or nt2 != other_interaction.nt2:
1151
+ other_interaction = OtherInteraction(nt1=nt1, nt2=nt2)
1152
+ other_interactions_new.append(other_interaction)
1153
+
1154
+ return cls(
1155
+ base_pairs=base_pairs_new,
1156
+ stackings=stackings_new,
1157
+ base_ribose_interactions=base_ribose_interactions_new,
1158
+ base_phosphate_interactions=base_phosphate_interactions_new,
1159
+ other_interactions=other_interactions_new,
1160
+ )
1161
+
1065
1162
 
1066
1163
  @dataclass(frozen=True, order=True)
1067
1164
  class InterStemParameters:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RNApolis
3
- Version: 0.10.3
3
+ Version: 0.10.5
4
4
  Summary: A Python library containing RNA-related bioinformatics functions and classes
5
5
  Home-page: https://github.com/tzok/rnapolis-py
6
6
  Author: Tomasz Zok
@@ -1,8 +1,8 @@
1
- rnapolis/adapter.py,sha256=X6whmJ4oTXteRZpdWwjP_KlTWN4Zxe26Rl7ANH-FDhI,48103
1
+ rnapolis/adapter.py,sha256=0Awt6owSeGcWq8kgLtkDv19suhrmcJgQip_Hi4Y3tK4,48513
2
2
  rnapolis/aligner.py,sha256=o7rQyjAZ3n4VXcnSPY3HVB8nLNRkVbl552O3NVh0mfg,3429
3
- rnapolis/annotator.py,sha256=OkqFVuxOtb-mySmw3bc5NF9ETu4BWq4ImtBecWJikrY,33899
3
+ rnapolis/annotator.py,sha256=HA2hfEUXdmBElObqRlASAB1FgkysjiHgwMTjEhsDiDE,30277
4
4
  rnapolis/clashfinder.py,sha256=AC9_tIx7QIk57sELq_aKfU1u3UMOXbgcccQeGHhMR6c,8517
5
- rnapolis/common.py,sha256=HTe-RSZa_9hEIi-j4-1afxdqt7zAD-BpZ7JxRZGX170,32390
5
+ rnapolis/common.py,sha256=hamlW892ZF5A0dSWsl7cOCZqOpbVQMgXjVPYDFzk3pE,36347
6
6
  rnapolis/component_A.csv,sha256=koirS-AwUZwoYGItT8yn3wS6Idvmh2FANfTQcOS_xh8,2897
7
7
  rnapolis/component_C.csv,sha256=NtvsAu_YrUgTjzZm3j4poW4IZ99x3dPARB09XVIiMCc,2803
8
8
  rnapolis/component_G.csv,sha256=Z5wl8OnHRyx4XhTyBiWgRZiEvmZXhoxtVRH8bn6Vxf0,2898
@@ -22,9 +22,9 @@ rnapolis/tertiary_v2.py,sha256=SgijTv0bPqMJwsMqyQk0O8QAnS2Ozk45vk8igxt9hRs,38001
22
22
  rnapolis/transformer.py,sha256=aC0nBmHHJf5TyLvBIV57Jj3tlwpvHbPo347opfAOlQA,3844
23
23
  rnapolis/unifier.py,sha256=2ge7IB9FdRgzSAiVD39U_ciwtdDJ2fGzf8mUIudbrqY,5820
24
24
  rnapolis/util.py,sha256=IdquFO3PV1_KDqodjupzm0Rqvgy0CeSzxGHaGEHYXVU,543
25
- rnapolis-0.10.3.dist-info/licenses/LICENSE,sha256=ZGRu12MzCgbYA-Lt8MyBlmjvPZh7xfiD5u5wBx0enq4,1066
26
- rnapolis-0.10.3.dist-info/METADATA,sha256=XvJW7MR1om_iashOz8wEcBe0A2RTkarkUtXzX0wnkU0,54611
27
- rnapolis-0.10.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
- rnapolis-0.10.3.dist-info/entry_points.txt,sha256=MZMWnYBUYnis-zWDmFfuA5yXtU3W5YdQrm5HA5LrkeM,474
29
- rnapolis-0.10.3.dist-info/top_level.txt,sha256=LcO18koxZcWoJ21KDRRRo_tyIbmXL5z61dPitZpy8yc,9
30
- rnapolis-0.10.3.dist-info/RECORD,,
25
+ rnapolis-0.10.5.dist-info/licenses/LICENSE,sha256=ZGRu12MzCgbYA-Lt8MyBlmjvPZh7xfiD5u5wBx0enq4,1066
26
+ rnapolis-0.10.5.dist-info/METADATA,sha256=LbY2r44uwiIoVASkHv1_TAwwKIOCi8Az33PDuTs7gdg,54611
27
+ rnapolis-0.10.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ rnapolis-0.10.5.dist-info/entry_points.txt,sha256=MZMWnYBUYnis-zWDmFfuA5yXtU3W5YdQrm5HA5LrkeM,474
29
+ rnapolis-0.10.5.dist-info/top_level.txt,sha256=LcO18koxZcWoJ21KDRRRo_tyIbmXL5z61dPitZpy8yc,9
30
+ rnapolis-0.10.5.dist-info/RECORD,,