The following example is a valid method of setting the pin factory to mock:
import gpiozero
from gpiozero.pins.mock import MockFactory
gpiozero.Device.pin_factory = MockFactory()
but it only works if import gpiozero is successful, having found a valid pin factory.
If you're on a Raspberry Pi, that's easy - you just install rpi.gpio (even if you don't intend to use it).
If you're on a PC, the following will happen:
And it runs out of options.
The only way to get it to work is to set the pin factory in an environment variable.
It would be nice if you could get past import gpiozero, and if no pin factory is found, it's set to None and fails when you try to create a device. Would that be that possible?
This would also resolve the issue with pinout on PCs needing pin factory set.
+1
This should probably be explained in the documentation in the meantime, I spent a while trying to figure out why the example code here was throwing errors when trying to run on my laptop without a pi connected.
I am frustated with this thing. Why does a mock exist, when you can't even use it on a PC?
In [3]: from gpiozero.pins.mock import MockFactory
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from rpigpio: No module named 'RPi'
'Falling back from %s: %s' % (name, str(e))))
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from rpio: No module named 'RPIO'
'Falling back from %s: %s' % (name, str(e))))
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from pigpio: No module named 'pigpio'
'Falling back from %s: %s' % (name, str(e))))
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from native: unable to open /dev/gpiomem or /dev/mem; upgrade your kernel or run as root
'Falling back from %s: %s' % (name, str(e))))
---------------------------------------------------------------------------
BadPinFactory Traceback (most recent call last)
<ipython-input-3-456f994e72a8> in <module>()
----> 1 from gpiozero.pins.mock import MockFactory
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/pins/mock.py in <module>()
26 PinInvalidPull,
27 )
---> 28 from ..devices import Device
29 from .pi import PiPin
30 from .local import LocalPiFactory
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/__init__.py in <module>()
20 # contamination here ... and besides, have you *seen* the list lately?!
21 from .exc import *
---> 22 from .devices import (
23 Device,
24 GPIODevice,
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py in <module>()
484
485
--> 486 Device.pin_factory = _default_pin_factory()
487 atexit.register(_shutdown)
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py in _default_pin_factory(name)
451 PinFactoryFallback(
452 'Falling back from %s: %s' % (name, str(e))))
--> 453 raise BadPinFactory('Unable to load any default pin factory!')
454 else:
455 # Try with the name verbatim first. If that fails, attempt with the
BadPinFactory: Unable to load any default pin factory!
In [1]: from gpiozero.pins.mock import MockFactory
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from rpigpio: No module named 'RPi'
'Falling back from %s: %s' % (name, str(e))))
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from rpio: No module named 'RPIO'
'Falling back from %s: %s' % (name, str(e))))
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from pigpio: No module named 'pigpio'
'Falling back from %s: %s' % (name, str(e))))
/home/dev/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py:452: PinFactoryFallback: Falling back from native: unable to determine peripheral base
'Falling back from %s: %s' % (name, str(e))))
---------------------------------------------------------------------------
BadPinFactory Traceback (most recent call last)
<ipython-input-1-456f994e72a8> in <module>()
----> 1 from gpiozero.pins.mock import MockFactory
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/__init__.py in <module>()
20 # contamination here ... and besides, have you *seen* the list lately?!
21 from .exc import *
---> 22 from .devices import (
23 Device,
24 GPIODevice,
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py in <module>()
484
485
--> 486 Device.pin_factory = _default_pin_factory()
487 atexit.register(_shutdown)
~/.pyenv/versions/3.6.4/lib/python3.6/site-packages/gpiozero/devices.py in _default_pin_factory(name)
451 PinFactoryFallback(
452 'Falling back from %s: %s' % (name, str(e))))
--> 453 raise BadPinFactory('Unable to load any default pin factory!')
454 else:
455 # Try with the name verbatim first. If that fails, attempt with the
BadPinFactory: Unable to load any default pin factory!
I am frustated with this thing. Why does a mock exist, when you can't even use it on a PC?
@devxpy It does. You just have to set it in an environment variable, like I said above.
I see you're running on Linux so you should be able to launch IPython with the env var set in-line:
$ GPIOZERO_PIN_FACTORY=mock ipython
or export it:
$ export GPIOZERO_PIN_FACTORY=mock
$ ipython
or add it to your .bashrc or equivalent.
This issue points out that it's not currently possible to set the pin factory to mock if the default pin factory fails to load. There are situations where it does, like on a Pi, but as you have also discovered, there are situations where it doesn't, and it's impossible to import without a fatal error.
For now, this is easily resolved by setting the pin factory to mock in an environment variable, and we'll try to fix it for the next release.
@bennuttall. I'm sorry, i was in a virtualenv and was exporting outside the virtualenv
How about if importing gpiozero without a valid pin factory just shows a warning?
>>> import gpiozero
Warning: No default pin factory found. Please set gpiozero.Device.pin_factory
>>> led = LED(2)
InvalidPinFactory: No default pin factory set
>>> gpiozero.Device.pin_factory = MockFactory()
>>> led = LED(2)
>>> led
<gpiozero.LED object on pin GPIO2, active_high=True, is_active=False>
@waveform80 agrees - let's get this in v1.5
In case anyone else hits this, hacky workaround:
import os
os.environ['GPIOZERO_PIN_FACTORY'] = os.environ.get('GPIOZERO_PIN_FACTORY', 'mock')
import gpiozero
NOTE from gpiozero.pins.mock import MockFactory as documented in https://gpiozero.readthedocs.io/en/stable/api_pins.html#mock-pins isn't needed with this (nor does it work without this). Setting the explicit mock factory is not required.
Note that if/when this gets fixed, the note I added to the docs will need removing: https://gpiozero.readthedocs.io/en/latest/remote_gpio.html#pin-factories
Note that if/when this gets fixed, the note I added to the docs will need removing: https://gpiozero.readthedocs.io/en/latest/remote_gpio.html#pin-factories
Seems that #722 forgot to do that? :wink:
EDIT: But #727 fixed this :+1:
Most helpful comment
In case anyone else hits this, hacky workaround:
NOTE
from gpiozero.pins.mock import MockFactoryas documented in https://gpiozero.readthedocs.io/en/stable/api_pins.html#mock-pins isn't needed with this (nor does it work without this). Setting the explicit mock factory is not required.