def __init__(self, pin_ids, blocking_mode):
        '''
            Creates a group of GPIO pins for blocking read (input) with read
            pin bit values expressed as Boolean values in an iterable
            sequence ordered as per the pin id sequence passed as pin_ids.

            pin_ids should be an iterable sequence of pin id values of the
            pins to be in group. It should contain at least one value
            otherwise a gpioerror.PinGroupIdsInvalidError exception is
            raised. Additionally any exception that might be raised by
            pin.open_pin may be raised other than those relating to bad mode
            values.
            
            blocking_mode should be one of the blocking mode characters that
            relate to blocking on input pin edge events - 'R', 'F' or 'B'.
            Other strings will raise a gpioerror.PinBlockModeInvalidError
            while values of other types will rasie a TypeError.
            
            On successful return an open pin group object will be open and
            ready to read from. Otherwise it will be closed.
        '''
        if blocking_mode==BlockMode.non_blocking_open_mode():
            raise PinBlockModeInvalidError
        super(PinListBlockingReader, self).__init__(pin_ids,'r'+blocking_mode)
        self._cached_value = []
        self._fd_to_pin_index = {}
        fds = self.file_descriptors()
        for i in range(len(fds)):
            self._cached_value.append(False)
            self._fd_to_pin_index[fds[i]] = i
    def __init__(self, pin_ids, blocking_mode):
        '''
            Creates a group of GPIO pins for blocking read (input) with read
            pin bit values expressed as bits in an integer with bit 0
            indicating the value for the first pin in the group, and bit 1
            the next and so on. Pins are ordered arounding to the position of
            their id in the pin_ids argument.

            pin_ids should be an iterable sequence of pin id values of the
            pins to be in group. It should contain at least one value
            otherwise a gpioerror.PinGroupIdsInvalidError exception is
            raised. Additionally any exception that might be raised by
            pin.open_pin may be raised other than those relating to bad mode
            values.
            
            blocking_mode should be one of the blocking mode characters that
            relate to blocking on input pin edge events - 'R', 'F' or 'B'.
            Other strings will raise a gpioerror.PinBlockModeInvalidError
            while values of other types will rasie a TypeError.
            
            On successful return an open pin group object will be open and
            ready to read from. Otherwise it will be closed.
        '''
        if blocking_mode==BlockMode.non_blocking_open_mode():
            raise PinBlockModeInvalidError
        super(PinWordBlockingReader, self).__init__(pin_ids,'r'+blocking_mode)
        self._cached_value = 0 #can be any int between 0 & 2**len(self._pins)-1
        self._fd_to_bit_value = {}
        fds = self.file_descriptors()
        for i in range(len(fds)):
            self._fd_to_bit_value[fds[i]] = 2**i
def open_pingroup(pin_ids, mode='rNI'):
    '''
        Open a group of GPIO pins managed as a single entity for IO purposes.

        Factory function creating GPIO pin group objects of appropriate types
        for the requested operational mode.

        pin_ids is sequence of BCM2835 (or BCM2807) GPIO id numbers, probably
        ones connected to the Raspberry Pi P1 connector. They can be PinId type
        instances or int values (which will be validated).

        mode is optional. 
        If passed it should be a string of 0 to 3 characters in length.
        A single mode applies to all pins in a group.
        The first character of the mode string indicates the desired data
        direction: read/input: 'r' or write/output: 'w'
        The second, optional, character indicates the blocking mode:
        block on nothing (non-blocking): 'N', 
        block on rising edge events (0 to 1 transitions): 'R',
        block on falling edge events (1 to 0 transitions): 'F',
        block on events for both edges: 'B'.
        The default is 'N', non-blocking, waiting on no events and is the
        only valid option when opening a pin for writing/output.
        The third optional character specifies the format of pin values in
        the group as presented to write or returned from read operations.
        I indicates the values are multiplexed into the bits of an integer
        word with bit 0 having the value of the GPIO pin specified by the
        first id in the passed pin_ids sequence, bit 1 the values of the pin
        specified by the next pin id in the passed pin_ids sequence.
        S indicates the values of the pins are presented as discrete Boolean
        values passed or returned in a sequence, with the value of the nth
        element being the value of the GPIO pin having the id that is the nth
        element of the pin_ids parameter.
        The default format is I.
        No mode argument or an empty mode string defaults to read/input,
        non-blocking, integer format - mode 'rNI'

        Value modes strings are:
        'rNI','rNS', 'rRI','rRS', 'rFI','rFS','rBI','rBS','wNI','wNS',
        '', 'r', 'rN', 'rI' : all the same as 'rNI'
        'rS'                : same as 'rNS'
        'rR'                : same as 'rRI'
        'rF'                : same as 'rFI'
        'rB'                : same as 'rBI'
        'w', 'wN', 'wI'     : all the same as 'wNI'
        'wS'                : same as 'wNS'
    '''
    mode_len = len(mode)
    direction_mode = DirectionMode(DirectionMode.read_open_mode())
    edge_mode = BlockMode(BlockMode.non_blocking_open_mode())
    format_mode = FormatMode(FormatMode.integer_open_mode())
    if mode_len >= 1:
        direction_mode = DirectionMode(mode[0])
        if mode_len == 3:
            edge_mode = BlockMode(mode[1])
            format_mode = FormatMode(mode[2])
        elif mode_len == 2: # 2nd char. could be blocking mode or format mode
            try:
                edge_mode = BlockMode(mode[1])
            except PinBlockModeInvalidError:
                try:
                    format_mode = FormatMode(mode[1])
                except PinGroupFormatModeInvalidError:
                    raise PinGroupOpenModeInvalidError
        elif mode_len > 3:
            raise PinGroupOpenModeInvalidError
    if direction_mode.is_write():
        if edge_mode.is_blocking(): # any other blocking mode meaningless for output
            raise PinBlockModeInvalidError
        if format_mode.is_integer():
            return PinWordWriter(pin_ids)
        else:
            return PinListWriter(pin_ids)
    else: # direction mode only read or write...
        assert( direction_mode.is_read() )
        if edge_mode.is_blocking():
            if format_mode.is_integer():
                return PinWordBlockingReader(pin_ids, edge_mode.open_mode_value())
            else:
                return PinListBlockingReader(pin_ids, edge_mode.open_mode_value())
        else:
            if format_mode.is_integer():
                return PinWordReader(pin_ids)
            else:
                return PinListReader(pin_ids)