diff --git a/.gitignore b/.gitignore index 5e9a642..fe3bfa9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pychache__/ *.pyc dist/ +db.sqlite3 +*.db diff --git a/README.md b/README.md index db8160c..238e696 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,17 @@ **Quicker** is a pythonic 🐍 tool for querying databases. -Quicker wraps popular Python libraries: +Quicker wraps Python libraries: - `mysqlclient` for MySQL. - `psycopg2` for PostgreSQL. -- `sqlite` from Python standard library for SQLite (not implemented yet). +- `sqlite3` from Python standard library for SQLite. # 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. @@ -135,7 +135,7 @@ logging.basicConfig(level=logging.DEBUG) ## Direct access to Cursor object ```python -from quicker import Connection, make_list +from quicker import Connection, mklist # config declaration here... @@ -144,5 +144,5 @@ with Connection(**config) as db: users = db.cursor.fetchall() # Note: "users" is tuple! Convert it to list of dicts! colnames = [desc[0] for desc in db.cursor.description] - users_list = make_list(colnames, users) -``` \ No newline at end of file + users_list = mklist(colnames, users) +``` diff --git a/pyproject.toml b/pyproject.toml index 43843cb..57da92f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "quicker" -version = "0.2.0" +version = "0.3.0" description = "Query databases quickly." authors = ["ge "] license = "Unlicense" diff --git a/quicker/main.py b/quicker.py similarity index 72% rename from quicker/main.py rename to quicker.py index 4392eab..6098674 100644 --- a/quicker/main.py +++ b/quicker.py @@ -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 importlib.util from enum import Enum @@ -21,10 +31,12 @@ def _import(module_name: str, symbol: Optional[str] = None): return module -def make_list( - column_names: List[str], rows: Union[tuple, Tuple[dict, ...]] -) -> List[dict]: - """Convert output to list of dicts from tuples.""" +def mklist(column_names: List[str], rows: Union[tuple, Tuple[dict, ...]]) -> List[dict]: + """ + Convert output to list of dicts from tuples. `rows` can be + default tuple or tuple of dicts if MySQL provider is used with + MySQLdb.cursors.DictCursor cursor class. + """ data = [] for row in rows: if isinstance(row, dict): @@ -38,7 +50,6 @@ def make_list( class Provider(str, Enum): - MYSQL = 'mysql' POSTGRES = 'postgres' SQLITE = 'sqlite' @@ -55,26 +66,17 @@ class Connection: >>> """ - def __init__(self, - provider: Provider = None, - commit: bool = True, - **kwargs - ): - if not provider: - raise ValueError('Database provider is not set') + def __init__(self, provider: Provider, commit: bool = True, **kwargs): self._provider = Provider(provider) self._commit = commit self._connection_args = kwargs - - def __enter__(self): logger.debug(f'Database provider={self._provider}') - # -- MySQL / MariaDB -- + if self._provider == Provider.MYSQL: MySQLdb = _import('MySQLdb') DictCursor = _import('MySQLdb.cursors', 'DictCursor') try: - if self._connection_args['cursorclass']: - cursorclass = DictCursor + cursorclass = self._connection_args.pop('cursorclass') except KeyError: cursorclass = DictCursor self._connection = MySQLdb.connect( @@ -83,23 +85,35 @@ class Connection: ) logger.debug('Session started') self._cursor = self._connection.cursor() - return Query(self) - # -- PostgreSQL -- + self._queryobj = Query(self) + if self._provider == Provider.POSTGRES: psycopg2 = _import('psycopg2') dbname = self._connection_args.pop('database') self._connection_args['dbname'] = dbname self._connection = psycopg2.connect(**self._connection_args) 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): + self.close() + + def close(self): if self._commit: + logger.debug('Commiting changes into database') self._connection.commit() - logger.debug('Changes commited into database') + logger.debug('Closing cursor and connection') self._cursor.close() self._connection.close() - logger.debug('Connection closed') class Query: @@ -128,13 +142,17 @@ class Query: self._fetchall = self._cursor.fetchall() except pgProgrammingError as e: 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: self._colnames = [] if self._cursor.description is not None: self._colnames = [ desc[0] for desc in self._cursor.description ] - return make_list(self._colnames, self._fetchall) + return mklist(self._colnames, self._fetchall) return None def commit(self) -> None: diff --git a/quicker/__init__.py b/quicker/__init__.py deleted file mode 100644 index cfb3958..0000000 --- a/quicker/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# ____ _ __ -# / __ \__ __(_)____/ /_____ _____ -# / / / / / / / / ___/ //_/ _ \/ ___/ -# / /_/ / /_/ / / /__/ ,< / __/ / -# \___\_\__,_/_/\___/_/|_|\___/_/ -# -# Quicker -- pythonic tool for querying databases - -from .main import Connection, make_list