Skip to content

Commit 83a3c43

Browse files
2 parents f974cd8 + 0c95f8d commit 83a3c43

6 files changed

Lines changed: 120 additions & 11 deletions

File tree

internal_filesystem/apps/com.micropythonos.doom_launcher/META-INF/MANIFEST.JSON

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.doom_launcher/mpks/com.micropythonos.doom_launcher_0.1.0.mpk",
88
"fullname": "com.micropythonos.doom_launcher",
99
"version": "0.1.0",
10-
"category": "games",
10+
"category": "games"
1111
}
1212

internal_filesystem/apps/com.micropythonos.espnow_chat/META-INF/MANIFEST.JSON

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"publisher": "MicroPythonOS",
44
"short_description": "ESPNow Chat",
55
"long_description": "Simple chat app using EspNow protocol for communication between devices.",
6-
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.espnow_chat/icons/com.micropythonos.espnow_chat_0.0.1_64x64.png",
7-
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.espnow_chat/mpks/com.micropythonos.espnow_chat_0.0.1.mpk",
6+
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.espnow_chat/icons/com.micropythonos.espnow_chat_0.1.0_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.espnow_chat/mpks/com.micropythonos.espnow_chat_0.1.0.mpk",
88
"fullname": "com.micropythonos.espnow_chat",
99
"version": "0.1.0",
1010
"category": "development",

internal_filesystem/apps/com.micropythonos.scan_bluetooth/META-INF/MANIFEST.JSON

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"publisher": "MicroPythonOS",
44
"short_description": "Scan Bluetooth",
55
"long_description": "Lists all nearby Bluetooth devices with some information",
6-
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.scan_bluetooth/icons/com.micropythonos.scan_bluetooth_0.0.1_64x64.png",
7-
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.scan_bluetooth/mpks/com.micropythonos.scan_bluetooth_0.0.1.mpk",
6+
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.scan_bluetooth/icons/com.micropythonos.scan_bluetooth_0.1.0_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.scan_bluetooth/mpks/com.micropythonos.scan_bluetooth_0.1.0.mpk",
88
"fullname": "com.micropythonos.scan_bluetooth",
99
"version": "0.1.0",
1010
"category": "development",

internal_filesystem/lib/aiohttp/__init__.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@ async def __aenter__(self):
109109
async def __aexit__(self, *args):
110110
return await asyncio.sleep(0)
111111

112-
# TODO: Implement timeouts
112+
# Connection timeout is supported via the timeout parameter on request methods
113113

