Skip to content

Commit d851ad0

Browse files
authored
Remove pydantic (#168)
1 parent dfb0a49 commit d851ad0

File tree

9 files changed

+116
-191
lines changed

9 files changed

+116
-191
lines changed

poetry.lock

Lines changed: 5 additions & 153 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ packages = [
2828
python = "^3.10"
2929
aiohttp = ">=3.0.0"
3030
yarl = ">=1.6.0"
31-
pydantic = ">=1.10.8"
3231
syrupy = "^4.5.0"
3332

3433
[tool.poetry.group.dev.dependencies]

src/python_opensky/const.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
"""Asynchronous Python client for the OpenSky API."""
2+
import logging
23
from enum import Enum
34

5+
LOGGER = logging.getLogger(__package__)
6+
47

58
class PositionSource(int, Enum):
69
"""Enum holding the Position source."""
710

11+
UNKNOWN = -1
812
ADSB = 0
913
ASTERIX = 1
1014
MLAT = 2

src/python_opensky/models.py

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,37 @@
22
from __future__ import annotations
33

44
from dataclasses import dataclass
5-
6-
from pydantic import BaseModel, Field
5+
from typing import TYPE_CHECKING, Any
76

87
from .const import AircraftCategory, PositionSource
98
from .exceptions import OpenSkyCoordinateError
9+
from .util import to_enum
10+
11+
if TYPE_CHECKING:
12+
from typing_extensions import Self
1013

1114

12-
class StatesResponse(BaseModel):
15+
@dataclass(slots=True)
16+
class StatesResponse:
1317
"""Represents the states response."""
1418

15-
states: list[StateVector] = Field(...)
16-
time: int = Field(...)
19+
states: list[StateVector]
20+
time: int
21+
22+
@classmethod
23+
def from_api(cls, data: dict[str, Any]) -> Self:
24+
"""Initialize from the API."""
25+
return cls(
26+
time=data["time"],
27+
states=[
28+
StateVector.from_api(vector_data) for vector_data in data["states"]
29+
],
30+
)
1731

1832

19-
class StateVector(BaseModel):
33+
@dataclass(slots=True)
34+
# pylint: disable-next=too-many-instance-attributes
35+
class StateVector:
2036
"""Represents the state of a vehicle at a particular time.
2137
2238
Attributes
@@ -43,27 +59,59 @@ class StateVector(BaseModel):
4359
category: Aircraft category.
4460
"""
4561

46-
icao24: str = Field(...)
47-
callsign: str | None = Field(None)
48-
origin_country: str = Field(...)
49-
time_position: int | None = Field(None)
50-
last_contact: int = Field(...)
51-
longitude: float | None = Field(None)
52-
latitude: float | None = Field(None)
53-
geo_altitude: float | None = Field(None)
54-
on_ground: bool = Field(...)
55-
velocity: float | None = Field(None)
56-
true_track: float | None = Field(None)
57-
vertical_rate: float | None = Field(None)
58-
sensors: list[int] | None = Field([])
59-
barometric_altitude: float | None = Field(None, alias="baro_altitude")
60-
transponder_code: str | None = Field(None, alias="squawk")
61-
special_purpose_indicator: bool = Field(..., alias="spi")
62-
position_source: PositionSource = Field(...)
63-
category: AircraftCategory = Field(...)
64-
65-
66-
@dataclass
62+
icao24: str
63+
callsign: str | None
64+
origin_country: str
65+
time_position: int | None
66+
last_contact: int
67+
longitude: float | None
68+
latitude: float | None
69+
geo_altitude: float | None
70+
on_ground: bool
71+
velocity: float | None
72+
true_track: float | None
73+
vertical_rate: float | None
74+
sensors: list[int] | None
75+
barometric_altitude: float | None
76+
transponder_code: str | None
77+
special_purpose_indicator: bool
78+
position_source: PositionSource
79+
category: AircraftCategory
80+
81+
@classmethod
82+
def from_api(cls, data: dict[str, Any]) -> Self:
83+
"""Initialize from the API."""
84+
return cls(
85+
icao24=data["icao24"],
86+
callsign=data.get("callsign"),
87+
origin_country=data["origin_country"],
88+
time_position=data.get("time_position"),
89+
last_contact=data["last_contact"],
90+
longitude=data.get("longitude"),
91+
latitude=data.get("latitude"),
92+
geo_altitude=data.get("geo_altitude"),
93+
on_ground=data["on_ground"],
94+
velocity=data.get("velocity"),
95+
true_track=data.get("true_track"),
96+
vertical_rate=data.get("vertical_rate"),
97+
sensors=data.get("sensors", []),
98+
barometric_altitude=data.get("baro_altitude"),
99+
transponder_code=data.get("squawk"),
100+
special_purpose_indicator=data["spi"],
101+
position_source=to_enum(
102+
PositionSource,
103+
data["position_source"],
104+
PositionSource.UNKNOWN,
105+
),
106+
category=to_enum(
107+
AircraftCategory,
108+
data["category"],
109+
AircraftCategory.NO_INFORMATION,
110+
),
111+
)
112+
113+
114+
@dataclass(slots=True)
67115
class BoundingBox:
68116
"""Bounding box for retrieving state vectors."""
69117

@@ -90,7 +138,3 @@ def _check_longitude(degrees: float) -> None:
90138
if degrees < -180 or degrees > 180:
91139
msg = f"Invalid longitude {degrees}! Must be in [-180, 180]."
92140
raise OpenSkyCoordinateError(msg)
93-
94-
95-
StatesResponse.update_forward_refs()
96-
StateVector.update_forward_refs()

src/python_opensky/opensky.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ async def get_states(
178178

179179
self._register_credit_usage(credit_cost)
180180

181-
return StatesResponse.parse_obj(data)
181+
return StatesResponse.from_api(data)
182182

183183
async def get_own_states(self, time: int = 0) -> StatesResponse:
184184
"""Retrieve state vectors from your own sensors."""
@@ -198,7 +198,7 @@ async def get_own_states(self, time: int = 0) -> StatesResponse:
198198
"states": [self._convert_state(state) for state in data["states"]],
199199
}
200200

201-
return StatesResponse.parse_obj(data)
201+
return StatesResponse.from_api(data)
202202

203203
@staticmethod
204204
def calculate_credit_costs(bounding_box: BoundingBox) -> int:

src/python_opensky/util.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Asynchronous Python client for OpenSky."""
2+
from __future__ import annotations
3+
4+
from typing import Any, TypeVar
5+
6+
from .const import LOGGER
7+
8+
_EnumT = TypeVar("_EnumT")
9+
10+
11+
def to_enum(
12+
enum_class: type[_EnumT],
13+
value: Any,
14+
default_value: _EnumT,
15+
) -> _EnumT:
16+
"""Convert a value to an enum and log if it doesn't exist."""
17+
try:
18+
return enum_class(value) # type: ignore[call-arg]
19+
except ValueError:
20+
LOGGER.warning(
21+
"%s is an unsupported value for %s, please report this at https://github.com/joostlek/python-opensky/issues",
22+
value,
23+
str(enum_class),
24+
)
25+
return default_value

tests/__snapshots__/test_states.ambr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
'transponder_code': None,
2121
'true_track': 342.17,
2222
'velocity': 137.8,
23-
'vertical_rate': 13.0,
23+
'vertical_rate': 13,
2424
}),
2525
dict({
2626
'barometric_altitude': None,
@@ -65,7 +65,7 @@
6565
dict({
6666
'barometric_altitude': 701.04,
6767
'callsign': 'N28CN ',
68-
'category': <AircraftCategory.GLIDER: 9>,
68+
'category': <AircraftCategory.NO_INFORMATION: 0>,
6969
'geo_altitude': 685.8,
7070
'icao24': 'a2cbe1',
7171
'last_contact': 1683488743,

tests/fixtures/states.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
["ab1644", "UAL421 ", "United States", 1683488743, 1683488743, -71.1656, 42.5372, 2217.42, false, 137.8, 342.17, 13, null, 2194.56, null, false, 0, 4],
44
["e8027e", "LPE2264 ", "Chile", 1683488479, 1683488722, -77.1296, -12.0115, null, true, 83.29, 149.16, 9.1, null, null, null, false, 0, 1],
55
["e8027d", "LPE2392 ", "Chile", 1683488743, 1683488743, -76.9275, -11.905, 4335.78, false, 187.32, 332.17, 10.4, null, null, null, false, 0, 1],
6-
["a2cbe1", "N28CN ", "United States", 1683488743, 1683488743, -93.9953, 44.9762, 701.04, false, 73.29, 293.15, -4.55, null, 685.8, null, false, 0, 9]
6+
["a2cbe1", "N28CN ", "United States", 1683488743, 1683488743, -93.9953, 44.9762, 701.04, false, 73.29, 293.15, -4.55, null, 685.8, null, false, 0, -1]
77
],
88
"time": 1683488744
99
}

tests/test_states.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for the OpenSky Library."""
22
import asyncio
3+
from dataclasses import asdict
34

45
import aiohttp
56
import pytest
@@ -40,7 +41,7 @@ async def test_states(
4041
async with aiohttp.ClientSession() as session:
4142
opensky = OpenSky(session=session)
4243
response: StatesResponse = await opensky.get_states()
43-
assert response.model_dump() == snapshot
44+
assert asdict(response) == snapshot
4445
await opensky.close()
4546

4647

0 commit comments

Comments
 (0)