Browse Source

set line length back to 79 instead of 125

pull/43/head
Alex Nørgaard 2 years ago
parent
commit
3a9be1410c
No known key found for this signature in database GPG Key ID: 94D54F6A3604E97
  1. 18
      a2s/__init__.py
  2. 83
      a2s/a2s_async.py
  3. 60
      a2s/a2s_sync.py
  4. 36
      a2s/byteio.py
  5. 9
      a2s/datacls.py
  6. 12
      a2s/info.py
  7. 14
      a2s/players.py
  8. 13
      a2s/rules.py
  9. 4
      pyproject.toml

18
a2s/__init__.py

@ -1,4 +1,16 @@
from a2s.exceptions import BrokenMessageError as BrokenMessageError, BufferExhaustedError as BufferExhaustedError
from a2s.info import GoldSrcInfo as GoldSrcInfo, SourceInfo as SourceInfo, ainfo as ainfo, info as info
from a2s.players import Player as Player, aplayers as aplayers, players as players
from a2s.exceptions import (
BrokenMessageError as BrokenMessageError,
BufferExhaustedError as BufferExhaustedError,
)
from a2s.info import (
GoldSrcInfo as GoldSrcInfo,
SourceInfo as SourceInfo,
ainfo as ainfo,
info as info,
)
from a2s.players import (
Player as Player,
aplayers as aplayers,
players as players,
)
from a2s.rules import arules as arules, rules as rules

83
a2s/a2s_async.py

@ -4,7 +4,18 @@ import asyncio
import io
import logging
import time
from typing import TYPE_CHECKING, Dict, List, NoReturn, Optional, Tuple, Type, TypeVar, Union, overload
from typing import (
TYPE_CHECKING,
Dict,
List,
NoReturn,
Optional,
Tuple,
Type,
TypeVar,
Union,
overload,
)
from a2s.a2s_fragment import A2SFragment, decode_fragment
from a2s.byteio import ByteReader
@ -30,28 +41,42 @@ T = TypeVar("T", InfoProtocol, PlayersProtocol, RulesProtocol)
@overload
async def request_async(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[InfoProtocol]
address: Tuple[str, int],
timeout: float,
encoding: str,
a2s_proto: Type[InfoProtocol],
) -> Union[SourceInfo, GoldSrcInfo]:
...
@overload
async def request_async(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[PlayersProtocol]
address: Tuple[str, int],
timeout: float,
encoding: str,
a2s_proto: Type[PlayersProtocol],
) -> List[Player]:
...
@overload
async def request_async(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[RulesProtocol]
address: Tuple[str, int],
timeout: float,
encoding: str,
a2s_proto: Type[RulesProtocol],
) -> Dict[Union[str, bytes], Union[str, bytes]]:
...
async def request_async(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[T]
) -> Union[SourceInfo, GoldSrcInfo, List[Player], Dict[Union[str, bytes], Union[str, bytes]]]:
) -> Union[
SourceInfo,
GoldSrcInfo,
List[Player],
Dict[Union[str, bytes], Union[str, bytes]],
]:
conn = await A2SStreamAsync.create(address, timeout)
response = await request_async_impl(conn, encoding, a2s_proto)
conn.close()
@ -101,7 +126,12 @@ async def request_async_impl(
challenge: int = 0,
retries: int = 0,
ping: Optional[float] = None,
) -> Union[SourceInfo, GoldSrcInfo, Dict[Union[str, bytes], Union[str, bytes]], List[Player]]:
) -> Union[
SourceInfo,
GoldSrcInfo,
Dict[Union[str, bytes], Union[str, bytes]],
List[Player],
]:
send_time = time.monotonic()
resp_data = await conn.request(a2s_proto.serialize_request(challenge))
recv_time = time.monotonic()
@ -114,12 +144,18 @@ async def request_async_impl(
response_type = reader.read_uint8()
if response_type == A2S_CHALLENGE_RESPONSE:
if retries >= DEFAULT_RETRIES:
raise BrokenMessageError("Server keeps sending challenge responses")
raise BrokenMessageError(
"Server keeps sending challenge responses"
)
challenge = reader.read_uint32()
return await request_async_impl(conn, encoding, a2s_proto, challenge, retries + 1, ping)
return await request_async_impl(
conn, encoding, a2s_proto, challenge, retries + 1, ping
)
if not a2s_proto.validate_response_type(response_type):
raise BrokenMessageError("Invalid response type: " + hex(response_type))
raise BrokenMessageError(
"Invalid response type: " + hex(response_type)
)
return a2s_proto.deserialize_response(reader, response_type, ping)
@ -153,15 +189,23 @@ class A2SProtocol(asyncio.DatagramProtocol):
if len(self.fragment_buf) < self.fragment_buf[0].fragment_count:
return # Wait for more packets to arrive
self.fragment_buf.sort(key=lambda f: f.fragment_id)
reassembled = b"".join(fragment.payload for fragment in self.fragment_buf)
reassembled = b"".join(
fragment.payload for fragment in self.fragment_buf
)
# Sometimes there's an additional header present
if reassembled.startswith(b"\xFF\xFF\xFF\xFF"):
reassembled = reassembled[4:]
logger.debug("Received %s part packet with content: %r", len(self.fragment_buf), reassembled)
logger.debug(
"Received %s part packet with content: %r",
len(self.fragment_buf),
reassembled,
)
self.recv_queue.put_nowait(reassembled)
self.fragment_buf = []
else:
self.error = BrokenMessageError("Invalid packet header: " + repr(header))
self.error = BrokenMessageError(
"Invalid packet header: " + repr(header)
)
self.error_event.set()
def error_received(self, exc: Exception) -> None:
@ -183,7 +227,12 @@ class A2SStreamAsync:
"timeout",
)
def __init__(self, transport: asyncio.DatagramTransport, protocol: A2SProtocol, timeout: float) -> None:
def __init__(
self,
transport: asyncio.DatagramTransport,
protocol: A2SProtocol,
timeout: float,
) -> None:
self.transport: asyncio.DatagramTransport = transport
self.protocol: A2SProtocol = protocol
self.timeout: float = timeout
@ -194,7 +243,9 @@ class A2SStreamAsync:
@classmethod
async def create(cls, address: Tuple[str, int], timeout: float) -> Self:
loop = asyncio.get_running_loop()
transport, protocol = await loop.create_datagram_endpoint(lambda: A2SProtocol(), remote_addr=address)
transport, protocol = await loop.create_datagram_endpoint(
lambda: A2SProtocol(), remote_addr=address
)
return cls(transport, protocol, timeout)
def send(self, payload: bytes) -> None:
@ -206,7 +257,9 @@ class A2SStreamAsync:
queue_task = asyncio.create_task(self.protocol.recv_queue.get())
error_task = asyncio.create_task(self.protocol.error_event.wait())
done, pending = await asyncio.wait(
{queue_task, error_task}, timeout=self.timeout, return_when=asyncio.FIRST_COMPLETED
{queue_task, error_task},
timeout=self.timeout,
return_when=asyncio.FIRST_COMPLETED,
)
for task in pending:

