database-wrapper-pgsql 0.1.33__tar.gz → 0.1.38__tar.gz

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.
Files changed (17) hide show
  1. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/PKG-INFO +3 -3
  2. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/README.md +1 -1
  3. database_wrapper_pgsql-0.1.38/database_wrapper_pgsql/db_wrapper_pgsql.py +131 -0
  4. database_wrapper_pgsql-0.1.38/database_wrapper_pgsql/db_wrapper_pgsql_async.py +141 -0
  5. database_wrapper_pgsql-0.1.38/database_wrapper_pgsql/db_wrapper_pgsql_mixin.py +171 -0
  6. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql.egg-info/PKG-INFO +3 -3
  7. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql.egg-info/SOURCES.txt +1 -0
  8. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql.egg-info/requires.txt +1 -1
  9. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/pyproject.toml +2 -2
  10. database_wrapper_pgsql-0.1.33/database_wrapper_pgsql/db_wrapper_pgsql.py +0 -498
  11. database_wrapper_pgsql-0.1.33/database_wrapper_pgsql/db_wrapper_pgsql_async.py +0 -518
  12. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql/__init__.py +0 -0
  13. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql/connector.py +0 -0
  14. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql/py.typed +0 -0
  15. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql.egg-info/dependency_links.txt +0 -0
  16. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/database_wrapper_pgsql.egg-info/top_level.txt +0 -0
  17. {database_wrapper_pgsql-0.1.33 → database_wrapper_pgsql-0.1.38}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: database_wrapper_pgsql
3
- Version: 0.1.33
3
+ Version: 0.1.38
4
4
  Summary: database_wrapper for PostgreSQL database
5
5
  Author-email: Gints Murans <gm@gm.lv>
6
6
  License: GNU General Public License v3.0 (GPL-3.0)
@@ -32,11 +32,11 @@ Classifier: Topic :: Software Development
32
32
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
33
33
  Requires-Python: >=3.8
34
34
  Description-Content-Type: text/markdown
35
- Requires-Dist: database_wrapper==0.1.33
35
+ Requires-Dist: database_wrapper==0.1.38
36
36
  Requires-Dist: psycopg[binary]>=3.2.0
37
37
  Requires-Dist: psycopg[pool]>=3.2.0
38
38
 
39
- # database_wrapper
39
+ # database_wrapper_pgsql
40
40
 
41
41
  _Part of the `database_wrapper` package._
42
42
 
@@ -1,4 +1,4 @@
1
- # database_wrapper
1
+ # database_wrapper_pgsql
2
2
 
3
3
  _Part of the `database_wrapper` package._
4
4
 
