[Lldb-commits] [lldb] r374911 - SBFile::GetFile: convert SBFile back into python native files.

Lawrence D'Anna via lldb-commits lldb-commits at lists.llvm.org
Tue Oct 15 09:46:28 PDT 2019


Author: lawrence_danna
Date: Tue Oct 15 09:46:27 2019
New Revision: 374911

URL: http://llvm.org/viewvc/llvm-project?rev=374911&view=rev
Log:
SBFile::GetFile: convert SBFile back into python native files.

Summary:
This makes SBFile::GetFile public and adds a SWIG typemap to convert
the result back into a python native file.

If the underlying File itself came from a python file, it is returned
identically.   Otherwise a new python file object is created using
the file descriptor.

Reviewers: JDevlieghere, jasonmolenda, labath

Reviewed By: labath

Subscribers: lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D68737

Modified:
    lldb/trunk/include/lldb/API/SBFile.h
    lldb/trunk/include/lldb/Host/File.h
    lldb/trunk/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
    lldb/trunk/scripts/Python/python-typemaps.swig
    lldb/trunk/scripts/interface/SBFile.i
    lldb/trunk/source/API/SBFile.cpp
    lldb/trunk/source/Host/common/File.cpp
    lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
    lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h

Modified: lldb/trunk/include/lldb/API/SBFile.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBFile.h?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/SBFile.h (original)
+++ lldb/trunk/include/lldb/API/SBFile.h Tue Oct 15 09:46:27 2019
@@ -36,6 +36,8 @@ public:
   operator bool() const;
   bool operator!() const;
 
+  FileSP GetFile() const;
+
 private:
   FileSP m_opaque_sp;
 };

Modified: lldb/trunk/include/lldb/Host/File.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Host/File.h?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Host/File.h (original)
+++ lldb/trunk/include/lldb/Host/File.h Tue Oct 15 09:46:27 2019
@@ -62,6 +62,8 @@ public:
   static mode_t ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options);
   static llvm::Expected<OpenOptions> GetOptionsFromMode(llvm::StringRef mode);
   static bool DescriptorIsValid(int descriptor) { return descriptor >= 0; };
+  static llvm::Expected<const char *>
+  GetStreamOpenModeFromOptions(OpenOptions options);
 
   File()
       : IOObject(eFDTypeFile), m_is_interactive(eLazyBoolCalculate),
@@ -317,6 +319,25 @@ public:
   ///     format string \a format.
   virtual size_t PrintfVarArg(const char *format, va_list args);
 
+  /// Return the OpenOptions for this file.
+  ///
+  /// Some options like eOpenOptionDontFollowSymlinks only make
+  /// sense when a file is being opened (or not at all)
+  /// and may not be preserved for this method.  But any valid
+  /// File should return either or both of eOpenOptionRead and
+  /// eOpenOptionWrite here.
+  ///
+  /// \return
+  ///    OpenOptions flags for this file, or an error.
+  virtual llvm::Expected<OpenOptions> GetOptions() const;
+
+  llvm::Expected<const char *> GetOpenMode() const {
+    auto opts = GetOptions();
+    if (!opts)
+      return opts.takeError();
+    return GetStreamOpenModeFromOptions(opts.get());
+  }
+
   /// Get the permissions for a this file.
   ///
   /// \return
@@ -352,6 +373,10 @@ public:
 
   bool operator!() const { return !IsValid(); };
 
+  static char ID;
+  virtual bool isA(const void *classID) const { return classID == &ID; }
+  static bool classof(const File *file) { return file->isA(&ID); }
+
 protected:
   LazyBool m_is_interactive;
   LazyBool m_is_real_terminal;
@@ -399,6 +424,13 @@ public:
   Status Flush() override;
   Status Sync() override;
   size_t PrintfVarArg(const char *format, va_list args) override;
