updated pg8000 imports

This commit is contained in:
ilvalle
2015-02-01 17:56:30 +01:00
parent 0f9fe09a15
commit 7bbeb669b9
4 changed files with 108 additions and 85 deletions
+15 -6
View File
@@ -38,7 +38,7 @@ for fmt in (
import datetime import datetime
import time import time
from pg8000.six import binary_type, integer_types, PY2 from .six import binary_type, integer_types, PY2
min_int2, max_int2 = -2 ** 15, 2 ** 15 min_int2, max_int2 = -2 ** 15, 2 ** 15
min_int4, max_int4 = -2 ** 31, 2 ** 31 min_int4, max_int4 = -2 ** 31, 2 ** 31
@@ -274,7 +274,7 @@ class Interval(object):
def __neq__(self, other): def __neq__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
import pg8000.core from .core import Connection
def connect( def connect(
@@ -291,6 +291,10 @@ def connect(
provided, pg8000 looks first for the PGUSER then the USER environment provided, pg8000 looks first for the PGUSER then the USER environment
variables. variables.
If your server character encoding is not ``ascii`` or ``utf8``, then
you need to provide ``user`` as bytes, eg.
``"my_name".encode('EUC-JP')``.
:keyword host: :keyword host:
The hostname of the PostgreSQL server to connect with. Providing this The hostname of the PostgreSQL server to connect with. Providing this
parameter is necessary for TCP/IP connections. One of either ``host`` parameter is necessary for TCP/IP connections. One of either ``host``
@@ -311,6 +315,10 @@ def connect(
optional; if omitted, the PostgreSQL server will assume the database optional; if omitted, the PostgreSQL server will assume the database
name is the same as the username. name is the same as the username.
If your server character encoding is not ``ascii`` or ``utf8``, then
you need to provide ``database`` as bytes, eg.
``"my_db".encode('EUC-JP')``.
:keyword password: :keyword password:
The user password to connect to the server with. This parameter is The user password to connect to the server with. This parameter is
optional; if omitted and the database server requests password-based optional; if omitted and the database server requests password-based
@@ -324,7 +332,7 @@ def connect(
:rtype: :rtype:
A :class:`Connection` object. A :class:`Connection` object.
""" """
return pg8000.core.Connection( return Connection(
user, host, unix_sock, port, database, password, ssl) user, host, unix_sock, port, database, password, ssl)
apilevel = "2.0" apilevel = "2.0"
@@ -472,7 +480,7 @@ def Binary(value):
return value return value
from pg8000.core import utc, Connection, Cursor from .core import utc, Cursor
__all__ = [ __all__ = [
Warning, Bytea, DataError, DatabaseError, connect, InterfaceError, Warning, Bytea, DataError, DatabaseError, connect, InterfaceError,
@@ -481,10 +489,11 @@ __all__ = [
ArrayDimensionsNotConsistentError, ArrayContentNotSupportedError, utc, ArrayDimensionsNotConsistentError, ArrayContentNotSupportedError, utc,
Connection, Cursor] Connection, Cursor]
from ._version import get_versions
__version__ = get_versions()['version']
"""Version string for pg8000. """Version string for pg8000.
.. versionadded:: 1.9.11 .. versionadded:: 1.9.11
""" """
from ._version import get_versions
__version__ = get_versions()['version']
del get_versions del get_versions
+45 -43
View File
@@ -1,20 +1,26 @@
# This file helps to compute a version number in source trees obtained from # This file helps to compute a version number in source trees obtained from
# git-archive tarball (such as those provided by githubs download-from-tag # git-archive tarball (such as those provided by githubs download-from-tag
# feature). Distribution tarballs (build by setup.py sdist) and build # feature). Distribution tarballs (built by setup.py sdist) and build
# directories (produced by setup.py build) will contain a much shorter file # directories (produced by setup.py build) will contain a much shorter file
# that just contains the computed version number. # that just contains the computed version number.
# This file is released into the public domain. Generated by # This file is released into the public domain. Generated by
# versioneer-0.10 (https://github.com/warner/python-versioneer) # versioneer-0.12 (https://github.com/warner/python-versioneer)
# these strings will be replaced by git during git-archive # these strings will be replaced by git during git-archive
git_refnames = "$Format:%d$" git_refnames = "$Format:%d$"
git_full = "$Format:%H$" git_full = "$Format:%H$"
# these strings are filled in when 'setup.py versioneer' creates _version.py
tag_prefix = ""
parentdir_prefix = "pg8000-"
versionfile_source = "pg8000/_version.py"
import subprocess import os
import sys import sys
import re
import subprocess
import errno import errno
@@ -50,38 +56,49 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
return stdout return stdout
import re def versions_from_parentdir(parentdir_prefix, root, verbose=False):
import os.path # Source tarballs conventionally unpack into a directory that includes
# both the project name and a version string.
dirname = os.path.basename(root)
if not dirname.startswith(parentdir_prefix):
if verbose:
print(
"guessing rootdir is '%s', but '%s' doesn't start with "
"prefix '%s'" % (root, dirname, parentdir_prefix))
return None
return {"version": dirname[len(parentdir_prefix):], "full": ""}
def get_expanded_variables(versionfile_abs): def git_get_keywords(versionfile_abs):
# the code embedded in _version.py can just fetch the value of these # the code embedded in _version.py can just fetch the value of these
# variables. When used from setup.py, we don't want to import # keywords. When used from setup.py, we don't want to import _version.py,
# _version.py, so we do it with a regexp instead. This function is not # so we do it with a regexp instead. This function is not used from
# used from _version.py. # _version.py.
variables = {} keywords = {}
try: try:
f = open(versionfile_abs, "r") f = open(versionfile_abs, "r")
for line in f.readlines(): for line in f.readlines():
if line.strip().startswith("git_refnames ="): if line.strip().startswith("git_refnames ="):
mo = re.search(r'=\s*"(.*)"', line) mo = re.search(r'=\s*"(.*)"', line)
if mo: if mo:
variables["refnames"] = mo.group(1) keywords["refnames"] = mo.group(1)
if line.strip().startswith("git_full ="): if line.strip().startswith("git_full ="):
mo = re.search(r'=\s*"(.*)"', line) mo = re.search(r'=\s*"(.*)"', line)
if mo: if mo:
variables["full"] = mo.group(1) keywords["full"] = mo.group(1)
f.close() f.close()
except EnvironmentError: except EnvironmentError:
pass pass
return variables return keywords
def versions_from_expanded_variables(variables, tag_prefix, verbose=False): def git_versions_from_keywords(keywords, tag_prefix, verbose=False):
refnames = variables["refnames"].strip() if not keywords:
return {} # keyword-finding function failed to find keywords
refnames = keywords["refnames"].strip()
if refnames.startswith("$Format"): if refnames.startswith("$Format"):
if verbose: if verbose:
print("variables are unexpanded, not using") print("keywords are unexpanded, not using")
return {} # unexpanded, so not in an unpacked git-archive tarball return {} # unexpanded, so not in an unpacked git-archive tarball
refs = set([r.strip() for r in refnames.strip("()").split(",")]) refs = set([r.strip() for r in refnames.strip("()").split(",")])
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
@@ -107,18 +124,20 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False):
r = ref[len(tag_prefix):] r = ref[len(tag_prefix):]
if verbose: if verbose:
print("picking %s" % r) print("picking %s" % r)
return {"version": r, "full": variables["full"].strip()} return {
"version": r,
"full": keywords["full"].strip()}
# no suitable tags, so we use the full revision id # no suitable tags, so we use the full revision id
if verbose: if verbose:
print("no suitable tags, using full revision id") print("no suitable tags, using full revision id")
return { return {
"version": variables["full"].strip(), "version": keywords["full"].strip(),
"full": variables["full"].strip()} "full": keywords["full"].strip()}
def versions_from_vcs(tag_prefix, root, verbose=False): def git_versions_from_vcs(tag_prefix, root, verbose=False):
# this runs 'git' from the root of the source tree. This only gets called # this runs 'git' from the root of the source tree. This only gets called
# if the git-archive 'subst' variables were *not* expanded, and # if the git-archive 'subst' keywords were *not* expanded, and
# _version.py hasn't already been rewritten with a short version string, # _version.py hasn't already been rewritten with a short version string,
# meaning we're inside a checked out source tree. # meaning we're inside a checked out source tree.
@@ -150,31 +169,14 @@ def versions_from_vcs(tag_prefix, root, verbose=False):
return {"version": tag, "full": full} return {"version": tag, "full": full}
def versions_from_parentdir(parentdir_prefix, root, verbose=False):
# Source tarballs conventionally unpack into a directory that includes
# both the project name and a version string.
dirname = os.path.basename(root)
if not dirname.startswith(parentdir_prefix):
if verbose:
print(
"guessing rootdir is '%s', but '%s' doesn't start with prefix "
"'%s'" % (root, dirname, parentdir_prefix))
return None
return {"version": dirname[len(parentdir_prefix):], "full": ""}
tag_prefix = ""
parentdir_prefix = "pg8000-"
versionfile_source = "pg8000/_version.py"
def get_versions(default={"version": "unknown", "full": ""}, verbose=False): def get_versions(default={"version": "unknown", "full": ""}, verbose=False):
# I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
# __file__, we can work backwards from there to the root. Some # __file__, we can work backwards from there to the root. Some
# py2exe/bbfreeze/non-CPython implementations don't do __file__, in which # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
# case we can only use expanded variables. # case we can only use expanded keywords.
variables = {"refnames": git_refnames, "full": git_full} keywords = {"refnames": git_refnames, "full": git_full}
ver = versions_from_expanded_variables(variables, tag_prefix, verbose) ver = git_versions_from_keywords(keywords, tag_prefix, verbose)
if ver: if ver:
return ver return ver
@@ -183,11 +185,11 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False):
# versionfile_source is the relative path from the top of the source # versionfile_source is the relative path from the top of the source
# tree (where the .git directory might live) to this file. Invert # tree (where the .git directory might live) to this file. Invert
# this to find the root from __file__. # this to find the root from __file__.
for i in range(len(versionfile_source.split("/"))): for i in range(len(versionfile_source.split(os.sep))):
root = os.path.dirname(root) root = os.path.dirname(root)
except NameError: except NameError:
return default return default
return (versions_from_vcs(tag_prefix, root, verbose) return (git_versions_from_vcs(tag_prefix, root, verbose)
or versions_from_parentdir(parentdir_prefix, root, verbose) or versions_from_parentdir(parentdir_prefix, root, verbose)
or default) or default)
+45 -32
View File
@@ -29,7 +29,7 @@ __author__ = "Mathieu Fenniak"
import datetime import datetime
from datetime import timedelta from datetime import timedelta
from pg8000 import ( from . import (
Interval, min_int2, max_int2, min_int4, max_int4, min_int8, max_int8, Interval, min_int2, max_int2, min_int4, max_int4, min_int8, max_int8,
Bytea, NotSupportedError, ProgrammingError, InternalError, IntegrityError, Bytea, NotSupportedError, ProgrammingError, InternalError, IntegrityError,
OperationalError, DatabaseError, InterfaceError, Error, OperationalError, DatabaseError, InterfaceError, Error,
@@ -45,11 +45,10 @@ import threading
from struct import pack from struct import pack
from hashlib import md5 from hashlib import md5
from decimal import Decimal from decimal import Decimal
import pg8000
from collections import deque, defaultdict from collections import deque, defaultdict
from itertools import count, islice from itertools import count, islice
from pg8000.six.moves import map from .six.moves import map
from pg8000.six import b, PY2, integer_types, next, PRE_26, text_type, u from .six import b, PY2, integer_types, next, PRE_26, text_type, u
from sys import exc_info from sys import exc_info
from uuid import UUID from uuid import UUID
from copy import deepcopy from copy import deepcopy
@@ -57,6 +56,12 @@ from calendar import timegm
import os import os
from distutils.version import LooseVersion from distutils.version import LooseVersion
try:
from json import loads
except ImportError:
pass # Can only use JSON with Python 2.6 and above
ZERO = timedelta(0) ZERO = timedelta(0)
@@ -891,7 +896,7 @@ class Connection(object):
return error return error
def __init__(self, user, host, unix_sock, port, database, password, ssl): def __init__(self, user, host, unix_sock, port, database, password, ssl):
self._client_encoding = "ascii" self._client_encoding = "utf8"
self._commands_with_count = ( self._commands_with_count = (
b("INSERT"), b("DELETE"), b("UPDATE"), b("MOVE"), b("INSERT"), b("DELETE"), b("UPDATE"), b("MOVE"),
b("FETCH"), b("COPY"), b("SELECT")) b("FETCH"), b("COPY"), b("SELECT"))
@@ -911,6 +916,9 @@ class Connection(object):
else: else:
self.user = user self.user = user
if isinstance(self.user, text_type):
self.user = self.user.encode('utf8')
self.password = password self.password = password
self.autocommit = False self.autocommit = False
self._xid = None self._xid = None
@@ -1085,6 +1093,10 @@ class Connection(object):
def bool_recv(d, o, l): def bool_recv(d, o, l):
return d[o] == "\x01" return d[o] == "\x01"
def json_in(data, offset, length):
return loads(unicode( # noqa
data[offset: offset + length], self._client_encoding))
else: else:
def text_recv(data, offset, length): def text_recv(data, offset, length):
return str( return str(
@@ -1093,6 +1105,10 @@ class Connection(object):
def bool_recv(data, offset, length): def bool_recv(data, offset, length):
return data[offset] == 1 return data[offset] == 1
def json_in(data, offset, length):
return loads(
str(data[offset: offset + length], self._client_encoding))
def time_in(data, offset, length): def time_in(data, offset, length):
hour = int(data[offset:offset + 2]) hour = int(data[offset:offset + 2])
minute = int(data[offset + 3:offset + 5]) minute = int(data[offset + 3:offset + 5])
@@ -1131,6 +1147,7 @@ class Connection(object):
25: (FC_BINARY, text_recv), # TEXT type 25: (FC_BINARY, text_recv), # TEXT type
26: (FC_TEXT, int_in), # oid 26: (FC_TEXT, int_in), # oid
28: (FC_TEXT, int_in), # xid 28: (FC_TEXT, int_in), # xid
114: (FC_TEXT, json_in), # json
700: (FC_BINARY, float4_recv), # float4 700: (FC_BINARY, float4_recv), # float4
701: (FC_BINARY, float8_recv), # float8 701: (FC_BINARY, float8_recv), # float8
705: (FC_BINARY, text_recv), # unknown 705: (FC_BINARY, text_recv), # unknown
@@ -1157,6 +1174,7 @@ class Connection(object):
1700: (FC_TEXT, numeric_in), # NUMERIC 1700: (FC_TEXT, numeric_in), # NUMERIC
2275: (FC_BINARY, text_recv), # cstring 2275: (FC_BINARY, text_recv), # cstring
2950: (FC_BINARY, uuid_recv), # uuid 2950: (FC_BINARY, uuid_recv), # uuid
3802: (FC_TEXT, json_in), # jsonb
}) })
self.py_types = { self.py_types = {
@@ -1183,7 +1201,7 @@ class Connection(object):
} }
if PY2: if PY2:
self.py_types[pg8000.Bytea] = (17, FC_BINARY, bytea_send) # bytea self.py_types[Bytea] = (17, FC_BINARY, bytea_send) # bytea
self.py_types[text_type] = (705, FC_TEXT, text_out) # unknown self.py_types[text_type] = (705, FC_TEXT, text_out) # unknown
self.py_types[long] = (705, FC_TEXT, unknown_out) # noqa self.py_types[long] = (705, FC_TEXT, unknown_out) # noqa
@@ -1242,11 +1260,12 @@ class Connection(object):
# String - A parameter name (user, database, or options) # String - A parameter name (user, database, or options)
# String - Parameter value # String - Parameter value
protocol = 196608 protocol = 196608
val = bytearray(i_pack(protocol) + b("user\x00")) val = bytearray(
val.extend(user.encode("ascii") + NULL_BYTE) i_pack(protocol) + b("user\x00") + self.user + NULL_BYTE)
if database is not None: if database is not None:
val.extend( if isinstance(database, text_type):
b("database\x00") + database.encode("ascii") + NULL_BYTE) database = database.encode('utf8')
val.extend(b("database\x00") + database + NULL_BYTE)
val.append(0) val.append(0)
self._write(i_pack(len(val) + 4)) self._write(i_pack(len(val) + 4))
self._write(val) self._write(val)
@@ -1421,9 +1440,9 @@ class Connection(object):
self._usock.close() self._usock.close()
self._sock = None self._sock = None
except AttributeError: except AttributeError:
raise pg8000.InterfaceError("connection is closed") raise InterfaceError("connection is closed")
except ValueError: except ValueError:
raise pg8000.InterfaceError("connection is closed") raise InterfaceError("connection is closed")
def close(self): def close(self):
"""Closes the database connection. """Closes the database connection.
@@ -1478,10 +1497,8 @@ class Connection(object):
"server requesting MD5 password authentication, but no " "server requesting MD5 password authentication, but no "
"password was provided") "password was provided")
pwd = b("md5") + md5( pwd = b("md5") + md5(
md5( md5(self.password.encode("ascii") + self.user).
self.password.encode("ascii") + hexdigest().encode("ascii") + salt).hexdigest().encode("ascii")
self.user.encode("ascii")).hexdigest().encode("ascii") +
salt).hexdigest().encode("ascii")
# Byte1('p') - Identifies the message as a password message. # Byte1('p') - Identifies the message as a password message.
# Int32 - Message length including self. # Int32 - Message length including self.
# String - The password. Password may be encrypted. # String - The password. Password may be encrypted.
@@ -1529,26 +1546,22 @@ class Connection(object):
count = h_unpack(data)[0] count = h_unpack(data)[0]
idx = 2 idx = 2
for i in range(count): for i in range(count):
field = {'name': data[idx:data.find(NULL_BYTE, idx)]} name = data[idx:data.find(NULL_BYTE, idx)]
idx += len(field['name']) + 1 idx += len(name) + 1
field.update( field = dict(
dict(zip(( zip((
"table_oid", "column_attrnum", "type_oid", "table_oid", "column_attrnum", "type_oid", "type_size",
"type_size", "type_modifier", "format"), "type_modifier", "format"), ihihih_unpack(data, idx)))
ihihih_unpack(data, idx)))) field['name'] = name
idx += 18 idx += 18
cursor.ps['row_desc'].append(field) cursor.ps['row_desc'].append(field)
try: field['pg8000_fc'], field['func'] = \
field['pg8000_fc'], field['func'] = self.pg_types[ self.pg_types[field['type_oid']]
field['type_oid']]
except KeyError:
raise NotSupportedError(
"type oid " + exc_info()[1] + " not supported")
def execute(self, cursor, operation, vals): def execute(self, cursor, operation, vals):
if vals is None: if vals is None:
vals = () vals = ()
paramstyle = pg8000.paramstyle from . import paramstyle
cache = self._caches[paramstyle] cache = self._caches[paramstyle]
try: try:
@@ -1700,11 +1713,11 @@ class Connection(object):
self._write(FLUSH_MSG) self._write(FLUSH_MSG)
except ValueError: except ValueError:
if str(exc_info()[1]) == "write to closed file": if str(exc_info()[1]) == "write to closed file":
raise pg8000.InterfaceError("connection is closed") raise InterfaceError("connection is closed")
else: else:
raise exc_info()[1] raise exc_info()[1]
except AttributeError: except AttributeError:
raise pg8000.InterfaceError("connection is closed") raise InterfaceError("connection is closed")
def send_EXECUTE(self, cursor): def send_EXECUTE(self, cursor):
# Byte1('E') - Identifies the message as an execute message. # Byte1('E') - Identifies the message as an execute message.
+3 -4
View File
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from pydal import DAL as pyDAL from pydal import DAL as pyDAL
from pydal import Field, SQLCustomType, geoPoint, geoLine, geoPolygon from pydal import Field, SQLCustomType, geoPoint, geoLine, geoPolygon
@@ -128,9 +127,9 @@ from pydal.drivers import DRIVERS
if not DRIVERS.get('pymysql'): if not DRIVERS.get('pymysql'):
from .contrib import pymysql from .contrib import pymysql
DRIVERS['pymysql'] = pymysql DRIVERS['pymysql'] = pymysql
#if not DRIVERS.get('pg8000'): if not DRIVERS.get('pg8000'):
# from .contrib import pg8000 from .contrib import pg8000
# DRIVERS['pg8000'] = pg8000 DRIVERS['pg8000'] = pg8000
if not DRIVERS.get('pyodbc'): if not DRIVERS.get('pyodbc'):
from .contrib import pypyodbc as pyodbc from .contrib import pypyodbc as pyodbc
DRIVERS['pyodbc'] = pyodbc DRIVERS['pyodbc'] = pyodbc