Skip to content

Commit b9cd188

Browse files
refactor: consolidate constants and common (#7)
Co-authored-by: Kieran Didi <58345129+kierandidi@users.noreply.github.com>
1 parent 2405193 commit b9cd188

64 files changed

Lines changed: 124 additions & 132 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
from __future__ import annotations
1+
"""Common functions used throughout the project."""
22

33
import copy
44
import hashlib
5-
from collections import OrderedDict
6-
from collections.abc import Callable, Iterable, Iterator
5+
from collections.abc import Callable
76
from functools import lru_cache, wraps
87
from typing import Any
98

@@ -12,26 +11,29 @@
1211

1312

1413
def exists(obj: Any) -> bool:
14+
"""Check that `obj` is not `None`."""
1515
return obj is not None
1616

1717

1818
def default(obj: Any, default: Any) -> Any:
19+
"""Return `obj` if not `None`, otherwise return `default`."""
1920
return obj if exists(obj) else default
2021

2122

22-
def deduplicate_iterator(iterator: Iterable) -> Iterator:
23-
"""Deduplicate an iterator while preserving order."""
24-
return iter(OrderedDict.fromkeys(iterator))
25-
26-
2723
def to_hashable(element: Any) -> Any:
2824
"""Convert an element to a hashable type."""
2925
return element if isinstance(element, int | str | np.integer | np.str_) else tuple(element)
3026

3127

28+
def string_to_md5_hash(s: str, truncate: int = 32) -> str:
29+
"""Generate an MD5 hash of a string and return the first `truncate` characters."""
30+
full_hash = hashlib.md5(s.encode("utf-8")).hexdigest()
31+
return full_hash[:truncate]
32+
33+
3234
def sum_string_arrays(*objs: np.ndarray | str) -> np.ndarray:
3335
"""
34-
Sum a list of string arrays / strings into a single string array by concatenating them and
36+
Sum a list of string arrays or strings into a single string array by concatenating them and
3537
determining the shortest string length to set as dtype.
3638
"""
3739
return reduce(np.char.add, objs).astype(object).astype(str)
@@ -47,6 +49,24 @@ def listmap(func: Callable, *iterables) -> list:
4749
return compose(list, map)(func, *iterables)
4850

4951

52+
def as_list(value: Any) -> list:
53+
"""Convert a value to a list.
54+
55+
Handles various types using duck typing:
56+
- Iterable objects (lists, tuples, strings, etc.): converted to list
57+
- Single values: wrapped in a list
58+
"""
59+
try:
60+
# Try to iterate over the value (duck typing approach)
61+
# Exclude strings since they're iterable but we want to treat them as single values
62+
if isinstance(value, str):
63+
return [value]
64+
return list(value)
65+
except TypeError:
66+
# If it's not iterable, wrap it in a list
67+
return [value]
68+
69+
5070
def immutable_lru_cache(maxsize: int = 128, typed: bool = False, deepcopy: bool = True) -> Callable:
5171
"""An immutable version of `lru_cache` for caching functions that return mutable objects."""
5272
copy_func = copy.deepcopy if deepcopy else copy.copy
@@ -89,9 +109,3 @@ def __call__(self, value: Any) -> int:
89109
self.key_to_id[value] = self.next_id
90110
self.next_id += 1
91111
return self.key_to_id[value]
92-
93-
94-
def md5_hash_string(s: str, length: int = 32) -> str:
95-
"""Generate an MD5 hash of a string and return the first `length` characters."""
96-
full_hash = hashlib.md5(s.encode("utf-8")).hexdigest()
97-
return full_hash[:length]

src/atomworks/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import numpy as np
88
from toolz import keymap
99

10-
from atomworks.io.constants import (
10+
from atomworks.constants import (
1111
AA_LIKE_CHEM_TYPES,
1212
DNA_LIKE_CHEM_TYPES,
1313
POLYPEPTIDE_D_CHEM_TYPES,

src/atomworks/io/parser.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
from toolz import keyfilter
1919

2020
import atomworks.io.transforms.atom_array as ta
21+
from atomworks.common import exists, string_to_md5_hash
22+
from atomworks.constants import CCD_MIRROR_PATH, CRYSTALLIZATION_AIDS, WATER_LIKE_CCDS
2123
from atomworks.io import template
22-
from atomworks.io.common import exists, md5_hash_string
23-
from atomworks.io.constants import CCD_MIRROR_PATH, CRYSTALLIZATION_AIDS, WATER_LIKE_CCDS
2424
from atomworks.io.transforms.categories import (
2525
category_to_dict,
2626
extract_crystallization_details,
@@ -218,7 +218,7 @@ def parse(
218218
}
219219
# Compose args_string from parse_arguments values (in order)
220220
args_string = ",".join(str(parse_arguments[k]) for k in parse_arguments)
221-
args_hash = md5_hash_string(args_string, length=8)
221+
args_hash = string_to_md5_hash(args_string, truncate=8)
222222

223223
# ... generate assembly info
224224
assembly_info = ",".join(build_assembly) if isinstance(build_assembly, list | tuple) else build_assembly

src/atomworks/io/template.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from biotite.structure import AtomArray, BondList
88

99
import atomworks.io.transforms.atom_array as ta
10-
from atomworks.io.common import exists, immutable_lru_cache
11-
from atomworks.io.constants import CCD_MIRROR_PATH, DO_NOT_MATCH_CCD
10+
from atomworks.common import exists, immutable_lru_cache
11+
from atomworks.constants import CCD_MIRROR_PATH, DO_NOT_MATCH_CCD
1212
from atomworks.io.utils.bonds import (
1313
correct_bond_types_for_nucleophilic_additions,
1414
correct_formal_charges_for_specified_atoms,

src/atomworks/io/tools/fasta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import os
77
import re
88

9+
from atomworks.constants import CCD_MIRROR_PATH
910
from atomworks.enums import ChainType
10-
from atomworks.io.constants import CCD_MIRROR_PATH
1111
from atomworks.io.utils.ccd import (
1212
check_ccd_codes_are_available,
1313
)

src/atomworks/io/tools/inference.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
from rdkit.Chem import AllChem
1515

1616
import atomworks.io.transforms.atom_array as ta
17-
from atomworks.enums import ChainType, ChainTypeInfo
18-
from atomworks.io import parse
19-
from atomworks.io.common import KeyToIntMapper, exists
20-
from atomworks.io.constants import (
17+
from atomworks.common import KeyToIntMapper, exists
18+
from atomworks.constants import (
2119
CCD_MIRROR_PATH,
2220
STANDARD_AA_ONE_LETTER,
2321
STANDARD_DNA_ONE_LETTER,
2422
STANDARD_RNA,
2523
UNKNOWN_LIGAND,
2624
)
25+
from atomworks.enums import ChainType, ChainTypeInfo
26+
from atomworks.io import parse
2727
from atomworks.io.parser import DEFAULT_PARSE_KWARGS
2828
from atomworks.io.template import build_template_atom_array
2929
from atomworks.io.tools.fasta import one_letter_to_ccd_code, split_generalized_fasta_sequence

src/atomworks/io/tools/rdkit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
from rdkit.DataStructs import ExplicitBitVect
2323

2424
import atomworks.io.transforms.atom_array as ta
25-
from atomworks.io.common import exists, immutable_lru_cache, not_isin
26-
from atomworks.io.constants import (
25+
from atomworks.common import exists, immutable_lru_cache, not_isin
26+
from atomworks.constants import (
2727
BIOTITE_DEFAULT_ANNOTATIONS,
2828
CCD_MIRROR_PATH,
2929
HYDROGEN_LIKE_SYMBOLS,

src/atomworks/io/transforms/atom_array.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import pandas as pd
1313
from biotite.structure import AtomArray, AtomArrayStack, stack
1414

15-
from atomworks.io.common import listmap, not_isin, sum_string_arrays
16-
from atomworks.io.constants import ELEMENT_NAME_TO_ATOMIC_NUMBER, HYDROGEN_LIKE_SYMBOLS, WATER_LIKE_CCDS
15+
from atomworks.common import listmap, not_isin, sum_string_arrays
16+
from atomworks.constants import ELEMENT_NAME_TO_ATOMIC_NUMBER, HYDROGEN_LIKE_SYMBOLS, WATER_LIKE_CCDS
1717
from atomworks.io.utils.bonds import (
1818
generate_inter_level_bond_hash,
1919
get_coarse_graph_as_nodes_and_edges,

src/atomworks/io/transforms/categories.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
from biotite.structure import AtomArray
1818
from biotite.structure.io.pdbx import CIFBlock
1919

20+
from atomworks.common import exists
21+
from atomworks.constants import CCD_MIRROR_PATH
2022
from atomworks.enums import ChainType
21-
from atomworks.io.common import deduplicate_iterator, exists
22-
from atomworks.io.constants import CCD_MIRROR_PATH
2323
from atomworks.io.utils.selection import get_residue_starts
2424
from atomworks.io.utils.sequence import get_1_from_3_letter_code
2525

@@ -253,7 +253,9 @@ def load_monomer_sequence_information_from_category(
253253

254254
# Build up the chain_info_dict with the sequence information
255255
res_starts = get_residue_starts(atom_array)
256-
for chain_id in deduplicate_iterator(struc.get_chains(atom_array)):
256+
# ... get the unique chain IDs by order of first appearance in the AtomArray
257+
chain_ids = dict.fromkeys(struc.get_chains(atom_array))
258+
for chain_id in chain_ids:
257259
rcsb_entity = int(chain_info_dict[chain_id]["rcsb_entity"])
258260

259261
if rcsb_entity in polymer_entity_id_to_res_names_and_ids:

0 commit comments

Comments
 (0)