Merge branch 'master' into fix-table-cache

This commit is contained in:
Grzegorz Niewisiewicz 2014-01-27 08:56:28 +01:00
commit a4d676f325
13 changed files with 65 additions and 61 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
*.pyc *.pyc
*.egg-info *.egg-info
dist/* dist/*
build/*
.DS_Store .DS_Store
.watchr .watchr

11
.travis.yml Normal file
View File

@ -0,0 +1,11 @@
---
language: python
python:
- '3.3'
- '2.7'
- '2.6'
install:
- pip install flake8
script:
- flake8 --ignore=E501,E123,E124,E126,E127,E128 dataset test
- python setup.py test

View File

@ -8,7 +8,6 @@ from dataset.persistence.util import sqlite_datetime_fix
from dataset.persistence.database import Database from dataset.persistence.database import Database
from dataset.persistence.table import Table from dataset.persistence.table import Table
from dataset.freeze.app import freeze from dataset.freeze.app import freeze
from sqlalchemy import Integer, Text
__all__ = ['Database', 'Table', 'freeze', 'connect'] __all__ = ['Database', 'Table', 'freeze', 'connect']

View File

@ -86,4 +86,3 @@ class Export(object):
@property @property
def name(self): def name(self):
return self.get('name', self.get('query')) return self.get('name', self.get('query'))

View File

@ -1,5 +1,4 @@
import os import os
import logging
import re import re
import locale import locale
@ -69,8 +68,7 @@ class Serializer(object):
@property @property
def wrap(self): def wrap(self):
return self.export.get_bool('wrap', return self.export.get_bool('wrap', default=self.mode == 'list')
default=self.mode=='list')
def serialize(self): def serialize(self):
self.init() self.init()

View File

@ -21,4 +21,3 @@ class TabsonSerializer(JSONSerializer):
if meta is not None: if meta is not None:
result['meta'] = meta result['meta'] = meta
return result return result

View File

@ -11,9 +11,9 @@ except ImportError:
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool from sqlalchemy.pool import NullPool
from sqlalchemy.schema import MetaData, Column, Index from sqlalchemy.schema import MetaData, Column
from sqlalchemy.schema import Table as SQLATable from sqlalchemy.schema import Table as SQLATable
from sqlalchemy import Integer, Text, String from sqlalchemy import Integer, String
from alembic.migration import MigrationContext from alembic.migration import MigrationContext
from alembic.operations import Operations from alembic.operations import Operations
@ -107,10 +107,8 @@ class Database(object):
@property @property
def tables(self): def tables(self):
""" Get a listing of all tables that exist in the database. """
Get a listing of all tables that exist in the database.
>>> print db.tables
set([u'user', u'action'])
""" """
return list( return list(
set(self.metadata.tables.keys()) | set(self._tables.keys()) set(self.metadata.tables.keys()) | set(self._tables.keys())

View File

@ -1,9 +1,5 @@
import logging import logging
from itertools import count from itertools import count
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict # Python < 2.7 drop-in
from sqlalchemy.sql import and_, expression from sqlalchemy.sql import and_, expression
from sqlalchemy.schema import Column, Index from sqlalchemy.schema import Column, Index
@ -28,9 +24,6 @@ class Table(object):
def columns(self): def columns(self):
""" """
Get a listing of all columns that exist in the table. Get a listing of all columns that exist in the table.
>>> print 'age' in table.columns
True
""" """
return set(self.table.columns.keys()) return set(self.table.columns.keys())
@ -105,7 +98,6 @@ class Table(object):
if chunk: if chunk:
_process_chunk(chunk) _process_chunk(chunk)
def update(self, row, keys, ensure=True, types={}): def update(self, row, keys, ensure=True, types={}):
""" """
Update a row in the table. The update is managed via Update a row in the table. The update is managed via
@ -126,7 +118,7 @@ class Table(object):
if not isinstance(keys, (list, tuple)): if not isinstance(keys, (list, tuple)):
keys = [keys] keys = [keys]
self._check_dropped() self._check_dropped()
if not keys or len(keys)==len(row): if not keys or len(keys) == len(row):
return False return False
clause = [(u, row.get(u)) for u in keys] clause = [(u, row.get(u)) for u in keys]
@ -288,7 +280,7 @@ class Table(object):
rp = self.database.executable.execute(query) rp = self.database.executable.execute(query)
data = rp.fetchone() data = rp.fetchone()
if data is not None: if data is not None:
return OrderedDict(zip(rp.keys(), data)) return data
def _args_to_order_by(self, order_by): def _args_to_order_by(self, order_by):
if order_by[0] == '-': if order_by[0] == '-':
@ -337,6 +329,9 @@ class Table(object):
rp = self.database.executable.execute(count_query) rp = self.database.executable.execute(count_query)
total_row_count = rp.fetchone()[0] total_row_count = rp.fetchone()[0]
if _limit is None:
_limit = total_row_count
if _step is None or _step is False or _step == 0: if _step is None or _step is False or _step == 0:
_step = total_row_count _step = total_row_count
@ -348,9 +343,7 @@ class Table(object):
for i in count(): for i in count():
qoffset = _offset + (_step * i) qoffset = _offset + (_step * i)
qlimit = _step qlimit = min(_limit - (_step * i), _step)
if _limit is not None:
qlimit = min(_limit - (_step * i), _step)
if qlimit <= 0: if qlimit <= 0:
break break
queries.append(self.table.select(whereclause=args, limit=qlimit, queries.append(self.table.select(whereclause=args, limit=qlimit,

View File

@ -1,9 +1,5 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from inspect import isgenerator from inspect import isgenerator
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict # Python < 2.7 drop-in
from sqlalchemy import Integer, UnicodeText, Float, DateTime, Boolean, types, Table, event from sqlalchemy import Integer, UnicodeText, Float, DateTime, Boolean, types, Table, event
@ -50,7 +46,7 @@ class ResultIter(object):
else: else:
# stop here # stop here
raise StopIteration raise StopIteration
return OrderedDict(zip(self.keys, row)) return row
next = __next__ next = __next__
@ -64,6 +60,8 @@ def sqlite_datetime_fix():
epoch = datetime(1970, 1, 1, 0, 0, 0) epoch = datetime(1970, 1, 1, 0, 0, 0)
def process_bind_param(self, value, dialect): def process_bind_param(self, value, dialect):
if isinstance(value, datetime):
return value
return (value / 1000 - self.epoch).total_seconds() return (value / 1000 - self.epoch).total_seconds()
def process_result_value(self, value, dialect): def process_result_value(self, value, dialect):

View File

@ -1,12 +1,12 @@
#coding: utf-8 #coding: utf-8
import re import re
from unicodedata import normalize as ucnorm, category
SLUG_REMOVE = re.compile(r'[,\s\.\(\)/\\;:]*') SLUG_REMOVE = re.compile(r'[,\s\.\(\)/\\;:]*')
class DatasetException(Exception): class DatasetException(Exception):
pass pass
class FreezeException(DatasetException): class FreezeException(DatasetException):
pass pass

View File

@ -25,17 +25,18 @@ setup(
author_email='info@okfn.org', author_email='info@okfn.org',
url='http://github.com/pudo/dataset', url='http://github.com/pudo/dataset',
license='MIT', license='MIT',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), packages=find_packages(exclude=['ez_setup', 'examples', 'test']),
namespace_packages=[], namespace_packages=[],
include_package_data=False, include_package_data=False,
zip_safe=False, zip_safe=False,
install_requires=[ install_requires=[
'sqlalchemy >= 0.8.1', 'sqlalchemy >= 0.9.1',
'alembic >= 0.6.1', 'alembic >= 0.6.2',
'python-slugify >= 0.0.6', 'python-slugify >= 0.0.6',
"PyYAML >= 3.10" "PyYAML >= 3.10"
] + py26_dependency, ] + py26_dependency,
tests_require=[], tests_require=[],
test_suite='test',
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'datafreeze = dataset.freeze.app:main', 'datafreeze = dataset.freeze.app:main',

View File

@ -2,15 +2,18 @@ import os
import unittest import unittest
from datetime import datetime from datetime import datetime
from sqlalchemy.exc import IntegrityError
from dataset import connect from dataset import connect
from dataset.util import DatasetException from dataset.util import DatasetException
from .sample_data import TEST_DATA, TEST_CITY_1 from .sample_data import TEST_DATA, TEST_CITY_1
from sqlalchemy.exc import IntegrityError
class DatabaseTestCase(unittest.TestCase): class DatabaseTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.old_db_url = os.environ.get('DATABASE_URL')
os.environ['DATABASE_URL'] = 'sqlite:///:memory:' os.environ['DATABASE_URL'] = 'sqlite:///:memory:'
self.db = connect('sqlite:///:memory:') self.db = connect('sqlite:///:memory:')
self.tbl = self.db['weather'] self.tbl = self.db['weather']
@ -19,7 +22,10 @@ class DatabaseTestCase(unittest.TestCase):
def tearDown(self): def tearDown(self):
# ensure env variable was unset # ensure env variable was unset
del os.environ['DATABASE_URL'] if self.old_db_url is None:
del os.environ['DATABASE_URL']
else:
os.environ['DATABASE_URL'] = self.old_db_url
def test_valid_database_url(self): def test_valid_database_url(self):
assert self.db.url, os.environ['DATABASE_URL'] assert self.db.url, os.environ['DATABASE_URL']
@ -46,7 +52,7 @@ class DatabaseTestCase(unittest.TestCase):
table.insert({ table.insert({
'string_id': 'foobar'}) 'string_id': 'foobar'})
assert table.find_one(string_id = 'foobar')['string_id'] == 'foobar' assert table.find_one(string_id='foobar')['string_id'] == 'foobar'
def test_create_table_custom_id2(self): def test_create_table_custom_id2(self):
pid = "string_id" pid = "string_id"
@ -57,19 +63,19 @@ class DatabaseTestCase(unittest.TestCase):
table.insert({ table.insert({
'string_id': 'foobar'}) 'string_id': 'foobar'})
assert table.find_one(string_id = 'foobar')['string_id'] == 'foobar' assert table.find_one(string_id='foobar')['string_id'] == 'foobar'
def test_create_table_custom_id3(self): def test_create_table_custom_id3(self):
pid = "int_id" pid = "int_id"
table = self.db.create_table("foo4", primary_id = pid) table = self.db.create_table("foo4", primary_id=pid)
assert table.table.exists() assert table.table.exists()
assert len(table.table.columns) == 1, table.table.columns assert len(table.table.columns) == 1, table.table.columns
assert pid in table.table.c, table.table.c assert pid in table.table.c, table.table.c
table.insert({'int_id': 123}) table.insert({'int_id': 123})
table.insert({'int_id': 124}) table.insert({'int_id': 124})
assert table.find_one(int_id = 123)['int_id'] == 123 assert table.find_one(int_id=123)['int_id'] == 123
assert table.find_one(int_id = 124)['int_id'] == 124 assert table.find_one(int_id=124)['int_id'] == 124
self.assertRaises(IntegrityError, lambda: table.insert({'int_id': 123})) self.assertRaises(IntegrityError, lambda: table.insert({'int_id': 123}))
def test_create_table_shorthand1(self): def test_create_table_shorthand1(self):
@ -81,8 +87,8 @@ class DatabaseTestCase(unittest.TestCase):
table.insert({'int_id': 123}) table.insert({'int_id': 123})
table.insert({'int_id': 124}) table.insert({'int_id': 124})
assert table.find_one(int_id = 123)['int_id'] == 123 assert table.find_one(int_id=123)['int_id'] == 123
assert table.find_one(int_id = 124)['int_id'] == 124 assert table.find_one(int_id=124)['int_id'] == 124
self.assertRaises(IntegrityError, lambda: table.insert({'int_id': 123})) self.assertRaises(IntegrityError, lambda: table.insert({'int_id': 123}))
def test_create_table_shorthand2(self): def test_create_table_shorthand2(self):
@ -94,7 +100,7 @@ class DatabaseTestCase(unittest.TestCase):
table.insert({ table.insert({
'string_id': 'foobar'}) 'string_id': 'foobar'})
assert table.find_one(string_id = 'foobar')['string_id'] == 'foobar' assert table.find_one(string_id='foobar')['string_id'] == 'foobar'
def test_create_table_shorthand3(self): def test_create_table_shorthand3(self):
pid = "string_id" pid = "string_id"
@ -105,7 +111,7 @@ class DatabaseTestCase(unittest.TestCase):
table.insert({ table.insert({
'string_id': 'foobar'}) 'string_id': 'foobar'})
assert table.find_one(string_id = 'foobar')['string_id'] == 'foobar' assert table.find_one(string_id='foobar')['string_id'] == 'foobar'
def test_load_table(self): def test_load_table(self):
tbl = self.db.load_table('weather') tbl = self.db.load_table('weather')
@ -131,7 +137,7 @@ class TableTestCase(unittest.TestCase):
'temperature': -10, 'temperature': -10,
'place': 'Berlin'} 'place': 'Berlin'}
) )
assert len(self.tbl) == len(TEST_DATA)+1, len(self.tbl) assert len(self.tbl) == len(TEST_DATA) + 1, len(self.tbl)
assert self.tbl.find_one(id=last_id)['place'] == 'Berlin' assert self.tbl.find_one(id=last_id)['place'] == 'Berlin'
def test_upsert(self): def test_upsert(self):
@ -141,17 +147,17 @@ class TableTestCase(unittest.TestCase):
'place': 'Berlin'}, 'place': 'Berlin'},
['place'] ['place']
) )
assert len(self.tbl) == len(TEST_DATA)+1, len(self.tbl) assert len(self.tbl) == len(TEST_DATA) + 1, len(self.tbl)
self.tbl.upsert({ self.tbl.upsert({
'date': datetime(2011, 1, 2), 'date': datetime(2011, 1, 2),
'temperature': -10, 'temperature': -10,
'place': 'Berlin'}, 'place': 'Berlin'},
['place'] ['place']
) )
assert len(self.tbl) == len(TEST_DATA)+1, len(self.tbl) assert len(self.tbl) == len(TEST_DATA) + 1, len(self.tbl)
def test_upsert_all_key(self): def test_upsert_all_key(self):
for i in range(0,2): for i in range(0, 2):
self.tbl.upsert({ self.tbl.upsert({
'date': datetime(2011, 1, 2), 'date': datetime(2011, 1, 2),
'temperature': -10, 'temperature': -10,
@ -165,7 +171,7 @@ class TableTestCase(unittest.TestCase):
'temperature': -10, 'temperature': -10,
'place': 'Berlin'} 'place': 'Berlin'}
) )
assert len(self.tbl) == len(TEST_DATA)+1, len(self.tbl) assert len(self.tbl) == len(TEST_DATA) + 1, len(self.tbl)
self.tbl.delete(place='Berlin') self.tbl.delete(place='Berlin')
assert len(self.tbl) == len(TEST_DATA), len(self.tbl) assert len(self.tbl) == len(TEST_DATA), len(self.tbl)
self.tbl.delete() self.tbl.delete()
@ -187,6 +193,10 @@ class TableTestCase(unittest.TestCase):
assert len(ds) == 3, ds assert len(ds) == 3, ds
ds = list(self.tbl.find(place=TEST_CITY_1, _limit=2)) ds = list(self.tbl.find(place=TEST_CITY_1, _limit=2))
assert len(ds) == 2, ds assert len(ds) == 2, ds
ds = list(self.tbl.find(place=TEST_CITY_1, _limit=2, _step=1))
assert len(ds) == 2, ds
ds = list(self.tbl.find(place=TEST_CITY_1, _limit=1, _step=2))
assert len(ds) == 1, ds
def test_distinct(self): def test_distinct(self):
x = list(self.tbl.distinct('place')) x = list(self.tbl.distinct('place'))
@ -195,8 +205,8 @@ class TableTestCase(unittest.TestCase):
assert len(x) == 6, x assert len(x) == 6, x
def test_insert_many(self): def test_insert_many(self):
data = TEST_DATA * 5000 data = TEST_DATA * 100
self.tbl.insert_many(data) self.tbl.insert_many(data, chunk_size=13)
assert len(self.tbl) == len(data) + 6 assert len(self.tbl) == len(data) + 6
def test_drop_warning(self): def test_drop_warning(self):
@ -239,7 +249,7 @@ class TableTestCase(unittest.TestCase):
tbl = self.tbl tbl = self.tbl
tbl.create_column('foo', FLOAT) tbl.create_column('foo', FLOAT)
assert 'foo' in tbl.table.c, tbl.table.c assert 'foo' in tbl.table.c, tbl.table.c
assert FLOAT == type(tbl.table.c['foo'].type), tbl.table.c['foo'].type assert isinstance(tbl.table.c['foo'].type, FLOAT), tbl.table.c['foo'].type
assert 'foo' in tbl.columns, tbl.columns assert 'foo' in tbl.columns, tbl.columns
def test_key_order(self): def test_key_order(self):
@ -247,6 +257,3 @@ class TableTestCase(unittest.TestCase):
keys = list(res.next().keys()) keys = list(res.next().keys())
assert keys[0] == 'temperature' assert keys[0] == 'temperature'
assert keys[1] == 'place' assert keys[1] == 'place'
if __name__ == '__main__':
unittest.main()