def SetUp(self): aliases = { 'i': resource_lex.Lexer('integer').Key(), 'v': resource_lex.Lexer('compound.string.value').Key(), } self.defaults = resource_projection_parser.Parse( '(compound.string:alias=s, floating:alias=f)', aliases=aliases) self.rewrite = resource_filter_scrub.Backend().Rewrite
def get_field(self, field_name, unused_args, unused_kwargs): r"""Returns the value of field_name for string.Formatter.format(). Args: field_name: The format string field name to get in the form name - the value of name in the payload, '' if undefined name?FORMAT - if name is non-empty then re-formats with FORMAT, where {?} is the value of name. For example, if name=NAME then {name?\nname is "{?}".} expands to '\nname is "NAME".'. .a.b.c - the value of a.b.c in the JSON decoded payload contents. For example, '{.errors.reason?[{?}]}' expands to [REASON] if .errors.reason is defined. unused_args: Ignored. unused_kwargs: Ignored. Returns: The value of field_name for string.Formatter.format(). """ field_name = _Expand(field_name) if field_name == '?': return self._value, field_name parts = field_name.split('?', 1) subparts = parts.pop(0).split(':', 1) name = subparts.pop(0) printer_format = subparts.pop(0) if subparts else None recursive_format = parts.pop(0) if parts else None if '.' in name: if name.startswith('.'): # Only check self.content. check_payload_attributes = False name = name[1:] else: # Check the payload attributes first, then self.content. check_payload_attributes = True key = resource_lex.Lexer(name).Key() content = self.content if check_payload_attributes and key: value = self.__dict__.get(key[0], None) if value: content = {key[0]: value} value = resource_property.Get(content, key, None) elif name: value = self.__dict__.get(name, None) else: value = None if not value and not isinstance(value, (int, float)): return '', name if printer_format or not isinstance( value, (six.text_type, six.binary_type, float) + six.integer_types): buf = io.StringIO() resource_printer.Print(value, printer_format or 'default', out=buf, single=True) value = buf.getvalue().strip() if recursive_format: self._value = value value = self.format(_Expand(recursive_format)) # pytype: disable=wrong-arg-types return value, name
def Run(self, args): if args.json_file: with open(args.json_file, 'r') as f: resources = json.load(f) else: resources = json.load(sys.stdin) # TODO(gsfowler): Drop this if when the --aggregate global flag lands. if args.aggregate: key = resource_lex.Lexer(args.aggregate).Key() resources = Aggregator(resources, key) # TODO(gsfowler): Return resources here when the --filter global flag lands. if not args.format: args.format = 'json' if not args.filter: return resources select = resource_filter.Compile(args.filter).Evaluate filtered_resources = [] if resource_property.IsListLike(resources): for resource in resources: if select(resource): filtered_resources.append(resource) elif select(resources): # treat non-iterable resources as a list of length 1 filtered_resources.append(resources) return filtered_resources
def _AddFlattenTap(self): """Taps one or more resource flatteners into self.resources if needed.""" def _Slice(key): """Helper to add one flattened slice tap.""" tap = display_taps.Flattener(key) # Apply the flatteners from left to right so the innermost flattener # flattens the leftmost slice. The outer flatteners can then access # the flattened keys to the left. self._resources = peek_iterable.Tapper(self._resources, tap) keys = self._GetFlag('flatten') if not keys: return for key in keys: flattened_key = [] sliced = False for k in resource_lex.Lexer(key).Key(): if k is None: sliced = True _Slice(flattened_key) else: sliced = False flattened_key.append(k) if not sliced: _Slice(flattened_key)
def SetUp(self): symbols = {'len': len, 'test_transform': lambda x: 'test'} aliases = {'y': resource_lex.Lexer('a.b.c').KeyWithAttribute()} self.defaults = resource_projection_parser.Parse( '(compound.string:alias=s, floating:alias=z)', aliases=aliases, symbols=symbols)
def Parse(self, expression, aliases=None): """Parses a resource list filter expression. This is a hand-rolled recursive descent parser based directly on the left-factorized BNF grammar in the file docstring. The parser is not thread safe. Each thread should use distinct _Parser objects. Args: expression: A resource list filter expression string. aliases: Resource key alias dictionary. Raises: ExpressionSyntaxError: The expression has a syntax error. Returns: tree: The backend expression tree. """ self._lex = resource_lex.Lexer(expression, aliases=aliases) tree = self._ParseExpr() if not self._lex.EndOfInput(): raise resource_exceptions.ExpressionSyntaxError( 'Unexpected tokens [{0}] in expression.'.format( self._lex.Annotate())) self._lex = None return tree or self._backend.ExprTRUE()
def get_field(self, field_name, unused_args, unused_kwargs): r"""Returns the value of field_name for string.Formatter.format(). Args: field_name: The format string field name to get in the form name - the value of name in the payload, '' if undefined name?FORMAT - if name is non-empty then re-formats with FORMAT, where {?} is the value of name. For example, if name=NAME then {name?\nname is "{?}".} expands to '\nname is "NAME".'. ':' may not appear in FORMAT, use {?COLON?} instead. .a.b.c - the value of a.b.c in the JSON decoded payload contents. For example, '{.errors.reason?[{?}]}' expands to [REASON] if .errors.reason is defined. unused_args: Ignored. unused_kwargs: Ignored. Returns: The value of field_name for string.Formatter.format(). """ if field_name.startswith('?'): if field_name == '?': return self._value, field_name if field_name == '?COLON?': return ':', field_name parts = field_name.split('?', 1) name = parts.pop(0) fmt = parts.pop(0) if parts else None if '.' in name: if name.startswith('.'): # Only check self.content. check_payload_attributes = False name = name[1:] else: # Check the payload attributes first, then self.content. check_payload_attributes = True key = resource_lex.Lexer(name).Key() content = self.content if check_payload_attributes and key: value = self.__dict__.get(key[0], None) if value: content = {key[0]: value} value = resource_property.Get(content, key, '') elif name: value = self.__dict__.get(name, '') else: value = '' if not value and not isinstance(value, (int, float)): return '', name if not isinstance(value, (basestring, int, float)): buf = StringIO.StringIO() resource_printer.Print(value, 'default', out=buf, single=True) value = buf.getvalue().strip() if fmt: self._value = value value = self.format(fmt) return value, name
def Parenthesize(self, expression): """Returns expression enclosed in (...) if it contains AND/OR.""" # Check for unparenthesized AND|OR. lex = resource_lex.Lexer(expression) while True: tok = lex.Token(' ()', balance_parens=True) if not tok: break if tok in ['AND', 'OR']: return '({expression})'.format(expression=expression) return expression
def RunSubTest(self, fun, expression, aliases=None, annotate=None, **kwargs): if aliases: defaults = resource_projection_spec.ProjectionSpec(None, aliases=aliases) else: defaults = None lex = resource_lex.Lexer(expression, defaults=defaults) try: actual = getattr(lex, fun)(**kwargs) finally: if annotate is not None: self.AddFollowOnTest('annotate', annotate, lex.Annotate) return actual
def Parse(self, expression=None): """Parse a projection expression. An empty projection is OK. Args: expression: The resource projection expression string. Raises: ExpressionSyntaxError: The expression has a syntax error. Returns: A ProjectionSpec for the expression. """ self._root = self._projection.GetRoot() if not self._root: self._root = self._Tree(self._Attribute(self._projection.DEFAULT)) self._projection.SetRoot(self._root) self._projection.SetEmpty( self._Tree(self._Attribute(self._projection.PROJECT))) if expression: self._lex = resource_lex.Lexer(expression, self._projection) defaults = False self.__key_attributes_only = False while self._lex.SkipSpace(): if self._lex.IsCharacter('('): if not self.__key_attributes_only: defaults = False self._projection.Defaults() self._ParseKeys() if self.__key_attributes_only: self.__key_attributes_only = False self._Reorder() elif self._lex.IsCharacter('['): self._ParseAttributes() elif self._lex.IsCharacter(':'): self.__key_attributes_only = True self.__key_order_offset = 0 else: here = self._lex.GetPosition() name = self._lex.Token(':([') # type: str if not name.isalpha(): raise resource_exceptions.ExpressionSyntaxError( 'Name expected [{0}].'.format( self._lex.Annotate(here))) self._projection.SetName(name) defaults = True self._lex = None if defaults: self._projection.Defaults() return self._projection
def _AddFlattenTap(self): """Taps one or more resource flatteners into self.resources if needed.""" keys = self._GetFlag('flatten') if not keys: return for key in keys: flattened_key = [] for k in resource_lex.Lexer(key).Key(): if k is None: # None represents a [] slice in resource keys. tap = display_taps.Flattener(flattened_key) # Apply the flatteners from left to right so the innermost flattener # flattens the leftmost slice. The outer flatteners can then access # the flattened keys to the left. self._resources = peek_iterable.Tapper(self._resources, tap) else: flattened_key.append(k)
def Parse(self, expression=None): """Parse a projection expression. An empty projection is OK. Args: expression: The resource projection expression string. Raises: SyntaxError: The expression has a syntax error. Returns: A ProjectionSpec for the expression. """ self._root = self._projection.GetRoot() if not self._root: self._root = self._Tree(self._Attribute(self._projection.DEFAULT)) self._projection.SetRoot(self._root) self._projection.SetEmpty( self._Tree(self._Attribute(self._projection.PROJECT))) if expression: self._lex = resource_lex.Lexer(expression, aliases=self._projection.aliases) while self._lex.SkipSpace(): self.__key_attributes_only = self._lex.IsCharacter(':') if self._lex.IsCharacter('('): if not self.__key_attributes_only: self._projection.Defaults() self._ordinal = 0 self._ParseKeys() elif self._lex.IsCharacter('['): self._ParseAttributes() else: here = self._lex.GetPosition() name = self._lex.Token('([') if not name.isalpha(): raise SyntaxError('Unexpected tokens [{0}].'.format( self._lex.Annotate(here))) self._projection.SetName(name) self._lex = None return self._projection
def _GetSortKeys(self): """Returns the list of --sort-by [(key, reverse)] tuples. Returns: The list of --sort-by [(key, reverse)] tuples, None if --sort-by was not specified. The keys are ordered from highest to lowest precedence. """ if not self._GetFlag('sort_by'): return None keys = [] for name in self._args.sort_by: # ~name reverses the sort for name. if name.startswith('~'): name = name.lstrip('~') reverse = True else: reverse = False # Slices default to the first list element for consistency. name = name.replace('[]', '[0]') keys.append((resource_lex.Lexer(name).Key(), reverse)) return keys
def _GetField(self, name): """Gets the value corresponding to name in self.content or class attributes. If `name` starts with a period, treat it as a key in self.content and get the corresponding value. Otherwise get the value of the class attribute named `name` first and fall back to checking keys in self.content. Args: name (str): The name of the attribute to return the value of. Returns: A tuple where the first value is `name` with any leading periods dropped, and the second value is the value of a class attribute or key in self.content. """ if '.' in name: if name.startswith('.'): # Only check self.content. check_payload_attributes = False name = name[1:] else: # Check the payload attributes first, then self.content. check_payload_attributes = True key = resource_lex.Lexer(name).Key() content = self.content if check_payload_attributes and key: value = self.__dict__.get(key[0], None) if value: content = {key[0]: value} value = resource_property.Get(content, key, None) elif name: value = self.__dict__.get(name, None) else: value = None return name, value
def _GetParsedKey(key): """Returns a parsed key from a dotted key string.""" # pylint: disable=g-import-not-at-top, circular dependency from googlecloudsdk.core.resource import resource_lex return resource_lex.Lexer(key).Key()
def testLexKey(self): def T(expected, expression, aliases=None, annotate=None, exception=None, **kwargs): self.Run(expected, 'Key', expression, aliases=aliases, annotate=annotate, depth=2, exception=exception, **kwargs) aliases = { 'r': resource_lex.Lexer('r.r').KeyWithAttribute(), 'x': resource_lex.Lexer('a.b.c').KeyWithAttribute(), 'y': resource_lex.Lexer('a.b[].c').KeyWithAttribute(), 'z': resource_lex.Lexer('a[123].b.c').KeyWithAttribute(), } # empty expression T([], '') # top level resource expressions T([], '.', annotate='. *HERE*') T([], '.:abc', annotate='. *HERE* :abc') # valid keys T(['a', 'b', 'c'], ' a.b.c next ', annotate=' a.b.c *HERE* next ') T(['a', 'b'], ' a. b next ', annotate=' a. b *HERE* next ') T(['a', 'b', None, 'c'], ' a.b[].c next ', annotate=' a.b[].c *HERE* next ') T(['a', 123, 'b', 'c'], ' a[123].b.c next ', annotate=' a[123].b.c *HERE* next ') T(['a', 'b', 'c'], ' a.b.c .d next ', annotate=' a.b.c *HERE* .d next ') T(['@type'], '@type:a', annotate='@type *HERE* :a') # [x] and x. or .x are equivalent in all combinations T(['a', 'b', 'c'], ' a.b[c] next ', annotate=' a.b[c] *HERE* next ') T(['a', 'b', 'c'], ' a[b].c next ', annotate=' a[b].c *HERE* next ') T(['a', 'b', 'c'], ' a[b][c] next ', annotate=' a[b][c] *HERE* next ') T(['a', 'b', 'c'], ' [a].b.c next ', annotate=' [a].b.c *HERE* next ') T(['a', 'b', 'c'], ' [a].b[c] next ', annotate=' [a].b[c] *HERE* next ') T(['a', 'b', 'c'], ' [a][b].c next ', annotate=' [a][b].c *HERE* next ') T(['a', 'b', 'c'], ' [a][b][c] next ', annotate=' [a][b][c] *HERE* next ') # aliases T(['r', 'r'], ' r next ', aliases=aliases, annotate=' r *HERE* next ') T(['r'], ' r() next ', aliases=aliases, annotate=' r *HERE* () next ') T(['a', 'b', 'c'], ' x next ', aliases=aliases, annotate=' x *HERE* next ') T(['a', 'b', 'c', 'b'], ' x. b next ', aliases=aliases, annotate=' x. b *HERE* next ') T(['a', 'b', None, 'c'], ' y next ', aliases=aliases, annotate=' y *HERE* next ') T(['a', 123, 'b', 'c'], ' z next ', aliases=aliases, annotate=' z *HERE* next ') T(['a', 'b', 'c'], ' x .d next ', aliases=aliases, annotate=' x *HERE* .d next ') # terminators T(['a', 'b'], ' a.b(c ', annotate=' a.b *HERE* (c ') T(['a', 'b'], ' a.b)c ', annotate=' a.b *HERE* )c ') T(['a', 'b'], ' a.b{c ', annotate=' a.b *HERE* {c ') T(['a', 'b'], ' a.b}c ', annotate=' a.b *HERE* }c ') T(['a', 'b'], ' a.b<c ', annotate=' a.b *HERE* <c ') T(['a', 'b'], ' a.b>c ', annotate=' a.b *HERE* >c ') T(['a', 'b'], ' a.b=c ', annotate=' a.b *HERE* =c ') T(['a', 'b'], ' a.b!c ', annotate=' a.b *HERE* !c ') T(['a', 'b'], ' a.b+c ', annotate=' a.b *HERE* +c ') T(['a', 'b'], ' a.b*c ', annotate=' a.b *HERE* *c ') T(['a', 'b'], ' a.b/c ', annotate=' a.b *HERE* /c ') T(['a', 'b'], ' a.b%c ', annotate=' a.b *HERE* %c ') T(['a', 'b_c'], ' a.b_c ', annotate=' a.b_c *HERE* ') T([None], ' []a cd', annotate=' [] *HERE* a cd') T([1, 'a'], ' [1].a cd', annotate=' [1].a *HERE* cd') # end of input T(['a'], 'a', annotate='a *HERE*') T(['a', None], 'a[]', annotate='a[] *HERE*') # exceptions T(None, '..', exception=resource_exceptions.ExpressionSyntaxError, annotate='. *HERE* .') T(None, ' a"b cd', exception=resource_exceptions.ExpressionSyntaxError, annotate='*HERE* a"b cd') T(None, ' a.', exception=resource_exceptions.ExpressionSyntaxError, annotate=' a. *HERE*') T(None, ' .a cd', exception=resource_exceptions.ExpressionSyntaxError, annotate=' . *HERE* a cd') T(None, ' a..b cd', exception=resource_exceptions.ExpressionSyntaxError, annotate=' a. *HERE* .b cd') T(None, ' a[ cd', exception=resource_exceptions.ExpressionSyntaxError, annotate=' a[ cd *HERE*') T(None, ' a] cd', exception=resource_exceptions.ExpressionSyntaxError, annotate=' a] *HERE* cd') T(None, '#type:a', exception=resource_exceptions.ExpressionSyntaxError, annotate='*HERE* #type:a')
class ResourceFilterTest(subtests.Base, sdk_test_base.WithLogCapture): _ALIASES = { 'i': resource_lex.Lexer('integer').KeyWithAttribute(), 'v': resource_lex.Lexer('compound.string.value').KeyWithAttribute(), } def SetUp(self): self.resource = None self.StartObjectPatch(times, 'Now', return_value=times.ParseDateTime( '2016-11-11T12:34:56.789-04:00')) def SetResource(self, resource): """Sets the resource for the next set of subtests.""" self.resource = resource def RunSubTest(self, expression, deprecated=False): def _Error(resource=None): """Always raises ValueError for testing. Args: resource: The resource object. Raises: ValueError: Always for testing. """ _ = resource raise ValueError('Transform function value error.') default_symbols = { 'date': resource_transform.TransformDate, 'len': lambda r, x=None: resource_transform.TransformLen(x or r), } defaults = resource_projection_parser.Parse( '(compound.string:alias=s, floating:alias=f)', symbols=default_symbols, aliases=self._ALIASES) symbols = { 'error': _Error, # 'error' not a magic name. } defaults = resource_projection_spec.ProjectionSpec( defaults=defaults, symbols=symbols) evaluate = resource_filter.Compile(expression, defaults=defaults).Evaluate if isinstance(self.resource, list): results = [] for r in self.resource: results.append(evaluate(r)) return results actual = evaluate(self.resource) err = self.GetErr() self.ClearErr() warning = ('WARNING: --filter : operator evaluation is changing for ' 'consistency across Google APIs.') if err and not deprecated: self.fail('Error [%s] not expected.' % err) elif not err and deprecated: self.fail('Warning [%s] expected.' % warning) elif err and deprecated and warning not in err: self.fail('Warning [%s] expected but got [%s].' % (warning, err)) return actual def testResourceFilter(self): def T(expected, expression, deprecated=False, exception=None): if exception is None and expected is None: exception = resource_exceptions.ExpressionSyntaxError self.Run(expected, expression, deprecated=deprecated, depth=2, exception=exception) self.SetResource(Resource()) # empty expression T(True, '') # integer terms T(False, 'integer:3') T(True, 'integer:2') T(False, 'integer:1') T(False, 'integer:-1') T(True, '-integer:3') T(False, '-integer:2') T(True, '-integer:1') T(True, '-integer:-1') T(True, 'NOT integer:3') T(False, 'NOT integer:2') T(True, 'NOT integer:1') T(True, 'NOT integer:-1') T(False, 'integer=3') T(True, 'integer=2') T(False, 'integer=1') T(False, 'integer=-1') T(True, '-integer=3') T(False, '-integer=2') T(True, '-integer=1') T(True, '-integer=-1') T(True, 'NOT integer=3') T(False, 'NOT integer=2') T(True, 'NOT integer=1') T(True, 'NOT integer=-1') T(True, 'integer<3') T(False, 'integer<2') T(False, 'integer<1') T(False, 'integer<-1') T(False, '-integer<3') T(True, '-integer<2') T(True, '-integer<1') T(True, '-integer<-1') T(False, 'NOT integer<3') T(True, 'NOT integer<2') T(True, 'NOT integer<1') T(True, 'NOT integer<-1') T(True, 'integer<=3') T(True, 'integer<=2') T(False, 'integer<=1') T(False, 'integer<=-1') T(False, '-integer<=3') T(False, '-integer<=2') T(True, '-integer<=1') T(True, '-integer<=-1') T(False, 'NOT integer<=3') T(False, 'NOT integer<=2') T(True, 'NOT integer<=1') T(True, 'NOT integer<=-1') T(False, 'integer>=3') T(True, 'integer>=2') T(True, 'integer>=1') T(True, 'integer>=-1') T(True, '-integer>=3') T(False, '-integer>=2') T(False, '-integer>=1') T(False, '-integer>=-1') T(True, 'NOT integer>=3') T(False, 'NOT integer>=2') T(False, 'NOT integer>=1') T(False, 'NOT integer>=-1') T(False, 'integer>3') T(False, 'integer>2') T(True, 'integer>1') T(True, 'integer>-1') T(True, '-integer>3') T(True, '-integer>2') T(False, '-integer>1') T(False, '-integer>-1') T(True, 'NOT integer>3') T(True, 'NOT integer>2') T(False, 'NOT integer>1') T(False, 'NOT integer>-1') T(True, 'integer!=3') T(False, 'integer!=2') T(True, 'integer!=1') T(True, 'integer!=-1') T(False, '-integer!=3') T(True, '-integer!=2') T(False, '-integer!=1') T(False, '-integer!=-1') T(False, 'NOT integer!=3') T(True, 'NOT integer!=2') T(False, 'NOT integer!=1') T(False, 'NOT integer!=-1') # numeric operand mismatches T(False, 'string > 1.23') T(True, 'integer > 1.23') # Boolean terms T(False, 'none:0') T(False, 'none:False') T(False, 'none:false') T(False, 'none:1') T(False, 'none:True') T(False, 'none:true') T(True, 'false:0') T(True, 'false:False') T(True, 'false:false') T(False, 'false:1') T(False, 'false:True') T(False, 'false:true') T(False, 'true:0') T(False, 'true:False') T(False, 'true:false') T(True, 'true:1') T(True, 'true:True') T(True, 'true:true') T(False, 'none=0') T(False, 'none=False') T(False, 'none=false') T(False, 'none=1') T(False, 'none=True') T(False, 'none=true') T(True, 'false=0') T(True, 'false=False') T(True, 'false=false') T(False, 'false=1') T(False, 'false=True') T(False, 'false=true') T(False, 'true=0') T(False, 'true=False') T(False, 'true=false') T(True, 'true=1') T(True, 'true=True') T(True, 'true=true') # case sensitive equality T(False, 'lower=ing') T(False, 'lower=Str') T(True, 'lower=string') T(True, 'lower=String') T(True, 'lower=STRING') T(False, 'mixed=ing') T(False, 'mixed=Str') T(True, 'mixed=String') T(True, 'mixed=StrIng') T(True, 'mixed=STRING') T(False, 'upper=ing') T(False, 'upper=Str') T(True, 'upper=string') T(True, 'upper=String') T(True, 'upper=STRING') T(False, 'undefined=String') T(False, 'undefined=STRING') T(False, 'undefined=string') T(False, 'undefined=Str') T(False, 'undefined=ing') # case insensitive : string match T(True, 'lower:String') T(True, 'lower:STRING') T(True, 'lower:string') T(True, 'lower:Str', deprecated=True) T(True, 'lower:ing', deprecated=True) T(True, 'mixed:STRING') T(True, 'mixed:Str', deprecated=True) T(True, 'mixed:String') T(True, 'mixed:ing', deprecated=True) T(True, 'mixed:string') T(True, 'upper:STRING') T(True, 'upper:Str', deprecated=True) T(True, 'upper:String') T(True, 'upper:ing', deprecated=True) T(True, 'upper:string') T(False, 'undefined:STRING') T(False, 'undefined:Str') T(False, 'undefined:String') T(False, 'undefined:ing') T(False, 'undefined:string') # case insensitive : string comma separated list match T(True, 'lower:(string, error)') T(True, 'lower:(String, Error)') T(False, 'lower:(no, match)') T(False, 'lower:(No, Match)') T(True, 'mixed:(string, error)') T(True, 'mixed:(String, Error)') T(False, 'mixed:(no, match)') T(False, 'mixed:(No, Match)') T(True, 'upper:(string, error)') T(True, 'upper:(String, Error)') T(False, 'upper:(no, match)') T(False, 'upper:(No, Match)') T(False, 'undefined:(string, error)') T(False, 'undefined:(String, Error)') T(False, 'undefined:(no, match)') T(False, 'undefined:(No, Match)') # case insensitive : string space separated list match T(True, 'lower:(string error)') T(True, 'lower:(String Error)') T(False, 'lower:(no match)') T(False, 'lower:(No Match)') T(True, 'mixed:(string error)') T(True, 'mixed:(String Error)') T(False, 'mixed:(no match)') T(False, 'mixed:(No Match)') T(True, 'upper:(string error)') T(True, 'upper:(String Error)') T(False, 'upper:(no match)') T(False, 'upper:(No Match)') T(False, 'undefined:(string error)') T(False, 'undefined:(String Error)') T(False, 'undefined:(no match)') T(False, 'undefined:(No Match)') # case insensitive : string OR separated list match T(True, 'lower:(string OR error)') T(True, 'lower:(String OR Error)') T(False, 'lower:(no OR match)') T(False, 'lower:(No OR Match)') T(True, 'mixed:(string OR error)') T(True, 'mixed:(String OR Error)') T(False, 'mixed:(no OR match)') T(False, 'mixed:(No OR Match)') T(True, 'upper:(string OR error)') T(True, 'upper:(String OR Error)') T(False, 'upper:(no OR match)') T(False, 'upper:(No OR Match)') T(False, 'logical:(string OR error)') T(False, 'logical:(String OR Error)') T(True, 'logical:(abc OR XYZ)') T(True, 'logical:(ABC OR xyz)') T(True, 'logical:(a* OR *Z)') T(False, 'logical:(aaa OR X*)', deprecated=True) T(True, 'logical:(aaa OR *Z)', deprecated=True) T(False, 'undefined:(string OR error)') T(False, 'undefined:(String OR Error)') T(False, 'undefined:(no OR match)') T(False, 'undefined:(No OR Match)') # anchored prefix/suffix case insensitive : string match T(True, 'lower:*') T(True, 'lower:*ing', deprecated=True) T(True, 'lower:S*ing', deprecated=True) T(True, 'lower:STR*ING', deprecated=True) T(True, 'lower:Str*ing', deprecated=True) T(True, 'lower:s*g', deprecated=True) T(True, 'lower:str*') T(True, 'mixed:*') T(True, 'mixed:*ing', deprecated=True) T(True, 'mixed:S*ing', deprecated=True) T(True, 'mixed:STR*ING', deprecated=True) T(True, 'mixed:Str*ing', deprecated=True) T(True, 'mixed:s*g', deprecated=True) T(True, 'mixed:str*') T(True, 'upper:*') T(True, 'upper:*ing', deprecated=True) T(True, 'upper:S*ing', deprecated=True) T(True, 'upper:STR*ING', deprecated=True) T(True, 'upper:Str*ing', deprecated=True) T(True, 'upper:s*g', deprecated=True) T(True, 'upper:str*') T(False, 'undefined:*') T(False, 'undefined:*ing') T(False, 'undefined:STR*ING') T(False, 'undefined:Str*ing') T(False, 'undefined:s*g') T(False, 'undefined:str*') # _Has() docstring examples T(True, 'subject:abc*xyz', deprecated=True) T(True, 'subject:abc*') T(True, 'subject:abc', deprecated=True) T(False, 'subject:*abc') T(False, 'subject:pdq*') T(True, 'subject:pdq', deprecated=True) T(False, 'subject:*pdq') T(True, 'subject:*') T(False, 'none:*') T(False, 'subject:xyz*') T(True, 'subject:xyz', deprecated=True) T(True, 'subject:*xyz', deprecated=True) # ~ regex match where ^ matches start of value, $ matches end of value T(True, 'lower~[a-z]') T(False, 'lower~[A-Z]') T(True, 'lower~ing') T(True, 'lower~ing$') T(True, 'lower~s.*g') T(True, 'lower~^s.*g') T(True, 'lower~^s.*g$') T(True, 'lower~s.*ing') T(True, 'lower~str') T(True, 'lower~^str') T(True, 'lower~st.*ng') T(True, 'lower~^st.*ng') T(True, 'lower~st.*ng$') T(True, 'lower~^st.*ng$') T(False, 'lower~STRING') T(False, 'lower~^STRING') T(False, 'lower~STRING$') T(False, 'lower~^STRING$') T(True, 'f~3.14') # !~ regex not match where ^ matches start of value, $ matches end of value T(False, 'lower!~[a-z]') T(True, 'lower!~[A-Z]') T(False, 'lower!~ing') T(False, 'lower!~ing$') T(False, 'lower!~s.*g') T(False, 'lower!~^s.*g') T(False, 'lower!~^s.*g$') T(False, 'lower!~s.*ing') T(False, 'lower!~str') T(False, 'lower!~^str') T(False, 'lower!~st.*ng') T(False, 'lower!~^st.*ng') T(False, 'lower!~st.*ng$') T(False, 'lower!~^st.*ng$') T(True, 'lower!~STRING') T(True, 'lower!~^STRING') T(True, 'lower!~STRING$') T(True, 'lower!~^STRING$') # "..." string operands for : T(True, 'lower:"STRING"') T(True, 'lower:"Str"', deprecated=True) T(True, 'lower:"Str*"') T(True, 'lower:"ri"', deprecated=True) T(True, 'lower:"rI"', deprecated=True) T(True, 'lower:"StrIng"') T(True, 'lower:"String"') T(True, 'lower:"ing"', deprecated=True) T(True, 'lower:"*ing"', deprecated=True) T(True, 'lower:"string"') T(True, 'mixed:"STRING"') T(True, 'mixed:"Str"', deprecated=True) T(True, 'mixed:"Str*"') T(True, 'mixed:"StrIng"') T(True, 'mixed:"String"') T(True, 'mixed:"ing"', deprecated=True) T(True, 'mixed:"*ing"', deprecated=True) T(True, 'upper:"STRING"') T(True, 'upper:"Str"', deprecated=True) T(True, 'upper:"Str*"') T(True, 'upper:"String"') T(True, 'upper:"ing"', deprecated=True) T(True, 'upper:"*ing"', deprecated=True) T(True, 'upper:"string"') T(False, 'undefined:"STRING"') T(False, 'undefined:"Str"') T(False, 'undefined:"Str"') T(False, 'undefined:"String"') T(False, 'undefined:"ing"') T(False, 'undefined:"ing"') T(False, 'undefined:"string"') T(True, 'compound.string.value:"Compound String"') T(True, 'compound.string.value:"Compound string"') T(True, 'compound.string.value:"c*g"', deprecated=True) T(True, 'compound.string.value:"compound string"') # "..." string operands for = T(False, 'lower="ing"') T(False, 'lower="str"') T(False, 'lower="Str"') T(True, 'lower="string"') T(True, 'lower="String"') T(True, 'lower="StrIng"') T(True, 'lower="STRING"') T(False, 'mixed="ing"') T(False, 'mixed="Ing"') T(False, 'mixed="Str"') T(True, 'mixed="String"') T(True, 'mixed="StrIng"') T(True, 'mixed="STRING"') T(False, 'upper="ing"') T(False, 'upper="Str"') T(True, 'upper="string"') T(True, 'upper="String"') T(True, 'upper="StrIng"') T(True, 'upper="STRING"') T(False, 'undefined="ing"') T(False, 'undefined="Str"') T(False, 'undefined="string"') T(False, 'undefined="String"') T(False, 'undefined="STRING"') T(True, 'compound.string.value="compound string"') T(True, 'compound.string.value="Compound string"') T(True, 'compound.string.value="Compound String"') # (number, string) X (list, dict) : tests T(True, 'compound.number.array:1') T(True, 'compound.number.array:3.14') T(False, 'compound.number.array:5') T(False, 'compound.number.array:abc') T(False, 'compound.number.array:Abc') T(True, 'compound.number.dictionary:1') T(True, 'compound.number.dictionary:3.14') T(False, 'compound.number.dictionary:5') T(True, 'compound.number.dictionary:Abc') T(True, 'compound.number.dictionary:abc') T(False, 'compound.string.array:1') T(False, 'compound.string.array:3.14') T(False, 'compound.string.array:5') T(True, 'compound.string.array:abc') T(True, 'compound.string.array:Abc') T(True, 'compound.string.array:ab', deprecated=True) T(True, 'compound.string.array:b', deprecated=True) T(True, 'compound.string.array:b', deprecated=True) T(True, 'compound.string.array:ab*') T(True, 'compound.string.array:*bC', deprecated=True) T(True, 'compound.string.dictionary:3.14') T(False, 'compound.string.dictionary:5') T(True, 'compound.string.dictionary:Abc') T(True, 'compound.string.dictionary:abc') T(True, 'compound.string.dictionary:ab*') # (...) set operands for : T(True, 'compound.string.array:(ab)', deprecated=True) T(True, 'compound.string.array:(ab*)') T(True, 'compound.string.dictionary:(ab*)') T(True, 'compound.number.array:(1)') T(True, 'compound.number.array:(aaa,1,zzz)') T(True, 'compound.number.array:(aaa, 1, zzz)') T(True, 'compound.number.array:(aaa 1 zzz)') T(True, 'compound.number.array:(aaa\n1\nzzz)') T(True, 'compound.number.array:( aaa 1 zzz )') T(False, 'compound.number.array:( aaa 5 zzz )') T(False, 'compound.number.array:( aaa zzz )') T(True, 'compound.number.dictionary:(1)') T(True, 'compound.number.dictionary:(aaa,1,zzz)') T(True, 'compound.number.dictionary:(aaa, 1, zzz)') T(True, 'compound.number.dictionary:(aaa 1 zzz)') T(True, 'compound.number.dictionary:(aaa\n1\nzzz)') T(True, 'compound.number.dictionary:( aaa 1 zzz )') T(False, 'compound.number.dictionary:( aaa 5 zzz )') T(False, 'compound.number.dictionary:( aaa zzz )') T(True, 'compound.string.array:(abc)') T(True, 'compound.string.array:(aaa,abc,zzz)') T(True, 'compound.string.array:(aaa, abc, zzz)') T(True, 'compound.string.array:(aaa abc zzz)') T(True, 'compound.string.array:(aaa\nabc\nzzz)') T(True, 'compound.string.array:( aaa abc zzz )') T(False, 'compound.string.array:( aaa zzz )') T(True, 'compound.string.dictionary:(abc)') T(True, 'compound.string.dictionary:(aaa,abc,zzz)') T(True, 'compound.string.dictionary:(aaa, abc, zzz)') T(True, 'compound.string.dictionary:(aaa abc zzz)') T(True, 'compound.string.dictionary:(aaa\nabc\nzzz)') T(True, 'compound.string.dictionary:( aaa abc zzz )') T(False, 'compound.string.dictionary:( aaa zzz )') # string X (list, dict) = tests T(False, 'compound.string.array=ab') T(False, 'compound.string.dictionary=ab') # (...) set operands for = T(False, 'compound.string.array=(ab)') T(False, 'compound.string.dictionary=(ab)') T(True, 'compound.number.array=(1)') T(True, 'compound.number.array=(aaa,1,zzz)') T(True, 'compound.number.array=(aaa, 1, zzz)') T(True, 'compound.number.array=(aaa 1 zzz)') T(True, 'compound.number.array=(aaa\n1\nzzz)') T(True, 'compound.number.array=( aaa 1 zzz )') T(False, 'compound.number.array=(3)', deprecated=True) T(False, 'compound.number.array=(aaa,3,zzz)', deprecated=True) T(False, 'compound.number.array=(aaa, 3, zzz)', deprecated=True) T(False, 'compound.number.array=(aaa 3 zzz)', deprecated=True) T(False, 'compound.number.array=(aaa\n3\nzzz)', deprecated=True) T(False, 'compound.number.array=( aaa 3 zzz )', deprecated=True) T(False, 'compound.number.array=( aaa 5 zzz )') T(False, 'compound.number.array=( aaa zzz )') T(True, 'compound.number.dictionary=(1)') T(True, 'compound.number.dictionary=(aaa,1,zzz)') T(True, 'compound.number.dictionary=(aaa, 1, zzz)') T(True, 'compound.number.dictionary=(aaa 1 zzz)') T(True, 'compound.number.dictionary=(aaa\n1\nzzz)') T(True, 'compound.number.dictionary=( aaa 1 zzz )') T(False, 'compound.number.dictionary=( aaa 5 zzz )') T(False, 'compound.number.dictionary=( aaa zzz )') T(True, 'compound.string.array=(abc)') T(True, 'compound.string.array=(aaa,abc,zzz)') T(True, 'compound.string.array=(aaa, abc, zzz)') T(True, 'compound.string.array=(aaa abc zzz)') T(True, 'compound.string.array=(aaa\nabc\nzzz)') T(True, 'compound.string.array=( aaa abc zzz )') T(False, 'compound.string.array=(ab)') T(False, 'compound.string.array=(aaa,ab,zzz)') T(False, 'compound.string.array=(aaa, ab, zzz)') T(False, 'compound.string.array=(aaa ab zzz)') T(False, 'compound.string.array=(aaa\nab\nzzz)') T(False, 'compound.string.array=( aaa ab zzz )') T(False, 'compound.string.array=( aaa zzz )') T(True, 'compound.string.dictionary=(abc)') T(True, 'compound.string.dictionary=(aaa,abc,zzz)') T(True, 'compound.string.dictionary=(aaa, abc, zzz)') T(True, 'compound.string.dictionary=(aaa abc zzz)') T(True, 'compound.string.dictionary=(aaa\nabc\nzzz)') T(True, 'compound.string.dictionary=( aaa abc zzz )') T(False, 'compound.string.dictionary=(ab)') T(False, 'compound.string.dictionary=(aaa,ab,zzz)') T(False, 'compound.string.dictionary=(aaa, ab, zzz)') T(False, 'compound.string.dictionary=(aaa ab zzz)') T(False, 'compound.string.dictionary=(aaa\nab\nzzz)') T(False, 'compound.string.dictionary=( aaa ab zzz )') T(False, 'compound.string.dictionary=( aaa zzz )') # AND precedence over OR - see the OnePlatform comment below # OR precedence over adjacent conjunction T(False, 'integer:0 integer:0 OR integer:0') T(False, '(integer:0 integer:0) OR integer:0') T(False, 'integer:0 AND (integer:0 OR integer:0)') T(False, 'integer:0 integer:0 OR integer:2') T(True, '(integer:0 integer:0) OR integer:2') T(False, 'integer:0 AND (integer:0 OR integer:2)') T(False, 'integer:0 integer:2 OR integer:0') T(False, '(integer:0 integer:2) OR integer:0') T(False, 'integer:0 AND (integer:2 OR integer:0)') T(False, 'integer:0 integer:2 OR integer:2') T(True, '(integer:0 integer:2) OR integer:2') T(False, 'integer:0 AND (integer:2 OR integer:2)') T(False, 'integer:2 integer:0 OR integer:0') T(False, '(integer:2 integer:0) OR integer:0') T(False, 'integer:2 AND (integer:0 OR integer:0)') T(True, 'integer:2 integer:0 OR integer:2') T(True, '(integer:2 integer:0) OR integer:2') T(True, 'integer:2 AND (integer:0 OR integer:2)') T(True, 'integer:2 integer:2 OR integer:0') T(True, '(integer:2 integer:2) OR integer:0') T(True, 'integer:2 AND (integer:2 OR integer:0)') T(True, 'integer:2 integer:2 OR integer:2') T(True, '(integer:2 integer:2) OR integer:2') T(True, 'integer:2 AND (integer:2 OR integer:2)') T(False, 'integer:0 OR integer:0 integer:0') T(False, 'integer:0 OR (integer:0 integer:0)') T(False, '(integer:0 OR integer:0) AND integer:0') T(False, 'integer:0 OR integer:0 integer:2') T(False, 'integer:0 OR (integer:0 integer:2)') T(False, '(integer:0 OR integer:0) AND integer:2') T(False, 'integer:0 OR integer:2 integer:0') T(False, 'integer:0 OR (integer:2 integer:0)') T(False, '(integer:0 OR integer:2) AND integer:0') T(True, 'integer:0 OR integer:2 integer:2') T(True, 'integer:0 OR (integer:2 integer:2)') T(True, '(integer:0 OR integer:2) AND integer:2') T(False, 'integer:2 OR integer:0 integer:0') T(True, 'integer:2 OR (integer:0 integer:0)') T(False, '(integer:2 OR integer:0) AND integer:0') T(True, 'integer:2 OR integer:0 integer:2') T(True, 'integer:2 OR (integer:0 integer:2)') T(True, '(integer:2 OR integer:0) AND integer:2') T(False, 'integer:2 OR integer:2 integer:0') T(True, 'integer:2 OR (integer:2 integer:0)') T(False, '(integer:2 OR integer:2) AND integer:0') T(True, 'integer:2 OR integer:2 integer:2') T(True, 'integer:2 OR (integer:2 integer:2)') T(True, '(integer:2 OR integer:2) AND integer:2') T(True, 'integer=2 OR floating=3.14') T(True, 'integer=2 floating=3.14') T(True, 'integer=2 ( floating=3.14 )') T(True, '( integer=2 ) floating=3.14') T(True, '( integer=2 floating=3.14 )') # AND, NOT OR and (...) combinations T(True, 't:1 AND ( t:1 OR t:1 )') T(True, 't:1 AND ( t:1 OR t:0 )') T(True, 't:1 AND ( t:0 OR t:1 )') T(False, 't:1 AND ( t:0 OR t:0 )') T(False, 't:0 AND ( t:1 OR t:1 )') T(False, 't:0 AND ( t:1 OR t:0 )') T(False, 't:0 AND ( t:0 OR t:1 )') T(False, 't:0 AND ( t:0 OR t:0 )') T(True, '( t:1 OR t:1 ) AND t:1') T(True, '( t:0 OR t:1 ) AND t:1') T(False, '( t:1 OR t:1 ) AND t:0') T(False, '( t:0 OR t:1 ) AND t:0') T(True, '( t:1 OR t:0 ) AND t:1') T(False, '( t:0 OR t:0 ) AND t:1') T(False, '( t:1 OR t:0 ) AND t:0') T(False, '( t:0 OR t:0 ) AND t:0') T(False, 'NOT t:1 AND ( t:1 OR t:1 )') T(False, 'NOT t:1 AND ( t:1 OR t:0 )') T(False, 'NOT t:1 AND ( t:0 OR t:1 )') T(False, 'NOT t:1 AND ( t:0 OR t:0 )') T(True, 'NOT t:0 AND ( t:1 OR t:1 )') T(True, 'NOT t:0 AND ( t:1 OR t:0 )') T(True, 'NOT t:0 AND ( t:0 OR t:1 )') T(False, 'NOT t:0 AND ( t:0 OR t:0 )') T(False, 'NOT ( t:1 OR t:1 ) AND t:1') T(False, 'NOT ( t:0 OR t:1 ) AND t:1') T(False, 'NOT ( t:1 OR t:1 ) AND t:0') T(False, 'NOT ( t:0 OR t:1 ) AND t:0') T(False, 'NOT ( t:1 OR t:0 ) AND t:1') T(True, 'NOT ( t:0 OR t:0 ) AND t:1') T(False, 'NOT ( t:1 OR t:0 ) AND t:0') T(False, 'NOT ( t:0 OR t:0 ) AND t:0') # OnePlatform queries have non-standard OR >>> AND precedence. Cloud SDK # requires parentheses when AND and OR are combined to clarify user intent. # This group adds (...) to achieve standard AND/OR precedence. T(True, '(integer>0 AND floating>0) OR integer>0') T(True, '(integer>0 AND floating>0) OR integer<0') T(True, '(integer>0 AND floating<0) OR integer>0') T(False, '(integer>0 AND floating<0) OR integer<0') T(True, '(integer<0 AND floating>0) OR integer>0') T(False, '(integer<0 AND floating>0) OR integer<0') T(True, '(integer<0 AND floating<0) OR integer>0') T(False, '(integer<0 AND floating<0) OR integer<0') T(True, 'integer>0 OR (integer>0 AND floating>0)') T(True, 'integer<0 OR (integer>0 AND floating>0)') T(True, 'integer>0 OR (integer>0 AND floating<0)') T(False, 'integer<0 OR (integer>0 AND floating<0)') T(True, 'integer>0 OR (integer<0 AND floating>0)') T(False, 'integer<0 OR (integer<0 AND floating>0)') T(True, 'integer>0 OR (integer<0 AND floating<0)') T(False, 'integer<0 OR (integer<0 AND floating<0)') T(True, '(NOT integer>0 AND floating>0) OR integer>0') T(False, '(NOT integer>0 AND floating>0) OR integer<0') T(True, '(NOT integer>0 AND floating<0) OR integer>0') T(False, '(NOT integer>0 AND floating<0) OR integer<0') T(True, '(NOT integer<0 AND floating>0) OR integer>0') T(True, '(NOT integer<0 AND floating>0) OR integer<0') T(True, '(NOT integer<0 AND floating<0) OR integer>0') T(False, '(NOT integer<0 AND floating<0) OR integer<0') T(True, 'NOT integer>0 OR (integer>0 AND floating>0)') T(True, 'NOT integer<0 OR (integer>0 AND floating>0)') T(False, 'NOT integer>0 OR (integer>0 AND floating<0)') T(True, 'NOT integer<0 OR (integer>0 AND floating<0)') T(False, 'NOT integer>0 OR (integer<0 AND floating>0)') T(True, 'NOT integer<0 OR (integer<0 AND floating>0)') T(False, 'NOT integer>0 OR (integer<0 AND floating<0)') T(True, 'NOT integer<0 OR (integer<0 AND floating<0)') T(False, '(integer:0 AND integer:0) OR integer:0') T(True, '(integer:0 AND integer:0) OR integer:2') T(False, '(integer:0 AND integer:2) OR integer:0') T(True, '(integer:0 AND integer:2) OR integer:2') T(False, '(integer:2 AND integer:0) OR integer:0') T(True, '(integer:2 AND integer:0) OR integer:2') T(True, '(integer:2 AND integer:2) OR integer:0') T(True, '(integer:2 AND integer:2) OR integer:2') T(False, 'integer:0 OR (integer:0 AND integer:0)') T(False, 'integer:0 OR (integer:0 AND integer:2)') T(False, 'integer:0 OR (integer:2 AND integer:0)') T(True, 'integer:0 OR (integer:2 AND integer:2)') T(True, 'integer:2 OR (integer:0 AND integer:0)') T(True, 'integer:2 OR (integer:0 AND integer:2)') T(True, 'integer:2 OR (integer:2 AND integer:0)') T(True, 'integer:2 OR (integer:2 AND integer:2)') # This group checks the AND/OR combination detection. T(None, 'integer>0 AND floating>0 OR integer>0') T(None, 'integer>0 AND floating>0 OR integer<0') T(None, 'integer>0 AND floating<0 OR integer>0') T(None, 'integer>0 AND floating<0 OR integer<0') T(None, 'integer<0 AND floating>0 OR integer>0') T(None, 'integer<0 AND floating>0 OR integer<0') T(None, 'integer<0 AND floating<0 OR integer>0') T(None, 'integer<0 AND floating<0 OR integer<0') T(None, 'integer>0 OR integer>0 AND floating>0') T(None, 'integer<0 OR integer>0 AND floating>0') T(None, 'integer>0 OR integer>0 AND floating<0') T(None, 'integer<0 OR integer>0 AND floating<0') T(None, 'integer>0 OR integer<0 AND floating>0') T(None, 'integer<0 OR integer<0 AND floating>0') T(None, 'integer>0 OR integer<0 AND floating<0') T(None, 'integer<0 OR integer<0 AND floating<0') T(None, 'NOT integer>0 AND floating>0 OR integer>0') T(None, 'NOT integer>0 AND floating>0 OR integer<0') T(None, 'NOT integer>0 AND floating<0 OR integer>0') T(None, 'NOT integer>0 AND floating<0 OR integer<0') T(None, 'NOT integer<0 AND floating>0 OR integer>0') T(None, 'NOT integer<0 AND floating>0 OR integer<0') T(None, 'NOT integer<0 AND floating<0 OR integer>0') T(None, 'NOT integer<0 AND floating<0 OR integer<0') T(None, 'NOT integer>0 OR integer>0 AND floating>0') T(None, 'NOT integer<0 OR integer>0 AND floating>0') T(None, 'NOT integer>0 OR integer>0 AND floating<0') T(None, 'NOT integer<0 OR integer>0 AND floating<0') T(None, 'NOT integer>0 OR integer<0 AND floating>0') T(None, 'NOT integer<0 OR integer<0 AND floating>0') T(None, 'NOT integer>0 OR integer<0 AND floating<0') T(None, 'NOT integer<0 OR integer<0 AND floating<0') T(None, 'integer:0 AND integer:0 OR integer:0') T(None, 'integer:0 AND integer:0 OR integer:2') T(None, 'integer:0 AND integer:2 OR integer:0') T(None, 'integer:0 AND integer:2 OR integer:2') T(None, 'integer:2 AND integer:0 OR integer:0') T(None, 'integer:2 AND integer:0 OR integer:2') T(None, 'integer:2 AND integer:2 OR integer:0') T(None, 'integer:2 AND integer:2 OR integer:2') T(None, 'integer:0 OR integer:0 AND integer:0') T(None, 'integer:0 OR integer:0 AND integer:2') T(None, 'integer:0 OR integer:2 AND integer:0') T(None, 'integer:0 OR integer:2 AND integer:2') T(None, 'integer:2 OR integer:0 AND integer:0') T(None, 'integer:2 OR integer:0 AND integer:2') T(None, 'integer:2 OR integer:2 AND integer:0') T(None, 'integer:2 OR integer:2 AND integer:2') # > 1 of the same operator for off-by-1 grammar production bugs T(False, 'integer:1 AND integer:2 AND integer:3') T(True, 'integer>1 AND integer:2 AND integer<3') T(True, 'integer:1 OR integer:2 OR integer:3') T(False, 'integer<1 OR -integer:2 OR integer>3') # (...) nesting T(False, '( t:0 OR t:0 ) AND ( t:0 OR t:0 )') T(False, '( t:0 OR t:0 ) AND ( t:0 OR t:1 )') T(False, '( t:0 OR t:0 ) AND ( t:1 OR t:0 )') T(False, '( t:0 OR t:0 ) AND ( t:1 OR t:1 )') T(False, '( t:0 OR t:1 ) AND ( t:0 OR t:0 )') T(True, '( t:0 OR t:1 ) AND ( t:0 OR t:1 )') T(True, '( t:0 OR t:1 ) AND ( t:1 OR t:0 )') T(True, '( t:0 OR t:1 ) AND ( t:1 OR t:1 )') T(False, '( t:1 OR t:0 ) AND ( t:0 OR t:0 )') T(True, '( t:1 OR t:0 ) AND ( t:0 OR t:1 )') T(True, '( t:1 OR t:0 ) AND ( t:1 OR t:0 )') T(True, '( t:1 OR t:0 ) AND ( t:1 OR t:1 )') T(False, '( t:1 OR t:1 ) AND ( t:0 OR t:0 )') T(True, '( t:1 OR t:1 ) AND ( t:0 OR t:1 )') T(True, '( t:1 OR t:1 ) AND ( t:1 OR t:0 )') T(True, '( t:1 OR t:1 ) AND ( t:1 OR t:1 )') # space combinations T(True, 'integer:2') T(True, 'integer:2 ') T(True, 'integer: 2') T(True, 'integer :2') T(True, ' integer:2') T(True, 'integer: 2 ') T(True, 'integer :2 ') T(True, ' integer:2 ') T(True, 'integer : 2') T(True, ' integer: 2') T(True, ' integer :2') T(True, 'integer : 2 ') T(True, ' integer: 2 ') T(True, ' integer : 2') T(True, ' integer : 2 ') T(False, '-integer:2') T(False, '-integer:2 ') T(False, '-integer: 2') T(False, '-integer :2') T(False, '-integer: 2 ') T(False, '-integer :2 ') T(False, '-integer : 2') T(False, '-integer : 2 ') T(False, ' -integer:2') T(False, ' -integer:2 ') T(False, ' -integer: 2') T(False, ' -integer :2') T(False, ' -integer: 2 ') T(False, ' -integer :2 ') T(False, ' -integer : 2') T(False, ' -integer : 2 ') T(False, '- integer:2') T(False, '- integer:2 ') T(False, '- integer: 2') T(False, '- integer :2') T(False, '- integer: 2 ') T(False, '- integer : 2') T(False, '- integer : 2 ') T(False, ' - integer:2') T(False, ' - integer:2 ') T(False, ' - integer: 2') T(False, ' - integer :2') T(False, ' - integer: 2 ') T(False, ' - integer : 2') T(False, ' - integer : 2 ') T(False, ' - integer:2') T(False, ' - integer:2 ') T(False, ' - integer: 2') T(False, ' - integer :2') T(False, ' - integer:2') T(False, ' - integer: 2 ') T(False, ' - integer :2 ') T(False, ' - integer:2 ') T(False, ' - integer : 2') T(False, ' - integer: 2') T(False, ' - integer :2') T(False, ' - integer : 2 ') T(False, ' - integer: 2 ') T(False, ' - integer : 2') T(False, ' - integer : 2 ') T(False, 'NOT integer:2') T(False, 'NOT integer:2 ') T(False, 'NOT integer: 2') T(False, 'NOT integer :2') T(False, 'NOT integer:2') T(False, 'NOT integer: 2 ') T(False, 'NOT integer :2 ') T(False, 'NOT integer:2 ') T(False, 'NOT integer : 2') T(False, 'NOT integer: 2') T(False, 'NOT integer :2') T(False, 'NOT integer : 2 ') T(False, 'NOT integer: 2 ') T(False, 'NOT integer : 2') T(False, 'NOT integer : 2 ') T(False, ' NOT integer:2') T(False, ' NOT integer:2 ') T(False, ' NOT integer: 2') T(False, ' NOT integer :2') T(False, ' NOT integer:2') T(False, ' NOT integer: 2 ') T(False, ' NOT integer :2 ') T(False, ' NOT integer:2 ') T(False, ' NOT integer : 2') T(False, ' NOT integer: 2') T(False, ' NOT integer :2') T(False, ' NOT integer : 2 ') T(False, ' NOT integer: 2 ') T(False, ' NOT integer : 2') T(False, ' NOT integer : 2 ') # key restrictions T(True, 'lower.len():6') T(True, 'lower.len()=6') T(False, 'lower.len()<6') T(None, 'integer.error()=0', exception=ValueError) # global function restrictions T(4, 'len(junk)') T(True, 'len(junk)=4') T(False, 'len') T(False, 'len (junk)') T(False, 'len OR (junk)') T(False, 'len (abc)') T(True, 'len OR (abc)') T(True, 'len OR abc') # global restrictions T(True, 'abc') T(True, 'pdq') T(True, 'xyz') T(False, 'aaa pdq zzz') T(True, 'aaa OR pdq OR zzz') T(False, 'aaa zzz') T(True, '(abc)') T(True, '(pdq)') T(True, 'abc (pdq)') T(True, '(abc) pdq') T(True, '(abc) (pdq)') # quotes T(True, 'lower="string"') T(True, 'double=a\\"" "\\"z') T(True, 'double=\'a" "z\'') T(True, "single=a\\'' '\\'z") T(True, "single=\"a' 'z\"") T(True, 'v~""') T(True, 'v~" "') T(True, 'v~"" OR i:1') T(True, 'v~"" OR i:2') T(False, 'v~"" AND i:1') T(True, 'v~"" AND i:2') T(True, 'v~"" NOT i:1') T(False, 'v~"" NOT i:2') T(True, 'i:1 OR v~""') T(True, 'i:2 OR v~""') T(False, 'i:1 AND v~""') T(True, 'i:2 AND v~""') T(True, 'NOT i:1 v~""') T(False, 'NOT i:2 v~""') T(True, 'v~" " OR i:1') T(True, 'v~" " OR i:2') T(False, 'v~" " AND i:1') T(True, 'v~" " AND i:2') T(True, 'v~" " NOT i:1') T(False, 'v~" " NOT i:2') # aliases T(True, 'i=2 OR f=3.14') T(True, 'i=2 f=3.14') T(True, 'i=2 ( f=3.14 )') T(True, '( i=2 ) f=3.14') T(True, '( i=2 f=3.14 )') T(False, 's.value="c*g"') T(True, 's.value:"c*g"', deprecated=True) T(False, 's.value="C*g"') T(True, 's.value:"C*g"', deprecated=True) T(True, 's.value="compound string"') T(True, 's.value:"compound string"') T(True, 's.value="Compound string"') T(True, 's.value:"Compound string"') T(True, 's.value="Compound String"') T(True, 's.value:"Compound String"') T(False, 'v="c*g"') T(True, 'v:"c*g"', deprecated=True) T(False, 'v="C*g"') T(True, 'v:"C*g"', deprecated=True) T(True, 'v="compound string"') T(True, 'v:"compound string"') T(True, 'v="Compound string"') T(True, 'v:"Compound string"') T(True, 'v="Compound String"') T(True, 'v:"Compound String"') # datetime T(True, 'timestamp.date(%Y)=2016') T(True, 'timestamp>"January 2016" timestamp<2016-09-01') T(True, 'timestamp<-p1m') T(False, 'timestamp<-p1y') # bad key OK global restriction T(True, '.') T(False, '..') T(False, '...') T(False, '.foo') T(False, 'foo.') T(False, 'foo..bar') # syntax errors T(None, 'AND') T(None, 'NOT') T(None, 'OR') T(None, '..len()') T(None, '.foo.len()') T(None, 'foo..len()') T(None, 'foo..bar.len()') T(None, 'foo.len(') T(None, 'foo.len)') T(None, '(') T(None, ')') T(None, ':') T(None, 'foo:') T(None, ':bar') T(None, ':.foo') T(None, 'foo> (') T(None, 'foo>bar (') T(None, '(') T(None, 'foo )') T(None, 'foo> )') T(None, 'foo>bar )') T(None, ')') T(None, '"') T(None, "'") T(None, 'foo:"bar') T(None, 'foo<(bar)') T(None, 'foo>(bar, baz)') T(None, 'a!b') T(None, 'a!!b') T(None, 'a!:b') T(None, 'a!<b') T(None, 'a!>b') T(None, 'a:!b') T(None, 'a::b') T(None, 'a:=b') T(None, 'a:<b') T(None, 'a:>b') T(None, 'a=!b') T(None, 'a=:b') T(None, 'a==b') T(None, 'a=<b') T(None, 'a=>b') T(None, 'a<!b') T(None, 'a<:b') T(None, 'a<<b') T(None, 'a<>b') T(None, 'a>!b') T(None, 'a>:b') T(None, 'a><b') T(None, 'a>>b') T(None, 'a:"b') T(None, "a:'b") T(None, 'a:\\"b"') T(None, '>1') T(None, 'NOT >1') T(None, 'a.foo()') T(None, 'a:b OR') T(None, 'a:b AND') T(None, 'a:b NOT') T(None, 'a: OR b:1') T(None, 'a: AND b:1') T(None, 'a: NOT b:1') T(None, 'a:b (') T(None, 'a:b )') T(None, 'a:b ()') T(None, 'v:"c*d s*g"') # ~ regex errors. T(None, 'v~*') T(None, 'v~)') # OnePlatform examples. self.SetResource({ 'foo': 'bar', 'obj': { 'x': 10, 'y': 20, }, 'ids': [2, 3, 4], 'required': True, 'ptr': None, 'members': [ { 'name': 'Joe', 'age': 40, }, { 'name': 'Jane', 'age': 50, }, ], }) T(True, 'foo = bar') T(True, 'obj.x = 10') T(True, 'required = true') T(True, 'ptr = null') T(True, 'members.name = Jane') T(True, 'members.age > 40') # Repeated value examples. self.SetResource([ { 'name': 'instance-1', 'networkInterfaces': [ { 'accessConfigs': [ { 'natIP': '23.251.133.75' }, { 'natIP': '23.251.133.76' }, { 'foo': 'bar' } ], 'networkIP': '10.0.0.1' }, { 'accessConfigs': [ { 'natIP': '23.251.133.74' } ], 'networkIP': '10.0.0.2' }, { 'bar': 'foo' } ] }, { 'name': 'instance-2', 'networkInterfaces': [ { 'accessConfigs': [ { 'natIP': '23.251.133.74' } ], 'networkIP': '10.0.0.2' }, { 'foo': 'bar' } ] }, { 'name': 'instance-3', 'networkInterfaces': [ { 'accessConfigs': [ { 'natIP': '23.251.133.76' } ], 'networkIP': '10.0.0.3' } ] } ]) T([False, False, False], 'networkInterfaces.len() > 3') T([False, False, False], 'networkInterfaces[3]:*') T([True, False, False], 'networkInterfaces.len() > 2') T([True, False, False], 'networkInterfaces[2]:*') T([True, True, False], 'networkInterfaces.len() > 1') T([True, True, False], 'networkInterfaces[1]:*') T([True, True, True], 'networkInterfaces.len() > 0') T([True, True, True], 'networkInterfaces[0]:*')