Skip to content

Utils

Utilities.

Point

Bases: BaseModel

A 2D point with x and y coordinates.

Parameters:

Name Type Description Default
x int | None

x coordinate

None
y int | None

y coordinate

None
Source code in src/snailz/utils.py
18
19
20
21
22
class Point(BaseModel):
    """A 2D point with x and y coordinates."""

    x: int | None = Field(default=None, description="x coordinate")
    y: int | None = Field(default=None, description="y coordinate")

UniqueIdGenerator

Generate unique IDs using provided function.

Source code in src/snailz/utils.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class UniqueIdGenerator:
    """Generate unique IDs using provided function."""

    def __init__(self, name: str, func: Callable, limit: int = 10000) -> None:
        """Initialize the unique ID generator.

        Parameters:
            name: A name for this generator (used in error messages)
            func: Function that creates IDs when called
            limit: Maximum number of attempts to find a unique ID
        """
        self._name = name
        self._func = func
        self._limit = limit
        self._seen = set()

    def next(self, *args: object) -> str:
        """Get next unique ID.

        Parameters:
            *args: Arguments to pass to the ID-generating function

        Returns:
            A unique identifier that hasn't been returned before

        Raises:
            RuntimeError: If unable to generate a unique ID within limit attempts
        """
        for i in range(self._limit):
            ident = self._func(*args)
            if ident in self._seen:
                continue
            self._seen.add(ident)
            return ident
        raise RuntimeError(f"failed to find unique ID for {self._name}")

__init__(name, func, limit=10000)

Initialize the unique ID generator.

Parameters:

Name Type Description Default
name str

A name for this generator (used in error messages)

required
func Callable

Function that creates IDs when called

required
limit int

Maximum number of attempts to find a unique ID

10000
Source code in src/snailz/utils.py
28
29
30
31
32
33
34
35
36
37
38
39
def __init__(self, name: str, func: Callable, limit: int = 10000) -> None:
    """Initialize the unique ID generator.

    Parameters:
        name: A name for this generator (used in error messages)
        func: Function that creates IDs when called
        limit: Maximum number of attempts to find a unique ID
    """
    self._name = name
    self._func = func
    self._limit = limit
    self._seen = set()

next(*args)

Get next unique ID.

Parameters:

Name Type Description Default
*args object

Arguments to pass to the ID-generating function

()

Returns:

Type Description
str

A unique identifier that hasn't been returned before

Raises:

Type Description
RuntimeError

If unable to generate a unique ID within limit attempts

Source code in src/snailz/utils.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def next(self, *args: object) -> str:
    """Get next unique ID.

    Parameters:
        *args: Arguments to pass to the ID-generating function

    Returns:
        A unique identifier that hasn't been returned before

    Raises:
        RuntimeError: If unable to generate a unique ID within limit attempts
    """
    for i in range(self._limit):
        ident = self._func(*args)
        if ident in self._seen:
            continue
        self._seen.add(ident)
        return ident
    raise RuntimeError(f"failed to find unique ID for {self._name}")

csv_writer(output)

Wrapper to get line terminator settings right.

Source code in src/snailz/utils.py
62
63
64
65
def csv_writer(output: TextIO | StringIO) -> Any:
    """Wrapper to get line terminator settings right."""

    return csv.writer(output, lineterminator="\n")

fail(msg)

Print message to standard error and exit with status 1.

Parameters:

Name Type Description Default
msg str

The error message to display

required
Source code in src/snailz/utils.py
68
69
70
71
72
73
74
75
def fail(msg: str) -> None:
    """Print message to standard error and exit with status 1.

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

load_data(parameter_name, filename, cls)

Construct a Pydantic model from serialized JSON.

Parameters:

Name Type Description Default
parameter_name str

Name of the parameter requiring this file (for error messages)

required
filename str | Path | None

Path to the JSON file to load (allowed to be None so that checking is done in one place)

required
cls Type[BaseModel]

The Pydantic model to instantiate with the loaded data

required

Returns:

Type Description
BaseModel

An instance of cls constructed from the JSON data

Raises:

Type Description
IOError

If the file cannot be read

Source code in src/snailz/utils.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def load_data(
    parameter_name: str, filename: str | Path | None, cls: Type[BaseModel]
) -> BaseModel:
    """Construct a Pydantic model from serialized JSON.

    Parameters:
        parameter_name: Name of the parameter requiring this file (for error messages)
        filename: Path to the JSON file to load (allowed to be None so that checking is done in one place)
        cls: The Pydantic model to instantiate with the loaded data

    Returns:
        An instance of cls constructed from the JSON data

    Raises:
        IOError: If the file cannot be read
    """
    assert filename is not None, f"--{parameter_name} is required"
    with open(filename, "r") as reader:
        return cls.model_validate(json.load(reader))

report_result(output, result)

Save or display result as JSON.

Parameters:

Name Type Description Default
output str | Path | None

Path to output file, or None to print to stdout

required
result BaseModel

The Pydantic model object to serialize as JSON

required
Side effects

Either writes to the specified output file or prints to stdout

Source code in src/snailz/utils.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def report_result(output: str | Path | None, result: BaseModel) -> None:
    """Save or display result as JSON.

    Parameters:
        output: Path to output file, or None to print to stdout
        result: The Pydantic model object to serialize as JSON

    Side effects:
        Either writes to the specified output file or prints to stdout
    """
    result_json = json.dumps(result.model_dump(), default=serialize_values)
    if output:
        with open(output, "w") as writer:
            writer.write(result_json)
    else:
        print(result_json)

serialize_values(obj)

Custom JSON serializer for JSON conversion.

Parameters:

Name Type Description Default
obj object

The object to serialize

required

Returns:

Type Description
str | dict

String representation of date objects or dict for Pydantic models

Raises:

Type Description
TypeError

If the object type is not supported for serialization

Source code in src/snailz/utils.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def serialize_values(obj: object) -> str | dict:
    """Custom JSON serializer for JSON conversion.

    Parameters:
        obj: The object to serialize

    Returns:
        String representation of date objects or dict for Pydantic models

    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()
    raise TypeError(f"Type {type(obj)} not serializable")

validate_date(ctx, param, value)

Validate and convert date string to date object.

Parameters:

Name Type Description Default
ctx object

Click context object

required
param object

Click parameter being processed

required
value str | None

The value to validate

required

Returns:

Type Description
date | None

None if value is None, otherwise a date object

Source code in src/snailz/utils.py
136
137
138
139
140
141
142
143
144
145
146
147
def validate_date(ctx: object, param: object, value: str | None) -> date | None:
    """Validate and convert date string to date object.

    Parameters:
        ctx: Click context object
        param: Click parameter being processed
        value: The value to validate

    Returns:
        None if value is None, otherwise a date object
    """
    return None if value is None else date.fromisoformat(value)