Skip to content

python_util.py

General Python Utilities

chunkify(lst, n)

Breaks list into n chunks.

Parameters:

Name Type Description Default
lst list

List to chunkify.

required
n int

Number of lists to make

required

Returns:

Type Description
list[list * n]

lst split into n chunks.

Source code in lavlab/python_util.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def chunkify(lst: list, n: int) -> list[list]:
    """
    Breaks list into n chunks.

    Parameters
    ----------
    lst: list
        List to chunkify.
    n: int
        Number of lists to make

    Returns
    -------
    list[list*n]
        lst split into n chunks.
    """
    size = ceil(len(lst) / n)
    return list(map(lambda x: lst[x * size : x * size + size], list(range(n))))

create_array(shape, dtype=np.float64)

Creates an in-memory array or a disk-based memmap array based on the available system memory.

Parameters:

Name Type Description Default
shape tuple

Shape of the array.

required
dtype np.dtype, Default: np.float64

Data-type of the array's elements.

float64

Returns:

Type Description
array

Numpy in-memory array or memmap array based on the available system memory.

Source code in lavlab/python_util.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def create_array(shape: tuple[int, ...], dtype=np.float64) -> np.ndarray:
    """
    Creates an in-memory array or a disk-based memmap array based on the available system memory.

    Parameters
    ----------
    shape : tuple
        Shape of the array.
    dtype : np.dtype, Default: np.float64
        Data-type of the array's elements.

    Returns
    -------
    array
        Numpy in-memory array or memmap array based on the available system memory.
    """
    if is_memsafe_array(shape, dtype):
        return np.zeros(shape, dtype)
    if not is_storage_safe_img(shape, dtype):
        raise MemoryError(
            f"Array of shape {shape} with dtype {dtype} is too large for storage."
        )
    path = tempfile.mkstemp(dir=lavlab.ctx.temp_dir)[1]
    return np.memmap(path, dtype=dtype, mode="w+", shape=shape)

desync(it) async

Turns sync iterable into an async iterable.

Parameters:

Name Type Description Default
it

Synchronous iterable-like object (can be used in for loop)

required

Returns:

Type Description
AsyncGenerator

asynchronously yields results from input iterable.

Source code in lavlab/python_util.py
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
async def desync(it) -> AsyncGenerator:
    """
    Turns sync iterable into an async iterable.

    Parameters
    ----------
    it: Iterable
        Synchronous iterable-like object (can be used in for loop)

    Returns
    -------
    AsyncGenerator
        asynchronously yields results from input iterable."""
    for x in it:
        yield x

interlace_lists(*lists)

Interlaces a list of lists. Useful for combining tileLists of different channels.

Parameters:

Name Type Description Default
*lists list

lists to merge.

()

Returns:

Type Description
list

Merged list.

Examples:

>>> interlace_lists([1,3],[2,4])
[1,2,3,4]
Source code in lavlab/python_util.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def interlace_lists(*lists: list) -> list:
    """
    Interlaces a list of lists. Useful for combining tileLists of different channels.

    Parameters
    ----------
    *lists: list
        lists to merge.

    Returns
    -------
    list
        Merged list.

    Examples
    --------
    >>> interlace_lists([1,3],[2,4])
    [1,2,3,4]
    """
    return [
        val
        for tup in zip_longest(*lists, fillvalue=None)
        for val in tup
        if val is not None
    ]

is_memsafe_array(shape, dtype=np.float64)

Checks if a desired array of given shape and datatype is too large for the memory constraints

Parameters:

Name Type Description Default
shape tuple[int, ...]

shape of the array

required
dtype dtype

datatype of given array, by default np.float64 for max safety

float64

Returns:

Type Description
bool

True if the array is safe to create in memory, False if it will blow your pc up.

Source code in lavlab/python_util.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def is_memsafe_array(shape: tuple[int, ...], dtype=np.float64) -> bool:
    """
    Checks if a desired array of given shape and datatype is too large for the memory constraints

    Parameters
    ----------
    shape : tuple[int,...]
        shape of the array
    dtype : np.dtype, optional
        datatype of given array, by default np.float64 for max safety

    Returns
    -------
    bool
        True if the array is safe to create in memory, False if it will blow your pc up.
    """
    size = np.prod(shape) * np.dtype(dtype).itemsize
    return size < lavlab.ctx.resources.max_memory

is_memsafe_pvimg(pv_img)

Checks if a given pyvips image is too large for the memory constraints

Parameters:

Name Type Description Default
pv_img Image

pyvips image to check

required

Returns:

Type Description
bool

True if the image is safe to create in memory, False if it will blow your pc up.

Source code in lavlab/python_util.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def is_memsafe_pvimg(pv_img: pv.Image) -> bool:
    """
    Checks if a given pyvips image is too large for the memory constraints

    Parameters
    ----------
    pv_img : pv.Image
        pyvips image to check

    Returns
    -------
    bool
        True if the image is safe to create in memory, False if it will blow your pc up.
    """
    # assume dtype is 64-bit float atm
    size = pv_img.width * pv_img.height * pv_img.bands * 64
    return size < lavlab.ctx.resources.max_memory

