Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
17766df
Move utils.py to utils/
Paillat-dev May 29, 2025
195e2a0
:fire: Remove `filter_params`
Paillat-dev May 29, 2025
fafa149
:recycle: Merge `time_snowflake` and `generate_snowflake`, move `basi…
Paillat-dev May 29, 2025
e2d8eb1
:recycle: Merge `time_snowflake` and `generate_snowflake`, move `basi…
Paillat-dev May 29, 2025
86bd168
:fire: Remove `utils.sleep_until`
Paillat-dev May 29, 2025
23c2c49
chore: Start migration to uv & ruff & hatch (#4)
Paillat-dev May 29, 2025
97ccda5
Setup CHANGELOG.md (#6)
Paillat-dev May 29, 2025
f2a4eb6
chore: update docs workflows to use 'uv' for dependency management (#33)
Paillat-dev May 30, 2025
40e20c0
:fire: Move stuff to private
Paillat-dev May 31, 2025
6e5ccb5
Merge branch 'master' into refactor-utils
Paillat-dev May 31, 2025
1da5de0
:refactor: move parse_time function to private utils and update refer…
Paillat-dev Jun 24, 2025
88180b2
:memo: update CHANGELOG to reflect utility function changes
Paillat-dev Jun 24, 2025
b5c8a58
:art: Format
Paillat-dev Jun 24, 2025
0cdcb31
:recycle: move deprecation utilities to private utils and update refe…
Paillat-dev Jun 24, 2025
09a11a9
:recycle: move snowflake_time function to public.py
Paillat-dev Jun 24, 2025
4a2753c
:recycle: move oauth_url and Undefined class to public.py; update imp…
Paillat-dev Jun 24, 2025
ca89539
Merge branch 'master' into refactor-utils
Paillat-dev Jun 24, 2025
1cf53ab
:memo: remove deprecated utility functions from documentation
Paillat-dev Jun 24, 2025
2ac8152
:memo: remove (re)moved utility functions from documentation
Paillat-dev Jun 24, 2025
24a367c
:memo: add utils.resolve_template to changelog and remove from docume…
Paillat-dev Jun 24, 2025
0c6270a
:bug: fix import path for warn_deprecated utility function
Paillat-dev Jun 24, 2025
84ff9df
:refactor: reorganize utility function imports and move evaluate_anno…
Paillat-dev Jun 24, 2025
8474f05
:recycle: update import paths for utility functions to use relative i…
Paillat-dev Jun 24, 2025
c8ddcb5
:recycle: move delay_task function to private
Paillat-dev Jul 7, 2025
8a02d54
:recycle: removed `utils.get` in favor of `utils.find`
Paillat-dev Jul 7, 2025
39aef0f
:recycle: removed `utils._unique`
Paillat-dev Jul 7, 2025
6b0379c
:recycle: move `async_all` to private
Paillat-dev Jul 7, 2025
cc8f27f
:recycle: move `maybe_coroutine` to private
Paillat-dev Jul 7, 2025
1d70dec
:recycle: rename `maybe_coroutine` to `maybe_awaitable`
Paillat-dev Jul 7, 2025
b2b8625
:recycle: move `sane_wait_for` to private
Paillat-dev Jul 7, 2025
d0a094d
:recycle: move `format_dt` to public
Paillat-dev Jul 7, 2025
fb554fd
:recycle: remove `as_chunks` function
Paillat-dev Jul 7, 2025
1a551cb
:memo: update `utils.sleep_until` and `utils.parse_time` changelog to…
Paillat-dev Jul 7, 2025
295ed46
:recycle: move `compute_timedelta` function
Paillat-dev Jul 7, 2025
4afe54a
:recycle: move `valid_icon_size` to `asset.py`
Paillat-dev Jul 7, 2025
a021450
:recycle: refactor `utils.get` to `utils.find` across multiple files
Paillat-dev Jul 7, 2025
c6f7914
:recycle: refactor markdown and mention handling functions in `__init…
Paillat-dev Jul 7, 2025
c29cdc2
:recycle: move SnowflakeList to `private.py`
Paillat-dev Jul 7, 2025
6c57d5e
:recycle: move `find` function from `__init__.py` to `public.py`
Paillat-dev Jul 7, 2025
d079c7c
:recycle: move `copy_doc` to private
Paillat-dev Jul 7, 2025
611c7c7
:recycle: refactor `get` to `find` in onboarding and sticker modules
Paillat-dev Jul 7, 2025
cd4e436
:bug: fix `copy_doc` decorator usage in context.py
Paillat-dev Jul 7, 2025
ad777e4
Merge branch 'master' into refactor-utils
Paillat-dev Jul 7, 2025
aec45a2
:recycle: move SequenceProxy to private module
Paillat-dev Jul 7, 2025
9755fe8
:recycle: move cached_slot_property to private
Paillat-dev Jul 7, 2025
006a7f8
:rotating_light: add noqa comments to prevent linting errors
Paillat-dev Jul 7, 2025
d5263c7
:recycle: move get_slots function to private module
Paillat-dev Jul 7, 2025
44f753e
:recycle: refactor JSON serialization functions to private module
Paillat-dev Jul 7, 2025
c961e36
:recycle: replace custom cached_property implementation with functool…
Paillat-dev Jul 7, 2025
a4bda5f
:pencil2: fix typo in CHANGELOG-V3.md
Paillat-dev Jul 7, 2025
b81d2d5
:heavy_minus_sign: remove unused dependencies from pyproject.toml and…
Paillat-dev Jul 7, 2025
a84e06d
:coffin: remove test.py
Paillat-dev Jul 7, 2025
330161c
:recycle: remove duplicate import of raw_mentions in __init__.py
Paillat-dev Jul 7, 2025
b3f919e
:bug: fix raw_role_mentions import in utils/__init__.py
Paillat-dev Jul 8, 2025
8d76921
:bug: fix dictionary access in test_typing_annotated.py to be type safe
Paillat-dev Jul 8, 2025
77bf9c3
:coffin: Remove dead tests
Paillat-dev Jul 8, 2025
c7a7a77
Merge branch 'refactor-utils' into feat/tests
Paillat-dev Jul 8, 2025
d93aa3d
:white_check_mark: add tox configuration and update dependencies in p…
Paillat-dev Jul 8, 2025
15449c6
:coffin: remove unused reveal_type calls in private.py
Paillat-dev Jul 8, 2025
a3b6eb9
:white_check_mark: add test workflow to GitHub Actions for multiple O…
Paillat-dev Jul 8, 2025
f3a8f1e
:pushpin: move pytest and pytest-asyncio to test dependencies in pypr…
Paillat-dev Jul 8, 2025
e2a4367
:white_check_mark: add unit tests for markdown related utils
Paillat-dev Jul 8, 2025
414e0fe
:white_check_mark: add unit tests for format_dt
Paillat-dev Jul 8, 2025
3870d73
:white_check_mark: add unit tests for snowflake generation and time c…
Paillat-dev Jul 8, 2025
96e5cce
:white_check_mark: add unit tests for discord.utils.find functionality
Paillat-dev Jul 8, 2025
352d1b6
:page_facing_up: add license headers to test files
Paillat-dev Jul 8, 2025
e58e310
:bug: fix test for find function to use == instead of is
Paillat-dev Jul 8, 2025
0c9272d
:fire: Duplicate `Iterable` import
Paillat-dev Aug 4, 2025
2b981dd
Apply suggestion from @Lumabots
Paillat-dev Aug 4, 2025
488c4de
Merge branch 'master' into refactor-utils
Paillat-dev Aug 4, 2025
df155d6
:fire: Simplify imports
Paillat-dev Aug 6, 2025
7d92da0
:fire: Simplify imports number 2
Paillat-dev Aug 6, 2025
856853f
:fire: Simplify imports number 3 omg this is amazing
Paillat-dev Aug 6, 2025
e32ba97
:fire: Simplify imports number 4 omg this is amazing yee
Paillat-dev Aug 6, 2025
d1be746
:fire: Simplify imports number 5
Paillat-dev Aug 6, 2025
bd59784
Update discord/ext/commands/converter.py
Paillat-dev Aug 6, 2025
34d5fde
Apply suggestions from code review
Paillat-dev Aug 6, 2025
5ef78ca
Update discord/state.py
Paillat-dev Aug 6, 2025
0acef26
Update discord/state.py
Paillat-dev Aug 6, 2025
c7db59c
:recycle: Remove _ prefix in from and to json
Paillat-dev Aug 6, 2025
0ca346d
:recycle: Make import less weird
Paillat-dev Aug 6, 2025
2a0427c
Update utils.po
Paillat-dev Aug 6, 2025
11e311d
empty commit
Paillat-dev Aug 26, 2025
32ed673
Update discord/poll.py
Paillat-dev Aug 26, 2025
0df56df
Update discord/utils/private.py
Paillat-dev Aug 26, 2025
cb5b0cd
Merge branch 'master' into refactor-utils
Paillat-dev Aug 31, 2025
51df432
:bug: Fix broken import after merge
Paillat-dev Aug 31, 2025
6ec99f2
:bug: Fix emojis.json path
Paillat-dev Aug 31, 2025
68a0fe3
:bug: Fix broken imports
Paillat-dev Aug 31, 2025
3dd1216
:art: Format stuff
Paillat-dev Aug 31, 2025
0d27afe
:bug: Fix import
Paillat-dev Sep 3, 2025
e202d7e
Merge branch 'master' into refactor-utils
Paillat-dev Sep 3, 2025
049517e
chore(deps): upgrade pre-commit hook astral-sh/ruff-pre-commit to v0.…
renovate[bot] Sep 3, 2025
580435a
Merge branch 'refactor-utils' into feat/tests
Paillat-dev Sep 3, 2025
052b54c
Merge branch 'master' into feat/tests
Paillat-dev Sep 3, 2025
26b4fe3
Merge branch 'master' into feat/tests
Paillat-dev Sep 4, 2025
55c49b4
Merge branch 'master' into feat/tests
Paillat-dev Sep 4, 2025
b05b56f
Apply suggestions from code review
Paillat-dev Sep 4, 2025
6ea3e05
Merge branch 'master' into feat/tests
Paillat-dev Sep 8, 2025
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 .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
* @Pycord-Development/pycord-next-contributors
* @Pycord-Development/pycord-next-contributors

/.github @Lulalaby
/crowdin.yml @Pycord-Development/maintain-translations
27 changes: 27 additions & 0 deletions .github/workflows/lib-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
uv run codespell --ignore-words-list="groupt,nd,ot,ro,falsy,BU" \
--exclude-file=".github/workflows/codespell.yml"
ruff:
if: ${{ github.event_name != 'schedule' }}
runs-on: ubuntu-latest
steps:
- name: "Checkout Repository"
Expand Down Expand Up @@ -93,3 +94,29 @@ jobs:
run: mkdir -p -v .mypy_cache
- name: "Run mypy"
run: uv run mypy --non-interactive discord/
tests:
if: ${{ github.event_name != 'schedule' }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.13', '3.12', '3.11', '3.10']
steps:
- name: "Checkout Repository"
uses: actions/checkout@v4

- name: "Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: "Install uv"
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Sync dependencies
run: uv sync --no-python-downloads --group dev

- name: "Run tests"
run: uv run tox
5 changes: 2 additions & 3 deletions discord/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,10 @@
raw_role_mentions,
remove_markdown,
escape_markdown,
DISCORD_EPOCH,
UNICODE_EMOJIS,
)

DISCORD_EPOCH = 1420070400000


__all__ = (
"oauth_url",
"snowflake_time",
Expand All @@ -69,6 +67,7 @@
"basic_autocomplete",
"Undefined",
"MISSING",
"DISCORD_EPOCH",
"UNICODE_EMOJIS",
)

Expand Down
1 change: 0 additions & 1 deletion discord/utils/private.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
Union,
Coroutine,
Awaitable,
reveal_type,
Generic,
Sequence,
Iterator,
Expand Down
30 changes: 26 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,17 @@ dev = [
"codespell==2.4.1",
"coverage~=7.8",
"mypy~=1.17.1",
"pre-commit==4.3.0",
"pytest~=8.4.1",
"pytest-asyncio~=0.26.0",
"pre-commit==4.2.0",
"python-dotenv>=1.1.1",
"ruff>=0.11.9",
"ruff>=0.12.12",
"tox>=4.27.0",
"tox-gh>=1.5.0",
"tox-uv>=1.26.1",
]

test = [# Not in `dev` becuase we use tox for testing. Tox will install these dependencies.
"pytest~=8.3.5",
"pytest-asyncio~=0.24.0",
]
ci = [
"pygithub>=2.7.0",
Expand Down Expand Up @@ -318,3 +324,19 @@ ignore_errors = true

[tool.pytest.ini_options]
asyncio_mode = "auto"

[tool.tox]
requires = ["tox>=4"]
env_list = ["3.13", "3.12", "3.11", "3.10"]

[tool.tox.env_run_base]
description = "run unit tests"
commands = [["pytest", { replace = "posargs", default = ["tests"], extend = true }]]
dependency_groups = ["test"]

# GitHub actions
[tool.tox.gh.python]
"3.13" = ["3.13"]
"3.12" = ["3.12"]
"3.11" = ["3.11"]
"3.10" = ["3.10"]
32 changes: 0 additions & 32 deletions tests/helpers.py

This file was deleted.

90 changes: 90 additions & 0 deletions tests/test_find_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
The MIT License (MIT)

Copyright (c) 2021-present Pycord Development

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

import pytest
from discord.utils import find


def is_even(x):
return x % 2 == 0


@pytest.mark.parametrize(
("seq", "predicate", "expected"),
[
([], lambda x: True, None),
([1, 2, 3], lambda x: x > 3, None),
([1, 2, 3], lambda x: x == 1, 1),
([1, 2, 3], lambda x: x == 2, 2),
("abc", lambda c: c == "b", "b"),
((10, 20, 30), lambda x: x == 30, 30),
([None, False, 0], lambda x: x is None, None),
([1, 2, 3, 4], is_even, 2),
],
)
def test_find_basic_parametrized(seq, predicate, expected):
result = find(predicate, seq)
if expected is None:
assert result is None
else:
assert result == expected


def test_find_with_truthy_non_boolean_predicate():
seq = [2, 4, 5, 6]
result = find(lambda x: x % 2, seq)
assert result == 5


def test_find_on_generator_and_stop_early():
def bad_gen():
yield "first"
raise RuntimeError("should not be reached")

assert find(lambda x: x == "first", bad_gen()) == "first"


def test_find_does_not_evaluate_rest():
calls = []

def predicate(x):
calls.append(x)
return x == "stop"

seq = ["go", "stop", "later"]
result = find(predicate, seq)
assert result == "stop"
assert calls == ["go", "stop"]


def test_find_with_set_returns_first_iterated_element():
data = {"a", "b", "c"}
result = find(lambda x: x in data, data)
assert result in data


def test_find_none_predicate():
seq = [42, 43, 44]
result = find(lambda x: True, seq)
assert result == 42
70 changes: 70 additions & 0 deletions tests/test_format_dt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
The MIT License (MIT)

Copyright (c) 2021-present Pycord Development

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

import datetime
import random
import pytest
from discord.utils import format_dt

# Fix seed so that time tests are reproducible
random.seed(42)

ALL_STYLES = ["t", "T", "d", "D", "f", "F", "R", None]

DATETIME_CASES = [
(datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc), 0),
(datetime.datetime(2000, 2, 29, 12, 0, 0, tzinfo=datetime.timezone.utc), 951825600),
(datetime.datetime(1999, 12, 31, 23, 59, 59, tzinfo=datetime.timezone.utc), 946684799),
(datetime.datetime(2023, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc), 1672628645),
(datetime.datetime(2050, 6, 15, 7, 30, 0, tzinfo=datetime.timezone.utc), 2538891000),
]


def random_time():
return datetime.time(
random.randint(0, 23),
random.randint(0, 59),
random.randint(0, 59),
)


@pytest.mark.parametrize(("dt", "expected_ts"), DATETIME_CASES)
@pytest.mark.parametrize("style", ALL_STYLES)
def test_format_dt_formats_datetime(dt, expected_ts, style):
if style is None:
expected = f"<t:{expected_ts}>"
else:
expected = f"<t:{expected_ts}:{style}>"
result = format_dt(dt, style=style)
assert result == expected


@pytest.mark.parametrize("style", ALL_STYLES)
def test_format_dt_formats_time_equivalence(style):
tm = random_time()
today = datetime.datetime.now().date()
result_time = format_dt(tm, style=style)
dt = datetime.datetime.combine(today, tm)
result_dt = format_dt(dt, style=style)
assert result_time == result_dt
Loading
Loading