xvfbwrapper 0.2.32__tar.gz → 0.2.34__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.32/xvfbwrapper.egg-info → xvfbwrapper-0.2.34}/PKG-INFO +16 -13
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/README.md +15 -12
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/pyproject.toml +5 -1
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/test_xvfb.py +32 -41
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/tox.ini +19 -0
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34/xvfbwrapper.egg-info}/PKG-INFO +16 -13
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/xvfbwrapper.py +13 -9
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/LICENSE +0 -0
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/MANIFEST.in +0 -0
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/setup.cfg +0 -0
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/xvfbwrapper.egg-info/SOURCES.txt +0 -0
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/xvfbwrapper.egg-info/dependency_links.txt +0 -0
- {xvfbwrapper-0.2.32 → xvfbwrapper-0.2.34}/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.34
|
|
4
4
|
Summary: Manage headless displays with Xvfb (X virtual framebuffer)
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -39,9 +39,8 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
`xvfbwrapper` is a Python library for managing X11 virtual displays with
|
|
41
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
|
-
|
|
44
|
-
program.
|
|
42
|
+
a virtual X server that runs without a physical display. It provides the `Xvfb` class,
|
|
43
|
+
a Python interface for configuring and controlling the Xvfb program.
|
|
45
44
|
|
|
46
45
|
See the [module API documentation][api-docs] for more information.
|
|
47
46
|
|
|
@@ -64,13 +63,21 @@ See the [module API documentation][api-docs] for more information.
|
|
|
64
63
|
</td>
|
|
65
64
|
</tr>
|
|
66
65
|
<tr>
|
|
67
|
-
<td>
|
|
66
|
+
<td>Tests (CI)</td>
|
|
68
67
|
<td>
|
|
69
68
|
<a href="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/test.yml">
|
|
70
69
|
<img src="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/test.yml/badge.svg">
|
|
71
70
|
</a>
|
|
72
71
|
</td>
|
|
73
72
|
</tr>
|
|
73
|
+
<tr>
|
|
74
|
+
<td>API Docs (CI)</td>
|
|
75
|
+
<td>
|
|
76
|
+
<a href="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/docs.yml">
|
|
77
|
+
<img src="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/docs.yml/badge.svg">
|
|
78
|
+
</a>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
74
81
|
<tr>
|
|
75
82
|
<td>Supported Python Versions</td>
|
|
76
83
|
<td>
|
|
@@ -218,12 +225,10 @@ xvfb.start() # Xvfb will be called with the `ttyxx -nocursor +extension RANDR`
|
|
|
218
225
|
|
|
219
226
|
#### Multithreaded execution:
|
|
220
227
|
|
|
221
|
-
To run several Xvfb displays at the same time, you can use the `environ`
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
`
|
|
225
|
-
environment, you must use the copy method of `os.environ` and not simply
|
|
226
|
-
assign a new variable to `os.environ`:
|
|
228
|
+
To run several Xvfb displays at the same time, you can use the `environ` keyword
|
|
229
|
+
when starting the `Xvfb` instances. This provides isolation between processes or
|
|
230
|
+
threads. If you wish to inherit your current environment, you must use the copy
|
|
231
|
+
method of `os.environ` and not simply assign a new variable to `os.environ`:
|
|
227
232
|
|
|
228
233
|
```python
|
|
229
234
|
import os
|
|
@@ -286,8 +291,6 @@ if __name__ == "__main__":
|
|
|
286
291
|
|
|
287
292
|
## Development
|
|
288
293
|
|
|
289
|
-
- Git: [github.com/cgoldberg/xvfbwrapper.git][github-repo]
|
|
290
|
-
|
|
291
294
|
- Create a virtual env and install required testing packages:
|
|
292
295
|
|
|
293
296
|
```
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`xvfbwrapper` is a Python library for managing X11 virtual displays with
|
|
10
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
|
-
|
|
13
|
-
program.
|
|
11
|
+
a virtual X server that runs without a physical display. It provides the `Xvfb` class,
|
|
12
|
+
a Python interface for configuring and controlling the Xvfb program.
|
|
14
13
|
|
|
15
14
|
See the [module API documentation][api-docs] for more information.
|
|
16
15
|
|
|
@@ -33,13 +32,21 @@ See the [module API documentation][api-docs] for more information.
|
|
|
33
32
|
</td>
|
|
34
33
|
</tr>
|
|
35
34
|
<tr>
|
|
36
|
-
<td>
|
|
35
|
+
<td>Tests (CI)</td>
|
|
37
36
|
<td>
|
|
38
37
|
<a href="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/test.yml">
|
|
39
38
|
<img src="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/test.yml/badge.svg">
|
|
40
39
|
</a>
|
|
41
40
|
</td>
|
|
42
41
|
</tr>
|
|
42
|
+
<tr>
|
|
43
|
+
<td>API Docs (CI)</td>
|
|
44
|
+
<td>
|
|
45
|
+
<a href="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/docs.yml">
|
|
46
|
+
<img src="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/docs.yml/badge.svg">
|
|
47
|
+
</a>
|
|
48
|
+
</td>
|
|
49
|
+
</tr>
|
|
43
50
|
<tr>
|
|
44
51
|
<td>Supported Python Versions</td>
|
|
45
52
|
<td>
|
|
@@ -187,12 +194,10 @@ xvfb.start() # Xvfb will be called with the `ttyxx -nocursor +extension RANDR`
|
|
|
187
194
|
|
|
188
195
|
#### Multithreaded execution:
|
|
189
196
|
|
|
190
|
-
To run several Xvfb displays at the same time, you can use the `environ`
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
`
|
|
194
|
-
environment, you must use the copy method of `os.environ` and not simply
|
|
195
|
-
assign a new variable to `os.environ`:
|
|
197
|
+
To run several Xvfb displays at the same time, you can use the `environ` keyword
|
|
198
|
+
when starting the `Xvfb` instances. This provides isolation between processes or
|
|
199
|
+
threads. If you wish to inherit your current environment, you must use the copy
|
|
200
|
+
method of `os.environ` and not simply assign a new variable to `os.environ`:
|
|
196
201
|
|
|
197
202
|
```python
|
|
198
203
|
import os
|
|
@@ -255,8 +260,6 @@ if __name__ == "__main__":
|
|
|
255
260
|
|
|
256
261
|
## Development
|
|
257
262
|
|
|
258
|
-
- Git: [github.com/cgoldberg/xvfbwrapper.git][github-repo]
|
|
259
|
-
|
|
260
263
|
- Create a virtual env and install required testing packages:
|
|
261
264
|
|
|
262
265
|
```
|
|
@@ -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.34"
|
|
8
8
|
description = "Manage headless displays with Xvfb (X virtual framebuffer)"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
@@ -40,6 +40,9 @@ download = "https://pypi.org/project/xvfbwrapper"
|
|
|
40
40
|
dev = [
|
|
41
41
|
"tox",
|
|
42
42
|
]
|
|
43
|
+
docs = [
|
|
44
|
+
"pdoc",
|
|
45
|
+
]
|
|
43
46
|
lint = [
|
|
44
47
|
"ruff",
|
|
45
48
|
]
|
|
@@ -60,6 +63,7 @@ py-modules = ["xvfbwrapper"]
|
|
|
60
63
|
|
|
61
64
|
[tool.pytest]
|
|
62
65
|
addopts = ["-v"]
|
|
66
|
+
filterwarnings = ["default"]
|
|
63
67
|
|
|
64
68
|
[tool.ruff]
|
|
65
69
|
line-length = 88
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"""Tests for xvfbwrapper."""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
-
import
|
|
8
|
+
import re
|
|
9
9
|
import tempfile
|
|
10
10
|
import unittest
|
|
11
11
|
from contextlib import suppress
|
|
@@ -167,17 +167,6 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
167
167
|
self.assertEqual(orig_display, os.environ["DISPLAY"])
|
|
168
168
|
self.assertIsNone(xvfb.proc)
|
|
169
169
|
|
|
170
|
-
def test_start_multiple_times(self):
|
|
171
|
-
xvfb = Xvfb()
|
|
172
|
-
xvfb.start()
|
|
173
|
-
self.addCleanup(xvfb.stop)
|
|
174
|
-
pid1 = xvfb.proc.pid
|
|
175
|
-
xvfb.stop()
|
|
176
|
-
xvfb.start()
|
|
177
|
-
pid2 = xvfb.proc.pid
|
|
178
|
-
self.assertIsNotNone(xvfb.proc)
|
|
179
|
-
self.assertNotEqual(pid1, pid2)
|
|
180
|
-
|
|
181
170
|
def test_stop_if_not_running_doesnt_raise_error(self):
|
|
182
171
|
xvfb = Xvfb()
|
|
183
172
|
xvfb.stop()
|
|
@@ -216,8 +205,7 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
216
205
|
unwriteable_dir = "/etc"
|
|
217
206
|
xvfb = Xvfb(tempdir=unwriteable_dir)
|
|
218
207
|
with self.assertRaisesRegex(
|
|
219
|
-
RuntimeError,
|
|
220
|
-
f"Could not access writable temp directory: {unwriteable_dir}",
|
|
208
|
+
RuntimeError, f"Could not access writable temp directory: {unwriteable_dir}"
|
|
221
209
|
):
|
|
222
210
|
xvfb.start()
|
|
223
211
|
self.assertIsNone(xvfb.proc)
|
|
@@ -226,8 +214,7 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
226
214
|
unknown_dir = "/tmp/some_unknown_path"
|
|
227
215
|
xvfb = Xvfb(tempdir=unknown_dir)
|
|
228
216
|
with self.assertRaisesRegex(
|
|
229
|
-
RuntimeError,
|
|
230
|
-
f"Could not access writable temp directory: {unknown_dir}",
|
|
217
|
+
RuntimeError, f"Could not access writable temp directory: {unknown_dir}"
|
|
231
218
|
):
|
|
232
219
|
xvfb.start()
|
|
233
220
|
self.assertIsNone(xvfb.proc)
|
|
@@ -268,8 +255,7 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
268
255
|
self.assertEqual(xvfb.new_display, display_num)
|
|
269
256
|
self.assertIsNotNone(xvfb.proc)
|
|
270
257
|
with self.assertRaisesRegex(
|
|
271
|
-
RuntimeError,
|
|
272
|
-
f"Could not lock display :{display_num}",
|
|
258
|
+
RuntimeError, f"Could not lock display: {display_num}"
|
|
273
259
|
):
|
|
274
260
|
xvfb2.start()
|
|
275
261
|
|
|
@@ -313,12 +299,32 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
313
299
|
"bar",
|
|
314
300
|
]
|
|
315
301
|
with self.assertRaisesRegex(
|
|
316
|
-
RuntimeError,
|
|
317
|
-
f"Xvfb display did not open: {expected_cmd_args}",
|
|
302
|
+
RuntimeError, f"Xvfb display did not open: {expected_cmd_args}"
|
|
318
303
|
):
|
|
319
304
|
xvfb.start()
|
|
320
305
|
self.assertIsNone(xvfb.proc)
|
|
321
306
|
|
|
307
|
+
def test_start_already_running(self):
|
|
308
|
+
xvfb = Xvfb()
|
|
309
|
+
self.addCleanup(xvfb.stop)
|
|
310
|
+
xvfb.start()
|
|
311
|
+
with self.assertRaisesRegex(
|
|
312
|
+
RuntimeError, re.escape(f"Xvfb is already running (PID: {xvfb.proc.pid})")
|
|
313
|
+
):
|
|
314
|
+
xvfb.start()
|
|
315
|
+
self.assertIsNotNone(xvfb.proc)
|
|
316
|
+
|
|
317
|
+
def test_multiple_lifecycles(self):
|
|
318
|
+
xvfb = Xvfb()
|
|
319
|
+
self.addCleanup(xvfb.stop)
|
|
320
|
+
xvfb.start()
|
|
321
|
+
pid1 = xvfb.proc.pid
|
|
322
|
+
xvfb.stop()
|
|
323
|
+
xvfb.start()
|
|
324
|
+
pid2 = xvfb.proc.pid
|
|
325
|
+
self.assertIsNotNone(xvfb.proc)
|
|
326
|
+
self.assertNotEqual(pid1, pid2)
|
|
327
|
+
|
|
322
328
|
def test_get_next_unused_display_does_not_reuse_lock(self):
|
|
323
329
|
xvfb = Xvfb()
|
|
324
330
|
xvfb2 = Xvfb()
|
|
@@ -330,21 +336,10 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
330
336
|
with patch("xvfbwrapper.randint", side_effect=side_effect) as mockrandint:
|
|
331
337
|
self.assertEqual(xvfb._get_next_unused_display(), 11)
|
|
332
338
|
self.assertEqual(mockrandint.call_count, 1)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
# (like PyPy).
|
|
338
|
-
with self.assertWarns(ResourceWarning):
|
|
339
|
-
self.assertEqual(xvfb2._get_next_unused_display(), 22)
|
|
340
|
-
self.assertEqual(mockrandint.call_count, 3)
|
|
341
|
-
self.assertEqual(xvfb3._get_next_unused_display(), 33)
|
|
342
|
-
self.assertEqual(mockrandint.call_count, 10)
|
|
343
|
-
else:
|
|
344
|
-
self.assertEqual(xvfb2._get_next_unused_display(), 22)
|
|
345
|
-
self.assertEqual(mockrandint.call_count, 3)
|
|
346
|
-
self.assertEqual(xvfb3._get_next_unused_display(), 33)
|
|
347
|
-
self.assertEqual(mockrandint.call_count, 10)
|
|
339
|
+
self.assertEqual(xvfb2._get_next_unused_display(), 22)
|
|
340
|
+
self.assertEqual(mockrandint.call_count, 3)
|
|
341
|
+
self.assertEqual(xvfb3._get_next_unused_display(), 33)
|
|
342
|
+
self.assertEqual(mockrandint.call_count, 10)
|
|
348
343
|
|
|
349
344
|
def test_environ_keyword_isolates_environment_modification(self):
|
|
350
345
|
# Check that start and stop methods modified the environ dict if
|
|
@@ -368,10 +363,7 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
368
363
|
self.addCleanup(xvfb.stop)
|
|
369
364
|
xvfb.start()
|
|
370
365
|
proc = psutil.Process(xvfb.proc.pid)
|
|
371
|
-
self.assertEqual(
|
|
372
|
-
proc.environ().get("XVFBWRAPPER_TEST_MARKER"),
|
|
373
|
-
marker_value,
|
|
374
|
-
)
|
|
366
|
+
self.assertEqual(proc.environ().get("XVFBWRAPPER_TEST_MARKER"), marker_value)
|
|
375
367
|
|
|
376
368
|
def test_start_failure_without_initial_display_env(self):
|
|
377
369
|
# Provide a custom env *without* DISPLAY so orig_display_var == None
|
|
@@ -392,8 +384,7 @@ class TestXvfb(XvfbCleanTestCase):
|
|
|
392
384
|
with (
|
|
393
385
|
patch.object(xvfb, "_local_display_exists", return_value=False),
|
|
394
386
|
self.assertRaisesRegex(
|
|
395
|
-
RuntimeError,
|
|
396
|
-
f"Xvfb display did not open: {expected_cmd_args}",
|
|
387
|
+
RuntimeError, f"Xvfb display did not open: {expected_cmd_args}"
|
|
397
388
|
),
|
|
398
389
|
):
|
|
399
390
|
xvfb.start()
|
|
@@ -57,3 +57,22 @@ dependency_groups =
|
|
|
57
57
|
skip_install = true
|
|
58
58
|
commands =
|
|
59
59
|
mypy --install-types --non-interactive {posargs:.}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
[testenv:docs]
|
|
63
|
+
description = build docs
|
|
64
|
+
platform = .*
|
|
65
|
+
allowlist_externals =
|
|
66
|
+
echo
|
|
67
|
+
dependency_groups =
|
|
68
|
+
docs
|
|
69
|
+
commands =
|
|
70
|
+
echo "Cleaning {tox_root}/build/"
|
|
71
|
+
{envpython} -c "import shutil; shutil.rmtree('build', ignore_errors=True)"
|
|
72
|
+
echo "Building docs..."
|
|
73
|
+
{envpython} -m pdoc xvfbwrapper \
|
|
74
|
+
--output-dir build/docs \
|
|
75
|
+
--template-directory docs/pdoc \
|
|
76
|
+
--no-search \
|
|
77
|
+
--docformat markdown
|
|
78
|
+
echo "Docs successfully built: {tox_root}/build/docs/index.html"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xvfbwrapper
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.34
|
|
4
4
|
Summary: Manage headless displays with Xvfb (X virtual framebuffer)
|
|
5
5
|
Author: Corey Goldberg
|
|
6
6
|
Maintainer: Corey Goldberg
|
|
@@ -39,9 +39,8 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
`xvfbwrapper` is a Python library for managing X11 virtual displays with
|
|
41
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
|
-
|
|
44
|
-
program.
|
|
42
|
+
a virtual X server that runs without a physical display. It provides the `Xvfb` class,
|
|
43
|
+
a Python interface for configuring and controlling the Xvfb program.
|
|
45
44
|
|
|
46
45
|
See the [module API documentation][api-docs] for more information.
|
|
47
46
|
|
|
@@ -64,13 +63,21 @@ See the [module API documentation][api-docs] for more information.
|
|
|
64
63
|
</td>
|
|
65
64
|
</tr>
|
|
66
65
|
<tr>
|
|
67
|
-
<td>
|
|
66
|
+
<td>Tests (CI)</td>
|
|
68
67
|
<td>
|
|
69
68
|
<a href="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/test.yml">
|
|
70
69
|
<img src="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/test.yml/badge.svg">
|
|
71
70
|
</a>
|
|
72
71
|
</td>
|
|
73
72
|
</tr>
|
|
73
|
+
<tr>
|
|
74
|
+
<td>API Docs (CI)</td>
|
|
75
|
+
<td>
|
|
76
|
+
<a href="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/docs.yml">
|
|
77
|
+
<img src="https://github.com/cgoldberg/xvfbwrapper/actions/workflows/docs.yml/badge.svg">
|
|
78
|
+
</a>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
74
81
|
<tr>
|
|
75
82
|
<td>Supported Python Versions</td>
|
|
76
83
|
<td>
|
|
@@ -218,12 +225,10 @@ xvfb.start() # Xvfb will be called with the `ttyxx -nocursor +extension RANDR`
|
|
|
218
225
|
|
|
219
226
|
#### Multithreaded execution:
|
|
220
227
|
|
|
221
|
-
To run several Xvfb displays at the same time, you can use the `environ`
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
`
|
|
225
|
-
environment, you must use the copy method of `os.environ` and not simply
|
|
226
|
-
assign a new variable to `os.environ`:
|
|
228
|
+
To run several Xvfb displays at the same time, you can use the `environ` keyword
|
|
229
|
+
when starting the `Xvfb` instances. This provides isolation between processes or
|
|
230
|
+
threads. If you wish to inherit your current environment, you must use the copy
|
|
231
|
+
method of `os.environ` and not simply assign a new variable to `os.environ`:
|
|
227
232
|
|
|
228
233
|
```python
|
|
229
234
|
import os
|
|
@@ -286,8 +291,6 @@ if __name__ == "__main__":
|
|
|
286
291
|
|
|
287
292
|
## Development
|
|
288
293
|
|
|
289
|
-
- Git: [github.com/cgoldberg/xvfbwrapper.git][github-repo]
|
|
290
|
-
|
|
291
294
|
- Create a virtual env and install required testing packages:
|
|
292
295
|
|
|
293
296
|
```
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Copyright (c) 2012-2026 Corey Goldberg
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
"""Manage headless displays with Xvfb (X virtual framebuffer)
|
|
6
5
|
|
|
7
6
|
This module provides a wrapper around Xvfb (X Virtual Framebuffer) for managing
|
|
@@ -102,13 +101,17 @@ class Xvfb:
|
|
|
102
101
|
def start(self) -> None:
|
|
103
102
|
"""Start Xvfb."""
|
|
104
103
|
|
|
104
|
+
if self.proc is not None:
|
|
105
|
+
raise RuntimeError(
|
|
106
|
+
f"Xvfb is already running (PID: {self.proc.pid})",
|
|
107
|
+
)
|
|
105
108
|
if not os.access(self._tempdir, os.W_OK):
|
|
106
109
|
raise RuntimeError(
|
|
107
110
|
f"Could not access writable temp directory: {self._tempdir}"
|
|
108
111
|
)
|
|
109
112
|
if self.new_display is not None:
|
|
110
113
|
if not self._get_lock_for_display(self.new_display):
|
|
111
|
-
raise RuntimeError(f"Could not lock display
|
|
114
|
+
raise RuntimeError(f"Could not lock display: {self.new_display}")
|
|
112
115
|
else:
|
|
113
116
|
self.new_display = self._get_next_unused_display()
|
|
114
117
|
display_var = f":{self.new_display}"
|
|
@@ -192,16 +195,17 @@ class Xvfb:
|
|
|
192
195
|
"""
|
|
193
196
|
tempfile_path = Path(self._tempdir, f".X{display}-lock")
|
|
194
197
|
try:
|
|
195
|
-
|
|
198
|
+
lock_file = tempfile_path.open("w")
|
|
196
199
|
except PermissionError:
|
|
197
200
|
return False
|
|
201
|
+
try:
|
|
202
|
+
fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
203
|
+
except BlockingIOError:
|
|
204
|
+
lock_file.close()
|
|
205
|
+
return False
|
|
198
206
|
else:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
except BlockingIOError:
|
|
202
|
-
return False
|
|
203
|
-
else:
|
|
204
|
-
return True
|
|
207
|
+
self._lock_display_file = lock_file
|
|
208
|
+
return True
|
|
205
209
|
|
|
206
210
|
def _get_next_unused_display(self) -> int:
|
|
207
211
|
"""Randomly choose a display number and try to acquire a lock for it.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|