• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rmcar17 / cogent3 / 17852834425

19 Sep 2025 08:22AM UTC coverage: 90.681% (+0.009%) from 90.672%
17852834425

push

github

rmcar17
TST: Convert dict views to lists

28257 of 31161 relevant lines covered (90.68%)

5.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.78
/src/cogent3/util/transform.py
1
#!/usr/bin/env python
2
"""Provides transformations of functions and other objects.
3

4
Includes:
5

6
Standard combinatorial higher-order functions adapted from David Mertz (2003),
7
"Text Processing in Python", Chapter 1.
8

9
Functions for performing complex tests on strings, e.g. includes_any or
10
includes_all.
11

12
Functions for generating combinations, permutations, or cartesian products
13
of lists.
14
"""
15

16
from collections.abc import Callable, Collection, Sized
6✔
17
from collections.abc import Sequence as PySeq
6✔
18
from typing import Generic, TypeVar
6✔
19

20
T = TypeVar("T")
6✔
21

22
maketrans = str.maketrans
6✔
23

24
# standard combinatorial HOF's from Mertz
25

26

27
def per_shortest(total: int, x: Sized, y: Sized) -> float:
6✔
28
    """Divides total by min(len(x), len(y)).
29

30
    Useful for normalizing per-item results from sequences that are zipped
31
    together. Always returns 0 if one of the sequences is empty (to
32
    avoid divide by zero error).
33
    """
34
    shortest = min(len(x), len(y))
6✔
35
    if not shortest:
6✔
36
        return 0
6✔
37
    return total / shortest
6✔
38

39

40
def per_longest(total: int, x: Sized, y: Sized) -> float:
6✔
41
    """Divides total by max(len(x), len(y)).
42

43
    Useful for normalizing per-item results from sequences that are zipped
44
    together. Always returns 0 if one of the sequences is empty (to
45
    avoid divide by zero error).
46
    """
47
    longest = max(len(x), len(y))
6✔
48
    if not longest:
6✔
49
        return 0
6✔
50
    return total / longest
6✔
51

52

53
class for_seq(Generic[T]):
6✔
54
    """Returns function that applies f(i,j) to i,j in zip(first, second).
55

56
    f: f(i,j) applying to elements of the sequence.
57

58
    aggregator: method to reduce the list of results to a scalar. Default: sum.
59

60
    normalizer: f(total, i, j) that normalizes the total as a function of
61
    i and j. Default is length_normalizer (divides by the length of the shorter
62
    of i and j). If normalizer is None, no normalization is performed.
63

64
    Will always truncate to length of the shorter sequence (because of the use
65
    of zip).
66
    """
67

68
    def __init__(
6✔
69
        self,
70
        f: Callable[[T, T], int],
71
        aggregator: Callable[[PySeq[int]], int] = sum,
72
        normalizer: Callable[[int, Sized, Sized], float] = per_shortest,
73
    ) -> None:
74
        self.f = f
6✔
75
        self.aggregator = aggregator
6✔
76
        self.normalizer = normalizer
6✔
77

78
    def __call__(self, first: Collection[T], second: Collection[T]) -> float:
6✔
79
        f = self.f
6✔
80
        if self.normalizer is None:
6✔
81
            return self.aggregator(
6✔
82
                [f(i, j) for i, j in zip(first, second, strict=False)],
83
            )
84
        return self.normalizer(
6✔
85
            self.aggregator([f(i, j) for i, j in zip(first, second, strict=False)]),
86
            first,
87
            second,
88
        )
89

90

91
# convenience functions for modifying objects
92

93

94
class KeepChars:
6✔
95
    """Returns a filter object o(s): call to return a filtered string.
96

97
    Specifically, strips out everything in s that is not in keep.
98
    This filter is case sensitive by default.
99
    """
100

101
    allchars = bytes(range(256))
6✔
102

103
    def __init__(self, keep: str, case_sens: bool = True) -> None:
6✔
104
        """Returns a new KeepChars object, based on string keep"""
105
        if not case_sens:
6✔
106
            low = keep.lower()
6✔
107
            up = keep.upper()
6✔
108
            keep = low + up
6✔
109

110
        self._strip_table = {
6✔
111
            c: None for c in self.allchars if c not in keep.encode("utf-8")
112
        }
113

114
    def __call__(self, s: str | bytes) -> str:
6✔
115
        """f(s) -> s, translates using self.allchars and self.delchars"""
116
        if s is None:
6✔
117
            raise TypeError
6✔
118
        if isinstance(s, bytes):
6✔
119
            s = s.decode("utf8")
×
120
        s = str(s)
6✔
121
        return s.translate(self._strip_table)
6✔
122

123

124
def first_index_in_set(seq: str, items: str) -> int | None:
6✔
125
    """Returns index of first occurrence of any of items in seq, or None."""
126
    for i, s in enumerate(seq):
6✔
127
        if s in items:
6✔
128
            return i
6✔
129
    return None
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc