根目录
robotide
1、入口函数
__init__.py:
def main(*args):
noupdatecheck, debug_console, inpath = _parse_args(args) # 命令解析 返回参数 noupdatecheck, debug_console, inpath 都传入Ride
def _parse_args(args):
noupdatecheck = '--noupdatecheck' in args
debug_console = '--debugconsole' in args
_run(inpath, not noupdatecheck, debug_console) 入口函数
def _run(inpath=None, updatecheck=True, debug_console=False):
# print(f"DEBUG: ENTER _run {inpath=}, {updatecheck=}, {debug_console=}")
try:
from robotide.application import RIDE # 主界面
from robotide.application import debugconsole # 调试后台
except ImportError:
_show_old_wxpython_warning_if_needed()
raise
ride = RIDE(inpath, updatecheck) # 初始化
if wx.VERSION <= (4, 0, 4, '', ''):
_show_old_wxpython_warning_if_needed(ride.frame)
else:
wx.CallAfter(_show_old_wxpython_warning_if_needed, ride.frame)
if debug_console:
wx.lib.inspection.InspectionTool().Show()
debugconsole.start(ride) # 启动debugconsole
ride.MainLoop() # 消息循环
robotide\application\application.py
class RIDE(wx.App):
def OnInit(self): # Overrides wx method
# DEBUG To test RTL
# self._initial_locale = wx.Locale(wx.LANGUAGE_ARABIC)
self._initial_locale = wx.Locale(wx.LANGUAGE_ENGLISH_US)
# Needed for SetToolTipString to work
wx.HelpProvider.Set(wx.SimpleHelpProvider()) # DEBUG: adjust to wx versions
# 配置读取
self.settings = RideSettings()
# 数据库初始化
librarydatabase.initialize_database()
# 配置
self.preferences = Preferences(self.settings)
# 初始化
self.namespace = Namespace(self.settings)
# 构建项目控制器
self._controller = Project(self.namespace, self.settings)
# 构建RideFrame
self.frame = RideFrame(self, self._controller)
# DEBUG self.frame.Show()
self._editor_provider = EditorProvider()
self._plugin_loader = PluginLoader(self, self._get_plugin_dirs(),
coreplugins.get_core_plugins())
self._plugin_loader.enable_plugins()
perspective = self.settings.get('AUI Perspective', None)
if perspective:
self.frame.aui_mgr.LoadPerspective(perspective, True)
try:
nb_perspective = self.settings.get('AUI NB Perspective', None)
if nb_perspective:
self.frame.notebook.LoadPerspective(nb_perspective)
except Exception as e:
print(f"RIDE: There was a problem loading panels position."
f" Please delete the definition 'AUI NB Perspective' in "
f"{os.path.join(context.SETTINGS_DIRECTORY, 'settings.cfg')}")
if not isinstance(e, IndexError): # If is with all notebooks disabled, continue
raise e
self.treeplugin = TreePlugin(self)
if self.treeplugin.settings['_enabled']:
self.treeplugin.register_frame(self.frame)
self.fileexplorerplugin = FileExplorerPlugin(self, self._controller)
if self.fileexplorerplugin.settings['_enabled']:
self.fileexplorerplugin.register_frame(self.frame)
if not self.treeplugin.opened:
self.treeplugin.close_tree()
# else:
# wx.CallLater(200, self.treeplugin.populate, self.model)
if not self.fileexplorerplugin.opened:
self.fileexplorerplugin.close_tree()
self.editor = self._get_editor()
self.robot_version = self._find_robot_installation()
# 加载数据-------
self._load_data()
self.treeplugin.populate(self.model)
self.treeplugin.set_editor(self.editor)
self._publish_system_info()
self.frame.Show() # ###### DEBUG DANGER ZONE
self.SetTopWindow(self.frame)
self.frame.aui_mgr.Update()
wx.CallLater(200, ReleaseNotes(self).bring_to_front)
wx.CallLater(200, self.fileexplorerplugin.update_tree)
if self._updatecheck:
wx.CallAfter(UpdateNotifierController(self.settings).notify_update_if_needed, UpdateDialog)
self.Bind(wx.EVT_ACTIVATE_APP, self.on_app_activate)
PUBLISHER.subscribe(self.SetGlobalColour, RideSettingsChanged)
PUBLISHER.subscribe(self.update_excludes, RideSettingsChanged)
RideSettingsChanged(keys=('Excludes', 'init'), old=None, new=None).publish()
return True
class Namespace(object):
def _init_caches(self):
# 库
self._lib_cache = LibraryCache(
self.settings, self.update, self._library_manager)
# 资源
self._resource_factory = ResourceFactory(self.settings)
# 数据文件 数据文件检索器
self._retriever = DatafileRetriever(self._lib_cache,
self._resource_factory, self)
self._context_factory = _RetrieverContextFactory()
class LibraryCache(object):
def __init__(self, settings, libraries_need_refresh_listener,
library_manager):
self._library_manager = None
self._settings = settings
if library_manager:
self.set_library_manager(library_manager) #设置_library_manager
self._libraries_need_refresh_listener = libraries_need_refresh_listener
self._library_keywords = {}
self.__default_libraries = None
self.__default_kws = None
class ResourceFactory(object):
_IGNORE_RESOURCE_DIRECTORY_SETTING_NAME = 'ignored resource directory'
def __init__(self, settings):
self.cache = {}
self.python_path_cache = {}
self._excludes = settings.excludes
self.check_path_from_excludes = self._excludes.contains
# print("DEBUG: ResourceFactory init path_excludes %s\n" % self.check_path_from_excludes)
class DatafileRetriever(object):
def __init__(self, lib_cache, resource_factory, namespace):
self._namespace = namespace
self._lib_cache = lib_cache
self._resource_factory = resource_factory
# 关键字缓存
self.keyword_cache = ExpiringCache() #过期缓存
self._default_kws = None
class Project(_BaseController, WithNamespace):
def __init__(self, namespace=None, settings=None, library_manager=None):
from .filecontrollers import ResourceFileControllerFactory
self._library_manager = self._construct_library_manager(library_manager, settings) # 静态方法 构建库管理器
if not self._library_manager.is_alive():
self._library_manager.start() # 开启_library_manager
self._name_space = namespace
self._set_namespace(self._name_space)
self.internal_settings = settings
self._loader = DataLoader(self._name_space, settings) # 数据加载器
self.controller = None
self.name = None
self.external_resources = []
self._resource_file_controller_factory = ResourceFileControllerFactory(self._name_space, self) # 构建资源文件控制器工厂
self._serializer = Serializer(settings, LOG) # 序列化
@staticmethod
def _construct_library_manager(library_manager, settings):
# 'C:\\Users\\Administrator\\AppData\\Roaming\\RobotFramework\\ride\\librarykeywords.db'
return library_manager or \
spec.LibraryManager(spec.DATABASE_FILE, SpecInitializer(settings.get('library xml directories', [])[:])) # 环境库地址
class LibraryManager(Thread): # 守护多线程
def __init__(self, database_name, spec_initializer=None):
self._database_name = database_name
self._database = None
self._messages = queue.Queue() # 消息队列
self._spec_initializer = spec_initializer or SpecInitializer()
Thread.__init__(self)
self.daemon = True
def run(self):
self._initiate_database_connection()
while True:
try:
if not self._handle_message():
break
except Exception as err:
msg = 'Library import handling threw an unexpected exception'
RideLogException(message=msg, exception=err, level='WARN').publish()
self._database.close()
def _handle_message(self):
# 库消息处理
message = self._messages.get()
if not message:
return False
msg_type = message[0]
if msg_type == 'fetch': # fetch
self._handle_fetch_keywords_message(message)
elif msg_type == 'insert': # insert
self._handle_insert_keywords_message(message)
elif msg_type == 'create': # create_database
self._database.create_database()
return True
def _handle_insert_keywords_message(self, message):
_, library_name, library_args, result_queue = message # message????何时传入
keywords = self._fetch_keywords(library_name, library_args)
self._insert(library_name, library_args, keywords,
lambda res: result_queue.put(res, timeout=3))
def _insert(self, library_name, library_args, keywords, callback):
self._database.insert_library_keywords( # LibraryDatabase::insert_library_keywords
library_name, library_args, keywords or [])
self._call(callback, keywords)
class DataLoader(object):
def __init__(self, namespace, settings):
self.namespace = namespace
self.namespace.reset_resource_and_library_cache() # namespace 重置资源库缓存
self._settings = settings
Namespace::def reset_resource_and_library_cache(self):
self._init_caches()
class ResourceFileControllerFactory(object):
def __init__(self, namespace, project):
self._resources = []
self._namespace = namespace
self._project = project
self._all_resource_imports_resolved = False
class Serializer(object):
def __init__(self, settings, logger):
self._settings = settings
self._logger = logger
self._errors = []
class RideFrame(wx.Frame):
def __init__(self, application, controller):
size = application.settings.get('mainframe size', (1100, 700))
# DEBUG self.general_settings = application.settings['General']
wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, title='RIDE',
pos=application.settings.get(MAINFRAME_POSITION, (50, 30)),
size=size, style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.BORDER_THEME)
# set Left to Right direction (while we don't have localization)
self.SetLayoutDirection(wx.Layout_LeftToRight)
# self.SetLayoutDirection(wx.Layout_RightToLeft)
self.aui_mgr = aui.AuiManager(self)
# tell AuiManager to manage this frame
self.aui_mgr.SetManagedWindow(self)
self.SetMinSize(wx.Size(400, 300))
self.ensure_on_screen()
if application.settings.get(MAINFRAME_MAXIMIZED, False):
self.Maximize()
self._application = application
self.controller = controller
self._image_provider = ImageProvider()
self.reformat = application.settings.get('reformat', False)
self.general_settings = application.settings['General'] # .get_without_default('General')
self.color_background_help = self.general_settings.get('background help', (240, 242, 80))
self.color_foreground_text = self.general_settings.get('foreground text', (7, 0, 70))
self.color_background = self.general_settings.get_without_default('background')
self.color_foreground = self.general_settings.get_without_default('foreground')
self.font_face = self.general_settings.get('font face', '')
self.font_size = self.general_settings.get('font size', 11)
self._init_ui()
self._task_bar_icon = RIDETaskBarIcon(self, self._image_provider)
self._plugin_manager = PluginManager(self.notebook)
self._review_dialog = None
self._view_all_tags_dialog = None
self._current_external_dir = None
self.Bind(wx.EVT_CLOSE, self.on_close)
self.Bind(wx.EVT_SIZE, self.on_size)
self.Bind(wx.EVT_MOVE, self.on_move)
self.Bind(wx.EVT_MAXIMIZE, self.on_maximize)
self.Bind(wx.EVT_DIRCTRL_FILEACTIVATED, self.on_open_file)
self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.on_menu_open_file)
self._subscribe_messages()
wx.CallAfter(self.actions.register_tools) # DEBUG
# DEBUG wx.CallAfter(self.OnSettingsChanged, self.general_settings)
RIDE::def _load_data(self):
self.workspace_path = self.workspace_path or self._get_latest_path()
if self.workspace_path:
# 更新当前默认项目地址
self._controller.update_default_dir(self.workspace_path)
# 加载进度服务
observer = LoadProgressObserver(self.frame)
self._controller.load_data(self.workspace_path, observer)
-->Project::def load_data(self, path, load_observer=None):
""" DEBUG: To be used in Localization
from robotide.context import APP
try:
robot_version = APP.robot_version
except AttributeError:
robot_version = b'6.1.1' # It is failing at unit tests
print(f"DEBUG: project.py Project ENTER robot version = {robot_version}")
"""
load_observer = load_observer or NullObserver()
if self._load_initfile(path, load_observer):
return
if self._load_datafile(path, load_observer):
return
if self._load_resource(path, load_observer):
return
try:
load_observer.error("Given file '%s' is not a valid Robot Framework "
"test case or resource file." % path)
except AttributeError: # DEBUG
# print(f"DEBUG: load_data error not valid datafile: {path}")
pass
-->_load_initfile
def _load_initfile(self, path, load_observer):
if os.path.splitext(os.path.split(path)[1])[0] != '__init__':
return None
initfile = self._loader.load_initfile(path, load_observer)
if not initfile:
return None
self._populate_from_datafile(path, initfile, load_observer)
return initfile
-->_load_datafile
-->_load_resource
OnInit--》
1、self.settings = RideSettings()
2、librarydatabase.initialize_database()
3、self.preferences = Preferences(self.settings)
4、self.namespace = Namespace(self.settings) --》
self._init_caches() # 缓存初始化--》
self._lib_cache = LibraryCache(self.settings, self.update, self._library_manager)--》
self._libraries_need_refresh_listener = libraries_need_refresh_listener==update
self._library_keywords = {}
self.__default_libraries = None
self.__default_kws = None
# 资源
self._resource_factory = ResourceFactory(self.settings)--》
self._excludes = settings.excludes
self.check_path_from_excludes = self._excludes.contains
# 数据文件 数据文件检索器
self._retriever = DatafileRetriever(self._lib_cache,self._resource_factory, self)--》
self._namespace = namespace
self._lib_cache = lib_cache
self._resource_factory = resource_factory
self.keyword_cache = ExpiringCache()
self._default_kws = None
self._context_factory = _RetrieverContextFactory()
self._set_pythonpath()
PUBLISHER.subscribe(self._setting_changed, RideSettingsChanged)
5、self._controller = Project(self.namespace, self.settings) --》
1、开启_library_manager线程
self._library_manager = self._construct_library_manager(library_manager, settings)
if not self._library_manager.is_alive():
self._library_manager.start()
2、self._set_namespace(self._name_space)
namespace.set_library_manager(self._library_manager)--》
def set_library_manager(self, library_manager):
self._library_manager = library_manager
self._lib_cache.set_library_manager(library_manager)--》
self._library_manager = library_manager
WithNamespace._set_namespace(self, namespace)
3、self._loader = DataLoader(self._name_space, settings) # 数据加载器
self.namespace.reset_resource_and_library_cache()
self._init_caches()
self._lib_cache = LibraryCache(self.settings, self.update, self._library_manager)
# 资源
self._resource_factory = ResourceFactory(self.settings)
# 数据文件 数据文件检索器
self._retriever = DatafileRetriever(self._lib_cache,self._resource_factory, self)
self._context_factory = _RetrieverContextFactory()
4、self._resource_file_controller_factory = ResourceFileControllerFactory(self._name_space, self)
self._resources = []
self._namespace = namespace
self._project = project
self._all_resource_imports_resolved = False
5、self._serializer = Serializer(settings, LOG)
self._settings = settings
self._logger = logger
self._errors = []
# 创建 RideFrame
6、self.frame = RideFrame(self, self._controller)
self.controller = controller
self._subscribe_messages()