# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['aerie']

package_data = \
{'': ['*']}

install_requires = \
['SQLAlchemy[asyncio]>=1.4.27,<2.0.0']

extras_require = \
{'full': ['aiosqlite>=0.17.0,<0.18.0', 'asyncpg>=0.25.0,<0.26.0'],
 'postgresql': ['asyncpg>=0.25.0,<0.26.0'],
 'sqlite': ['aiosqlite>=0.17.0,<0.18.0']}

setup_kwargs = {
    'name': 'aerie',
    'version': '0.7.1',
    'description': 'An async database toolkit.',
    'long_description': '# Aerie\n\nA wrapper around SQLAlchemy made to support asynchronous workloads.\n\n[Aerie](https://baldursgate.fandom.com/wiki/Aerie) - is an [avariel](https://baldursgate.fandom.com/wiki/Elf#Avariel)\n(or winged elf) from Baldur\'s Gate II game.\n\n![PyPI](https://img.shields.io/pypi/v/aerie)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/alex-oleshkevich/aerie/Lint)\n![GitHub](https://img.shields.io/github/license/alex-oleshkevich/aerie)\n![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/pypi/aerie)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/aerie)\n![GitHub Release Date](https://img.shields.io/github/release-date/alex-oleshkevich/aerie)\n![Lines of code](https://img.shields.io/tokei/lines/github/alex-oleshkevich/aerie)\n\n## Installation\n\nInstall `aerie` using PIP or poetry:\n\n```bash\npip install aerie[postgresql]\n# or\npoetry add aerie[postgresql]\n```\n\nFor SQLite use "sqlite" extra. To install all drivers use "full" extra.\n\n## Features\n\n- full async/await support\n- plain SQL with bound params\n- SQLAlchemy query builders support\n- SQLAlchemy ORM support\n- pagination\n\n## TODO\n\n* simplify column definition: `sa.Column(sa.Integer)` -> `models.IntergerField()`\n* integrate with Alembic CLI\n\n## Quick start\n\nSee example application in [examples/](examples/) directory of this repository.\n\n## Usage\n\nAs this library is based on [SQLAlchemy](https://docs.sqlalchemy.org/en/14/index.html), it is strictly recommended\ngetting yourself familiar with it.\n\nA general usage is:\n\n* create an instance of Aerie\n* define ORM models\n* create tables in the database (or, preferably, use Alembic migrations)\n* obtain a session and perform database queries\n\n### Aerie instance\n\nCreate an instance of Aerie class and pass a connection string to it:\n\n```python\nfrom aerie import Aerie\n\ndb = Aerie(\'sqlite+aiosqlite:///tmp/database.sqlite2\')\n# or\ndb = Aerie(\'postgresql+asyncpg://postgres:postgres@localhost/aerie\')\n```\n\n> You need appropriate driver installed. Add "aiosqlite" for SQLite support, or add "asyncpg" for PostreSQL support.\n\n### Raw SQL queries\n\nAt this step Aerie is ready to work. Create a new transaction and execute any query you need.\n\n```python\nfrom sqlalchemy.sql import text\n\n# assuming "users" table exists\nsql = text(\'select * from users where user_id = :user_id\')\nrows = await db.query(sql, {\'user_id\': 1}).all()\n```\n\nFull listing [examples/raw_sql.py](examples/raw_sql.py)\n\n### Using query builder\n\nSure, you are not limited to plain SQL. SQLAlchemy query builders also supported (because Aerie is a tiny layer on top\nof the SQLAlchemy)\n\n```python\nfrom sqlalchemy.sql import text\nimport sqlalchemy as sa\nfrom aerie import metadata\n\nusers = sa.Table(\n    \'users\', metadata,\n    sa.Column(sa.Integer, name=\'id\', primary_key=True),\n    sa.Column(sa.String, name=\'name\'),\n)\n\n# create tables\nawait db.schema.create_tables()\n\nstmt = select(users).where(users.c.id == 2)\nrows = await db.query(stmt).all()\n```\n\nFull listing [examples/tables.py](examples/tables.py)\n\n### Using ORM models and sessions\n\nAnother option to low-level query builder are ORM models. Aerie provides `aerie.Model` class that you should extend to\ncreate your model.\n\n```python\nfrom aerie import Base\nimport sqlalchemy as sa\n\n\nclass User(Base):\n    __tablename__ = \'users\'\n\n    id = sa.Column(sa.Integer, primary_key=True)\n    name = sa.Column(sa.String)\n\n\n# create tables\nawait db.schema.create_tables()\n\nasync with db.session() as session:\n    session.add_all([\n        User(id=1, name=\'One\'),\n        User(id=2, name=\'Two\'),\n        User(id=3, name=\'Three\'),\n    ])\n    await session.flush()\n\n    # get first user in the row set\n    user = await session.query(User).first()\n```\n\n> Make sure the module with models is imported before you create tables.\n> Otherwise they will not be added to the `metadata` and, thus, not created.\n\nFull listing [examples/orm.py](examples/orm.py)\n\n### Pagination\n\nAerie\'s DbSession ships with pagination utilities out of the box. When you need to paginate a query just\ncall `DbSession.paginate` method.\n\n```python\nasync with db.session() as session:\n    page = await session.query(User).paginate(page=1, page_size=10)\n\n    for user in page:\n        print(user)\n\n    print(\'Next page: %s\' % page.next_page)\n    print(\'Previous page: %s\' % page.previous_page)\n    print(\'Displaying items: %s - %s\' % (page.start_index, page.end_index))\n```\n\nThe page object has more helper attributes:\n\n| Property      | Type | Description                                                                                              |\n|---------------|------|----------------------------------------------------------------------------------------------------------|\n| total_pages   | int  | Total pages in the row set.                                                                              |\n| has_next      | bool | Test if the next page is available.                                                                      |\n| has_previous  | bool | Test if the previous page is available.                                                                  |\n| has_other     | bool | Test if there are another pages except current one.                                                      |\n| next_page     | int  | Next page number. Always returns an integer. If there is no more pages the current page number returned. |\n| previous_page | int  | Previous page number. Always returns an integer. If there is no previous page, the number 1 returned.    |\n| start_index   | int  | The 1-based index of the first item on this page.                                                        |\n| end_index     | int  | The 1-based index of the last item on this page.                                                         |\n| total_rows    | int  | Total rows in result set.                                                                                |\n\n## Alembic migrations\n\nAlembic usage is well documented in the official\ndocs: [Using Asyncio with Alembic](https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic)\n\nNote, you need to use `aerie.metadata` when you configure `target_metadata` option:\n\n```python\n# migrations/env.py\n\nfrom aerie import metadata\n\ntarget_metadata = metadata\n```\n\nAlso, don\'t forget to import all your models in Alembic\'s `env.py` file so their structure is fully loaded and no models\nforgotten.\n\n## Shared instances\n\nYou can configure Aerie to populate Aerie.instances class-level variable, so you can access database instances from\nanywhere of your code. For that, just pass `name` argument to Aerie constructor.\n\n```python\n# migrations/env.py\n\nfrom aerie import Aerie\n\ndb = Aerie(name=\'shared\', ...)\n\n# other file\ndb = Aerie.get_instance(\'shared\')\n```\n\n> Note, instances without name cannot be shared.\n',
    'author': 'Alex Oleshkevich',
    'author_email': 'alex.oleshkevich@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/alex-oleshkevich/aerie',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'python_requires': '>=3.9.0,<4.0.0',
}


setup(**setup_kwargs)
