From 00bb397b0dc79fcad27bfe63456a2100039706f2 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 27 Oct 2020 09:14:40 -0700 Subject: [lldb] Support Python imports relative the to the current file being sourced Make it possible to use a relative path in command script import to the location of the file being sourced. This allows the user to put Python scripts next to LLDB command files and importing them without having to specify an absolute path. To enable this behavior pass `-c` to `command script import`. The argument can only be used when sourcing the command from a file. rdar://68310384 Differential revision: https://reviews.llvm.org/D89334 --- .../Python/ScriptInterpreterPython.cpp | 111 ++++++++++++--------- 1 file changed, 66 insertions(+), 45 deletions(-) (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp') diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 8d4a698..e5802ad 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -2733,8 +2733,9 @@ uint64_t replace_all(std::string &str, const std::string &oldStr, bool ScriptInterpreterPythonImpl::LoadScriptingModule( const char *pathname, bool init_session, lldb_private::Status &error, - StructuredData::ObjectSP *module_sp) { + StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { namespace fs = llvm::sys::fs; + namespace path = llvm::sys::path; if (!pathname || !pathname[0]) { error.SetErrorString("invalid pathname"); @@ -2743,44 +2744,23 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this(); - FileSpec target_file(pathname); - FileSystem::Instance().Resolve(target_file); - FileSystem::Instance().Collect(target_file); - std::string basename(target_file.GetFilename().GetCString()); - - StreamString command_stream; - // Before executing Python code, lock the GIL. Locker py_lock(this, Locker::AcquireLock | (init_session ? Locker::InitSession : 0) | Locker::NoSTDIN, Locker::FreeAcquiredLock | (init_session ? Locker::TearDownSession : 0)); - fs::file_status st; - std::error_code ec = status(target_file.GetPath(), st); - - if (ec || st.type() == fs::file_type::status_error || - st.type() == fs::file_type::type_unknown || - st.type() == fs::file_type::file_not_found) { - // if not a valid file of any sort, check if it might be a filename still - // dot can't be used but / and \ can, and if either is found, reject - if (strchr(pathname, '\\') || strchr(pathname, '/')) { - error.SetErrorString("invalid pathname"); - return false; - } - basename = pathname; // not a filename, probably a package of some sort, - // let it go through - } else if (is_directory(st) || is_regular_file(st)) { - if (target_file.GetDirectory().IsEmpty()) { - error.SetErrorString("invalid directory name"); - return false; + + auto ExtendSysPath = [this](std::string directory) -> llvm::Error { + if (directory.empty()) { + return llvm::make_error( + "invalid directory name", llvm::inconvertibleErrorCode()); } - std::string directory = target_file.GetDirectory().GetCString(); replace_all(directory, "\\", "\\\\"); replace_all(directory, "'", "\\'"); - // now make sure that Python has "directory" in the search path + // Make sure that Python has "directory" in the search path. StreamString command_stream; command_stream.Printf("if not (sys.path.__contains__('%s')):\n " "sys.path.insert(1,'%s');\n\n", @@ -2792,27 +2772,68 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( .SetSetLLDBGlobals(false)) .Success(); if (!syspath_retval) { - error.SetErrorString("Python sys.path handling failed"); - return false; + return llvm::make_error( + "Python sys.path handling failed", llvm::inconvertibleErrorCode()); } + return llvm::Error::success(); + }; + + std::string module_name(pathname); + + if (extra_search_dir) { + if (llvm::Error e = ExtendSysPath(extra_search_dir.GetPath())) { + error = std::move(e); + return false; + } } else { - error.SetErrorString("no known way to import this module specification"); - return false; + FileSpec module_file(pathname); + FileSystem::Instance().Resolve(module_file); + FileSystem::Instance().Collect(module_file); + + fs::file_status st; + std::error_code ec = status(module_file.GetPath(), st); + + if (ec || st.type() == fs::file_type::status_error || + st.type() == fs::file_type::type_unknown || + st.type() == fs::file_type::file_not_found) { + // if not a valid file of any sort, check if it might be a filename still + // dot can't be used but / and \ can, and if either is found, reject + if (strchr(pathname, '\\') || strchr(pathname, '/')) { + error.SetErrorString("invalid pathname"); + return false; + } + // Not a filename, probably a package of some sort, let it go through. + } else if (is_directory(st) || is_regular_file(st)) { + if (module_file.GetDirectory().IsEmpty()) { + error.SetErrorString("invalid directory name"); + return false; + } + if (llvm::Error e = + ExtendSysPath(module_file.GetDirectory().GetCString())) { + error = std::move(e); + return false; + } + module_name = module_file.GetFilename().GetCString(); + } else { + error.SetErrorString("no known way to import this module specification"); + return false; + } } // Strip .py or .pyc extension - llvm::StringRef extension = target_file.GetFileNameExtension().GetCString(); + llvm::StringRef extension = llvm::sys::path::extension(module_name); if (!extension.empty()) { if (extension == ".py") - basename.resize(basename.length() - 3); + module_name.resize(module_name.length() - 3); else if (extension == ".pyc") - basename.resize(basename.length() - 4); + module_name.resize(module_name.length() - 4); } // check if the module is already import-ed + StreamString command_stream; command_stream.Clear(); - command_stream.Printf("sys.modules.__contains__('%s')", basename.c_str()); + command_stream.Printf("sys.modules.__contains__('%s')", module_name.c_str()); bool does_contain = false; // this call will succeed if the module was ever imported in any Debugger // in the lifetime of the process in which this LLDB framework is living @@ -2827,9 +2848,9 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( // this call will fail if the module was not imported in this Debugger // before command_stream.Clear(); - command_stream.Printf("sys.getrefcount(%s)", basename.c_str()); + command_stream.Printf("sys.getrefcount(%s)", module_name.c_str()); bool was_imported_locally = GetSessionDictionary() - .GetItemForKey(PythonString(basename)) + .GetItemForKey(PythonString(module_name)) .IsAllocated(); bool was_imported = (was_imported_globally || was_imported_locally); @@ -2839,12 +2860,12 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( if (was_imported) { if (!was_imported_locally) - command_stream.Printf("import %s ; reload_module(%s)", basename.c_str(), - basename.c_str()); + command_stream.Printf("import %s ; reload_module(%s)", + module_name.c_str(), module_name.c_str()); else - command_stream.Printf("reload_module(%s)", basename.c_str()); + command_stream.Printf("reload_module(%s)", module_name.c_str()); } else - command_stream.Printf("import %s", basename.c_str()); + command_stream.Printf("import %s", module_name.c_str()); error = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions() @@ -2855,8 +2876,8 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( // if we are here, everything worked // call __lldb_init_module(debugger,dict) - if (!LLDBSwigPythonCallModuleInit(basename.c_str(), m_dictionary_name.c_str(), - debugger_sp)) { + if (!LLDBSwigPythonCallModuleInit(module_name.c_str(), + m_dictionary_name.c_str(), debugger_sp)) { error.SetErrorString("calling __lldb_init_module failed"); return false; } @@ -2864,7 +2885,7 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( if (module_sp) { // everything went just great, now set the module object command_stream.Clear(); - command_stream.Printf("%s", basename.c_str()); + command_stream.Printf("%s", module_name.c_str()); void *module_pyobj = nullptr; if (ExecuteOneLineWithReturn( command_stream.GetData(), -- cgit v1.1