def _attribute(self): # Get type and name. type_ = self.get_token() name = self.get_token() # Get values: value_1 (, value_n)* values = [] while not self._check(';'): value = self.get_token() if type_.lower() in ['string', 'url']: value = expr_eval(repr(value)) value = value.strip('"') elif type_.lower() == 'alias': # Support for Alias is not documented in the DAP spec. I based # this on the Java documentation from the OPeNDAP website at: # http://www.opendap.org/api/javaDocs/dods/dap/Alias.html if value.startswith('.'): tokens = value[1:].split('.') value = self._dataset else: tokens = value.split('.') value = self._target for token in tokens: if token in value: value = value[token] elif token: value = value.attributes.get( token, "Alias pointing to non-existing attribute.") break else: value = value.attributes elif type_.lower() == 'float32': # Convert to right precision; otherwise floats # are converted to Float64 automatically by # Python. value = array.array('f', [float(value)])[0] elif type_.lower() == 'float64': value = array.array('d', [float(value)])[0] else: value = int(value) values.append(value) if self._check(','): self._consume(',') self._consume(';') # Return single attributes as values, not list. if len(values) == 1: values = values[0] return name, values
def buildfilter(queries, vars_): """This function is a filter builder. Given a list of DAP formatted queries and a list of variable names, this function returns a dynamic filter function to filter rows. From the example in the DAP specification: >>> vars_ = ['index', 'temperature', 'site'] >>> data = [] >>> data.append([10, 17.2, 'Diamond_St']) >>> data.append([11, 15.1, 'Blacktail_Loop']) >>> data.append([12, 15.3, 'Platinum_St']) >>> data.append([13, 15.1, 'Kodiak_Trail']) Rows where index is greater-than-or-equal 11: >>> f = buildfilter(['index>=11'], vars_) >>> for line in itertools.ifilter(f, data): ... print line [11, 15.1, 'Blacktail_Loop'] [12, 15.300000000000001, 'Platinum_St'] [13, 15.1, 'Kodiak_Trail'] Rows where site ends with '_St': >>> f = buildfilter(['site=~".*_St"'], vars_) >>> for line in itertools.ifilter(f, data): ... print line [10, 17.199999999999999, 'Diamond_St'] [12, 15.300000000000001, 'Platinum_St'] Index greater-or-equal-than 11 AND site ends with '_St': >>> f = buildfilter(['site=~".*_St"', 'index>=11'], vars_) >>> for line in itertools.ifilter(f, data): ... print line [12, 15.300000000000001, 'Platinum_St'] Site is either 'Diamond_St' OR 'Blacktail_Loop': >>> f = buildfilter(['site={"Diamond_St", "Blacktail_Loop"}'], vars_) >>> for line in itertools.ifilter(f, data): ... print line [10, 17.199999999999999, 'Diamond_St'] [11, 15.1, 'Blacktail_Loop'] Index is either 10 OR 12: >>> f = buildfilter(['index={10, 12}'], vars_) >>> for line in itertools.ifilter(f, data): ... print line [10, 17.199999999999999, 'Diamond_St'] [12, 15.300000000000001, 'Platinum_St'] Python is great, isn't it? :) """ filters = [] p = re.compile(r'''^ # Start of selection {? # Optional { for multi-valued constants (?P<var1>.*?) # Anything }? # Closing } (?P<op><=|>=|!=|=~|>|<|=) # Operators {? # { (?P<var2>.*?) # Anything }? # } $ # EOL ''', re.VERBOSE) for query in queries: m = p.match(query) if not m: raise ConstraintExpressionError('Invalid constraint expression: %s.' % query) # Functions associated with each operator. op = {'<' : operator.lt, '>' : operator.gt, '!=': operator.ne, '=' : operator.eq, '>=': operator.ge, '<=': operator.le, '=~': lambda a,b: re.match(b,a), }[m.group('op')] # Allow multiple comparisons in one line. Python rulez! op = multicomp(op) # Build the filter for the first variable. if m.group('var1') in vars_: i = vars_.index(m.group('var1')) var1 = lambda L, i=i: operator.getitem(L, i) # Build the filter for the second variable. It could be either # a name or a constant. if m.group('var2') in vars_: i = vars_.index(m.group('var2')) var2 = lambda L, i=i: operator.getitem(L, i) else: var2 = lambda x, m=m: expr_eval(m.group('var2')) # This is the filter. We apply the function (op) to the variable # filters (var1 and var2). filter0 = lambda x, op=op, var1=var1, var2=var2: op(var1(x), var2(x)) filters.append(filter0) if filters: # We have to join all the filters that were built, using the AND # operator. Believe me, this line does exactly that. # # You are not expected to understand this. filter0 = lambda i: reduce(lambda x,y: x and y, [f(i) for f in filters]) else: filter0 = bool return filter0