@@ -0,0 +1,131 @@
1
+ import logging
2
+ from typing import Any, overload
3
+
4
+ from psycopg import Cursor, sql
5
+ from psycopg.rows import class_row
6
+
7
+ from database_wrapper import DataModelType, DBWrapper
8
+
9
+ from .db_wrapper_pgsql_mixin import DBWrapperPgSQLMixin
10
+ from .connector import (
11
+ # Sync
12
+ PgConnectionType,
13
+ PgCursorType,
14
+ PgSQL,
15
+ )
16
+
17
+
18
+ class DBWrapperPgSQL(DBWrapperPgSQLMixin, DBWrapper):
19
+ """
20
+ Sync database wrapper for postgres
21
+ """
22
+
23
+ # Override db instance
24
+ db: PgSQL
25
+ """ PostgreSQL database connector """
26
+
27
+ dbConn: PgConnectionType | None = None
28
+ """ PostgreSQL connection object """
29
+
30
+ #######################
31
+ ### Class lifecycle ###
32
+ #######################
33
+
34
+ # Meta methods
35
+ # We are overriding the __init__ method for the type hinting
36
+ def __init__(
37
+ self,
38
+ db: PgSQL | None = None,
39
+ dbConn: PgConnectionType | None = None,
40
+ logger: logging.Logger | None = None,
41
+ ):
42
+ """
43
+ Initializes a new instance of the DBWrapper class.
44
+
45
+ Args:
46
+ db (MySQL): The PostgreSQL connector.
47
+ dbConn (MySqlConnection, optional): The PostgreSQL connection object. Defaults to None.
48
+ logger (logging.Logger, optional): The logger object. Defaults to None.
49
+ """
50
+ super().__init__(db, dbConn, logger)
51
+
52
+ ###############
53
+ ### Setters ###
54
+ ###############
55
+
56
+ def updateDb(self, db: PgSQL) -> None:
57
+ """
58
+ Updates the database backend object.
59
+
60
+ Args:
61
+ db (DatabaseBackend): The new database backend object.
62
+ """
63
+ self.db = db
64
+
65
+ def updateDbConn(self, dbConn: PgConnectionType) -> None:
66
+ """
67
+ Updates the database connection object.
68
+
69
+ Args:
70
+ dbConn (Any): The new database connection object.
71
+ """
72
+ self.dbConn = dbConn
73
+
74
+ ######################
75
+ ### Helper methods ###
76
+ ######################
77
+
78
+ @overload
79
+ def createCursor(self) -> PgCursorType: ...
80
+
81
+ @overload
82
+ def createCursor(
83
+ self,
84
+ emptyDataClass: DataModelType,
85
+ ) -> Cursor[DataModelType]: ...
86
+
87
+ def createCursor(
88
+ self,
89
+ emptyDataClass: DataModelType | None = None,
90
+ ) -> Cursor[DataModelType] | PgCursorType:
91
+ """
92
+ Creates a new cursor object.
93
+
94
+ Args:
95
+ emptyDataClass (DBDataModel | None, optional): The data model to use for the cursor.
96
+ Defaults to None.
97
+
98
+ Returns:
99
+ PgAsyncCursorType | AsyncCursor[DBDataModel]: The created cursor object.
100
+ """
101
+ assert self.db is not None, "Database connection is not set"
102
+
103
+ # First we need connection
104
+ if self.dbConn is None:
105
+ self.dbConn = self.db.connection
106
+
107
+ # Lets make sure we have a connection
108
+ if self.dbConn is None:
109
+ raise Exception("Failed to get connection")
110
+
111
+ if emptyDataClass is None:
112
+ return self.dbConn.cursor()
113
+
114
+ return self.dbConn.cursor(row_factory=class_row(emptyDataClass.__class__))
115
+
116
+ def logQuery(
117
+ self,
118
+ cursor: Cursor[Any],
119
+ query: sql.SQL | sql.Composed,
120
+ params: tuple[Any, ...],
121
+ ) -> None:
122
+ """
123
+ Logs the given query and parameters.
124
+
125
+ Args:
126
+ cursor (Any): The database cursor.
127
+ query (Any): The query to log.
128
+ params (tuple[Any, ...]): The parameters to log.
129
+ """
130
+ queryString = query.as_string(self.dbConn)
131
+ logging.getLogger().debug(f"Query: {queryString}")
@@ -0,0 +1,141 @@
1
+ import logging
2
+ from typing import Any, overload
3
+
4
+ from psycopg import AsyncCursor, sql
5
+ from psycopg.rows import class_row
6
+
7
+ from database_wrapper import DataModelType, DBWrapperAsync
8
+
9
+ from .db_wrapper_pgsql_mixin import DBWrapperPgSQLMixin
10
+ from .connector import (
11
+ # Async
12
+ PgAsyncConnectionType,
13
+ PgAsyncCursorType,
14
+ PgSQLWithPoolingAsync,
15
+ )
16
+
17
+
18
+ class DBWrapperPgSQLAsync(DBWrapperPgSQLMixin, DBWrapperAsync):
19
+ """
20
+ Async database wrapper for postgres
21
+
22
+ This is meant to be used in async environments.
23
+ Also remember to call close() when done as we cannot do that in __del__.
24
+ """
25
+
26
+ # Override db instance
27
+ db: PgSQLWithPoolingAsync
28
+ """ Async PostgreSQL database connector """
29
+
30
+ dbConn: PgAsyncConnectionType | None = None
31
+ """ Async PostgreSQL connection object """
32
+
33
+ #######################
34
+ ### Class lifecycle ###
35
+ #######################
36
+
37
+ # Meta methods
38
+ # We are overriding the __init__ method for the type hinting
39
+ def __init__(
40
+ self,
41
+ db: PgSQLWithPoolingAsync | None = None,
42
+ dbConn: PgAsyncConnectionType | None = None,
43
+ logger: logging.Logger | None = None,
44
+ ):
45
+ """
46
+ Initializes a new instance of the DBWrapper class.
47
+
48
+ Args:
49
+ db (MySQL): The PostgreSQL database connector.
50
+ dbConn (MySqlConnection, optional): The PostgreSQL connection object. Defaults to None.
51
+ logger (logging.Logger, optional): The logger object. Defaults to None.
52
+ """
53
+ super().__init__(db, dbConn, logger)
54
+
55
+ async def close(self) -> None:
56
+ if hasattr(self, "dbConn") and self.dbConn and hasattr(self, "db") and self.db:
57
+ await self.db.returnConnection(self.dbConn)
58
+
59
+ await super().close()
60
+
61
+ ###############
62
+ ### Setters ###
63
+ ###############
64
+
65
+ def updateDb(self, db: PgSQLWithPoolingAsync) -> None:
66
+ """
67
+ Updates the database backend object.
68
+
69
+ Args:
70
+ db (DatabaseBackend): The new database backend object.
71
+ """
72
+ self.db = db
73
+
74
+ def updateDbConn(self, dbConn: PgAsyncConnectionType) -> None:
75
+ """
76
+ Updates the database connection object.
77
+
78
+ Args:
79
+ dbConn (Any): The new database connection object.
80
+ """
81
+ self.dbConn = dbConn
82
+
83
+ ######################
84
+ ### Helper methods ###
85
+ ######################
86
+
87
+ @overload
88
+ async def createCursor(self) -> PgAsyncCursorType: ...
89
+
90
+ @overload
91
+ async def createCursor(
92
+ self,
93
+ emptyDataClass: DataModelType,
94
+ ) -> AsyncCursor[DataModelType]: ...
95
+
96
+ async def createCursor(
97
+ self,
98
+ emptyDataClass: DataModelType | None = None,
99
+ ) -> AsyncCursor[DataModelType] | PgAsyncCursorType:
100
+ """
101
+ Creates a new cursor object.
102
+
103
+ Args:
104
+ emptyDataClass (DBDataModel | None, optional): The data model to use for the cursor.
105
+ Defaults to None.
106
+
107
+ Returns:
108
+ PgAsyncCursorType | AsyncCursor[DBDataModel]: The created cursor object.
109
+ """
110
+ assert self.db is not None, "Database connection is not set"
111
+
112
+ # First we need connection
113
+ if self.dbConn is None:
114
+ status = await self.db.newConnection()
115
+ if not status:
116
+ raise Exception("Failed to create new connection")
117
+
118
+ (pgConn, _pgCur) = status
119
+ self.dbConn = pgConn
120
+
121
+ if emptyDataClass is None:
122
+ return self.dbConn.cursor()
123
+
124
+ return self.dbConn.cursor(row_factory=class_row(emptyDataClass.__class__))
125
+
126
+ def logQuery(
127
+ self,
128
+ cursor: AsyncCursor[Any],
129
+ query: sql.SQL | sql.Composed,
130
+ params: tuple[Any, ...],
131
+ ) -> None:
132
+ """
133
+ Logs the given query and parameters.
134
+
135
+ Args:
136
+ cursor (Any): The database cursor.
137
+ query (Any): The query to log.
138
+ params (tuple[Any, ...]): The parameters to log.
139
+ """
140
+ queryString = query.as_string(self.dbConn)
141
+ logging.getLogger().debug(f"Query: {queryString}")
@@ -0,0 +1,171 @@
1
+ from typing import Any, cast
2
+
3
+ from psycopg import sql
4
+
5
+ from database_wrapper import OrderByItem, DataModelType
6
+
7
+
8
+ class DBWrapperPgSQLMixin:
9
+ """
10
+ Mixin for providing methods that can be used by both sync and async versions of the DBWrapperPgSQL class.
11
+ """
12
+
13
+ ######################
14
+ ### Helper methods ###
15
+ ######################
16
+
17
+ def makeIdentifier(self, schema: str | None, name: str) -> sql.Identifier | str:
18
+ """
19
+ Creates a SQL identifier object from the given name.
20
+
21
+ Args:
22
+ name (str): The name to create the identifier from.
23
+
24
+ Returns:
25
+ sql.Identifier: The created SQL identifier object.
26
+ """
27
+ if schema:
28
+ return sql.Identifier(schema, name)
29
+
30
+ return sql.Identifier(name)
31
+
32
+ def turnDataIntoModel(
33
+ self,
34
+ emptyDataClass: DataModelType,
35
+ dbData: dict[str, Any],
36
+ ) -> DataModelType:
37
+ """
38
+ Turns the given data into a data model.
39
+ By default we are pretty sure that there is no factory in the cursor,
40
+ So we need to create a new instance of the data model and fill it with data
41
+
42
+ Args:
43
+ emptyDataClass (DataModelType): The data model to use.
44
+ dbData (dict[str, Any]): The data to turn into a model.
45
+
46
+ Returns:
47
+ DataModelType: The data model filled with data.
48
+ """
49
+
50
+ return cast(DataModelType, dbData)
51
+
52
+ #####################
53
+ ### Query methods ###
54
+ #####################
55
+
56
+ def filterQuery(
57
+ self,
58
+ schemaName: str | None,
59
+ tableName: str,
60
+ ) -> sql.SQL | sql.Composed | str:
61
+ """
62
+ Creates a SQL query to filter data from the given table.
63
+
64
+ Args:
65
+ schemaName (str): The name of the schema to filter data from.
66
+ tableName (str): The name of the table to filter data from.
67
+
68
+ Returns:
69
+ sql.SQL | sql.Composed: The created SQL query object.
70
+ """
71
+ return sql.SQL("SELECT * FROM {table}").format(
72
+ table=self.makeIdentifier(schemaName, tableName)
73
+ )
74
+
75
+ def orderQuery(
76
+ self,
77
+ orderBy: OrderByItem | None = None,
78
+ ) -> sql.SQL | sql.Composed | None:
79
+ """
80
+ Creates a SQL query to order the results by the given column.
81
+
82
+ Args:
83
+ orderBy (OrderByItem | None, optional): The column to order the results by. Defaults to None.
84
+
85
+ Returns:
86
+ Any: The created SQL query object.
87
+
88
+ TODO: Fix return type
89
+ """
90
+ if orderBy is None:
91
+ return None
92
+
93
+ orderList = [
94
+ f"{item[0]} {item[1] if len(item) > 1 and item[1] != None else 'ASC'}"
95
+ for item in orderBy
96
+ ]
97
+ return sql.SQL("ORDER BY %s" % ", ".join(orderList)) # type: ignore
98
+
99
+ def limitQuery(
100
+ self,
101
+ offset: int = 0,
102
+ limit: int = 100,
103
+ ) -> sql.Composed | sql.SQL | None:
104
+ if limit == 0:
105
+ return None
106
+
107
+ return sql.SQL("LIMIT {} OFFSET {}").format(limit, offset)
108
+
109
+ def _formatFilterQuery(
110
+ self,
111
+ query: sql.SQL | sql.Composed | str,
112
+ qFilter: sql.SQL | sql.Composed | None,
113
+ order: sql.SQL | sql.Composed | None,
114
+ limit: sql.SQL | sql.Composed | None,
115
+ ) -> sql.Composed:
116
+
117
+ if isinstance(query, str):
118
+ query = sql.SQL(query) # type: ignore
119
+
120
+ queryParts: list[sql.Composable] = [query]
121
+ if qFilter is not None:
122
+ queryParts.append(qFilter)
123
+ if order is not None:
124
+ queryParts.append(order)
125
+ if limit is not None:
126
+ queryParts.append(limit)
127
+
128
+ return sql.SQL(" ").join(queryParts)
129
+
130
+ def _formatInsertQuery(
131
+ self,
132
+ tableIdentifier: sql.Identifier | str,
133
+ storeData: dict[str, Any],
134
+ returnKey: sql.Identifier | str,
135
+ ) -> sql.Composed:
136
+ keys = storeData.keys()
137
+ values = list(storeData.values())
138
+
139
+ return sql.SQL(
140
+ "INSERT INTO {table} ({columns}) VALUES ({values}) RETURNING {id_key}"
141
+ ).format(
142
+ table=tableIdentifier,
143
+ columns=sql.SQL(", ").join(map(sql.Identifier, keys)),
144
+ values=sql.SQL(", ").join(sql.Placeholder() * len(values)),
145
+ id_key=returnKey,
146
+ )
147
+
148
+ def _formatUpdateQuery(
149
+ self,
150
+ tableIdentifier: sql.Identifier | str,
151
+ updateKey: sql.Identifier | str,
152
+ updateData: dict[str, Any],
153
+ ) -> sql.Composed:
154
+ keys = updateData.keys()
155
+ set_clause = sql.SQL(", ").join(
156
+ sql.Identifier(key) + sql.SQL(" = %s") for key in keys
157
+ )
158
+ return sql.SQL("UPDATE {table} SET {set_clause} WHERE {id_key} = %s").format(
159
+ table=tableIdentifier,
160
+ set_clause=set_clause,
161
+ id_key=updateKey,
162
+ )
163
+
164
+ def _formatDeleteQuery(
165
+ self,
166
+ tableIdentifier: sql.Identifier | str,
167
+ deleteKey: sql.Identifier | str,
168
+ ) -> sql.Composed:
169
+ return sql.SQL("DELETE FROM {table} WHERE {id_key} = %s").format(
170
+ table=tableIdentifier, id_key=deleteKey
171
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: database_wrapper_pgsql
3
- Version: 0.1.33
3
+ Version: 0.1.38
4
4
  Summary: database_wrapper for PostgreSQL database
5
5
  Author-email: Gints Murans <gm@gm.lv>
6
6
  License: GNU General Public License v3.0 (GPL-3.0)
@@ -32,11 +32,11 @@ Classifier: Topic :: Software Development
32
32
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
33
33
  Requires-Python: >=3.8
34
34
  Description-Content-Type: text/markdown
35
- Requires-Dist: database_wrapper==0.1.33
35
+ Requires-Dist: database_wrapper==0.1.38
36
36
  Requires-Dist: psycopg[binary]>=3.2.0
37
37
  Requires-Dist: psycopg[pool]>=3.2.0
38
38
 
39
- # database_wrapper
39
+ # database_wrapper_pgsql
40
40
 
41
41
  _Part of the `database_wrapper` package._
42
42
 
@@ -4,6 +4,7 @@ database_wrapper_pgsql/__init__.py
4
4
  database_wrapper_pgsql/connector.py
5
5
  database_wrapper_pgsql/db_wrapper_pgsql.py
6
6
  database_wrapper_pgsql/db_wrapper_pgsql_async.py
7
+ database_wrapper_pgsql/db_wrapper_pgsql_mixin.py
7
8
  database_wrapper_pgsql/py.typed
8
9
  database_wrapper_pgsql.egg-info/PKG-INFO
9
10
  database_wrapper_pgsql.egg-info/SOURCES.txt
@@ -1,3 +1,3 @@
1
- database_wrapper==0.1.33
1
+ database_wrapper==0.1.38
2
2
  psycopg[binary]>=3.2.0
3
3
  psycopg[pool]>=3.2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "database_wrapper_pgsql"
7
- version = "0.1.33"
7
+ version = "0.1.38"
8
8
  description = "database_wrapper for PostgreSQL database"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -35,7 +35,7 @@ classifiers = [
35
35
  ]
36
36
  keywords = ["database", "wrapper", "python", "postgresql", "pgsql"]
37
37
  dependencies = [
38
- "database_wrapper == 0.1.33",
38
+ "database_wrapper == 0.1.38",
39
39
  "psycopg[binary] >= 3.2.0",
40
40
  "psycopg[pool] >= 3.2.0",
41
41
  ]