Core: Modify metadata Python handling to use dict

Rather than trying to use a generic Py::Object and assigning properties
to it, use a Py::Dict object for simple data structures like Url,
Maintainer, and Author.

Update metadata standard to include subdirectory
This commit is contained in:
Chris Hennes
2021-10-18 23:19:30 -05:00
parent fc642482dd
commit 1844a0161e
5 changed files with 81 additions and 35 deletions

View File

@@ -117,6 +117,7 @@
#include "ExpressionParser.h"
#include "Transactions.h"
#include <App/MaterialPy.h>
#include <App/MetadataPy.h>
#include <Base/GeometryPyCXX.h>
#include "Link.h"
@@ -307,6 +308,7 @@ Application::Application(std::map<std::string,std::string> &mConfig)
Base::Interpreter().addType(&Base::TypePy ::Type,pBaseModule,"TypeId");
Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material");
Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata");
// Add document types
Base::Interpreter().addType(&App::PropertyContainerPy::Type, pAppModule, "PropertyContainer");

View File

@@ -182,6 +182,11 @@ std::string Metadata::classname() const
return _classname;
}
boost::filesystem::path App::Metadata::subdirectory() const
{
return _subdirectory;
}
std::vector<fs::path> Metadata::file() const
{
return _file;
@@ -285,6 +290,11 @@ void Metadata::setClassname(const std::string& name)
_classname = name;
}
void App::Metadata::setSubdirectory(const boost::filesystem::path& path)
{
_subdirectory = path;
}
void Metadata::addFile(const fs::path& path)
{
_file.push_back(path);
@@ -482,6 +492,9 @@ void Metadata::appendToElement(DOMElement* root) const
case Meta::UrlType::documentation: typeAsString = "documentation"; break;
}
addAttribute(element, "type", typeAsString);
if (url.type == Meta::UrlType::repository) {
addAttribute(element, "branch", url.branch);
}
}
}
@@ -507,6 +520,8 @@ void Metadata::appendToElement(DOMElement* root) const
appendSimpleXMLNode(root, "classname", _classname);
appendSimpleXMLNode(root, "subdirectory", _subdirectory.string());
for (const auto& file : _file)
appendSimpleXMLNode(root, "file", file.string());
@@ -572,6 +587,8 @@ void Metadata::parseVersion1(const DOMNode* startNode)
_file.emplace_back(StrXUTF8(element->getTextContent()).str);
else if (tagString == "classname")
_classname = StrXUTF8(element->getTextContent()).str;
else if (tagString == "subdirectory")
_subdirectory = StrXUTF8(element->getTextContent()).str;
else if (tagString == "icon")
_icon = fs::path(StrXUTF8(element->getTextContent()).str);
else if (tagString == "content")
@@ -652,6 +669,10 @@ Meta::Url::Url(const XERCES_CPP_NAMESPACE::DOMElement* e)
type = UrlType::readme;
else if (typeAttribute == "documentation")
type = UrlType::documentation;
if (type == UrlType::repository)
branch = StrXUTF8(e->getAttribute(XUTF8Str("branch").unicodeForm())).str;
location = StrXUTF8(e->getTextContent()).str;
}

View File

