diff options
author | Zachary Turner <zturner@google.com> | 2015-10-15 19:35:48 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2015-10-15 19:35:48 +0000 |
commit | 9c40264fdadbb229ce311c9bda898981d7ac8eae (patch) | |
tree | 93437006427295b7cd98fd7e8a3ddd14a4410b9f /lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp | |
parent | b26d8070333117beb92bde3007ddd37729929c3e (diff) | |
download | bcm5719-llvm-9c40264fdadbb229ce311c9bda898981d7ac8eae.tar.gz bcm5719-llvm-9c40264fdadbb229ce311c9bda898981d7ac8eae.zip |
Introduce a `PythonFile` object, and use it everywhere.
Python file handling got an overhaul in Python 3, and it affects
the way we have to interact with files. Notably:
1) `PyFile_FromFile` no longer exists, and instead we have to use
`PyFile_FromFd`. This means having a way to get an fd from
a FILE*. For this we reuse the lldb_private::File class to
convert between FILE*s and fds, since there are some subtleties
regarding ownership rules when FILE*s and fds refer to the same
file.
2) PyFile is no longer a builtin type, so there is no such thing as
`PyFile_Check`. Instead, files in Python 3 are just instances
of `io.IOBase`. So the logic for checking if something is a file
in Python 3 is to check if it is a subclass of that module.
Additionally, some unit tests are added to verify that `PythonFile`
works as expected on Python 2 and Python 3, and
`ScriptInterpreterPython` is updated to use `PythonFile` instead of
manual calls to the various `PyFile_XXX` methods.
llvm-svn: 250444
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp')
-rw-r--r-- | lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index fa556ed3ac5..5c458bfd628 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -75,6 +75,8 @@ PythonObject::GetObjectType() const return PyObjectType::String; if (PythonInteger::Check(m_py_obj)) return PyObjectType::Integer; + if (PythonFile::Check(m_py_obj)) + return PyObjectType::File; return PyObjectType::Unknown; } @@ -101,6 +103,15 @@ PythonObject::Str() } bool +PythonObject::HasAttribute(llvm::StringRef attr) const +{ + if (!IsValid()) + return false; + PythonString py_attr(attr); + return !!PyObject_HasAttr(m_py_obj, py_attr.get()); +} + +bool PythonObject::IsNone() const { return m_py_obj == Py_None; @@ -566,4 +577,78 @@ PythonDictionary::CreateStructuredDictionary() const return result; } +PythonFile::PythonFile(File &file, const char *mode) +{ + Reset(file, mode); +} + +PythonFile::PythonFile(PyRefType type, PyObject *o) +{ + Reset(type, o); +} + +PythonFile::~PythonFile() +{ +} + +bool +PythonFile::Check(PyObject *py_obj) +{ +#if PY_MAJOR_VERSION < 3 + return PyFile_Check(py_obj); +#else + // In Python 3, there is no `PyFile_Check`, and in fact PyFile is not even a + // first-class object type anymore. `PyFile_FromFd` is just a thin wrapper + // over `io.open()`, which returns some object derived from `io.IOBase`. + // As a result, the only way to detect a file in Python 3 is to check whether + // it inherits from `io.IOBase`. Since it is possible for non-files to also + // inherit from `io.IOBase`, we additionally verify that it has the `fileno` + // attribute, which should guarantee that it is backed by the file system. + PythonObject io_module(PyRefType::Owned, PyImport_ImportModule("io")); + PythonDictionary io_dict(PyRefType::Borrowed, PyModule_GetDict(io_module.get())); + PythonObject io_base_class = io_dict.GetItemForKey(PythonString("IOBase")); + + PythonObject object_type(PyRefType::Owned, PyObject_Type(py_obj)); + + if (1 != PyObject_IsSubclass(object_type.get(), io_base_class.get())) + return false; + if (!object_type.HasAttribute("fileno")) + return false; + + return true; +#endif +} + +void +PythonFile::Reset(PyRefType type, PyObject *py_obj) +{ + // Grab the desired reference type so that if we end up rejecting + // `py_obj` it still gets decremented if necessary. + PythonObject result(type, py_obj); + + if (!PythonFile::Check(py_obj)) + { + PythonObject::Reset(); + return; + } + + // Calling PythonObject::Reset(const PythonObject&) will lead to stack + // overflow since it calls back into the virtual implementation. + PythonObject::Reset(PyRefType::Borrowed, result.get()); +} + +void +PythonFile::Reset(File &file, const char *mode) +{ + 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 +} + #endif |