Skip to content

Commit 4cf3aef

Browse files
authored
Merge pull request #1457 from Textualize/win-sleep
Win sleep
2 parents c261894 + 91e23ff commit 4cf3aef

File tree

6 files changed

+76
-12
lines changed

6 files changed

+76
-12
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## [0.9.1] - 2022-12-30
9+
10+
### Added
11+
12+
- Added textual._win_sleep for Python on Windows < 3.11 https://github.com/Textualize/textual/pull/1457
13+
814
## [0.9.0] - 2022-12-30
915

1016
### Added
@@ -316,6 +322,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
316322
- New handler system for messages that doesn't require inheritance
317323
- Improved traceback handling
318324

325+
[0.9.1]: https://github.com/Textualize/textual/compare/v0.9.0...v0.9.1
319326
[0.9.0]: https://github.com/Textualize/textual/compare/v0.8.2...v0.9.0
320327
[0.8.2]: https://github.com/Textualize/textual/compare/v0.8.1...v0.8.2
321328
[0.8.1]: https://github.com/Textualize/textual/compare/v0.8.0...v0.8.1

docs/blog/posts/better-sleep-on-windows.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ async def sleep(sleep_for: float) -> None:
4343
4444
Args:
4545
sleep_for (float): Seconds to sleep for.
46-
"""
47-
print("sleep")
46+
"""
4847
await get_running_loop().run_in_executor(None, time_sleep, sleep_for)
4948

5049
```

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "textual"
3-
version = "0.9.0"
3+
version = "0.9.1"
44
homepage = "https://github.com/Textualize/textual"
55
description = "Modern Text User Interface framework"
66
authors = ["Will McGugan <[email protected]>"]

src/textual/_time.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import platform
2+
import sys
23

34
from asyncio import sleep as asyncio_sleep, get_running_loop
45
from time import monotonic, perf_counter, sleep as time_sleep
@@ -16,15 +17,23 @@
1617

1718
if WINDOWS:
1819

19-
async def sleep(sleep_for: float) -> None:
20-
"""An asyncio sleep.
20+
if sys.version_info >= (3, 11, 0):
2121

22-
On Windows this achieves a better granularity that asyncio.sleep
22+
async def sleep(sleep_for: float) -> None:
23+
"""An asyncio sleep.
2324
24-
Args:
25-
sleep_for (float): Seconds to sleep for.
26-
"""
27-
await get_running_loop().run_in_executor(None, time_sleep, sleep_for)
25+
On Windows this achieves a better granularity that asyncio.sleep
26+
27+
Args:
28+
sleep_for (float): Seconds to sleep for.
29+
"""
30+
await get_running_loop().run_in_executor(None, time_sleep, sleep_for)
31+
32+
else:
33+
from ._win_sleep import sleep as win_sleep
34+
35+
async def sleep(sleep_for: float) -> None:
36+
await get_running_loop().run_in_executor(None, win_sleep, sleep_for)
2837

2938
else:
3039
sleep = asyncio_sleep

src/textual/_win_sleep.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import ctypes
2+
from ctypes.wintypes import LARGE_INTEGER
3+
from time import sleep as time_sleep
4+
5+
__all__ = ["sleep"]
6+
7+
kernel32 = ctypes.windll.kernel32
8+
9+
INFINITE = 0xFFFFFFFF
10+
WAIT_FAILED = 0xFFFFFFFF
11+
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002
12+
13+
14+
def sleep(sleep_for: float) -> None:
15+
"""A replacement sleep for Windows.
16+
17+
Python 3.11 added a more accurate sleep. This may be used on < Python 3.11
18+
19+
Args:
20+
sleep_for (float): Seconds to sleep for.
21+
"""
22+
handle = kernel32.CreateWaitableTimerExW(
23+
None,
24+
None,
25+
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
26+
0x1F0003,
27+
)
28+
if not handle:
29+
time_sleep(sleep_for)
30+
return
31+
32+
sleep_for -= 1 / 1000
33+
if not kernel32.SetWaitableTimer(
34+
handle,
35+
ctypes.byref(LARGE_INTEGER(int(sleep_for * -10_000_000))),
36+
0,
37+
None,
38+
None,
39+
0,
40+
):
41+
kernel32.CloseHandle(handle)
42+
print("error")
43+
time_sleep(sleep_for)
44+
return
45+
46+
if kernel32.WaitForSingleObject(handle, INFINITE) == WAIT_FAILED:
47+
time_sleep(sleep_for)
48+
kernel32.CloseHandle(handle)

tests/snapshot_tests/test_snapshots.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ def test_input_and_focus(snap_compare):
7171
"tab",
7272
*"Darren", # Focus first input, write "Darren"
7373
"tab",
74-
*"Burns", # Tab focus to second input, write "Burns"
74+
*"Burns",
75+
"_", # Tab focus to second input, write "Burns"
7576
]
7677
assert snap_compare(WIDGET_EXAMPLES_DIR / "input.py", press=press)
7778

@@ -178,6 +179,6 @@ def test_demo(snap_compare):
178179
"""Test the demo app (python -m textual)"""
179180
assert snap_compare(
180181
Path("../../src/textual/demo.py"),
181-
press=["down", "down", "down", "_"],
182+
press=["down", "down", "down", "_", "_"],
182183
terminal_size=(100, 30),
183184
)

0 commit comments

Comments
 (0)