From ff2b79a41f9d4414f650ce1c6f2753fbeb5c12a9 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 10 Nov 2022 14:04:25 +0100 Subject: [PATCH] Distinguish between nil and derived in validate.py --- .../ifcopenshell/validate.py | 26 ++++++++++++-- src/ifcwrap/IfcParseWrapper.i | 34 +++++++++++++++++++ src/ifcwrap/utils/typemaps_out.i | 9 ++++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py index 81feccd1e9..094ac823cf 100644 --- a/src/ifcopenshell-python/ifcopenshell/validate.py +++ b/src/ifcopenshell-python/ifcopenshell/validate.py @@ -284,7 +284,16 @@ def validate(f, logger): It is recommended to supply the path to the file, so that internal C++ errors reported during the parse stage are also captured. """ + + # Originally there was no way in Python to distinguish on an entity instance attribute value whether the + # value supplied in the model was NIL ($) or 'missing because derived in subtype' (*). For validation this + # however this may be important, and hence a feature switch has been implemented to return *-values as + # instances of a dedicated type `ifcopenshell.ifcopenshell_wrapper.attribute_value_derived`. + attribute_value_derived_org = ifcopenshell.ifcopenshell_wrapper.get_feature('use_attribute_value_derived') + ifcopenshell.ifcopenshell_wrapper.set_feature('use_attribute_value_derived', True) + filename = None + if not isinstance(f, ifcopenshell.file): # get_log() clears log existing output @@ -335,7 +344,18 @@ def validate(f, logger): zip(attrs, values, entity.derived()) ): - if val is None and not (is_derived or attr.optional()): + if is_derived and not isinstance(val, ifcopenshell.ifcopenshell_wrapper.attribute_value_derived): + if hasattr(logger, "set_instance"): + logger.error("Attribute %s.%s is derived in subtype", entity, attr) + else: + logger.error( + "For instance:\n %s\n %s\nWith attribute:\n %s\nDerived in subtype\n", + inst, + annotate_inst_attr_pos(inst, i), + attr, + ) + + if val is None and not attr.optional() and not is_derived: if hasattr(logger, "set_instance"): logger.error("Attribute %s.%s not optional", entity, attr) else: @@ -346,7 +366,7 @@ def validate(f, logger): attr, ) - if val is not None: + if val is not None and not is_derived: attr_type = attr.type_of_attribute() try: assert_valid(attr_type, val, schema, attr=attr) @@ -379,6 +399,8 @@ def validate(f, logger): # are verified. log_internal_cpp_errors(filename, logger) + # Restore the original value for 'use_attribute_value_derived' + ifcopenshell.ifcopenshell_wrapper.set_feature('use_attribute_value_derived', attribute_value_derived_org) if __name__ == "__main__": import sys diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index e6e38eedea..04e79d91e9 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -54,7 +54,41 @@ private: %rename("add") addEntity; %rename("remove") removeEntity; +class attribute_value_derived {}; %{ +class attribute_value_derived {}; +%} + +%extend attribute_value_derived { + %pythoncode %{ + def __bool__(self): return False + def __repr__(self): return '*' + %} +} + +%inline %{ +static bool feature_use_attribute_value_derived = false; + +void set_feature(const std::string& x, PyObject* v) { + if (PyBool_Check(v) && x == "use_attribute_value_derived") { + feature_use_attribute_value_derived = v == Py_True; + } else { + throw std::runtime_error("Invalid feature specification"); + } +} + +PyObject* get_feature(const std::string& x) { + if (x == "use_attribute_value_derived") { + return PyBool_FromLong(feature_use_attribute_value_derived); + } else { + throw std::runtime_error("Invalid feature specification"); + } +} + +%} + +%{ + static const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl) { return decl->name(); } diff --git a/src/ifcwrap/utils/typemaps_out.i b/src/ifcwrap/utils/typemaps_out.i index 07f07a1bcc..757ad932dd 100644 --- a/src/ifcwrap/utils/typemaps_out.i +++ b/src/ifcwrap/utils/typemaps_out.i @@ -38,9 +38,16 @@ try { const Argument& arg = *($1.second); const IfcUtil::ArgumentType type = $1.first; - if (arg.isNull() || type == IfcUtil::Argument_DERIVED) { + if (arg.isNull()) { Py_INCREF(Py_None); $result = Py_None; + } else if (type == IfcUtil::Argument_DERIVED) { + if (feature_use_attribute_value_derived) { + $result = SWIG_NewPointerObj(new attribute_value_derived, SWIGTYPE_p_attribute_value_derived, SWIG_POINTER_OWN); + } else { + Py_INCREF(Py_None); + $result = Py_None; + } } else { switch(type) { case IfcUtil::Argument_INT: {