@@ -77,7 +77,7 @@ namespace App {
/**
* \struct Url
* \brief A URL, including type information (e.g. website, repository, or bugtracker, in package.xml v3)
* \brief A URL, including type information (e.g. website, repository, or bugtracker, in package.xml)
*/
struct AppExport Url {
Url() = default;
@@ -85,6 +85,7 @@ namespace App {
explicit Url(const XERCES_CPP_NAMESPACE::DOMElement* e);
std::string location; //< The actual URL, including protocol
UrlType type; //< What kind of URL this is
std::string branch; //< If it's a repository, which branch to use
};
/**
@@ -194,6 +195,7 @@ namespace App {
std::vector<std::string> tag() const; //< Zero or more text tags related to this package.
boost::filesystem::path icon() const; //< Path to an icon file.
std::string classname() const; //< Recognized for convenience -- generally only used by Workbenches.
boost::filesystem::path subdirectory() const; //< Optional, override the default subdirectory name for this item.
std::vector<boost::filesystem::path> file() const; //< Arbitrary files associated with this package or content item.
Meta::Version freecadmin() const; //< The minimum FreeCAD version.
Meta::Version freecadmax() const; //< The maximum FreeCAD version.
@@ -246,6 +248,7 @@ namespace App {
void addTag(const std::string& tag);
void setIcon(const boost::filesystem::path& path);
void setClassname(const std::string& name);
void setSubdirectory(const boost::filesystem::path& path);
void addFile(const boost::filesystem::path& path);
void addContentItem(const std::string& tag, const Metadata& item);
void setFreeCADMin(const Meta::Version& version);
@@ -277,6 +280,7 @@ namespace App {
std::vector<std::string> _tag;
boost::filesystem::path _icon;
std::string _classname;
boost::filesystem::path _subdirectory;
std::vector<boost::filesystem::path> _file;
Meta::Version _freecadmin;
Meta::Version _freecadmax;

View File

@@ -56,7 +56,7 @@
</Documentation>
<Parameter Name="License" Type="Object" />
</Attribute>
<Attribute Name="Url" ReadOnly="true">
<Attribute Name="Urls" ReadOnly="true">
<Documentation>
<UserDocu>
List of URLs as objects with 'location' and 'urltype' string attributes, where urltype is one of:
@@ -67,7 +67,7 @@
* documentation
</UserDocu>
</Documentation>
<Parameter Name="Url" Type="Object" />
<Parameter Name="Urls" Type="Object" />
</Attribute>
<Attribute Name="Author" ReadOnly="true">
<Documentation>
@@ -120,6 +120,12 @@
</Documentation>
<Parameter Name="Classname" Type="Object" />
</Attribute>
<Attribute Name="Subdirectory" ReadOnly="true">
<Documentation>
<UserDocu>String: the name of the subdirectory this content item is located in. If empty, the item is in a directory named the same as the content item.</UserDocu>
</Documentation>
<Parameter Name="Classname" Type="Object" />
</Attribute>
<Attribute Name="File" ReadOnly="true">
<Documentation>
<UserDocu>A list of files associated with this item -- the meaning of each file is implementation-defined</UserDocu>

View File

@@ -49,10 +49,16 @@ PyObject* MetadataPy::PyMake(struct _typeobject*, PyObject* args, PyObject*) //
{
// create a new instance of MetadataPy and the Twin object
const char* filename;
if (!PyArg_ParseTuple(args, "s!", &filename))
if (!PyArg_ParseTuple(args, "s", &filename))
return nullptr;
return new MetadataPy(new Metadata(filename));
try {
auto md = new Metadata(filename);
return new MetadataPy(md);
}
catch (...) {
PyErr_SetString(Base::BaseExceptionFreeCADError, "Failed to create Metadata object");
return nullptr;
}
}
// constructor method
@@ -65,7 +71,7 @@ int MetadataPy::PyInit(PyObject* args, PyObject* /*kwd*/)
// Main class constructor -- takes a file path, loads the metadata from it
PyErr_Clear();
const char* file;
if (PyArg_ParseTuple(args, "s!", &file)) {
if (PyArg_ParseTuple(args, "s", &file)) {
App::Metadata* a = new Metadata(file);
*(getMetadataPtr()) = *a;
return 0;
@@ -104,9 +110,9 @@ Py::Object MetadataPy::getMaintainer(void) const
auto maintainers = getMetadataPtr()->maintainer();
Py::List pyMaintainers;
for (const auto& m : maintainers) {
Py::Object pyMaintainer;
pyMaintainer.setAttr("name", Py::String(m.name));
pyMaintainer.setAttr("email", Py::String(m.email));
Py::Dict pyMaintainer;
pyMaintainer["name"] = Py::String(m.name);
pyMaintainer["email"] = Py::String(m.email);
pyMaintainers.append(pyMaintainer);
}
return pyMaintainers;
@@ -117,9 +123,9 @@ Py::Object MetadataPy::getAuthor(void) const
auto authors = getMetadataPtr()->author();
Py::List pyAuthors;
for (const auto& a : authors) {
Py::Object pyAuthor;
pyAuthor.setAttr("name", Py::String(a.name));
pyAuthor.setAttr("email", Py::String(a.email));
Py::Dict pyAuthor;
pyAuthor["name"] = Py::String(a.name);
pyAuthor["email"] = Py::String(a.email);
pyAuthors.append(pyAuthor);
}
return pyAuthors;
@@ -130,28 +136,30 @@ Py::Object MetadataPy::getLicense(void) const
auto licenses = getMetadataPtr()->license();
Py::List pyLicenses;
for (const auto& lic : licenses) {
Py::Object pyLicense;
pyLicense.setAttr("name", Py::String(lic.name));
pyLicense.setAttr("file", Py::String(lic.file.string()));
Py::Dict pyLicense;
pyLicense["name"] = Py::String(lic.name);
pyLicense["file"] = Py::String(lic.file.string());
pyLicenses.append(pyLicense);
}
return pyLicenses;
}
Py::Object MetadataPy::getUrl(void) const
Py::Object MetadataPy::getUrls(void) const
{
auto urls = getMetadataPtr()->url ();
Py::List pyUrls;
for (const auto& url : urls) {
Py::Object pyUrl;
pyUrl.setAttr("location", Py::String(url.location));
Py::Dict pyUrl;
pyUrl["location"] = Py::String(url.location);
switch (url.type) {
case Meta::UrlType::website: pyUrl.setAttr("type", Py::String("website")); break;
case Meta::UrlType::repository: pyUrl.setAttr("type", Py::String("repository")); break;
case Meta::UrlType::bugtracker: pyUrl.setAttr("type", Py::String("bugtracker")); break;
case Meta::UrlType::readme: pyUrl.setAttr("type", Py::String("readme")); break;
case Meta::UrlType::documentation: pyUrl.setAttr("type", Py::String("documentation")); break;
case Meta::UrlType::website: pyUrl["type"] = Py::String("website"); break;
case Meta::UrlType::repository: pyUrl["type"] = Py::String("repository"); break;
case Meta::UrlType::bugtracker: pyUrl["type"] = Py::String("bugtracker"); break;
case Meta::UrlType::readme: pyUrl["type"] = Py::String("readme"); break;
case Meta::UrlType::documentation: pyUrl["type"] = Py::String("documentation"); break;
}
if (url.type == Meta::UrlType::repository)
pyUrl["branch"] = Py::String(url.branch);
pyUrls.append(pyUrl);
}
return pyUrls;
@@ -159,14 +167,14 @@ Py::Object MetadataPy::getUrl(void) const
Py::Object dependencyToPyObject(const Meta::Dependency& d)
{
Py::Object pyDependency;
pyDependency.setAttr("package",Py::String(d.package));
pyDependency.setAttr("version_lt", Py::String(d.version_lt));
pyDependency.setAttr("version_lte", Py::String(d.version_lte));
pyDependency.setAttr("version_eq", Py::String(d.version_eq));
pyDependency.setAttr("version_gt", Py::String(d.version_gt));
pyDependency.setAttr("version_gte", Py::String(d.version_gte));
pyDependency.setAttr("condition", Py::String(d.condition));
Py::Dict pyDependency;
pyDependency["package"] = Py::String(d.package);
pyDependency["version_lt"] = Py::String(d.version_lt);
pyDependency["version_lte"] = Py::String(d.version_lte);
pyDependency["version_eq"] = Py::String(d.version_eq);
pyDependency["version_gt"] = Py::String(d.version_gt);
pyDependency["version_gte"] = Py::String(d.version_gte);
pyDependency["condition"] = Py::String(d.condition);
return pyDependency;
}
@@ -222,6 +230,11 @@ Py::Object MetadataPy::getClassname(void) const
return Py::String(getMetadataPtr()->classname());
}
Py::Object MetadataPy::getSubdirectory(void) const
{
return Py::String(getMetadataPtr()->subdirectory().string());
}
Py::Object MetadataPy::getFile(void) const
{
auto files = getMetadataPtr()->file();
@@ -262,13 +275,13 @@ PyObject* MetadataPy::getGenericMetadata(PyObject* args)
auto gm = (*getMetadataPtr())[name];
auto pyGenericMetadata = new Py::List;
for (const auto& item : gm) {
Py::Object pyItem;
pyItem.setAttr("contents", Py::String(item.contents));
Py::Dict pyItem;
pyItem["contents"] = Py::String(item.contents);
Py::Dict pyAttributes;
for (const auto& attribute : item.attributes) {
pyAttributes[attribute.first] = Py::String(attribute.second);
}
pyItem.setAttr("attributes", pyAttributes);
pyItem["attributes"] = pyAttributes;
pyGenericMetadata->append(pyItem);
}
return pyGenericMetadata->ptr();