Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deps: support Python 3.13, drop Python 3.8 support #328

Merged
merged 8 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 12 additions & 12 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ jobs:
linting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: snok/install-poetry@v1
- name: Install dependencies
run: |
Expand All @@ -31,31 +31,31 @@ jobs:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
exclude:
- os: macos-latest
python-version: "3.9"
- os: macos-latest
python-version: "3.10"
- os: macos-latest
python-version: "3.11"
- os: windows-latest
python-version: "3.9"
- os: macos-latest
python-version: "3.12"
- os: windows-latest
python-version: "3.10"
- os: windows-latest
python-version: "3.11"
- os: windows-latest
python-version: "3.12"
runs-on: ${{ matrix.os }}

defaults:
run:
shell: bash

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -67,7 +67,7 @@ jobs:

- name: Load cached venv
id: cached-pip-wheels
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.cache
key: venv-${{ runner.os }}-${{ matrix.python-version }}
Expand All @@ -80,7 +80,7 @@ jobs:
run: |
poetry run pytest --junitxml=test-reports/report-${{ matrix.os }}-${{ matrix.python-version }}.xml -o junit_suite_name=suite-${{ matrix.os }}-${{ matrix.python-version }}

- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: always()
with:
name: report-${{ matrix.os }}-${{ matrix.python-version }}
Expand All @@ -99,7 +99,7 @@ jobs:

steps:
- name: Download Test Result Artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
path: test-reports

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ aioslsk

aioslsk is a Python library for the SoulSeek protocol built on top of asyncio.

Supported Python versions are currently 3.8 - 3.12
Supported Python versions are currently 3.9 - 3.13

You can find the full documentation `here <http://aioslsk.readthedocs.io/>`_

Expand Down
8 changes: 4 additions & 4 deletions docs/source/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ A couple of methods are available to retrieve transfers:

from aioslsk.transfer.model import Transfer

all_transfers = List[Transfer] = client.transfers.transfers
downloads: List[Transfer] = client.transfers.get_downloads()
uploads: List[Transfer] = client.transfers.get_uploads()
all_transfers = list[Transfer] = client.transfers.transfers
downloads: list[Transfer] = client.transfers.get_downloads()
uploads: list[Transfer] = client.transfers.get_uploads()


Events are available to listen for the transfer progress:
Expand Down Expand Up @@ -591,7 +591,7 @@ Example a strategy that places files in a directory containing the current date:
class DatetimeDirectoryStrategy(NamingStrategy):

# Override the apply method
def apply(self, remote_path: str, local_dir: str, local_filename: str) -> Tuple[str, str]:
def apply(self, remote_path: str, local_dir: str, local_filename: str) -> tuple[str, str]:
current_datetime = datetime.now().strftime('%Y-%M-%d')
return os.path.join(local_dir, current_datetime), local_filename

Expand Down
1,247 changes: 675 additions & 572 deletions poetry.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ packages = [{include = "aioslsk", from = "src"}]
keywords = ["soulseek", "p2p", "async"]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Communications :: File Sharing"
]

[tool.poetry.dependencies]
python = ">=3.8,<3.13"
python = ">=3.9,<3.14"
mutagen = "^1.47.0"
aiofiles = ">=22.1,<25.0"
async-upnp-client = ">=0.33,<0.42"
Expand All @@ -31,7 +31,7 @@ typing-extensions = "^4.12.2"
multidict = "^6.0.5"

[tool.poetry.group.dev.dependencies]
flake8 = { version = ">=6,<8", python = ">=3.8.1,<3.13" }
flake8 = { version = ">=6,<8", python = ">=3.9.1,<3.14" }
mypy = "^1.11.2"
no-implicit-optional = "^1.4"
pytest = ">=7.4.2,<9.0.0"
Expand All @@ -40,7 +40,7 @@ pytest-unordered = ">=0.5.2,<0.7.0"
pytest-asyncio = ">=0.21.1,<0.25.0"
sphinx = "^7.0.0"
sphinx-rtd-theme = "^2.0.0"
autodoc-pydantic = { version = "^2.2.0", python = ">=3.8.1,<3.13" }
autodoc-pydantic = { version = "^2.2.0", python = ">=3.9.1,<3.14" }

[tool.poetry.group.tools]
optional = true
Expand Down
3 changes: 1 addition & 2 deletions src/aioslsk/base_manager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from abc import ABC
import asyncio
from typing import List


