Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rqlite/pyrqlite
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: paulstuart/pydqlite
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 5 commits
  • 17 files changed
  • 1 contributor

Commits on Nov 28, 2019

  1. basic init

    Paul Stuart committed Nov 28, 2019
    Copy the full SHA
    2068360 View commit details
  2. rename v1

    Paul Stuart committed Nov 28, 2019
    Copy the full SHA
    18c8612 View commit details
  3. rename v2

    Paul Stuart committed Nov 28, 2019
    Copy the full SHA
    410dc48 View commit details

Commits on Dec 3, 2019

  1. make tests work (some are excluded for now)

    Paul Stuart committed Dec 3, 2019
    Copy the full SHA
    4761f93 View commit details
  2. remove debugging cruft

    Paul Stuart committed Dec 3, 2019
    Copy the full SHA
    da82be1 View commit details
22 changes: 11 additions & 11 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=======
pyrqlite
pydqlite
=======
This package contains a pure-Python rqlite client library.
This package contains a pure-Python dqlite client library.

.. contents::

@@ -12,7 +12,7 @@ Requirements

- CPython_ >= 2.7 or >= 3.3

* rqlite Server
* dqlite Server


Installation
@@ -21,17 +21,17 @@ Installation
The last stable release is available on github and can be installed with ``pip``::

$ cd
$ git clone https://github.com/rqlite/pyrqlite.git
$ pip install ./pyrqlite
$ git clone https://github.com/paulstuart/pydqlite.git
$ pip install ./pydqlite

Alternatively (e.g. if ``pip`` is not available), a tarball can be downloaded
from GitHub and installed with Setuptools::

$ # X.X is the desired pyrqlite version (e.g. 0.5 or 0.6).
$ curl -L https://github.com/rqlite/tarball/pyrqlite-X.X | tar xz
$ cd pyrqlite*
$ # X.X is the desired pydqlite version (e.g. 0.5 or 0.6).
$ curl -L https://github.com/paulstuart/tarball/pydqlite-X.X | tar xz
$ cd pydqlite*
$ python setup.py install
$ # The folder pyrqlite* can be safely removed now.
$ # The folder pydqlite* can be safely removed now.

Test Suite
----------
@@ -50,7 +50,7 @@ The following code creates a connection and executes some statements:

.. code:: python
import pyrqlite.dbapi2 as dbapi2
import pydqlite.dbapi2 as dbapi2
# Connect to the database
connection = dbapi2.connect(
@@ -101,4 +101,4 @@ DB-API 2.0: http://www.python.org/dev/peps/pep-0249

License
-------
pyrqlite is released under the MIT License. See LICENSE for more information.
pydqlite is released under the MIT License. See LICENSE for more information.
9 changes: 9 additions & 0 deletions init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

pyenv install 3.6.8
pyenv virtualenv 3.6.8 mypyrqlite
pyenv activate mypyrqlite
pip install --upgrade pip
pip install pytest

python setup.py install
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
)

