Skip to content

Utils

Snailz utilities.

MinimalSpecimen

Bases: BaseModel

Minimal specimen to avoid circular import issues.

Parameters:

Name Type Description Default
ident str

unique identifier

required
location Point

where specimen was collected

required
mass float

specimen mass in grams

0.0
Source code in src/snailz/utils.py
44
45
46
47
48
49
class MinimalSpecimen(BaseModel):
    """Minimal specimen to avoid circular import issues."""

    ident: str = Field(description="unique identifier")
    location: Point = Field(description="where specimen was collected")
    mass: float = Field(default=0.0, ge=0, description="specimen mass in grams")

choose_one(items, weights=None)

Choose one item at random.

Source code in src/snailz/utils.py
52
53
54
def choose_one(items, weights=None):
    """Choose one item at random."""
    return random.choices(items, weights=weights, k=1)[0]

fail(msg)

Report failure and exit.

Parameters:

Name Type Description Default
msg str

Error message to display

required
Source code in src/snailz/utils.py
57
58
59
60
61
62
63
64
def fail(msg: str) -> None:
    """Report failure and exit.

    Parameters:
        msg: Error message to display
    """
    print(msg, file=sys.stderr)
    sys.exit(1)

json_dump(obj, indent=2)

Dump as JSON with appropriate settings.

Source code in src/snailz/utils.py
67
68
69
def json_dump(obj: BaseModel, indent: int | None = 2) -> str:
    """Dump as JSON with appropriate settings."""
    return json.dumps(obj, indent=indent, default=_serialize_json)

report(verbose, msg)

Report if verbosity turned on.

Parameters:

Name Type Description Default
verbose bool

Is display on or off?

required
msg str

Message to display

required
Source code in src/snailz/utils.py
72
73
74
75
76
77
78
79
80
def report(verbose: bool, msg: str) -> None:
    """Report if verbosity turned on.

    Parameters:
        verbose: Is display on or off?
        msg: Message to display
    """
    if verbose:
        print(msg)

sigmoid(x)

Calculate sigmoid curve value for x in 0..1.

Sigmoid parameters are chosen so that s(0)=0, s(0.5)=0.5, and s(1)=1.

Parameters:

Name Type Description Default
x float

input value

required

Returns:

Type Description
float

Sigmoid curve value.

Source code in src/snailz/utils.py
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def sigmoid(x: float) -> float:
    """Calculate sigmoid curve value for x in 0..1.

    Sigmoid parameters are chosen so that s(0)=0, s(0.5)=0.5, and s(1)=1.

    Parameters:
        x: input value

    Returns:
        Sigmoid curve value.
    """
    a = 16.0
    b = 0.5
    c = -0.0002
    return 1 / (1 + math.exp(-a * (x - b))) + c

to_csv(rows, fields, f_make_row)

Generic converter from list of models to CSV string.

Parameters:

Name Type Description Default
rows list

List of rows to convert.

required
fields list

List of names of columns.

required
f_make_row Callable

Function that converts a row to text.

required

Returns:

Type Description
str

CSV representation of data.

Source code in src/snailz/utils.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def to_csv(rows: list, fields: list, f_make_row: Callable) -> str:
    """Generic converter from list of models to CSV string.

    Parameters:
        rows: List of rows to convert.
        fields: List of names of columns.
        f_make_row: Function that converts a row to text.

    Returns:
        CSV representation of data.
    """

    output = io.StringIO()
    writer = csv.writer(output, lineterminator="\n")
    writer.writerow(fields)
    for r in rows:
        writer.writerow(f_make_row(r))
    return output.getvalue()

unique_id(name, func, limit=TRIAL_LIMIT)

Generate unique IDs.

Parameters:

Name Type Description Default
name str

name of this generator

required
func Callable

function to generate next candidate

required
limit int

how many tries per ID

TRIAL_LIMIT

Returns:

Type Description
None

Unique ID.

Source code in src/snailz/utils.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def unique_id(
    name: str, func: Callable, limit: int = TRIAL_LIMIT
) -> Generator[str, tuple | None, None]:
    """Generate unique IDs.

    Parameters:
        name: name of this generator
        func: function to generate next candidate
        limit: how many tries per ID

    Returns:
        Unique ID.
    """
    gen = _make_unique_id_generator(name, func, limit)
    next(gen)  # prime the generator
    return gen

_make_unique_id_generator(name, func, limit)

Create and prime a unique ID generator.

Parameters:

Name Type Description Default
name str

name of this generator

required
func Callable

function to generate next candidate

required
limit int

how many tries per ID

required

Returns:

Type Description
None

Unique ID.

Raises:

Type Description
RuntimeError

if no unique ID can be round.

Source code in src/snailz/utils.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def _make_unique_id_generator(
    name: str, func: Callable, limit: int
) -> Generator[str, tuple | None, None]:
    """Create and prime a unique ID generator.

    Parameters:
        name: name of this generator
        func: function to generate next candidate
        limit: how many tries per ID

    Returns:
        Unique ID.

    Raises:
        RuntimeError: if no unique ID can be round.
    """
    seen = set()
    provided = yield ""  # to prime the generator
    while True:
        found = False
        for _ in range(limit):
            if provided is None:
                provided = ()
            temp = func(*provided)
            assert isinstance(temp, str)
            if temp in seen:
                continue
            seen.add(temp)
            found = True
            break
        if not found:
            raise RuntimeError(f"{name} unable to find unique ID")
        provided = yield temp

_serialize_json(obj)

Custom JSON serializer for JSON conversion.

Parameters:

Name Type Description Default
obj object

The object to serialize

required

Returns:

Type Description
str | dict | None

String representation of date objects or dict for Pydantic models;

str | dict | None

None for PIL images.

Raises:

Type Description
TypeError

If the object type is not supported for serialization

Source code in src/snailz/utils.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def _serialize_json(obj: object) -> str | dict | None:
    """Custom JSON serializer for JSON conversion.

    Parameters:
        obj: The object to serialize

    Returns:
        String representation of date objects or dict for Pydantic models;
        None for PIL images.

    Raises:
        TypeError: If the object type is not supported for serialization
    """
    if isinstance(obj, date):
        return obj.isoformat()
    if isinstance(obj, BaseModel):
        return obj.model_dump()
    if isinstance(obj, PilImage):
        return None
    raise TypeError(f"Type {type(obj)} not serializable")