Skip to content

Commit c52f851

Browse files
committed
#1807 method mapping proposal
1 parent 3380158 commit c52f851

4 files changed

Lines changed: 83 additions & 37 deletions

File tree

src/ifcopenshell-python/ifcopenshell/entity_instance.py

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,51 @@
3333
logging = type("logger", (object,), {"exception": staticmethod(lambda s: print(s))})
3434

3535

36+
def set_derived_atribute(*args):
37+
raise TypeError("Unable to set derived attribute")
38+
39+
40+
# For every schema and its entities populate a list
41+
# of functions for every entity attribute (including
42+
# inherited attributes) to set that particular
43+
# attribute by index.
44+
# For example. IFC2X3.IfcWall with have a list of
45+
# 9 methods. The first will point at
46+
# ifcopenshell.ifcopenshell_wrapper.entity_instance.setArgumentAsString
47+
# because the first attribute GlobalId ultimately
48+
# is of type string.
49+
# Previously, resolving the appropriate function was
50+
# done for each invocation of __setitem__. Now this
51+
# mapping is built once during initialization of the
52+
# module.
53+
_method_dict = {}
54+
for nm in ifcopenshell_wrapper.schema_names():
55+
schema = ifcopenshell_wrapper.schema_by_name(nm)
56+
for decl in schema.declarations():
57+
if isinstance(decl, ifcopenshell_wrapper.entity):
58+
fq_name = ".".join((nm, decl.name()))
59+
60+
# get type strings as reported by IfcOpenShell C++
61+
type_strs = decl.argument_types()
62+
63+
# convert case for setter function
64+
type_strs = [x.title().replace(" ", "") for x in type_strs]
65+
66+
# binary and enumeration are passed from python as string as well
67+
type_strs = [x.replace("Binary", "String") for x in type_strs]
68+
type_strs = [x.replace("Enumeration", "String") for x in type_strs]
69+
70+
# prefix to get method names
71+
fn_names = ["setArgumentAs" + x for x in type_strs]
72+
73+
# resolve to actual functions in wrapper
74+
functions = [
75+
set_derived_atribute if mname == "setArgumentAsDerived" else getattr(ifcopenshell_wrapper.entity_instance, mname) \
76+
for mname in fn_names]
77+
78+
_method_dict[fq_name] = functions
79+
80+
3681
class entity_instance(object):
3782
"""This is the base Python class for all IFC objects.
3883
@@ -52,6 +97,7 @@ def __init__(self, e, file=None):
5297
if isinstance(e, tuple):
5398
e = ifcopenshell_wrapper.new_IfcBaseClass(*e)
5499
super(entity_instance, self).__setattr__("wrapped_data", e)
100+
super(entity_instance, self).__setattr__("method_list", None)
55101
self.wrapped_data.file = file
56102

57103
def __getattr__(self, name):
@@ -65,7 +111,7 @@ def __getattr__(self, name):
65111
return entity_instance.wrap_value(self.wrapped_data.get_inverse(name), self.wrapped_data.file)
66112
else:
67113
raise AttributeError(
68-
"entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(), name)
114+
"entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(True), name)
69115
)
70116

71117
@staticmethod
@@ -129,38 +175,16 @@ def __setitem__(self, idx, value):
129175
if self.wrapped_data.file and self.wrapped_data.file.transaction:
130176
self.wrapped_data.file.transaction.store_edit(self, idx, value)
131177

132-
attr_type = real_attr_type = self.attribute_type(idx).title().replace(" ", "")
133-
real_attr_type = real_attr_type.replace("Derived", "None")
134-
attr_type = attr_type.replace("Binary", "String")
135-
attr_type = attr_type.replace("Enumeration", "String")
136-
178+
if self.method_list is None:
179+
super(entity_instance, self).__setattr__("method_list", _method_dict[self.is_a(True)])
180+
181+
method = self.method_list[idx]
182+
137183
if value is None:
138-
if attr_type != "Derived":
184+
if method is not set_derived_atribute:
139185
self.wrapped_data.setArgumentAsNull(idx)
140-
else:
141-
valid = attr_type != "Derived"
142-
if valid:
143-
try:
144-
if isinstance(value, unicode):
145-
value = value.encode("utf-8")
146-
except BaseException:
147-
pass
148-
149-
try:
150-
if attr_type != "Derived":
151-
getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)(
152-
idx, entity_instance.unwrap_value(value)
153-
)
154-
except BaseException as e:
155-
import traceback
156-
traceback.print_exc()
157-
valid = False
158-
159-
if not valid:
160-
raise ValueError(
161-
"Expected %s for attribute %s.%s, got %r"
162-
% (real_attr_type, self.is_a(), self.attribute_name(idx), value)
163-
)
186+
else:
187+
self.method_list[idx](self.wrapped_data, idx, entity_instance.unwrap_value(value))
164188

165189
return value
166190

src/ifcparse/IfcSchema.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,18 @@ const IfcParse::schema_definition* IfcParse::schema_by_name(const std::string& n
134134
}
135135
return it->second;
136136
}
137+
138+
std::vector<std::string> IfcParse::schema_names() {
139+
// Load schema modules
140+
try {
141+
IfcParse::schema_by_name("IFC2X3");
142+
} catch (IfcParse::IfcException&) {}
143+
144+
// Populate vector with map keys
145+
std::vector<std::string> return_value;
146+
for (auto& pair : schemas) {
147+
return_value.push_back(pair.first);
148+
}
149+
150+
return return_value;
151+
}

src/ifcparse/IfcSchema.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,9 @@ namespace IfcParse {
448448

449449
IFC_PARSE_API const schema_definition* schema_by_name(const std::string&);
450450

451-
void register_schema(schema_definition*);
451+
IFC_PARSE_API std::vector<std::string> schema_names();
452+
453+
IFC_PARSE_API void register_schema(schema_definition*);
452454
}
453455

454456
#endif

src/ifcwrap/IfcParseWrapper.i

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,12 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
225225
return self->declaration().is(s);
226226
}
227227

228-
std::string is_a() const {
229-
return self->declaration().name();
228+
std::string is_a(bool with_schema=false) const {
229+
auto t = self->declaration().name();
230+
if (with_schema) {
231+
t = self->declaration().schema()->name() + "." + t;
232+
}
233+
return t;
230234
}
231235

232236
std::pair<IfcUtil::ArgumentType,Argument*> get_argument(unsigned i) {
@@ -661,11 +665,12 @@ static IfcUtil::ArgumentType helper_fn_attribute_type(const IfcUtil::IfcBaseClas
661665
auto pt = attr->type_of_attribute();
662666
if ($self->derived()[i++]) {
663667
at = IfcUtil::Argument_DERIVED;
664-
}
665-
if (pt == 0) {
668+
} else if (!pt) {
666669
at = IfcUtil::Argument_UNKNOWN;
670+
} else {
671+
at = IfcUtil::from_parameter_type(pt);
667672
}
668-
r.push_back(IfcUtil::ArgumentTypeToString(IfcUtil::from_parameter_type(pt)));
673+
r.push_back(IfcUtil::ArgumentTypeToString(at));
669674
}
670675
return r;
671676
}

0 commit comments

Comments
 (0)