postgresql-access 2.0__tar.gz → 2.0.1__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 (19) hide show
  1. {postgresql_access-2.0/src/postgresql_access.egg-info → postgresql_access-2.0.1}/PKG-INFO +1 -1
  2. {postgresql_access-2.0 → postgresql_access-2.0.1}/pyproject.toml +1 -1
  3. postgresql_access-2.0.1/src/postgresql_access/certificatedatabase.py +89 -0
  4. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access/database.py +5 -3
  5. {postgresql_access-2.0 → postgresql_access-2.0.1/src/postgresql_access.egg-info}/PKG-INFO +1 -1
  6. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/tests/atest.py +5 -1
  7. postgresql_access-2.0/src/postgresql_access/certificatedatabase.py +0 -69
  8. {postgresql_access-2.0 → postgresql_access-2.0.1}/LICENSE +0 -0
  9. {postgresql_access-2.0 → postgresql_access-2.0.1}/README.md +0 -0
  10. {postgresql_access-2.0 → postgresql_access-2.0.1}/setup.cfg +0 -0
  11. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/alchemy/satest.py +0 -0
  12. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access/__init__.py +0 -0
  13. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access/main.py +0 -0
  14. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access.egg-info/SOURCES.txt +0 -0
  15. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access.egg-info/dependency_links.txt +0 -0
  16. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access.egg-info/entry_points.txt +0 -0
  17. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access.egg-info/requires.txt +0 -0
  18. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/postgresql_access.egg-info/top_level.txt +0 -0
  19. {postgresql_access-2.0 → postgresql_access-2.0.1}/src/tests/grouptest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: postgresql_access
3
- Version: 2.0
3
+ Version: 2.0.1
4
4
  Author-email: Gerard <gweatherby@uchc.edu>
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "postgresql_access"
3
- version = "2.0"
3
+ version = "2.0.1"
4
4
  dependencies = [
5
5
  "keyring"
6
6
  ]
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import pwd
5
+ from typing import Mapping
6
+
7
+ try:
8
+ import psycopg # psycopg3
9
+ PSYCOPG_VERSION = 3
10
+ except ImportError:
11
+ import psycopg2 as psycopg # fallback to psycopg2
12
+ PSYCOPG_VERSION = 2
13
+
14
+ """
15
+ object wrapper for psycopg2 or psycopg3
16
+ """
17
+
18
+ def _current_user():
19
+ """get current linux user"""
20
+ return pwd.getpwuid(os.geteuid()).pw_name
21
+
22
+
23
+ class CertificateDatabase:
24
+
25
+ def __init__(self, *, host: str, database: str, user: str, application_name: str,
26
+ client_cert: str, client_key: str, root_cert: str):
27
+ self.app_name = 'Python app'
28
+ self.schema = None
29
+ self.port = 5432
30
+ self.host = host
31
+ self.database = database
32
+ self.user = user
33
+ self.application_name = application_name
34
+ self.client_cert = client_cert
35
+ self.client_key = client_key
36
+ self.root_cert = root_cert
37
+
38
+ files = (self.client_cert, self.client_key, self.root_cert)
39
+ missing = [f for f in files if not os.path.exists(f)]
40
+ if missing:
41
+ raise ValueError(f"Missing configuration file(s): {','.join(missing)}")
42
+ permissions = [f for f in files if not os.access(f, os.R_OK)]
43
+ if permissions:
44
+ raise ValueError(f"No read access file(s): {','.join(permissions)}")
45
+
46
+ def connect(self):
47
+ """Connect to database, set schema if present, return connection"""
48
+ conninfo = (
49
+ f"host={self.host} "
50
+ f"dbname={self.database} "
51
+ f"user={self.user} "
52
+ f"port={self.port} "
53
+ f"application_name={self.application_name} "
54
+ f"sslmode=verify-full "
55
+ f"sslcert={self.client_cert} "
56
+ f"sslkey={self.client_key} "
57
+ f"sslrootcert={self.root_cert}"
58
+ )
59
+
60
+ try:
61
+ if PSYCOPG_VERSION == 3:
62
+ conn = psycopg.connect(conninfo)
63
+ else:
64
+ conn = psycopg.connect(conninfo)
65
+ except psycopg.OperationalError as oe:
66
+ if 'no password' in str(oe):
67
+ raise ValueError(f"Invalid certificates or key for user {self.user}, or not authorized by server")
68
+ raise
69
+
70
+ if self.schema is not None:
71
+ with conn.cursor() as cursor:
72
+ cursor.execute(f"SET search_path TO {self.schema}")
73
+ conn.commit()
74
+
75
+ return conn
76
+
77
+ @staticmethod
78
+ def create_from_dict(data: Mapping, application_name: str):
79
+ h = data['host']
80
+ d = data['database']
81
+ u = data.get('user', _current_user())
82
+ cc = data['client certificate']
83
+ rc = data['root certificate']
84
+ ck = data['client key']
85
+ return CertificateDatabase(
86
+ host=h, database=d, user=u, application_name=application_name,
87
+ client_cert=cc, client_key=ck, root_cert=rc
88
+ )
89
+
@@ -15,11 +15,13 @@ try:
15
15
  psycopg_module = 'psycopg3'
