Skip to content

Commit c988ae3

Browse files
committed
added more documentation
1 parent 875e556 commit c988ae3

13 files changed

Lines changed: 410 additions & 40 deletions

python_utils/containers.py

Lines changed: 191 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
>>> d[1:4]
5454
SliceableDeque([2, 3, 4])
5555
"""
56+
5657
# pyright: reportIncompatibleMethodOverride=false
5758
import abc
5859
import collections
@@ -109,15 +110,16 @@ class CastedDictBase(types.Dict[KT, VT], abc.ABC):
109110
Sets the item in the dictionary, casting the key if a key cast
110111
callable is provided.
111112
"""
113+
112114
_key_cast: KT_cast[KT]
113115
_value_cast: VT_cast[VT]
114116

115117
def __init__(
116-
self,
117-
key_cast: KT_cast[KT] = None,
118-
value_cast: VT_cast[VT] = None,
119-
*args: DictUpdateArgs[KT, VT],
120-
**kwargs: VT,
118+
self,
119+
key_cast: KT_cast[KT] = None,
120+
value_cast: VT_cast[VT] = None,
121+
*args: DictUpdateArgs[KT, VT],
122+
**kwargs: VT,
121123
) -> None:
122124
"""
123125
Initializes the CastedDictBase with optional key and value
@@ -138,10 +140,16 @@ def __init__(
138140
self.update(*args, **kwargs)
139141

140142
def update(
141-
self,
142-
*args: DictUpdateArgs[types.Any, types.Any],
143-
**kwargs: types.Any
143+
self, *args: DictUpdateArgs[types.Any, types.Any], **kwargs: types.Any
144144
) -> None:
145+
"""
146+
Updates the dictionary with the given arguments.
147+
148+
Args:
149+
*args (DictUpdateArgs[types.Any, types.Any]): Arguments to update
150+
the dictionary.
151+
**kwargs (types.Any): Keyword arguments to update the dictionary.
152+
"""
145153
if args:
146154
kwargs.update(*args)
147155

@@ -150,6 +158,14 @@ def update(
150158
self[key] = value
151159

152160
def __setitem__(self, key: types.Any, value: types.Any) -> None:
161+
"""
162+
Sets the item in the dictionary, casting the key if a key cast
163+
callable is provided.
164+
165+
Args:
166+
key (types.Any): The key to set in the dictionary.
167+
value (types.Any): The value to set in the dictionary.
168+
"""
153169
if self._key_cast is not None:
154170
key = self._key_cast(key)
155171

@@ -246,12 +262,30 @@ class LazyCastedDict(CastedDictBase[KT, VT]):
246262
"""
247263

248264
def __setitem__(self, key: types.Any, value: types.Any):
265+
"""
266+
Sets the item in the dictionary, casting the key if a key cast
267+
callable is provided.
268+
269+
Args:
270+
key (types.Any): The key to set in the dictionary.
271+
value (types.Any): The value to set in the dictionary.
272+
"""
249273
if self._key_cast is not None:
250274
key = self._key_cast(key)
251275

252276
super().__setitem__(key, value)
253277

254278
def __getitem__(self, key: types.Any) -> VT:
279+
"""
280+
Gets the item from the dictionary, casting the value if a value cast
281+
callable is provided.
282+
283+
Args:
284+
key (types.Any): The key to get from the dictionary.
285+
286+
Returns:
287+
VT: The value from the dictionary.
288+
"""
255289
if self._key_cast is not None:
256290
key = self._key_cast(key)
257291

@@ -263,15 +297,31 @@ def __getitem__(self, key: types.Any) -> VT:
263297
return value
264298

265299
def items( # type: ignore
266-
self,
300+
self,
267301
) -> types.Generator[types.Tuple[KT, VT], None, None]:
302+
"""
303+
Returns a generator of the dictionary's items, casting the values if a
304+
value cast callable is provided.
305+
306+
Yields:
307+
types.Generator[types.Tuple[KT, VT], None, None]: A generator of
308+
the dictionary's items.
309+
"""
268310
if self._value_cast is None:
269311
yield from super().items()
270312
else:
271313
for key, value in super().items():
272314
yield key, self._value_cast(value)
273315

274316
def values(self) -> types.Generator[VT, None, None]: # type: ignore
317+
"""
318+
Returns a generator of the dictionary's values, casting the values if a
319+
value cast callable is provided.
320+
321+
Yields:
322+
types.Generator[VT, None, None]: A generator of the dictionary's
323+
values.
324+
"""
275325
if self._value_cast is None:
276326
yield from super().values()
277327
else:
@@ -316,17 +366,36 @@ class UniqueList(types.List[HT]):
316366
_set: types.Set[HT]
317367

