def visit_FunctionDef(node): '''visitor to function definitions, needed to extract decorator''' for e in node.decorator_list: assert isinstance(e, (ast.Call, ast.Name)) decorator_name = None if isinstance(e, ast.Call): # @task(args, ...) decorator_name = e.func.id for k in e.keywords: if k.arg in ('accepts', 'returns'): types = to.convert_to_types(to.parse_val(k.value)) types_list = to.make_iterable(types) if isinstance(types_list, tuple): types_list = list(types_list) props[k.arg] = types_list else: raise KeyError('decorator has unknown argument') elif isinstance(e, ast.Name): # @task [ie: naked] decorator_name = e.id if 'task' == decorator_name: visit_FunctionDef.task_count += 1 visit_FunctionDef.function_name = node.name visit_FunctionDef.function_params = [to.parse_val(arg) for arg in node.args.args] else: continue
def visit_FunctionDef(node): '''visitor to function definitions, needed to extract decorator''' for e in node.decorator_list: assert isinstance(e, (ast.Call, ast.Name)) decorator_name = None if isinstance(e, ast.Call): # @task(args, ...) decorator_name = e.func.id for k in e.keywords: if k.arg in ('accepts', 'returns'): types = to.convert_to_types(to.parse_val(k.value)) types_list = to.make_iterable(types) if isinstance(types_list, tuple): types_list = list(types_list) props[k.arg] = types_list else: raise KeyError('decorator has unknown argument') elif isinstance(e, ast.Name): # @task [ie: naked] decorator_name = e.id if 'task' == decorator_name: visit_FunctionDef.task_count += 1 visit_FunctionDef.function_name = node.name visit_FunctionDef.function_params = [ to.parse_val(arg) for arg in node.args.args ] else: continue
def visit_Assign(node): '''visitor to assigns, needed for inline properties''' if not hasattr(node.targets[0], 'id'): return if 1 == len(node.targets) and node.targets[0].id in inline_properties: value = to.parse_val(node.value) attr_name = inline_properties.pop(node.targets[0].id) props[attr_name] = value
def visit_FunctionDef(node): '''visitor to function definitions, needed to extract decorator''' for e in node.decorator_list: assert isinstance(e, (ast.Call, ast.Name)) decorator_name = '' if isinstance(e, ast.Call): # @task(args, ...) decorator_name = e.func.id elif isinstance(e, ast.Name): # @task [ie: naked] decorator_name = e.id if 'task' == decorator_name: visit_FunctionDef.task_count += 1 visit_FunctionDef.function_name = node.name visit_FunctionDef.function_params = [to.parse_val(arg) for arg in node.args.args] visit_FunctionDef.doc = ast.get_docstring(node) or ''
def visit_FunctionDef(node): '''visitor to function definitions, needed to extract decorator''' for e in node.decorator_list: assert isinstance(e, (ast.Call, ast.Name)) decorator_name = '' if isinstance(e, ast.Call): # @task(args, ...) decorator_name = e.func.id elif isinstance(e, ast.Name): # @task [ie: naked] decorator_name = e.id if 'task' == decorator_name: visit_FunctionDef.task_count += 1 visit_FunctionDef.function_name = node.name visit_FunctionDef.function_params = [ to.parse_val(arg) for arg in node.args.args ] visit_FunctionDef.doc = ast.get_docstring(node) or ''
def parse_properties_from_source(module_str): # pylint: disable=R0912 '''we need to find the task parameters, such that we can serve a them for the gui generation. However, we don't want to load the module, because we can't trust the code. So we parse the AST instead From: http://stackoverflow.com/a/9580006 ''' import ast props = { 'accepts': [], 'returns': [] } def visit_FunctionDef(node): '''visitor to function definitions, needed to extract decorator''' for e in node.decorator_list: assert isinstance(e, (ast.Call, ast.Name)) decorator_name = None if isinstance(e, ast.Call): # @task(args, ...) decorator_name = e.func.id for k in e.keywords: if k.arg in ('accepts', 'returns'): types = to.convert_to_types(to.parse_val(k.value)) types_list = to.make_iterable(types) if isinstance(types_list, tuple): types_list = list(types_list) props[k.arg] = types_list else: raise KeyError('decorator has unknown argument') elif isinstance(e, ast.Name): # @task [ie: naked] decorator_name = e.id if 'task' == decorator_name: visit_FunctionDef.task_count += 1 visit_FunctionDef.function_name = node.name visit_FunctionDef.function_params = [to.parse_val(arg) for arg in node.args.args] else: continue visit_FunctionDef.function_name = None visit_FunctionDef.function_params = [] visit_FunctionDef.task_count = 0 # number of decorators named task # extract inline meta properties inline_properties = dict(_task_full_name='full_name', _task_caption='caption', _task_description='description', _task_author='author', _task_categories='categories', _task_compatible_queues='compatible_queues') def visit_Assign(node): '''visitor to assigns, needed for inline properties''' if not hasattr(node.targets[0], 'id'): return if 1 == len(node.targets) and node.targets[0].id in inline_properties: value = to.parse_val(node.value) attr_name = inline_properties.pop(node.targets[0].id) props[attr_name] = value visitor = ast.NodeVisitor() visitor.visit_FunctionDef = visit_FunctionDef visitor.visit_Assign = visit_Assign visitor.visit(compile(module_str, '?', 'exec', ast.PyCF_ONLY_AST)) if 0 == visit_FunctionDef.task_count: raise ValueError('Task has no @task decorator') elif 1 < visit_FunctionDef.task_count: raise ValueError('Task has too many @task decorators') props['task_name'] = visit_FunctionDef.function_name # organize types in the accepts list to be the same as the function props['accepts'] = to.order_args(visit_FunctionDef.function_params, props['accepts']) props['accepts'] = \ [{'name': param_name, 'type': type_def} for param_name, type_def in zip(visit_FunctionDef.function_params, props['accepts'])] # this could be easily extracted from the decorator or docstring # but for the time being, let's just auto generate them func_rets = ['return_%d' % i for i in range(len(props['returns']))] props['returns'] = [{'name': ret_name, 'type': type_def} for ret_name, type_def in zip(func_rets, props['returns'])] return props
def parse_properties_from_source(module_str): # pylint: disable=R0912 '''we need to find the task parameters, such that we can serve a them for the gui generation. However, we don't want to load the module, because we can't trust the code. So we parse the AST instead From: http://stackoverflow.com/a/9580006 ''' import ast props = {'accepts': [], 'returns': []} def visit_FunctionDef(node): '''visitor to function definitions, needed to extract decorator''' for e in node.decorator_list: assert isinstance(e, (ast.Call, ast.Name)) decorator_name = None if isinstance(e, ast.Call): # @task(args, ...) decorator_name = e.func.id for k in e.keywords: if k.arg in ('accepts', 'returns'): types = to.convert_to_types(to.parse_val(k.value)) types_list = to.make_iterable(types) if isinstance(types_list, tuple): types_list = list(types_list) props[k.arg] = types_list else: raise KeyError('decorator has unknown argument') elif isinstance(e, ast.Name): # @task [ie: naked] decorator_name = e.id if 'task' == decorator_name: visit_FunctionDef.task_count += 1 visit_FunctionDef.function_name = node.name visit_FunctionDef.function_params = [ to.parse_val(arg) for arg in node.args.args ] else: continue visit_FunctionDef.function_name = None visit_FunctionDef.function_params = [] visit_FunctionDef.task_count = 0 # number of decorators named task # extract inline meta properties inline_properties = dict(_task_full_name='full_name', _task_caption='caption', _task_description='description', _task_author='author', _task_categories='categories', _task_compatible_queues='compatible_queues') def visit_Assign(node): '''visitor to assigns, needed for inline properties''' if not hasattr(node.targets[0], 'id'): return if 1 == len(node.targets) and node.targets[0].id in inline_properties: value = to.parse_val(node.value) attr_name = inline_properties.pop(node.targets[0].id) props[attr_name] = value visitor = ast.NodeVisitor() visitor.visit_FunctionDef = visit_FunctionDef visitor.visit_Assign = visit_Assign visitor.visit(compile(module_str, '?', 'exec', ast.PyCF_ONLY_AST)) if 0 == visit_FunctionDef.task_count: raise ValueError('Task has no @task decorator') elif 1 < visit_FunctionDef.task_count: raise ValueError('Task has too many @task decorators') props['task_name'] = visit_FunctionDef.function_name # organize types in the accepts list to be the same as the function props['accepts'] = to.order_args(visit_FunctionDef.function_params, props['accepts']) props['accepts'] = \ [{'name': param_name, 'type': type_def} for param_name, type_def in zip(visit_FunctionDef.function_params, props['accepts'])] # this could be easily extracted from the decorator or docstring # but for the time being, let's just auto generate them func_rets = ['return_%d' % i for i in range(len(props['returns']))] props['returns'] = [{ 'name': ret_name, 'type': type_def } for ret_name, type_def in zip(func_rets, props['returns'])] return props