Skip to content

Commit fa9cd0e

Browse files
author
James William Pye
committed
Make postgresql.version a bit more flexible.
Prior, split would error out if a version string with more than three version fields was given. This can be troublesome for using the parser with versions given by "non-pg" servers. So, make it more flexible, but guarantee three version fields and two state fields. Nicely, this ended up simplifying split and join.
1 parent fb34a97 commit fa9cd0e

1 file changed

Lines changed: 47 additions & 58 deletions

File tree

postgresql/version.py

Lines changed: 47 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -15,63 +15,53 @@
1515
0
1616
"""
1717

18-
def split(vstr):
18+
def split(vstr : str) -> (
19+
'major','minor','patch',...,'state_class','state_level'
20+
):
1921
"""
2022
Split a PostgreSQL version string into a tuple
21-
(major,minor,patch,state_class,state_level)
23+
(major,minor,patch,...,state_class,state_level)
2224
"""
23-
v = vstr.strip().split('.', 3)
25+
v = vstr.strip().split('.')
2426

25-
# Get rid of the numbers around the state_class (beta,a,dev,alpha)
27+
# Get rid of the numbers around the state_class (beta,a,dev,alpha, etc)
2628
state_class = v[-1].strip('0123456789')
2729
if state_class:
28-
last_version_num, state_level = v[-1].split(state_class)
30+
last_version, state_level = v[-1].split(state_class)
2931
if not state_level:
3032
state_level = None
3133
else:
3234
state_level = int(state_level)
35+
vlist = [int(x or '0') for x in v[:-1]]
36+
if last_version:
37+
vlist.append(int(last_version))
38+
vlist += [None] * (3 - len(vlist))
39+
vlist += [state_class, state_level]
3340
else:
34-
last_version_num = v[-1]
3541
state_level = None
3642
state_class = None
43+
vlist = [int(x or '0') for x in v]
44+
# pad the difference with `None` objects, and +2 for the state_*.
45+
vlist += [None] * ((3 - len(vlist)) + 2)
46+
return tuple(vlist)
3747

38-
if last_version_num:
39-
last_version_num = int(last_version_num)
40-
else:
41-
last_version_num = None
42-
43-
if len(v) == 3:
44-
major = int(v[0])
45-
if v[1]:
46-
minor = int(v[1])
47-
else:
48-
minor = None
49-
patch = last_version_num
50-
elif len(v) == 2:
51-
major = int(v[0])
52-
minor = last_version_num
53-
patch = None
54-
else:
55-
major = last_version_num
56-
minor = None
57-
patch = None
58-
59-
return (
60-
major,
61-
minor,
62-
patch,
63-
state_class,
64-
state_level
48+
def unsplit(vtup : tuple) -> str:
49+
'join a version tuple back into the original version string'
50+
svtup = [str(x) for x in vtup[:-2] if x is not None]
51+
state_class, state_level = vtup[-2:]
52+
return '.'.join(svtup) + (
53+
'' if state_class is None else state_class + str(state_level)
6554
)
6655

67-
def unsplit(vtup):
68-
'join a version tuple back into a version string'
69-
return '%s%s%s%s%s' %(
70-
vtup[0],
71-
vtup[1] is not None and '.' + str(vtup[1]) or '',
72-
vtup[2] is not None and '.' + str(vtup[2]) or '',
73-
vtup[3] is not None and str(vtup[3]) or '',
74-
vtup[4] is not None and str(vtup[4]) or ''
56+
def normalize(split_version : "a tuple returned by `split`") -> tuple:
57+
"""
58+
Given a tuple produced by `split`, normalize the `None` objects into int(0)
59+
or 'final' if it's the ``state_class``
60+
"""
61+
(*head, state_class, state_level) = split_version
62+
mmp = [x if x is not None else 0 for x in head]
63+
return tuple(
64+
mmp + [state_class or 'final', state_level or 0]
7565
)
7666

7767
default_state_class_priority = [
@@ -81,6 +71,7 @@ def unsplit(vtup):
8171
'b',
8272
'beta',
8373
'rc',
74+
'final',
8475
None,
8576
]
8677

@@ -108,17 +99,15 @@ def compare(
10899
raise ValueError("second argument has unknown state class %r" %(v2[-2],))
109100
return cmp(v1l, v2l)
110101

111-
112-
def python(self):
113-
return repr(self)
102+
python = repr
114103

115104
def xml(self):
116105
return '<version type="one">\n' + \
117106
' <major>' + str(self[0]) + '</major>\n' + \
118107
' <minor>' + str(self[1]) + '</minor>\n' + \
119108
' <patch>' + str(self[2]) + '</patch>\n' + \
120-
' <state>' + str(self[3]) + '</state>\n' + \
121-
' <level>' + str(self[4]) + '</level>\n' + \
109+
' <state>' + str(self[-2]) + '</state>\n' + \
110+
' <level>' + str(self[-1]) + '</level>\n' + \
122111
'</version>'
123112

124113
def sh(self):
@@ -130,12 +119,13 @@ def sh(self):
130119
str(self[0]),
131120
str(self[1]),
132121
str(self[2]),
133-
str(self[3]),
134-
str(self[4]),
122+
str(self[-2]),
123+
str(self[-1]),
135124
)
136125

137126
if __name__ == '__main__':
138127
import sys
128+
import os
139129
from optparse import OptionParser
140130
op = OptionParser()
141131
op.add_option('-f', '--format',
@@ -145,20 +135,19 @@ def sh(self):
145135
choices=('sh', 'xml', 'python'),
146136
default='sh',
147137
)
148-
op.add_option('-t', '--type',
149-
type='choice',
150-
dest='type',
151-
help='type of version string to parse',
152-
choices=('auto', 'one',),
153-
default='auto',
138+
op.add_option('-n', '--normalize',
139+
action='store_true',
140+
dest='normalize',
141+
help='replace missing values with defaults',
142+
default=False,
154143
)
155144
op.set_usage(op.get_usage().strip() + ' "version to parse"')
156145
co, ca = op.parse_args()
157146
if len(ca) != 1:
158147
op.error('requires exactly one argument, the version')
159-
if co.type != 'auto':
160-
v = getattr(sys.modules[__name__], co.type).parse(ca[0])
161148
else:
162149
v = split(ca[0])
163-
sys.stdout.write(getattr(v, co.format)())
164-
sys.stdout.write('\n')
150+
if co.normalize:
151+
v = normalize(v)
152+
sys.stdout.write(getattr(sys.modules[__name__], co.format)(v))
153+
sys.stdout.write(os.linesep)

0 commit comments

Comments
 (0)