318368
def __init__(
319-
self,
320-
*args: HT,
321-
on_duplicate: OnDuplicate = 'ignore',
369+
self,
370+
*args: HT,
371+
on_duplicate: OnDuplicate = 'ignore',
322372
):
373+
"""
374+
Initializes the UniqueList with optional duplicate handling behavior.
375+
376+
Args:
377+
*args (HT): Initial values for the list.
378+
on_duplicate (OnDuplicate, optional): Behavior on duplicates.
379+
Defaults to 'ignore'.
380+
"""
323381
self.on_duplicate = on_duplicate
324382
self._set = set()
325383
super().__init__()
326384
for arg in args:
327385
self.append(arg)
328386

329387
def insert(self, index: types.SupportsIndex, value: HT) -> None:
388+
"""
389+
Inserts a value at the specified index, ensuring uniqueness.
390+
391+
Args:
392+
index (types.SupportsIndex): The index to insert the value at.
393+
value (HT): The value to insert.
394+
395+
Raises:
396+
ValueError: If the value is a duplicate and `on_duplicate` is set
397+
to 'raise'.
398+
"""
330399
if value in self._set:
331400
if self.on_duplicate == 'raise':
332401
raise ValueError(f'Duplicate value: {value}')
@@ -337,6 +406,16 @@ def insert(self, index: types.SupportsIndex, value: HT) -> None:
337406
super().insert(index, value)
338407

339408
def append(self, value: HT) -> None:
409+
"""
410+
Appends a value to the list, ensuring uniqueness.
411+
412+
Args:
413+
value (HT): The value to append.
414+
415+
Raises:
416+
ValueError: If the value is a duplicate and `on_duplicate` is set
417+
to 'raise'.
418+
"""
340419
if value in self._set:
341420
if self.on_duplicate == 'raise':
342421
raise ValueError(f'Duplicate value: {value}')
@@ -347,25 +426,45 @@ def append(self, value: HT) -> None:
347426
super().append(value)
348427

349428
def __contains__(self, item: HT) -> bool: # type: ignore
429+
"""
430+
Checks if the list contains the specified item.
431+
432+
Args:
433+
item (HT): The item to check for.
434+
435+
Returns:
436+
bool: True if the item is in the list, False otherwise.
437+
"""
350438
return item in self._set
351439

352440
@typing.overload
353441
def __setitem__(
354-
self, indices: types.SupportsIndex, values: HT
355-
) -> None:
356-
...
442+
self, indices: types.SupportsIndex, values: HT
443+
) -> None: ...
357444

358445
@typing.overload
359446
def __setitem__(
360-
self, indices: slice, values: types.Iterable[HT]
361-
) -> None:
362-
...
447+
self, indices: slice, values: types.Iterable[HT]
448+
) -> None: ...
363449

364450
def __setitem__(
365-
self,
366-
indices: types.Union[slice, types.SupportsIndex],
367-
values: types.Union[types.Iterable[HT], HT],
451+
self,
452+
indices: types.Union[slice, types.SupportsIndex],
453+
values: types.Union[types.Iterable[HT], HT],
368454
) -> None:
455+
"""
456+
Sets the item(s) at the specified index/indices, ensuring uniqueness.
457+
458+
Args:
459+
indices (types.Union[slice, types.SupportsIndex]): The index or
460+
slice to set the value(s) at.
461+
values (types.Union[types.Iterable[HT], HT]): The value(s) to set.
462+
463+
Raises:
464+
RuntimeError: If `on_duplicate` is 'ignore' and setting slices.
465+
ValueError: If the value(s) are duplicates and `on_duplicate` is
466+
set to 'raise'.
467+
"""
369468
if isinstance(indices, slice):
370469
values = types.cast(types.Iterable[HT], values)
371470
if self.on_duplicate == 'ignore':
@@ -394,8 +493,15 @@ def __setitem__(
394493
)
395494

