Skip to content

Commit 9637544

Browse files
committed
Fix bugs
1 parent 8aabedb commit 9637544

5 files changed

Lines changed: 70 additions & 9 deletions

File tree

.github/workflows/automatic-update.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ jobs:
1212
contents: write
1313
steps:
1414
- name: Checkout
15-
uses: actions/checkout@v4
15+
uses: actions/checkout@v6
1616
with:
1717
persist-credentials: false
1818
fetch-depth: 0
1919
- name: Setup Python uv
20-
uses: astral-sh/setup-uv@v5
20+
uses: astral-sh/setup-uv@v7
2121
with:
2222
python-version: "3.12"
23-
enable-cache: true
23+
enable-cache: "auto"
2424
cache-dependency-glob: "**/pyproject.toml"
2525
- name: Setup java
26-
uses: actions/setup-java@v4
26+
uses: actions/setup-java@v5
2727
with:
2828
distribution: "temurin"
2929
java-version: "17"
@@ -33,7 +33,8 @@ jobs:
3333
libs/PlatformTools/**/gradle-wrapper.properties
3434
- name: Run scripts
3535
run: uv run src/main.py
36-
- name: Push updates
37-
uses: GuillaumeFalourd/git-commit-push@v1.3
36+
- name: Commit & Push changes
37+
uses: actions-js/push@master
3838
with:
39-
commit_message: Automatic update ${{ steps.date.outputs.date }}
39+
github_token: ${{ secrets.GITHUB_TOKEN }}
40+
message: Automatic update ${{ steps.date.outputs.date }}

src/android_info/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,5 @@
6565
34: ["14.0.0"],
6666
35: ["15.0.0"],
6767
36: ["16.0.0"],
68+
37: ["17.0.0"],
6869
}

src/android_info/repository.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# noinspection PyProtectedMember
1010
from lxml.etree import _Element
1111

12+
from .task import SingleFlight
1213
from .utils import xml_to_dict
1314

1415
# noinspection HttpUrlsUsage
@@ -32,6 +33,7 @@ class AndroidRepository:
3233
def __init__(self, client: aiohttp.ClientSession):
3334
self._client = client
3435
self._tree: _Element | None = None
36+
self._task_manager: SingleFlight = SingleFlight()
3537

3638
@staticmethod
3739
def cached_instance(client: aiohttp.ClientSession) -> 'AndroidRepository':
@@ -120,7 +122,7 @@ def get_best_archive_url(pkg_dict: dict) -> str:
120122
else:
121123
raise ValueError(f"Unknown archives format: {archives}")
122124

123-
async def download_archive(self, archive_name: str, output_dir: str | None = None) -> str:
125+
async def _download_archive_task(self, archive_name: str, output_dir: str | None) -> str:
124126
if output_dir is None:
125127
output_dir = "."
126128
if not os.path.isdir(output_dir):
@@ -138,6 +140,14 @@ async def download_archive(self, archive_name: str, output_dir: str | None = Non
138140
raise ValueError(f"Missing download tmp: {tmp_file_path}")
139141
return target_file_path
140142

143+
async def download_archive(self, archive_name: str, output_dir: str | None = None) -> str:
144+
return await self._task_manager.run(
145+
archive_name,
146+
self._download_archive_task,
147+
archive_name,
148+
output_dir
149+
)
150+
141151
async def get_packages(self, path: str, channel: str | None = None) -> list[dict]:
142152
pkg_elements = await self._get_package_elements(path)
143153
return sorted([

src/android_info/task.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import asyncio
2+
from typing import Callable, Awaitable, ParamSpec, TypeVar
3+
4+
P = ParamSpec("P")
5+
R = TypeVar("R")
6+
7+
8+
class SingleFlight:
9+
def __init__(self) -> None:
10+
self._locks: dict[str, asyncio.Lock] = {}
11+
self._futures: dict[str, asyncio.Future] = {}
12+
self._global_lock: asyncio.Lock = asyncio.Lock()
13+
14+
async def run(
15+
self,
16+
key: str,
17+
coro_factory: Callable[P, Awaitable[R]],
18+
*args: P.args,
19+
**kwargs: P.kwargs,
20+
) -> R:
21+
loop = asyncio.get_running_loop()
22+
23+
async with self._global_lock:
24+
if key not in self._locks:
25+
self._locks[key] = asyncio.Lock()
26+
self._futures[key] = loop.create_future()
27+
28+
lock = self._locks[key]
29+
future = self._futures[key]
30+
31+
if lock.locked() and not future.done():
32+
return await future
33+
34+
async with lock:
35+
if future.done():
36+
return future.result()
37+
38+
try:
39+
result = await coro_factory(*args, **kwargs)
40+
future.set_result(result)
41+
return result
42+
except Exception as e:
43+
future.set_exception(e)
44+
raise
45+
finally:
46+
async with self._global_lock:
47+
self._locks.pop(key, None)
48+
await self._futures.pop(key, None)

src/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import http
33
import json
44
import os
5+
from typing import Any
56

67
import aiofiles.os
78
import aiohttp
@@ -35,7 +36,7 @@ def filter_available_api_levels(api_levels: list[AndroidAPILevel]) -> list[Andro
3536
return [i for i in api_levels if i.api >= 4 and i.api != 11 and i.api != 12 and i.api != 20]
3637

3738

38-
async def dump_json(data: any, output_path: str):
39+
async def dump_json(data: Any, output_path: str):
3940
async with aiofiles.open(output_path, "w", encoding="utf-8") as f:
4041
await f.write(json.dumps(data, ensure_ascii=False, indent=4))
4142

0 commit comments

Comments
 (0)