def stencil_id(self) -> StencilID: fingerprint = { "__main__": self.builder.definition._gtscript_["canonical_ast"], "docstring": inspect.getdoc(self.builder.definition), "api_annotations": f"[{', '.join(self._extract_api_annotations())}]", **self._extract_externals(), } # typeignore because attrclass StencilID has generated constructor return StencilID( # type: ignore self.builder.options.qualified_name, gt_utils.shashed_id(gt_utils.shashed_id(fingerprint), self.options_id), )
def compile_definition( definition_func, name: str, module: str, *, externals: dict = None, dtypes: dict = None, rebuild=False, **kwargs, ): _, original_annotations = gtscript._set_arg_dtypes(definition_func, dtypes=dtypes or {}) build_options = gt_definitions.BuildOptions( name=name, module=module, rebuild=rebuild, backend_opts=kwargs, build_info=None ) options_id = gt_utils.shashed_id(build_options) stencil_id = frontend.get_stencil_id( build_options.qualified_name, definition_func, externals, options_id ) definition_ir = gt_frontend.GTScriptParser( definition_func, externals=externals or {}, options=build_options ).run() setattr(definition_func, "__annotations__", original_annotations) return stencil_id, definition_ir
def get_stencil_id(cls, qualified_name, definition, externals, options_id): GTScriptParser.annotate_definition(definition) resolved_externals = GTScriptParser.resolve_external_symbols( definition._gtscript_["nonlocals"], definition._gtscript_["imported"], externals) definition._gtscript_["externals"] = resolved_externals fingerprint = {"__main__": definition._gtscript_["canonical_ast"]} for name, value in resolved_externals.items(): fingerprint[name] = (value._gtscript_["canonical_ast"] if hasattr( value, "_gtscript_") else value) definition_id = gt_utils.shashed_id(fingerprint) version = gt_utils.shashed_id(definition_id, options_id) stencil_id = gt_definitions.StencilID(qualified_name, version) return stencil_id
def compile_definition( definition_func, name: str, module: str, *, externals: dict, rebuild=False, **kwargs ): build_options = gt_definitions.BuildOptions( name=name, module=module, rebuild=rebuild, backend_opts=kwargs, build_info=None ) options_id = gt_utils.shashed_id(build_options) _ = frontend.get_stencil_id( build_options.qualified_name, definition_func, externals, options_id ) gt_frontend.GTScriptParser(definition_func, externals=externals, options=build_options).run()
def shashed_id(self): result = gt_utils.shashed_id( self.name, self.module, self.format_source, *tuple(sorted(self.backend_opts.items())) ) return result
def id_version(): return gt_utils.shashed_id(str(datetime.datetime.now()))
def visit_Call(self, node: ast.Call, *, target_node=None): call_name = node.func.id assert call_name in self.context and hasattr(self.context[call_name], "_gtscript_") # Recursively inline any possible nested subroutine call call_info = self.context[call_name]._gtscript_ call_ast = copy.deepcopy(call_info["ast"]) CallInliner.apply(call_ast, call_info["local_context"]) # Extract call arguments call_signature = call_info["api_signature"] arg_infos = {arg.name: arg.default for arg in call_signature} try: assert len(node.args) <= len(call_signature) call_args = {} for i, arg_value in enumerate(node.args): assert not call_signature[i].is_keyword call_args[call_signature[i].name] = arg_value for kwarg in node.keywords: assert kwarg.arg in arg_infos call_args[kwarg.arg] = kwarg.value # Add default values for missing args when possible for name in arg_infos: if name not in call_args: assert arg_infos[name] != gt_ir.Empty if ((arg_infos[name] is True) or arg_infos[name] is False or arg_infos[name] is None): call_args[name] = ast.Num( n=0.0) # ast.NameConstant(value=arg_infos[name]) else: call_args[name] = ast.Num(n=arg_infos[name]) except Exception: raise GTScriptSyntaxError(message="Invalid call signature", loc=gt_ir.Location.from_ast_node(node)) # Rename local names in subroutine to avoid conflicts with caller context names assign_targets = gt_meta.collect_assign_targets(call_ast) assert all( len(target) == 1 and isinstance(target[0], ast.Name) for target in assign_targets) assigned_symbols = set(target[0].id for target in assign_targets) name_mapping = { name: value.id for name, value in call_args.items() if isinstance(value, ast.Name) and name not in assigned_symbols } call_id = gt_utils.shashed_id(call_name)[:3] call_id_suffix = f"{call_id}_{node.lineno}_{node.col_offset}" template_fmt = "{name}__" + call_id_suffix gt_meta.map_symbol_names(call_ast, name_mapping, template_fmt=template_fmt, skip_names=self.all_skip_names) # Replace returns by assignments in subroutine if target_node is None: target_node = ast.Name( ctx=ast.Store(), lineno=node.lineno, col_offset=node.col_offset, id=template_fmt.format(name="RETURN_VALUE"), ) assert isinstance(target_node, (ast.Name, ast.Tuple)) and isinstance( target_node.ctx, ast.Store) ReturnReplacer.apply(call_ast, target_node) # Add subroutine sources prepending the required arg assignments inlined_stmts = [] for arg_name, arg_value in call_args.items(): if arg_name not in name_mapping: inlined_stmts.append( ast.Assign( lineno=node.lineno, col_offset=node.col_offset, targets=[ ast.Name( ctx=ast.Store(), lineno=node.lineno, col_offset=node.col_offset, id=template_fmt.format(name=arg_name), ) ], value=arg_value, )) # Add inlined statements to the current block and return name node with the result inlined_stmts.extend(call_ast.body) self.current_block.extend(inlined_stmts) if isinstance(target_node, ast.Name): result_node = ast.Name( ctx=ast.Load(), lineno=target_node.lineno, col_offset=target_node.col_offset, id=target_node.id, ) else: result_node = ast.Tuple( ctx=ast.Load(), lineno=target_node.lineno, col_offset=target_node.col_offset, elts=target_node.elts, ) return result_node