upd, add sqlite
This commit is contained in:
parent
a511550f71
commit
ff69b68ba3
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
__pychache__/
|
__pychache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
dist/
|
dist/
|
||||||
|
db.sqlite3
|
||||||
|
*.db
|
||||||
|
12
README.md
12
README.md
@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
**Quicker** is a pythonic 🐍 tool for querying databases.
|
**Quicker** is a pythonic 🐍 tool for querying databases.
|
||||||
|
|
||||||
Quicker wraps popular Python libraries:
|
Quicker wraps Python libraries:
|
||||||
|
|
||||||
- `mysqlclient` for MySQL.
|
- `mysqlclient` for MySQL.
|
||||||
- `psycopg2` for PostgreSQL.
|
- `psycopg2` for PostgreSQL.
|
||||||
- `sqlite` from Python standard library for SQLite (not implemented yet).
|
- `sqlite3` from Python standard library for SQLite.
|
||||||
|
|
||||||
# Why is it needed?
|
# Why is it needed?
|
||||||
|
|
||||||
Briefly — just make queries without bothering to learn the abstractions of the library.
|
At work, I periodically have to make queries to different databases and then somehow process the information received. This may be necessary for one-time use, so I don't want to write a lot of code. You may also want to do all the work right in the interactive Python shell.
|
||||||
|
|
||||||
At work, I periodically have to make queries to different databases and then somehow process the information. I am not an analyst and Quicker is not intended for analysts. The main use of the library is to quickly execute raw SQL queries into the database. You may want to do this entirely in the Python interactive shell. Quicker interface is as simple as possible, thanks to which lazy system administrators can now effortlessly extract data from the database.
|
Quicker interface is as simple as possible, thanks to which lazy system administrators can now effortlessly extract data from the database.
|
||||||
|
|
||||||
Of course, this library **should not be used in production**. This is just a small assistant in routine tasks.
|
Of course, this library **should not be used in production**. This is just a small assistant in routine tasks.
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ logging.basicConfig(level=logging.DEBUG)
|
|||||||
## Direct access to Cursor object
|
## Direct access to Cursor object
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from quicker import Connection, make_list
|
from quicker import Connection, mklist
|
||||||
|
|
||||||
# config declaration here...
|
# config declaration here...
|
||||||
|
|
||||||
@ -144,5 +144,5 @@ with Connection(**config) as db:
|
|||||||
users = db.cursor.fetchall()
|
users = db.cursor.fetchall()
|
||||||
# Note: "users" is tuple! Convert it to list of dicts!
|
# Note: "users" is tuple! Convert it to list of dicts!
|
||||||
colnames = [desc[0] for desc in db.cursor.description]
|
colnames = [desc[0] for desc in db.cursor.description]
|
||||||
users_list = make_list(colnames, users)
|
users_list = mklist(colnames, users)
|
||||||
```
|
```
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "quicker"
|
name = "quicker"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
description = "Query databases quickly."
|
description = "Query databases quickly."
|
||||||
authors = ["ge <ge@nixhacks.net>"]
|
authors = ["ge <ge@nixhacks.net>"]
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
#
|
||||||
|
# .88888. oo dP
|
||||||
|
# d8' `8b 88
|
||||||
|
# 88 88 dP dP dP .d8888b. 88 .dP .d8888b. 88d888b.
|
||||||
|
# 88 db 88 88 88 88 88' `"" 88888" 88ooood8 88' `88
|
||||||
|
# Y8. Y88P 88. .88 88 88. ... 88 `8b. 88. ... 88
|
||||||
|
# `8888PY8b `88888P' dP `88888P' dP `YP `88888P' dP
|
||||||
|
#
|
||||||
|
# Quicker -- pythonic tool for querying databases.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import importlib.util
|
import importlib.util
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@ -21,10 +31,12 @@ def _import(module_name: str, symbol: Optional[str] = None):
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
def make_list(
|
def mklist(column_names: List[str], rows: Union[tuple, Tuple[dict, ...]]) -> List[dict]:
|
||||||
column_names: List[str], rows: Union[tuple, Tuple[dict, ...]]
|
"""
|
||||||
) -> List[dict]:
|
Convert output to list of dicts from tuples. `rows` can be
|
||||||
"""Convert output to list of dicts from tuples."""
|
default tuple or tuple of dicts if MySQL provider is used with
|
||||||
|
MySQLdb.cursors.DictCursor cursor class.
|
||||||
|
"""
|
||||||
data = []
|
data = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if isinstance(row, dict):
|
if isinstance(row, dict):
|
||||||
@ -38,7 +50,6 @@ def make_list(
|
|||||||
|
|
||||||
|
|
||||||
class Provider(str, Enum):
|
class Provider(str, Enum):
|
||||||
|
|
||||||
MYSQL = 'mysql'
|
MYSQL = 'mysql'
|
||||||
POSTGRES = 'postgres'
|
POSTGRES = 'postgres'
|
||||||
SQLITE = 'sqlite'
|
SQLITE = 'sqlite'
|
||||||
@ -55,26 +66,17 @@ class Connection:
|
|||||||
>>>
|
>>>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, provider: Provider, commit: bool = True, **kwargs):
|
||||||
provider: Provider = None,
|
|
||||||
commit: bool = True,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
if not provider:
|
|
||||||
raise ValueError('Database provider is not set')
|
|
||||||
self._provider = Provider(provider)
|
self._provider = Provider(provider)
|
||||||
self._commit = commit
|
self._commit = commit
|
||||||
self._connection_args = kwargs
|
self._connection_args = kwargs
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
logger.debug(f'Database provider={self._provider}')
|
logger.debug(f'Database provider={self._provider}')
|
||||||
# -- MySQL / MariaDB --
|
|
||||||
if self._provider == Provider.MYSQL:
|
if self._provider == Provider.MYSQL:
|
||||||
MySQLdb = _import('MySQLdb')
|
MySQLdb = _import('MySQLdb')
|
||||||
DictCursor = _import('MySQLdb.cursors', 'DictCursor')
|
DictCursor = _import('MySQLdb.cursors', 'DictCursor')
|
||||||
try:
|
try:
|
||||||
if self._connection_args['cursorclass']:
|
cursorclass = self._connection_args.pop('cursorclass')
|
||||||
cursorclass = DictCursor
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
cursorclass = DictCursor
|
cursorclass = DictCursor
|
||||||
self._connection = MySQLdb.connect(
|
self._connection = MySQLdb.connect(
|
||||||
@ -83,23 +85,35 @@ class Connection:
|
|||||||
)
|
)
|
||||||
logger.debug('Session started')
|
logger.debug('Session started')
|
||||||
self._cursor = self._connection.cursor()
|
self._cursor = self._connection.cursor()
|
||||||
return Query(self)
|
self._queryobj = Query(self)
|
||||||
# -- PostgreSQL --
|
|
||||||
if self._provider == Provider.POSTGRES:
|
if self._provider == Provider.POSTGRES:
|
||||||
psycopg2 = _import('psycopg2')
|
psycopg2 = _import('psycopg2')
|
||||||
dbname = self._connection_args.pop('database')
|
dbname = self._connection_args.pop('database')
|
||||||
self._connection_args['dbname'] = dbname
|
self._connection_args['dbname'] = dbname
|
||||||
self._connection = psycopg2.connect(**self._connection_args)
|
self._connection = psycopg2.connect(**self._connection_args)
|
||||||
self._cursor = self._connection.cursor()
|
self._cursor = self._connection.cursor()
|
||||||
return Query(self)
|
self._queryobj = Query(self)
|
||||||
|
|
||||||
|
if self._provider == Provider.SQLITE:
|
||||||
|
sqlite3 = _import('sqlite3') # Python may built without SQLite
|
||||||
|
self._connection = sqlite3.connect(**self._connection_args)
|
||||||
|
self._cursor = self._connection.cursor()
|
||||||
|
self._queryobj = Query(self)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self._queryobj
|
||||||
|
|
||||||
def __exit__(self, exception_type, exception_value, exception_traceback):
|
def __exit__(self, exception_type, exception_value, exception_traceback):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
if self._commit:
|
if self._commit:
|
||||||
|
logger.debug('Commiting changes into database')
|
||||||
self._connection.commit()
|
self._connection.commit()
|
||||||
logger.debug('Changes commited into database')
|
logger.debug('Closing cursor and connection')
|
||||||
self._cursor.close()
|
self._cursor.close()
|
||||||
self._connection.close()
|
self._connection.close()
|
||||||
logger.debug('Connection closed')
|
|
||||||
|
|
||||||
|
|
||||||
class Query:
|
class Query:
|
||||||
@ -128,13 +142,17 @@ class Query:
|
|||||||
self._fetchall = self._cursor.fetchall()
|
self._fetchall = self._cursor.fetchall()
|
||||||
except pgProgrammingError as e:
|
except pgProgrammingError as e:
|
||||||
self._fetchall = None
|
self._fetchall = None
|
||||||
|
if self._provider == Provider.SQLITE:
|
||||||
|
self._cursor.execute(*args, **kwargs)
|
||||||
|
logger.debug(f'sqlite3 ran: {args}')
|
||||||
|
self._fetchall = self._cursor.fetchall()
|
||||||
if self._fetchall is not None:
|
if self._fetchall is not None:
|
||||||
self._colnames = []
|
self._colnames = []
|
||||||
if self._cursor.description is not None:
|
if self._cursor.description is not None:
|
||||||
self._colnames = [
|
self._colnames = [
|
||||||
desc[0] for desc in self._cursor.description
|
desc[0] for desc in self._cursor.description
|
||||||
]
|
]
|
||||||
return make_list(self._colnames, self._fetchall)
|
return mklist(self._colnames, self._fetchall)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def commit(self) -> None:
|
def commit(self) -> None:
|
@ -1,9 +0,0 @@
|
|||||||
# ____ _ __
|
|
||||||
# / __ \__ __(_)____/ /_____ _____
|
|
||||||
# / / / / / / / / ___/ //_/ _ \/ ___/
|
|
||||||
# / /_/ / /_/ / / /__/ ,< / __/ /
|
|
||||||
# \___\_\__,_/_/\___/_/|_|\___/_/
|
|
||||||
#
|
|
||||||
# Quicker -- pythonic tool for querying databases
|
|
||||||
|
|
||||||
from .main import Connection, make_list
|
|
Loading…
Reference in New Issue
Block a user