Name | based JSON |
Version |
0.6.1
JSON |
| download |
home_page | None |
Summary | A based asynchronous database connection manager. |
upload_time | 2025-02-10 23:20:40 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | MIT License
Copyright (c) 2024 ansipunk <kysput@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
|
keywords |
database
sqlalchemy
sqlite
asyncio
postgresql
mysql
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
A based asynchronous database connection manager.
Based is designed to be used with SQLAlchemy Core requests. Currently, the only
supported databases are SQLite, PostgreSQL and MySQL. It's fairly simple to add
a new backend, should you need one. Work in progress - any contributions -
issues or pull requests - are very welcome. API might change, as library is
still at its early experiment stage.
This library is inspired by [databases](https://github.com/encode/databases).
The source code for this project is available
[here](https://github.com/ansipunk/based).
## Usage
```bash
pip install based[sqlite] # or based[postgres] or based[mysql]
```
```python
import based
database = based.Database("sqlite:///database.sqlite")
await database.connect()
async with database.session() as session:
query = Movies.select().where(Movies.c.year >= 2010)
movies = await session.fetch_all(query)
if movies:
async with session.transaction():
query = "DELETE FROM movies WHERE year >= :year;"
params = {"year": 2010}
await session.execute(query, params)
async with session.transaction():
for movie in movies:
query = "INSERT INTO movies (title, year) VALUES (?, ?);"
params = [movie["title"], movie["year"] - 1000]
await session.execute(query, params)
await database.disconnect()
```
## `force_rollback`
Databases can be initialized in `force_rollback=True` mode. When it's enabled,
everything will work as it usually does, but all the changes to the database
will be discarded upon disconneciton. It can be useful for testing purposes,
where you don't want to manually clean up made changes after each test.
To make it possible, `Backend` object will only operate with one single session
and each new requested session will actually be the same session.
```python
async with Database(
"postgresql://user:pass@localhost/based",
force_rollback=True,
) as database:
async with database.session() as session:
query = Movies.insert().values(title="Newboy", year=2004)
await session.execute(query)
async with Database(
"postgresql://user:pass@localhost/based",
force_rollback=True,
) as database:
async with database.session() as session:
query = Movies.select().where(Movies.c.title == "Newboy")
movie = await session.execute(query)
assert movie is None
```
## Connection pools and parallel requests
Based supports connection pools for PostgreSQL databases thanks to psycopg_pool.
However, when running in `force_rollback` mode, it will only use a single
connection so it can be rolled back upon database disconnection. SQLite is
unaffected by `force_rollback` mode, as it doesn't have a connection pool either
way. This means that PostgreSQL backend in `force_rollback` mode and SQLite
backend in both modes are not guaranteed to work consistently when multiple
sessions are used in parallel.
For this problem `based` uses async locks on sessions in `force_rollback` mode.
Locks can be used in default mode as well with `use_lock` flag during database
initialization, however, it will only have effect if the database of your choice
is SQLite, as in other cases isolation of sessions will be guaranteed by using
separate connections for each session.
## Design choices
As you can see, database backends are split into two classes - `BasedBackend`
and `Session`. This design choice might be not very clear with SQLite, however,
it is handy with backends that support connection pools like PostgreSQL.
## Contributing
This library was designed to make adding new backends as simple as possible. You
need to implement `Backend` class and add its initialization to the `Database`
class. You only need to implement methods that raise `NotImplementedError` in
the base class, adding private helpers as needed.
### Testing
Pass database URLs for those you want to run the tests against. Comma separated
list.
```bash
BASED_TEST_DB_URLS='postgresql://postgres:postgres@localhost:5432/postgres,mysql://root:mariadb@127.0.0.1:3306/mariadb' make test`
```
## TODO
- [x] CI/CD
- [x] Building and uploading packages to PyPi
- [x] Testing with multiple Python versions
- [x] Database URL parsing and building
- [x] MySQL backend
- [x] Add comments and docstrings
- [x] Add lock for PostgreSQL in `force_rollback` mode and SQLite in both modes
- [x] Refactor tests
- [x] PostgreSQL backend
- [x] Replace nested sessions with transaction stack
Raw data
{
"_id": null,
"home_page": null,
"name": "based",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "database, sqlalchemy, sqlite, asyncio, postgresql, mysql",
"author": null,
"author_email": "ansipunk <kysput@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/bf/9e/6e9a83d3836bd7751daa2618ca9bd005411361dadd4868bcd46adfe4d804/based-0.6.1.tar.gz",
"platform": null,
"description": "A based asynchronous database connection manager.\n\nBased is designed to be used with SQLAlchemy Core requests. Currently, the only\nsupported databases are SQLite, PostgreSQL and MySQL. It's fairly simple to add\na new backend, should you need one. Work in progress - any contributions -\nissues or pull requests - are very welcome. API might change, as library is\nstill at its early experiment stage.\n\nThis library is inspired by [databases](https://github.com/encode/databases).\nThe source code for this project is available\n[here](https://github.com/ansipunk/based).\n\n## Usage\n\n```bash\npip install based[sqlite] # or based[postgres] or based[mysql]\n```\n\n```python\nimport based\n\ndatabase = based.Database(\"sqlite:///database.sqlite\")\nawait database.connect()\n\nasync with database.session() as session:\n query = Movies.select().where(Movies.c.year >= 2010)\n movies = await session.fetch_all(query)\n\n if movies:\n async with session.transaction():\n query = \"DELETE FROM movies WHERE year >= :year;\"\n params = {\"year\": 2010}\n await session.execute(query, params)\n\n async with session.transaction():\n for movie in movies:\n query = \"INSERT INTO movies (title, year) VALUES (?, ?);\"\n params = [movie[\"title\"], movie[\"year\"] - 1000]\n await session.execute(query, params)\n\nawait database.disconnect()\n```\n\n## `force_rollback`\n\nDatabases can be initialized in `force_rollback=True` mode. When it's enabled,\neverything will work as it usually does, but all the changes to the database\nwill be discarded upon disconneciton. It can be useful for testing purposes,\nwhere you don't want to manually clean up made changes after each test.\n\nTo make it possible, `Backend` object will only operate with one single session\nand each new requested session will actually be the same session.\n\n```python\nasync with Database(\n\t\"postgresql://user:pass@localhost/based\",\n\tforce_rollback=True,\n) as database:\n\tasync with database.session() as session:\n\t\tquery = Movies.insert().values(title=\"Newboy\", year=2004)\n\t\tawait session.execute(query)\n\nasync with Database(\n\t\"postgresql://user:pass@localhost/based\",\n\tforce_rollback=True,\n) as database:\n\tasync with database.session() as session:\n\t\tquery = Movies.select().where(Movies.c.title == \"Newboy\")\n\t\tmovie = await session.execute(query)\n\t\tassert movie is None\n```\n\n## Connection pools and parallel requests\n\nBased supports connection pools for PostgreSQL databases thanks to psycopg_pool.\nHowever, when running in `force_rollback` mode, it will only use a single\nconnection so it can be rolled back upon database disconnection. SQLite is\nunaffected by `force_rollback` mode, as it doesn't have a connection pool either\nway. This means that PostgreSQL backend in `force_rollback` mode and SQLite\nbackend in both modes are not guaranteed to work consistently when multiple\nsessions are used in parallel.\n\nFor this problem `based` uses async locks on sessions in `force_rollback` mode.\nLocks can be used in default mode as well with `use_lock` flag during database\ninitialization, however, it will only have effect if the database of your choice\nis SQLite, as in other cases isolation of sessions will be guaranteed by using\nseparate connections for each session.\n\n## Design choices\n\nAs you can see, database backends are split into two classes - `BasedBackend`\nand `Session`. This design choice might be not very clear with SQLite, however,\nit is handy with backends that support connection pools like PostgreSQL.\n\n## Contributing\n\nThis library was designed to make adding new backends as simple as possible. You\nneed to implement `Backend` class and add its initialization to the `Database`\nclass. You only need to implement methods that raise `NotImplementedError` in\nthe base class, adding private helpers as needed.\n\n### Testing\n\nPass database URLs for those you want to run the tests against. Comma separated\nlist.\n\n```bash\nBASED_TEST_DB_URLS='postgresql://postgres:postgres@localhost:5432/postgres,mysql://root:mariadb@127.0.0.1:3306/mariadb' make test`\n```\n\n## TODO\n\n- [x] CI/CD\n - [x] Building and uploading packages to PyPi\n - [x] Testing with multiple Python versions\n- [x] Database URL parsing and building\n- [x] MySQL backend\n- [x] Add comments and docstrings\n- [x] Add lock for PostgreSQL in `force_rollback` mode and SQLite in both modes\n- [x] Refactor tests\n- [x] PostgreSQL backend\n- [x] Replace nested sessions with transaction stack\n",
"bugtrack_url": null,
"license": "MIT License\n \n Copyright (c) 2024 ansipunk <kysput@gmail.com>\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n ",
"summary": "A based asynchronous database connection manager.",
"version": "0.6.1",
"project_urls": {
"Homepage": "https://github.com/ansipunk/based",
"Issues": "https://github.com/ansipunk/based/issues",
"Repository": "https://github.com/ansipunk/based.git"
},
"split_keywords": [
"database",
" sqlalchemy",
" sqlite",
" asyncio",
" postgresql",
" mysql"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "146d6a3447091113e77092c19c6f71b25c304d05b7bc784da0425537e9b98493",
"md5": "523788c837f9190aadfc2a41e94f5f9e",
"sha256": "4fbc243e819636bb71c98ea045920c10d279137f37ceb6ae145cf6085091baca"
},
"downloads": -1,
"filename": "based-0.6.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "523788c837f9190aadfc2a41e94f5f9e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 12714,
"upload_time": "2025-02-10T23:20:38",
"upload_time_iso_8601": "2025-02-10T23:20:38.778769Z",
"url": "https://files.pythonhosted.org/packages/14/6d/6a3447091113e77092c19c6f71b25c304d05b7bc784da0425537e9b98493/based-0.6.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "bf9e6e9a83d3836bd7751daa2618ca9bd005411361dadd4868bcd46adfe4d804",
"md5": "ab2e6c61a77aff7efd9679a8751254d7",
"sha256": "21f4ff7d96fe8c8c63eb76b672c09f1185da6dcc67921a1b1e4f275ded9a7a42"
},
"downloads": -1,
"filename": "based-0.6.1.tar.gz",
"has_sig": false,
"md5_digest": "ab2e6c61a77aff7efd9679a8751254d7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 16904,
"upload_time": "2025-02-10T23:20:40",
"upload_time_iso_8601": "2025-02-10T23:20:40.748970Z",
"url": "https://files.pythonhosted.org/packages/bf/9e/6e9a83d3836bd7751daa2618ca9bd005411361dadd4868bcd46adfe4d804/based-0.6.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-10 23:20:40",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ansipunk",
"github_project": "based",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "based"
}