Metadata-Version: 2.4
Name: restalchemy
Version: 16.0.4
Summary: REST HTTP Abstraction Library
Author-email: Genesis Corporation <eugene@frolov.net.ru>
License: Apache-2.0
Project-URL: homepage, https://github.com/infraguys/restalchemy/
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.8
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: oslo.config<10.0.0,>=3.22.2
Requires-Dist: WebOb<1.9.0,>=1.8.9
Requires-Dist: requests<3.0.0,>=2.32.4
Requires-Dist: mysql-connector-python==8.4.0
Requires-Dist: PyYAML>=6.0
Requires-Dist: netaddr>=0.7.18
Requires-Dist: psycopg[binary,pool]<4.0.0,>=3.2.4
Requires-Dist: email-validator<3.0.0,>=2.2.0
Requires-Dist: orjson<4.0.0,>=3.10.0
Provides-Extra: dev
Requires-Dist: tox>=4.0.0; extra == "dev"
Requires-Dist: tox-uv; extra == "dev"
Requires-Dist: pytest>=7.0.0; extra == "dev"
Provides-Extra: test
Requires-Dist: coverage>=4.0; extra == "test"
Requires-Dist: mock<6.0.0,>=3.0.5; extra == "test"
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "test"
Requires-Dist: parameterized<1.0.0,>=0.9.0; extra == "test"
Requires-Dist: pytest-xdist[psutil]<4.0.0,>=3.6.1; extra == "test"
Requires-Dist: pytest-timer<2.0.0,>=1.0.0; extra == "test"
Requires-Dist: pytest-cov<7.0.0,>=5.0.0; extra == "test"
Provides-Extra: mypy
Requires-Dist: mypy; extra == "mypy"
Provides-Extra: ruff
Requires-Dist: ruff; extra == "ruff"
Provides-Extra: shellcheck
Requires-Dist: shellcheck-py; extra == "shellcheck"
Provides-Extra: testenv
Requires-Dist: setuptools-git>=0.4.0; extra == "testenv"
Provides-Extra: docs
Requires-Dist: zensical>=0.0.32; python_version >= "3.10" and extra == "docs"
Dynamic: license-file

.. image:: https://github.com/infraguys/restalchemy/actions/workflows/tests.yml/badge.svg
   :target: https://github.com/infraguys/restalchemy/actions/workflows/tests.yml
.. image:: https://img.shields.io/pypi/pyversions/restalchemy
   :target: https://img.shields.io/pypi/pyversions/restalchemy
.. image:: https://img.shields.io/pypi/dm/restalchemy
   :target: https://img.shields.io/pypi/dm/restalchemy

|

RESTAlchemy
============

RESTAlchemy is a Python toolkit for building HTTP REST APIs on top of a flexible data model and storage abstraction.

It combines:

- A **Data Model (DM)** layer for defining domain models and validation.
- A **Storage** layer for persisting models (for example, SQL databases).
- An **API** layer for exposing models as RESTful HTTP resources.
- Optional **OpenAPI** support for discoverable, documented APIs.


Features
--------

- Clear separation between domain models, storage implementation details, and HTTP API.
- Strongly typed, validated data model with reusable properties.
- Minimal boilerplate to expose models as REST resources.
- Built-in migration tooling for evolving database schemas.


Performance
-----------

**On all three request patterns, RESTAlchemy is the fastest framework
measured here, and stands nearer to the floor of what such a request can
cost at all than to any framework behind it.** The benchmark in this
repository asks six stacks the same three questions against the same
PostgreSQL and the same rows: read a collection of 100, read one
resource, create one. The first row of the table is not one of them: it
is a hand-written query and ``orjson.dumps``, which is what the database
and the JSON cost before any framework has run.

15 rounds × best of 20 calls, 1000 rows in the table, 100 per page,
Python 3.12, PostgreSQL 16, one AMD EPYC 7742. The full run, with
confidence intervals and the machine it was measured on, is in
`bench/results/results.md <bench/results/results.md>`_; these are its
numbers. In parentheses: the same number as a multiple of RESTAlchemy's.

=======================  =================  =============  =============  ==============
Stack                    Collection of 100  One resource   POST one       Per row
=======================  =================  =============  =============  ==============
raw psycopg + orjson †   1272 µs (0.7×)     174 µs (0.5×)  229 µs (0.6×)  11.1 µs (0.8×)
RESTAlchemy              **1705 µs**        **317 µs**     **397 µs**     **14.0 µs**
Flask + SQLAlchemy       2267 µs (1.3×)     636 µs (2.0×)  606 µs (1.5×)  16.5 µs (1.2×)
FastAPI + SQLAlchemy     2417 µs (1.4×)     894 µs (2.8×)  829 µs (2.1×)  15.4 µs (1.1×)
Litestar + SQLAlchemy    2524 µs (1.5×)     857 µs (2.7×)  811 µs (2.0×)  16.8 µs (1.2×)
Django + DRF             5276 µs (3.1×)     943 µs (3.0×)  895 µs (2.3×)  43.8 µs (3.1×)
=======================  =================  =============  =============  ==============

