Returns an SPI interface, for the specified SPI *port* and *device*, or
        for the specified pins (*clock_pin*, *mosi_pin*, *miso_pin*, and
        *select_pin*).  Only one of the schemes can be used; attempting to mix
        *port* and *device* with pin numbers will raise
        :exc:`~gpiozero.SPIBadArgs`.

        If the pins specified match the hardware SPI pins (clock on GPIO11,
        MOSI on GPIO10, MISO on GPIO9, and chip select on GPIO8 or GPIO7), and
        the spidev module can be imported, a hardware based interface (using
        spidev) will be returned. Otherwise, a software based interface will be
        returned which will use simple bit-banging to communicate.

        Both interfaces have the same API, support clock polarity and phase
        attributes, and can handle half and full duplex communications, but the
        hardware interface is significantly faster (though for many things this
        doesn't matter).

def spi(self, **spi_args):
        """
        Returns an SPI interface, for the specified SPI *port* and *device*, or
        for the specified pins (*clock_pin*, *mosi_pin*, *miso_pin*, and
        *select_pin*).  Only one of the schemes can be used; attempting to mix
        *port* and *device* with pin numbers will raise
        :exc:`~gpiozero.SPIBadArgs`.

        If the pins specified match the hardware SPI pins (clock on GPIO11,
        MOSI on GPIO10, MISO on GPIO9, and chip select on GPIO8 or GPIO7), and
        the spidev module can be imported, a hardware based interface (using
        spidev) will be returned. Otherwise, a software based interface will be
        returned which will use simple bit-banging to communicate.

        Both interfaces have the same API, support clock polarity and phase
        attributes, and can handle half and full duplex communications, but the
        hardware interface is significantly faster (though for many things this
        doesn't matter).
        """
        spi_args, kwargs = self._extract_spi_args(**spi_args)
        shared = 'shared' if kwargs.pop('shared', False) else 'exclusive'
        if kwargs:
            raise SPIBadArgs(
                'unrecognized keyword argument %s' % kwargs.popitem()[0])
        for port, pins in SPI_HARDWARE_PINS.items():
            if all((
                    spi_args['clock_pin']  == pins['clock'],
                    spi_args['mosi_pin']   == pins['mosi'],
                    spi_args['miso_pin']   == pins['miso'],
                    spi_args['select_pin'] in pins['select'],
                    )):
                try:
                    return self.spi_classes[('hardware', shared)](
                        self, port=port,
                        device=pins['select'].index(spi_args['select_pin'])
                        )
                except Exception as e:
                    warnings.warn(
                        SPISoftwareFallback(
                            'failed to initialize hardware SPI, falling back to '
                            'software (error was: %s)' % str(e)))
                    break
        return self.spi_classes[('software', shared)](self, **spi_args)