+  llvm::Expected<OpenOptions> GetOptions() const override;
+
+  static char ID;
+  virtual bool isA(const void *classID) const override {
+    return classID == &ID || File::isA(classID);
+  }
+  static bool classof(const File *file) { return file->isA(&ID); }
 
 protected:
   bool DescriptorIsValid() const {

Modified: lldb/trunk/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py Tue Oct 15 09:46:27 2019
@@ -772,7 +772,21 @@ class FileHandleTestCase(lldbtest.TestBa
 
 
     @add_test_categories(['pyapi'])
-    @expectedFailureAll() # FIXME implement SBFile::GetFile
+    def test_stdout_file(self):
+        with open(self.out_filename, 'w') as f:
+            status = self.debugger.SetOutputFile(f)
+            self.assertTrue(status.Success())
+            self.handleCmd(r"script sys.stdout.write('foobar\n')")
+        with open(self.out_filename, 'r') as f:
+            # In python2 sys.stdout.write() returns None, which
+            # the REPL will ignore, but in python3 it will
+            # return the number of bytes written, which the REPL
+            # will print out.
+            lines = [x for x in f.read().strip().split() if x != "7"]
+            self.assertEqual(lines, ["foobar"])
+
+
+    @add_test_categories(['pyapi'])
     @skipIf(py_version=['<', (3,)])
     def test_identity(self):
 
@@ -826,3 +840,22 @@ class FileHandleTestCase(lldbtest.TestBa
 
         with open(self.out_filename, 'r') as f:
             self.assertEqual("foobar", f.read().strip())
+
+
+    @add_test_categories(['pyapi'])
+    def test_back_and_forth(self):
+        with open(self.out_filename, 'w') as f:
+            # at each step here we're borrowing the file, so we have to keep
+            # them all alive until the end.
+            sbf = lldb.SBFile.Create(f, borrow=True)
+            def i(sbf):
+                for i in range(10):
+                    f = sbf.GetFile()
+                    yield f
+                    sbf = lldb.SBFile.Create(f, borrow=True)
+                    yield sbf
+                    sbf.Write(str(i).encode('ascii') + b"\n")
+            files = list(i(sbf))
+        with open(self.out_filename, 'r') as f:
+            self.assertEqual(list(range(10)), list(map(int, f.read().strip().split())))
+

Modified: lldb/trunk/scripts/Python/python-typemaps.swig
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/Python/python-typemaps.swig?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/scripts/Python/python-typemaps.swig (original)
+++ lldb/trunk/scripts/Python/python-typemaps.swig Tue Oct 15 09:46:27 2019
@@ -434,6 +434,22 @@ bool SetNumberFromPyObject<double>(doubl
   }
 }
 
+%typemap(out) lldb::FileSP {
+  using namespace lldb_private;
+  $result = nullptr;
+  lldb::FileSP &sp = $1;
+  if (sp) {
+    PythonFile pyfile = unwrapOrSetPythonException(PythonFile::FromFile(*sp));
+    if (!pyfile.IsValid())
+      return nullptr;
+    $result = pyfile.release();
+  }
+  if (!$result)
+  {
+      $result = Py_None;
+      Py_INCREF(Py_None);
+  }
+}
 
 // FIXME both of these paths wind up calling fdopen() with no provision for ever calling
 // fclose() on the result.  SB interfaces that use FILE* should be deprecated for scripting

Modified: lldb/trunk/scripts/interface/SBFile.i
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/interface/SBFile.i?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/scripts/interface/SBFile.i (original)
+++ lldb/trunk/scripts/interface/SBFile.i Tue Oct 15 09:46:27 2019
@@ -77,6 +77,23 @@ public:
     operator bool() const;
 
     SBError Close();
+
+    %feature("docstring", "
+    Convert this SBFile into a python io.IOBase file object.
+
+    If the SBFile is itself a wrapper around a python file object,
+    this will return that original object.
+
+    The file returned from here should be considered borrowed,
+    in the sense that you may read and write to it, and flush it,
+    etc, but you should not close it.   If you want to close the
+    SBFile, call SBFile.Close().
+
+    If there is no underlying python file to unwrap, GetFile will
+    use the file descriptor, if availble to create a new python
+    file object using `open(fd, mode=..., closefd=False)`
+    ");
+    FileSP GetFile();
 };
 
 } // namespace lldb

Modified: lldb/trunk/source/API/SBFile.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBFile.cpp?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/source/API/SBFile.cpp (original)
+++ lldb/trunk/source/API/SBFile.cpp Tue Oct 15 09:46:27 2019
@@ -108,6 +108,11 @@ bool SBFile::operator!() const {
   return LLDB_RECORD_RESULT(!IsValid());
 }
 
+FileSP SBFile::GetFile() const {
+  LLDB_RECORD_METHOD_CONST_NO_ARGS(FileSP, SBFile, GetFile);
+  return m_opaque_sp;
+}
+
 namespace lldb_private {
 namespace repro {
 
@@ -117,6 +122,7 @@ template <> void RegisterMethods<SBFile>
   LLDB_REGISTER_METHOD_CONST(bool, SBFile, IsValid, ());
   LLDB_REGISTER_METHOD_CONST(bool, SBFile, operator bool,());
   LLDB_REGISTER_METHOD_CONST(bool, SBFile, operator!,());
+  LLDB_REGISTER_METHOD_CONST(FileSP, SBFile, GetFile, ());
   LLDB_REGISTER_METHOD(lldb::SBError, SBFile, Close, ());
 }
 } // namespace repro

Modified: lldb/trunk/source/Host/common/File.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/common/File.cpp?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/source/Host/common/File.cpp (original)
+++ lldb/trunk/source/Host/common/File.cpp Tue Oct 15 09:46:27 2019
@@ -39,7 +39,8 @@ using namespace lldb;
 using namespace lldb_private;
 using llvm::Expected;
 
-static Expected<const char *> GetStreamOpenModeFromOptions(uint32_t options) {
+Expected<const char *>
+File::GetStreamOpenModeFromOptions(File::OpenOptions options) {
   if (options & File::eOpenOptionAppend) {
     if (options & File::eOpenOptionRead) {
       if (options & File::eOpenOptionCanCreateNewOnly)
@@ -226,6 +227,12 @@ size_t File::PrintfVarArg(const char *fo
   return result;
 }
 
+Expected<File::OpenOptions> File::GetOptions() const {
+  return llvm::createStringError(
+      llvm::inconvertibleErrorCode(),
+      "GetOptions() not implemented for this File class");
+}
+
 uint32_t File::GetPermissions(Status &error) const {
   int fd = GetDescriptor();
   if (!DescriptorIsValid(fd)) {
@@ -241,6 +248,8 @@ uint32_t File::GetPermissions(Status &er
   return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
 }
 
+Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }
+
 int NativeFile::GetDescriptor() const {
   if (DescriptorIsValid())
     return m_descriptor;
@@ -758,3 +767,6 @@ mode_t File::ConvertOpenOptionsForPOSIXO
 
   return mode;
 }
+
+char File::ID = 0;
+char NativeFile::ID = 0;

Modified: lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp (original)
+++ lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp Tue Oct 15 09:46:27 2019
@@ -22,6 +22,7 @@
 #include "lldb/Utility/Stream.h"
 
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/Errno.h"
 
@@ -1012,8 +1013,6 @@ operator()(std::initializer_list<PythonO
 
 PythonFile::PythonFile() : PythonObject() {}
 
-PythonFile::PythonFile(File &file, const char *mode) { Reset(file, mode); }
-
 PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); }
 
 PythonFile::~PythonFile() {}
@@ -1063,25 +1062,6 @@ void PythonFile::Reset(PyRefType type, P
   PythonObject::Reset(PyRefType::Borrowed, result.get());
 }
 
-void PythonFile::Reset(File &file, const char *mode) {
-  if (!file.IsValid()) {
-    Reset();
-    return;
-  }
-
-  char *cmode = const_cast<char *>(mode);
-#if PY_MAJOR_VERSION >= 3
-  Reset(PyRefType::Owned, PyFile_FromFd(file.GetDescriptor(), nullptr, cmode,
-                                        -1, nullptr, "ignore", nullptr, 0));
-#else
-  // Read through the Python source, doesn't seem to modify these strings
-  Reset(PyRefType::Owned,
-        PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode,
-                        nullptr));
-#endif
-}
-
-
 FileUP PythonFile::GetUnderlyingFile() const {
   if (!IsValid())
     return nullptr;
@@ -1238,6 +1218,13 @@ public:
     return base_error;
   };
 
+  PyObject *GetPythonObject() const {
+    assert(m_py_obj.IsValid());
+    return m_py_obj.get();
+  }
+
+  static bool classof(const File *file) = delete;
+
 protected:
   PythonFile m_py_obj;
   bool m_borrowed;
@@ -1252,7 +1239,14 @@ public:
   SimplePythonFile(const PythonFile &file, bool borrowed, int fd,
                    File::OpenOptions options)
       : OwnedPythonFile(file, borrowed, fd, options, false) {}
+
+  static char ID;
+  bool isA(const void *classID) const override {
+    return classID == &ID || NativeFile::isA(classID);
+  }
+  static bool classof(const File *file) { return file->isA(&ID); }
 };
+char SimplePythonFile::ID = 0;
 } // namespace
 
 #if PY_MAJOR_VERSION >= 3
@@ -1321,7 +1315,18 @@ public:
     return Status();
   }
 
+  Expected<File::OpenOptions> GetOptions() const override {
+    GIL takeGIL;
+    return GetOptionsForPyObject(m_py_obj);
+  }
+
+  static char ID;
+  bool isA(const void *classID) const override {
+    return classID == &ID || File::isA(classID);
+  }
+  static bool classof(const File *file) { return file->isA(&ID); }
 };
