<p align="center" style="margin: 0 0 10px">
<img width="200" height="200" src="https://www.postgresql.org/media/img/about/press/elephant.png" alt='Python' style="border-radius: 15px">
</p>
<h1 align="center" style="font-size: 3rem; font-weight: 400; margin: -15px 0">
Python Postgres
</h1>
---
Python Postgres is an abstraction over [psycopg](https://www.psycopg.org/psycopg3/docs/index.html) and aims to provide
the simplest way to interact with PostgreSQL databases in Python.
**I have just started this project, and it is not ready for production use. I am still working on every aspect of it and
may abandon it at any point without warning.**
---
### Installation
```shell
pip install python_postgres
```
### Basic Usage
```python
from python_postgres import Postgres
pg = Postgres("pgadmin", "password", "pg-is-king.postgres.database.azure.com")
async def main():
await pg.open() # Open the connection pool, this requires a running event loop.
files = await pg("SELECT * FROM files")
await pg("INSERT INTO files (name, size) VALUES (%s, %s)", [("file1", 1024), ("file2", 2048)])
async with pg.transaction() as tran:
file_id = (
await tran(
"INSERT INTO files (name, size) VALUES VALUES (%s, %s) RETURNING file_id;",
("you_may_not_exist", 0),
)
)[0]
await tran("INSERT INTO pages (page_number, file_id) VALUES (%s, %s);", (4, file_id))
raise ValueError("Oopsie")
await pg.close() # Close the connection pool. Python Postgres will attempt to automatically
# close the pool when the instance is garbage collected, but this is not guaranteed to succeed.
# Be civilized and close it yourself.
```
### Pydantic Integration
Python Postgres supports [Pydantic](https://docs.pydantic.dev/latest/) Models as insert parameters.
```python
from pydantic import BaseModel
class File(BaseModel):
file_name: str
size: int
async def main():
await pg.open()
await pg(
"INSERT INTO files (file_name, size) VALUES (%s, %s)",
File(file_name="rubbish.pdf", size=8096),
)
await pg.close()
```
### A more in-depth look
The basic idea of this project is to provide one callable instance of the `Postgres` class. The `Postgres` class manages
a connection pool in the background and will get a connection from the pool when called, spawn a binary cursor on it,
run your query, return the results (or the number of rows affected), and then return the connection to the pool. As a
query, you can pass either a literal - string or bytes - or a `SQL` or
`Composed` [object](https://www.psycopg.org/psycopg3/docs/api/sql.html) from the `psycopg` library.
In Essence, the `Postgres` class is syntactic sugar for turning this:
```python
async def exec_query(
query: LiteralString | bytes | SQL | Composed,
params: tuple | list[tuple],
is_retry: bool = False,
) -> list[tuple]:
try:
async with con_pool.connection() as con: # type: psycopg.AsyncConnection
async with con.cursor(binary=True) as cur: # type: psycopg.AsyncCursor
if isinstance(params, list):
await cur.executemany(query, params)
else:
await cur.execute(query, params)
await con.commit()
return (
await cur.fetchall()
if cur.pgresult and cur.pgresult.ntuples > 0
else cur.rowcount or -1
)
except psycopg.OperationalError as error:
if is_retry:
raise IOError from error
await con_pool.check()
await exec_query(query, params, True)
except psycopg.Error as error:
raise IOError from error
await exec_query("SELECT * FROM files WHERE id = %s", (1234,))
```
into
```python
await pg("SELECT * FROM files WHERE id = %s", (1234,))
```
### Notes
Other than providing simpler syntax through a thin abstraction, this project inherits all the design choices of psycopg,
including the [caching of query execution plans](https://www.psycopg.org/psycopg3/docs/advanced/prepare.html#index-0)
Raw data
{
"_id": null,
"home_page": "https://github.com/VinzenzKlass/python-postgres",
"name": "python-postgres",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "python-postgres, postgres",
"author": "Vinzenz Klass",
"author_email": "vinzenz.klass@ba.valantic.com",
"download_url": "https://files.pythonhosted.org/packages/a1/7b/48a5f4a1a06609078298dbbb2f596f9c6432a01ad2421c57c6e09b9d6d25/python_postgres-0.0.5.tar.gz",
"platform": null,
"description": "<p align=\"center\" style=\"margin: 0 0 10px\">\n<img width=\"200\" height=\"200\" src=\"https://www.postgresql.org/media/img/about/press/elephant.png\" alt='Python' style=\"border-radius: 15px\">\n</p>\n\n<h1 align=\"center\" style=\"font-size: 3rem; font-weight: 400; margin: -15px 0\">\nPython Postgres\n</h1>\n\n---\n\nPython Postgres is an abstraction over [psycopg](https://www.psycopg.org/psycopg3/docs/index.html) and aims to provide\nthe simplest way to interact with PostgreSQL databases in Python.\n\n**I have just started this project, and it is not ready for production use. I am still working on every aspect of it and\nmay abandon it at any point without warning.**\n\n---\n\n### Installation\n\n```shell\npip install python_postgres \n```\n\n### Basic Usage\n\n```python\nfrom python_postgres import Postgres\n\npg = Postgres(\"pgadmin\", \"password\", \"pg-is-king.postgres.database.azure.com\")\n\n\nasync def main():\n await pg.open() # Open the connection pool, this requires a running event loop.\n files = await pg(\"SELECT * FROM files\")\n await pg(\"INSERT INTO files (name, size) VALUES (%s, %s)\", [(\"file1\", 1024), (\"file2\", 2048)])\n\n async with pg.transaction() as tran:\n file_id = (\n await tran(\n \"INSERT INTO files (name, size) VALUES VALUES (%s, %s) RETURNING file_id;\",\n (\"you_may_not_exist\", 0),\n )\n )[0]\n await tran(\"INSERT INTO pages (page_number, file_id) VALUES (%s, %s);\", (4, file_id))\n raise ValueError(\"Oopsie\")\n await pg.close() # Close the connection pool. Python Postgres will attempt to automatically\n # close the pool when the instance is garbage collected, but this is not guaranteed to succeed.\n # Be civilized and close it yourself.\n```\n\n### Pydantic Integration\n\nPython Postgres supports [Pydantic](https://docs.pydantic.dev/latest/) Models as insert parameters.\n\n```python\nfrom pydantic import BaseModel\n\n\nclass File(BaseModel):\n file_name: str\n size: int\n\n\nasync def main():\n await pg.open()\n await pg(\n \"INSERT INTO files (file_name, size) VALUES (%s, %s)\",\n File(file_name=\"rubbish.pdf\", size=8096),\n )\n await pg.close()\n```\n\n### A more in-depth look\n\nThe basic idea of this project is to provide one callable instance of the `Postgres` class. The `Postgres` class manages\na connection pool in the background and will get a connection from the pool when called, spawn a binary cursor on it,\nrun your query, return the results (or the number of rows affected), and then return the connection to the pool. As a\nquery, you can pass either a literal - string or bytes - or a `SQL` or\n`Composed` [object](https://www.psycopg.org/psycopg3/docs/api/sql.html) from the `psycopg` library.\n\nIn Essence, the `Postgres` class is syntactic sugar for turning this:\n\n```python\nasync def exec_query(\n query: LiteralString | bytes | SQL | Composed,\n params: tuple | list[tuple],\n is_retry: bool = False,\n) -> list[tuple]:\n try:\n async with con_pool.connection() as con: # type: psycopg.AsyncConnection\n async with con.cursor(binary=True) as cur: # type: psycopg.AsyncCursor\n if isinstance(params, list):\n await cur.executemany(query, params)\n else:\n await cur.execute(query, params)\n await con.commit()\n return (\n await cur.fetchall()\n if cur.pgresult and cur.pgresult.ntuples > 0\n else cur.rowcount or -1\n )\n except psycopg.OperationalError as error:\n if is_retry:\n raise IOError from error\n await con_pool.check()\n await exec_query(query, params, True)\n except psycopg.Error as error:\n raise IOError from error\n\n\nawait exec_query(\"SELECT * FROM files WHERE id = %s\", (1234,))\n```\n\ninto\n\n```python\nawait pg(\"SELECT * FROM files WHERE id = %s\", (1234,))\n```\n\n### Notes\n\nOther than providing simpler syntax through a thin abstraction, this project inherits all the design choices of psycopg,\nincluding the [caching of query execution plans](https://www.psycopg.org/psycopg3/docs/advanced/prepare.html#index-0)\n",
"bugtrack_url": null,
"license": null,
"summary": "python-postgres aims to provide the simplest way to interact with PostgreSQL databases.",
"version": "0.0.5",
"project_urls": {
"Homepage": "https://github.com/VinzenzKlass/python-postgres",
"Repository": "https://github.com/VinzenzKlass/python-postgres"
},
"split_keywords": [
"python-postgres",
" postgres"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "5372aeef55e06e11e2d962bb7fd350cac2b08d657acead574ee636ac30852c51",
"md5": "a1be2524813bd48fbfc10b54f6e7342b",
"sha256": "9fbfaf26a2e82968c2847780f820763b3513ee05b3763de111fd680183b2d928"
},
"downloads": -1,
"filename": "python_postgres-0.0.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a1be2524813bd48fbfc10b54f6e7342b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 6145,
"upload_time": "2024-12-04T07:02:17",
"upload_time_iso_8601": "2024-12-04T07:02:17.830294Z",
"url": "https://files.pythonhosted.org/packages/53/72/aeef55e06e11e2d962bb7fd350cac2b08d657acead574ee636ac30852c51/python_postgres-0.0.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a17b48a5f4a1a06609078298dbbb2f596f9c6432a01ad2421c57c6e09b9d6d25",
"md5": "4eda87c3084eb2fb306261e84e1eb363",
"sha256": "aa923723a821e90c3e85607f3572667d81cbf7f90969b2125cc204bf2fc0b825"
},
"downloads": -1,
"filename": "python_postgres-0.0.5.tar.gz",
"has_sig": false,
"md5_digest": "4eda87c3084eb2fb306261e84e1eb363",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 5159,
"upload_time": "2024-12-04T07:02:19",
"upload_time_iso_8601": "2024-12-04T07:02:19.313070Z",
"url": "https://files.pythonhosted.org/packages/a1/7b/48a5f4a1a06609078298dbbb2f596f9c6432a01ad2421c57c6e09b9d6d25/python_postgres-0.0.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-04 07:02:19",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "VinzenzKlass",
"github_project": "python-postgres",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "python-postgres"
}