예제 #1
0
    def test_table_column_name_freed(self):
        c_string = ctypes.create_string_buffer(b'Foo!')
        with mock.patch.object(_lib, 'TableColumnName', return_value=c_string):
            table = _lib.TableNew()
            binding = Binding(table)

            self.assertEqual(binding.table_column_name(0), 'Foo!')
            _lib.StringFree.assert_called_once_with(c_string)
예제 #2
0
 def __init__(self, schema):
     schema = self.validate_schema(schema)
     self._table_ptr = Binding.table_new()
     self.binding = Binding(self._table_ptr)
     self._init_columns(schema)
     self._len_changed = True
예제 #3
0
class Table:
    VALID_COLUMN_NAME = re.compile(r'^[A-Za-z\d]+$')
    VALID_COLUMN_TYPES = {'string': str, 'int': int}

    def __init__(self, schema):
        schema = self.validate_schema(schema)
        self._table_ptr = Binding.table_new()
        self.binding = Binding(self._table_ptr)
        self._init_columns(schema)
        self._len_changed = True

    @classmethod
    def validate_schema(cls, schema):
        for column in schema:
            if len(column) != 2:
                raise TypeError('Expected schema format is [(name, type), (name, type) ...]')

            if not cls.VALID_COLUMN_NAME.match(column[0]):
                raise ValueError('Invalid column name %s: it must only contain letters and numbers.' % column[0])

            if column[1] not in cls.VALID_COLUMN_TYPES:
                raise TypeError('Invalid column type: %s: it must be one of %s' % (column[1], cls.VALID_COLUMN_TYPES))

        return schema

    def _init_columns(self, schema):
        self.binding.init_columns(schema)

    @property
    def width(self):
        return self.binding.table_width()

    @property
    def column_names(self):
        """An list of column names in the schema."""
        return [self.binding.table_column_name(i) for i in range(self.width)]

    @property
    def column_types(self):
        """An list of column types in the schema."""
        return [self.binding.table_column_type(i) for i in range(self.width)]

    @property
    def schema(self):
        """Return the schema of the table."""
        return list(zip(self.column_names, self.column_types))

    def append(self, row):
        for (name, typ), element in zip(self.schema, row):
            if not isinstance(element, self.VALID_COLUMN_TYPES[typ]):
                raise TypeError('Column %s expects %s, got %s.' % (
                    name,
                    self.VALID_COLUMN_TYPES[typ],
                    type(element)
                ))

        self.binding.table_append(row)
        self._len_changed = True

    def extend(self, rows):
        for row in rows:
            self.append(row)

    def __len__(self):
        if self._len_changed:
            self._len = self.binding.table_len()
            self._len_changed = False
        return self._len

    def __getitem__(self, row_index):
        if row_index < 0:
            row_index = abs(row_index + len(self))

        if row_index > len(self) - 1:
            raise IndexError('Table index out of range')

        return self.binding.table_row(row_index)

    def __del__(self):
        """Free the underlying table.

        In case Table instatiation failing, then there is nothing to free.

        """
        try:
            self.binding.table_free()
        except AttributeError:
            pass
예제 #4
0
 def test_py_str(self):
     c_string = ctypes.create_string_buffer(b'Foo!')
     self.assertEqual(Binding.py_str(c_string), 'Foo!')