Permalink
Browse files

merge!

  • Loading branch information...
2 parents 74e4ee0 + c6db572 commit 0096a3990f0fc22a49f365f7f4962df4060723c3 root@localhost committed Mar 14, 2009
@@ -0,0 +1,30 @@
+
+"""Tools for Python database scripts."""
+
+import skytools.quoting
+import skytools.config
+import skytools.psycopgwrapper
+import skytools.sqltools
+import skytools.gzlog
+import skytools.scripting
+import skytools.parsing
+import skytools.dbstruct
+
+from skytools.psycopgwrapper import *
+from skytools.config import *
+from skytools.dbstruct import *
+from skytools.gzlog import *
+from skytools.scripting import *
+from skytools.sqltools import *
+from skytools.quoting import *
+from skytools.parsing import *
+
+__all__ = (skytools.psycopgwrapper.__all__
+ + skytools.config.__all__
+ + skytools.dbstruct.__all__
+ + skytools.gzlog.__all__
+ + skytools.scripting.__all__
+ + skytools.sqltools.__all__
+ + skytools.quoting.__all__
+ + skytools.parsing.__all__)
+
Binary file not shown.
@@ -0,0 +1,194 @@
+# _pyquoting.py
+
+"""Various helpers for string quoting/unquoting.
+
+Here is pure Python that should match C code in _cquoting.
+"""
+
+import urllib, re
+
+__all__ = [
+ "quote_literal", "quote_copy", "quote_bytea_raw",
+ "db_urlencode", "db_urldecode", "unescape",
+ "unquote_literal",
+]
+
+#
+# SQL quoting
+#
+
+def quote_literal(s):
+ """Quote a literal value for SQL.
+
+ If string contains '\\', extended E'' quoting is used,
+ otherwise standard quoting. Input value of None results
+ in string "null" without quotes.
+
+ Python implementation.
+ """
+
+ if s == None:
+ return "null"
+ s = str(s).replace("'", "''")
+ s2 = s.replace("\\", "\\\\")
+ if len(s) != len(s2):
+ return "E'" + s2 + "'"
+ return "'" + s2 + "'"
+
+def quote_copy(s):
+ """Quoting for copy command. None is converted to \\N.
+
+ Python implementation.
+ """
+
+ if s == None:
+ return "\\N"
+ s = str(s)
+ s = s.replace("\\", "\\\\")
+ s = s.replace("\t", "\\t")
+ s = s.replace("\n", "\\n")
+ s = s.replace("\r", "\\r")
+ return s
+
+_bytea_map = None
+def quote_bytea_raw(s):
+ """Quoting for bytea parser. Returns None as None.
+
+ Python implementation.
+ """
+ global _bytea_map
+ if s == None:
+ return None
+ if 1 and _bytea_map is None:
+ _bytea_map = {}
+ for i in xrange(256):
+ c = chr(i)
+ if i < 0x20 or i >= 0x7F:
+ _bytea_map[c] = "\\%03o" % i
+ elif c == "\\":
+ _bytea_map[c] = r"\\"
+ else:
+ _bytea_map[c] = c
+ return "".join([_bytea_map[c] for c in s])
+
+#
+# Database specific urlencode and urldecode.
+#
+
+def db_urlencode(dict):
+ """Database specific urlencode.
+
+ Encode None as key without '='. That means that in "foo&bar=",
+ foo is NULL and bar is empty string.
+
+ Python implementation.
+ """
+
+ elem_list = []
+ for k, v in dict.items():
+ if v is None:
+ elem = urllib.quote_plus(str(k))
+ else:
+ elem = urllib.quote_plus(str(k)) + '=' + urllib.quote_plus(str(v))
+ elem_list.append(elem)
+ return '&'.join(elem_list)
+
+def db_urldecode(qs):
+ """Database specific urldecode.
+
+ Decode key without '=' as None.
+ This also does not support one key several times.
+
+ Python implementation.
+ """
+
+ res = {}
+ for elem in qs.split('&'):
+ if not elem:
+ continue
+ pair = elem.split('=', 1)
+ name = urllib.unquote_plus(pair[0])
+
+ # keep only one instance around
+ name = intern(str(name))
+
+ if len(pair) == 1:
+ res[name] = None
+ else:
+ res[name] = urllib.unquote_plus(pair[1])
+ return res
+
+#
+# Remove C-like backslash escapes
+#
+
+_esc_re = r"\\([0-7]{1,3}|.)"
+_esc_rc = re.compile(_esc_re)
+_esc_map = {
+ 't': '\t',
+ 'n': '\n',
+ 'r': '\r',
+ 'a': '\a',
+ 'b': '\b',
+ "'": "'",
+ '"': '"',
+ '\\': '\\',
+}
+
+def _sub_unescape_c(m):
+ v = m.group(1)
+ if (len(v) == 1) and (v < '0' or v > '7'):
+ try:
+ return _esc_map[v]
+ except KeyError:
+ return v
+ else:
+ return chr(int(v, 8))
+
+def unescape(val):
+ """Removes C-style escapes from string.
+ Python implementation.
+ """
+ return _esc_rc.sub(_sub_unescape_c, val)
+
+_esql_re = r"''|\\([0-7]{1,3}|.)"
+_esql_rc = re.compile(_esc_re)
+def _sub_unescape_sqlext(m):
+ if m.group() == "''":
+ return "'"
+ v = m.group(1)
+ if (len(v) == 1) and (v < '0' or v > '7'):
+ try:
+ return _esc_map[v]
+ except KeyError:
+ return v
+ return chr(int(v, 8))
+
+def unquote_literal(val, stdstr = False):
+ """Unquotes SQL string.
+
+ E'..' -> extended quoting.
+ '..' -> standard or extended quoting
+ null -> None
+ other -> returned as-is
+ """
+ if val[0] == "'" and val[-1] == "'":
+ if stdstr:
+ return val[1:-1].replace("''", "'")
+ else:
+ return _esql_rc.sub(_sub_unescape_sqlext, val[1:-1])
+ elif len(val) > 2 and val[0] in ('E', 'e') and val[1] == "'" and val[-1] == "'":
+ return _esql_rc.sub(_sub_unescape_sqlext, val[2:-1])
+ elif len(val) >= 2 and val[0] == '$' and val[-1] == '$':
+ p1 = val.find('$', 1)
+ p2 = val.rfind('$', 1, -1)
+ if p1 > 0 and p2 > p1:
+ t1 = val[:p1+1]
+ t2 = val[p2:]
+ if t1 == t2:
+ return val[len(t1):-len(t1)]
+ raise Exception("Bad dollar-quoted string")
+ elif val.lower() == "null":
+ return None
+ return val
+
Binary file not shown.
@@ -0,0 +1,146 @@
+
+"""Nicer config class."""
+
+import sys, os, ConfigParser, socket
+
+__all__ = ['Config']
+
+class Config(object):
+ """Bit improved ConfigParser.
+
+ Additional features:
+ - Remembers section.
+ - Acceps defaults in get() functions.
+ - List value support.
+ """
+ def __init__(self, main_section, filename, sane_config = 1, user_defs = {}):
+ """Initialize Config and read from file.
+
+ @param sane_config: chooses between ConfigParser/SafeConfigParser.
+ """
+ defs = {
+ 'job_name': main_section,
+ 'service_name': main_section,
+ 'host_name': socket.gethostname(),
+ }
+ defs.update(user_defs)
+
+ self.main_section = main_section
+ self.filename = filename
+ self.sane_config = sane_config
+ if sane_config:
+ self.cf = ConfigParser.SafeConfigParser(defs)
+ else:
+ self.cf = ConfigParser.ConfigParser(defs)
+
+ if filename is None:
+ self.cf.add_section(main_section)
+ return
+
+ if not os.path.isfile(filename):
+ raise Exception('Config file not found: '+filename)
+ self.cf.read(filename)
+ if not self.cf.has_section(main_section):
+ raise Exception("Wrong config file, no section '%s'"%main_section)
+
+ def reload(self):
+ """Re-reads config file."""
+ if self.filename:
+ self.cf.read(self.filename)
+
+ def get(self, key, default=None):
+ """Reads string value, if not set then default."""
+ try:
+ return self.cf.get(self.main_section, key)
+ except ConfigParser.NoOptionError, det:
+ if default == None:
+ raise Exception("Config value not set: " + key)
+ return default
+
+ def getint(self, key, default=None):
+ """Reads int value, if not set then default."""
+ try:
+ return self.cf.getint(self.main_section, key)
+ except ConfigParser.NoOptionError, det:
+ if default == None:
+ raise Exception("Config value not set: " + key)
+ return default
+
+ def getboolean(self, key, default=None):
+ """Reads boolean value, if not set then default."""
+ try:
+ return self.cf.getboolean(self.main_section, key)
+ except ConfigParser.NoOptionError, det:
+ if default == None:
+ raise Exception("Config value not set: " + key)
+ return default
+
+ def getfloat(self, key, default=None):
+ """Reads float value, if not set then default."""
+ try:
+ return self.cf.getfloat(self.main_section, key)
+ except ConfigParser.NoOptionError, det:
+ if default == None:
+ raise Exception("Config value not set: " + key)
+ return default
+
+ def getlist(self, key, default=None):
+ """Reads comma-separated list from key."""
+ try:
+ s = self.cf.get(self.main_section, key).strip()
+ res = []
+ if not s:
+ return res
+ for v in s.split(","):
+ res.append(v.strip())
+ return res
+ except ConfigParser.NoOptionError, det:
+ if default == None:
+ raise Exception("Config value not set: " + key)
+ return default
+
+ def getfile(self, key, default=None):
+ """Reads filename from config.
+
+ In addition to reading string value, expands ~ to user directory.
+ """
+ fn = self.get(key, default)
+ if fn == "" or fn == "-":
+ return fn
+ # simulate that the cwd is script location
+ #path = os.path.dirname(sys.argv[0])
+ # seems bad idea, cwd should be cwd
+
+ fn = os.path.expanduser(fn)
+
+ return fn
+
+ def get_wildcard(self, key, values=[], default=None):
+ """Reads a wildcard property from conf and returns its string value, if not set then default."""
+
+ orig_key = key
+ keys = [key]
+
+ for wild in values:
+ key = key.replace('*', wild, 1)
+ keys.append(key)
+ keys.reverse()
+
+ for key in keys:
+ try:
+ return self.cf.get(self.main_section, key)
+ except ConfigParser.NoOptionError, det:
+ pass
+
+ if default == None:
+ raise Exception("Config value not set: " + orig_key)
+ return default
+
+ def sections(self):
+ """Returns list of sections in config file, excluding DEFAULT."""
+ return self.cf.sections()
+
+ def clone(self, main_section):
+ """Return new Config() instance with new main section on same config file."""
+ return Config(main_section, self.filename, self.sane_config)
+
Binary file not shown.
Oops, something went wrong.

0 comments on commit 0096a39

Please sign in to comment.