code: def spi(self, **spi_args):
          
          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)
docs: 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).