def __init__(self, spec=None): if spec is None: spec = '' if not isinstance(spec, str): raise Error('merge argument must be a string') merge_operator, merge_count, exclude_operator, exclude_count = '=', 0, '=', 0 match = re.match(r'([><]?=?)(\d+)(,([><]?=?)(\d+))?', spec) if match: merge_operator, merge_count, _, exclude_operator, exclude_count = match.groups( ) self.merge_operator = MODIFIERS[ merge_operator] if merge_operator else MODIFIERS['='] self.exclude_operator = MODIFIERS[ exclude_operator] if exclude_operator else MODIFIERS['='] try: self.merge_count = int(merge_count) if merge_count else 0 self.exclude_count = int(exclude_count) if exclude_count else 0 except ValueError: raise Error('invalid merge argument: {!r}'.format(spec)) if self.merge_count < 0 or self.exclude_count < 0: raise Error('invalid merge argument: {!r}'.format(spec))
def __init__(self, size, number): self.limit = None self.offset = 0 if size is None and number is not None: raise Error('please provide page[size]'.format(size)) if size is not None: try: self.limit = int(size) except ValueError: raise Error('invalid value for page[size]: {!r}'.format(size)) else: if self.limit <= 0: raise Error( 'invalid value for page[size]: {!r}'.format(size)) if number is not None: try: self.offset = (int(number) - 1) * self.limit except ValueError: raise Error( 'invalid value for page[number]: {!r}'.format(number)) else: if int(number) <= 0: raise Error( 'invalid value for page[number]: {!r}'.format(number))
def get(self, expr, op, val): # # multiple values # if ',' in val: if not self.has_operator(op, multiple=True): raise Error('invalid operator: {}'.format(op)) values = self.parse_values(val) if all(mod == '=' for mod, _ in values): if op in ('', 'eq'): return expr.in_(val for _, val in values) elif op == 'ne': return expr.notin_(val for _, val in values) else: raise Error('invalid operator: {}') else: expressions = list() for i, (mod, val) in enumerate(values): if val is True or val is False or val is None: if mod == '=': expressions.append(expr.is_(val)) elif mod in ('!=', '<>'): expressions.append(expr.isnot(val)) else: raise Error('invalid modifier: {}'.format(mod)) else: and_expr = list() and_expr.append(MODIFIERS[mod](expr, val)) if i > 0: val_before = values[i - 1][1] if val_before is not None: and_expr.append(MODIFIERS['>'](expr, values[i - 1][1])) if i < len(values) - 1: val_after = values[i + 1][1] if val_after is not None: and_expr.append(MODIFIERS['<'](expr, values[i + 1][1])) expressions.append(and_(*and_expr)) return or_(*expressions) # # single values # else: op = 'eq' if not op else op if not self.has_operator(op): raise Error('invalid operator: {}'.format(op)) v = self.data_type.parse(val) if v is False or v is True or v is None: if op == 'eq': return expr.is_(v) if op == 'ne': return expr.isnot(v) raise Error('invalid operator: {}'.format(op)) return getattr(operators, op)(expr, v)
def check_refs(self, refs): for ref in refs: if not isinstance(ref, Column): raise Error('invalid "ref" value: {!r}'.format(ref)) if self.cardinality == Cardinality.MANY_TO_MANY and len(refs) != 2: raise Error('two "ref" columns required: {}'.format(', '.join(r.name) for r in refs)) if self.cardinality in (Cardinality.MANY_TO_ONE, Cardinality.ONE_TO_MANY) and len(refs) != 1: raise Error('one "ref" column required: {}'.format(', '.join(r.name) for r in refs)) if self.cardinality == Cardinality.ONE_TO_ONE and len(refs) > 1: raise Error('too many "ref" columns: {}'.format(', '.join(r.name) for r in refs))
def get_type(cls): if cls.type_ is None: type_ = dasherize(underscore(cls.__name__)) return type_.replace('model', '').strip('-') if not isinstance(cls.type_, str): raise Error('"type_" must be a string') return cls.type_
def __init__(self, args): """ :param args: a dictionary representing the request query string """ args = args if args else dict() try: self.include = tuple( AttributePath(path) for path in args['include'].split( ',')) if 'include' in args else () self.fields = { f.type: f for f in (FieldArgument(k, args[k]) for k in args.keys() if k.startswith('fields')) } self.sort = tuple( SortArgument(spec) for spec in args['sort'].split(',')) if 'sort' in args else () self.filter = self._group_filter_args( FilterArgument(k, args[k]) for k in args.keys() if k.startswith('filter')) self.page = PageArgument(args.get('page[size]', None), args.get('page[number]', None)) self.merge = MergeArgument( args['merge']) if 'merge' in args else None self.options = { o.name: o.value for o in (OptionArgument(k, v) for k, v in args.items() if k.startswith('option')) } except (AttributeError, TypeError): raise Error( 'argument parser | invalid dictionary: {!r}'.format(args))
def __init__(self, table, **kwargs): """ :param table: an SQLAlchemy Table or Alias object :param onclause: an onclause join expression (optional) :param left: if set perform outer left join (optional) """ self.table = table self.onclause = kwargs.get('onclause', None) self.left = bool(kwargs.get('left', False)) if not isinstance(self.table, Selectable): raise Error('[FromItem] invalid "table" argument: {}'.format(self.table)) if self.onclause is not None: if not is_clause(self.onclause): raise Error('[FromItem] invalid "onclause" argument: {}'.format( self.onclause))
def __init__(self, value): try: order, dot_path = re.search(r'([-+]?)(.+)', value).groups() except AttributeError: raise Error('invalid sort argument: {!r}'.format(value)) else: self.desc = order == '-' self.path = AttributePath(dot_path)
def __init__(self, spec, value): try: name = re.match(r'option\[([-_\w]+)\]', spec).group(1) except AttributeError: raise Error('invalid option parameter: {!r}'.format(spec)) else: self.name = camelize(name, False) self.value = value
def __init__(self, spec, value): try: self.type = re.search(r'^fields\[([-_\w]+)\]$', spec).group(1) except AttributeError: raise Error('invalid fieldset spec: {!r}'.format(spec)) else: self.names = tuple( set(underscore(x) for x in value.split(',') + ['id']))
def __init__(self, name, data_type=None): if data_type is not None and not isinstance(data_type, DataType): raise Error('invalid data type provided: "{}"'.format(data_type)) self.name = name self.data_type = data_type self.expr = None self.exclude = False self.sort_by = False self.filter_clause = None
def __init__(self, *models, searchable=False): if len(models) < 2: raise Error('at least two models are required') models_uniq = list(set(models)) if len(models) > len(models_uniq): raise Error('models must be unique') for i, model in enumerate(models_uniq): if isinstance(model, type) and issubclass(model, Model): models_uniq[i] = model() elif not isinstance(model, Model): raise Error('invalid model: {!r}'.format(model)) if searchable: for model in models_uniq: if model.search is None: raise Error('model must be searchable: {!r}'.format(model)) self._models = set(models_uniq)
def __init__(self, spec, value): try: group, dot_path, op = re.match( r'filter(\[\w+\])?\[([-_.\w]+)(:[-_\w]+)?\]', spec).groups() except AttributeError: raise Error('invalid filter parameter: {!r}'.format(spec)) else: self.path = AttributePath(dot_path) self.operator = op.strip(':') if op else 'eq' self.value = value self.group = group
def add_custom(self, name, custom_clause): if is_clause(custom_clause): self.where.append(custom_clause) else: try: if len(custom_clause) == 2 and is_clause(custom_clause[0]) \ and all(is_from_item(fi for fi in custom_clause[1])): self.where.append(custom_clause[0]) self.from_items.extend(custom_clause[1]) else: raise TypeError except TypeError: raise Error('filter:{} | expected a where clause and a sequence of from items'.format(name))
def get_from_items(self, related=False): if self.model is None and self.parent is None: raise Error('relationship: {!r} not loaded') if self.where is not None and not is_clause(self.where): raise ModelError('{!r} | invalid "where" clause'.format(self), self.model) from_items = list() if self.cardinality == Cardinality.ONE_TO_ONE: if self.refs: from_items.append(FromItem( self.refs[0].table, onclause=get_primary_key(self.refs[0].table) == self.parent.primary_key, left=True)) from_items.append(FromItem( self.model.from_clause(), onclause=self.model.primary_key == (self.refs[0] if self.refs else self.parent.primary_key), left=True)) elif self.cardinality == Cardinality.MANY_TO_ONE: ref = self.parent.from_clause.get_column(self.refs[0]) if ref is not None: from_items.append(FromItem( self.parent.from_clause() if related else self.model.from_clause(), onclause=self.model.primary_key == ref, left=True)) else: if related: from_items.append(FromItem( self.refs[0].table, onclause=self.model.primary_key == self.refs[0], left=True)) from_items.append(FromItem( self.parent.from_clause(), onclause=get_primary_key(self.refs[0].table) == self.parent.primary_key, left=True)) else: from_items.append(FromItem( self.refs[0].table, onclause=self.parent.primary_key == get_primary_key(self.refs[0].table), left=True)) from_items.append(FromItem( self.model.from_clause(), onclause=self.refs[0] == self.model.primary_key, left=True)) elif self.cardinality == Cardinality.ONE_TO_MANY: ref = self.model.from_clause.get_column(self.refs[0]) if ref is not None: from_items.append(FromItem( ref.table, onclause=self.parent.primary_key == ref, left=True)) if self.model.primary_key.table != ref.table: from_items.append(FromItem( self.model.primary_key.table, onclause=get_primary_key(ref.table) == self.model.primary_key, left=True)) else: from_items.append(FromItem( self.refs[0].table, onclause=self.model.primary_key == get_primary_key(self.refs[0]), left=True)) from_items.append(FromItem( self.parent.from_clause(), onclause=self.refs[0] == self.parent.primary_key, left=True)) else: if related: onclause = self.model.primary_key == self.refs[1] if self.where is not None: onclause = and_(onclause, self.where) from_items.append(FromItem(self.refs[1].table, onclause=onclause, left=True)) from_items.append(FromItem( self.parent.from_clause(), onclause=self.parent.primary_key == self.refs[0], left=True)) else: onclause = self.refs[0] == self.parent.primary_key if self.where is not None: onclause = and_(onclause, self.where) from_items.append(FromItem(self.refs[0].table, onclause=onclause, left=True)) from_items.append(FromItem( self.model.from_clause(), onclause=self.model.primary_key == self.refs[1], left=True)) return tuple(from_items)
def value(item): if not isinstance(item, (Table, Alias, FromItem)): raise Error('FromClause | invalid item: {!r}') return item if isinstance(item, FromItem) else FromItem(item)