+char PythonIOFile::ID = 0;
 } // namespace
 
 namespace {
@@ -1542,4 +1547,42 @@ PythonFile::ConvertToFileForcingUseOfScr
 #endif
 }
 
+Expected<PythonFile> PythonFile::FromFile(File &file, const char *mode) {
+  if (!file.IsValid())
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "invalid file");
+
+  if (auto *simple = llvm::dyn_cast<SimplePythonFile>(&file))
+    return Retain<PythonFile>(simple->GetPythonObject());
+#if PY_MAJOR_VERSION >= 3
+  if (auto *pythonio = llvm::dyn_cast<PythonIOFile>(&file))
+    return Retain<PythonFile>(pythonio->GetPythonObject());
+#endif
+
+  if (!mode) {
+    auto m = file.GetOpenMode();
+    if (!m)
+      return m.takeError();
+    mode = m.get();
+  }
+
+  PyObject *file_obj;
+#if PY_MAJOR_VERSION >= 3
+  file_obj = PyFile_FromFd(file.GetDescriptor(), nullptr, mode, -1, nullptr,
+                           "ignore", nullptr, 0);
+#else
+  // Read through the Python source, doesn't seem to modify these strings
+  char *cmode = const_cast<char *>(mode);
+  // We pass ::flush instead of ::fclose here so we borrow the FILE* --
+  // the lldb_private::File still owns it.
+  file_obj =
+      PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode, ::fflush);
+#endif
+
+  if (!file_obj)
+    return exception();
+
+  return Take<PythonFile>(file_obj);
+}
+
 #endif

