xvfbwrapper 0.2.25__tar.gz → 0.2.26__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xvfbwrapper-0.2.25/xvfbwrapper.egg-info → xvfbwrapper-0.2.26}/PKG-INFO +1 -1
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/pyproject.toml +2 -2
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26/xvfbwrapper.egg-info}/PKG-INFO +1 -1
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/xvfbwrapper.py +27 -15
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/LICENSE +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/MANIFEST.in +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/README.md +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/setup.cfg +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/test_xvfb.py +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/tox.ini +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/xvfbwrapper.egg-info/SOURCES.txt +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/xvfbwrapper.egg-info/dependency_links.txt +0 -0
- {xvfbwrapper-0.2.25 → xvfbwrapper-0.2.26}/xvfbwrapper.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "xvfbwrapper"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.26"
|
|
8
8
|
description = "Manage headless displays with Xvfb (X virtual framebuffer)"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
@@ -70,7 +70,7 @@ target-version = "py310"
|
|
|
70
70
|
|
|
71
71
|
[tool.ruff.lint]
|
|
72
72
|
select = ["ALL"]
|
|
73
|
-
|
|
73
|
+
ignore = [
|
|
74
74
|
"ANN", "COM812", "D", "EM101", "EM102", "FBT", "PERF203",
|
|
75
75
|
"PLR", "PT", "PYI034", "SLF001", "S101", "S108", "S311",
|
|
76
76
|
"S603", "TRY003", "TRY400"
|
|
@@ -14,6 +14,8 @@ from collections.abc import MutableMapping, Sequence
|
|
|
14
14
|
from contextlib import suppress
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from random import randint
|
|
17
|
+
from types import TracebackType
|
|
18
|
+
from typing import TextIO
|
|
17
19
|
|
|
18
20
|
try:
|
|
19
21
|
import fcntl
|
|
@@ -25,7 +27,7 @@ except ImportError as e:
|
|
|
25
27
|
class Xvfb:
|
|
26
28
|
# Maximum value to use for a display. 32-bit maxint is the
|
|
27
29
|
# highest Xvfb currently supports
|
|
28
|
-
MAX_DISPLAY = 2147483647
|
|
30
|
+
MAX_DISPLAY: int = 2147483647
|
|
29
31
|
|
|
30
32
|
def __init__(
|
|
31
33
|
self,
|
|
@@ -37,14 +39,14 @@ class Xvfb:
|
|
|
37
39
|
set_xdg_session_type: bool = False,
|
|
38
40
|
environ: MutableMapping[str, str] | None = None,
|
|
39
41
|
extra_args: Sequence[str] | None = None,
|
|
40
|
-
timeout:
|
|
41
|
-
**kwargs,
|
|
42
|
-
):
|
|
42
|
+
timeout: float = 10,
|
|
43
|
+
**kwargs: str,
|
|
44
|
+
) -> None:
|
|
43
45
|
self.width: int = width
|
|
44
46
|
self.height: int = height
|
|
45
47
|
self.colordepth: int = colordepth
|
|
46
48
|
self._tempdir: Path | str = tempdir or tempfile.gettempdir()
|
|
47
|
-
self._timeout:
|
|
49
|
+
self._timeout: float = timeout
|
|
48
50
|
self.new_display: int | None = display
|
|
49
51
|
self.environ: MutableMapping[str, str] = environ or os.environ
|
|
50
52
|
|
|
@@ -78,12 +80,18 @@ class Xvfb:
|
|
|
78
80
|
self.orig_display_var = None
|
|
79
81
|
|
|
80
82
|
self.proc: subprocess.Popen[bytes] | None = None
|
|
83
|
+
self._lock_display_file: TextIO | None = None
|
|
81
84
|
|
|
82
85
|
def __enter__(self) -> "Xvfb":
|
|
83
86
|
self.start()
|
|
84
87
|
return self
|
|
85
88
|
|
|
86
|
-
def __exit__(
|
|
89
|
+
def __exit__(
|
|
90
|
+
self,
|
|
91
|
+
exc_type: type[BaseException] | None,
|
|
92
|
+
exc_val: BaseException | None,
|
|
93
|
+
exc_tb: TracebackType | None,
|
|
94
|
+
) -> None:
|
|
87
95
|
self.stop()
|
|
88
96
|
|
|
89
97
|
def start(self) -> None:
|
|
@@ -125,11 +133,12 @@ class Xvfb:
|
|
|
125
133
|
self.environ.pop("DISPLAY", None)
|
|
126
134
|
else:
|
|
127
135
|
self._set_display(self.orig_display_var)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
|
|
137
|
+
with suppress(OSError):
|
|
138
|
+
self.proc.terminate()
|
|
139
|
+
self.proc.wait(self._timeout)
|
|
140
|
+
|
|
141
|
+
self.proc = None
|
|
133
142
|
finally:
|
|
134
143
|
self._cleanup_lock_file()
|
|
135
144
|
|
|
@@ -137,7 +146,7 @@ class Xvfb:
|
|
|
137
146
|
"""Check that Xvfb is available on PATH and is executable."""
|
|
138
147
|
return shutil.which("Xvfb") is not None
|
|
139
148
|
|
|
140
|
-
def _cleanup_lock_file(self):
|
|
149
|
+
def _cleanup_lock_file(self) -> None:
|
|
141
150
|
"""Delete lock files when stopping.
|
|
142
151
|
|
|
143
152
|
This gets called if the process exits safely with Xvfb.stop(),
|
|
@@ -148,11 +157,14 @@ class Xvfb:
|
|
|
148
157
|
Xvfb.stop() in a finally block, or use Xvfb as a context manager
|
|
149
158
|
to ensure lock files are purged.
|
|
150
159
|
"""
|
|
160
|
+
if self._lock_display_file is None:
|
|
161
|
+
return
|
|
162
|
+
|
|
151
163
|
self._lock_display_file.close()
|
|
152
164
|
with suppress(OSError):
|
|
153
165
|
Path(self._lock_display_file.name).unlink()
|
|
154
166
|
|
|
155
|
-
def _get_lock_for_display(self, display) -> bool:
|
|
167
|
+
def _get_lock_for_display(self, display: int) -> bool:
|
|
156
168
|
"""Attempt to acquire an exclusive lock for a display.
|
|
157
169
|
|
|
158
170
|
In order to ensure multi-process safety, this method attempts
|
|
@@ -183,12 +195,12 @@ class Xvfb:
|
|
|
183
195
|
if self._get_lock_for_display(rand):
|
|
184
196
|
return rand
|
|
185
197
|
|
|
186
|
-
def _local_display_exists(self, display) -> bool:
|
|
198
|
+
def _local_display_exists(self, display: int) -> bool:
|
|
187
199
|
tempdir = "/tmp"
|
|
188
200
|
# We need read access to the real system temp directory
|
|
189
201
|
if not os.access(tempdir, os.R_OK):
|
|
190
202
|
raise RuntimeError(f"Could not access {tempdir} directory: {self._tempdir}")
|
|
191
203
|
return Path(tempdir, ".X11-unix", f"X{display}").exists()
|
|
192
204
|
|
|
193
|
-
def _set_display(self, display_var):
|
|
205
|
+
def _set_display(self, display_var: str) -> None:
|
|
194
206
|
self.environ["DISPLAY"] = display_var
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|