114-
async def _request(self, method, url, data=None, json=None, ssl=None, params=None, headers={}):
114+
async def _request(self, method, url, data=None, json=None, ssl=None, params=None, headers={}, timeout=None):
115115
redir_cnt = 0
116116
while redir_cnt < 2:
117-
reader = await self.request_raw(method, url, data, json, ssl, params, headers)
117+
reader = await self.request_raw(method, url, data, json, ssl, params, headers, timeout=timeout)
118118
_headers = []
119119
sline = await reader.readline()
120120
sline = sline.split(None, 2)
@@ -167,6 +167,7 @@ async def request_raw(
167167
headers={},
168168
is_handshake=False,
169169
version=None,
170+
timeout=None,
170171
):
171172
if json and isinstance(json, dict):
172173
data = _json.dumps(json)
@@ -193,7 +194,12 @@ async def request_raw(
193194
host, port = host.split(":", 1)
194195
port = int(port)
195196

196-
reader, writer = await asyncio.open_connection(host, port, ssl=ssl)
197+
if timeout is not None:
198+
reader, writer = await asyncio.wait_for(
199+
asyncio.open_connection(host, port, ssl=ssl), timeout
200+
)
201+
else:
202+
reader, writer = await asyncio.open_connection(host, port, ssl=ssl)
197203

198204
# Use protocol 1.0, because 1.1 always allows to use chunked transfer-encoding
199205
# But explicitly set Connection: close, even though this should be default for 1.0,
@@ -232,7 +238,7 @@ async def request_raw(
232238
await writer.awrite(query)
233239
return reader, writer
234240

235-
def request(self, method, url, data=None, json=None, ssl=None, params=None, headers={}):
241+
def request(self, method, url, data=None, json=None, ssl=None, params=None, headers={}, timeout=None):
236242
return _RequestContextManager(
237243
self,
238244
self._request(
@@ -243,6 +249,7 @@ def request(self, method, url, data=None, json=None, ssl=None, params=None, head
243249
ssl=ssl,
244250
params=params,
245251
headers=dict(**self._base_headers, **headers),
252+
timeout=timeout,
246253
),
247254
)
248255

scripts/run_desktop.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ else
3939
if [ -f "$CONFIG_FILE" ]; then
4040
if grep -q '"auto_start_app"' "$CONFIG_FILE"; then
4141
echo "Updating auto_start_app field using sed"
42-
sed -i.backup -e 's/"auto_start_app": ".*"/"auto_start_app": "'$script'"/' "$CONFIG_FILE"
42+
sed -i.backup -e 's/"auto_start_app": "[^"]*"/"auto_start_app": "'$script'"/' "$CONFIG_FILE"
4343
else
4444
echo "Adding auto_start_app to config file"
4545
sed -i.backup -E 's/[[:space:]]*}[[:space:]]*$/,"auto_start_app": "'$script'"}/' "$CONFIG_FILE"

tests/test_apps_manifest.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import json
2+
import unittest
3+
from pathlib import Path
4+
5+
APPS_BASE_PATH = Path(__file__).parent.parent / "internal_filesystem" / "apps"
6+
7+
8+
def iter_app_path():
9+
for app in APPS_BASE_PATH.iterdir():
10+
if app.is_dir():
11+
yield app
12+
13+
14+
class TestAppsManifest(unittest.TestCase):
15+
def test_all_apps_manifest(self):
16+
for app_path in iter_app_path():
17+
with self.subTest(app=app_path.name):
18+
manifest = app_path / "META-INF" / "MANIFEST.JSON"
19+
self.assertTrue(manifest.is_file(), f"Missing {manifest=}")
20+
try:
21+
with manifest.open("r", encoding="utf-8") as f:
22+
data = json.load(f)
23+
except Exception as e:
24+
self.fail(f"Invalid JSON in {manifest=}: {e}")
25+
26+
fullname = app_path.name
27+
self.assertEqual(data.get("fullname"), fullname)
28+
29+
# Check name:
30+
name = data.get("name")
31+
self.assertTrue(name, f"Missing name in {manifest=}")
32+
33+
# Check version is a valid Version
34+
version = data.get("version")
35+
self.assertTrue(version, f"Missing version in {manifest=}")
36+
try:
37+
# Until we can use packaging.version.Version check canonical version manually:
38+
version_tuple = tuple(int(x) for x in version.split("."))
39+
version_str = ".".join(str(x) for x in version_tuple)
40+
except Exception as e:
41+
self.fail(f"Invalid version {version=} in {manifest=}: {e}")
42+
self.assertEqual(
43+
version_str,
44+
version,
45+
f"{version=} in {manifest=} is not in canonical form",
46+
)
47+
48+
# Test download_url:
49+
download_url = data.get("download_url")
50+
self.assertTrue(download_url, f"Missing download_url in {manifest=}")
51+
self.assertTrue(
52+
download_url.startswith("https://apps.micropythonos.com/apps/"),
53+
f"Invalid download_url in {manifest=}: {download_url}",
54+
)
55+
self.assertEqual(
56+
download_url,
57+
f"https://apps.micropythonos.com/apps/{fullname}/mpks/{fullname}_{version}.mpk",
58+
)
59+
60+
# Test icon_url:
61+
icon_url = data.get("icon_url")
62+
self.assertTrue(icon_url, f"Missing icon_url in {manifest=}")
63+
self.assertTrue(
64+
icon_url.startswith("https://apps.micropythonos.com/apps/"),
65+
f"Invalid icon_url in {manifest=}: {icon_url}",
66+
)
67+
self.assertEqual(
68+
icon_url,
69+
f"https://apps.micropythonos.com/apps/{fullname}/icons/{fullname}_{version}_64x64.png",
70+
)
71+
72+
# Test activities.entrypoint
73+
activities = data.get("activities", [])
74+
for act in activities:
75+
entrypoint = act.get("entrypoint")
76+
self.assertTrue(
77+
entrypoint, f"Missing entrypoint in activity of {manifest=}"
78+
)
79+
self.assertTrue(
80+
entrypoint.endswith(".py"),
81+
f"Invalid entrypoint in activity of {manifest=}: {entrypoint}",
82+
)
83+
entrypoint_path = app_path / entrypoint
84+
self.assertTrue(
85+
entrypoint_path.is_file(),
86+
f"{entrypoint=} in {manifest=} does not exist as file",
87+
)
88+
89+
classname = act.get("classname")
90+
self.assertTrue(
91+
classname, f"Missing classname in activity of {manifest=}"
92+
)
93+
entrypoint_code = entrypoint_path.read_text()
94+
self.assertIn(
95+
classname,
96+
entrypoint_code,
97+
f"{classname=} not found in {entrypoint=} of {manifest=}",
98+
)
99+
100+
101+
if __name__ == "__main__":
102+
unittest.main()

0 commit comments

Comments
 (0)