def _resolve_keywords(self, closure_type, args, keywords): func_def = closure_type.closure.func_def argnames = [name.id for name in func_def.args.args] expected = len(argnames) - len(args) if len(keywords) != expected: raise error.NumbaError( self.call_node, "Expected %d arguments, got %d" % (len(argnames), len(args) + len(keywords))) argpositions = dict(zip(argnames, range(len(argnames)))) positional = [None] * (len(argnames) - len(args)) for keyword in keywords: argname = keyword.arg pos = argpositions.get(argname, None) if pos is None: raise error.NumbaError( keyword, "Not a valid keyword argument name: %s" % argname) elif pos < len(args): raise error.NumbaError( keyword, "Got multiple values for positional " "argument %r" % argname) else: positional[pos] = keyword.value return positional
def report(self, post_mortem=False): self.messages.sort(key=sort_message) if self.messages: self.header() errors = [] for node, is_error, message in self.messages: if is_error: errors.append((node, message)) type = "Error" else: type = "Warning" self.report_message(message, node, type) if self.messages: self.buf[-1] = self.buf[-1].rstrip() + '\n' self.footer() message = "".join(self.buf) # clear buffer del self.messages[:] del self.buf[:] if errors and not post_mortem: if len(message.splitlines()) == 1: raise error.NumbaError(*errors[0]) raise error.NumbaError("(see below)\n" + message.strip(), has_report=True) else: self.file.write(message)
def tensordot(typesystem, a, b, axes): '''Typing function for numpy.tensordot(). Defaults to Python object for any caller that isn't using the default argument to axes. Otherwise, it is similar to inner(), but subtracts four dimensions from the result instead of two. Without symbolic execution of the actual axes argument, this can't determine the number of axes to sum over, so it punts. This typing function could use an array type of unknown dimensionality, were one available. See: https://www.pivotaltracker.com/story/show/43687249 ''' lhs_type = array_from_object(a) rhs_type = array_from_object(b) if lhs_type.ndim < 1: raise error.NumbaError(a, 'First argument to numpy.tensordot() ' 'requires array of dimensionality >= 1.') elif rhs_type.ndim < 1: raise error.NumbaError(b, 'First argument to numpy.tensordot() ' 'requires array of dimensionality >= 1.') dtype = typesystem.promote(lhs_type.dtype, rhs_type.dtype) if axes is None: result_ndim = lhs_type.ndim + rhs_type.ndim - 4 if result_ndim < 0: raise error.NumbaError(a, 'Arguments to numpy.tensordot() should ' 'have combined dimensionality >= 4 (when ' 'axes argument is not specified).') result_type = typesystem.array(dtype, result_ndim) else: # XXX Issue warning to user? result_type = object_ return result_type
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) # TODO: the below is a problem due to implicit string <-> int coercions! if (node_type.is_string and dst_type.is_numeric and not (node_type.is_pointer or node_type.is_null)): if dst_type.typename in ('char', 'uchar'): raise error.NumbaError( node, "Conversion from string to (u)char not yet supported") result = self.str_to_int(dst_type, node) elif self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError( node, "Cannot coerce to or from object in " "nopython context") elif is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode( nodes.CoerceToObject(node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError( node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def __call__(self, *args, **kwargs): if self.ctypes_func: return self.invoke_compiled(self.ctypes_func, *args, **kwargs) else: if kwargs: raise error.NumbaError("Cannot handle keyword arguments yet") nargs = self.py_func.func_code.co_argcount if len(args) != nargs: raise error.NumbaError("Expected %d arguments, got %d" % (nargs, len(args))) return self.wrapper(self, *args, **kwargs)
def parse_argtypes(visit_func, decorator, func_def, jit_args): argtypes_node = jit_args['argtypes'] if argtypes_node is None: raise error.NumbaError(func_def.args[0], "Expected an argument type") argtypes = assert_constant(visit_func, decorator, argtypes_node) if not isinstance(argtypes, (list, tuple)): raise error.NumbaError(argtypes_node, 'Invalid argument for argtypes') for argtype in argtypes: check_valid_argtype(argtypes_node, argtype) return argtypes
def visit_CoerceToNative(self, node): """ Try to perform fast coercion using e.g. PyLong_AsLong(), with a fallback to PyArg_ParseTuple(). """ new_node = None from_type = node.node.type node_type = node.type if node_type.is_numeric: cls = None if node_type == size_t: node_type = ulonglong if node_type.is_int: # and not cls = self._get_int_conversion_func(node_type, functions._as_long) if not node_type.signed or node_type == Py_ssize_t: # PyLong_AsLong calls __int__, but # PyLong_AsUnsignedLong doesn't... node.node = nodes.call_pyfunc(long, [node.node]) elif node_type.is_float: cls = functions.PyFloat_AsDouble #elif node_type.is_complex: # cls = functions.PyComplex_AsCComplex if cls: # TODO: error checking! new_node = self.function_cache.call(cls.__name__, node.node) elif node_type.is_pointer: raise error.NumbaError(node, "Obtaining pointers from objects " "is not yet supported") elif node_type.is_void: raise error.NumbaError(node, "Cannot coerce %s to void" % (from_type,)) if new_node is None: # Create a tuple for PyArg_ParseTuple new_node = node new_node.node = ast.Tuple(elts=[node.node], ctx=ast.Load()) else: # Fast coercion new_node = nodes.CoercionNode(new_node, node.type) if new_node is node: self.generic_visit(new_node) else: new_node = self.visit(new_node) return new_node
def _print_nopython(self, value, dest=None): if dest is not None: raise error.NumbaError(dest, "No file may be given in nopython mode") stdin, stdout, stderr = stdio_util.get_stdio_streams() stdout = stdio_util.get_stream_as_node(stdout) format = codegen.get_printf_specifier(value.type) if format is None: raise error.NumbaError( value, "Printing values of type '%s' is not supported " "in nopython mode" % (value.type,)) return self.function_cache.call( 'printf', nodes.const(format, c_string_type), value)
def _resolve_struct_attribute(self, node, type): if not node.attr in type.fielddict: raise error.NumbaError( node, "Struct %s has no field %r" % (type, node.attr)) if isinstance(node.ctx, ast.Store): if not isinstance(node.value, (ast.Name, ast.Subscript, nodes.StructVariable)): raise error.NumbaError( node, "Can only assign to struct attributes of " "variables or array indices") node.value.ctx = ast.Store() return nodes.StructAttribute(node.value, node.attr, node.ctx, node.value.variable.type)
def _process_signature(ext_type, method, default_signature, is_static=False, is_class=False): if isinstance(method, minitypes.Function): # @double(...) # def func(self, ...): ... return _process_signature(ext_type, method.py_func, method.signature, is_static, is_class) elif isinstance(method, types.FunctionType): if default_signature is None: # TODO: construct dependency graph, toposort, type infer # TODO: delayed types raise error.NumbaError( "Method '%s' does not have signature" % (method.__name__,)) validate_method(method, default_signature or object_(), is_static) if default_signature is None: default_signature = minitypes.FunctionType(return_type=None, args=[]) sig = get_signature(ext_type, is_class, is_static, default_signature) return Method(method, is_class, is_static), sig.return_type, sig.args else: if isinstance(method, staticmethod): is_static = True elif isinstance(method, classmethod): is_class = True else: return None, None, None method, restype, argtypes = _process_signature( ext_type, method.__func__, default_signature, is_static=is_static, is_class=is_class) return method, restype, argtypes
def validate_method(py_func, sig, is_static): assert isinstance(py_func, types.FunctionType) nargs = py_func.func_code.co_argcount - 1 + is_static if len(sig.args) != nargs: raise error.NumbaError( "Expected %d argument types in function " "%s (don't include 'self')" % (nargs, py_func.__name__))
def visit_With(self, node): """ Rewrite the With statement. Python < 3.3: With(expr context_expr, expr? optional_vars, stmt* body) Python 3.3: With(withitem* items, stmt* body) withitem = (expr context_expr, expr? optional_vars) """ if sys.version_info[:2] >= (3, 3): if len(node.items) > 1: raise error.NumbaError(node, "Only one 'with' context is support") withitem = node.items[0] new_node = With() new_node.context_expr = withitem.context_expr new_node.optional_vars = withitem.optional_vars new_node.body = node.body node = ast.copy_location(new_node, node) self.generic_visit(node) return node
def visit_Attribute(self, node): if (self.nopython and not node.value.type.is_module and not node.value.type.is_complex): raise error.NumbaError( node, "Cannot access Python attribute in nopython context (%s)" % node.attr) if node.value.type.is_complex: value = self.visit(node.value) return nodes.ComplexAttributeNode(value, node.attr) elif node.type.is_numpy_attribute: return nodes.ObjectInjectNode(node.type.value) elif node.type.is_numpy_dtype: dtype_type = node.type.dtype return nodes.ObjectInjectNode(dtype_type.get_dtype()) elif is_obj(node.value.type): if node.value.type.is_module: # Resolve module attributes as constants if node.type.is_module_attribute: new_node = nodes.ObjectInjectNode(node.type.value) else: new_node = nodes.ConstNode( getattr(node.value.type.module, node.attr)) else: new_node = function_util.external_call( self.context, self.llvm_module, 'PyObject_GetAttrString', args=[node.value, nodes.ConstNode(node.attr)]) return self.visit(new_node) self.generic_visit(node) return node
def visit_Slice(self, node): """ Rewrite slice objects. Do this late in the pipeline so that other code can still recognize the code structure. """ slice_values = [node.lower, node.upper, node.step] if self.nopython: raise error.NumbaError(node, "Cannot slice in nopython context") if node.variable.is_constant: return self.visit( nodes.ObjectInjectNode(node.variable.constant_value)) bounds = [] for node in slice_values: if node is None: bounds.append(nodes.NULL_obj) else: bounds.append(node) new_slice = function_util.external_call(self.context, self.llvm_module, 'PySlice_New', args=bounds, temp_name='slice') return self.visit(new_slice)
def visit_ArrayNewNode(self, node): if self.nopython: raise error.NumbaError( node, "Cannot yet allocate new array in nopython context") PyArray_Type = nodes.ObjectInjectNode(np.ndarray) descr = nodes.ObjectInjectNode(node.type.dtype.get_dtype()).cloneable ndim = nodes.const(node.type.ndim, int_) flags = nodes.const(0, int_) args = [PyArray_Type, descr.clone, ndim, node.shape, node.strides, node.data, flags] incref_descr = nodes.IncrefNode(descr) incref_base = None setbase = None if node.base is None: args.append(nodes.NULL_obj) else: base = nodes.CloneableNode(node.base) incref_base = nodes.IncrefNode(base) args.append(base.clone) array = nodes.PyArray_NewFromDescr(args) array = nodes.ObjectTempNode(array).cloneable body = [incref_descr, incref_base, array, setbase] if node.base is not None: body.append(nodes.PyArray_SetBaseObject([array.clone, base.clone])) # TODO: PyArray_UpdateFlags() result = nodes.ExpressionNode(filter(None, body), array.clone) return self.visit(result)
def init_locals(self): arg_types = self.func_signature.args if len(arg_types) > len(self.local_names): raise error.NumbaError("Too many types specified in @jit()") for i, arg_type in enumerate(arg_types): varname = self.local_names[i] self.symtab[varname] = Variable(arg_type, is_local=True, name=varname) for varname in self.local_names[len(arg_types):]: self.symtab[varname] = Variable(None, is_local=True, name=varname) self.symtab['None'] = Variable(numba_types.none, is_constant=True, constant_value=None) arg_types = list(arg_types) for local_name, local_type in self.locals.iteritems(): if local_name not in self.symtab: self.symtab[local_name] = Variable(local_type, is_local=True, name=local_name) variable = self.symtab[local_name] variable.type = local_type variable.promotable_type = False if local_name in self.argnames: idx = self.argnames.index(local_name) arg_types[idx] = local_type self.func_signature.args = tuple(arg_types)
def visit_While(self, node): if node.orelse: raise error.NumbaError(node.orelse, 'Else in for-loop is not implemented.') node.test = nodes.CoercionNode(self.visit(node.test), minitypes.bool_) node.body = self.visitlist(node.body) return node
def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) op = type(node.ops[0]) if op == ast.Eq: lhs = compare(real1, ast.Eq(), real2) rhs = compare(imag1, ast.Eq(), imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) elif op == ast.NotEq: lhs = compare(real1, ast.NotEq(), real2) rhs = compare(imag1, ast.NotEq(), imag2) result = ast.BoolOp(ast.Or(), [lhs, rhs]) else: raise NotImplementedError("ordered comparisons are not " "implemented for complex numbers") node = nodes.typednode(result, bool_) elif node.left.type.is_complex and rhs.type.is_datetime: raise error.NumbaError( node, "datetime comparisons not yet implemented") return node
def visit_ExtensionMethod(self, node, call_node=None): """ Resolve an extension method: typedef { double (*method1)(double); ... } vtab_struct; vtab_struct *vtab = *(vtab_struct **) (((char *) obj) + vtab_offset) void *method = vtab[index] """ if call_node is None: raise error.NumbaError(node, "Referenced extension method '%s' " "must be called" % node.attr) # Make the object we call the method on clone-able node.value = nodes.CloneableNode(self.visit(node.value)) ext_type = node.value.type offset = ext_type.vtab_offset struct_type = ext_type.vtab_type.pointer() vmethod = self.lookup_offset_attribute(node.attr, ast.Load(), node, offset, struct_type, is_pointer=True) # Visit argument list for call args = self.visitlist(call_node.args) # Insert first argument 'self' in args list args.insert(0, nodes.CloneNode(node.value)) result = nodes.NativeFunctionCallNode(node.type, vmethod, args) result.signature.is_bound_method = False return self.visit(result)
def _handle_jit_decorator(self, func_def, decorator): from numba import ast_type_inference jit_args = ast_type_inference._parse_args( decorator, ['restype', 'argtypes', 'backend', 'target', 'nopython']) if decorator.args or decorator.keywords: restype = self._parse_restype(decorator, jit_args) if restype is not None and restype.is_function: signature = restype else: argtypes = self._parse_argtypes(decorator, func_def, jit_args) signature = minitypes.FunctionType(restype, argtypes, name=func_def.name) else: #elif func_def.args: raise error.NumbaError( decorator, "The argument types and return type " "need to be specified") #else: # signature = minitypes.FunctionType(None, []) # TODO: Analyse closure at call or outer function return time to # TODO: infer return type # TODO: parse out nopython argument return signature
def require(ast_nodes, properties): "Assert that the types of the given nodes meets a certain requirement" for ast_node in ast_nodes: if not any(getattr(get_type(ast_node), p) for p in properties): typenames = ", or ".join( p[3:] for p in properties) # remove 'is_' prefix raise error.NumbaError(ast_node, "Expected an %s" % (typenames, ))
def expect_n_args(node, name, nargs): if not isinstance(nargs, tuple): nargs = (nargs, ) if len(node.args) not in nargs: expected = " or ".join(map(str, nargs)) raise error.NumbaError( node, "builtin %s expects %s arguments" % (name, expected))
def visit_With(self, node): self.visit(node.context_expr) if node.optional_vars: raise error.NumbaError( node.context_expr, "Only 'with python' and 'with nopython' is " "supported at this moment") self.generic_visit(node)
def visit_For(self, node): if not isinstance(node.target, (ast.Name, ast.Attribute, nodes.TempStoreNode)): raise error.NumbaError( node.target, "Only a single target iteration variable is " "supported at the moment") self.generic_visit(node)
def visit_For(self, node): if not isinstance(node.target, (ast.Name, ast.Attribute)): raise error.NumbaError( node.target, "Only a single target iteration variable is " "supported at the moment") self.visitchildren(node) return node
def validate_signature(tree, env): arg_types = env.translation.crnt.func_signature.args if (isinstance(tree, ast_module.FunctionDef) and len(arg_types) != len(tree.args.args)): raise error.NumbaError( "Incorrect number of types specified in @jit() for function %r" % env.crnt.func_name) return tree
def visit_WithNoPythonNode(self, node, errorcheck=True): if self.nopython and errorcheck: raise error.NumbaError(node, "Not in 'with python' context") self.nopython += 1 self.visitlist(node.body) self.nopython -= 1 return node
def visit_Raise(self, node): raise error.NumbaError(node, "Raise statement not implemented yet") self.visitchildren(node) if self.flow.exceptions: self.flow.block.add_child(self.flow.exceptions[-1].entry_point) self.flow.block = None return node
def visit_With(self, node): node.context_expr = self.visit(node.context_expr) if node.optional_vars: raise error.NumbaError( node.context_expr, "Only 'with python' and 'with nopython' is " "supported at this moment") self.visitlist(node.body) return node
def find_iterator_impl(node): "Find a suitable iterator type for which we have an implementation" type = node.iter.type for pattern, impl in iterator_impls: if typematch(pattern, type): return impl raise error.NumbaError(node, "Unsupported iterator " "type: %s" % (type, ))