† Not a framework, and faster than every framework under it, as it should
be: it is the floor the rest are read against, not a stack anyone would
build a service on.

RESTAlchemy adds 143 µs to that bare single-row query, 168 µs to the bare
insert, and under three microseconds to each row of a collection — a REST
service, routed, validated and packed, for a few microseconds over the
floor.

Every stack is written the way its own documentation writes it, answers
with the same rows — checked field by field before anything is timed — and
is called at its own interface, so what is measured is the framework and
its database work rather than a web server. The command that runs it, and
what the numbers do not say, are in `bench/README.md <bench/README.md>`_.


Documentation
-------------

The rendered documentation is published at https://infraguys.github.io/restalchemy/.

All documentation is available in four languages. The structure of files and sections is identical across languages:

- English: `docs/en/index.md <docs/en/index.md>`_
- Russian: `docs/ru/index.md <docs/ru/index.md>`_
- German: `docs/de/index.md <docs/de/index.md>`_
- Chinese: `docs/zh/index.md <docs/zh/index.md>`_

If you are new to RESTAlchemy, start with:

- `Installation <docs/en/installation.md>`_
- `Getting started <docs/en/getting-started.md>`_


Quick start
-----------

Install from PyPI:

.. code-block:: bash

   pip install restalchemy

Define a simple DM model (simplified from the getting started guide):

.. code-block:: python

   from restalchemy.dm import models
   from restalchemy.dm import properties
   from restalchemy.dm import types


   class FooModel(models.ModelWithUUID):
       value = properties.property(types.Integer(), required=True)


For a complete in-memory REST service example, including controllers, routes and a WSGI application, see
`docs/en/getting-started.md <docs/en/getting-started.md>`_.


Examples
--------

Real code examples live in the ``examples/`` directory:

- ``examples/restapi_foo_bar_service.py`` – a simple REST API service built with in-memory storage.
- ``examples/dm_mysql_storage.py`` – data model with MySQL storage example.
- ``examples/openapi_app.py`` – example API with OpenAPI specification generation.


Migration commands
------------------

RESTAlchemy provides command-line tools for managing database migrations.

.. warning::
  New naming scheme is implemented for migration file names. The old naming scheme is supported as well.
  Recommended new file name format:

::

  <migration number>-<message>-<hash>.py

.. note::

  In order to rename the migration files for the new naming scheme, please use the following command:

::

  $ ra-rename-migrations -p <path-to-migrations>

Create migrations:

.. warning::
    Auto migration should not depend on manual one.

::

  $ ra-new-migration --path examples/migrations/ --message "1st migration"
  $ ra-new-migration --path examples/migrations/ --message "2st migration" --depend 1st
  $ ra-new-migration --path examples/migrations/ --message "3st migration" --depend 2st
  $ ra-new-migration --path examples/migrations/ --message "4st migration"
  $ ra-new-migration --path examples/migrations/ --message "5st migration" --depend 3st --depend 4st

.. note::
    You can create MANUAL migrations using --manual parameter

    $ ra-new-migration --path examples/migrations/ --message "manual migration" --manual


Apply migrations:

::

  $ ra-apply-migration --path examples/migrations/ --db-connection mysql://test:test@localhost/test -m 5st
  > upgrade 1st
  > upgrade 2st
  > upgrade 3st
  > upgrade 4st
  > upgrade 5st

.. note::
    If you want to apply the latest migration, run ``ra-apply-migration`` without the ``-m`` parameter.

    $ ra-apply-migration --path examples/migrations/ --db-connection mysql://test:test@localhost/test

    If it is impossible to find the latest migration, the tool will crash with the error
    "Head migration for current migrations couldnt be found".


Rolled back migrations:

::

  $ ra-rollback-migration --path examples/migrations/ --db-connection mysql://test:test@localhost/test -m 4st
  > downgrade 5st
  > downgrade 4st

::

  $ ra-rollback-migration --path examples/migrations/ --db-connection mysql://test:test@localhost/test -m 1st
  > downgrade 3st
  > downgrade 2st
  > downgrade 1st


Tests
-----

Tests are managed via ``tox``. The default environment list includes Python 3.8, 3.10, 3.12 and 3.13.

Run the full test suite:

.. code-block:: bash

   tox

Run tests for a specific Python version (for example, Python 3.10):

.. code-block:: bash

   tox -e py310

Run functional tests (require access to a MySQL database):

.. code-block:: bash

   export DATABASE_URI="mysql://root:@localhost:3306/radatabase"
   tox -e py310-functional

Run functional tests with PostgreSQL:

.. code-block:: bash

   export DATABASE_URI="postgresql://postgres:password@localhost:5432/radatabase"
   tox -e py310-functional


License
-------

RESTAlchemy is licensed under the Apache License, Version 2.0.

Copyright (c) Genesis Corporation, 2025.

See the ``LICENSE`` file in this repository or
https://www.apache.org/licenses/LICENSE-2.0 for the full license text.
