Skip to content

Commit 579d0aa

Browse files
committed
Bump minimum Python version to 3.11
Python 3.10 enters security-only phase in April 2026 and reaches EOL in October 2026. Raise minimum to 3.11 in pyproject.toml, update the ruff target-version, and adopt datetime.UTC (py3.11+) in place of timezone.utc throughout.
1 parent 22e9c14 commit 579d0aa

8 files changed

Lines changed: 32 additions & 46 deletions

File tree

.python-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.10
1+
3.11

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "slackcli"
33
version = "0.1.0"
44
description = "A command-line interface for Slack"
55
readme = "README.md"
6-
requires-python = ">=3.10"
6+
requires-python = ">=3.11"
77
dependencies = [
88
"certifi>=2024.0.0",
99
"rich>=14.3.2",
@@ -29,7 +29,7 @@ packages = ["src/slackcli"]
2929

3030
[tool.ruff]
3131
line-length = 120
32-
target-version = "py310"
32+
target-version = "py311"
3333

3434
[tool.ruff.lint]
3535
select = ["E", "F", "I", "UP", "B", "C4", "SIM"]

src/slackcli/commands/pins.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from datetime import datetime, timezone
5+
from datetime import UTC, datetime
66
from typing import TYPE_CHECKING, Annotated, Any
77

88
import typer
@@ -56,7 +56,7 @@ def _format_pinned_item(
5656
if msg_ts:
5757
try:
5858
ts_float = float(msg_ts)
59-
dt = datetime.fromtimestamp(ts_float, tz=timezone.utc)
59+
dt = datetime.fromtimestamp(ts_float, tz=UTC)
6060
datetime_str = dt.strftime("%Y-%m-%d %H:%M:%S")
6161
except (ValueError, OSError):
6262
datetime_str = msg_ts
@@ -170,7 +170,7 @@ def list_pins(
170170
pinned_at = ""
171171
if pin["pinned_at"]:
172172
try:
173-
dt = datetime.fromtimestamp(pin["pinned_at"], tz=timezone.utc)
173+
dt = datetime.fromtimestamp(pin["pinned_at"], tz=UTC)
174174
pinned_at = f" on {dt.strftime('%Y-%m-%d')}"
175175
except (ValueError, OSError):
176176
pass

src/slackcli/commands/search.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from datetime import datetime, timezone
5+
from datetime import UTC, datetime
66
from typing import TYPE_CHECKING, Annotated, Any
77

88
import typer
@@ -169,7 +169,7 @@ def output_search_messages_text(
169169

170170
# Parse timestamp
171171
try:
172-
dt = datetime.fromtimestamp(float(ts), tz=timezone.utc)
172+
dt = datetime.fromtimestamp(float(ts), tz=UTC)
173173
date_str = dt.strftime("%Y-%m-%d %H:%M")
174174
except (ValueError, OSError):
175175
date_str = ts
@@ -222,7 +222,7 @@ def output_search_files_text(
222222

223223
# Format date
224224
try:
225-
dt = datetime.fromtimestamp(created, tz=timezone.utc)
225+
dt = datetime.fromtimestamp(created, tz=UTC)
226226
date_str = dt.strftime("%Y-%m-%d %H:%M")
227227
except (ValueError, OSError):
228228
date_str = str(created)

src/slackcli/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import re
1111
from collections.abc import Callable
1212
from dataclasses import dataclass, field
13-
from datetime import datetime, timezone
13+
from datetime import UTC, datetime
1414
from typing import Any
1515

1616
# Type alias for message text extraction and mention resolution functions
@@ -149,7 +149,7 @@ class Message:
149149
def datetime(self) -> datetime | None:
150150
"""Get the message timestamp as a datetime object."""
151151
try:
152-
return datetime.fromtimestamp(float(self.ts), tz=timezone.utc)
152+
return datetime.fromtimestamp(float(self.ts), tz=UTC)
153153
except (ValueError, OSError):
154154
return None
155155

src/slackcli/time_utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from __future__ import annotations
88

99
import re
10-
from datetime import datetime, timedelta, timezone
10+
from datetime import UTC, datetime, timedelta
1111

1212

1313
def parse_relative_time(spec: str, base: datetime | None = None) -> timedelta | None:
@@ -92,7 +92,7 @@ def parse_time_spec(spec: str) -> datetime:
9292
ValueError: If spec cannot be parsed.
9393
"""
9494
spec_lower = spec.strip().lower()
95-
now = datetime.now(tz=timezone.utc)
95+
now = datetime.now(tz=UTC)
9696

9797
# Keywords
9898
if spec_lower == "today":
@@ -112,7 +112,7 @@ def parse_time_spec(spec: str) -> datetime:
112112
if dt is not None:
113113
# If no timezone, assume UTC
114114
if dt.tzinfo is None:
115-
dt = dt.replace(tzinfo=timezone.utc)
115+
dt = dt.replace(tzinfo=UTC)
116116
return dt
117117

118118
raise ValueError(f"Cannot parse time specification: {spec}")
@@ -136,7 +136,7 @@ def parse_date_spec(spec: str) -> str:
136136
ValueError: If spec cannot be parsed.
137137
"""
138138
spec_lower = spec.strip().lower()
139-
now = datetime.now(tz=timezone.utc)
139+
now = datetime.now(tz=UTC)
140140

141141
# Keywords
142142
if spec_lower == "today":

tests/test_time_utils.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from datetime import datetime, timedelta, timezone
5+
from datetime import UTC, datetime, timedelta
66

77
import pytest
88

@@ -72,39 +72,39 @@ class TestParseTimeSpec:
7272
def test_today_keyword(self) -> None:
7373
"""Test 'today' keyword returns start of today in UTC."""
7474
result = parse_time_spec("today")
75-
now = datetime.now(tz=timezone.utc)
75+
now = datetime.now(tz=UTC)
7676
expected = now.replace(hour=0, minute=0, second=0, microsecond=0)
7777
assert result == expected
7878

7979
def test_yesterday_keyword(self) -> None:
8080
"""Test 'yesterday' keyword returns start of yesterday in UTC."""
8181
result = parse_time_spec("yesterday")
82-
now = datetime.now(tz=timezone.utc)
82+
now = datetime.now(tz=UTC)
8383
expected = (now - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
8484
assert result == expected
8585

8686
def test_now_keyword(self) -> None:
8787
"""Test 'now' keyword returns current time in UTC."""
88-
before = datetime.now(tz=timezone.utc)
88+
before = datetime.now(tz=UTC)
8989
result = parse_time_spec("now")
90-
after = datetime.now(tz=timezone.utc)
90+
after = datetime.now(tz=UTC)
9191
assert before <= result <= after
9292

9393
def test_relative_times(self) -> None:
9494
"""Test relative time specifications."""
95-
before = datetime.now(tz=timezone.utc)
95+
before = datetime.now(tz=UTC)
9696
result = parse_time_spec("7d")
97-
after = datetime.now(tz=timezone.utc)
97+
after = datetime.now(tz=UTC)
9898

9999
expected_min = before - timedelta(days=7)
100100
expected_max = after - timedelta(days=7)
101101
assert expected_min <= result <= expected_max
102102

103103
def test_relative_hours(self) -> None:
104104
"""Test relative hour specifications."""
105-
before = datetime.now(tz=timezone.utc)
105+
before = datetime.now(tz=UTC)
106106
result = parse_time_spec("2h")
107-
after = datetime.now(tz=timezone.utc)
107+
after = datetime.now(tz=UTC)
108108

109109
expected_min = before - timedelta(hours=2)
110110
expected_max = after - timedelta(hours=2)
@@ -113,19 +113,19 @@ def test_relative_hours(self) -> None:
113113
def test_iso_date(self) -> None:
114114
"""Test ISO date parsing."""
115115
result = parse_time_spec("2024-01-15")
116-
expected = datetime(2024, 1, 15, 0, 0, 0, tzinfo=timezone.utc)
116+
expected = datetime(2024, 1, 15, 0, 0, 0, tzinfo=UTC)
117117
assert result == expected
118118

119119
def test_iso_datetime_with_t(self) -> None:
120120
"""Test ISO datetime parsing with T separator."""
121121
result = parse_time_spec("2024-01-15T10:30:00")
122-
expected = datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
122+
expected = datetime(2024, 1, 15, 10, 30, 0, tzinfo=UTC)
123123
assert result == expected
124124

125125
def test_iso_datetime_with_space(self) -> None:
126126
"""Test ISO datetime parsing with space separator."""
127127
result = parse_time_spec("2024-01-15 10:30:00")
128-
expected = datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
128+
expected = datetime(2024, 1, 15, 10, 30, 0, tzinfo=UTC)
129129
assert result == expected
130130

131131
def test_case_insensitive_keywords(self) -> None:
@@ -154,27 +154,27 @@ class TestParseDateSpec:
154154
def test_today_keyword(self) -> None:
155155
"""Test 'today' keyword returns today's date."""
156156
result = parse_date_spec("today")
157-
now = datetime.now(tz=timezone.utc)
157+
now = datetime.now(tz=UTC)
158158
assert result == now.strftime("%Y-%m-%d")
159159

160160
def test_yesterday_keyword(self) -> None:
161161
"""Test 'yesterday' keyword returns yesterday's date."""
162162
result = parse_date_spec("yesterday")
163-
now = datetime.now(tz=timezone.utc)
163+
now = datetime.now(tz=UTC)
164164
expected = (now - timedelta(days=1)).strftime("%Y-%m-%d")
165165
assert result == expected
166166

167167
def test_relative_days(self) -> None:
168168
"""Test relative day specifications."""
169169
result = parse_date_spec("7d")
170-
now = datetime.now(tz=timezone.utc)
170+
now = datetime.now(tz=UTC)
171171
expected = (now - timedelta(days=7)).strftime("%Y-%m-%d")
172172
assert result == expected
173173

174174
def test_relative_30_days(self) -> None:
175175
"""Test 30 days ago."""
176176
result = parse_date_spec("30d")
177-
now = datetime.now(tz=timezone.utc)
177+
now = datetime.now(tz=UTC)
178178
expected = (now - timedelta(days=30)).strftime("%Y-%m-%d")
179179
assert result == expected
180180

uv.lock

Lines changed: 1 addition & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)