Skip to content

Commit 839701a

Browse files
committed
3.8 compatibility
1 parent 607ad56 commit 839701a

File tree

4 files changed

+36
-25
lines changed

4 files changed

+36
-25
lines changed

tests/installer/dialogs.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import asyncio
2-
from typing import cast
2+
from typing import cast, TypeVar, Generic
33

44
from textual import on
55
from textual.app import App, ComposeResult
@@ -11,7 +11,10 @@
1111
from installer.widgets import DottedLoadingIndicator, ShortcutButton
1212

1313

14-
class RunnableDialog[T](ModalScreen[None]):
14+
T = TypeVar('T')
15+
16+
17+
class RunnableDialog(ModalScreen[None], Generic[T]):
1518
def __init__(self) -> None:
1619
super().__init__()
1720
self.done = asyncio.get_running_loop().create_future()

tests/installer/install_methods.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def _save_env() -> None:
3434

3535
class InstallMethod(ABC):
3636
@abstractmethod
37-
def is_available(self) -> Tuple[bool, str | None]:
37+
def is_available(self) -> Tuple[bool, Optional[str]]:
3838
pass
3939

4040
@abstractmethod
@@ -73,7 +73,7 @@ def __init__(self) -> None:
7373
minute = random.randint(0, 59)
7474
self.line = f'{minute} {hour} * * * "{cwd}/psij-ci-run" --log'
7575

76-
def is_available(self) -> Tuple[bool, str | None]:
76+
def is_available(self) -> Tuple[bool, Optional[str]]:
7777
if not _succeeds("ps -eo command | awk '{print $1}' | grep cron"):
7878
return False, 'not found'
7979
if _succeeds('crontab -l 2>&1 | grep "not allowed"'):
@@ -126,7 +126,7 @@ def __init__(self) -> None:
126126
self.minute = random.randint(0, 59)
127127
self.cmd = f'psij-ci-run --reschedule {self.hour}:{self.minute} --log'
128128

129-
def is_available(self) -> Tuple[bool, str | None]:
129+
def is_available(self) -> Tuple[bool, Optional[str]]:
130130
fd, path = mkstemp()
131131
os.close(fd)
132132

@@ -187,7 +187,7 @@ class Screen(InstallMethod):
187187
def __init__(self) -> None:
188188
self.cmd = 'screen -d -m bash -c "./psij-ci-run --repeat --log"'
189189

190-
def is_available(self) -> Tuple[bool, str | None]:
190+
def is_available(self) -> Tuple[bool, Optional[str]]:
191191
if _succeeds('which screen'):
192192
return True, None
193193
else:
@@ -219,7 +219,7 @@ def help_message(self) -> str:
219219

220220

221221
class Custom(InstallMethod):
222-
def is_available(self) -> Tuple[bool, str | None]:
222+
def is_available(self) -> Tuple[bool, Optional[str]]:
223223
return True, None
224224

225225
def already_installed(self) -> bool:
@@ -255,7 +255,7 @@ def help_message(self) -> str:
255255
]
256256

257257

258-
def existing() -> InstallMethod | None:
258+
def existing() -> Optional[InstallMethod]:
259259
for method in METHODS:
260260
if method.already_installed():
261261
return method

tests/installer/panels/schedule_panel.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import cast
1+
from typing import cast, Optional
22

33
from .panel import Panel
44

@@ -16,7 +16,8 @@ class MRadioSet(RadioSet):
1616
('enter,space', 'select')
1717
]
1818

19-
def __init__(self, panel: 'SchedulePanel', *radios: RadioButton, id: str | None = None) -> None:
19+
def __init__(self, panel: 'SchedulePanel', *radios: RadioButton,
20+
id: Optional[str] = None) -> None:
2021
super().__init__(*radios, id=id)
2122
self.panel = panel
2223

@@ -39,7 +40,7 @@ def action_select(self) -> None:
3940

4041
self.action_toggle_button()
4142

42-
def get_selected_index(self) -> int | None:
43+
def get_selected_index(self) -> Optional[int]:
4344
return self._selected
4445

4546

@@ -86,7 +87,7 @@ def label(self) -> str:
8687
def name(self) -> str:
8788
return 'install'
8889

