diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..8e9b6dd17 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +branch = False +source = datajoint + +[report] diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 000000000..2eeede133 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1 @@ +repo_token: fd0BoXG46TPReEem0uMy7BJO5j0w1MQiY \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 20c2f651b..c52c11a1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,6 @@ install: # command to run tests script: - nosetests -vv --with-coverage --cover-package=datajoint + after_success: - coveralls diff --git a/datajoint/base_relation.py b/datajoint/base_relation.py index 1775c783d..bbe2be3f5 100644 --- a/datajoint/base_relation.py +++ b/datajoint/base_relation.py @@ -288,7 +288,7 @@ def delete(self): restrictions = defaultdict(list) if self.restrictions: restrict_by_me.add(self.full_table_name) - restrictions[self.full_table_name] = self.restrictions # copy own restrictions + restrictions[self.full_table_name].append(self.restrictions) # copy own restrictions for r in relations.values(): restrict_by_me.update(r.references) for name, r in relations.items(): diff --git a/datajoint/fetch.py b/datajoint/fetch.py index 4474482ac..ffbea5c75 100644 --- a/datajoint/fetch.py +++ b/datajoint/fetch.py @@ -2,6 +2,7 @@ from collections.abc import Callable, Iterable from functools import wraps import warnings + from .blob import unpack import numpy as np from datajoint import DataJointError @@ -39,6 +40,7 @@ def copy_first(f): """ Decorates methods that return an altered copy of self """ + @wraps(f) def ret(*args, **kwargs): args = list(args) @@ -148,7 +150,7 @@ def __call__(self, **kwargs): if behavior['limit'] is None and behavior['offset'] is not None: warnings.warn('Offset set, but no limit. Setting limit to a large number. ' 'Consider setting a limit explicitly.') - behavior['limit'] = 2*len(self._relation) + behavior['limit'] = 2 * len(self._relation) cur = self._relation.cursor(**behavior) heading = self._relation.heading @@ -217,19 +219,11 @@ def __getitem__(self, item): return return_values[0] if single_output else return_values def __repr__(self): - limit = config['display.limit'] - width = config['display.width'] - rel = self._relation.project(*self._relation.heading.non_blobs) # project out blobs - template = '%%-%d.%ds' % (width, width) - columns = rel.heading.names - repr_string = ' '.join([template % column for column in columns]) + '\n' - repr_string += ' '.join(['+' + '-' * (width - 2) + '+' for _ in columns]) + '\n' - for tup in rel.fetch(limit=limit): - repr_string += ' '.join([template % column for column in tup]) + '\n' - if len(rel) > limit: - repr_string += '...\n' - repr_string += ' (%d tuples)\n' % len(rel) - return repr_string + repr_str = """Fetch object for {items} items on {name}\n""".format(name=self._relation.__class__.__name__, + items=len(self._relation) ) + repr_str += '\n'.join( + ["\t{key}:\t{value}".format(key=k, value=str(v)) for k, v in self.behavior.items() if v is not None]) + return repr_str def __len__(self): return len(self._relation) diff --git a/datajoint/relational_operand.py b/datajoint/relational_operand.py index 698201015..0187d1c32 100644 --- a/datajoint/relational_operand.py +++ b/datajoint/relational_operand.py @@ -45,24 +45,26 @@ def where_clause(self): """ def make_condition(arg, _negate=False): - if isinstance(arg, (str, AndList)): - return str(arg), _negate + if isinstance(arg, str): + return arg, _negate + elif isinstance(arg, AndList): + return '(' + ' AND '.join([make_condition(element)[0] for element in arg]) + ')', _negate # semijoin or antijoin - if isinstance(arg, RelationalOperand): + elif isinstance(arg, RelationalOperand): common_attributes = [q for q in self.heading.names if q in arg.heading.names] if not common_attributes: - condition = 'FALSE' if negate else 'TRUE' + condition = 'FALSE' if _negate else 'TRUE' else: common_attributes = '`' + '`,`'.join(common_attributes) + '`' condition = '({fields}) {not_}in ({subquery})'.format( fields=common_attributes, - not_="not " if negate else "", + not_="not " if _negate else "", subquery=arg.make_select(common_attributes)) - return condition, False # negate is cleared + return condition, False # _negate is cleared # mappings are turned into ANDed equality conditions - if isinstance(arg, Mapping): + elif isinstance(arg, Mapping): condition = ['`%s`=%r' % (k, v if not isinstance(v, (datetime.date, datetime.datetime, datetime.time)) else str(v)) for k, v in arg.items() if k in self.heading] @@ -91,6 +93,8 @@ def make_condition(arg, _negate=False): conditions.append(('NOT (%s)' if negate else '(%s)') % item) return ' WHERE ' + ' AND '.join(conditions) + def __repr__(self): + return 'AND List: ' + repr(self._list) class RelationalOperand(metaclass=abc.ABCMeta): """ @@ -244,9 +248,25 @@ def _repr_helper(self): """ def __repr__(self): - ret = self._repr_helper() - if self._restrictions: - ret += ' & %r' % self._restrictions + if config['loglevel'].lower() == 'debug': + ret = self._repr_helper() + if self._restrictions: + ret += ' & %r' % self._restrictions + else: + limit = config['display.limit'] + width = config['display.width'] + rel = self.project(*self.heading.non_blobs) # project out blobs + template = '%%-%d.%ds' % (width, width) + columns = rel.heading.names + repr_string = ' '.join([template % column for column in columns]) + '\n' + repr_string += ' '.join(['+' + '-' * (width - 2) + '+' for _ in columns]) + '\n' + for tup in rel.fetch(limit=limit): + repr_string += ' '.join([template % column for column in tup]) + '\n' + if len(rel) > limit: + repr_string += '...\n' + repr_string += ' (%d tuples)\n' % len(rel) + return repr_string + return ret def _repr_html_(self): @@ -259,8 +279,6 @@ def _repr_html_(self): ['\n'.join(['