From 1872559f3cb401d7584af802d119a2a15d0d3565 Mon Sep 17 00:00:00 2001 From: Crispen Gari Date: Thu, 1 Feb 2024 22:14:14 +0200 Subject: [PATCH] connections-objects --- dataloom/__init__.py | 3 + dataloom/conn/__init__.py | 83 +++++++++++ {orm => dataloom}/constants/__init__.py | 0 {orm => dataloom}/db/__init__.py | 38 +++++- {orm => dataloom}/exceptions/__init__.py | 4 + {orm => dataloom}/keys.py | 0 {orm => dataloom/model}/__init__.py | 0 {orm => dataloom}/model/column.py | 2 +- {orm => dataloom}/model/model.py | 7 +- {orm => dataloom}/model/statements.py | 0 {orm => dataloom}/tests/test_connection.py | 12 +- {orm => dataloom}/tests/test_create_table.py | 40 +++--- {orm => dataloom}/tests/test_delete.py | 24 ++-- {orm => dataloom}/tests/test_insert.py | 24 ++-- {orm => dataloom}/tests/test_query.py | 8 +- {orm => dataloom}/tests/test_update.py | 24 ++-- {orm => dataloom}/types/__init__.py | 0 orm/model/__init__.py => hi.db | 0 playground.py | 136 ++++++++++--------- requirements.txt | 2 + todo.txt | 8 ++ 21 files changed, 278 insertions(+), 137 deletions(-) create mode 100644 dataloom/__init__.py create mode 100644 dataloom/conn/__init__.py rename {orm => dataloom}/constants/__init__.py (100%) rename {orm => dataloom}/db/__init__.py (88%) rename {orm => dataloom}/exceptions/__init__.py (66%) rename {orm => dataloom}/keys.py (100%) rename {orm => dataloom/model}/__init__.py (100%) rename {orm => dataloom}/model/column.py (98%) rename {orm => dataloom}/model/model.py (99%) rename {orm => dataloom}/model/statements.py (100%) rename {orm => dataloom}/tests/test_connection.py (75%) rename {orm => dataloom}/tests/test_create_table.py (77%) rename {orm => dataloom}/tests/test_delete.py (83%) rename {orm => dataloom}/tests/test_insert.py (85%) rename {orm => dataloom}/tests/test_query.py (92%) rename {orm => dataloom}/tests/test_update.py (86%) rename {orm => dataloom}/types/__init__.py (100%) rename orm/model/__init__.py => hi.db (100%) diff --git a/dataloom/__init__.py b/dataloom/__init__.py new file mode 100644 index 0000000..fc446a0 --- /dev/null +++ b/dataloom/__init__.py @@ -0,0 +1,3 @@ +from dataloom.db import Dataloom + +dataloom = Dataloom() diff --git a/dataloom/conn/__init__.py b/dataloom/conn/__init__.py new file mode 100644 index 0000000..443427a --- /dev/null +++ b/dataloom/conn/__init__.py @@ -0,0 +1,83 @@ +from dataclasses import dataclass, field +from dataloom.exceptions import UnsupportedDialect +from sqlite3.dbapi2 import Connection +from os import PathLike +from typing_extensions import TypeAlias + +StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] + + +@dataclass(kw_only=True) +class Dialect: + def connection_options[T](self) -> T: + return {} + + +@dataclass(kw_only=True) +class PostgresDialect(Dialect): + # "postgres://postgres:postgres@localhost:5432/db" + connection_string: str | None = field(default=None) + database: str + user: str | None = field(default="postgres") + host: str | None = field(default="localhost") + port: int | None = field(default=5432) + password: str | None = field(default="postgres") + + def connection_options[T](self) -> T: + return ( + self.connection_string if self.connection_string is not None else vars(self) + ) + + +@dataclass(kw_only=True) +class MySQLDialect(Dialect): + connection_string: str | None = field(default=None) + database: str + user: str | None = field(default="root") + host: str | None = field(default="localhost") + port: int | None = field(default=3306) + password: str | None = field(default="root") + + def connection_options[T](self) -> T: + return ( + self.connection_string if self.connection_string is not None else vars(self) + ) + + +@dataclass(kw_only=True) +class SQLiteDialect(Dialect): + database: StrOrBytesPath = field(default="users.db") + timeout: float | None = field(default=None) + detect_types: int | None = field(default=None) + isolation_level: str | None = field(default=None) + check_same_thread: bool = field(default=None) + factory: type[Connection] | None = field(default=None) + cached_statements: int | None = field(default=None) + uri: bool | None = field(default=None) + + def connection_options[T](self) -> T: + return vars(self) + + +@dataclass +class ConnectionOptionsFactory: + @staticmethod + def get_connection_options(dialect, **kwargs): + if dialect == "postgres": + return { + k: v + for k, v in PostgresDialect(**kwargs).connection_options().items() + if v is not None + } + elif dialect == "sqlite": + return SQLiteDialect(**kwargs).connection_options() + elif dialect == "mysql": + return { + k: v + for k, v in MySQLDialect(**kwargs).connection_options().items() + if v is not None + } + else: + raise UnsupportedDialect( + "The dialect passed is not supported the supported dialects are: {'postgres', 'mysql', 'sqlite'}" + ) diff --git a/orm/constants/__init__.py b/dataloom/constants/__init__.py similarity index 100% rename from orm/constants/__init__.py rename to dataloom/constants/__init__.py diff --git a/orm/db/__init__.py b/dataloom/db/__init__.py similarity index 88% rename from orm/db/__init__.py rename to dataloom/db/__init__.py index b2567b2..fcabae9 100644 --- a/orm/db/__init__.py +++ b/dataloom/db/__init__.py @@ -1,9 +1,13 @@ import psycopg2 +from mysql import connector +import sqlite3 import inspect -from orm.constants import instances -from orm.model.statements import Statements -from orm.model.model import Model -from orm.model.column import ( +from dataloom.constants import instances +from dataloom.model.statements import Statements +from dataloom.exceptions import UnsupportedDialect +from dataloom.model.model import Model +from dataloom.conn import ConnectionOptionsFactory +from dataloom.model.column import ( Column, UpdatedAtColumn, PrimaryKeyColumn, @@ -12,6 +16,32 @@ ) +class Dataloom: + conn = None + + def connect(self, dialect, **kwargs): + if dialect == "postgres": + options = ConnectionOptionsFactory.get_connection_options(dialect, **kwargs) + self.conn = psycopg2.connect(**options) + return self.conn + elif dialect == "mysql": + options = ConnectionOptionsFactory.get_connection_options(dialect, **kwargs) + self.conn = connector.connect(**options) + return self.conn + elif dialect == "sqlite": + options = ConnectionOptionsFactory.get_connection_options(dialect, **kwargs) + self.conn = ( + sqlite3.connect(options.get("database")) + if "database" in options + else sqlite3.connect(**options) + ) + return self.conn + else: + raise UnsupportedDialect( + "The dialect passed is not supported the supported dialects are: {'postgres', 'mysql', 'sqlite'}" + ) + + class Database: def __init__( self, diff --git a/orm/exceptions/__init__.py b/dataloom/exceptions/__init__.py similarity index 66% rename from orm/exceptions/__init__.py rename to dataloom/exceptions/__init__.py index 7ad3e9c..5eccfd4 100644 --- a/orm/exceptions/__init__.py +++ b/dataloom/exceptions/__init__.py @@ -4,3 +4,7 @@ class PkNotDefinedException(Exception): class TooManyPkException(Exception): pass + + +class UnsupportedDialect(ValueError): + pass diff --git a/orm/keys.py b/dataloom/keys.py similarity index 100% rename from orm/keys.py rename to dataloom/keys.py diff --git a/orm/__init__.py b/dataloom/model/__init__.py similarity index 100% rename from orm/__init__.py rename to dataloom/model/__init__.py diff --git a/orm/model/column.py b/dataloom/model/column.py similarity index 98% rename from orm/model/column.py rename to dataloom/model/column.py index fc8f270..7f839fe 100644 --- a/orm/model/column.py +++ b/dataloom/model/column.py @@ -1,4 +1,4 @@ -from orm.types import POSTGRES_SQL_TYPES +from dataloom.types import POSTGRES_SQL_TYPES class CreatedAtColumn: diff --git a/orm/model/model.py b/dataloom/model/model.py similarity index 99% rename from orm/model/model.py rename to dataloom/model/model.py index 0511cb9..8cd9512 100644 --- a/orm/model/model.py +++ b/dataloom/model/model.py @@ -1,13 +1,13 @@ from typing import Any -from orm.model.column import ( +from dataloom.model.column import ( Column, CreatedAtColumn, UpdatedAtColumn, ForeignKeyColumn, PrimaryKeyColumn, ) -from orm.model.statements import Statements -from orm.exceptions import * +from dataloom.model.statements import Statements +from dataloom.exceptions import * import inspect from datetime import datetime import re @@ -388,7 +388,6 @@ def _get_update_bulk_where_stm(cls, filters: dict = {}, args: dict = {}): placeholder_values.append(f'"{updatedAtColumName}" = %s') values.append(current_time_stamp) - sql = Statements.UPDATE_BULK_WHERE_COMMAND.format( table_name=cls._get_name(), placeholder_values=", ".join(placeholder_values), diff --git a/orm/model/statements.py b/dataloom/model/statements.py similarity index 100% rename from orm/model/statements.py rename to dataloom/model/statements.py diff --git a/orm/tests/test_connection.py b/dataloom/tests/test_connection.py similarity index 75% rename from orm/tests/test_connection.py rename to dataloom/tests/test_connection.py index 4559481..05bba6e 100644 --- a/orm/tests/test_connection.py +++ b/dataloom/tests/test_connection.py @@ -1,7 +1,7 @@ class TestConnectionPG: def test_connect(self): - from orm.db import Database - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) conn = db.connect() @@ -9,10 +9,10 @@ def test_connect(self): conn.close() def test_connect_sync(self): - from orm.db import Database - from orm.keys import password, database, user - from orm.model.model import Model - from orm.model.column import Column, PrimaryKeyColumn + from dataloom.db import Database + from dataloom.keys import password, database, user + from dataloom.model.model import Model + from dataloom.model.column import Column, PrimaryKeyColumn class User(Model): __tablename__ = "users" diff --git a/orm/tests/test_create_table.py b/dataloom/tests/test_create_table.py similarity index 77% rename from orm/tests/test_create_table.py rename to dataloom/tests/test_create_table.py index b7559ee..ff0dcbb 100644 --- a/orm/tests/test_create_table.py +++ b/dataloom/tests/test_create_table.py @@ -1,9 +1,9 @@ class TestCreatingTablePG: def test_2_pk_error(self): - from orm.db import Database - from orm.model.column import Column, PrimaryKeyColumn - from orm.model.model import Model - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column, PrimaryKeyColumn + from dataloom.model.model import Model + from dataloom.keys import password, database, user import pytest db = Database(database, password=password, user=user) @@ -26,10 +26,10 @@ class User(Model): conn.close() def test_no_pk_error(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import Model - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import Model + from dataloom.keys import password, database, user import pytest db = Database(database, password=password, user=user) @@ -47,10 +47,10 @@ class User(Model): conn.close() def test_table_name(self): - from orm.db import Database - from orm.model.column import Column, PrimaryKeyColumn - from orm.model.model import Model - from orm.keys import database, password, user + from dataloom.db import Database + from dataloom.model.column import Column, PrimaryKeyColumn + from dataloom.model.model import Model + from dataloom.keys import database, password, user db = Database(database, password=password, user=user) conn = db.connect() @@ -70,10 +70,10 @@ class User(Model): conn.close() def test_connect_sync(self): - from orm.db import Database - from orm.keys import password, database, user - from orm.model.model import Model - from orm.model.column import Column, PrimaryKeyColumn + from dataloom.db import Database + from dataloom.keys import password, database, user + from dataloom.model.model import Model + from dataloom.model.column import Column, PrimaryKeyColumn class User(Model): __tablename__ = "users" @@ -97,10 +97,10 @@ class Post(Model): conn.close() def test_syncing_tables(self): - from orm.db import Database - from orm.keys import password, database, user - from orm.model.model import Model - from orm.model.column import Column, PrimaryKeyColumn + from dataloom.db import Database + from dataloom.keys import password, database, user + from dataloom.model.model import Model + from dataloom.model.column import Column, PrimaryKeyColumn class User(Model): __tablename__ = "users" diff --git a/orm/tests/test_delete.py b/dataloom/tests/test_delete.py similarity index 83% rename from orm/tests/test_delete.py rename to dataloom/tests/test_delete.py index d383c6a..a48a6b1 100644 --- a/orm/tests/test_delete.py +++ b/dataloom/tests/test_delete.py @@ -1,9 +1,9 @@ class TestDeletingOnPG: def test_delete_by_pk_single_fn(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import Model, PrimaryKeyColumn - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import Model, PrimaryKeyColumn + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) conn = db.connect() @@ -25,10 +25,10 @@ class User(Model): conn.close() def test_delete_one_fn(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import Model, PrimaryKeyColumn - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import Model, PrimaryKeyColumn + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) conn = db.connect() @@ -60,10 +60,10 @@ class User(Model): conn.close() def test_delete_bulk_fn(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import Model, PrimaryKeyColumn - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import Model, PrimaryKeyColumn + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) conn = db.connect() diff --git a/orm/tests/test_insert.py b/dataloom/tests/test_insert.py similarity index 85% rename from orm/tests/test_insert.py rename to dataloom/tests/test_insert.py index 772f781..94fe8b6 100644 --- a/orm/tests/test_insert.py +++ b/dataloom/tests/test_insert.py @@ -1,9 +1,9 @@ class TestInsertingOnPG: def test_insetting_single_document(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import Model, PrimaryKeyColumn - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import Model, PrimaryKeyColumn + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) conn = db.connect() @@ -21,16 +21,16 @@ class Users(Model): conn.close() def test_insetting_multiple_document(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import ( + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import ( Model, CreatedAtColumn, UpdatedAtColumn, ForeignKeyColumn, PrimaryKeyColumn, ) - from orm.keys import password, database, user + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) @@ -65,16 +65,16 @@ class Post(Model): conn.close() def test_relational_instances(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import ( + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import ( Model, CreatedAtColumn, UpdatedAtColumn, ForeignKeyColumn, PrimaryKeyColumn, ) - from orm.keys import password, database, user + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) diff --git a/orm/tests/test_query.py b/dataloom/tests/test_query.py similarity index 92% rename from orm/tests/test_query.py rename to dataloom/tests/test_query.py index e712e78..1be42f8 100644 --- a/orm/tests/test_query.py +++ b/dataloom/tests/test_query.py @@ -1,9 +1,9 @@ class TestQueryingPG: def test_querying_data(self): - from orm.db import Database - from orm.model.column import Column, PrimaryKeyColumn - from orm.model.model import Model - from orm.keys import password, database, user + from dataloom.db import Database + from dataloom.model.column import Column, PrimaryKeyColumn + from dataloom.model.model import Model + from dataloom.keys import password, database, user db = Database(database, password=password, user=user) conn = db.connect() diff --git a/orm/tests/test_update.py b/dataloom/tests/test_update.py similarity index 86% rename from orm/tests/test_update.py rename to dataloom/tests/test_update.py index 11f16ca..4d87697 100644 --- a/orm/tests/test_update.py +++ b/dataloom/tests/test_update.py @@ -1,14 +1,14 @@ class TestDeletingOnPG: def test_update_by_pk_single_fn(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import ( + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import ( Model, PrimaryKeyColumn, CreatedAtColumn, UpdatedAtColumn, ) - from orm.keys import password, database, user + from dataloom.keys import password, database, user import time, pytest db = Database(database, password=password, user=user) @@ -38,15 +38,15 @@ class User(Model): assert exc_info.value.pgcode == "25P02" def test_update_one_fn(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import ( + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import ( Model, PrimaryKeyColumn, CreatedAtColumn, UpdatedAtColumn, ) - from orm.keys import password, database, user + from dataloom.keys import password, database, user import time, pytest db = Database(database, password=password, user=user) @@ -76,15 +76,15 @@ class User(Model): assert exc_info.value.pgcode == "25P02" def test_update_bulk_fn(self): - from orm.db import Database - from orm.model.column import Column - from orm.model.model import ( + from dataloom.db import Database + from dataloom.model.column import Column + from dataloom.model.model import ( Model, PrimaryKeyColumn, CreatedAtColumn, UpdatedAtColumn, ) - from orm.keys import password, database, user + from dataloom.keys import password, database, user import time, pytest db = Database(database, password=password, user=user) diff --git a/orm/types/__init__.py b/dataloom/types/__init__.py similarity index 100% rename from orm/types/__init__.py rename to dataloom/types/__init__.py diff --git a/orm/model/__init__.py b/hi.db similarity index 100% rename from orm/model/__init__.py rename to hi.db diff --git a/playground.py b/playground.py index 91dd085..efc8b34 100644 --- a/playground.py +++ b/playground.py @@ -1,64 +1,76 @@ -from orm.db import Database -from orm.model.column import ( - Column, - CreatedAtColumn, - UpdatedAtColumn, - ForeignKeyColumn, - PrimaryKeyColumn, -) -from orm.model.model import Model - - -class User(Model): - __tablename__ = "users" - id = PrimaryKeyColumn(type="bigint", auto_increment=True) - username = Column(type="text", nullable=False) - name = Column(type="varchar", unique=False, length=255) - createdAt = CreatedAtColumn() - updatedAt = UpdatedAtColumn() - - def __str__(self) -> str: - return f"User<{self.id}>" - - def __repr__(self) -> str: - return f"User<{self.id}>" - - def to_dict(self): - return { - "id": self.id, - "name": self.name, - "username": self.username, - "createdAt": self.createAt, - "updatedAt": self.updatedAt, - } - - -class Post(Model): - __tablename__ = "posts" - id = PrimaryKeyColumn(type="bigint", auto_increment=True) - title = Column(type="text", nullable=False, default="Hello there!!") - createdAt = CreatedAtColumn() - updatedAt = UpdatedAtColumn() - userId = ForeignKeyColumn(User, onDelete="CASCADE", onUpdate="CASCADE") - - def to_dict(self): - return { - "id": self.id, - "title": self.title, - "userId": self.userId, - "createdAt": self.createdAt, - "updatedAt": self.updatedAt, - } - - -db = Database("hi", password="root", user="postgres") -conn, tables = db.connect_and_sync([User, Post], drop=True, force=True) -user = User(name="Crispen", username="heyy") -userId = db.create(user) -posts = db.create_bulk([Post(userId=userId, title=f"Post {i}") for i in range(2)]) - -post = db.find_by_pk(Post, 1, options={"include": [User]}) - -print(post.to_dict()) +from dataloom import dataloom + +# conn = dataloom.connect("postgres", database="hi", password="root", user="postgres") +# conn = dataloom.connect("mysql", database="hi", password="root", user="root") +conn = dataloom.connect("sqlite", database="hi.db") + +print(dir(conn)) if __name__ == "__main__": conn.close() + pass + + +# from dataloom.db import Database +# from dataloom.model.column import ( +# Column, +# CreatedAtColumn, +# UpdatedAtColumn, +# ForeignKeyColumn, +# PrimaryKeyColumn, +# ) +# from dataloom.model.model import Model + + +# class User(Model): +# __tablename__ = "users" +# id = PrimaryKeyColumn(type="bigint", auto_increment=True) +# username = Column(type="text", nullable=False) +# name = Column(type="varchar", unique=False, length=255) +# createdAt = CreatedAtColumn() +# updatedAt = UpdatedAtColumn() + +# def __str__(self) -> str: +# return f"User<{self.id}>" + +# def __repr__(self) -> str: +# return f"User<{self.id}>" + +# def to_dict(self): +# return { +# "id": self.id, +# "name": self.name, +# "username": self.username, +# "createdAt": self.createAt, +# "updatedAt": self.updatedAt, +# } + + +# class Post(Model): +# __tablename__ = "posts" +# id = PrimaryKeyColumn(type="bigint", auto_increment=True) +# title = Column(type="text", nullable=False, default="Hello there!!") +# createdAt = CreatedAtColumn() +# updatedAt = UpdatedAtColumn() +# userId = ForeignKeyColumn(User, onDelete="CASCADE", onUpdate="CASCADE") + +# def to_dict(self): +# return { +# "id": self.id, +# "title": self.title, +# "userId": self.userId, +# "createdAt": self.createdAt, +# "updatedAt": self.updatedAt, +# } + + +# db = Database("hi", password="root", user="postgres") +# conn, tables = db.connect_and_sync([User, Post], drop=True, force=True) +# user = User(name="Crispen", username="heyy") +# userId = db.create(user) +# posts = db.create_bulk([Post(userId=userId, title=f"Post {i}") for i in range(2)]) + +# post = db.find_by_pk(Post, 1, options={"include": [User]}) + +# print(post.to_dict()) +# if __name__ == "__main__": +# conn.close() diff --git a/requirements.txt b/requirements.txt index aa9d41d..d74722d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,8 @@ colorama==0.4.6 iniconfig==2.0.0 +mysql-connector-python==8.3.0 packaging==23.2 pluggy==1.4.0 psycopg2==2.9.9 pytest==8.0.0 +typing_extensions==4.9.0 diff --git a/todo.txt b/todo.txt index 7894f91..8fa1f58 100644 --- a/todo.txt +++ b/todo.txt @@ -12,3 +12,11 @@ 13. grouping data +--------- conn + +1. create 3 connections for: + * postgres + * mysql + * sqlite3 + +