ponty / PyVirtualDisplay

Python wrapper for Xvfb, Xephyr and Xvnc
BSD 2-Clause "Simplified" License
697 stars 78 forks source link

`Display was started twice` even though `stop` was called #66

Closed Sebadst closed 2 years ago

Sebadst commented 3 years ago

Hello,

I am having an issue and my use case is the simplest you can imagine.

I am simply executing these lines:

from pyvirtualdisplay import Display

display = Display()
display.start()
display.stop()
display.start()

The last line generates the following exception:

File "/Users/seba/PycharmProjects/fortress/.venv/lib/python3.8/site-packages/pyvirtualdisplay/display.py", line 72, in start
    self._obj.start()
  File "/Users/seba/PycharmProjects/fortress/.venv/lib/python3.8/site-packages/pyvirtualdisplay/abstractdisplay.py", line 148, in start
    raise XStartError(self, "Display was started twice.")
pyvirtualdisplay.abstractdisplay.XStartError: (<pyvirtualdisplay.xvfb.XvfbDisplay object at 0x10d66bd30>, 'Display was started twice.')

It looks like the display hasn't actually been stopped. I am having this issue both in local (Mac OS X machine) and in a GCP machine. Is there anything trivial I am missing?

ponty commented 3 years ago

Display wraps one call to Xvfb, so you can use start() only once on it. Don't call it twice.

from pyvirtualdisplay import Display

display = Display()
display.start()
display.stop()
Sebadst commented 3 years ago

what does the stop() call do exactly then?

ponty commented 3 years ago

start() starts Xvfb stop() stops Xvfb.

Example from README:

from pyvirtualdisplay import Display
disp = Display().start()
# display is active
disp.stop()
# display is stopped
dchiang commented 2 years ago

@Sebadst you can do:

from pyvirtualdisplay import Display

display = Display()
display.start()
display.stop()
del display
display = Display()
display.start()

When calling display.stop() it in fact stops the Xvfb, Xephir or Xvnc displays but the Display object still exists. If I get rid of it and instantiate it again I am able to start the display again.

ponty commented 2 years ago

You don't even have to delete the old object

>>> from pyvirtualdisplay import Display
>>> display = Display()
>>> display.start()
<pyvirtualdisplay.display.Display object at 0x7f0b585e9df0>
>>> display.stop()
<pyvirtualdisplay.display.Display object at 0x7f0b585e9df0>
>>> display = Display()
>>> display.start()
<pyvirtualdisplay.display.Display object at 0x7f0b5868b1f0>

You don't have such problems with context manager (check the README)

from pyvirtualdisplay import Display
with Display() as disp:
    # display is active
    pass
# display is stopped
dchiang commented 2 years ago

Yes, working with context manager is an alternative but in my case I had a multi-threaded scenario with the virtual display as a shared resource where using context wouldn't allow me to implement the flexibility I needed and so I ended up just deleting the old object after stopping the display to be able to start it again when required.