Modified: lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h?rev=374911&r1=374910&r2=374911&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h (original)
+++ lldb/trunk/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h Tue Oct 15 09:46:27 2019
@@ -638,7 +638,7 @@ public:
   void Reset(PyRefType type, PyObject *py_obj) override;
 
   ArgInfo GetNumArguments() const;
-  
+
   // If the callable is a Py_Class, then find the number of arguments
   // of the __init__ method.
   ArgInfo GetNumInitArguments() const;
@@ -658,7 +658,6 @@ public:
 class PythonFile : public PythonObject {
 public:
   PythonFile();
-  PythonFile(File &file, const char *mode);
   PythonFile(PyRefType type, PyObject *o);
 
   ~PythonFile() override;
@@ -668,7 +667,21 @@ public:
   using PythonObject::Reset;
 
   void Reset(PyRefType type, PyObject *py_obj) override;
-  void Reset(File &file, const char *mode);
+
+  static llvm::Expected<PythonFile> FromFile(File &file,
+                                             const char *mode = nullptr);
+
+  // FIXME delete this after FILE* typemaps are deleted
+  // and ScriptInterpreterPython is fixed
+  PythonFile(File &file, const char *mode = nullptr) {
+    auto f = FromFile(file, mode);
+    if (f)
+      *this = std::move(f.get());
+    else {
+      Reset();
+      llvm::consumeError(f.takeError());
+    }
+  }
 
   lldb::FileUP GetUnderlyingFile() const;
 




More information about the lldb-commits mailing list