xvfbwrapper 0.2.31__tar.gz → 0.2.33__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xvfbwrapper
3
- Version: 0.2.31
3
+ Version: 0.2.33
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
- [`Xvfb`][api-docs] class, a Python interface for configuring and controlling the Xvfb
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
 
@@ -245,10 +244,10 @@ finally:
245
244
  xvfb2.stop()
246
245
  ```
247
246
 
248
- #### Example: headless browser tests:
247
+ #### Headless browser tests:
249
248
 
250
249
  This uses [`selenium`][selenium-docs] and `xvfbwrapper` to run a test
251
- on Chrome with a headless display.
250
+ on Chrome inside a headless display.
252
251
 
253
252
  [selenium-docs]: https://www.selenium.dev/selenium/docs/api/py
254
253
 
@@ -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
- [`Xvfb`][api-docs] class, a Python interface for configuring and controlling the Xvfb
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
 
@@ -214,10 +213,10 @@ finally:
214
213
  xvfb2.stop()
215
214
  ```
216
215
 
217
- #### Example: headless browser tests:
216
+ #### Headless browser tests:
218
217
 
219
218
  This uses [`selenium`][selenium-docs] and `xvfbwrapper` to run a test
220
- on Chrome with a headless display.
219
+ on Chrome inside a headless display.
221
220
 
222
221
  [selenium-docs]: https://www.selenium.dev/selenium/docs/api/py
223
222
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xvfbwrapper"
7
- version = "0.2.31"
7
+ version = "0.2.33"
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
  ]
@@ -5,6 +5,7 @@
5
5
  """Tests for xvfbwrapper."""
6
6
 
7
7
  import os
8
+ import re
8
9
  import sys
9
10
  import tempfile
10
11
  import unittest
@@ -167,17 +168,6 @@ class TestXvfb(XvfbCleanTestCase):
167
168
  self.assertEqual(orig_display, os.environ["DISPLAY"])
168
169
  self.assertIsNone(xvfb.proc)
169
170
 
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
171
  def test_stop_if_not_running_doesnt_raise_error(self):
182
172
  xvfb = Xvfb()
183
173
  xvfb.stop()
@@ -269,7 +259,7 @@ class TestXvfb(XvfbCleanTestCase):
269
259
  self.assertIsNotNone(xvfb.proc)
270
260
  with self.assertRaisesRegex(
271
261
  RuntimeError,
272
- f"Could not lock display :{display_num}",
262
+ f"Could not lock display: {display_num}",
273
263
  ):
274
264
  xvfb2.start()
275
265
 
@@ -319,6 +309,28 @@ class TestXvfb(XvfbCleanTestCase):
319
309
  xvfb.start()
320
310
  self.assertIsNone(xvfb.proc)
321
311
 
312
+ def test_start_already_running(self):
313
+ xvfb = Xvfb()
314
+ self.addCleanup(xvfb.stop)
315
+ xvfb.start()
316
+ with self.assertRaisesRegex(
317
+ RuntimeError,
318
+ re.escape(f"Xvfb is already running (PID: {xvfb.proc.pid})"),
319
+ ):
320
+ xvfb.start()
321
+ self.assertIsNotNone(xvfb.proc)
322
+
323
+ def test_multiple_lifecycles(self):
324
+ xvfb = Xvfb()
325
+ self.addCleanup(xvfb.stop)
326
+ xvfb.start()
327
+ pid1 = xvfb.proc.pid
328
+ xvfb.stop()
329
+ xvfb.start()
330
+ pid2 = xvfb.proc.pid
331
+ self.assertIsNotNone(xvfb.proc)
332
+ self.assertNotEqual(pid1, pid2)
333
+
322
334
  def test_get_next_unused_display_does_not_reuse_lock(self):
323
335
  xvfb = Xvfb()
324
336
  xvfb2 = Xvfb()
@@ -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.31
3
+ Version: 0.2.33
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
- [`Xvfb`][api-docs] class, a Python interface for configuring and controlling the Xvfb
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
 
@@ -245,10 +244,10 @@ finally:
245
244
  xvfb2.stop()
246
245
  ```
247
246
 
248
- #### Example: headless browser tests:
247
+ #### Headless browser tests:
249
248
 
250
249
  This uses [`selenium`][selenium-docs] and `xvfbwrapper` to run a test
251
- on Chrome with a headless display.
250
+ on Chrome inside a headless display.
252
251
 
253
252
  [selenium-docs]: https://www.selenium.dev/selenium/docs/api/py
254
253
 
@@ -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 :{self.new_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}"
@@ -143,10 +146,6 @@ class Xvfb:
143
146
  and, if necessary, an additional duration of the same length after killing the
144
147
  process. If `self._timeout` is `None`, wait indefinitely for the process to
145
148
  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
149
  """
151
150
  if self.proc is None:
152
151
  return
File without changes
File without changes
File without changes