Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4f00dbb

Browse files
committed
improved setup.py, new enum for transaction isolation levels
1 parent 18d6d79 commit 4f00dbb

File tree

3 files changed

+85
-29
lines changed

3 files changed

+85
-29
lines changed

setup.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
from distutils.core import setup
1+
import sys
2+
3+
try:
4+
from setuptools import setup
5+
except ImportError:
6+
from distutils.core import setup
7+
8+
# Basic dependencies
9+
install_requires = ["pg8000", "six", "port-for"]
10+
11+
# Add compatibility enum class
12+
if sys.version_info < (3, 4):
13+
install_requires.append("enum34")
214

315
setup(
416
name='testgres',
@@ -11,4 +23,4 @@
1123
url='https://github.com/postgrespro/testgres',
1224
keywords=['testing', 'postgresql'],
1325
classifiers=[],
14-
install_requires=["pg8000", "six", "port-for", "enum34"])
26+
install_requires=install_requires)

testgres/testgres.py

+46-27
Original file line numberDiff line numberDiff line change
@@ -171,19 +171,12 @@ def stopped(self):
171171
return self.stop_event.isSet()
172172

173173

174-
def log_watch(node_name, pg_logname):
174+
class IsolationLevel(Enum):
175175
"""
176-
Starts thread for node that redirects
177-
postgresql logs to python logging system
176+
Transaction isolation level for NodeConnection
178177
"""
179178

180-
reader = TestgresLogger(node_name, open(pg_logname, 'r'))
181-
reader.start()
182-
183-
global util_threads
184-
util_threads.append(reader)
185-
186-
return reader
179+
ReadUncommitted, ReadCommitted, RepeatableRead, Serializable = range(4)
187180

188181

189182
class NodeConnection(object):
@@ -218,7 +211,7 @@ def __enter__(self):
218211
def __exit__(self, type, value, traceback):
219212
self.close()
220213

221-
def begin(self, isolation_level=0):
214+
def begin(self, isolation_level=IsolationLevel.ReadCommitted):
222215
# yapf: disable
223216
levels = [
224217
'read uncommitted',
@@ -227,40 +220,51 @@ def begin(self, isolation_level=0):
227220
'serializable'
228221
]
229222

230-
# Check if level is int [0..3]
231-
if (isinstance(isolation_level, int) and
232-
isolation_level in range(0, 4)):
223+
# Check if level is an IsolationLevel
224+
if (isinstance(isolation_level, IsolationLevel)):
233225

234-
# Replace index with isolation level type
235-
isolation_level = levels[isolation_level]
226+
# Get index of isolation level
227+
level_idx = isolation_level.value
228+
assert(level_idx in range(4))
236229

237-
# Or it might be a string
238-
elif (isinstance(isolation_level, six.text_type) and
239-
isolation_level.lower() in levels):
230+
# Replace isolation level with its name
231+
isolation_level = levels[level_idx]
240232

241-
# Nothing to do here
242-
pass
243-
244-
# Something is wrong, emit exception
245233
else:
246-
raise QueryException(
247-
'Invalid isolation level "{}"'.format(isolation_level))
234+
# Get name of isolation level
235+
level_str = str(isolation_level).lower()
236+
237+
# Validate level string
238+
if level_str not in levels:
239+
error = 'Invalid isolation level "{}"'
240+
raise QueryException(error.format(level_str))
241+
242+
# Replace isolation level with its name
243+
isolation_level = level_str
248244

249-
self.cursor.execute(
250-
'SET TRANSACTION ISOLATION LEVEL {}'.format(isolation_level))
245+
# Set isolation level
246+
cmd = 'SET TRANSACTION ISOLATION LEVEL {}'
247+
self.cursor.execute(cmd.format(isolation_level))
248+
249+
return self
251250

252251
def commit(self):
253252
self.connection.commit()
254253

254+
return self
255+
255256
def rollback(self):
256257
self.connection.rollback()
257258

259+
return self
260+
258261
def execute(self, query, *args):
259262
self.cursor.execute(query, args)
260263

261264
try:
262265
res = self.cursor.fetchall()
263266

267+
# pg8000 might return tuples
264268
if isinstance(res, tuple):
265269
res = [tuple(t) for t in res]
266270

@@ -1311,3 +1315,18 @@ def configure_testgres(**options):
13111315

13121316
for key, option in options.items():
13131317
setattr(TestgresConfig, key, option)
1318+
1319+
1320+
def log_watch(node_name, pg_logname):
1321+
"""
1322+
Start thread for node that redirects
1323+
PostgreSQL logs to python logging system.
1324+
"""
1325+
1326+
reader = TestgresLogger(node_name, open(pg_logname, 'r'))
1327+
reader.start()
1328+
1329+
global util_threads
1330+
util_threads.append(reader)
1331+
1332+
return reader

tests/test_simple.py

+25
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from testgres import get_new_node, get_pg_config, configure_testgres
2424
from testgres import bound_ports
2525
from testgres import NodeStatus
26+
from testgres import IsolationLevel
2627

2728

2829
class SimpleTest(unittest.TestCase):
@@ -442,6 +443,30 @@ def test_configure(self):
442443
# return to the base state
443444
configure_testgres(cache_initdb=True, cache_pg_config=True)
444445

446+
def test_isolation_levels(self):
447+
with get_new_node('node').init().start() as node:
448+
with node.connect() as con:
449+
# string levels
450+
con.begin('Read Uncommitted').commit()
451+
con.begin('Read Committed').commit()
452+
con.begin('Repeatable Read').commit()
453+
con.begin('Serializable').commit()
454+
455+
# enum levels
456+
con.begin(IsolationLevel.ReadUncommitted).commit()
457+
con.begin(IsolationLevel.ReadCommitted).commit()
458+
con.begin(IsolationLevel.RepeatableRead).commit()
459+
con.begin(IsolationLevel.Serializable).commit()
460+
461+
# check wrong level
462+
got_exception = False
463+
try:
464+
con.begin('Garbage').commit()
465+
except QueryException:
466+
got_exception = True
467+
468+
self.assertTrue(got_exception)
469+
445470

446471
if __name__ == '__main__':
447472
unittest.main()

0 commit comments

Comments
 (0)