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:
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user