class BaseManager(ABC):
Expand All @@ -23,7 +22,7 @@ async def start(self):
loading the data but before connecting
"""

async def stop(self) -> List[asyncio.Task]:
async def stop(self) -> list[asyncio.Task]:
"""Cancel all running tasks. The implementation of this method should
simply cancel the task and return, the code calling this method should
be responsible for awaiting the cancellation
Expand Down
4 changes: 2 additions & 2 deletions src/aioslsk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import asyncio
from async_timeout import timeout as atimeout
import logging
from typing import List, Optional
from typing import Optional

from .base_manager import BaseManager
from .commands import BaseCommand, RC, RT
Expand Down Expand Up @@ -82,7 +82,7 @@ def __init__(
self.searches: SearchManager = self.create_search_manager()
self.server_manager: ServerManager = self.create_server_manager()

self.services: List[BaseManager] = [
self.services: list[BaseManager] = [
self.users,
self.rooms,
self.interests,
Expand Down
44 changes: 21 additions & 23 deletions src/aioslsk/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
import time
from typing import (
Generic,
List,
NamedTuple,
Optional,
Union,
Tuple,
TypeVar,
TYPE_CHECKING,
)
Expand Down Expand Up @@ -77,9 +75,9 @@
RT = TypeVar('RT')
"""Response value type"""

Recommendations = Tuple[List[Recommendation], List[Recommendation]]
UserInterests = Tuple[List[str], List[str]]
SharesReply = Tuple[List[DirectoryData], List[DirectoryData]]
Recommendations = tuple[list[Recommendation], list[Recommendation]]
UserInterests = tuple[list[str], list[str]]
SharesReply = tuple[list[DirectoryData], list[DirectoryData]]


class UserStatusInfo(NamedTuple):
Expand Down Expand Up @@ -171,7 +169,7 @@ def handle_response(self, client: SoulSeekClient, response: GetUserStats.Respons
)


class GetRoomListCommand(BaseCommand[RoomList.Response, List[Room]]):
class GetRoomListCommand(BaseCommand[RoomList.Response, list[Room]]):

async def send(self, client: SoulSeekClient):
await client.network.send_server_messages(
Expand All @@ -184,7 +182,7 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
RoomList.Response
)

def handle_response(self, client: SoulSeekClient, response: RoomList.Response) -> List[Room]:
def handle_response(self, client: SoulSeekClient, response: RoomList.Response) -> list[Room]:
return [
room for name, room in client.rooms.rooms.items()
if name in response.rooms
Expand Down Expand Up @@ -238,7 +236,7 @@ def handle_response(self, client: SoulSeekClient, response: LeaveRoom.Response)
return client.rooms.get_or_create_room(response.room)


class GrantRoomMembershipCommand(BaseCommand[PrivateRoomGrantMembership.Response, Tuple[Room, User]]):
class GrantRoomMembershipCommand(BaseCommand[PrivateRoomGrantMembership.Response, tuple[Room, User]]):

def __init__(self, room: str, username: str):
self.room: str = room
Expand All @@ -260,14 +258,14 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
)

def handle_response(
self, client: SoulSeekClient, response: PrivateRoomGrantMembership.Response) -> Tuple[Room, User]:
self, client: SoulSeekClient, response: PrivateRoomGrantMembership.Response) -> tuple[Room, User]:
return (
client.rooms.get_or_create_room(response.room),
client.users.get_user_object(response.username)
)


class RevokeRoomMembershipCommand(BaseCommand[PrivateRoomRevokeMembership.Response, Tuple[Room, User]]):
class RevokeRoomMembershipCommand(BaseCommand[PrivateRoomRevokeMembership.Response, tuple[Room, User]]):

def __init__(self, room: str, username: str):
self.room: str = room
Expand All @@ -289,7 +287,7 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
)

def handle_response(
self, client: SoulSeekClient, response: PrivateRoomRevokeMembership.Response) -> Tuple[Room, User]:
self, client: SoulSeekClient, response: PrivateRoomRevokeMembership.Response) -> tuple[Room, User]:
return (
client.rooms.get_or_create_room(response.room),
client.users.get_user_object(response.username)
Expand Down Expand Up @@ -329,7 +327,7 @@ async def send(self, client: SoulSeekClient):
)


class GetItemRecommendationsCommand(BaseCommand[GetItemRecommendations.Response, List[Recommendation]]):
class GetItemRecommendationsCommand(BaseCommand[GetItemRecommendations.Response, list[Recommendation]]):

def __init__(self, item: str):
self.item: str = item
Expand All @@ -349,7 +347,7 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
)

def handle_response(
self, client: SoulSeekClient, response: GetItemRecommendations.Response) -> List[Recommendation]:
self, client: SoulSeekClient, response: GetItemRecommendations.Response) -> list[Recommendation]:
return response.recommendations