60
a2s/a2s_sync.py

@ -27,26 +27,42 @@ T = TypeVar("T", InfoProtocol, RulesProtocol, PlayersProtocol)
@overload
def request_sync(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[InfoProtocol]
address: Tuple[str, int],
timeout: float,
encoding: str,
a2s_proto: Type[InfoProtocol],
) -> Union[SourceInfo, GoldSrcInfo]:
...
@overload
def request_sync(address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[PlayersProtocol]) -> List[Player]:
def request_sync(
address: Tuple[str, int],
timeout: float,
encoding: str,
a2s_proto: Type[PlayersProtocol],
) -> List[Player]:
...
@overload
def request_sync(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[RulesProtocol]
address: Tuple[str, int],
timeout: float,
encoding: str,
a2s_proto: Type[RulesProtocol],
) -> Dict[Union[str, bytes], Union[str, bytes]]:
...
def request_sync(
address: Tuple[str, int], timeout: float, encoding: str, a2s_proto: Type[T]
) -> Union[List[Player], GoldSrcInfo, SourceInfo, Dict[Union[str, bytes], Union[str, bytes]]]:
) -> Union[
List[Player],
GoldSrcInfo,
SourceInfo,
Dict[Union[str, bytes], Union[str, bytes]],
]:
conn = A2SStream(address, timeout)
response = request_sync_impl(conn, encoding, a2s_proto)
conn.close()
@ -90,8 +106,18 @@ def request_sync_impl(
def request_sync_impl(
conn: A2SStream, encoding: str, a2s_proto: Type[T], challenge: int = 0, retries: int = 0, ping: Optional[float] = None
) -> Union[SourceInfo, GoldSrcInfo, List[Player], Dict[Union[str, bytes], Union[str, bytes]]]:
conn: A2SStream,
encoding: str,
a2s_proto: Type[T],
challenge: int = 0,
retries: int = 0,
ping: Optional[float] = None,
) -> Union[
SourceInfo,
GoldSrcInfo,
List[Player],
Dict[Union[str, bytes], Union[str, bytes]],
]:
send_time = time.monotonic()
resp_data = conn.request(a2s_proto.serialize_request(challenge))
recv_time = time.monotonic()
@ -104,12 +130,18 @@ def request_sync_impl(
response_type = reader.read_uint8()
if response_type == A2S_CHALLENGE_RESPONSE:
if retries >= DEFAULT_RETRIES:
raise BrokenMessageError("Server keeps sending challenge responses")
raise BrokenMessageError(
"Server keeps sending challenge responses"
)
challenge = reader.read_uint32()
return request_sync_impl(conn, encoding, a2s_proto, challenge, retries + 1, ping)
return request_sync_impl(
conn, encoding, a2s_proto, challenge, retries + 1, ping
)
if not a2s_proto.validate_response_type(response_type):
raise BrokenMessageError("Invalid response type: " + hex(response_type))
raise BrokenMessageError(
"Invalid response type: " + hex(response_type)
)
return a2s_proto.deserialize_response(reader, response_type, ping)
@ -122,7 +154,9 @@ class A2SStream:
def __init__(self, address: Tuple[str, int], timeout: float) -> None:
self.address: Tuple[str, int] = address
self._socket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._socket: socket.socket = socket.socket(
socket.AF_INET, socket.SOCK_DGRAM
)
self._socket.settimeout(timeout)
def __del__(self) -> None:
@ -150,7 +184,11 @@ class A2SStream:
# Sometimes there's an additional header present
if reassembled.startswith(b"\xFF\xFF\xFF\xFF"):
reassembled = reassembled[4:]
logger.debug("Received %s part packet with content: %r", len(fragments), reassembled)
logger.debug(
"Received %s part packet with content: %r",
len(fragments),
reassembled,
)
return reassembled
else:
raise BrokenMessageError("Invalid packet header: " + repr(header))

36
a2s/byteio.py

@ -10,7 +10,27 @@ if TYPE_CHECKING:
from typing_extensions import Literal
STRUCT_OPTIONS = Literal[
"x", "c", "b", "B", "?", "h", "H", "i", "I", "l", "L", "q", "Q", "n", "N", "e", "f", "d", "s", "p", "P"
"x",
"c",
"b",
"B",
"?",
"h",
"H",
"i",
"I",
"l",
"L",
"q",
"Q",
"n",
"N",
"e",
"f",
"d",
"s",
"p",
"P",
]
@ -21,7 +41,12 @@ class ByteReader:
"encoding",
)
def __init__(self, stream: io.BytesIO, endian: str = "=", encoding: Optional[str] = None) -> None:
def __init__(
self,
stream: io.BytesIO,
endian: str = "=",
encoding: Optional[str] = None,
) -> None:
self.stream: io.BytesIO = stream
self.endian: str = endian
self.encoding: Optional[str] = encoding
@ -111,7 +136,12 @@ class ByteWriter:
"encoding",
)
def __init__(self, stream: io.BytesIO, endian: str = "=", encoding: Optional[str] = None) -> None:
def __init__(
self,
stream: io.BytesIO,
endian: str = "=",
encoding: Optional[str] = None,
) -> None:
self.stream: io.BytesIO = stream
self.endian: str = endian
self.encoding: Optional[str] = encoding

