Skip to content

Commit 28bf649

Browse files
committed
Alter postgresql.temporal to prefer PGTEST environment over PGINSTALLATION.
- Allows tests to run against arbitrary PQ servers(docker/podman/etc). - Adjust non-pg_tmp tests to skip if PGINSTALLATION is not available.
1 parent 72841f1 commit 28bf649

8 files changed

Lines changed: 150 additions & 102 deletions

File tree

postgresql/documentation/changes-v1.3.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ Changes in v1.3
77
* Commit DB-API 2.0 ClientCannotConnect exception correction.
88
* Eliminate types-as-documentation annotations.
99
* Eliminate multiple inheritance in `postgresql.api` in favor of ABC registration.
10+
* Add support for PGTEST environment variable (pq-IRI) to improve test performance
11+
and to aid in cases where the target fixture is already available.
12+
This should help for testing the driver against servers that are not actually
13+
postgresql.

postgresql/temporal.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ class Temporal(object):
2929
Or `pg_tmp` can decorate a method or function.
3030
"""
3131

32-
#: Format the cluster directory name.
33-
cluster_dirname = 'pg_tmp_{0}_{1}'.format
32+
format_sandbox_id = staticmethod(('sandbox{0}_{1}').format)
33+
cluster_dirname = staticmethod(('pg_tmp_{0}_{1}').format)
3434
cluster = None
3535

3636
_init_pid_ = None
@@ -91,7 +91,7 @@ def init(self,
9191
"environment variable to the `pg_config` path"
9292
}
9393
):
94-
if self.cluster is not None:
94+
if self.cluster is not None or 'PGTEST' in os.environ:
9595
return
9696
##
9797
# Hasn't been created yet, but doesn't matter.
@@ -156,7 +156,7 @@ def init(self,
156156
unix_socket_directories = cluster.data_directory,
157157
))
158158

159-
# Start it up.
159+
# Start the database cluster.
160160
with open(self.logfile, 'w') as lfo:
161161
cluster.start(logfile = lfo)
162162
cluster.wait_until_started()
@@ -165,18 +165,23 @@ def init(self,
165165
c = cluster.connection(user = 'test', database = 'template1',)
166166
with c:
167167
c.execute('create database test')
168-
# It's ready.
169168
self.cluster = cluster
170169

171170
def push(self):
172-
c = self.cluster.connection(user = 'test')
173-
c.connect()
171+
if 'PGTEST' in os.environ:
172+
from . import open as pg_open
173+
c = pg_open(os.environ['PGTEST']) # Ignoring PGINSTALLATION.
174+
else:
175+
c = self.cluster.connection(user = 'test')
176+
c.connect()
177+
174178
extras = []
179+
sbid = self.format_sandbox_id(os.getpid(), self.sandbox_id + 1)
175180

176-
def new_pg_tmp_connection(l = extras, c = c, sbid = 'sandbox' + str(self.sandbox_id + 1)):
181+
def new_pg_tmp_connection(l = extras, clone = c.clone, sbid = sbid):
177182
# Used to create a new connection that will be closed
178183
# when the context stack is popped along with 'db'.
179-
l.append(c.clone())
184+
l.append(clone())
180185
l[-1].settings['search_path'] = str(sbid) + ',' + l[-1].settings['search_path']
181186
return l[-1]
182187

@@ -205,7 +210,7 @@ def new_pg_tmp_connection(l = extras, c = c, sbid = 'sandbox' + str(self.sandbox
205210
builtins.__dict__.update(local_builtins)
206211
self.sandbox_id += 1
207212

208-
def pop(self, exc, drop_schema = 'DROP SCHEMA sandbox{0} CASCADE'.format):
213+
def pop(self, exc, drop_schema = ('DROP SCHEMA {0} CASCADE').format):
209214
local_builtins, extras = self.builtins_stack.pop()
210215
self.sandbox_id -= 1
211216

@@ -235,32 +240,36 @@ def pop(self, exc, drop_schema = 'DROP SCHEMA sandbox{0} CASCADE'.format):
235240

236241
# Interrupted and closed all the other connections at this level;
237242
# now remove the sandbox schema.
238-
c = self.cluster.connection(user = 'test')
239-
with c:
243+
xdb = local_builtins['db']
244+
with xdb.clone() as c:
240245
# Use a new connection so that the state of
241246
# the context connection will not have to be
242247
# contended with.
243-
c.execute(drop_schema(self.sandbox_id+1))
248+
c.execute(drop_schema(self.format_sandbox_id(os.getpid(), self.sandbox_id + 1)))
244249
else:
245-
# interrupt
250+
# interrupt exception; avoid waiting for close
246251
pass
247252

253+
def _init_c(self, cxn):
254+
cxn.connect()
255+
sb = self.format_sandbox_id(os.getpid(), self.sandbox_id)
256+
cxn.execute('CREATE SCHEMA ' + sb)
257+
cxn.settings['search_path'] = ','.join((sb, cxn.settings['search_path']))
258+
248259
def __enter__(self):
249260
if self.cluster is None:
250261
self.init()
262+
251263
self.push()
252264
try:
253-
db.connect()
254-
db.execute('CREATE SCHEMA sandbox' + str(self.sandbox_id))
255-
db.settings['search_path'] = 'sandbox' + str(self.sandbox_id) + ',' + db.settings['search_path']
265+
self._init_c(builtins.db)
256266
except Exception as e:
257267
# failed to initialize sandbox schema; pop it.
258268
self.pop(e)
259269
raise
260270

261271
def __exit__(self, exc, val, tb):
262-
if self.cluster is not None:
263-
self.pop(val)
272+
self.pop(val)
264273

265-
#: The process' temporary cluster.
274+
#: The process' temporary cluster or connection source.
266275
pg_tmp = Temporal()

postgresql/test/test_cluster.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,16 @@
99
from .. import installation
1010
from ..cluster import Cluster, ClusterStartupError
1111

12-
default_install = installation.default()
13-
if default_install is None:
14-
sys.stderr.write("ERROR: cannot find 'default' pg_config\n")
15-
sys.stderr.write("HINT: set the PGINSTALLATION environment variable to the `pg_config` path\n")
16-
sys.exit(1)
12+
default_installation = installation.default()
1713

1814
class test_cluster(unittest.TestCase):
1915
def setUp(self):
20-
self.cluster = Cluster(default_install, 'test_cluster',)
16+
self.cluster = Cluster(default_installation, 'test_cluster',)
2117

2218
def tearDown(self):
23-
self.cluster.drop()
24-
self.cluster = None
19+
if self.cluster.installation is not None:
20+
self.cluster.drop()
21+
self.cluster = None
2522

2623
def start_cluster(self, logfile = None):
2724
self.cluster.start(logfile = logfile)
@@ -46,6 +43,7 @@ def init(self, *args, **kw):
4643
usd : self.cluster.data_directory,
4744
})
4845

46+
@unittest.skipIf(default_installation is None, "no installation provided by environment")
4947
def testSilentMode(self):
5048
self.init()
5149
self.cluster.settings['silent_mode'] = 'on'
@@ -66,6 +64,7 @@ def testSilentMode(self):
6664
elif self.cluster.installation.version_info[:2] >= (9, 2):
6765
self.fail("silent_mode unexpectedly supported on PostgreSQL >=9.2")
6866

67+
@unittest.skipIf(default_installation is None, "no installation provided by environment")
6968
def testSuperPassword(self):
7069
self.init(
7170
user = 'test',
@@ -81,6 +80,7 @@ def testSuperPassword(self):
8180
with c:
8281
self.assertEqual(c.prepare('select 1').first(), 1)
8382

83+
@unittest.skipIf(default_installation is None, "no installation provided by environment")
8484
def testNoParameters(self):
8585
"""
8686
Simple init and drop.

0 commit comments

Comments
 (0)