def _check_index(value: int, length: int, bound: bool) -> int: if not isinstance(value, int): raise GridIndexError('Indices must be integers, found {}.'.format(value)) if value < 0: value = value + length if value < 0 + bound or value >= length + bound: raise GridIndexError('Index out of range.') return value
def _key_to_rows_columns(self, key: Any) -> Tuple[int, int, int, int]: # TODO spaghetti code cleanup needed! if isinstance(key, tuple): if len(key) == 1: rows_cols = self._key_to_rows_columns(key[0]) else: try: row_key, column_key = key except ValueError: raise GridIndexError( 'Index must be 1 or 2 values, found {}'.format(key)) if isinstance(row_key, int): row_start = _check_index(row_key, len(self.rows), False) row_end = row_start + 1 elif isinstance(row_key, slice): row_start, row_end = _slice_to_start_end( row_key, len(self.rows)) row_start = _check_index(row_start, len(self.rows), False) row_end = _check_index(row_end, len(self.rows), True) else: raise GridIndexError( 'Cannot index with {}, pass in a int or a slice.'. format(row_key)) if isinstance(column_key, int): column_start = _check_index(column_key, len(self.columns), False) column_end = column_start + 1 elif isinstance(column_key, slice): column_start, column_end = _slice_to_start_end( column_key, len(self.columns)) column_start = _check_index(column_start, len(self.columns), False) column_end = _check_index(column_end, len(self.columns), True) else: raise GridIndexError( 'Cannot index with {}, pass in a int or a slice.'. format(column_key)) rows_cols = row_start, column_start, row_end, column_end elif isinstance(key, slice): start, end = _slice_to_start_end(key, len(self.rows)) start = _check_index(start, len(self.rows), False) end = _check_index(end, len(self.rows), True) rows_cols = start, 0, end, len(self.columns) elif isinstance(key, int): row_start = _check_index(key, len(self.rows), False) rows_cols = row_start, 0, row_start + 1, len(self.columns) else: raise GridIndexError('Invalid index {}'.format(key)) return rows_cols
def __setitem__(self, key, widget): """Add widget to the view.""" if isinstance(key, tuple): if len(key) == 1: self[key[0]] = widget else: try: row_key, column_key = key except ValueError: raise GridIndexError('Index must be 1 or 2 values, found {}'.format(key)) if isinstance(row_key, int): row_start = row_key row_end = None elif isinstance(row_key, slice): row_start, row_end = _slice_to_start_end(row_key, len(self.rows)) else: raise GridIndexError( 'Cannot index with {}, pass in a int or a slice.'.format(row_key) ) if isinstance(column_key, int): column_start = column_key column_end = None elif isinstance(column_key, slice): column_start, column_end = _slice_to_start_end(column_key, len(self.columns)) else: raise GridIndexError( 'Cannot index with {}, pass in a int or a slice.'.format(column_key) ) self._add( widget, row_start=row_start, column_start=column_start, row_end=row_end, column_end=column_end ) elif isinstance(key, slice): start, end = _slice_to_start_end(key, len(self.rows)) self._add( widget, row_start=start, column_start=0, row_end=end, column_end=len(self.columns) ) elif isinstance(key, int): self._add(widget, row_start=key, column_start=0, column_end=len(self.columns)) else: raise GridIndexError('Invalid index {}'.format(key))
def _slice_to_start_end(slc: slice, length: int) -> Tuple[int, int]: if slc.step is not None and slc.step != 1: raise GridIndexError( 'slice step is not supported must be None or 1, was {}'.format(slc.step) ) start = 0 if slc.start is not None: start = slc.start end = length if slc.stop is not None: end = slc.stop return start, end
def _add(self, widget: Component, row_start: Optional[int] = None, column_start: Optional[int] = None, row_end: Optional[int] = None, column_end: Optional[int] = None) -> None: """Add a widget to the grid. Zero-based index and exclusive. Parameters ---------- widget : bowtie._Component A Bowtie widget instance. row_start : int, optional Starting row for the widget. column_start : int, optional Starting column for the widget. row_end : int, optional Ending row for the widget. column_end : int, optional Ending column for the widget. """ if not isinstance(widget, Component): raise ValueError('Widget must be a type of Component, found {}'.format(type(widget))) if row_start is not None and row_end is not None and row_start >= row_end: raise GridIndexError('row_start: {} must be less than row_end: {}' .format(row_start, row_end)) if column_start is not None and column_end is not None and column_start >= column_end: raise GridIndexError('column_start: {} must be less than column_end: {}' .format(column_start, column_end)) # pylint: disable=protected-access if widget._PACKAGE: self.packages.add(widget._PACKAGE) self.templates.add(widget._TEMPLATE) self.imports.add(_Import(component=widget._COMPONENT, module=widget._TEMPLATE[:widget._TEMPLATE.find('.')])) used_msg = 'Cell at [{}, {}] is already used.' if row_start is None or column_start is None: if row_start is not None: raise MissingRowOrColumn( 'Only row_start was defined. ' 'Please specify both column_start and row_start or neither.' ) if column_start is not None: raise MissingRowOrColumn( 'Only column_start was defined. ' 'Please specify both column_start and row_start or neither.' ) if row_end is not None or column_end is not None: raise MissingRowOrColumn( 'If you specify an end index you must ' 'specify both row_start and column_start.' ) row, col = None, None for (row, col), use in self.used.items(): if not use: break else: raise NoUnusedCellsError() span = Span(row, col) self.used[row, col] = True elif row_end is None and column_end is None: if self.used[row_start, column_start]: raise UsedCellsError(used_msg.format(row_start, column_start)) span = Span(row_start, column_start) self.used[row_start, column_start] = True else: if row_end is None: row_end = row_start + 1 if column_end is None: column_end = column_start + 1 for row, col in product(range(row_start, row_end), range(column_start, column_end)): if self.used[row, col]: raise UsedCellsError(used_msg.format(row, col)) for row, col in product(range(row_start, row_end), range(column_start, column_end)): self.used[row, col] = True span = Span(row_start, column_start, row_end, column_end) self.spans[span].append(widget)