Add a `PythonModule` class, and a root-level method for resolving names.
llvm-svn: 252765
This commit is contained in:
parent
7d7814ae8a
commit
7841efbb92
|
@ -67,6 +67,8 @@ PythonObject::GetObjectType() const
|
|||
if (!IsAllocated())
|
||||
return PyObjectType::None;
|
||||
|
||||
if (PythonModule::Check(m_py_obj))
|
||||
return PyObjectType::Module;
|
||||
if (PythonList::Check(m_py_obj))
|
||||
return PyObjectType::List;
|
||||
if (PythonDictionary::Check(m_py_obj))
|
||||
|
@ -81,7 +83,7 @@ PythonObject::GetObjectType() const
|
|||
}
|
||||
|
||||
PythonString
|
||||
PythonObject::Repr()
|
||||
PythonObject::Repr() const
|
||||
{
|
||||
if (!m_py_obj)
|
||||
return PythonString();
|
||||
|
@ -92,7 +94,7 @@ PythonObject::Repr()
|
|||
}
|
||||
|
||||
PythonString
|
||||
PythonObject::Str()
|
||||
PythonObject::Str() const
|
||||
{
|
||||
if (!m_py_obj)
|
||||
return PythonString();
|
||||
|
@ -102,6 +104,43 @@ PythonObject::Str()
|
|||
return PythonString(PyRefType::Owned, str);
|
||||
}
|
||||
|
||||
PythonObject
|
||||
PythonObject::ResolveNameGlobal(llvm::StringRef name)
|
||||
{
|
||||
return PythonModule::MainModule().ResolveName(name);
|
||||
}
|
||||
|
||||
PythonObject
|
||||
PythonObject::ResolveName(llvm::StringRef name) const
|
||||
{
|
||||
// Resolve the name in the context of the specified object. If,
|
||||
// for example, `this` refers to a PyModule, then this will look for
|
||||
// `name` in this module. If `this` refers to a PyType, then it will
|
||||
// resolve `name` as an attribute of that type. If `this` refers to
|
||||
// an instance of an object, then it will resolve `name` as the value
|
||||
// of the specified field.
|
||||
//
|
||||
// This function handles dotted names so that, for example, if `m_py_obj`
|
||||
// refers to the `sys` module, and `name` == "path.append", then it
|
||||
// will find the function `sys.path.append`.
|
||||
|
||||
size_t dot_pos = name.find_first_of('.');
|
||||
if (dot_pos == llvm::StringRef::npos)
|
||||
{
|
||||
// No dots in the name, we should be able to find the value immediately
|
||||
// as an attribute of `use_object`.
|
||||
return GetAttributeValue(name);
|
||||
}
|
||||
|
||||
// Look up the first piece of the name, and resolve the rest as a child of that.
|
||||
PythonObject parent = ResolveName(name.substr(0, dot_pos));
|
||||
if (!parent.IsAllocated())
|
||||
return PythonObject();
|
||||
|
||||
// Tail recursion.. should be optimized by the compiler
|
||||
return parent.ResolveName(name.substr(dot_pos + 1));
|
||||
}
|
||||
|
||||
bool
|
||||
PythonObject::HasAttribute(llvm::StringRef attr) const
|
||||
{
|
||||
|
@ -605,6 +644,62 @@ PythonDictionary::CreateStructuredDictionary() const
|
|||
return result;
|
||||
}
|
||||
|
||||
PythonModule::PythonModule() : PythonObject()
|
||||
{
|
||||
}
|
||||
|
||||
PythonModule::PythonModule(PyRefType type, PyObject *py_obj)
|
||||
{
|
||||
Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a module
|
||||
}
|
||||
|
||||
PythonModule::PythonModule(const PythonModule &dict) : PythonObject(dict)
|
||||
{
|
||||
}
|
||||
|
||||
PythonModule::~PythonModule()
|
||||
{
|
||||
}
|
||||
|
||||
PythonModule
|
||||
PythonModule::MainModule()
|
||||
{
|
||||
return PythonModule(PyRefType::Borrowed, PyImport_AddModule("__main__"));
|
||||
}
|
||||
|
||||
bool
|
||||
PythonModule::Check(PyObject *py_obj)
|
||||
{
|
||||
if (!py_obj)
|
||||
return false;
|
||||
|
||||
return PyModule_Check(py_obj);
|
||||
}
|
||||
|
||||
void
|
||||
PythonModule::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 (!PythonModule::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());
|
||||
}
|
||||
|
||||
PythonDictionary
|
||||
PythonModule::GetDictionary() const
|
||||
{
|
||||
return PythonDictionary(PyRefType::Borrowed, PyModule_GetDict(m_py_obj));
|
||||
}
|
||||
|
||||
PythonFile::PythonFile()
|
||||
: PythonObject()
|
||||
{
|
||||
|
|
|
@ -69,6 +69,7 @@ enum class PyObjectType
|
|||
Dictionary,
|
||||
List,
|
||||
String,
|
||||
Module,
|
||||
File
|
||||
};
|
||||
|
||||
|
@ -185,15 +186,6 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
PyObjectType
|
||||
GetObjectType() const;
|
||||
|
||||
PythonString
|
||||
Repr ();
|
||||
|
||||
PythonString
|
||||
Str ();
|
||||
|
||||
PythonObject &
|
||||
operator=(const PythonObject &other)
|
||||
{
|
||||
|
@ -201,6 +193,21 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
PyObjectType
|
||||
GetObjectType() const;
|
||||
|
||||
PythonString
|
||||
Repr() const;
|
||||
|
||||
PythonString
|
||||
Str() const;
|
||||
|
||||
static PythonObject
|
||||
ResolveNameGlobal(llvm::StringRef name);
|
||||
|
||||
PythonObject
|
||||
ResolveName(llvm::StringRef name) const;
|
||||
|
||||
bool
|
||||
HasAttribute(llvm::StringRef attribute) const;
|
||||
|
||||
|
@ -224,7 +231,8 @@ public:
|
|||
return T(PyRefType::Borrowed, m_py_obj);
|
||||
}
|
||||
|
||||
StructuredData::ObjectSP CreateStructuredObject() const;
|
||||
StructuredData::ObjectSP
|
||||
CreateStructuredObject() const;
|
||||
|
||||
protected:
|
||||
PyObject* m_py_obj;
|
||||
|
@ -338,6 +346,27 @@ public:
|
|||
StructuredData::DictionarySP CreateStructuredDictionary() const;
|
||||
};
|
||||
|
||||
class PythonModule : public PythonObject
|
||||
{
|
||||
public:
|
||||
PythonModule();
|
||||
PythonModule(PyRefType type, PyObject *o);
|
||||
PythonModule(const PythonModule &dict);
|
||||
|
||||
~PythonModule() override;
|
||||
|
||||
static bool Check(PyObject *py_obj);
|
||||
|
||||
static PythonModule MainModule();
|
||||
|
||||
// Bring in the no-argument base class version
|
||||
using PythonObject::Reset;
|
||||
|
||||
void Reset(PyRefType type, PyObject *py_obj) override;
|
||||
|
||||
PythonDictionary GetDictionary() const;
|
||||
};
|
||||
|
||||
class PythonFile : public PythonObject
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -96,6 +96,66 @@ TEST_F(PythonDataObjectsTest, TestBorrowedReferences)
|
|||
EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt);
|
||||
}
|
||||
|
||||
TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot)
|
||||
{
|
||||
PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
|
||||
EXPECT_TRUE(sys_module.IsAllocated());
|
||||
EXPECT_TRUE(PythonModule::Check(sys_module.get()));
|
||||
}
|
||||
|
||||
TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot)
|
||||
{
|
||||
PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
|
||||
PythonObject sys_path = sys_module.ResolveName("path");
|
||||
PythonObject sys_version_info = sys_module.ResolveName("version_info");
|
||||
EXPECT_TRUE(sys_path.IsAllocated());
|
||||
EXPECT_TRUE(sys_version_info.IsAllocated());
|
||||
|
||||
EXPECT_TRUE(PythonList::Check(sys_path.get()));
|
||||
}
|
||||
|
||||
TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot)
|
||||
{
|
||||
PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
|
||||
PythonObject sys_version_info = sys_module.ResolveName("version_info");
|
||||
|
||||
PythonObject version_info_type(PyRefType::Owned, PyObject_Type(sys_version_info.get()));
|
||||
EXPECT_TRUE(version_info_type.IsAllocated());
|
||||
PythonObject major_version_field = version_info_type.ResolveName("major");
|
||||
EXPECT_TRUE(major_version_field.IsAllocated());
|
||||
}
|
||||
|
||||
TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot)
|
||||
{
|
||||
PythonObject sys_module = PythonObject::ResolveNameGlobal("sys");
|
||||
PythonObject sys_version_info = sys_module.ResolveName("version_info");
|
||||
PythonObject major_version_field = sys_version_info.ResolveName("major");
|
||||
PythonObject minor_version_field = sys_version_info.ResolveName("minor");
|
||||
|
||||
EXPECT_TRUE(major_version_field.IsAllocated());
|
||||
EXPECT_TRUE(minor_version_field.IsAllocated());
|
||||
|
||||
PythonInteger major_version_value = major_version_field.AsType<PythonInteger>();
|
||||
PythonInteger minor_version_value = minor_version_field.AsType<PythonInteger>();
|
||||
|
||||
EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger());
|
||||
EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger());
|
||||
}
|
||||
|
||||
TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot)
|
||||
{
|
||||
PythonObject sys_path = PythonObject::ResolveNameGlobal("sys.path");
|
||||
EXPECT_TRUE(sys_path.IsAllocated());
|
||||
EXPECT_TRUE(PythonList::Check(sys_path.get()));
|
||||
|
||||
PythonInteger version_major = PythonObject::ResolveNameGlobal("sys.version_info.major").AsType<PythonInteger>();
|
||||
PythonInteger version_minor = PythonObject::ResolveNameGlobal("sys.version_info.minor").AsType<PythonInteger>();
|
||||
EXPECT_TRUE(version_major.IsAllocated());
|
||||
EXPECT_TRUE(version_minor.IsAllocated());
|
||||
EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger());
|
||||
EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger());
|
||||
}
|
||||
|
||||
TEST_F(PythonDataObjectsTest, TestPythonInteger)
|
||||
{
|
||||
// Test that integers behave correctly when wrapped by a PythonInteger.
|
||||
|
|
Loading…
Reference in New Issue