Expand Down Expand Up @@ -387,7 +385,7 @@ def handle_response(self, client: SoulSeekClient, response: GetGlobalRecommendat
return response.recommendations, response.unrecommendations


class GetItemSimilarUsersCommand(BaseCommand[GetItemSimilarUsers.Response, List[User]]):
class GetItemSimilarUsersCommand(BaseCommand[GetItemSimilarUsers.Response, list[User]]):

def __init__(self, item: str):
self.item: str = item
Expand All @@ -406,11 +404,11 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
}
)

def handle_response(self, client: SoulSeekClient, response: GetItemSimilarUsers.Response) -> List[User]:
def handle_response(self, client: SoulSeekClient, response: GetItemSimilarUsers.Response) -> list[User]:
return list(map(client.users.get_user_object, response.usernames))


class GetSimilarUsersCommand(BaseCommand[GetSimilarUsers.Response, List[Tuple[User, int]]]):
class GetSimilarUsersCommand(BaseCommand[GetSimilarUsers.Response, list[tuple[User, int]]]):

async def send(self, client: SoulSeekClient):
await client.network.send_server_messages(
Expand All @@ -423,7 +421,7 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
GetSimilarUsers.Response
)

def handle_response(self, client: SoulSeekClient, response: GetSimilarUsers.Response) -> List[Tuple[User, int]]:
def handle_response(self, client: SoulSeekClient, response: GetSimilarUsers.Response) -> list[tuple[User, int]]:
similar_users = []
for similar_user in response.users:
similar_users.append(
Expand All @@ -436,7 +434,7 @@ def handle_response(self, client: SoulSeekClient, response: GetSimilarUsers.Resp
return similar_users


class GetPeerAddressCommand(BaseCommand[GetPeerAddress.Response, Tuple[str, int, Optional[int]]]):
class GetPeerAddressCommand(BaseCommand[GetPeerAddress.Response, tuple[str, int, Optional[int]]]):

def __init__(self, username: str):
self.username: str = username
Expand All @@ -456,7 +454,7 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
)

def handle_response(
self, client: SoulSeekClient, response: GetPeerAddress.Response) -> Tuple[str, int, Optional[int]]:
self, client: SoulSeekClient, response: GetPeerAddress.Response) -> tuple[str, int, Optional[int]]:
return (response.ip, response.port, response.obfuscated_port)


Expand Down Expand Up @@ -633,8 +631,8 @@ async def send(self, client: SoulSeekClient):
class PrivateMessageUsersCommand(BaseCommand[None, None]):
"""Sends a private message to multiple users"""

def __init__(self, usernames: List[str], message: str):
self.usernames: List[str] = usernames
def __init__(self, usernames: list[str], message: str):
self.usernames: list[str] = usernames
self.message: str = message

async def send(self, client: SoulSeekClient):
Expand Down Expand Up @@ -926,7 +924,7 @@ def handle_response(
return response.directories, locked_dirs


class PeerGetDirectoryContentCommand(BaseCommand[PeerDirectoryContentsReply.Request, List[DirectoryData]]):
class PeerGetDirectoryContentCommand(BaseCommand[PeerDirectoryContentsReply.Request, list[DirectoryData]]):

def __init__(self, username: str, directory: str):
self.username: str = username
Expand All @@ -951,5 +949,5 @@ def build_expected_response(self, client: SoulSeekClient) -> Optional[ExpectedRe
)

def handle_response(
self, client: SoulSeekClient, response: PeerDirectoryContentsReply.Request) -> List[DirectoryData]:
self, client: SoulSeekClient, response: PeerDirectoryContentsReply.Request) -> list[DirectoryData]:
return response.directories
3 changes: 1 addition & 2 deletions src/aioslsk/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
from typing import List


DEFAULT_LISTENING_HOST: str = '0.0.0.0'
Expand Down Expand Up @@ -28,7 +27,7 @@
UPNP_DEFAULT_CHECK_INTERVAL: int = 600
UPNP_DEFAULT_LEASE_DURATION: int = 6 * 60 * 60
UPNP_DEFAULT_SEARCH_TIMEOUT: int = 10
UPNP_MAPPING_SERVICES: List[str] = ["WANIPC", "WANPPP"]
UPNP_MAPPING_SERVICES: list[str] = ["WANIPC", "WANPPP"]
POTENTIAL_PARENTS_CACHE_SIZE: int = 20
"""Maximum amount of potential parents stored"""
DEFAULT_COMMAND_TIMEOUT: float = 10
Loading
Loading