89-
def _get_selected_method(self) -> InstallMethod | None:
90+
def _get_selected_method(self) -> Optional[InstallMethod]:
9091
radio_set = self.get_widget_by_id('rs-method')
9192
assert isinstance(radio_set, MRadioSet)
9293
selected_index = radio_set.get_selected_index()
@@ -115,7 +116,7 @@ async def activate(self) -> None:
115116
btn.value = True
116117
break
117118

118-
def radio_focused(self, btn: RadioButton | None) -> None:
119+
def radio_focused(self, btn: Optional[RadioButton]) -> None:
119120
log.write(f'focused {btn}\n')
120121
preview = self.get_widget_by_id('method-preview')
121122
assert isinstance(preview, TextArea)

tests/installer/state.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import asyncio
2+
import functools
23
import json
34
import os
45
import pathlib
56
import types
67

78
import requests
89
from collections import namedtuple
9-
from typing import List, Dict, Optional, Callable, Awaitable, Tuple, cast
10+
from typing import List, Dict, Optional, Callable, Awaitable, Tuple, cast, Union
1011

1112
from .install_methods import InstallMethod
1213
from .log import log
@@ -18,21 +19,27 @@
1819
Attr = namedtuple('Attr', ['filter', 'name', 'value'])
1920

2021

22+
async def _to_thread(func, /, *args, **kwargs): # type: ignore
23+
loop = asyncio.get_running_loop()
24+
func_call = functools.partial(func, *args, **kwargs)
25+
return await loop.run_in_executor(None, func_call)
26+
27+
2128
class _Options:
2229
def __init__(self) -> None:
2330
self.custom_attributes = None
2431

2532

2633
class ConfWrapper:
27-
def __init__(self, dict: Dict[str, str | int | bool | None]):
34+
def __init__(self, dict: Dict[str, Union[str, int, bool, None]]):
2835
self.dict = dict
2936
self.option = _Options()
3037
dict['run_id'] = 'x'
3138
dict['save_results'] = False
3239
dict['upload_results'] = True
3340
dict['branch_name_override'] = None
3441

35-
def getoption(self, name: str) -> str | int | bool | None:
42+
def getoption(self, name: str) -> Union[str, int, bool, None]:
3643
return self.dict[name]
3744

3845

@@ -43,16 +50,16 @@ def __init__(self, conftest: types.ModuleType, ci_runner: types.ModuleType):
4350
log.write('Conf: ' + str(self.conf) + '\n')
4451
log.write(str(self.env) + '\n')
4552
self.disable_install = False
46-
self.install_method: InstallMethod | None = None
47-
self.active_panel: int | None = None
48-
self.scheduler: str | None = None
53+
self.install_method: Optional[InstallMethod] = None
54+
self.active_panel: Optional[int] = None
55+
self.scheduler: Optional[str] = None
4956
self.attrs = self._parse_attributes()
5057
self.run_test_job = True
5158
self.has_key = KEY_PATH.exists()
5259
if self.has_key:
5360
with open(KEY_PATH, 'r') as f:
5461
self.key = f.read().strip()
55-
self.key_is_valid: bool | None = None
62+
self.key_is_valid: Optional[bool] = None
5663
log.write(f'has key: {self.has_key}\n')
5764

5865
def _parse_attributes(self) -> List[Attr]:
@@ -89,15 +96,15 @@ def update_conf(self, name: str, value: str) -> None:
8996
self._write_conf_value(name, value)
9097

9198
async def request(self, query: str, data: Dict[str, object], title: str,
92-
error_cb: Callable[[str, str], Awaitable[Dict[str, object] | None]]) \
93-
-> Dict[str, object] | None:
99+
error_cb: Callable[[str, str], Awaitable[Optional[Dict[str, object]]]]) \
100+
-> Optional[Dict[str, object]]:
94101
baseUrl = self.conf['server_url']
95-
response = await asyncio.to_thread(requests.post, baseUrl + query, data=data)
102+
response = await _to_thread(requests.post, baseUrl + query, data=data) # type: ignore
96103
return await self._check_error(response, title, error_cb)
97104

98105
async def _check_error(self, response: requests.Response, title: str,
99-
error_cb: Callable[[str, str], Awaitable[Dict[str, object] | None]]) \
100-
-> Dict[str, object] | None:
106+
error_cb: Callable[[str, str], Awaitable[Optional[Dict[str, object]]]]) \
107+
-> Optional[Dict[str, object]]:
101108
log.write(f'Response: {response.text}\n')
102109
if response.status_code != 200:
103110
msg = self._extract_response_message(response.text)

0 commit comments

Comments
 (0)