Skip to content

Commit ae3c8d2

Browse files
committed
feat: Port to httpx (deprecate lacking aiohttp)
1 parent 6ae07ab commit ae3c8d2

48 files changed

Lines changed: 741 additions & 847 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Authentication is supported via OAuth2.
2020
## Dependencies
2121

2222
- Python >= 3.7
23-
- Libraries: aiohttp, appdirs, ms_cv, pydantic, urwid, yarl, ecdsa
23+
- Libraries: httpx, appdirs, ms_cv, pydantic, urwid, yarl, ecdsa
2424

2525
## How to use
2626

@@ -59,7 +59,7 @@ API usage
5959
```py
6060
import sys
6161
import asyncio
62-
from aiohttp import ClientSession, ClientResponseError
62+
from httpx import AsyncClient, HTTPStatusError
6363
from xbox.webapi.api.client import XboxLiveClient
6464
from xbox.webapi.authentication.manager import AuthenticationManager
6565
from xbox.webapi.authentication.models import OAuth2TokenResponse
@@ -75,7 +75,7 @@ tokens_file = TOKENS_FILE
7575
For doing authentication, see xbox/webapi/scripts/authenticate.py
7676
"""
7777
async def async_main():
78-
async with ClientSession() as session:
78+
async with AsyncClient() as session:
7979
auth_mgr = AuthenticationManager(
8080
session, client_id, client_secret, ""
8181
)
@@ -90,7 +90,7 @@ async def async_main():
9090

9191
try:
9292
await auth_mgr.refresh_tokens()
93-
except ClientResponseError:
93+
except HTTPStatusError:
9494
print("Could not refresh tokens")
9595
sys.exit(-1)
9696

readme_example.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22
import asyncio
3-
from aiohttp import ClientSession, ClientResponseError
3+
from httpx import AsyncClient, HTTPStatusError
44
from xbox.webapi.api.client import XboxLiveClient
55
from xbox.webapi.authentication.manager import AuthenticationManager
66
from xbox.webapi.authentication.models import OAuth2TokenResponse
@@ -16,7 +16,7 @@
1616
For doing authentication, see xbox/webapi/scripts/authenticate.py
1717
"""
1818
async def async_main():
19-
async with ClientSession() as session:
19+
async with AsyncClient() as session:
2020
auth_mgr = AuthenticationManager(
2121
session, client_id, client_secret, ""
2222
)
@@ -31,7 +31,7 @@ async def async_main():
3131

3232
try:
3333
await auth_mgr.refresh_tokens()
34-
except ClientResponseError:
34+
except HTTPStatusError:
3535
print("Could not refresh tokens")
3636
sys.exit(-1)
3737

requirements.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
# Run
2-
aiohttp
2+
httpx
33
appdirs
44
ecdsa
55
ms_cv
66
pydantic
77
urwid
88
yarl
99

10-
# aiohttp speedups
11-
aiodns
12-
1310
# Dev
1411
pytest
1512
pytest-cov
1613
pylint
17-
aresponses
14+
respx
1815

1916
# pre-commit
2017
pre-commit==2.20.0

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@
3131
],
3232
test_suite="tests",
3333
install_requires=[
34-
"aiohttp",
34+
"httpx",
3535
"appdirs",
3636
"ms_cv",
3737
"pydantic",
3838
"ecdsa",
3939
],
4040
setup_requires=["pytest-runner"],
41-
tests_require=["pytest", "pytest-cov", "aresponses"],
41+
tests_require=["pytest", "pytest-cov", "respx"],
4242
extras_require={
4343
"dev": [
4444
"pip",

tests/common.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import os
2+
import json
23

34
TEST_PATH = os.path.dirname(__file__)
45

56

67
def get_response(name):
78
"""Read a response file."""
89
with open(f"{TEST_PATH}/data/responses/{name}.json",encoding='utf8') as f:
9-
response = f.read()
10-
return response
10+
return f.read()
11+
12+
def get_response_json(name):
13+
"""Read a response file and return json dict."""
14+
text = get_response(name)
15+
return json.loads(text)

tests/conftest.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from datetime import datetime, timedelta
2-
import json
3-
import os
1+
from datetime import datetime
42

5-
from aiohttp import ClientSession
3+
from httpx import AsyncClient
4+
import respx
5+
from ecdsa.keys import SigningKey, VerifyingKey
66
import pytest
77
import pytest_asyncio
88

@@ -11,26 +11,45 @@
1111
from xbox.webapi.authentication.models import (
1212
OAuth2TokenResponse,
1313
XAUResponse,
14-
XSTSDisplayClaims,
1514
XSTSResponse,
1615
)
16+
from xbox.webapi.common.request_signer import RequestSigner
1717

1818
from tests.common import get_response
1919

2020
collect_ignore = ["setup.py"]
2121

22-
2322
@pytest_asyncio.fixture(scope="function")
2423
async def auth_mgr(event_loop):
25-
session = ClientSession(loop=event_loop)
24+
session = AsyncClient()
2625
mgr = AuthenticationManager(session, "abc", "123", "http://localhost")
2726
mgr.oauth = OAuth2TokenResponse.parse_raw(get_response("auth_oauth2_token"))
2827
mgr.user_token = XAUResponse.parse_raw(get_response("auth_user_token"))
2928
mgr.xsts_token = XSTSResponse.parse_raw(get_response("auth_xsts_token"))
3029
yield mgr
31-
await session.close()
30+
await session.aclose()
3231

3332

3433
@pytest.fixture(scope="function")
3534
def xbl_client(auth_mgr):
3635
return XboxLiveClient(auth_mgr)
36+
37+
@pytest.fixture(scope="session")
38+
def ecdsa_signing_key_str() -> str:
39+
with open("tests/data/test_signing_key.pem") as f:
40+
return f.read()
41+
42+
43+
@pytest.fixture(scope="session")
44+
def ecdsa_signing_key(ecdsa_signing_key_str: str) -> SigningKey:
45+
return SigningKey.from_pem(ecdsa_signing_key_str)
46+
47+
48+
@pytest.fixture(scope="session")
49+
def synthetic_request_signer(ecdsa_signing_key) -> RequestSigner:
50+
return RequestSigner(ecdsa_signing_key)
51+
52+
53+
@pytest.fixture(scope="session")
54+
def synthetic_timestamp() -> datetime:
55+
return datetime.utcfromtimestamp(1586999965)

tests/data/real_signed_request.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/test_account.py

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from aiohttp import ClientResponseError
1+
from httpx import HTTPStatusError, Response
22
import pytest
33

44
from xbox.webapi.api.provider.account.models import (
@@ -8,58 +8,39 @@
88

99

1010
@pytest.mark.asyncio
11-
async def test_claim_gamertag(aresponses, xbl_client):
12-
aresponses.add(
13-
"user.mgt.xboxlive.com",
14-
method_pattern="POST",
15-
response=aresponses.Response(status=200),
16-
)
11+
async def test_claim_gamertag(respx_mock, xbl_client):
12+
route = respx_mock.post("https://user.mgt.xboxlive.com").mock(return_value=Response(200))
1713
ret = await xbl_client.account.claim_gamertag("2669321029139235", "PrettyPony")
18-
await xbl_client._auth_mgr.session.close()
1914

2015
assert ret == ClaimGamertagResult.Available
21-
aresponses.assert_plan_strictly_followed()
16+
assert route.called
2217

2318

2419
@pytest.mark.asyncio
25-
async def test_claim_gamertag_error(aresponses, xbl_client):
26-
aresponses.add(
27-
"user.mgt.xboxlive.com",
28-
method_pattern="POST",
29-
response=aresponses.Response(status=500),
30-
)
31-
with pytest.raises(ClientResponseError) as err:
20+
async def test_claim_gamertag_error(respx_mock, xbl_client):
21+
route = respx_mock.post("https://user.mgt.xboxlive.com").mock(return_value=Response(500))
22+
with pytest.raises(HTTPStatusError) as err:
3223
await xbl_client.account.claim_gamertag("2669321029139235", "PrettyPony")
33-
await xbl_client._auth_mgr.session.close()
3424

35-
assert err.value.status == 500
36-
aresponses.assert_plan_strictly_followed()
25+
print(err.value.request.__dict__)
26+
assert err.value.response.status_code == 500
27+
assert route.called
3728

3829

3930
@pytest.mark.asyncio
40-
async def test_change_gamertag(aresponses, xbl_client):
41-
aresponses.add(
42-
"accounts.xboxlive.com",
43-
method_pattern="POST",
44-
response=aresponses.Response(status=200),
45-
)
31+
async def test_change_gamertag(respx_mock, xbl_client):
32+
route = respx_mock.post("https://accounts.xboxlive.com").mock(return_value=Response(200))
4633
ret = await xbl_client.account.change_gamertag("2669321029139235", "PrettyPony")
47-
await xbl_client._auth_mgr.session.close()
4834

4935
assert ret == ChangeGamertagResult.ChangeSuccessful
50-
aresponses.assert_plan_strictly_followed()
36+
assert route.called
5137

5238

5339
@pytest.mark.asyncio
54-
async def test_change_gamertag_error(aresponses, xbl_client):
55-
aresponses.add(
56-
"accounts.xboxlive.com",
57-
method_pattern="POST",
58-
response=aresponses.Response(status=500),
59-
)
60-
with pytest.raises(ClientResponseError) as err:
40+
async def test_change_gamertag_error(respx_mock, xbl_client):
41+
route = respx_mock.post("https://accounts.xboxlive.com").mock(return_value=Response(500))
42+
with pytest.raises(HTTPStatusError) as err:
6143
await xbl_client.account.change_gamertag("2669321029139235", "PrettyPony")
62-
await xbl_client._auth_mgr.session.close()
6344

64-
assert err.value.status == 500
65-
aresponses.assert_plan_strictly_followed()
45+
assert err.value.response.status_code == 500
46+
assert route.called

tests/test_achievements.py

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,84 @@
11
import os
22

33
import pytest
4+
from httpx import Response
45

5-
from tests.common import get_response
6+
from tests.common import get_response_json
67

78

89
@pytest.mark.asyncio
9-
async def test_achievement_360_all(aresponses, xbl_client):
10-
aresponses.add(
11-
"achievements.xboxlive.com", response=get_response("achievements_360_all")
12-
)
10+
async def test_achievement_360_all(respx_mock, xbl_client):
11+
route = respx_mock.get("https://achievements.xboxlive.com").mock(return_value=Response(200, json=get_response_json("achievements_360_all")))
1312

1413
ret = await xbl_client.achievements.get_achievements_xbox360_all(
1514
"2669321029139235", 1297290392
1615
)
17-
await xbl_client._auth_mgr.session.close()
1816

1917
assert ret.paging_info.total_records == 15
20-
aresponses.assert_plan_strictly_followed()
18+
assert route.called
2119

2220

2321
@pytest.mark.asyncio
24-
async def test_achievement_360_earned(aresponses, xbl_client):
25-
aresponses.add(
26-
"achievements.xboxlive.com", response=get_response("achievements_360_earned")
27-
)
22+
async def test_achievement_360_earned(respx_mock, xbl_client):
23+
route = respx_mock.get("https://achievements.xboxlive.com").mock(return_value=Response(200, json=get_response_json("achievements_360_earned")))
2824

2925
ret = await xbl_client.achievements.get_achievements_xbox360_earned(
3026
"2669321029139235", 1297290392
3127
)
32-
await xbl_client._auth_mgr.session.close()
33-
28+
3429
assert len(ret.achievements) == 1
35-
aresponses.assert_plan_strictly_followed()
30+
assert route.called
3631

3732

3833
@pytest.mark.asyncio
39-
async def test_achievement_360_recent_progress(aresponses, xbl_client):
40-
aresponses.add(
41-
"achievements.xboxlive.com",
42-
response=get_response("achievements_360_recent_progress"),
43-
)
34+
async def test_achievement_360_recent_progress(respx_mock, xbl_client):
35+
route = respx_mock.get("https://achievements.xboxlive.com").mock(return_value=Response(200, json=get_response_json("achievements_360_recent_progress")))
4436

4537
ret = (
4638
await xbl_client.achievements.get_achievements_xbox360_recent_progress_and_info(
4739
xuid="2669321029139235"
4840
)
4941
)
50-
await xbl_client._auth_mgr.session.close()
51-
42+
5243
assert len(ret.titles) == 32
53-
aresponses.assert_plan_strictly_followed()
44+
assert route.called
5445

5546

5647
@pytest.mark.asyncio
57-
async def test_achievement_one_details(aresponses, xbl_client):
58-
aresponses.add(
59-
"achievements.xboxlive.com", response=get_response("achievements_one_details")
60-
)
48+
async def test_achievement_one_details(respx_mock, xbl_client):
49+
route = respx_mock.get("https://achievements.xboxlive.com").mock(return_value=Response(200, json=get_response_json("achievements_one_details")))
6150

6251
ret = await xbl_client.achievements.get_achievements_detail_item(
6352
xuid="2669321029139235",
6453
service_config_id="1370999b-fca2-4c53-8ec5-73493bcb67e5",
6554
achievement_id="39",
6655
)
67-
await xbl_client._auth_mgr.session.close()
68-
56+
6957
assert len(ret.achievements) == 1
70-
aresponses.assert_plan_strictly_followed()
58+
assert route.called
7159

7260

7361
@pytest.mark.asyncio
74-
async def test_achievement_one_gameprogress(aresponses, xbl_client):
75-
aresponses.add(
76-
"achievements.xboxlive.com",
77-
response=get_response("achievements_one_gameprogress"),
78-
)
62+
async def test_achievement_one_gameprogress(respx_mock, xbl_client):
63+
route = respx_mock.get("https://achievements.xboxlive.com").mock(return_value=Response(200, json=get_response_json("achievements_one_gameprogress")))
7964

8065
ret = await xbl_client.achievements.get_achievements_xboxone_gameprogress(
8166
xuid="2669321029139235", title_id=219630713
8267
)
83-
await xbl_client._auth_mgr.session.close()
84-
68+
8569
assert len(ret.achievements) == 32
86-
aresponses.assert_plan_strictly_followed()
70+
assert route.called
8771

8872

8973
@pytest.mark.asyncio
90-
async def test_achievement_one_recent_progress(aresponses, xbl_client):
91-
aresponses.add(
92-
"achievements.xboxlive.com",
93-
response=get_response("achievements_one_recent_progress"),
94-
)
74+
async def test_achievement_one_recent_progress(respx_mock, xbl_client):
75+
route = respx_mock.get("https://achievements.xboxlive.com").mock(return_value=Response(200, json=get_response_json("achievements_one_recent_progress")))
9576

9677
ret = (
9778
await xbl_client.achievements.get_achievements_xboxone_recent_progress_and_info(
9879
xuid="2669321029139235"
9980
)
10081
)
101-
await xbl_client._auth_mgr.session.close()
102-
82+
10383
assert len(ret.titles) == 32
104-
aresponses.assert_plan_strictly_followed()
84+
assert route.called

0 commit comments

Comments
 (0)