9
a2s/datacls.py

@ -29,11 +29,16 @@ class DataclsBase:
yield (name, getattr(self, name))
def __repr__(self) -> str:
return "{}({})".format(self.__class__.__name__, ", ".join(name + "=" + repr(value) for name, value in self))
return "{}({})".format(
self.__class__.__name__,
", ".join(name + "=" + repr(value) for name, value in self),
)
class DataclsMeta(type):
def __new__(cls, name: str, bases: Tuple[type, ...], prop: Dict[str, Any]) -> Self:
def __new__(
cls, name: str, bases: Tuple[type, ...], prop: Dict[str, Any]
) -> Self:
values: OrderedDict[str, Any] = OrderedDict()
for member_name in prop["__annotations__"].keys():
# Check if member has a default value set as class variable

12
a2s/info.py

@ -182,13 +182,17 @@ class GoldSrcInfo(metaclass=DataclsMeta):
def info(
address: Tuple[str, int], timeout: float = DEFAULT_TIMEOUT, encoding: str = DEFAULT_ENCODING
address: Tuple[str, int],
timeout: float = DEFAULT_TIMEOUT,
encoding: str = DEFAULT_ENCODING,
) -> Union[SourceInfo, GoldSrcInfo]:
return request_sync(address, timeout, encoding, InfoProtocol)
async def ainfo(
address: Tuple[str, int], timeout: float = DEFAULT_TIMEOUT, encoding: str = DEFAULT_ENCODING
address: Tuple[str, int],
timeout: float = DEFAULT_TIMEOUT,
encoding: str = DEFAULT_ENCODING,
) -> Union[SourceInfo, GoldSrcInfo]:
return await request_async(address, timeout, encoding, InfoProtocol)
@ -201,7 +205,9 @@ class InfoProtocol:
@staticmethod
def serialize_request(challenge: int) -> bytes:
if challenge:
return b"\x54Source Engine Query\0" + challenge.to_bytes(4, "little")
return b"\x54Source Engine Query\0" + challenge.to_bytes(
4, "little"
)
else:
return b"\x54Source Engine Query\0"