16
16
  connect = psycopg.connect
17
17
  OperationalError = psycopg.OperationalError
18
+ readonly = 'read_only'
18
19
  except ImportError:
19
20
  import psycopg2 as psycopg
20
21
  psycopg_module = 'psycopg2'
21
22
  connect = psycopg.connect
22
23
  OperationalError = psycopg.OperationalError
24
+ readonly = 'readonly'
23
25
 
24
26
 
25
27
  class AbstractDatabase(ABC):
@@ -161,15 +163,15 @@ class ReadOnlyCursor:
161
163
  self._factory = cursor_factory
162
164
 
163
165
  def __enter__(self):
164
- self.existing_readonly = self._conn.readonly
165
- self._conn.readonly = True
166
+ self.existing_readonly = getattr(self._conn,readonly)
167
+ setattr(self._conn,readonly, True)
166
168
  self._curs = self._conn.cursor(cursor_factory=self._factory)
167
169
  return self._curs
168
170
 
169
171
  def __exit__(self, exc_type, exc_val, exc_tb):
170
172
  self._curs.close()
171
173
  self._conn.rollback()
172
- self._conn.readonly = self.existing_readonly
174
+ setattr(self._conn,readonly, self.existing_readonly)
173
175
 
174
176
  @property
175
177
  def connection(self): return self._conn
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: postgresql_access
3
- Version: 2.0
3
+ Version: 2.0.1
4
4
  Author-email: Gerard <gweatherby@uchc.edu>
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -7,7 +7,7 @@ import keyring
7
7
  from keyring import backend
8
8
 
9
9
  from postgresql_access import postgresql_access_logger
10
- from postgresql_access.database import DatabaseConfig
10
+ from postgresql_access.database import DatabaseConfig, ReadOnlyCursor
11
11
 
12
12
 
13
13
  def main():
@@ -31,6 +31,10 @@ def main():
31
31
  with c.cursor() as cursor:
32
32
  cursor.execute('select now()')
33
33
  print(cursor.fetchone()[0])
34
+ c.rollback()
35
+ with ReadOnlyCursor(c) as cursor:
36
+ cursor.execute('select now()')
37
+ print(cursor.fetchone()[0])
34
38
 
35
39
 
36
40
  if __name__ == "__main__":
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env python3
2
- import os
3
- import pwd
4
- from typing import Mapping
5
-
6
- import psycopg2
7
-
8
- """
9
- object wrapper for psycopg2
10
- """
11
-
12
- def _current_user():
13
- """get current linux user"""
14
- return pwd.getpwuid(os.geteuid()).pw_name
15
-
16
-
17
- class CertificateDatabase:
18
-
19
- def __init__(self,*,host:str,database:str,user:str,application_name:str,client_cert:str,client_key:str,
20
- root_cert:str):
21
- self.app_name = 'Python app'
22
- self.schema = None
23
- self.port = 5432
24
- self.host = host
25
- self.database = database
26
- self.user = user
27
- self.application_name = application_name
28
- self.client_cert = client_cert
29
- self.client_key = client_key
30
- self.root_cert = root_cert
31
- files = (self.client_cert,self.client_key,self.root_cert)
32
- missing = [f for f in files if not os.path.exists(f)]
33
- if missing:
34
- raise ValueError(f"Missing configuration file(s): {','.join(missing)}")
35
- permissions = [f for f in files if not os.access(f,os.R_OK)]
36
- if permissions:
37
- raise ValueError(f"No read access file(s): {','.join(permissions)}")
38
-
39
-
40
- def connect(self):
41
- """Connect to database, set schema if present, return connection"""
42
- connect_string = f"""host='{self.host}' dbname='{self.database}' user='{self.user}' port={self.port}"""
43
- try:
44
- conn = psycopg2.connect(connect_string, application_name=self.application_name,
45
- sslmode='verify-full',
46
- sslcert=self.client_cert,
47
- sslkey=self.client_key,sslrootcert=self.root_cert)
48
- except psycopg2.OperationalError as oe:
49
- if 'no password' in str(oe):
50
- raise ValueError(f"Invalid certificates or key for user {self.user}, or not authorized by server")
51
- raise
52
- if self.schema is not None:
53
- with conn.cursor() as cursor:
54
- cursor.execute(f"set search_path to {self.schema}")
55
- conn.commit()
56
-
57
- return conn
58
-
59
- @staticmethod
60
- def create_from_dict(data:Mapping,application_name:str):
61
- h = data['host']
62
- d = data['database']
63
- u = data.get('user',_current_user())
64
- cc = data['client certificate']
65
- rc = data['root certificate']
66
- ck = data['client key']
67
- return CertificateDatabase(host=h, database=d, user=u, application_name=application_name, client_cert=cc,
68
- client_key=ck, root_cert=rc)
69
-