is_storage_safe_img(shape, dtype=np.float64)

Checks if a desired array of given shape and datatype is too large for the storage constraints

Parameters:

Name Type Description Default
shape tuple[int, ...]

shape of the array

required
dtype dtype

datatype of given array, by default np.float64 for max safety

float64

Returns:

Type Description
bool

True if the array is safe to create in memory, False if it will blow your pc up.

Source code in lavlab/python_util.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def is_storage_safe_img(shape: tuple[int, ...], dtype=np.float64) -> bool:
    """
    Checks if a desired array of given shape and datatype is too large for the storage constraints

    Parameters
    ----------
    shape : tuple[int,...]
        shape of the array
    dtype : np.dtype, optional
        datatype of given array, by default np.float64 for max safety

    Returns
    -------
    bool
        True if the array is safe to create in memory, False if it will blow your pc up.
    """
    size = np.prod(shape) * np.dtype(dtype).itemsize
    return size < lavlab.ctx.resources.max_temp_storage

merge_async_iters(*a_iters)

Merges async generators using a asyncio.Queue.

Notes

Code from: https://stackoverflow.com/a/55317623

Parameters:

Name Type Description Default
*a_iters

AsyncGenerators to merge

()

Returns:

Type Description
AsyncGenerator

Generator that calls all input generators

Source code in lavlab/python_util.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def merge_async_iters(*a_iters) -> AsyncGenerator:
    """
    Merges async generators using a asyncio.Queue.

    Notes
    -----
    Code from: https://stackoverflow.com/a/55317623

    Parameters
    ----------
    *a_iters: AsyncGenerator
        AsyncGenerators to merge

    Returns
    -------
    AsyncGenerator
        Generator that calls all input generators
    """
    queue = asyncio.Queue(1)  # type: ignore
    run_count = len(a_iters)
    cancelling = False

    async def drain(a_iter):
        nonlocal run_count
        try:
            async for item in a_iter:
                await queue.put((False, item))
        except IOError as e:
            if not cancelling:
                await queue.put((True, e))
            else:
                raise
        finally:
            run_count -= 1

    async def merged():
        try:
            while run_count:
                raised, next_item = await queue.get()
                if raised:
                    cancel_tasks()
                    raise next_item
                yield next_item
        finally:
            cancel_tasks()

    def cancel_tasks():
        nonlocal cancelling
        cancelling = True
        for t in tasks:
            t.cancel()

    tasks = [asyncio.create_task(drain(a_iter)) for a_iter in a_iters]
    return merged()

rgba_to_uint(red, green, blue, alpha=255)

Return the color as an Integer in RGBA encoding.

Parameters:

Name Type Description Default
red int

Red color val (0-255)

required
green int

Green color val (0-255)

required
blue int

Blue color val (0-255)

required
alpha

Alpha opacity val (0-255)

255

Returns:

Type Description
int

Integer encoding rgba value.

Source code in lavlab/python_util.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
def rgba_to_uint(red: int, green: int, blue: int, alpha=255) -> int:
    """
    Return the color as an Integer in RGBA encoding.

    Parameters
    ----------
    red: int
        Red color val (0-255)
    green: int
        Green color val (0-255)
    blue: int
        Blue color val (0-255)
    alpha: int
        Alpha opacity val (0-255)

    Returns
    -------
    int
        Integer encoding rgba value.
    """
    r = red << 24
    g = green << 16
    b = blue << 8
    a = alpha
    uint = r + g + b + a
    if uint > (2**31 - 1):  # convert to signed 32-bit int
        uint = uint - 2**32
    return int(uint)

uint_to_rgba(uint)

Return the color as an Integer in RGBA encoding.

Parameters:

Name Type Description Default
int

Integer encoding rgba value.

required

Returns:

Name Type Description
red int

Red color val (0-255)

green int

Green color val (0-255)

blue int

Blue color val (0-255)

alpha int

Alpha opacity val (0-255)

Source code in lavlab/python_util.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
def uint_to_rgba(uint: int) -> tuple[int, int, int, int]:
    """
    Return the color as an Integer in RGBA encoding.

    Parameters
    ----------
    int
        Integer encoding rgba value.

    Returns
    -------
    red: int
        Red color val (0-255)
    green: int
        Green color val (0-255)
    blue: int
        Blue color val (0-255)
    alpha: int
        Alpha opacity val (0-255)"""
    if uint < 0:  # convert from signed 32-bit int
        uint = uint + 2**32

    red = (uint >> 24) & 0xFF
    green = (uint >> 16) & 0xFF
    blue = (uint >> 8) & 0xFF
    alpha = uint & 0xFF

    return red, green, blue, alpha