db-sdk 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
db_sdk/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ from .core.factory import ConnectionFactory
2
+ from .core.exceptions import (
3
+ UniversalDBException,
4
+ UnsupportedDatabaseError,
5
+ DatabaseConnectionError,
6
+ ConfigurationError,
7
+ )
8
+
9
+ __version__ = "1.0.0"
10
+ __author__ = "Neha"
11
+
12
+
13
+ __all__ = [
14
+ "ConnectionFactory",
15
+ "UniversalDBException",
16
+ "UnsupportedDatabaseError",
17
+ "DatabaseConnectionError",
18
+ "ConfigurationError",
19
+ ]
@@ -0,0 +1,19 @@
1
+ import os
2
+ from db_sdk.core.exceptions import ConfigurationError
3
+
4
+
5
+ def load_config_from_env():
6
+ db_type = os.getenv("DB_TYPE")
7
+
8
+ if not db_type:
9
+ raise ConfigurationError("DB_TYPE environment variable not set.")
10
+
11
+ return {
12
+ "db_type": db_type,
13
+ "host": os.getenv("DB_HOST"),
14
+ "user": os.getenv("DB_USER"),
15
+ "password": os.getenv("DB_PASSWORD"),
16
+ "database": os.getenv("DB_NAME"),
17
+ "port": os.getenv("DB_PORT"),
18
+ "uri": os.getenv("DB_URI"),
19
+ }
File without changes
File without changes
@@ -0,0 +1,15 @@
1
+ from pymongo import MongoClient
2
+ from ..core.base_connector import BaseConnector
3
+ from ..core.exceptions import DatabaseConnectionError
4
+
5
+
6
+ class MongoConnector(BaseConnector):
7
+
8
+ def connect(self):
9
+ try:
10
+ uri = self.config.get("uri")
11
+ client = MongoClient(uri)
12
+ return client
13
+
14
+ except Exception as e:
15
+ raise DatabaseConnectionError(f"MongoDB connection failed: {e}")
@@ -0,0 +1,20 @@
1
+ import mysql.connector
2
+ from ..core.base_connector import BaseConnector
3
+ from ..core.exceptions import DatabaseConnectionError
4
+
5
+
6
+ class MySQLConnector(BaseConnector):
7
+
8
+ def connect(self):
9
+ try:
10
+ connection = mysql.connector.connect(
11
+ host=self.config.get("host"),
12
+ user=self.config.get("user"),
13
+ password=self.config.get("password"),
14
+ database=self.config.get("database"),
15
+ port=self.config.get("port", 3306),
16
+ )
17
+ return connection
18
+
19
+ except mysql.connector.Error as e:
20
+ raise DatabaseConnectionError(f"MySQL connection failed: {e}")
@@ -0,0 +1,20 @@
1
+ import psycopg2
2
+ from ..core.base_connector import BaseConnector
3
+ from ..core.exceptions import DatabaseConnectionError
4
+
5
+
6
+ class PostgresConnector(BaseConnector):
7
+
8
+ def connect(self):
9
+ try:
10
+ connection = psycopg2.connect(
11
+ host=self.config.get("host"),
12
+ user=self.config.get("user"),
13
+ password=self.config.get("password"),
14
+ dbname=self.config.get("database"),
15
+ port=self.config.get("port", 5432),
16
+ )
17
+ return connection
18
+
19
+ except psycopg2.Error as e:
20
+ raise DatabaseConnectionError(f"PostgreSQL connection failed: {e}")
@@ -0,0 +1,20 @@
1
+ import redis
2
+ from ..core.base_connector import BaseConnector
3
+ from ..core.exceptions import DatabaseConnectionError
4
+
5
+
6
+ class RedisConnector(BaseConnector):
7
+
8
+ def connect(self):
9
+ try:
10
+ connection = redis.Redis(
11
+ host=self.config.get("host"),
12
+ port=self.config.get("port", 6379),
13
+ password=self.config.get("password"),
14
+ decode_responses=True,
15
+ )
16
+ connection.ping() # Test connection
17
+ return connection
18
+
19
+ except redis.RedisError as e:
20
+ raise DatabaseConnectionError(f"Redis connection failed: {e}")
File without changes
@@ -0,0 +1,11 @@
1
+ class BaseConnector:
2
+ """
3
+ Base connector class.
4
+ All database connectors must inherit from this.
5
+ """
6
+
7
+ def __init__(self, config: dict):
8
+ self.config = config
9
+
10
+ def connect(self):
11
+ raise NotImplementedError("Connect method must be implemented by subclass.")
@@ -0,0 +1,18 @@
1
+ class UniversalDBException(Exception):
2
+ """Base exception for Universal DB SDK."""
3
+ pass
4
+
5
+
6
+ class UnsupportedDatabaseError(UniversalDBException):
7
+ """Raised when unsupported database type is provided."""
8
+ pass
9
+
10
+
11
+ class DatabaseConnectionError(UniversalDBException):
12
+ """Raised when connection fails."""
13
+ pass
14
+
15
+
16
+ class ConfigurationError(UniversalDBException):
17
+ """Raised when configuration is invalid."""
18
+ pass
db_sdk/core/factory.py ADDED
@@ -0,0 +1,30 @@
1
+ from db_sdk.connectors.mysql import MySQLConnector
2
+ from db_sdk.connectors.postgres import PostgresConnector
3
+ from db_sdk.connectors.mongodb import MongoConnector
4
+ from db_sdk.connectors.redis import RedisConnector
5
+ # from db_sdk.core.exceptions import UnsupportedDatabaseError
6
+ from .exceptions import UnsupportedDatabaseError
7
+
8
+
9
+ class ConnectionFactory:
10
+
11
+ CONNECTOR_MAPPING = {
12
+ "mysql": MySQLConnector,
13
+ "postgres": PostgresConnector,
14
+ "mongodb": MongoConnector,
15
+ "redis": RedisConnector,
16
+ }
17
+
18
+ @staticmethod
19
+ def create_connection(db_type: str, config: dict):
20
+ db_type = db_type.lower()
21
+
22
+ if db_type not in ConnectionFactory.CONNECTOR_MAPPING:
23
+ raise UnsupportedDatabaseError(
24
+ f"Unsupported database type: {db_type}"
25
+ )
26
+
27
+ connector_class = ConnectionFactory.CONNECTOR_MAPPING[db_type]
28
+ connector = connector_class(config)
29
+
30
+ return connector.connect()
@@ -0,0 +1,3 @@
1
+ from .builder import QueryBuilder
2
+
3
+ __all__ = ["QueryBuilder"]
@@ -0,0 +1,55 @@
1
+ # db_sdk/query/builder.py
2
+
3
+ class QueryBuilder:
4
+
5
+ def __init__(self, conn, db_type: str = "mysql"):
6
+ self._conn = conn
7
+ self._db_type = db_type # ← pass this to executor
8
+ self._table = None
9
+ self._fields = ["*"]
10
+ self._filters = []
11
+ self._values = []
12
+ self._limit = None
13
+ self._order = None
14
+ self._action = "select"
15
+ self._data = {}
16
+
17
+ def table(self, name: str) -> "QueryBuilder":
18
+ self._table = name
19
+ return self
20
+
21
+ def select(self, *fields) -> "QueryBuilder":
22
+ self._fields = list(fields) if fields else ["*"]
23
+ self._action = "select"
24
+ return self
25
+
26
+ def where(self, column: str, value) -> "QueryBuilder":
27
+ self._filters.append(f"{column} = %s")
28
+ self._values.append(value)
29
+ return self
30
+
31
+ def order_by(self, column: str, direction: str = "ASC") -> "QueryBuilder":
32
+ self._order = f"{column} {direction.upper()}"
33
+ return self
34
+
35
+ def limit(self, n: int) -> "QueryBuilder":
36
+ self._limit = n
37
+ return self
38
+
39
+ def insert(self, data: dict) -> "QueryBuilder":
40
+ self._action = "insert"
41
+ self._data = data
42
+ return self
43
+
44
+ def update(self, data: dict) -> "QueryBuilder":
45
+ self._action = "update"
46
+ self._data = data
47
+ return self
48
+
49
+ def delete(self) -> "QueryBuilder":
50
+ self._action = "delete"
51
+ return self
52
+
53
+ def execute(self):
54
+ from db_sdk.query.executor import Executor
55
+ return Executor(self._conn, self._db_type).run(self)
@@ -0,0 +1,127 @@
1
+ # db_sdk/query/executor.py
2
+
3
+ from db_sdk.core.exceptions import DatabaseConnectionError
4
+
5
+
6
+ class Executor:
7
+ """
8
+ Common executor for all databases — MySQL, PostgreSQL.
9
+ Works by receiving a ready cursor from the builder.
10
+ No database-specific logic lives here.
11
+ """
12
+
13
+ def __init__(self, conn, db_type: str = "mysql"):
14
+ self._conn = conn
15
+ self._db_type = db_type.lower()
16
+
17
+ def _get_cursor(self):
18
+ """
19
+ Returns a dictionary cursor based on the database type.
20
+ This is the only place database-specific logic lives.
21
+ """
22
+ if self._db_type == "mysql":
23
+ return self._conn.cursor(dictionary=True)
24
+
25
+ elif self._db_type == "postgres":
26
+ from psycopg2.extras import RealDictCursor
27
+ return self._conn.cursor(cursor_factory=RealDictCursor)
28
+
29
+ else:
30
+ raise ValueError(f"Unsupported db_type for executor: {self._db_type}")
31
+
32
+ def _fetch_insert_id(self, cur, table: str):
33
+ """
34
+ Gets the new row ID after INSERT.
35
+ MySQL uses lastrowid, PostgreSQL uses RETURNING id.
36
+ """
37
+ if self._db_type == "mysql":
38
+ return cur.lastrowid
39
+ elif self._db_type == "postgres":
40
+ result = cur.fetchone()
41
+ return result["id"]
42
+
43
+ def run(self, q):
44
+ """Route to the correct method based on action."""
45
+ actions = {
46
+ "select": self._select,
47
+ "insert": self._insert,
48
+ "update": self._update,
49
+ "delete": self._delete,
50
+ }
51
+ handler = actions.get(q._action)
52
+ if not handler:
53
+ raise ValueError(f"Unknown query action: {q._action}")
54
+ return handler(q)
55
+
56
+ # ── SELECT ───────────────────────────────────────────────
57
+
58
+ def _select(self, q):
59
+ fields = ", ".join(q._fields)
60
+ sql = f"SELECT {fields} FROM {q._table}"
61
+
62
+ if q._filters:
63
+ sql += " WHERE " + " AND ".join(q._filters)
64
+ if q._order:
65
+ sql += f" ORDER BY {q._order}"
66
+ if q._limit:
67
+ sql += f" LIMIT {q._limit}"
68
+
69
+ try:
70
+ cur = self._get_cursor()
71
+ cur.execute(sql, q._values)
72
+ return [dict(row) for row in cur.fetchall()]
73
+ except Exception as e:
74
+ raise DatabaseConnectionError(f"SELECT failed: {e}")
75
+
76
+ # ── INSERT ───────────────────────────────────────────────
77
+
78
+ def _insert(self, q):
79
+ keys = ", ".join(q._data.keys())
80
+ placeholders = ", ".join(["%s"] * len(q._data))
81
+ sql = f"INSERT INTO {q._table} ({keys}) VALUES ({placeholders})"
82
+
83
+ # PostgreSQL needs RETURNING id to get the new row id
84
+ if self._db_type == "postgres":
85
+ sql += " RETURNING id"
86
+
87
+ try:
88
+ cur = self._get_cursor()
89
+ cur.execute(sql, list(q._data.values()))
90
+ self._conn.commit()
91
+ return self._fetch_insert_id(cur, q._table)
92
+ except Exception as e:
93
+ raise DatabaseConnectionError(f"INSERT failed: {e}")
94
+
95
+ # ── UPDATE ───────────────────────────────────────────────
96
+
97
+ def _update(self, q):
98
+ set_clause = ", ".join(f"{k} = %s" for k in q._data.keys())
99
+ values = list(q._data.values()) + q._values
100
+ sql = f"UPDATE {q._table} SET {set_clause}"
101
+
102
+ if q._filters:
103
+ sql += " WHERE " + " AND ".join(q._filters)
104
+
105
+ try:
106
+ cur = self._get_cursor()
107
+ cur.execute(sql, values)
108
+ self._conn.commit()
109
+ return cur.rowcount
110
+ except Exception as e:
111
+ raise DatabaseConnectionError(f"UPDATE failed: {e}")
112
+
113
+ # ── DELETE ───────────────────────────────────────────────
114
+
115
+ def _delete(self, q):
116
+ sql = f"DELETE FROM {q._table}"
117
+
118
+ if q._filters:
119
+ sql += " WHERE " + " AND ".join(q._filters)
120
+
121
+ try:
122
+ cur = self._get_cursor()
123
+ cur.execute(sql, q._values)
124
+ self._conn.commit()
125
+ return cur.rowcount
126
+ except Exception as e:
127
+ raise DatabaseConnectionError(f"DELETE failed: {e}")
@@ -0,0 +1,15 @@
1
+ from db_sdk.core.factory import ConnectionFactory
2
+
3
+ config = {
4
+ "host": "192.168.137.9",
5
+ "user": "pwf",
6
+ "password": "JqYVmy5uPTzG",
7
+ "database": "pwf",
8
+ "port": 3306
9
+ }
10
+
11
+ try:
12
+ connection = ConnectionFactory.create_connection("mysql", config)
13
+ print("✅ Database connected successfully!")
14
+ except Exception as e:
15
+ print("❌ Connection failed:", e)
@@ -0,0 +1,175 @@
1
+ Metadata-Version: 2.4
2
+ Name: db_sdk
3
+ Version: 1.1.0
4
+ Summary: A unified database connector SDK for Python
5
+ Author: Neha
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: mysql-connector-python>=8.3.0
9
+ Requires-Dist: psycopg2-binary>=2.9.0
10
+ Requires-Dist: pymongo>=4.0.0
11
+ Requires-Dist: redis>=4.0.0
12
+ Dynamic: author
13
+ Dynamic: description
14
+ Dynamic: description-content-type
15
+ Dynamic: requires-dist
16
+ Dynamic: requires-python
17
+ Dynamic: summary
18
+
19
+ # 🗄️ Universal DB SDK
20
+
21
+ A lightweight, plug-and-play Python SDK for connecting to multiple databases
22
+ (MySQL, PostgreSQL, MongoDB, Redis) through a single unified interface.
23
+
24
+ ---
25
+
26
+ ## 📦 Installation
27
+ ```bash
28
+ pip install db-sdk
29
+ ```
30
+
31
+ Install only the drivers you need:
32
+ ```bash
33
+ pip install mysql-connector-python # MySQL
34
+ pip install psycopg2-binary # PostgreSQL
35
+ pip install pymongo # MongoDB
36
+ pip install redis # Redis
37
+ ```
38
+
39
+ ---
40
+
41
+ ## ✨ Features
42
+
43
+ - **One interface** for MySQL, PostgreSQL, MongoDB, and Redis
44
+ - **No boilerplate** — one function call returns a live connection
45
+ - **Easy to extend** — add a new database in minutes
46
+ - **Clean error handling** — typed exceptions for every failure mode
47
+ - **Zero framework dependency** — works with FastAPI, Django, Flask, or plain Python
48
+
49
+ ---
50
+
51
+ ## 🚀 Quick Start
52
+ ```python
53
+ from db_sdk.core.factory import ConnectionFactory
54
+
55
+ conn = ConnectionFactory.create_connection("mysql", {
56
+ "host": "localhost",
57
+ "user": "root",
58
+ "password": "yourpassword",
59
+ "database": "mydb",
60
+ "port": 3306
61
+ })
62
+ ```
63
+
64
+ That's it. `conn` is a live, ready-to-use connection object.
65
+
66
+ ---
67
+
68
+ ## 🔌 Supported Databases
69
+
70
+ | Database | Type string | Driver used |
71
+ |------------|--------------|------------------------------|
72
+ | MySQL | `"mysql"` | `mysql-connector-python` |
73
+ | PostgreSQL | `"postgres"` | `psycopg2` |
74
+ | MongoDB | `"mongodb"` | `pymongo` |
75
+ | Redis | `"redis"` | `redis` |
76
+
77
+ ---
78
+
79
+ ## ⚙️ Configuration Reference
80
+
81
+ ### MySQL
82
+ ```python
83
+ conn = ConnectionFactory.create_connection("mysql", {
84
+ "host": "localhost",
85
+ "user": "root",
86
+ "password": "yourpassword",
87
+ "database": "mydb",
88
+ "port": 3306 # optional, defaults to 3306
89
+ })
90
+ ```
91
+
92
+ ### PostgreSQL
93
+ ```python
94
+ conn = ConnectionFactory.create_connection("postgres", {
95
+ "host": "localhost",
96
+ "user": "postgres",
97
+ "password": "yourpassword",
98
+ "database": "mydb",
99
+ "port": 5432 # optional, defaults to 5432
100
+ })
101
+ ```
102
+
103
+ ### MongoDB
104
+ ```python
105
+ conn = ConnectionFactory.create_connection("mongodb", {
106
+ "uri": "mongodb://user:password@localhost:27017/mydb"
107
+ })
108
+ ```
109
+
110
+ > MongoDB uses a URI string instead of separate fields.
111
+ > Format: `mongodb://user:password@host:port/database`
112
+
113
+ ### Redis
114
+ ```python
115
+ conn = ConnectionFactory.create_connection("redis", {
116
+ "host": "localhost",
117
+ "port": 6379 # optional, defaults to 6379
118
+ })
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 🏗️ Project Structure
124
+ ```
125
+ db_sdk/
126
+ ├── __init__.py
127
+ ├── settings.py
128
+ ├── core/
129
+ │ ├── base_connector.py # Abstract base all connectors inherit from
130
+ │ ├── exceptions.py # All SDK exception types
131
+ │ └── factory.py # ConnectionFactory — the main entry point
132
+ └── connectors/
133
+ ├── mysql.py # MySQL connector
134
+ ├── postgres.py # PostgreSQL connector
135
+ ├── mongodb.py # MongoDB connector
136
+ └── redis.py # Redis connector
137
+ ```
138
+
139
+ ---
140
+
141
+ ## ⚠️ Error Handling
142
+
143
+ The SDK raises typed exceptions so you can handle failures precisely:
144
+ ```python
145
+ from db_sdk.core.factory import ConnectionFactory
146
+ from db_sdk.core.exceptions import (
147
+ DatabaseConnectionError,
148
+ UnsupportedDatabaseError,
149
+ ConfigurationError,
150
+ )
151
+
152
+ try:
153
+ conn = ConnectionFactory.create_connection("mysql", config)
154
+
155
+ except DatabaseConnectionError as e:
156
+ # Connection failed — wrong password, DB offline, network issue
157
+ print(f"Could not connect: {e}")
158
+
159
+ except UnsupportedDatabaseError as e:
160
+ # db_type string not recognised
161
+ print(f"Unknown database type: {e}")
162
+
163
+ except ConfigurationError as e:
164
+ # Missing or invalid keys in the config dict
165
+ print(f"Bad config: {e}")
166
+ ```
167
+
168
+ ### Exception Hierarchy
169
+ ```
170
+ UniversalDBException ← catch this for any SDK error
171
+ ├── DatabaseConnectionError
172
+ ├── UnsupportedDatabaseError
173
+ └── ConfigurationError
174
+ ```
175
+
@@ -0,0 +1,20 @@
1
+ db_sdk/__init__.py,sha256=wrz2jB0CIfzDtzSkLqNyclG1xzIB7xzBzFLlPkrm0V8,387
2
+ db_sdk/test_connection.py,sha256=iEnbWKy5JE9PQx_Ltyrd-B46295uGzzCH-EyDRhqEDc,371
3
+ db_sdk/config/__init__.py,sha256=8oYWCzSeU8jAHKIdO03T92VMmBHjgjLc7UZENoxrJR4,509
4
+ db_sdk/config/settings.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ db_sdk/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ db_sdk/connectors/mongodb.py,sha256=0qp1pZme06h3U0NNzt_Q8ffBV1v6aLxuY2SFzTjUW3Y,423
7
+ db_sdk/connectors/mysql.py,sha256=M8UkfrfIbCW4PwytQYuisi4QvxHx49BrfbOHYyOuQx8,665
8
+ db_sdk/connectors/postgres.py,sha256=0XXKbHdQJWPX95pfaU14Jn7eEnJVjOeovYA55w0alb0,650
9
+ db_sdk/connectors/redis.py,sha256=WwVqKvJkk9DPwQg0dPy5SJmQh4T23_fELGy0uApsyoE,626
10
+ db_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ db_sdk/core/base_connector.py,sha256=IoOpxqUPgNDsOK2om755qF0Mz-zrQIplXBngGeMQsLk,291
12
+ db_sdk/core/exceptions.py,sha256=R9q4_FkxaZhyf24Cwmg8pfs0dh5sFoXf_Ih88bdr2kA,431
13
+ db_sdk/core/factory.py,sha256=hrydt3J3S2YrYgoGo1iBjG_E8dFvvbX_QwhkbdSrFpo,960
14
+ db_sdk/query/__init__.py,sha256=65_3tvrhYPlEHOCza3phXVZvOUwSnTgB_p7hGbvo8Kc,61
15
+ db_sdk/query/builder.py,sha256=zl2LZ4om7rjDySncySp4XXuA-Jnw9_1XEurxYRTgGkg,1584
16
+ db_sdk/query/executor.py,sha256=EpM1TbA_5fU95RqKeI4XkSgq4Btzhdetm1SovbzRfIQ,4501
17
+ db_sdk-1.1.0.dist-info/METADATA,sha256=rUOvk9C48lCIhRPhlm4_FmrsFZf9ocV44a2TwhKn0pQ,4512
18
+ db_sdk-1.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
19
+ db_sdk-1.1.0.dist-info/top_level.txt,sha256=sWeCT6R-hslk8L6onXix41smZ45S8k6LmYDErKvC8xA,7
20
+ db_sdk-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ db_sdk