2010-02-15 04:12:13 +03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
"""Generate the errorcodes module starting from PostgreSQL documentation.
|
|
|
|
|
|
|
|
The script can be run at a new PostgreSQL release to refresh the module.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Copyright (C) 2010 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
|
|
|
#
|
2014-05-20 20:50:53 +04:00
|
|
|
# psycopg2 is free software: you can redistribute it and/or modify it
|
|
|
|
# under the terms of the GNU Lesser General Public License as published
|
|
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
2010-02-15 04:12:13 +03:00
|
|
|
#
|
2014-05-20 20:50:53 +04:00
|
|
|
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
# License for more details.
|
2010-02-15 04:12:13 +03:00
|
|
|
|
|
|
|
import sys
|
|
|
|
import urllib2
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
from BeautifulSoup import BeautifulSoup as BS
|
|
|
|
|
|
|
|
def main():
|
|
|
|
if len(sys.argv) != 2:
|
|
|
|
print >>sys.stderr, "usage: %s /path/to/errorcodes.py" % sys.argv[0]
|
|
|
|
return 2
|
|
|
|
|
|
|
|
filename = sys.argv[1]
|
|
|
|
|
|
|
|
file_start = read_base_file(filename)
|
2012-09-21 04:59:02 +04:00
|
|
|
classes, errors = fetch_errors(
|
|
|
|
['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2'])
|
2010-02-15 04:12:13 +03:00
|
|
|
|
|
|
|
f = open(filename, "w")
|
|
|
|
for line in file_start:
|
|
|
|
print >>f, line
|
|
|
|
for line in generate_module_data(classes, errors):
|
|
|
|
print >>f, line
|
|
|
|
|
|
|
|
def read_base_file(filename):
|
|
|
|
rv = []
|
|
|
|
for line in open(filename):
|
|
|
|
rv.append(line.rstrip("\n"))
|
|
|
|
if line.startswith("# autogenerated"):
|
|
|
|
return rv
|
|
|
|
|
|
|
|
raise ValueError("can't find the separator. Is this the right file?")
|
|
|
|
|
|
|
|
def parse_errors(url):
|
2011-12-16 18:47:09 +04:00
|
|
|
page = BS(urllib2.urlopen(url))
|
2010-02-15 04:12:13 +03:00
|
|
|
table = page('table')[1]('tbody')[0]
|
|
|
|
|
|
|
|
classes = {}
|
|
|
|
errors = defaultdict(dict)
|
|
|
|
|
|
|
|
for tr in table('tr'):
|
|
|
|
if tr.td.get('colspan'): # it's a class
|
2011-12-16 18:47:09 +04:00
|
|
|
label = ' '.join(' '.join(tr(text=True)).split()) \
|
|
|
|
.replace(u'\u2014', '-').encode('ascii')
|
2010-02-15 04:12:13 +03:00
|
|
|
assert label.startswith('Class')
|
|
|
|
class_ = label.split()[1]
|
|
|
|
assert len(class_) == 2
|
|
|
|
classes[class_] = label
|
|
|
|
|
|
|
|
else: # it's an error
|
|
|
|
errcode = tr.tt.string.encode("ascii")
|
|
|
|
assert len(errcode) == 5
|
2010-02-16 00:59:49 +03:00
|
|
|
|
2011-08-22 20:20:56 +04:00
|
|
|
tds = tr('td')
|
|
|
|
if len(tds) == 3:
|
2011-12-16 18:47:09 +04:00
|
|
|
errlabel = '_'.join(tds[1].string.split()).encode('ascii')
|
2011-08-22 20:20:56 +04:00
|
|
|
|
|
|
|
# double check the columns are equal
|
2011-12-16 18:47:09 +04:00
|
|
|
cond_name = tds[2].string.strip().upper().encode("ascii")
|
2011-08-22 20:20:56 +04:00
|
|
|
assert errlabel == cond_name, tr
|
|
|
|
|
|
|
|
elif len(tds) == 2:
|
2011-12-16 18:47:09 +04:00
|
|
|
# found in PG 9.1 docs
|
2011-08-22 20:20:56 +04:00
|
|
|
errlabel = tds[1].tt.string.upper().encode("ascii")
|
|
|
|
|
|
|
|
else:
|
|
|
|
assert False, tr
|
2010-02-16 00:59:49 +03:00
|
|
|
|
2010-02-15 04:12:13 +03:00
|
|
|
errors[class_][errcode] = errlabel
|
|
|
|
|
|
|
|
return classes, errors
|
|
|
|
|
|
|
|
errors_url="http://www.postgresql.org/docs/%s/static/errcodes-appendix.html"
|
|
|
|
|
|
|
|
def fetch_errors(versions):
|
|
|
|
classes = {}
|
|
|
|
errors = defaultdict(dict)
|
|
|
|
|
|
|
|
for version in versions:
|
|
|
|
c1, e1 = parse_errors(errors_url % version)
|
|
|
|
classes.update(c1)
|
|
|
|
for c, cerrs in e1.iteritems():
|
|
|
|
errors[c].update(cerrs)
|
|
|
|
|
|
|
|
return classes, errors
|
|
|
|
|
|
|
|
def generate_module_data(classes, errors):
|
|
|
|
yield ""
|
|
|
|
yield "# Error classes"
|
|
|
|
for clscode, clslabel in sorted(classes.items()):
|
|
|
|
err = clslabel.split(" - ")[1].split("(")[0] \
|
|
|
|
.strip().replace(" ", "_").replace('/', "_").upper()
|
|
|
|
yield "CLASS_%s = %r" % (err, clscode)
|
|
|
|
|
|
|
|
for clscode, clslabel in sorted(classes.items()):
|
|
|
|
yield ""
|
|
|
|
yield "# %s" % clslabel
|
|
|
|
|
|
|
|
for errcode, errlabel in sorted(errors[clscode].items()):
|
|
|
|
yield "%s = %r" % (errlabel, errcode)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main())
|
|
|
|
|
|
|
|
|