14
a2s/players.py

@ -23,12 +23,18 @@ class Player(metaclass=DataclsMeta):
"""Time the player has been connected to the server"""
def players(address: Tuple[str, int], timeout: float = DEFAULT_TIMEOUT, encoding: str = DEFAULT_ENCODING) -> List[Player]:
def players(
address: Tuple[str, int],
timeout: float = DEFAULT_TIMEOUT,
encoding: str = DEFAULT_ENCODING,
) -> List[Player]:
return request_sync(address, timeout, encoding, PlayersProtocol)
async def aplayers(
address: Tuple[str, int], timeout: float = DEFAULT_TIMEOUT, encoding: str = DEFAULT_ENCODING
address: Tuple[str, int],
timeout: float = DEFAULT_TIMEOUT,
encoding: str = DEFAULT_ENCODING,
) -> List[Player]:
return await request_async(address, timeout, encoding, PlayersProtocol)
@ -43,7 +49,9 @@ class PlayersProtocol:
return b"\x55" + challenge.to_bytes(4, "little")
@staticmethod
def deserialize_response(reader: ByteReader, response_type: int, ping: Optional[float]) -> List[Player]:
def deserialize_response(
reader: ByteReader, response_type: int, ping: Optional[float]
) -> List[Player]:
player_count = reader.read_uint8()
resp = [
Player(

13
a2s/rules.py

@ -9,13 +9,17 @@ A2S_RULES_RESPONSE = 0x45
def rules(
address: Tuple[str, int], timeout: float = DEFAULT_TIMEOUT, encoding: str = DEFAULT_ENCODING
address: Tuple[str, int],
timeout: float = DEFAULT_TIMEOUT,
encoding: str = DEFAULT_ENCODING,
) -> Dict[Union[str, bytes], Union[str, bytes]]:
return request_sync(address, timeout, encoding, RulesProtocol)
async def arules(
address: Tuple[str, int], timeout: float = DEFAULT_TIMEOUT, encoding: str = DEFAULT_ENCODING
address: Tuple[str, int],
timeout: float = DEFAULT_TIMEOUT,
encoding: str = DEFAULT_ENCODING,
) -> Dict[Union[str, bytes], Union[str, bytes]]:
return await request_async(address, timeout, encoding, RulesProtocol)
@ -35,5 +39,8 @@ class RulesProtocol:
) -> Dict[Union[str, bytes], Union[str, bytes]]:
rule_count = reader.read_int16()
# Have to use tuples to preserve evaluation order
resp = dict((reader.read_cstring(), reader.read_cstring()) for _ in range(rule_count))
resp = dict(
(reader.read_cstring(), reader.read_cstring())
for _ in range(rule_count)
)
return resp

4
pyproject.toml

@ -1,12 +1,12 @@
[tool.black]
line-length = 125
line-length = 79
target-version = ["py37"]
[tool.isort]
profile = "black"
combine_as_imports = true
combine_star = true
line_length = 125
line_length = 79
[tool.pyright]
include = ["a2s/**/*.py", "a2s/**/*.pyi"]

Loading…
Cancel
Save