xvfbwrapper 0.2.29__tar.gz → 0.2.31__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.29/xvfbwrapper.egg-info → xvfbwrapper-0.2.31}/PKG-INFO +12 -11
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/README.md +11 -10
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/pyproject.toml +1 -1
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/test_xvfb.py +7 -4
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/tox.ini +0 -3
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31/xvfbwrapper.egg-info}/PKG-INFO +12 -11
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/xvfbwrapper.py +40 -18
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/LICENSE +0 -0
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/MANIFEST.in +0 -0
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/setup.cfg +0 -0
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/xvfbwrapper.egg-info/SOURCES.txt +0 -0
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/xvfbwrapper.egg-info/dependency_links.txt +0 -0
- {xvfbwrapper-0.2.29 → xvfbwrapper-0.2.31}/xvfbwrapper.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xvfbwrapper
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.31
|
|
4
4
|
Summary: Manage headless displays with Xvfb (X virtual framebuffer)
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -37,18 +37,19 @@ Dynamic: license-file
|
|
|
37
37
|
|
|
38
38
|
## About
|
|
39
39
|
|
|
40
|
-
`xvfbwrapper` is a Python library for
|
|
40
|
+
`xvfbwrapper` is a Python library for managing X11 virtual displays with
|
|
41
|
+
[Xvfb (X virtual framebuffer)](https://x.org/releases/X11R7.7/doc/man/man1/Xvfb.1.xhtml),
|
|
42
|
+
a virtual X server that runs without a physical display. It provides the
|
|
43
|
+
[`Xvfb`][api-docs] class, a Python interface for configuring and controlling the Xvfb
|
|
44
|
+
program.
|
|
45
|
+
|
|
46
|
+
See the [module API documentation][api-docs] for more information.
|
|
41
47
|
|
|
42
48
|
- Copyright (c) 2012-2026 [Corey Goldberg][github-profile]
|
|
43
49
|
- Development: [GitHub][github-repo]
|
|
44
50
|
- Releases: [PyPI][pypi-home]
|
|
45
51
|
- License: [MIT][mit-license]
|
|
46
52
|
|
|
47
|
-
The package provides the `xvfvwrapper.Xvfb` class that is used to interact with
|
|
48
|
-
an Xvfb server.
|
|
49
|
-
|
|
50
|
-
See the [module API documentation][api-docs] for more information.
|
|
51
|
-
|
|
52
53
|
----
|
|
53
54
|
|
|
54
55
|
## Status
|
|
@@ -116,7 +117,7 @@ pip install xvfbwrapper
|
|
|
116
117
|
- Python 3.10+
|
|
117
118
|
- X Window System
|
|
118
119
|
- Xvfb (`sudo apt-get install xvfb`, `yum install xorg-x11-server-Xvfb`, etc)
|
|
119
|
-
- Support for locking
|
|
120
|
+
- Support for file locking using `fcntl` (POSIX systems)
|
|
120
121
|
|
|
121
122
|
----
|
|
122
123
|
|
|
@@ -244,10 +245,10 @@ finally:
|
|
|
244
245
|
xvfb2.stop()
|
|
245
246
|
```
|
|
246
247
|
|
|
247
|
-
####
|
|
248
|
+
#### Example: headless browser tests:
|
|
248
249
|
|
|
249
|
-
This
|
|
250
|
-
on Chrome with a headless display.
|
|
250
|
+
This uses [`selenium`][selenium-docs] and `xvfbwrapper` to run a test
|
|
251
|
+
on Chrome with a headless display.
|
|
251
252
|
|
|
252
253
|
[selenium-docs]: https://www.selenium.dev/selenium/docs/api/py
|
|
253
254
|
|
|
@@ -6,18 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
## About
|
|
8
8
|
|
|
9
|
-
`xvfbwrapper` is a Python library for
|
|
9
|
+
`xvfbwrapper` is a Python library for managing X11 virtual displays with
|
|
10
|
+
[Xvfb (X virtual framebuffer)](https://x.org/releases/X11R7.7/doc/man/man1/Xvfb.1.xhtml),
|
|
11
|
+
a virtual X server that runs without a physical display. It provides the
|
|
12
|
+
[`Xvfb`][api-docs] class, a Python interface for configuring and controlling the Xvfb
|
|
13
|
+
program.
|
|
14
|
+
|
|
15
|
+
See the [module API documentation][api-docs] for more information.
|
|
10
16
|
|
|
11
17
|
- Copyright (c) 2012-2026 [Corey Goldberg][github-profile]
|
|
12
18
|
- Development: [GitHub][github-repo]
|
|
13
19
|
- Releases: [PyPI][pypi-home]
|
|
14
20
|
- License: [MIT][mit-license]
|
|
15
21
|
|
|
16
|
-
The package provides the `xvfvwrapper.Xvfb` class that is used to interact with
|
|
17
|
-
an Xvfb server.
|
|
18
|
-
|
|
19
|
-
See the [module API documentation][api-docs] for more information.
|
|
20
|
-
|
|
21
22
|
----
|
|
22
23
|
|
|
23
24
|
## Status
|
|
@@ -85,7 +86,7 @@ pip install xvfbwrapper
|
|
|
85
86
|
- Python 3.10+
|
|
86
87
|
- X Window System
|
|
87
88
|
- Xvfb (`sudo apt-get install xvfb`, `yum install xorg-x11-server-Xvfb`, etc)
|
|
88
|
-
- Support for locking
|
|
89
|
+
- Support for file locking using `fcntl` (POSIX systems)
|
|
89
90
|
|
|
90
91
|
----
|
|
91
92
|
|
|
@@ -213,10 +214,10 @@ finally:
|
|
|
213
214
|
xvfb2.stop()
|
|
214
215
|
```
|
|
215
216
|
|
|
216
|
-
####
|
|
217
|
+
#### Example: headless browser tests:
|
|
217
218
|
|
|
218
|
-
This
|
|
219
|
-
on Chrome with a headless display.
|
|
219
|
+
This uses [`selenium`][selenium-docs] and `xvfbwrapper` to run a test
|
|
220
|
+
on Chrome with a headless display.
|
|
220
221
|
|
|
221
222
|
[selenium-docs]: https://www.selenium.dev/selenium/docs/api/py
|
|
222
223
|
|
|
@@ -216,7 +216,8 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
216
216
|
unwriteable_dir = "/etc"
|
|
217
217
|
xvfb = Xvfb(tempdir=unwriteable_dir)
|
|
218
218
|
with self.assertRaisesRegex(
|
|
219
|
-
RuntimeError,
|
|
219
|
+
RuntimeError,
|
|
220
|
+
f"Could not access writable temp directory: {unwriteable_dir}",
|
|
220
221
|
):
|
|
221
222
|
xvfb.start()
|
|
222
223
|
self.assertIsNone(xvfb.proc)
|
|
@@ -225,7 +226,8 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
225
226
|
unknown_dir = "/tmp/some_unknown_path"
|
|
226
227
|
xvfb = Xvfb(tempdir=unknown_dir)
|
|
227
228
|
with self.assertRaisesRegex(
|
|
228
|
-
RuntimeError,
|
|
229
|
+
RuntimeError,
|
|
230
|
+
f"Could not access writable temp directory: {unknown_dir}",
|
|
229
231
|
):
|
|
230
232
|
xvfb.start()
|
|
231
233
|
self.assertIsNone(xvfb.proc)
|
|
@@ -300,7 +302,7 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
300
302
|
self.assertIsNotNone(xvfb.proc)
|
|
301
303
|
|
|
302
304
|
def test_start_fails_with_unknown_kwargs(self):
|
|
303
|
-
xvfb = Xvfb(foo="bar", timeout=
|
|
305
|
+
xvfb = Xvfb(foo="bar", timeout=2)
|
|
304
306
|
expected_cmd_args = [
|
|
305
307
|
"Xvfb",
|
|
306
308
|
r":\d",
|
|
@@ -311,7 +313,8 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
311
313
|
"bar",
|
|
312
314
|
]
|
|
313
315
|
with self.assertRaisesRegex(
|
|
314
|
-
RuntimeError,
|
|
316
|
+
RuntimeError,
|
|
317
|
+
f"Xvfb display did not open: {expected_cmd_args}",
|
|
315
318
|
):
|
|
316
319
|
xvfb.start()
|
|
317
320
|
self.assertIsNone(xvfb.proc)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xvfbwrapper
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.31
|
|
4
4
|
Summary: Manage headless displays with Xvfb (X virtual framebuffer)
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -37,18 +37,19 @@ Dynamic: license-file
|
|
|
37
37
|
|
|
38
38
|
## About
|
|
39
39
|
|
|
40
|
-
`xvfbwrapper` is a Python library for
|
|
40
|
+
`xvfbwrapper` is a Python library for managing X11 virtual displays with
|
|
41
|
+
[Xvfb (X virtual framebuffer)](https://x.org/releases/X11R7.7/doc/man/man1/Xvfb.1.xhtml),
|
|
42
|
+
a virtual X server that runs without a physical display. It provides the
|
|
43
|
+
[`Xvfb`][api-docs] class, a Python interface for configuring and controlling the Xvfb
|
|
44
|
+
program.
|
|
45
|
+
|
|
46
|
+
See the [module API documentation][api-docs] for more information.
|
|
41
47
|
|
|
42
48
|
- Copyright (c) 2012-2026 [Corey Goldberg][github-profile]
|
|
43
49
|
- Development: [GitHub][github-repo]
|
|
44
50
|
- Releases: [PyPI][pypi-home]
|
|
45
51
|
- License: [MIT][mit-license]
|
|
46
52
|
|
|
47
|
-
The package provides the `xvfvwrapper.Xvfb` class that is used to interact with
|
|
48
|
-
an Xvfb server.
|
|
49
|
-
|
|
50
|
-
See the [module API documentation][api-docs] for more information.
|
|
51
|
-
|
|
52
53
|
----
|
|
53
54
|
|
|
54
55
|
## Status
|
|
@@ -116,7 +117,7 @@ pip install xvfbwrapper
|
|
|
116
117
|
- Python 3.10+
|
|
117
118
|
- X Window System
|
|
118
119
|
- Xvfb (`sudo apt-get install xvfb`, `yum install xorg-x11-server-Xvfb`, etc)
|
|
119
|
-
- Support for locking
|
|
120
|
+
- Support for file locking using `fcntl` (POSIX systems)
|
|
120
121
|
|
|
121
122
|
----
|
|
122
123
|
|
|
@@ -244,10 +245,10 @@ finally:
|
|
|
244
245
|
xvfb2.stop()
|
|
245
246
|
```
|
|
246
247
|
|
|
247
|
-
####
|
|
248
|
+
#### Example: headless browser tests:
|
|
248
249
|
|
|
249
|
-
This
|
|
250
|
-
on Chrome with a headless display.
|
|
250
|
+
This uses [`selenium`][selenium-docs] and `xvfbwrapper` to run a test
|
|
251
|
+
on Chrome with a headless display.
|
|
251
252
|
|
|
252
253
|
[selenium-docs]: https://www.selenium.dev/selenium/docs/api/py
|
|
253
254
|
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
"""
|
|
5
|
+
"""Manage headless displays with Xvfb (X virtual framebuffer)
|
|
6
|
+
|
|
7
|
+
This module provides a wrapper around Xvfb (X Virtual Framebuffer) for managing
|
|
8
|
+
virtual X displays.
|
|
9
|
+
|
|
10
|
+
- Releases: https://pypi.org/project/xvfbwrapper
|
|
11
|
+
- Development: https://github.com/cgoldberg/xvfbwrapper
|
|
12
|
+
"""
|
|
6
13
|
|
|
7
14
|
import os
|
|
8
15
|
import platform
|
|
@@ -25,6 +32,11 @@ except ImportError as e:
|
|
|
25
32
|
|
|
26
33
|
|
|
27
34
|
class Xvfb:
|
|
35
|
+
"""Manage an Xvfb (X Virtual Framebuffer) process.
|
|
36
|
+
|
|
37
|
+
Provides an interface for configuring and controlling a virtual X display.
|
|
38
|
+
"""
|
|
39
|
+
|
|
28
40
|
# Maximum value to use for a display. 32-bit maxint is the
|
|
29
41
|
# highest Xvfb currently supports
|
|
30
42
|
MAX_DISPLAY: int = 2147483647
|
|
@@ -39,46 +51,39 @@ class Xvfb:
|
|
|
39
51
|
set_xdg_session_type: bool = False,
|
|
40
52
|
environ: MutableMapping[str, str] | None = None,
|
|
41
53
|
extra_args: Sequence[str] | None = None,
|
|
42
|
-
timeout: float = 10,
|
|
54
|
+
timeout: float | None = 10,
|
|
43
55
|
**kwargs: str,
|
|
44
56
|
) -> None:
|
|
45
57
|
self.width: int = width
|
|
46
58
|
self.height: int = height
|
|
47
59
|
self.colordepth: int = colordepth
|
|
48
60
|
self._tempdir: Path | str = tempdir or tempfile.gettempdir()
|
|
49
|
-
self._timeout: float = timeout
|
|
61
|
+
self._timeout: float | None = timeout
|
|
50
62
|
self.new_display: int | None = display
|
|
51
63
|
self.environ: MutableMapping[str, str] = environ or os.environ
|
|
52
|
-
|
|
53
64
|
if set_xdg_session_type:
|
|
54
65
|
os.environ["XDG_SESSION_TYPE"] = "x11"
|
|
55
|
-
|
|
66
|
+
self.environ["XDG_SESSION_TYPE"] = "x11"
|
|
56
67
|
if not self._xvfb_exists():
|
|
57
68
|
raise FileNotFoundError(
|
|
58
69
|
"Could not find Xvfb. Please install it and try again"
|
|
59
70
|
)
|
|
60
|
-
|
|
61
71
|
self.xvfb_cmd: list[str] = []
|
|
62
|
-
|
|
63
72
|
if not extra_args:
|
|
64
73
|
extra_args = []
|
|
65
|
-
|
|
66
74
|
self.extra_xvfb_args: list[str] = [
|
|
67
75
|
"-screen",
|
|
68
76
|
"0",
|
|
69
77
|
f"{self.width}x{self.height}x{self.colordepth}",
|
|
70
78
|
*extra_args,
|
|
71
79
|
]
|
|
72
|
-
|
|
73
80
|
for key, value in kwargs.items():
|
|
74
81
|
self.extra_xvfb_args += [f"-{key}", value]
|
|
75
|
-
|
|
76
82
|
self.orig_display_var: str | None
|
|
77
83
|
if "DISPLAY" in self.environ:
|
|
78
84
|
self.orig_display_var = self.environ["DISPLAY"]
|
|
79
85
|
else:
|
|
80
86
|
self.orig_display_var = None
|
|
81
|
-
|
|
82
87
|
self.proc: subprocess.Popen[bytes] | None = None
|
|
83
88
|
self._lock_display_file: TextIO | None = None
|
|
84
89
|
|
|
@@ -95,6 +100,8 @@ class Xvfb:
|
|
|
95
100
|
self.stop()
|
|
96
101
|
|
|
97
102
|
def start(self) -> None:
|
|
103
|
+
"""Start Xvfb."""
|
|
104
|
+
|
|
98
105
|
if not os.access(self._tempdir, os.W_OK):
|
|
99
106
|
raise RuntimeError(
|
|
100
107
|
f"Could not access writable temp directory: {self._tempdir}"
|
|
@@ -113,10 +120,11 @@ class Xvfb:
|
|
|
113
120
|
close_fds=True,
|
|
114
121
|
env=dict(self.environ),
|
|
115
122
|
)
|
|
116
|
-
start = time.
|
|
123
|
+
start = time.monotonic()
|
|
117
124
|
while not self._local_display_exists(self.new_display):
|
|
118
125
|
time.sleep(1e-3)
|
|
119
|
-
|
|
126
|
+
elapsed = time.monotonic() - start
|
|
127
|
+
if self._timeout is not None and elapsed > self._timeout:
|
|
120
128
|
self.stop()
|
|
121
129
|
raise RuntimeError(f"Xvfb display did not open: {self.xvfb_cmd}")
|
|
122
130
|
ret_code = self.proc.poll()
|
|
@@ -127,6 +135,19 @@ class Xvfb:
|
|
|
127
135
|
raise RuntimeError(f"Xvfb did not start ({ret_code}): {self.xvfb_cmd}")
|
|
128
136
|
|
|
129
137
|
def stop(self) -> None:
|
|
138
|
+
"""Stop Xvfb and clean up its resources.
|
|
139
|
+
|
|
140
|
+
Terminate the process, escalating to kill if it does not exit within the
|
|
141
|
+
timeout. If the process has already exited, termination is treated as
|
|
142
|
+
successful. If `self._timeout` is set, wait up to that duration for termination
|
|
143
|
+
and, if necessary, an additional duration of the same length after killing the
|
|
144
|
+
process. If `self._timeout` is `None`, wait indefinitely for the process to
|
|
145
|
+
exit and be reaped.
|
|
146
|
+
|
|
147
|
+
This is a one-way lifecycle transition: once called, this object is considered
|
|
148
|
+
stopped and cannot be reused, regardless of whether the process exits
|
|
149
|
+
successfully.
|
|
150
|
+
"""
|
|
130
151
|
if self.proc is None:
|
|
131
152
|
return
|
|
132
153
|
try:
|
|
@@ -134,13 +155,15 @@ class Xvfb:
|
|
|
134
155
|
self.environ.pop("DISPLAY", None)
|
|
135
156
|
else:
|
|
136
157
|
self._set_display(self.orig_display_var)
|
|
137
|
-
|
|
138
|
-
with suppress(OSError):
|
|
158
|
+
with suppress(ProcessLookupError):
|
|
139
159
|
self.proc.terminate()
|
|
160
|
+
try:
|
|
161
|
+
self.proc.wait(self._timeout)
|
|
162
|
+
except subprocess.TimeoutExpired:
|
|
163
|
+
self.proc.kill()
|
|
140
164
|
self.proc.wait(self._timeout)
|
|
141
|
-
|
|
142
|
-
self.proc = None
|
|
143
165
|
finally:
|
|
166
|
+
self.proc = None
|
|
144
167
|
self._cleanup_lock_file()
|
|
145
168
|
|
|
146
169
|
def _xvfb_exists(self) -> bool:
|
|
@@ -160,7 +183,6 @@ class Xvfb:
|
|
|
160
183
|
"""
|
|
161
184
|
if self._lock_display_file is None:
|
|
162
185
|
return
|
|
163
|
-
|
|
164
186
|
self._lock_display_file.close()
|
|
165
187
|
with suppress(OSError):
|
|
166
188
|
Path(self._lock_display_file.name).unlink()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|