Skip to content

Commit

Permalink
Update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
gvx committed Jan 18, 2021
1 parent 4c11b40 commit 1a5ca61
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 13 deletions.
49 changes: 45 additions & 4 deletions docs/source/wurm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Connecting to a database
Defining tables
---------------

.. autoclass:: wurm.Table
.. autoclass:: wurm.tables.BaseTable
:members:

.. method:: query(**kwargs)
Expand Down Expand Up @@ -49,16 +49,57 @@ Defining tables

.. note:: This method accesses the connected database.

.. class:: wurm.Table

Baseclass for regular rowid tables. See
:class:`~wurm.tables.BaseTable` for methods available on
subclasses.

.. class:: wurm.WithoutRowid

Baseclass for ``WITHOUT ROWID`` tables. You need to add an explicit
primary key using :data:`~wurm.Primary` for these kinds of tables.
See :class:`~wurm.tables.BaseTable` for methods available on
subclasses.

Annotations
***********

Tables are defined using :pep:`526` type annotations, where the
type for each column has to be one of the following:

* One of the basic supported types (currently :class:`str`,
:class:`bytes`, :class:`int`, :class:`float`, :class:`bool`,
:class:`datetime.date`, :class:`datetime.time`,
:class:`datetime.datetime` and :class:`pathlib.Path`).
* A type registered with :func:`wurm.register_type`.
* :samp:`wurm.Primary[{T}]` or :samp:`wurm.Unique[{T}]`, where
:samp:`{T}` is one of the types mentioned above.


.. data:: wurm.Primary

Using :samp:`Primary[{T}]` as a type annotation in a table definition
is equivalent to using :samp:`{T}`, except that the column will be
the primary key.

If you attempt change the database in a way that would cause two
rows to share a primary key, the operation is rolled back, and a
:class:`~wurm.WurmError` is raised.

.. data:: wurm.Unique

Using :samp:`Unique[{T}]` as a type annotation in a table definition
is equivalent to using :samp:`{T}`, except that a ``UNIQUE`` index
is created for the field. Note that SQL considers ``None`` values to
be different from other ``None`` values for this purpose.

If you attempt to call :meth:`Table.insert` or :meth:`Table.commit`
in a way that would violate such a constraint, the operation is
rolled back, and a :class:`WurmError` is raised.
If you attempt to call :meth:`~wurm.tables.BaseTable.insert` or
:meth:`~wurm.tables.BaseTable.commit` in a way that would violate
such a constraint, the operation is rolled back, and a
:class:`~wurm.WurmError` is raised.

.. autofunction:: wurm.register_type

-------------
Queries
Expand Down
2 changes: 1 addition & 1 deletion wurm/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def delete(self):
"""Delete the objects matching this query.
.. warning:: Calling this on an empty query deletes all rows
in the database
of the relevant table in the database
.. note:: This method accesses the connected database.
Expand Down
34 changes: 28 additions & 6 deletions wurm/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ def query(self, **kwargs):
return Query(self, kwargs)

@dataclass
class WithoutRowid(metaclass=TableMeta, abstract=True):
class BaseTable(metaclass=TableMeta, abstract=True):
"""Baseclass for your own tables. Tables must be dataclasses.
Do not use directly, subclass :class:`wurm.Table` or
:class:`wurm.WithoutRowid` instead.
Use the keyword argument *name* in the class definition to set the
table name::
Expand All @@ -81,7 +84,22 @@ class MyTable(Table, name='mytable'):
...
If not given, wurm uses the class name to automatically derive a
suitable table name."""
suitable table name.
Use the keyword argument *abstract* in the class definition to
add fields or methods that you want to share between several
tables::
@dataclass
class HasOwner(Table, abstract=True):
owner: str
def display_owner(self):
return self.owner.capitalize()
The above will not create a new table, but subclasses of
``HasOwner`` will have a column called ``owner`` and a method
called ``display_owner``.
"""
__fields_info__: ClassVar[Dict[str, type]]
__datafields__: ClassVar[Tuple[str, ...]]
__primary_key__: ClassVar[Tuple[str, ...]]
Expand Down Expand Up @@ -112,7 +130,7 @@ def delete(self):
.. note:: This method accesses the connected database.
:raises ValueError: if called twice on the same instance, or
called on a fresh instance that has not been inserted yèt.
called on a fresh instance that has not been inserted yet.
"""
Query(type(self), self._primary_key()).delete()
Expand All @@ -122,7 +140,11 @@ def _encode_row(self):
return {key: to_stored(ty, getattr(self, key)) for key, ty in self.__fields_info__.items()}

@dataclass
class Table(WithoutRowid, abstract=True):
class WithoutRowid(BaseTable, abstract=True):
pass

@dataclass
class Table(BaseTable, abstract=True):
# technically rowid is Optional[int], but that's not implemented yet
rowid: Primary[int] = field(init=False, default=None, compare=False, repr=False)
def delete(self):
Expand All @@ -131,7 +153,7 @@ def delete(self):
.. note:: This method accesses the connected database.
:raises ValueError: if called twice on the same instance, or
called on a fresh instance that has not been inserted yèt.
called on a fresh instance that has not been inserted yet.
"""
if self.rowid is None:
Expand All @@ -151,4 +173,4 @@ def setup_connection(conn):
This records the connection and ensures all tables are created."""
connection.set(conn)
create_tables(WithoutRowid, conn)
create_tables(BaseTable, conn)
9 changes: 7 additions & 2 deletions wurm/typemaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ def register_type(python_type, sql_type, *, encode, decode):
For example::
class Foo:
def __repr__(self):
...
@classmethod
def from_string(cls, string):
...
...
register_type(Foo, str, encode=repr, decode=Foo.from_string)
Expand All @@ -47,9 +52,9 @@ class Foo:
:class:`str`, :class:`float`, :class:`bytes`
:param encode: The function to prepare to store a value in the
database
:type encode: Callable[[python_type], sql_type]
:type encode: python_type -> sql_type
:param decode: The function to interpret the stored value
:type decode: Callable[[sql_type], python_type]"""
:type decode: sql_type -> python_type"""
assert python_type not in TYPE_MAPPING
TYPE_MAPPING[python_type] = StoredValueTypeMap(SQL_EQUIVALENTS[sql_type], encode, decode)

Expand Down

0 comments on commit 1a5ca61

Please sign in to comment.