Skip to content

Commit

Permalink
Modernize examples. (#403)
Browse files Browse the repository at this point in the history
  • Loading branch information
jettify authored Mar 17, 2023
1 parent b854906 commit 162afb1
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
- name: Test
run: |
make cov
make ci
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v3
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,12 @@ checkfmt:
isort --check-only --diff $(FILES)
black -l 79 --check $(FILES)

run_examples:
python examples/example_context_managers.py
python examples/example_pool.py
python examples/example_simple.py
python examples/example_complex_queries.py

ci: cov run_examples

.PHONY: all flake test vtest cov clean doc
53 changes: 18 additions & 35 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ aioodbc
:target: https://gitter.im/aio-libs/Lobby
:alt: Chat on Gitter

**aioodbc** is a Python 3.5+ module that makes it possible to access ODBC_ databases
**aioodbc** is a Python 3.7+ module that makes it possible to access ODBC_ databases
with asyncio_. It relies on the awesome pyodbc_ library and preserves the same look and
feel. *aioodbc* was written using `async/await` syntax (PEP492_) and thus is not compatible
with Python versions older than 3.5. Internally *aioodbc* employs threads to avoid
Expand All @@ -21,20 +21,6 @@ drivers like motor_ use the same approach.
**aioodbc** is fully compatible and tested with uvloop_. Take a look at the test
suite, all tests are executed with both the default event loop and uvloop_.

Supported Databases
-------------------

**aioodbc** should work with all databases supported by pyodbc_. But for now the
library has been tested with: **SQLite**, **MySQL** and **PostgreSQL**. Feel
free to add other databases to the test suite by submitting a PR.


Community
---------
Mailing List: https://groups.google.com/forum/#!forum/aio-libs

Chat room: https://gitter.im/aio-libs/Lobby


Basic Example
-------------
Expand All @@ -49,15 +35,13 @@ Properties are unchanged, so ``conn.prop`` is correct as well as
.. code:: python
import asyncio
import aioodbc
loop = asyncio.get_event_loop()
import aioodbc
async def test_example():
dsn = 'Driver=SQLite;Database=sqlite.db'
conn = await aioodbc.connect(dsn=dsn, loop=loop)
dsn = "Driver=SQLite;Database=sqlite.db"
conn = await aioodbc.connect(dsn=dsn)
cur = await conn.cursor()
await cur.execute("SELECT 42 AS age;")
Expand All @@ -68,7 +52,8 @@ Properties are unchanged, so ``conn.prop`` is correct as well as
await cur.close()
await conn.close()
loop.run_until_complete(test_example())
asyncio.run(test_example())
Connection Pool
Expand All @@ -78,15 +63,13 @@ Connection pooling is ported from aiopg_ and relies on PEP492_ features:
.. code:: python
import asyncio
import aioodbc
loop = asyncio.get_event_loop()
import aioodbc
async def test_pool():
dsn = 'Driver=SQLite;Database=sqlite.db'
pool = await aioodbc.create_pool(dsn=dsn, loop=loop)
dsn = "Driver=SQLite3;Database=sqlite.db"
pool = await aioodbc.create_pool(dsn=dsn)
async with pool.acquire() as conn:
cur = await conn.cursor()
Expand All @@ -98,7 +81,8 @@ Connection pooling is ported from aiopg_ and relies on PEP492_ features:
pool.close()
await pool.wait_closed()
loop.run_until_complete(test_pool())
asyncio.run(test_pool())
Context Managers
Expand All @@ -109,24 +93,23 @@ protocol:
.. code:: python
import asyncio
import aioodbc
loop = asyncio.get_event_loop()
import aioodbc
async def test_example():
dsn = 'Driver=SQLite;Database=sqlite.db'
dsn = "Driver=SQLite;Database=sqlite.db"
async with aioodbc.create_pool(dsn=dsn, loop=loop) as pool:
async with aioodbc.create_pool(dsn=dsn) as pool:
async with pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute('SELECT 42 AS age;')
await cur.execute("SELECT 42 AS age;")
val = await cur.fetchone()
print(val)
print(val.age)
loop.run_until_complete(test_example())
asyncio.run(test_example())
Installation
Expand Down Expand Up @@ -172,7 +155,7 @@ Other SQL Drivers
Requirements
------------

* Python_ 3.5+
* Python_ 3.7+
* pyodbc_
* uvloop_ (optional)

Expand Down
39 changes: 18 additions & 21 deletions examples/example_complex_queries.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
import asyncio
from functools import partial

import aioodbc

dsn = "Driver=SQLite3;Database=sqlite.db"
dsn = "Driver=SQLite3;Database=sqlite_complex.db"


# Sometimes you may want to reuse same connection parameters multiple times.
# This can be accomplished in a way below using partial function
connect = partial(aioodbc.connect, dsn=dsn, echo=True, autocommit=True)


async def test_init_database(loop=None):
async def test_init_database():
"""
Initialize test database with sample schema/data to reuse in other tests.
Make sure that in real applications you have database initialization
file as separate *.sql script or rely on autogenerated code provided
by your ORM.
"""
async with connect(loop=loop) as conn:
async with aioodbc.connect(dsn=dsn, echo=True, autocommit=True) as conn:
async with conn.cursor() as cur:
sql = "CREATE TABLE IF NOT EXISTS t1(n INTEGER, v TEXT);"
await cur.execute(sql)


async def test_error_without_context_managers(loop=None):
async def test_error_without_context_managers():
"""
When not using context manager you may end up having unclosed connections
in case of any error which lead to resource leakage. To avoid
`Unclosed connection` errors in your code always close after yourself.
"""
conn = await aioodbc.connect(dsn=dsn, loop=loop)
conn = await aioodbc.connect(dsn=dsn)
cur = await conn.cursor()

try:
Expand All @@ -44,15 +38,15 @@ async def test_error_without_context_managers(loop=None):
await conn.close()


async def test_insert_with_values(loop=None):
async def test_insert_with_values():
"""
When providing data to your SQL statement make sure to parametrize it with
question marks placeholders. Do not use string formatting or make sure
your data is escaped to prevent sql injections.
NOTE: pyodbc does not support named placeholders syntax.
"""
async with connect(loop=loop) as conn:
async with aioodbc.connect(dsn=dsn, echo=True, autocommit=True) as conn:
async with conn.cursor() as cur:
# Substitute sql markers with variables
await cur.execute(
Expand All @@ -72,19 +66,19 @@ async def test_insert_with_values(loop=None):
print(result[0])


async def test_commit(loop=None):
async def test_commit():
"""
When not using `autocommit` parameter do not forget to explicitly call
this method for your changes to persist within database.
"""
async with aioodbc.connect(dsn=dsn, loop=loop) as conn:
async with aioodbc.connect(dsn=dsn) as conn:
async with conn.cursor() as cur:
sql = 'INSERT INTO t1 VALUES(1, "test");'
await cur.execute(sql)
# Make sure your changes will be actually saved into database
await cur.commit()

async with aioodbc.connect(dsn=dsn, loop=loop) as conn:
async with aioodbc.connect(dsn=dsn) as conn:
async with conn.cursor() as cur:
sql_select = "SELECT * FROM t1;"
await cur.execute(sql_select)
Expand All @@ -93,9 +87,12 @@ async def test_commit(loop=None):
print(await cur.fetchone())


async def run_all():
await test_init_database()
await test_commit()
await test_insert_with_values()
await test_error_without_context_managers()


if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(test_init_database(loop))
loop.run_until_complete(test_commit(loop))
loop.run_until_complete(test_insert_with_values(loop))
loop.run_until_complete(test_error_without_context_managers(loop))
asyncio.run(run_all())
8 changes: 3 additions & 5 deletions examples/example_context_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import aioodbc

loop = asyncio.get_event_loop()


async def test_example():
dsn = "Driver=SQLite;Database=sqlite.db"
dsn = "Driver=SQLite3;Database=sqlite_context.db"

async with aioodbc.create_pool(dsn=dsn, loop=loop) as pool:
async with aioodbc.create_pool(dsn=dsn) as pool:
async with pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute("SELECT 42 AS age;")
Expand All @@ -17,4 +15,4 @@ async def test_example():
print(val.age)


loop.run_until_complete(test_example())
asyncio.run(test_example())
8 changes: 3 additions & 5 deletions examples/example_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import aioodbc

loop = asyncio.get_event_loop()


async def test_pool():
dsn = "Driver=SQLite;Database=sqlite.db"
pool = await aioodbc.create_pool(dsn=dsn, loop=loop)
dsn = "Driver=SQLite3;Database=sqlite_pool.db"
pool = await aioodbc.create_pool(dsn=dsn)

async with pool.acquire() as conn:
cur = await conn.cursor()
Expand All @@ -20,4 +18,4 @@ async def test_pool():
await pool.wait_closed()


loop.run_until_complete(test_pool())
asyncio.run(test_pool())
8 changes: 3 additions & 5 deletions examples/example_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import aioodbc

loop = asyncio.get_event_loop()


async def test_example():
dsn = "Driver=SQLite;Database=sqlite.db"
conn = await aioodbc.connect(dsn=dsn, loop=loop)
dsn = "Driver=SQLite3;Database=sqlite_simple.db"
conn = await aioodbc.connect(dsn=dsn)

cur = await conn.cursor()
await cur.execute("SELECT 42 AS age;")
Expand All @@ -19,4 +17,4 @@ async def test_example():
await conn.close()


loop.run_until_complete(test_example())
asyncio.run(test_example())

0 comments on commit 162afb1

Please sign in to comment.