def _generate_compute_method(self):
     compute_method = 4 * ' ' + 'def compute(self, dataset_pool): \n'
     # Generate local bindings for variables.  We need to avoid overwriting an existing binding,
     # so keep track of which have already been generated.  For example, when we first encounter
     # urbansim.gridcell.population, we need to generate all of the bindings (as described in the
     # long comment before this method).  After that, if we encounter urbansim.household.has_children
     # we need to add a household attribute to urbansim -- but not reinitialize the variable
     # urbansim itself.  Then if we find urbansim.gridcell.population_density we should just add
     # the population_density attribute to urbansim.gridcell (and not reinitialize either urbansim
     # or urbansim.gridcell).
     already_generated = set()
     for pkg, ds, short in self._dependents:
         # if the dataset name ends in self._constant_suffix, get the dataset out of the dataset pool; otherwise it is self.get_dataset()
         if ds is not None and ds.endswith(self._constant_suffix):
             getter = 'dataset_pool.get_dataset("%s").get_attribute("%s.%s")' % (
                 ds, ds, short)
         else:
             getter = 'self.get_dataset().get_attribute("%s.%s")' % (ds,
                                                                     short)
         if ds is None:
             # the dependent is an unqualified attribute name
             compute_method = compute_method + 8 * ' ' + '%s = %s \n' % (
                 short, getter)
         elif pkg is None:
             if (None, ds) not in already_generated:
                 compute_method = compute_method + 8 * ' ' + '%s = DummyDataset(self, "%s", dataset_pool) \n' % (
                     ds, ds)
                 already_generated.add((None, ds))
             compute_method = compute_method + 8 * ' ' + '%s.%s = %s \n' % (
                 ds, short, getter)
         else:
             if pkg not in already_generated:
                 compute_method = compute_method + 8 * ' ' + '%s = DummyName() \n' % pkg
                 already_generated.add(pkg)
             if (pkg, ds) not in already_generated:
                 compute_method = compute_method + 8 * ' ' + '%s.%s = DummyDataset(self, "%s", dataset_pool) \n' % (
                     pkg, ds, ds)
                 already_generated.add((pkg, ds))
             compute_method = compute_method + 8 * ' ' + '%s.%s.%s = %s \n' % (
                 pkg, ds, short, getter)
     # generate bindings for receivers of aggregate and disaggregate methods
     for receiver, method, pkg, aggregated_dataset, aggregated_attr, intermediates, op in self._aggregation_calls:
         if (None, receiver) not in already_generated:
             compute_method = compute_method + 8 * ' ' + '%s = DummyDataset(self, "%s", dataset_pool) \n' % (
                 receiver, receiver)
             already_generated.add((None, receiver))
     for receiver in self._special_dataset_receivers:
         if (None, receiver) not in already_generated:
             compute_method = compute_method + 8 * ' ' + '%s = DummyDataset(self, "%s", dataset_pool) \n' % (
                 receiver, receiver)
             already_generated.add((None, receiver))
     # If we need to replace parts of the parse tree, do the replacements, and turn the parse tree
     # back into a string to generate the method.  Otherwise just use the original expression.
     # (We could always turn the tree back into a string I guess ...)
     if len(self._parsetree_replacements) == 0:
         newexpr = self._expr
     else:
         newtree = parsetree_substitute(self._expr_parsetree,
                                        self._parsetree_replacements)
         newexpr = parsetree_to_string(newtree)
         # the parse tree doesn't include the assignment statement that sets up the alias
         if self._alias is not None:
             newexpr = self._alias + ' = ' + newexpr
     # If alias is None, we can just return the expression.  If alias is not none, then expr will bind
     # the alias to the desired value, so execute expr, and return the value in the alias.
     if self._alias is None:
         compute_method = compute_method + 8 * ' ' + 'return ' + newexpr + '\n'
     else:
         compute_method = compute_method + 8 * ' ' + newexpr + '\n'
         compute_method = compute_method + 8 * ' ' + 'return ' + self._alias + '\n'
     return compute_method
 def _generate_compute_method(self):
     compute_method = 4*' ' + 'def compute(self, dataset_pool): \n'
     # Generate local bindings for variables.  We need to avoid overwriting an existing binding,
     # so keep track of which have already been generated.  For example, when we first encounter
     # urbansim.gridcell.population, we need to generate all of the bindings (as described in the
     # long comment before this method).  After that, if we encounter urbansim.household.has_children
     # we need to add a household attribute to urbansim -- but not reinitialize the variable
     # urbansim itself.  Then if we find urbansim.gridcell.population_density we should just add
     # the population_density attribute to urbansim.gridcell (and not reinitialize either urbansim
     # or urbansim.gridcell).
     already_generated = set()
     for pkg, ds, short in self._dependents:
         # if the dataset name ends in self._constant_suffix, get the dataset out of the dataset pool; otherwise it is self.get_dataset()
         if ds is not None and ds.endswith(self._constant_suffix):
             getter = 'dataset_pool.get_dataset("%s").get_attribute("%s.%s")' % (ds,ds,short)
         else:
             getter = 'self.get_dataset().get_attribute("%s.%s")' % (ds,short)
         if ds is None:
             # the dependent is an unqualified attribute name
             compute_method = compute_method + 8*' ' + '%s = %s \n' % (short, getter)
         elif pkg is None:
             if (None,ds) not in already_generated:
                 compute_method = compute_method + 8*' ' + '%s = DummyDataset(self, "%s", dataset_pool) \n' % (ds, ds)
                 already_generated.add( (None,ds) )
             compute_method = compute_method + 8*' ' + '%s.%s = %s \n' % (ds, short, getter)
         else:
             if pkg not in already_generated:
                 compute_method = compute_method + 8*' ' + '%s = DummyName() \n' % pkg
                 already_generated.add(pkg)
             if (pkg,ds) not in already_generated:
                 compute_method = compute_method + 8*' ' + '%s.%s = DummyDataset(self, "%s", dataset_pool) \n' % (pkg, ds, ds)
                 already_generated.add( (pkg,ds) )
             compute_method = compute_method + 8*' ' + '%s.%s.%s = %s \n' % (pkg, ds, short, getter)
     # generate bindings for receivers of aggregate and disaggregate methods
     for receiver, method, pkg, aggregated_dataset, aggregated_attr, intermediates, op in self._aggregation_calls:
         if (None,receiver) not in already_generated:
             compute_method = compute_method + 8*' ' + '%s = DummyDataset(self, "%s", dataset_pool) \n' % (receiver, receiver)
             already_generated.add( (None,receiver) )
     for receiver in self._special_dataset_receivers:
         if (None,receiver) not in already_generated:
             compute_method = compute_method + 8*' ' + '%s = DummyDataset(self, "%s", dataset_pool) \n' % (receiver, receiver)
             already_generated.add( (None,receiver) )
     # If we need to replace parts of the parse tree, do the replacements, and turn the parse tree
     # back into a string to generate the method.  Otherwise just use the original expression.
     # (We could always turn the tree back into a string I guess ...)
     if len(self._parsetree_replacements)==0:
         newexpr = self._expr
     else:
         newtree = parsetree_substitute(self._expr_parsetree, self._parsetree_replacements)
         newexpr = parsetree_to_string(newtree)
         # the parse tree doesn't include the assignment statement that sets up the alias
         if self._alias is not None:
             newexpr = self._alias + ' = ' + newexpr
     # If alias is None, we can just return the expression.  If alias is not none, then expr will bind 
     # the alias to the desired value, so execute expr, and return the value in the alias.
     if self._alias is None:
         compute_method = compute_method + 8*' ' + 'return ' + newexpr + '\n'
     else:
         compute_method = compute_method + 8*' ' + newexpr + '\n'
         compute_method = compute_method + 8*' ' + 'return ' + self._alias + '\n'
     return compute_method
    def _analyze_aggregation_method_call(self, receiver, method, args):
        same, vars = match(SUBPATTERN_AGGREGATION, args)
        if not same:
            raise ValueError, "syntax error for aggregation method call"
        arg_dict = self._get_arguments(
            ('arg1', 'arg2', 'arg3'),
            ('aggr_var', 'intermediates', 'function'), vars)
        if 'aggr_var' not in arg_dict:
            raise ValueError, "syntax error for aggregation method call (problem with argument for variable being aggregated)"
        same1, vars1 = match(SUBPATTERN_FULLY_QUALIFIED_VARIABLE_ARG,
                             arg_dict['aggr_var'])
        if same1:
            # the aggregated variable is a fully-qualified name
            pkg = vars1['package']
            dataset = vars1['dataset']
            attr = vars1['shortname']
        else:
            same2, vars2 = match(SUBPATTERN_DATASET_QUALIFIED_VARIABLE_ARG,
                                 arg_dict['aggr_var'])
            if same2:
                # the aggregated variable is a dataset-qualified name
                pkg = None
                dataset = vars2['dataset']
                attr = vars2['shortname']
            else:
                # The thing being aggregated is an expression.  Generate a new autogen variable for that expression,
                # and use the autogen variable in the aggregation call.
                subexpr = arg_dict['aggr_var']
                newvar = VariableName(parsetree_to_string(subexpr))
                pkg = None
                dataset = newvar.get_dataset_name()
                if dataset is None:
                    raise ValueError, "syntax error for aggregation method call - could not determine dataset for variable being aggregated"
                attr = newvar.get_short_name()
                # TODO DELETE BELOW:
#                replacements = {'dataset': dataset, 'attribute': attr}
#                newvar_tree = parsetree_substitute(DATASET_QUALIFIED_VARIABLE_TEMPLATE, replacements)
#                self._parsetree_replacements[subexpr] = newvar_tree
        if 'intermediates' in arg_dict:
            # make sure that it really is a list
            s, v = match(SUBPATTERN_LIST_ARG, arg_dict['intermediates'])
            if not s:
                raise ValueError, "syntax error for aggregation method call (list of intermediate datasets not a list?)"
            intermediates = tuple(
                self._extract_names(arg_dict['intermediates']))
        else:
            intermediates = ()
        if 'function' in arg_dict:
            # bind fcn to a string that is the name of the function, or to the string "None"
            s, v = match(SUBPATTERN_NAME_ARG, arg_dict['function'])
            if not s:
                raise ValueError, "syntax error for aggregation method call (problem with the function argument in the call)"
            fcn = v['name']
        else:
            fcn = None
        self._aggregation_calls.add(
            (receiver, method, pkg, dataset, attr, intermediates, fcn))
        quoted_intermediates = "" if len(intermediates) == 0 else quote(
            intermediates[0])
        for n in intermediates[1:]:
            quoted_intermediates = quoted_intermediates + ', ' + quote(n)
        # 'call' is a string representing the new aggregation call.  Parse it, extract the args, and then add a replacement to
        # parsetree_replacements for the old args.  We want to replace just the args and not the entire call to aggregate,
        # since the way Python represents parsetrees the whole tree may include astype and exponentiation calls, and it's simpler
        # to just replace the args part.
        call = "%s.%s(%s, %s,%s, [%s], %s)" % (
            receiver, method, quote(pkg), quote(dataset), quote(attr),
            quoted_intermediates, quote(fcn))
        (newtree, _) = self._parse_expr(call)
        s, v = match(FULL_EXPRESSION_METHOD_CALL, newtree)
        if not s:
            raise StandardError, 'internal error - problem generating new aggregation expression'
        self._parsetree_replacements[args] = v['args']
    def _analyze_aggregation_method_call(self, receiver, method, args):
        same, vars = match(SUBPATTERN_AGGREGATION, args)
        if not same:
            raise ValueError, "syntax error for aggregation method call"
        arg_dict = self._get_arguments( ('arg1', 'arg2','arg3'), ('aggr_var', 'intermediates','function'), vars )
        if 'aggr_var' not in arg_dict:
            raise ValueError, "syntax error for aggregation method call (problem with argument for variable being aggregated)"
        same1, vars1 = match(SUBPATTERN_FULLY_QUALIFIED_VARIABLE_ARG, arg_dict['aggr_var'])
        if same1:
            # the aggregated variable is a fully-qualified name
            pkg = vars1['package']
            dataset = vars1['dataset']
            attr = vars1['shortname']
        else:
            same2, vars2 = match(SUBPATTERN_DATASET_QUALIFIED_VARIABLE_ARG, arg_dict['aggr_var'])
            if same2:
                # the aggregated variable is a dataset-qualified name
                pkg = None
                dataset = vars2['dataset']
                attr = vars2['shortname']
            else:
                # The thing being aggregated is an expression.  Generate a new autogen variable for that expression,
                # and use the autogen variable in the aggregation call.
                subexpr = arg_dict['aggr_var']
                newvar = VariableName(parsetree_to_string(subexpr))
                pkg = None
                dataset = newvar.get_dataset_name()
                if dataset is None:
                    raise ValueError, "syntax error for aggregation method call - could not determine dataset for variable being aggregated"
                attr = newvar.get_short_name()
                # TODO DELETE BELOW:
#                replacements = {'dataset': dataset, 'attribute': attr}
#                newvar_tree = parsetree_substitute(DATASET_QUALIFIED_VARIABLE_TEMPLATE, replacements)
#                self._parsetree_replacements[subexpr] = newvar_tree
        if 'intermediates' in arg_dict:
            # make sure that it really is a list
            s, v = match(SUBPATTERN_LIST_ARG, arg_dict['intermediates'])
            if not s:
                raise ValueError, "syntax error for aggregation method call (list of intermediate datasets not a list?)"
            intermediates = tuple(self._extract_names(arg_dict['intermediates']))
        else:
            intermediates = ()
        if 'function' in arg_dict:
            # bind fcn to a string that is the name of the function, or to the string "None"
            s,v = match(SUBPATTERN_NAME_ARG, arg_dict['function'])
            if not s:
                raise ValueError, "syntax error for aggregation method call (problem with the function argument in the call)"
            fcn = v['name']
        else:
            fcn = None
        self._aggregation_calls.add( (receiver, method, pkg, dataset, attr, intermediates, fcn) )
        quoted_intermediates = "" if len(intermediates)==0 else quote(intermediates[0])
        for n in intermediates[1:]:
            quoted_intermediates = quoted_intermediates + ', ' + quote(n)
        # 'call' is a string representing the new aggregation call.  Parse it, extract the args, and then add a replacement to
        # parsetree_replacements for the old args.  We want to replace just the args and not the entire call to aggregate,
        # since the way Python represents parsetrees the whole tree may include astype and exponentiation calls, and it's simpler
        # to just replace the args part. 
        call = "%s.%s(%s, %s,%s, [%s], %s)" % (receiver, method, quote(pkg), quote(dataset), quote(attr),  quoted_intermediates, quote(fcn))
        (newtree,_) = self._parse_expr(call)
        s, v = match(FULL_EXPRESSION_METHOD_CALL, newtree)
        if not s:
            raise StandardError, 'internal error - problem generating new aggregation expression'
        self._parsetree_replacements[args] = v['args']