396495
def __delitem__(
397-
self, index: types.Union[types.SupportsIndex, slice]
496+
self, index: types.Union[types.SupportsIndex, slice]
398497
) -> None:
498+
"""
499+
Deletes the item(s) at the specified index/indices.
500+
501+
Args:
502+
index (types.Union[types.SupportsIndex, slice]): The index or slice
503+
to delete the item(s) at.
504+
"""
399505
if isinstance(index, slice):
400506
for value in self[index]:
401507
self._set.remove(value)
@@ -408,28 +514,49 @@ def __delitem__(
408514
# Type hinting `collections.deque` does not work consistently between Python
409515
# runtime, mypy and pyright currently so we have to ignore the errors
410516
class SliceableDeque(types.Generic[T], collections.deque): # type: ignore
517+
"""
518+
A deque that supports slicing and enhanced equality checks.
519+
520+
Methods:
521+
__getitem__(index: types.Union[types.SupportsIndex, slice]) ->
522+
types.Union[T, 'SliceableDeque[T]']:
523+
Returns the item or slice at the given index.
524+
__eq__(other: types.Any) -> bool:
525+
Checks equality with another object, allowing for comparison with
526+
lists, tuples, and sets.
527+
pop(index: int = -1) -> T:
528+
Removes and returns the item at the given index. Only supports
529+
index 0 and the last index.
530+
"""
531+
411532
@typing.overload
412-
def __getitem__(self, index: types.SupportsIndex) -> T:
413-
...
533+
def __getitem__(self, index: types.SupportsIndex) -> T: ...
414534

415535
@typing.overload
416-
def __getitem__(self, index: slice) -> 'SliceableDeque[T]':
417-
...
536+
def __getitem__(self, index: slice) -> 'SliceableDeque[T]': ...
418537

419538
def __getitem__(
420-
self, index: types.Union[types.SupportsIndex, slice]
539+
self, index: types.Union[types.SupportsIndex, slice]
421540
) -> types.Union[T, 'SliceableDeque[T]']:
422541
"""
423542
Return the item or slice at the given index.
424543
425-
>>> d = SliceableDeque[int]([1, 2, 3, 4, 5])
426-
>>> d[1:4]
427-
SliceableDeque([2, 3, 4])
544+
Args:
545+
index (types.Union[types.SupportsIndex, slice]): The index or
546+
slice to retrieve.
428547
429-
>>> d = SliceableDeque[str](['a', 'b', 'c'])
430-
>>> d[-2:]
431-
SliceableDeque(['b', 'c'])
548+
Returns:
549+
types.Union[T, 'SliceableDeque[T]']: The item or slice at the
550+
given index.
432551
552+
Examples:
553+
>>> d = SliceableDeque[int]([1, 2, 3, 4, 5])
554+
>>> d[1:4]
555+
SliceableDeque([2, 3, 4])
556+
557+
>>> d = SliceableDeque[str](['a', 'b', 'c'])
558+
>>> d[-2:]
559+
SliceableDeque(['b', 'c'])
433560
"""
434561
if isinstance(index, slice):
435562
start, stop, step = index.indices(len(self))
@@ -438,7 +565,16 @@ def __getitem__(
438565
return types.cast(T, super().__getitem__(index))
439566

440567
def __eq__(self, other: types.Any) -> bool:
441-
# Allow for comparison with a list or tuple
568+
"""
569+
Checks equality with another object, allowing for comparison with
570+
lists, tuples, and sets.
571+
572+
Args:
573+
other (types.Any): The object to compare with.
574+
575+
Returns:
576+
bool: True if the objects are equal, False otherwise.
577+
"""
442578
if isinstance(other, list):
443579
return list(self) == other
444580
elif isinstance(other, tuple):
@@ -449,8 +585,27 @@ def __eq__(self, other: types.Any) -> bool:
449585
return super().__eq__(other)
450586

451587
def pop(self, index: int = -1) -> T:
452-
# We need to allow for an index but a deque only allows the removal of
453-
# the first or last item.
588+
"""
589+
Removes and returns the item at the given index. Only supports index 0
590+
and the last index.
591+
592+
Args:
593+
index (int, optional): The index of the item to remove. Defaults to
594+
-1.
595+
596+
Returns:
597+
T: The removed item.
598+
599+
Raises:
600+
IndexError: If the index is not 0 or the last index.
601+
602+
Examples:
603+
>>> d = SliceableDeque([1, 2, 3])
604+
>>> d.pop(0)
605+
1
606+
>>> d.pop()
607+
3
608+
"""
454609
if index == 0:
455610
return typing.cast(T, super().popleft())
456611
elif index in {-1, len(self) - 1}:

python_utils/converters.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
"""
2+
This module provides utility functions for type conversion.
3+
4+
Functions:
5+
- to_int: Convert a string to an integer with optional regular expression
6+
matching.
7+
- to_float: Convert a string to a float with optional regular expression
8+
matching.
9+
- to_unicode: Convert objects to Unicode strings.
10+
- to_str: Convert objects to byte strings.
11+
- scale_1024: Scale a number down to a suitable size based on powers of
12+
1024.
13+
- remap: Remap a value from one range to another.
14+
"""
115
from __future__ import annotations
216

317
import decimal

0 commit comments

Comments
 (0)