sys.path.insert(0, 'src')
from pyrqlite.constants import (
from pydqlite.constants import (
__author__,
__email__,
__license__,
@@ -71,14 +71,14 @@ def run(self):


setup(
name="pyrqlite",
name="pydqlite",
version=__version__,
url='https://github.com/rqlite/pyrqlite/',
url='https://github.com/paulstuart/pydqlite/',
author=__author__,
author_email=__email__,
maintainer=__author__,
maintainer_email=__email__,
description='python DB API 2.0 driver for rqlite',
description='python DB API 2.0 driver for dqlite',
license=__license__,
package_dir={'': 'src'},
packages=find_packages('src', exclude=['test']),
File renamed without changes.
4 changes: 2 additions & 2 deletions src/pyrqlite/_ephemeral.py → src/pydqlite/_ephemeral.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
import time


class EphemeralRqlited(object):
class EphemeralDqlited(object):
def __init__(self):
self.host = None
self.http = None
@@ -55,7 +55,7 @@ def _start(self):
self.http = (self.host, http_port)
self.raft = (self.host, raft_port)
with open(os.devnull, mode='wb', buffering=0) as devnull:
filename = 'rqlited'
filename = 'dqlited'
try:
self._proc = subprocess.Popen([filename,
'-http-addr', '{}:{}'.format(*self.http),
6 changes: 2 additions & 4 deletions src/pyrqlite/connections.py → src/pydqlite/connections.py
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
)

from .cursors import Cursor
from ._ephemeral import EphemeralRqlited as _EphemeralRqlited
from ._ephemeral import EphemeralDqlited as _EphemeralDqlited
from .extensions import PARSE_DECLTYPES, PARSE_COLNAMES


@@ -43,7 +43,6 @@ class Connection(object):
def __init__(self, host='localhost', port=4001,
user=None, password=None, connect_timeout=None,
detect_types=0, max_redirects=UNLIMITED_REDIRECTS):

self.messages = []
self.host = host
self.port = port
@@ -52,15 +51,14 @@ def __init__(self, host='localhost', port=4001,
self._headers['Authorization'] = 'Basic ' + \
codecs.encode('{}:{}'.format(user, password).encode('utf-8'),
'base64').decode('utf-8').rstrip('\n')

self.connect_timeout = connect_timeout
self.max_redirects = max_redirects
self.detect_types = detect_types
self.parse_decltypes = detect_types & PARSE_DECLTYPES
self.parse_colnames = detect_types & PARSE_COLNAMES
self._ephemeral = None
if host == ':memory:':
self._ephemeral = _EphemeralRqlited().__enter__()
self._ephemeral = _EphemeralDqlited().__enter__()
self.host, self.port = self._ephemeral.http
self._connection = self._init_connection()

14 changes: 14 additions & 0 deletions src/pydqlite/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

from __future__ import unicode_literals

__version__ = "HEAD"
__project__ = "pydqlite"

__author__ = "originally Zac Medico, adapted by Paul Stuart"
__email__ = "zmedico@gmail.com or pauleyphonic@gmail.com"

__copyright__ = "Copyright (C) 2016 Zac Medico"
__license__ = "MIT"
__description__ = "Python dbapi2 driver for dqlite"

UNLIMITED_REDIRECTS = -1
19 changes: 16 additions & 3 deletions src/pyrqlite/cursors.py → src/pydqlite/cursors.py
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ def _urlencode(query, doseq=0):
class Cursor(object):
arraysize = 1

def __init__(self, connection):
def __init__(self, connection, debug=False):
self._connection = connection
self.messages = []
self.lastrowid = None
@@ -45,6 +45,7 @@ def __init__(self, connection):
self.arraysize = 1
self._rows = None
self._column_type_cache = {}
self.debug = debug

def __enter__(self):
return self
@@ -76,8 +77,11 @@ def _request(self, method, uri, body=None, headers={}):
response.reason)
response_text = response.read().decode('utf-8')
logger.debug("raw response: %s", response_text)
response_json = json.loads(
response_text, object_pairs_hook=OrderedDict)
try:
response_json = json.loads(
response_text, object_pairs_hook=OrderedDict)
except Exception as e:
raise(e)
if debug:
logger.debug(
"formatted response: %s",
@@ -86,6 +90,7 @@ def _request(self, method, uri, body=None, headers={}):
indent=4))
return response_json

#TODO: remove this -- pass args directly to DB and let it do param subst
def _substitute_params(self, operation, parameters):
'''
SQLite natively supports only the types TEXT, INTEGER, REAL, BLOB and
@@ -167,9 +172,17 @@ def execute(self, operation, parameters=None):
rows_affected = -1
payload_rows = {}
try:
if self.debug:
print(f"PAYLOAD: {payload}")
results = payload["results"]
"""
except KeyError:
print("ERP KEY ERROR")
pass
"""
except TypeError as e:
print(f"payload is {payload}")
raise(e)
else:
rows_affected = 0
for item in results:
File renamed without changes.
File renamed without changes.
31 changes: 27 additions & 4 deletions src/pyrqlite/extensions.py → src/pydqlite/extensions.py
Original file line number Diff line number Diff line change
@@ -2,10 +2,9 @@

"""
SQLite natively supports only the types TEXT, INTEGER, REAL, BLOB and NULL.
And RQLite always answers 'bytes' values.
Converters transforms RQLite answers to Python native types.
Adapters transforms Python native types to RQLite-aware values.
Converters transforms DQLite answers to Python native types.
Adapters transforms Python native types to DQLite-aware values.
"""

import codecs
@@ -67,14 +66,35 @@ def _convert_date(val):

def _convert_timestamp(val):
datepart, timepart = val.split("T")
ymd = datepart.split("-")
year, month, day = map(int, datepart.split("-"))
timepart_full = timepart.strip('Z').split(".")
hours, minutes, seconds = map(int, timepart_full[0].split(":"))
if len(timepart_full) == 2:
microseconds = int('{:0<6.6}'.format(timepart_full[1]))
else:
microseconds = 0
val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds)
return val

def XX_convert_timestamp(val):
if isinstance(val, str):
#val = val.encode('utf-8')
val = bytes(val, 'utf-8')
print(f"VAL BECOMES: {val} is a: {type(val)}")
v2 = bytes(val)
print(f"V2 BECOMES: {v2} is a: {type(v2)}")
datepart, timepart = v2.decode().split("T")
ymd = datepart.split("-")
print(f"YMD: {ymd}")
year, month, day = map(int, datepart.split("-"))
timepart_full = timepart.strip('Z').split(".")
print(f"TIMEPART: {timepart_full}")
hours, minutes, seconds = map(int, timepart_full[0].split(":"))
if len(timepart_full) == 2:
microseconds = int('{:0<6.6}'.format(timepart_full[1]))
else:
microseconds = 0
val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds)
return val

@@ -109,11 +129,14 @@ def _null_wrapper(converter, value):
'BLOB': lambda x: x,
'DATE': functools.partial(_null_wrapper, _convert_date),
'DATETIME': lambda x: x.replace('T', ' ').rstrip('Z'),
'TIME': functools.partial(_null_wrapper, _convert_timestamp),
'TIMESTAMP': functools.partial(_null_wrapper, _convert_timestamp),
}

# Non-native converters will be decoded from base64 before fed into converter
_native_converters = ('BOOL', 'FLOAT', 'INTEGER', 'REAL', 'NUMBER', 'NULL', 'DATE', 'DATETIME', 'TIMESTAMP')
# TODO: 'TIME' is only one passed (datetime/timestamp nopes)
#_native_converters = ('BOOL', 'FLOAT', 'INTEGER', 'REAL', 'NUMBER', 'NULL', 'DATE', 'DATETIME', 'TIMESTAMP', 'TIME')
_native_converters = ('BOOL', 'FLOAT', 'INTEGER', 'REAL', 'NUMBER', 'NULL', 'DATE', 'TIME')

# SQLite TEXT affinity: https://www.sqlite.org/datatype3.html
_text_affinity_re = re.compile(r'CHAR|CLOB|TEXT')
File renamed without changes.
File renamed without changes.
14 changes: 0 additions & 14 deletions src/pyrqlite/constants.py

This file was deleted.

22 changes: 13 additions & 9 deletions src/test/test_dbapi.py
Original file line number Diff line number Diff line change
@@ -30,7 +30,10 @@
from test import test_support
import unittest

import pyrqlite.dbapi2 as sqlite
import pydqlite.dbapi2 as sqlite

localdb = ":memory:"
localdb = "localhost"

if sys.version_info[0] >= 3:
StandardError = Exception
@@ -94,7 +97,7 @@ def test_CheckNotSupportedError(self):
class ConnectionTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.cx = sqlite.connect(":memory:")
cls.cx = sqlite.connect(localdb)

def setUp(self):
cu = self.cx.cursor()
@@ -141,7 +144,7 @@ def test_CheckFailedOpen(self):
return
self.fail("should have raised an OperationalError")

def test_CheckClose(self):
def test_CheckCloseX0(self):
# This would interfere with other tests, and
# tearDownClass exercises it already.
#self.cx.close()
@@ -163,7 +166,7 @@ def test_CheckExceptions(self):
class CursorTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.cx = sqlite.connect(":memory:")
cls.cx = sqlite.connect(localdb)

def setUp(self):
self.cu = self.cx.cursor()
@@ -337,7 +340,7 @@ def test_CheckExecuteDictMappingUnnamed(self):
except sqlite.ProgrammingError:
pass

def test_CheckClose(self):
def test_CheckCloseX2(self):
self.cu.close()
self.cu = self.cx.cursor()

@@ -470,7 +473,6 @@ def test_CheckArraySize(self):
self.cu.execute("insert into test(name) values ('C')")
self.cu.execute("select name from test")
res = self.cu.fetchmany()

self.assertEqual(len(res), 2)

def test_CheckFetchmany(self):
@@ -518,6 +520,7 @@ def f(): pass
return
self.fail("should have raised a ValueError")

"""
@unittest.skip('not implemented')
def test_CheckCursorWrongClass(self):
class Foo: pass
@@ -527,6 +530,7 @@ class Foo: pass
self.fail("should have raised a ValueError")
except TypeError:
pass
"""


class ConstructorTests(unittest.TestCase):
@@ -557,7 +561,7 @@ def test_CheckBinary(self):
class ExtensionTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.con = sqlite.connect(":memory:")
cls.con = sqlite.connect(localdb)

def tearDown(self):
for row in self.con.execute(
@@ -640,7 +644,7 @@ def test_CheckConnectionExecutescript(self):
class ClosedConTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.con = sqlite.connect(":memory:")
cls.con = sqlite.connect(localdb)
cls.cur = cls.con.cursor()
cls.con.close()

@@ -754,7 +758,7 @@ def test_CheckClosedCall(self):
class ClosedCurTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.con = sqlite.connect(":memory:")
cls.con = sqlite.connect(localdb)
cls.cur = cls.con.cursor()
cls.cur.close()

Loading