Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
8c3bd5f
2026-01-18 09:15:51 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
cecec70
2026-01-18 09:25:50 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
88eb1c4
2026-01-18 09:28:52 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
e795ae7
2026-01-18 09:29:43 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
d4701b8
2026-01-18 09:34:47 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
cd3b7b3
2026-01-18 09:36:01 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
3dec052
2026-01-18 09:36:17 (Sun) > DW-Mac > derekwan
web-flow Jan 18, 2026
3a7ab09
2026-01-21 14:41:04 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
fb4be2a
2026-01-21 14:58:09 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
0f7a045
2026-01-21 14:58:59 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
133eae9
2026-01-21 14:59:36 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
c6a0ff2
2026-01-21 15:05:29 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
1293b2f
2026-01-21 15:06:41 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
6e4a8ec
2026-01-21 15:08:26 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
3121242
2026-01-21 15:11:46 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
057bebb
2026-01-21 15:17:55 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
5636a9a
2026-01-21 15:21:33 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
673488e
2026-01-21 15:22:11 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
298cafc
2026-01-21 15:25:08 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
127cf41
2026-01-21 15:25:31 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
27b2cf2
2026-01-21 15:25:45 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
1d8a747
2026-01-21 15:25:57 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
fc66f32
2026-01-21 15:29:13 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
c1b97b1
2026-01-21 15:30:01 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
25c551d
2026-01-21 15:34:42 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
1f8897f
2026-01-21 15:35:07 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
bd824e1
2026-01-21 15:42:18 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
2d03c41
2026-01-21 15:48:44 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
25c0984
2026-01-21 15:50:54 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
2bad3e9
2026-01-21 15:51:20 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
bc5cfaa
2026-01-21 15:53:13 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
24393cf
2026-01-21 15:53:34 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
9fac4b0
2026-01-21 15:55:52 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
752e491
2026-01-21 15:57:36 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
4a95265
2026-01-21 15:58:05 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
88de179
2026-01-21 15:58:08 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
1d511e3
2026-01-21 16:01:14 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
96b6510
2026-01-21 16:01:37 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
8394350
2026-01-21 16:11:48 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
a4501dc
2026-01-21 16:11:52 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
82fd0a5
2026-01-21 16:11:56 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
df5e552
2026-01-21 16:11:58 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
683bcb2
2026-01-21 16:12:22 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
8fab8c5
2026-01-21 16:12:26 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
672b805
2026-01-21 16:13:00 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
61c78ef
2026-01-21 16:13:26 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
70e020e
2026-01-21 16:15:01 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
b00e41b
2026-01-21 16:15:27 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
a9381ae
2026-01-21 16:23:22 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
b76b039
2026-01-21 16:23:48 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
d827b5a
2026-01-21 16:24:31 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
eb00bca
2026-01-21 16:24:57 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
08bc179
2026-01-21 16:25:43 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
f1451fe
2026-01-21 16:27:04 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
5cc5735
2026-01-21 16:27:24 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
811306e
2026-01-21 16:27:41 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
00a15ed
2026-01-21 16:28:54 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
7067c09
2026-01-21 16:29:53 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
e1a069c
2026-01-21 16:32:41 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
eae5b29
2026-01-21 16:33:34 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
71300f0
2026-01-21 16:34:30 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
bdf120e
2026-01-21 16:35:04 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
e0e052e
2026-01-21 16:38:10 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
36c11ab
2026-01-21 17:29:52 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
48a2c87
2026-01-21 17:32:27 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
daa250d
2026-01-21 17:36:58 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
445f112
2026-01-21 17:37:01 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
4644baa
2026-01-21 17:39:29 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
936cdb4
2026-01-21 17:39:49 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
15ae7b5
2026-01-21 17:40:13 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
2093c91
2026-01-21 17:40:46 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
859650e
2026-01-21 17:41:06 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
aa2f677
2026-01-21 17:41:27 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
617802a
2026-01-21 17:41:31 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
39ef97b
2026-01-21 17:41:44 (Wed) > DW-Mac > derekwan
web-flow Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/dycw/pre-commit-hooks
rev: 0.14.108
rev: 0.14.109
hooks:
- id: add-hooks
args:
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"tzlocal>=5.3.1",
"whenever>=0.9.5",
]
cryptography = ["cryptography>=46.0.3"]
cvxpy = ["cvxpy>=1.7.5"]
dataclasses-test = ["orjson>=3.11.5", "polars>=1.37.1"]
dev = [
Expand Down Expand Up @@ -116,7 +115,7 @@
name = "dycw-utilities"
readme = "README.md"
requires-python = ">= 3.12"
version = "0.184.6"
version = "0.184.7"

[project.entry-points.pytest11]
pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
Expand Down
86 changes: 0 additions & 86 deletions scripts/run_tests.py

This file was deleted.

2 changes: 1 addition & 1 deletion src/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from utilities.asyncio import sleep
from utilities.constants import IS_CI, IS_CI_AND_NOT_LINUX, MINUTE
from utilities.contextlib import enhanced_context_manager
from utilities.core import TemporaryDirectory, TemporaryFile
from utilities.pytest import skipif_ci
from utilities.re import ExtractGroupError, extract_group
from utilities.tempfile import TemporaryDirectory, TemporaryFile
from utilities.whenever import get_now_local_plain

if TYPE_CHECKING:
Expand Down
1 change: 1 addition & 0 deletions src/tests/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from __future__ import annotations
172 changes: 172 additions & 0 deletions src/tests/core/test_builtins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
from __future__ import annotations

import sys
from collections.abc import Callable
from functools import cache, lru_cache, partial, wraps
from itertools import chain
from operator import neg
from types import NoneType
from typing import TYPE_CHECKING, Any

from hypothesis import given
from hypothesis.strategies import (
DataObject,
data,
integers,
lists,
none,
permutations,
sampled_from,
)
from pytest import mark, param, raises

from utilities.core import (
MaxNullableError,
MinNullableError,
get_class,
get_class_name,
get_func_name,
identity,
max_nullable,
min_nullable,
)
from utilities.errors import ImpossibleCaseError

if TYPE_CHECKING:
from collections.abc import Callable, Iterable


class TestGetClass:
@mark.parametrize(
("obj", "expected"), [param(None, NoneType), param(NoneType, NoneType)]
)
def test_main(self, *, obj: Any, expected: type[Any]) -> None:
assert get_class(obj) is expected


class TestGetClassName:
def test_class(self) -> None:
class Example: ...

assert get_class_name(Example) == "Example"

def test_instance(self) -> None:
class Example: ...

assert get_class_name(Example()) == "Example"

def test_qual(self) -> None:
assert (
get_class_name(ImpossibleCaseError, qual=True)
== "utilities.errors.ImpossibleCaseError"
)


class TestGetFuncName:
@mark.parametrize(
("func", "expected"),
[
param(identity, "identity"),
param(lambda x: x, "<lambda>"), # pyright: ignore[reportUnknownLambdaType]
param(len, "len"),
param(neg, "neg"),
param(object.__init__, "object.__init__"),
param(object.__str__, "object.__str__"),
param(repr, "repr"),
param(str, "str"),
param(str.join, "str.join"),
param(sys.exit, "exit"),
],
)
def test_main(self, *, func: Callable[..., Any], expected: str) -> None:
assert get_func_name(func) == expected

def test_cache(self) -> None:
@cache
def cache_func(x: int, /) -> int:
return x

assert get_func_name(cache_func) == "cache_func"

def test_decorated(self) -> None:
@wraps(identity)
def wrapped[T](x: T, /) -> T:
return identity(x)

assert get_func_name(wrapped) == "identity"

def test_lru_cache(self) -> None:
@lru_cache
def lru_cache_func(x: int, /) -> int:
return x

assert get_func_name(lru_cache_func) == "lru_cache_func"

def test_object(self) -> None:
class Example:
def __call__[T](self, x: T, /) -> T:
return identity(x)

assert get_func_name(Example()) == "Example"

def test_obj_method(self) -> None:
class Example:
def obj_method[T](self, x: T) -> T:
return identity(x)

assert get_func_name(Example().obj_method) == "Example.obj_method"

def test_obj_classmethod(self) -> None:
class Example:
@classmethod
def obj_classmethod[T](cls: T) -> T:
return identity(cls)

assert get_func_name(Example.obj_classmethod) == "Example.obj_classmethod"

def test_obj_staticmethod(self) -> None:
class Example:
@staticmethod
def obj_staticmethod[T](x: T) -> T:
return identity(x)

assert get_func_name(Example.obj_staticmethod) == "Example.obj_staticmethod"

def test_partial(self) -> None:
assert get_func_name(partial(identity)) == "identity"


class TestMinMaxNullable:
@given(
data=data(),
values=lists(integers(), min_size=1),
nones=lists(none()),
case=sampled_from([(min_nullable, min), (max_nullable, max)]),
)
def test_main(
self,
*,
data: DataObject,
values: list[int],
nones: list[None],
case: tuple[
Callable[[Iterable[int | None]], int], Callable[[Iterable[int]], int]
],
) -> None:
func_nullable, func_builtin = case
values_use = data.draw(permutations(list(chain(values, nones))))
result = func_nullable(values_use)
expected = func_builtin(values)
assert result == expected

@mark.parametrize("func", [param(min_nullable), param(max_nullable)])
def test_default(self, *, func: Callable[..., int]) -> None:
assert func([], default=True) is True

def test_error_min(self) -> None:
with raises(MinNullableError, match=r"Minimum of \[\] is undefined"):
_ = min_nullable([])

def test_error_max(self) -> None:
with raises(MaxNullableError, match=r"Maximum of \[\] is undefined"):
max_nullable([])
33 changes: 33 additions & 0 deletions src/tests/core/test_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from pytest import mark, param

from utilities.constants import sentinel
from utilities.core import is_none, is_not_none, is_sentinel

if TYPE_CHECKING:
from collections.abc import Callable


class TestIsNoneAndIsNotNone:
@mark.parametrize(
("func", "obj", "expected"),
[
param(is_none, None, True),
param(is_none, 0, False),
param(is_not_none, None, False),
param(is_not_none, 0, True),
],
)
def test_main(
self, *, func: Callable[[Any], bool], obj: Any, expected: bool
) -> None:
assert func(obj) is expected


class TestIsSentinel:
@mark.parametrize(("obj", "expected"), [param(None, False), param(sentinel, True)])
def test_main(self, *, obj: Any, expected: bool) -> None:
assert is_sentinel(obj) is expected
Loading
Loading