def _run_task(self): kwfiles = [] processed_files = [] for library_name in self.options["path"]: kwfile = KeywordFile(library_name) try: if self.is_pageobject_library(library_name): PageObjects._reset() module = Importer().import_class_or_module_by_path( os.path.abspath(library_name)) kwfile.doc = module.__doc__ if hasattr(module, "TITLE"): kwfile.title = module.TITLE for pobj_name, pobj in sorted( PageObjects.registry.items()): pobj = PageObjects.registry[pobj_name] libname = "{}.{}".format(pobj.__module__, pobj.__name__) libdoc = LibraryDocBuilder().build(libname) libdoc.src = os.path.basename(library_name) libdoc.pobj = libname kwfile.add_keywords(libdoc, pobj_name) else: libdoc = DocumentationBuilder(library_name).build( library_name) kwfile.add_keywords(libdoc) # if we get here, we were able to process the file correctly kwfiles.append(kwfile) processed_files.append(library_name) except RobotNotRunningError as e: # oddly, robot's exception has a traceback embedded in the message, so we'll # only print out the first line to hide most of the noise self.logger.warn("unexpected error: {}".format( str(e).split("\n")[0])) try: with open(self.options["output"], "w") as f: html = self._render_html(kwfiles) f.write(html) self.logger.info("created {}".format(f.name)) except Exception as e: raise TaskOptionsError( "Unable to create output file '{}' ({})".format( self.options["output"], e.strerror)) return {"files": processed_files, "html": html}
def get_lib_keywords(lib_name): """Get keywords of library lib_name""" lib = LibraryDocBuilder().build(lib_name) keywords = [] for keyword in lib.keywords: doc = keyword.doc.split('\n')[0] keywords.append({'name': keyword.name, 'lib': lib_name, 'doc': doc}) return keywords
def create_python_tests(fout): """ Walk the python keywords and create tests """ for keyword in LibraryDocBuilder().build('FusionLibrary').keywords: print(keyword.name) fout.write('Test {}\n'.format(keyword.name)) fout.write(' [Documentation] {}\n'.format(keyword.name)) fout.write(' {} '.format(keyword.name)) for arg in keyword.args: if '=' in arg: fout.write(' {} '.format(arg.split('=')[-1])) else: if arg.startswith('&'): fout.write(' testk=testv ') else: fout.write(' test ') fout.write('\n\n')
def __init__(self): self.file_path = None self.rf_variables = Variables() self.rf_var_storage = VariableStore(self.rf_variables) self.libdoc = LibraryDocBuilder()
class DataParser(): """ This class is used to parse different tables in test data. Class will return the the test data as in json format. Can parse Python libraries, library xml documentation generated by the libdoc resource and test suite files. """ def __init__(self): self.file_path = None self.rf_variables = Variables() self.rf_var_storage = VariableStore(self.rf_variables) self.libdoc = LibraryDocBuilder() def parse_resource(self, file_path): self.file_path = file_path if path.exists(file_path): if '__init__.' in file_path: folder = path.dirname(file_path) model = parsing.TestDataDirectory(source=folder).populate() else: model = parsing.ResourceFile(file_path).populate() return self._parse_robot_data(file_path, model) else: logging.error('File %s could not be found', file_path) raise ValueError('File does not exist: {0}'.format(file_path)) def parse_suite(self, file_path): self.file_path = file_path if path.exists(file_path): model = parsing.TestCaseFile(source=file_path).populate() return self._parse_robot_data(file_path, model) else: logging.error('File %s could not be found', file_path) raise ValueError('File does not exist: {0}'.format(file_path)) def parse_variable_file(self, file_path, args=None): if not args: args = [] data = {} data[DBJsonSetting.file_name] = path.basename(file_path) data[DBJsonSetting.file_path] = normalise_path(file_path) self.file_path = file_path setter = VariableFileSetter(self.rf_var_storage) var_list = [] try: variables = setter.set(file_path, args) except DataError: variables = [] for variable in variables: var_list.append(variable[0]) data[DBJsonSetting.variables] = sorted(var_list) return data def parse_library(self, library, args=None): """Parses RF library to dictionary Uses internally libdoc modules to parse the library. Possible arguments to the library are provided in the args parameter. """ data = {} if not args: data[DBJsonSetting.arguments] = [] else: arg_list = [] for arg in args: arg_list.append(arg) data[DBJsonSetting.arguments] = arg_list if path.isfile(library): data[DBJsonSetting.file_path] = normalise_path(library) if library.endswith('.xml'): library_module, keywords = self._parse_xml_doc(library) data[DBJsonSetting.keywords] = keywords data[DBJsonSetting.library_module] = library_module elif library.endswith('.py'): data[DBJsonSetting.file_name] = path.basename(library) data[DBJsonSetting.library_module] = path.splitext( data[DBJsonSetting.file_name])[0] data[DBJsonSetting.keywords] = self._parse_python_lib( library, data[DBJsonSetting.arguments]) else: raise ValueError('Unknown library') else: library, data = self._locate_file_from_path(library, data) data[DBJsonSetting.keywords] = self._parse_python_lib( library, data[DBJsonSetting.arguments]) if data[DBJsonSetting.keywords] is None: raise ValueError('Library did not contain keywords') else: return data @staticmethod def _locate_file_from_path(library, data): file_name = path.basename(library) for path_ in sys.path: file_path = path.join(path_, file_name) if path.isfile(file_path): data[DBJsonSetting.file_name] = path.basename(file_path) data[DBJsonSetting.library_module] = path.splitext( data[DBJsonSetting.file_name])[0] return file_path, data data[DBJsonSetting.library_module] = library return library, data def register_console_logger(self): ROBOT_LOGGER.register_console_logger() def unregister_console_logger(self): ROBOT_LOGGER.unregister_console_logger() def close_logger(self): ROBOT_LOGGER.close() def _parse_python_lib(self, library, args): lib_with_args = self._lib_arg_formatter(library, args) kws = {} try: lib = self.libdoc.build(lib_with_args) except DataError: raise ValueError('Library does not exist: {0}'.format(library)) if library in STDLIBS: import_name = 'robot.libraries.' + library else: import_name = library importer = Importer('test library') libcode = importer.import_class_or_module(import_name, return_source=False) kw_with_deco = self._get_keywords_with_robot_name(libcode) for keyword in lib.keywords: kw = {} kw[DBJsonSetting.keyword_name] = keyword.name kw[DBJsonSetting.tags] = list(keyword.tags._tags) kw[DBJsonSetting.keyword_arguments] = keyword.args kw[DBJsonSetting.documentation] = keyword.doc if keyword.name in kw_with_deco: function_name = kw_with_deco[keyword.name] else: function_name = keyword.name kw[DBJsonSetting.keyword_file] = self._get_library_kw_source( libcode, function_name) kws[strip_and_lower(keyword.name)] = kw return kws def _get_keywords_with_robot_name(self, libcode): """Returns keywords which uses Robot keyword decorator with robot_name The keyword name can be chaned with Robot Framework keyword decorator and by using the robot_name attribute. Return dictinionary which key is the value of the robot_name attribute and the orinal function name. """ kw_deco = {} for key in libcode.__dict__: if callable(libcode.__dict__[key]): try: if 'robot_name' in libcode.__dict__[key].__dict__: kw = libcode.__dict__[key].__dict__['robot_name'] kw_deco[kw] = key except AttributeError: pass return kw_deco def _get_library_kw_source(self, libcode, keyword): kw_func = keyword.lower().replace(' ', '_') func = None func_file = None if hasattr(libcode, kw_func): func = getattr(libcode, kw_func) if func: kw_class = self.get_class_that_defined_method(func) if kw_class: func_file = self.get_function_file(kw_class) else: func_file = self.get_function_file(func) return func_file def get_class_that_defined_method(self, meth): try: class_mro = inspect.getmro(meth.im_class) except AttributeError: return None for cls in class_mro: if meth.__name__ in cls.__dict__: return cls return None def get_function_file(self, kw_class): file_ = inspect.getsourcefile(kw_class) if file_ and path.exists(file_): return normalise_path(file_) else: return None def _lib_arg_formatter(self, library, args): args = self._argument_path_formatter(library, args) if not args: return library else: for item in args: library = '{lib}::{item}'.format(lib=library, item=item) return library def _argument_path_formatter(self, library, args): """Replace robot folder with real path If ${/}, ${OUTPUT_DIR} or ${EXECDIR} is found from args then a temporary directory is created and that one is used instead.""" arguments = [] for arg in args: if '${/}' in arg or '${OUTPUT_DIR}' in arg or '${EXECDIR}' in arg: f = mkdtemp() logging.info( 'Possible robot path encountered in library arguments') logging.debug('In library %s', library) logging.debug('Instead of %s using: %s', arg, f) arguments.append(f) else: arguments.append(arg) return arguments def _parse_xml_doc(self, library): root = ET.parse(library).getroot() if ('type', DBJsonSetting.library) in root.items(): return root.attrib['name'], self._parse_xml_lib(root) else: raise ValueError('XML file is not library: {0}'.format( root.attrib['name'])) def _parse_xml_lib(self, root): kws = {} for element in root.findall('kw'): kw = {} kw[DBJsonSetting.keyword_file] = None kw[DBJsonSetting.keyword_name] = element.attrib['name'] kw[DBJsonSetting.documentation] = element.find('doc').text tags = [] [tags.append(tag.text) for tag in element.findall('.//tags/tag')] kw[DBJsonSetting.tags] = tags arg = [] [ arg.append(tag.text) for tag in element.findall('.//arguments/arg') ] kw[DBJsonSetting.keyword_arguments] = arg kws[strip_and_lower(kw[DBJsonSetting.keyword_name])] = kw return kws def _parse_robot_data(self, file_path, model): data = {} data[DBJsonSetting.file_name] = path.basename(file_path) data[DBJsonSetting.file_path] = normalise_path(file_path) data[DBJsonSetting.keywords] = self._get_keywords(model) data[DBJsonSetting.variables] = self._get_global_variables(model) lib, res, v_files = self._get_imports( model, path.dirname(normalise_path(file_path)), file_path) data[DBJsonSetting.resources] = res data[DBJsonSetting.libraries] = lib data[DBJsonSetting.variable_files] = v_files return data def _get_keywords(self, model): kw_data = {} for kw in model.keywords: tmp = {} tmp[DBJsonSetting.keyword_arguments] = kw.args.value tmp[DBJsonSetting.documentation] = kw.doc.value tmp[DBJsonSetting.tags] = kw.tags.value tmp[DBJsonSetting.keyword_name] = kw.name kw_data[strip_and_lower(kw.name)] = tmp return kw_data def _get_imports(self, model, file_dir, file_path): lib = [] res = [] var_files = [] for setting in model.setting_table.imports: if setting.type == 'Library': lib.append(self._format_library(setting, file_dir)) elif setting.type == 'Resource': res.append(self._format_resource(setting, file_path)) elif setting.type == 'Variables': var_files.append(self._format_variable_file(setting)) return lib, res, var_files def _format_library(self, setting, file_dir): data = {} lib_name = setting.name if lib_name.endswith('.py') and not path.isfile(lib_name): lib_path = path.abspath(path.join(file_dir, lib_name)) lib_name = path.basename(lib_path) elif lib_name.endswith('.py') and path.isfile(lib_name): lib_path = normalise_path(lib_name) lib_name = path.basename(lib_name) else: lib_path = None data[DBJsonSetting.library_name] = lib_name data[DBJsonSetting.library_alias] = setting.alias data[DBJsonSetting.library_arguments] = setting.args data[DBJsonSetting.library_path] = lib_path return data def _format_resource(self, setting, file_path): if path.isabs(setting.name): return setting.name else: c_dir = path.dirname(self.file_path) resource_path = normalise_path(path.join(c_dir, setting.name)) if not path.isfile(resource_path): print('Import failure on file: {0},'.format(file_path), 'could not locate: {0}'.format(setting.name)) return resource_path def _format_variable_file(self, setting): data = {} v_path = normalise_path( path.join(path.dirname(self.file_path), setting.name)) args = {} args['variable_file_arguments'] = setting.args data[v_path] = args return data def _get_global_variables(self, model): var_data = [] for var in model.variable_table.variables: if var: var_data.append(var.name) return var_data
class DataParser(): """ This class is used to parse different tables in test data. Class will return the the test data as in json format. Can parse Python libraries, library xml documentation generated by the libdoc resource and test suite files. """ def __init__(self): self.file_path = None self.rf_variables = Variables() self.rf_var_storage = VariableStore(self.rf_variables) self.libdoc = LibraryDocBuilder() def parse_resource(self, file_path): self.file_path = file_path if path.exists(file_path): if '__init__.' in file_path: folder = path.dirname(file_path) model = parsing.TestDataDirectory(source=folder).populate() else: model = parsing.ResourceFile(file_path).populate() return self._parse_robot_data(file_path, model) else: logging.error('File %s could not be found', file_path) raise ValueError( 'File does not exist: {0}'.format(file_path)) def parse_suite(self, file_path): self.file_path = file_path if path.exists(file_path): model = parsing.TestCaseFile(source=file_path).populate() return self._parse_robot_data(file_path, model) else: logging.error('File %s could not be found', file_path) raise ValueError( 'File does not exist: {0}'.format(file_path)) def parse_variable_file(self, file_path, args=None): if not args: args = [] data = {} data[DBJsonSetting.file_name] = path.basename(file_path) data[DBJsonSetting.file_path] = normalise_path(file_path) self.file_path = file_path setter = VariableFileSetter(self.rf_var_storage) var_list = [] try: variables = setter.set(file_path, args) except DataError: variables = [] for variable in variables: var_list.append(variable[0]) data[DBJsonSetting.variables] = sorted(var_list) return data def parse_library(self, library, args=None): """Parses RF library to dictionary Uses internally libdoc modules to parse the library. Possible arguments to the library are provided in the args parameter. """ data = {} if not args: data[DBJsonSetting.arguments] = [] else: arg_list = [] for arg in args: arg_list.append(arg) data[DBJsonSetting.arguments] = arg_list if path.isfile(library): data[DBJsonSetting.file_path] = normalise_path(library) if library.endswith('.xml'): library_module, keywords = self._parse_xml_doc(library) data[DBJsonSetting.keywords] = keywords data[DBJsonSetting.library_module] = library_module elif library.endswith('.py'): data[DBJsonSetting.file_name] = path.basename(library) data[DBJsonSetting.library_module] = path.splitext( data[DBJsonSetting.file_name])[0] data[DBJsonSetting.keywords] = self._parse_python_lib( library, data[DBJsonSetting.arguments]) else: raise ValueError('Unknown library') else: data[DBJsonSetting.library_module] = library data[DBJsonSetting.keywords] = self._parse_python_lib( library, data[DBJsonSetting.arguments]) if data[DBJsonSetting.keywords] is None: raise ValueError('Library did not contain keywords') else: return data def register_console_logger(self): ROBOT_LOGGER.register_console_logger() def unregister_console_logger(self): ROBOT_LOGGER.unregister_console_logger() def close_logger(self): ROBOT_LOGGER.close() def _parse_python_lib(self, library, args): lib_with_args = self._lib_arg_formatter(library, args) kws = {} try: lib = self.libdoc.build(lib_with_args) except DataError: raise ValueError( 'Library does not exist: {0}'.format(library)) if library in STDLIBS: import_name = 'robot.libraries.' + library else: import_name = library importer = Importer('test library') libcode = importer.import_class_or_module( import_name, return_source=False) kw_with_deco = self._get_keywords_with_robot_name(libcode) for keyword in lib.keywords: kw = {} kw[DBJsonSetting.keyword_name] = keyword.name kw[DBJsonSetting.tags] = list(keyword.tags._tags) kw[DBJsonSetting.keyword_arguments] = keyword.args kw[DBJsonSetting.documentation] = keyword.doc if keyword.name in kw_with_deco: function_name = kw_with_deco[keyword.name] else: function_name = keyword.name kw[DBJsonSetting.keyword_file] = self._get_library_kw_source( libcode, function_name) kws[strip_and_lower(keyword.name)] = kw return kws def _get_keywords_with_robot_name(self, libcode): """Returns keywords which uses Robot keyword decorator with robot_name The keyword name can be chaned with Robot Framework keyword decorator and by using the robot_name attribute. Return dictinionary which key is the value of the robot_name attribute and the orinal function name. """ kw_deco = {} for key in libcode.__dict__: if callable(libcode.__dict__[key]): try: if 'robot_name' in libcode.__dict__[key].__dict__: kw = libcode.__dict__[key].__dict__['robot_name'] kw_deco[kw] = key except AttributeError: pass return kw_deco def _get_library_kw_source(self, libcode, keyword): kw_func = keyword.lower().replace(' ', '_') func = None func_file = None if hasattr(libcode, kw_func): func = getattr(libcode, kw_func) if func: kw_class = self.get_class_that_defined_method(func) if kw_class: func_file = self.get_function_file(kw_class) else: func_file = self.get_function_file(func) return func_file def get_class_that_defined_method(self, meth): try: class_mro = inspect.getmro(meth.im_class) except AttributeError: return None for cls in class_mro: if meth.__name__ in cls.__dict__: return cls return None def get_function_file(self, kw_class): file_ = inspect.getsourcefile(kw_class) if file_ and path.exists(file_): return normalise_path(file_) else: return None def _lib_arg_formatter(self, library, args): args = self._argument_path_formatter(library, args) if not args: return library else: for item in args: library = '{lib}::{item}'.format(lib=library, item=item) return library def _argument_path_formatter(self, library, args): """Replace robot folder with real path If ${/}, ${OUTPUT_DIR} or ${EXECDIR} is found from args then a temporary directory is created and that one is used instead.""" arguments = [] for arg in args: if '${/}' in arg or '${OUTPUT_DIR}' in arg or '${EXECDIR}' in arg: f = mkdtemp() logging.info( 'Possible robot path encountered in library arguments') logging.debug('In library %s', library) logging.debug('Instead of %s using: %s', arg, f) arguments.append(f) else: arguments.append(arg) return arguments def _parse_xml_doc(self, library): root = ET.parse(library).getroot() if ('type', DBJsonSetting.library) in root.items(): return root.attrib['name'], self._parse_xml_lib(root) else: raise ValueError('XML file is not library: {0}'.format( root.attrib['name']) ) def _parse_xml_lib(self, root): kws = {} for element in root.findall('kw'): kw = {} kw[DBJsonSetting.keyword_file] = None kw[DBJsonSetting.keyword_name] = element.attrib['name'] kw[DBJsonSetting.documentation] = element.find('doc').text tags = [] [tags.append(tag.text) for tag in element.findall('.//tags/tag')] kw[DBJsonSetting.tags] = tags arg = [] [arg.append(tag.text) for tag in element.findall('.//arguments/arg')] kw[DBJsonSetting.keyword_arguments] = arg kws[strip_and_lower(kw[DBJsonSetting.keyword_name])] = kw return kws def _parse_robot_data(self, file_path, model): data = {} data[DBJsonSetting.file_name] = path.basename(file_path) data[DBJsonSetting.file_path] = normalise_path(file_path) data[DBJsonSetting.keywords] = self._get_keywords(model) data[DBJsonSetting.variables] = self._get_global_variables(model) lib, res, v_files = self._get_imports( model, path.dirname(normalise_path(file_path)), file_path ) data[DBJsonSetting.resources] = res data[DBJsonSetting.libraries] = lib data[DBJsonSetting.variable_files] = v_files return data def _get_keywords(self, model): kw_data = {} for kw in model.keywords: tmp = {} tmp[DBJsonSetting.keyword_arguments] = kw.args.value tmp[DBJsonSetting.documentation] = kw.doc.value tmp[DBJsonSetting.tags] = kw.tags.value tmp[DBJsonSetting.keyword_name] = kw.name kw_data[strip_and_lower(kw.name)] = tmp return kw_data def _get_imports(self, model, file_dir, file_path): lib = [] res = [] var_files = [] for setting in model.setting_table.imports: if setting.type == 'Library': lib.append(self._format_library(setting, file_dir)) elif setting.type == 'Resource': res.append(self._format_resource(setting, file_path)) elif setting.type == 'Variables': var_files.append(self._format_variable_file(setting)) return lib, res, var_files def _format_library(self, setting, file_dir): data = {} lib_name = setting.name if lib_name.endswith('.py') and not path.isfile(lib_name): lib_path = path.abspath(path.join(file_dir, lib_name)) lib_name = path.basename(lib_path) elif lib_name.endswith('.py') and path.isfile(lib_name): lib_path = normalise_path(lib_name) lib_name = path.basename(lib_name) else: lib_path = None data[DBJsonSetting.library_name] = lib_name data[DBJsonSetting.library_alias] = setting.alias data[DBJsonSetting.library_arguments] = setting.args data[DBJsonSetting.library_path] = lib_path return data def _format_resource(self, setting, file_path): if path.isabs(setting.name): return setting.name else: c_dir = path.dirname(self.file_path) resource_path = normalise_path(path.join(c_dir, setting.name)) if not path.isfile(resource_path): print ('Import failure on file: {0},'.format(file_path), 'could not locate: {0}'.format(setting.name)) return resource_path def _format_variable_file(self, setting): data = {} v_path = normalise_path(path.join( path.dirname(self.file_path), setting.name)) args = {} args['variable_file_arguments'] = setting.args data[v_path] = args return data def _get_global_variables(self, model): var_data = [] for var in model.variable_table.variables: if var: var_data.append(var.name) return var_data