mirror of
https://github.com/shmilylty/OneForAll.git
synced 2026-08-26 04:47:48 +08:00
优化覆盖率
This commit is contained in:
@@ -305,32 +305,6 @@ class Database(object):
|
|||||||
with self.get_connection() as conn:
|
with self.get_connection() as conn:
|
||||||
conn.bulk_query(query, *multiparams)
|
conn.bulk_query(query, *multiparams)
|
||||||
|
|
||||||
def query_file(self, path, fetchall=False, **params):
|
|
||||||
"""Like Database.query, but takes a filename to load a query from."""
|
|
||||||
|
|
||||||
with self.get_connection() as conn:
|
|
||||||
return conn.query_file(path, fetchall, **params)
|
|
||||||
|
|
||||||
def bulk_query_file(self, path, *multiparams):
|
|
||||||
"""Like Database.bulk_query, but takes a filename to load a query from."""
|
|
||||||
|
|
||||||
with self.get_connection() as conn:
|
|
||||||
conn.bulk_query_file(path, *multiparams)
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def transaction(self):
|
|
||||||
"""A context manager for executing a transaction on this Database."""
|
|
||||||
|
|
||||||
conn = self.get_connection()
|
|
||||||
tx = conn.transaction()
|
|
||||||
try:
|
|
||||||
yield conn
|
|
||||||
tx.commit()
|
|
||||||
except:
|
|
||||||
tx.rollback()
|
|
||||||
finally:
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
"""A Database connection."""
|
"""A Database connection."""
|
||||||
@@ -378,49 +352,6 @@ class Connection(object):
|
|||||||
|
|
||||||
self._conn.execute(text(query), *multiparams)
|
self._conn.execute(text(query), *multiparams)
|
||||||
|
|
||||||
def query_file(self, path, fetchall=False, **params):
|
|
||||||
"""Like Connection.query, but takes a filename to load a query from."""
|
|
||||||
|
|
||||||
# If path doesn't exists
|
|
||||||
if not os.path.exists(path):
|
|
||||||
raise IOError("File '{}' not found!".format(path))
|
|
||||||
|
|
||||||
# If it's a directory
|
|
||||||
if os.path.isdir(path):
|
|
||||||
raise IOError("'{}' is a directory!".format(path))
|
|
||||||
|
|
||||||
# Read the given .sql file into memory.
|
|
||||||
with open(path) as f:
|
|
||||||
query = f.read()
|
|
||||||
|
|
||||||
# Defer processing to self.query method.
|
|
||||||
return self.query(query=query, fetchall=fetchall, **params)
|
|
||||||
|
|
||||||
def bulk_query_file(self, path, *multiparams):
|
|
||||||
"""Like Connection.bulk_query, but takes a filename to load a query
|
|
||||||
from.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# If path doesn't exists
|
|
||||||
if not os.path.exists(path):
|
|
||||||
raise IOError("File '{}'' not found!".format(path))
|
|
||||||
|
|
||||||
# If it's a directory
|
|
||||||
if os.path.isdir(path):
|
|
||||||
raise IOError("'{}' is a directory!".format(path))
|
|
||||||
|
|
||||||
# Read the given .sql file into memory.
|
|
||||||
with open(path) as f:
|
|
||||||
query = f.read()
|
|
||||||
|
|
||||||
self._conn.execute(text(query), *multiparams)
|
|
||||||
|
|
||||||
def transaction(self):
|
|
||||||
"""Returns a transaction object. Call ``commit`` or ``rollback``
|
|
||||||
on the returned object as appropriate."""
|
|
||||||
|
|
||||||
return self._conn.begin()
|
|
||||||
|
|
||||||
|
|
||||||
def _reduce_datetimes(row):
|
def _reduce_datetimes(row):
|
||||||
"""Receives a row, converts datetimes to strings."""
|
"""Receives a row, converts datetimes to strings."""
|
||||||
@@ -431,10 +362,3 @@ def _reduce_datetimes(row):
|
|||||||
if hasattr(row[i], 'isoformat'):
|
if hasattr(row[i], 'isoformat'):
|
||||||
row[i] = row[i].isoformat()
|
row[i] = row[i].isoformat()
|
||||||
return tuple(row)
|
return tuple(row)
|
||||||
|
|
||||||
|
|
||||||
def print_bytes(content):
|
|
||||||
try:
|
|
||||||
stdout.buffer.write(content)
|
|
||||||
except AttributeError:
|
|
||||||
stdout.write(content)
|
|
||||||
|
|||||||
+27
-61
@@ -4,7 +4,33 @@ import csv
|
|||||||
from io import StringIO
|
from io import StringIO
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from . import tablib
|
""" Tablib - formats
|
||||||
|
"""
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
class Registry(object):
|
||||||
|
_formats = OrderedDict()
|
||||||
|
|
||||||
|
def register(self, key, format_or_path):
|
||||||
|
# Create Databook.<format> read or read/write properties
|
||||||
|
|
||||||
|
# Create Dataset.<format> read or read/write properties,
|
||||||
|
# and Dataset.get_<format>/set_<format> methods.
|
||||||
|
self._formats[key] = format_or_path
|
||||||
|
|
||||||
|
def register_builtins(self):
|
||||||
|
# Registration ordering matters for autodetection.
|
||||||
|
self.register('csv', CSVFormat())
|
||||||
|
self.register('json', JSONFormat())
|
||||||
|
|
||||||
|
def get_format(self, key):
|
||||||
|
if key not in self._formats:
|
||||||
|
raise Exception("OneForAll has no format '%s'." % key)
|
||||||
|
return self._formats[key]
|
||||||
|
|
||||||
|
|
||||||
|
registry = Registry()
|
||||||
|
|
||||||
|
|
||||||
def serialize_objects_handler(obj):
|
def serialize_objects_handler(obj):
|
||||||
@@ -30,38 +56,6 @@ class JSONFormat(object):
|
|||||||
"""Returns JSON representation of Dataset."""
|
"""Returns JSON representation of Dataset."""
|
||||||
return json.dumps(dataset.dict, default=serialize_objects_handler)
|
return json.dumps(dataset.dict, default=serialize_objects_handler)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def export_book(cls, databook):
|
|
||||||
"""Returns JSON representation of Databook."""
|
|
||||||
return json.dumps(databook._package(), default=serialize_objects_handler)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def import_set(cls, dset, in_stream):
|
|
||||||
"""Returns dataset from JSON stream."""
|
|
||||||
|
|
||||||
dset.wipe()
|
|
||||||
dset.dict = json.load(in_stream)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def import_book(cls, dbook, in_stream):
|
|
||||||
"""Returns databook from JSON stream."""
|
|
||||||
|
|
||||||
dbook.wipe()
|
|
||||||
for sheet in json.load(in_stream):
|
|
||||||
data = tablib.Dataset()
|
|
||||||
data.title = sheet['title']
|
|
||||||
data.dict = sheet['data']
|
|
||||||
dbook.add_sheet(data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def detect(cls, stream):
|
|
||||||
"""Returns True if given stream is valid JSON."""
|
|
||||||
try:
|
|
||||||
json.load(stream)
|
|
||||||
return True
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
""" Tablib - CSV Support.
|
""" Tablib - CSV Support.
|
||||||
"""
|
"""
|
||||||
@@ -93,31 +87,3 @@ class CSVFormat(object):
|
|||||||
"""Returns CSV representation of Dataset."""
|
"""Returns CSV representation of Dataset."""
|
||||||
stream = cls.export_stream_set(dataset, **kwargs)
|
stream = cls.export_stream_set(dataset, **kwargs)
|
||||||
return stream.getvalue()
|
return stream.getvalue()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def import_set(cls, dset, in_stream, headers=True, **kwargs):
|
|
||||||
"""Returns dataset from CSV stream."""
|
|
||||||
|
|
||||||
dset.wipe()
|
|
||||||
|
|
||||||
kwargs.setdefault('delimiter', cls.DEFAULT_DELIMITER)
|
|
||||||
|
|
||||||
rows = csv.reader(in_stream, **kwargs)
|
|
||||||
for i, row in enumerate(rows):
|
|
||||||
|
|
||||||
if (i == 0) and (headers):
|
|
||||||
dset.headers = row
|
|
||||||
elif row:
|
|
||||||
if i > 0 and len(row) < dset.width:
|
|
||||||
row += [''] * (dset.width - len(row))
|
|
||||||
dset.append(row)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def detect(cls, stream, delimiter=None):
|
|
||||||
"""Returns True if given stream is valid CSV."""
|
|
||||||
try:
|
|
||||||
csv.Sniffer().sniff(stream.read(1024),
|
|
||||||
delimiters=delimiter or cls.DEFAULT_DELIMITER)
|
|
||||||
return True
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|||||||
+1
-175
@@ -1,49 +1,5 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from io import BytesIO, StringIO
|
from .format import registry
|
||||||
from .registry import registry
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_input(stream):
|
|
||||||
"""
|
|
||||||
Accept either a str/bytes stream or a file-like object and always return a
|
|
||||||
file-like object.
|
|
||||||
"""
|
|
||||||
if isinstance(stream, str):
|
|
||||||
return StringIO(stream)
|
|
||||||
elif isinstance(stream, bytes):
|
|
||||||
return BytesIO(stream)
|
|
||||||
return stream
|
|
||||||
|
|
||||||
|
|
||||||
def import_set(stream, format=None, **kwargs):
|
|
||||||
"""Return dataset of given stream (file-like object, string, or bytestring)."""
|
|
||||||
|
|
||||||
return Dataset().load(normalize_input(stream), format, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def detect_format(stream):
|
|
||||||
"""Return format name of given stream (file-like object, string, or bytestring)."""
|
|
||||||
stream = normalize_input(stream)
|
|
||||||
fmt_title = None
|
|
||||||
for fmt in registry.formats():
|
|
||||||
try:
|
|
||||||
if fmt.detect(stream):
|
|
||||||
fmt_title = fmt.title
|
|
||||||
break
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
if hasattr(stream, 'seek'):
|
|
||||||
stream.seek(0)
|
|
||||||
return fmt_title
|
|
||||||
|
|
||||||
|
|
||||||
def get_format(format):
|
|
||||||
"""
|
|
||||||
Determine if the format is available
|
|
||||||
:param format:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Row:
|
class Row:
|
||||||
@@ -173,78 +129,6 @@ class Dataset:
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.height
|
return self.height
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
if isinstance(key, str):
|
|
||||||
if key in self.headers:
|
|
||||||
pos = self.headers.index(key) # get 'key' index from each data
|
|
||||||
return [row[pos] for row in self._data]
|
|
||||||
else:
|
|
||||||
raise KeyError
|
|
||||||
else:
|
|
||||||
_results = self._data[key]
|
|
||||||
if isinstance(_results, Row):
|
|
||||||
return _results.tuple
|
|
||||||
else:
|
|
||||||
return [result.tuple for result in _results]
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self._validate(value)
|
|
||||||
self._data[key] = Row(value)
|
|
||||||
|
|
||||||
def __delitem__(self, key):
|
|
||||||
if isinstance(key, str):
|
|
||||||
|
|
||||||
if key in self.headers:
|
|
||||||
|
|
||||||
pos = self.headers.index(key)
|
|
||||||
del self.headers[pos]
|
|
||||||
|
|
||||||
for i, row in enumerate(self._data):
|
|
||||||
del row[pos]
|
|
||||||
self._data[i] = row
|
|
||||||
else:
|
|
||||||
raise KeyError
|
|
||||||
else:
|
|
||||||
del self._data[key]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
try:
|
|
||||||
return '<%s dataset>' % (self.title.lower())
|
|
||||||
except AttributeError:
|
|
||||||
return '<dataset object>'
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
result = []
|
|
||||||
|
|
||||||
# Add str representation of headers.
|
|
||||||
if self.__headers:
|
|
||||||
result.append([str(h) for h in self.__headers])
|
|
||||||
|
|
||||||
# Add str representation of rows.
|
|
||||||
result.extend(list(map(str, row)) for row in self._data)
|
|
||||||
|
|
||||||
lens = [list(map(len, row)) for row in result]
|
|
||||||
field_lens = list(map(max, zip(*lens)))
|
|
||||||
|
|
||||||
# delimiter between header and data
|
|
||||||
if self.__headers:
|
|
||||||
result.insert(1, ['-' * length for length in field_lens])
|
|
||||||
|
|
||||||
format_string = '|'.join('{%s:%s}' % item for item in enumerate(field_lens))
|
|
||||||
|
|
||||||
return '\n'.join(format_string.format(*row) for row in result)
|
|
||||||
|
|
||||||
# ---------
|
|
||||||
# Internals
|
|
||||||
# ---------
|
|
||||||
|
|
||||||
def _get_in_format(self, fmt_key, **kwargs):
|
|
||||||
return registry.get_format(fmt_key).export_set(self, **kwargs)
|
|
||||||
|
|
||||||
def _set_in_format(self, fmt_key, in_stream, **kwargs):
|
|
||||||
in_stream = normalize_input(in_stream)
|
|
||||||
return registry.get_format(fmt_key).import_set(self, in_stream, **kwargs)
|
|
||||||
|
|
||||||
def _validate(self, row=None, col=None, safety=False):
|
def _validate(self, row=None, col=None, safety=False):
|
||||||
"""Assures size of every row in dataset is of proper proportions."""
|
"""Assures size of every row in dataset is of proper proportions."""
|
||||||
if row:
|
if row:
|
||||||
@@ -365,22 +249,6 @@ class Dataset:
|
|||||||
|
|
||||||
dict = property(_get_dict, _set_dict)
|
dict = property(_get_dict, _set_dict)
|
||||||
|
|
||||||
def _clean_col(self, col):
|
|
||||||
"""Prepares the given column for insert/append."""
|
|
||||||
|
|
||||||
col = list(col)
|
|
||||||
|
|
||||||
if self.headers:
|
|
||||||
header = [col.pop(0)]
|
|
||||||
else:
|
|
||||||
header = []
|
|
||||||
|
|
||||||
if len(col) == 1 and hasattr(col[0], '__call__'):
|
|
||||||
col = list(map(col[0], self._data))
|
|
||||||
col = tuple(header + col)
|
|
||||||
|
|
||||||
return col
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self):
|
def height(self):
|
||||||
"""The number of rows currently in the :class:`Dataset`.
|
"""The number of rows currently in the :class:`Dataset`.
|
||||||
@@ -464,29 +332,6 @@ class Dataset:
|
|||||||
# Misc
|
# Misc
|
||||||
# ----
|
# ----
|
||||||
|
|
||||||
def add_formatter(self, col, handler):
|
|
||||||
"""Adds a formatter to the :class:`Dataset`.
|
|
||||||
|
|
||||||
.. version added:: 0.9.5
|
|
||||||
|
|
||||||
:param col: column to. Accepts index int or header str.
|
|
||||||
:param handler: reference to callback function to execute against
|
|
||||||
each cell value.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if isinstance(col, str):
|
|
||||||
if col in self.headers:
|
|
||||||
col = self.headers.index(col) # get 'key' index from each data
|
|
||||||
else:
|
|
||||||
raise KeyError
|
|
||||||
|
|
||||||
if not col > self.width:
|
|
||||||
self._formatters.append((col, handler))
|
|
||||||
else:
|
|
||||||
raise InvalidDatasetIndex
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def remove_duplicates(self):
|
def remove_duplicates(self):
|
||||||
"""Removes all duplicate rows from the :class:`Dataset` object
|
"""Removes all duplicate rows from the :class:`Dataset` object
|
||||||
while maintaining the original order."""
|
while maintaining the original order."""
|
||||||
@@ -499,25 +344,6 @@ class Dataset:
|
|||||||
self._data = list()
|
self._data = list()
|
||||||
self.__headers = None
|
self.__headers = None
|
||||||
|
|
||||||
def load(self, in_stream, format, **kwargs):
|
|
||||||
"""
|
|
||||||
Import `in_stream` to the :class:`Databook` object using the `format`.
|
|
||||||
`in_stream` can be a file-like object, a string, or a bytestring.
|
|
||||||
|
|
||||||
:param \\*\\*kwargs: (optional) custom configuration to the format `import_book`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
stream = normalize_input(in_stream)
|
|
||||||
if not format:
|
|
||||||
format = detect_format(stream)
|
|
||||||
|
|
||||||
fmt = registry.get_format(format)
|
|
||||||
if not hasattr(fmt, 'import_book'):
|
|
||||||
raise UnsupportedFormat('Format {} cannot be loaded.'.format(format))
|
|
||||||
|
|
||||||
fmt.import_book(self, stream, **kwargs)
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
registry.register_builtins()
|
registry.register_builtins()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user