diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index a4698e93d5..582559230a 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -24,24 +24,20 @@ from __future__ import print_function import os import sys -if hasattr(os, 'uname'): +if hasattr(os, "uname"): platform_system = os.uname()[0].lower() else: - platform_system = 'windows' + platform_system = "windows" if sys.maxsize == (1 << 31) - 1: - platform_architecture = '32bit' + platform_architecture = "32bit" else: - platform_architecture = '64bit' + platform_architecture = "64bit" -python_version_tuple = tuple(sys.version.split(' ')[0].split('.')) +python_version_tuple = tuple(sys.version.split(" ")[0].split(".")) -python_distribution = os.path.join(platform_system, - platform_architecture, - 'python%s.%s' % python_version_tuple[:2]) -sys.path.append(os.path.abspath(os.path.join( - os.path.dirname(__file__), - 'lib', python_distribution))) +python_distribution = os.path.join(platform_system, platform_architecture, "python%s.%s" % python_version_tuple[:2]) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "lib", python_distribution))) try: from . import ifcopenshell_wrapper @@ -51,7 +47,7 @@ except Exception as e: import traceback traceback.print_exc() - print('-' * 64) + print("-" * 64) raise ImportError("IfcOpenShell not built for '%s'" % python_distribution) from . import guid @@ -66,17 +62,21 @@ def open(fn): else: raise IOError("Unable to open file for reading") + def create_entity(type, *args, **kwargs): e = entity_instance(type) - attrs = list(enumerate(args)) + \ - [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] + attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] for idx, arg in attrs: e[idx] = arg return e - + + gcroot = [] + + def register_schema(schema): gcroot.append(schema) ifcopenshell_wrapper.register_schema(schema.schema) + from .main import * diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index 313e898e1f..18f6597a0f 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -30,7 +30,7 @@ from . import ifcopenshell_wrapper try: import logging except ImportError as e: - logging = type('logger', (object,), {'exception': staticmethod(lambda s: print(s))}) + logging = type("logger", (object,), {"exception": staticmethod(lambda s: print(s))}) class entity_instance(object): @@ -47,22 +47,25 @@ class entity_instance(object): print(products[0].Representation) >>> #423=IfcProductDefinitionShape($,$,(#409,#421)) """ + def __init__(self, e): if isinstance(e, tuple): e = ifcopenshell_wrapper.new_IfcBaseClass(*e) - super(entity_instance, self).__setattr__('wrapped_data', e) + super(entity_instance, self).__setattr__("wrapped_data", e) def __getattr__(self, name): INVALID, FORWARD, INVERSE = range(3) attr_cat = self.wrapped_data.get_attribute_category(name) if attr_cat == FORWARD: return entity_instance.wrap_value( - self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name))) + self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name)) + ) elif attr_cat == INVERSE: return entity_instance.wrap_value(self.wrapped_data.get_inverse(name)) else: raise AttributeError( - "entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(), name)) + "entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(), name) + ) @staticmethod def walk(f, g, value): @@ -75,17 +78,21 @@ class entity_instance(object): @staticmethod def wrap_value(v): - def wrap(e): return entity_instance(e) + def wrap(e): + return entity_instance(e) - def is_instance(e): return isinstance(e, ifcopenshell_wrapper.entity_instance) + def is_instance(e): + return isinstance(e, ifcopenshell_wrapper.entity_instance) return entity_instance.walk(is_instance, wrap, v) @staticmethod def unwrap_value(v): - def unwrap(e): return e.wrapped_data + def unwrap(e): + return e.wrapped_data - def is_instance(e): return isinstance(e, entity_instance) + def is_instance(e): + return isinstance(e, entity_instance) return entity_instance.walk(is_instance, unwrap, v) @@ -117,32 +124,36 @@ class entity_instance(object): return entity_instance.wrap_value(self.wrapped_data.get_argument(key)) def __setitem__(self, idx, value): - attr_type = real_attr_type = self.attribute_type(idx).title().replace(' ', '') - real_attr_type = real_attr_type.replace('Derived', 'None') - attr_type = attr_type.replace('Binary', 'String') - attr_type = attr_type.replace('Enumeration', 'String') - + attr_type = real_attr_type = self.attribute_type(idx).title().replace(" ", "") + real_attr_type = real_attr_type.replace("Derived", "None") + attr_type = attr_type.replace("Binary", "String") + attr_type = attr_type.replace("Enumeration", "String") + if value is None: if attr_type != "Derived": self.wrapped_data.setArgumentAsNull(idx) else: valid = attr_type != "Derived" - if valid: + if valid: try: if isinstance(value, unicode): value = value.encode("utf-8") except BaseException: pass - + try: if attr_type != "Derived": - getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)(idx, entity_instance.unwrap_value(value)) + getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)( + idx, entity_instance.unwrap_value(value) + ) except BaseException as e: valid = False if not valid: - raise ValueError("Expected %s for attribute %s.%s, got %r" % ( - real_attr_type, self.is_a(), self.attribute_name(idx), value)) + raise ValueError( + "Expected %s for attribute %s.%s, got %r" + % (real_attr_type, self.is_a(), self.attribute_name(idx), value) + ) return value @@ -189,11 +200,15 @@ class entity_instance(object): return hash((self.id(), self.wrapped_data.file_pointer())) def __dir__(self): - return sorted(set(itertools.chain( - dir(type(self)), - map(str, self.wrapped_data.get_attribute_names()), - map(str, self.wrapped_data.get_inverse_attribute_names()) - ))) + return sorted( + set( + itertools.chain( + dir(type(self)), + map(str, self.wrapped_data.get_attribute_names()), + map(str, self.wrapped_data.get_inverse_attribute_names()), + ) + ) + ) def get_info(self, include_identifier=True, recursive=False, return_type=dict, ignore=()): """Return a dictionary of the entity_instance's properties (Python and IFC) and their values. @@ -218,6 +233,7 @@ class entity_instance(object): >>> dict_keys(['Description', 'Name', 'BuildingAddress', 'LongName', 'GlobalId', 'ObjectPlacement', 'OwnerHistory', 'ObjectType', >>> ...'ElevationOfTerrain', 'CompositionType', 'id', 'Representation', 'type', 'ElevationOfRefHeight']) """ + def _(): try: if include_identifier: @@ -231,18 +247,21 @@ class entity_instance(object): continue attr_value = self[i] if recursive: - def is_instance(e): return isinstance(e, entity_instance) + + def is_instance(e): + return isinstance(e, entity_instance) def get_info_(inst): # for ty in ignore: # if inst.is_a(ty): # return None - return entity_instance.get_info(inst, - include_identifier=include_identifier, - recursive=recursive, - return_type=return_type, - ignore=ignore - ) + return entity_instance.get_info( + inst, + include_identifier=include_identifier, + recursive=recursive, + return_type=return_type, + ignore=ignore, + ) attr_value = entity_instance.walk(is_instance, get_info_, attr_value) yield self.attribute_name(i), attr_value diff --git a/src/ifcopenshell-python/ifcopenshell/express/__init__.py b/src/ifcopenshell-python/ifcopenshell/express/__init__.py index 2b3c80b285..3411eb24ed 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/express/__init__.py @@ -10,11 +10,12 @@ exp_parser_fn = os.path.join(d, "express_parser.py") if not os.path.exists(exp_parser_fn): with open(exp_parser_fn, "w") as f: subprocess.call([sys.executable, "bootstrap.py"], cwd=d, stdout=f) - + import express_parser import schema_class import ifcopenshell.ifcopenshell_wrapper + def parse(fn): mapping = express_parser.parse(fn) return schema_class.SchemaClass(mapping, schema_class.LateBoundSchemaInstantiator).code diff --git a/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py b/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py index c5a16a2188..332dcc4b64 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py +++ b/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py @@ -25,47 +25,63 @@ import itertools from pyparsing import * -try: from functools import reduce -except: pass +try: + from functools import reduce +except: + pass + class Expression: def __init__(self, contents): self.contents = contents[0] + def __repr__(self): - if self.op is None: return repr(self.contents) - c = [isinstance(c,str) and c or str(c) for c in self.contents] - if "%s" in self.op: return self.op % (" ".join(c)) - else: return "(%s)" % (" %s "%self.op).join(c) + if self.op is None: + return repr(self.contents) + c = [isinstance(c, str) and c or str(c) for c in self.contents] + if "%s" in self.op: + return self.op % (" ".join(c)) + else: + return "(%s)" % (" %s " % self.op).join(c) + def __iter__(self): return self.contents.__iter__() + class Union(Expression): op = "|" + class Concat(Expression): op = "+" + class Optional(Expression): op = "Optional(%s)" + class Repeated(Expression): op = "ZeroOrMore(%s)" + class Term(Expression): op = None + class Keyword: def __init__(self, contents): self.contents = contents[0] + def __repr__(self): return self.contents + class Terminal: def __init__(self, contents): self.contents = contents[0] s = self.contents - self.is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \ - all(c in alphanums+"_" for c in s[1:-1]) + self.is_keyword = len(s) >= 4 and s[0 :: len(s) - 1] == '""' and all(c in alphanums + "_" for c in s[1:-1]) + def __repr__(self): ty = "CaselessKeyword" if self.is_keyword else "CaselessLiteral" return "%s(%s)" % (ty, self.contents) @@ -78,31 +94,33 @@ RBRACK = Suppress("]") LBRACE = Suppress("{") RBRACE = Suppress("}") EQUALS = Suppress("=") -VBAR = Suppress("|") +VBAR = Suppress("|") PERIOD = Suppress(".") -HASH = Suppress("#") +HASH = Suppress("#") -identifier = Word(alphanums+"_") -keyword = Word(alphanums+"_").setParseAction(Keyword) +identifier = Word(alphanums + "_") +keyword = Word(alphanums + "_").setParseAction(Keyword) expression = Forward() -optional = Group(LBRACK + expression + RBRACK).setParseAction(Optional) -repeated = Group(LBRACE + expression + RBRACE).setParseAction(Repeated) -terminal = quotedString.setParseAction(Terminal) -term = (keyword | terminal | optional | repeated | (LPAREN + expression + RPAREN)).setParseAction(Term) -concat = Group(term + OneOrMore(term)).setParseAction(Concat) -factor = concat | term -union = Group(factor + OneOrMore(VBAR + factor)).setParseAction(Union) -rule = identifier + EQUALS + expression + PERIOD +optional = Group(LBRACK + expression + RBRACK).setParseAction(Optional) +repeated = Group(LBRACE + expression + RBRACE).setParseAction(Repeated) +terminal = quotedString.setParseAction(Terminal) +term = (keyword | terminal | optional | repeated | (LPAREN + expression + RPAREN)).setParseAction(Term) +concat = Group(term + OneOrMore(term)).setParseAction(Concat) +factor = concat | term +union = Group(factor + OneOrMore(VBAR + factor)).setParseAction(Union) +rule = identifier + EQUALS + expression + PERIOD expression << (union | factor) grammar = OneOrMore(Group(rule)) grammar.ignore(HASH + restOfLine) -express = grammar.parseFile(os.path.join(os.path.dirname(__file__), 'express.bnf')) +express = grammar.parseFile(os.path.join(os.path.dirname(__file__), "express.bnf")) -def find_bytype(expr, ty, li = None): - if li is None: li = [] + +def find_bytype(expr, ty, li=None): + if li is None: + li = [] if isinstance(expr, Term): expr = expr.contents if isinstance(expr, ty): @@ -113,34 +131,35 @@ def find_bytype(expr, ty, li = None): find_bytype(term, ty, li) return set(li) + actions = { - 'type_decl' : "TypeDeclaration", - 'entity_decl' : "EntityDeclaration", - 'enumeration_type' : "EnumerationType", - 'aggregation_types' : "AggregationType", - 'general_aggregation_types' : "AggregationType", - 'select_type' : "SelectType", - 'binary_type' : "BinaryType", - 'subtype_declaration' : "SubTypeExpression", - 'supertype_constraint' : "SuperTypeExpression", - 'derive_clause' : "AttributeList", - 'inverse_clause' : "AttributeList", - 'inverse_attr' : "InverseAttribute", - 'bound_spec' : "BoundSpecification", - 'explicit_attr' : "ExplicitAttribute", - 'width_spec' : "WidthSpec", - 'string_type' : "StringType", - 'named_types' : "NamedType", - 'simple_types' : "SimpleType", + "type_decl": "TypeDeclaration", + "entity_decl": "EntityDeclaration", + "enumeration_type": "EnumerationType", + "aggregation_types": "AggregationType", + "general_aggregation_types": "AggregationType", + "select_type": "SelectType", + "binary_type": "BinaryType", + "subtype_declaration": "SubTypeExpression", + "supertype_constraint": "SuperTypeExpression", + "derive_clause": "AttributeList", + "inverse_clause": "AttributeList", + "inverse_attr": "InverseAttribute", + "bound_spec": "BoundSpecification", + "explicit_attr": "ExplicitAttribute", + "width_spec": "WidthSpec", + "string_type": "StringType", + "named_types": "NamedType", + "simple_types": "SimpleType", } to_emit = set(id for id, expr in express) emitted = set() to_combine = set(["simple_id"]) statements = [] - -terminals = reduce(lambda x,y: x | y, (find_bytype(e, Terminal) for id, e in express)) -keywords = list(filter(operator.attrgetter('is_keyword'), terminals)) + +terminals = reduce(lambda x, y: x | y, (find_bytype(e, Terminal) for id, e in express)) +keywords = list(filter(operator.attrgetter("is_keyword"), terminals)) negated_keywords = map(lambda s: "~%s" % s, keywords) no_action = {"letter", "digit", "digits", "real_literal", "integer_literal"} @@ -157,14 +176,15 @@ while True: stmt = " + ".join(itertools.chain(negated_keywords, ("originalTextFor(Combine%s)" % stmt,))) if id not in no_action and not isinstance(expr.contents, Keyword) and not id in to_combine: node_type = "ListNode" if "ZeroOrMore" in stmt else "Node" - action = actions.get(id, "lambda s, loc, t: %s(s, loc, t, rule=\"%s\")" % (node_type, id)) + action = actions.get(id, 'lambda s, loc, t: %s(s, loc, t, rule="%s")' % (node_type, id)) stmt = "%s.setParseAction(%s)" % (stmt, action) - statements.append("%s = %s(\"%s\")" % (id, stmt, id)) + statements.append('%s = %s("%s")' % (id, stmt, id)) to_emit -= emitted_in_loop - if not emitted_in_loop: break + if not emitted_in_loop: + break for id in to_emit: - statements.append("%s = Forward()(\"%s\")" % (id, id)) + statements.append('%s = Forward()("%s")' % (id, id)) for id in to_emit: expr = [e for k, e in express if k == id][0] @@ -173,11 +193,14 @@ for id in to_emit: stmt = "Suppress%s" % stmt if id not in no_action and not isinstance(expr.contents, Keyword): node_type = "ListNode" if "ZeroOrMore" in stmt else "Node" - action = ".setParseAction(%s)" % (actions[id] if id in actions else "lambda s, loc, t: %s(s, loc, t, rule=\"%s\")" % (node_type, id)) + action = ".setParseAction(%s)" % ( + actions[id] if id in actions else 'lambda s, loc, t: %s(s, loc, t, rule="%s")' % (node_type, id) + ) stmt = "(%s)%s" % (stmt, action) statements.append("%s << %s" % (id, stmt)) -print (""" +print( + """ # This file is generated by IfcOpenShell ifcexpressparser bootstrap.py import os @@ -215,4 +238,6 @@ if __name__ == "__main__": mdl = importlib.import_module(output) mdl.Generator(m).emit() sys.stdout.write(m.schema.name) -"""%('\n '.join(statements))) +""" + % ("\n ".join(statements)) +) diff --git a/src/ifcopenshell-python/ifcopenshell/express/codegen.py b/src/ifcopenshell-python/ifcopenshell/express/codegen.py index 050562a591..854c199648 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/codegen.py +++ b/src/ifcopenshell-python/ifcopenshell/express/codegen.py @@ -17,19 +17,23 @@ # # ############################################################################### + class Base(object): """ A base class for all code generation classes. Currently only working around some python 2/3 incompatibilities in terms of unicode file handling. """ + def emit(self): import platform + if tuple(map(int, platform.python_version_tuple())) < (2, 8): from io import open as unicode_open + unicode_type = unicode else: unicode_open = open unicode_type = lambda x, *args, **kwargs: x - f = unicode_open(self.file_name, 'w', encoding='utf-8') - f.write(unicode_type(repr(self), encoding='utf-8', errors='ignore')) + f = unicode_open(self.file_name, "w", encoding="utf-8") + f.write(unicode_type(repr(self), encoding="utf-8", errors="ignore")) f.close() diff --git a/src/ifcopenshell-python/ifcopenshell/express/definitions.py b/src/ifcopenshell-python/ifcopenshell/express/definitions.py index d1563c096c..6f98b91c5c 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/definitions.py +++ b/src/ifcopenshell-python/ifcopenshell/express/definitions.py @@ -24,45 +24,47 @@ import codegen from collections import defaultdict + class Definitions(codegen.Base): def __init__(self, mapping): - + schema_name = mapping.schema.name self.schema_name = schema_name_title = schema_name.capitalize() - - statements = [''] - + + statements = [""] + def write_entity(schema_name, name, type): - + attribute_names = list(map(lambda t: (t.name, t.optional), type.attributes)) for attr, is_optional in attribute_names: statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals()) if is_optional: statements.append("#define SCHEMA_%(name)s_%(attr)s_IS_OPTIONAL" % locals()) - inverse_attribute_names = list(map(operator.attrgetter('name'), type.inverse)) + inverse_attribute_names = list(map(operator.attrgetter("name"), type.inverse)) for attr in inverse_attribute_names: statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals()) - + def write(name): statements.append("#define SCHEMA_HAS_%(name)s" % locals()) fn = None if mapping.schema.is_entity(name): fn = write_entity - + if fn is not None: decl = mapping.schema[name] if isinstance(decl, nodes.TypeDeclaration): decl = decl.type.type fn(schema_name, name, decl) is not False - + for name in mapping.schema: write(name) - + self.str = "\n".join(statements) + "\n" - - self.file_name = '%s-definitions.h' % self.schema_name + + self.file_name = "%s-definitions.h" % self.schema_name def __repr__(self): return self.str + Generator = Definitions diff --git a/src/ifcopenshell-python/ifcopenshell/express/documentation.py b/src/ifcopenshell-python/ifcopenshell/express/documentation.py index 917749790b..6e1a8fdb29 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/documentation.py +++ b/src/ifcopenshell-python/ifcopenshell/express/documentation.py @@ -31,10 +31,12 @@ import re import os import csv -from schema import OrderedCaseInsensitiveDict +from schema import OrderedCaseInsensitiveDict -try: from html.entities import entitydefs -except: from htmlentitydefs import entitydefs +try: + from html.entities import entitydefs +except: + from htmlentitydefs import entitydefs make_absolute = lambda fn: os.path.join(os.path.dirname(os.path.realpath(__file__)), fn) @@ -42,36 +44,45 @@ name_to_oid = OrderedCaseInsensitiveDict() oid_to_desc = {} oid_to_name = {} oid_to_pid = {} -regices = list(zip([re.compile(s,re.M) for s in [r'<[\w\n=" \-/\.;_\t:%#,\?\(\)]+>',r'(\n[\t ]*){2,}',r'^[\t ]+']],['','\n\n',' '])) +regices = list( + zip( + [re.compile(s, re.M) for s in [r'<[\w\n=" \-/\.;_\t:%#,\?\(\)]+>', r"(\n[\t ]*){2,}", r"^[\t ]+"]], + ["", "\n\n", " "], + ) +) -definition_files = ['DocEntity.csv', 'DocEnumeration.csv', 'DocDefined.csv', 'DocSelect.csv'] +definition_files = ["DocEntity.csv", "DocEnumeration.csv", "DocDefined.csv", "DocSelect.csv"] definition_files = map(make_absolute, definition_files) for fn in definition_files: - with open(fn, encoding="utf8", errors='ignore') as f: - for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'): + with open(fn, encoding="utf8", errors="ignore") as f: + for oid, name, desc in csv.reader(f, delimiter=";", quotechar='"'): name_to_oid[name] = oid oid_to_name[oid] = name oid_to_desc[oid] = desc -with open(make_absolute('DocEntityAttributes.csv')) as f: - for pid, x, oid in csv.reader(f, delimiter=';', quotechar='"'): +with open(make_absolute("DocEntityAttributes.csv")) as f: + for pid, x, oid in csv.reader(f, delimiter=";", quotechar='"'): oid_to_pid[oid] = pid -with open(make_absolute('DocAttribute.csv')) as f: - for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'): +with open(make_absolute("DocAttribute.csv")) as f: + for oid, name, desc in csv.reader(f, delimiter=";", quotechar='"'): pid = oid_to_pid[oid] pname = oid_to_name[pid] name_to_oid[".".join((pname, name))] = oid oid_to_desc[oid] = desc + def description(item): global name_to_oid, oid_to_desc, oid_to_name, oid_to_pid - oid = name_to_oid.get(item,0) + oid = name_to_oid.get(item, 0) desc = oid_to_desc.get(oid, None) if desc: - for a,b in entitydefs.items(): desc = desc.replace("&%s;"%a,b) - desc = desc.replace("\r","") - for r,s in regices: desc = r.sub(s,desc) + for a, b in entitydefs.items(): + desc = desc.replace("&%s;" % a, b) + desc = desc.replace("\r", "") + for r, s in regices: + desc = r.sub(s, desc) desc = desc.strip() return desc.split("\n") - else: return [] + else: + return [] diff --git a/src/ifcopenshell-python/ifcopenshell/express/header.py b/src/ifcopenshell-python/ifcopenshell/express/header.py index 35bb0e03e2..6541aae3e9 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/header.py +++ b/src/ifcopenshell-python/ifcopenshell/express/header.py @@ -23,31 +23,35 @@ import codegen import templates import documentation + class Header(codegen.Base): def __init__(self, mapping): declarations = [] - write = lambda str, **kwargs: declarations.append(str%dict({ - 'documentation': templates.multi_line_comment(documentation.description(kwargs['name']))}, **kwargs)) - + write = lambda str, **kwargs: declarations.append( + str + % dict({"documentation": templates.multi_line_comment(documentation.description(kwargs["name"]))}, **kwargs) + ) + forward_names = list(mapping.schema.entities.keys()) + list(mapping.schema.simpletypes.keys()) - forward_definitions = "".join(["class %s; "%n for n in forward_names]) - + forward_definitions = "".join(["class %s; " % n for n in forward_names]) + for name, type in mapping.schema.selects.items(): write(templates.select, name=name) for name, type in mapping.schema.enumerations.items(): short_name = name[:-4] if name.endswith("Enum") else name - write(templates.enumeration, name=name, values=", ".join(["%s_%s"%(short_name, v) for v in type.values])) - + write(templates.enumeration, name=name, values=", ".join(["%s_%s" % (short_name, v) for v in type.values])) + emitted_simpletypes = set() while len(emitted_simpletypes) < len(mapping.schema.simpletypes): for name, type in mapping.schema.simpletypes.items(): - if name.lower() in emitted_simpletypes: continue + if name.lower() in emitted_simpletypes: + continue type_str = mapping.make_type_string(mapping.flatten_type_string(type)) attr_type = mapping.make_argument_type(type) superclass = mapping.simple_type_parent(name) - if superclass is None: + if superclass is None: superclass = "IfcUtil::IfcBaseType" elif superclass.lower() not in emitted_simpletypes: continue @@ -59,91 +63,154 @@ class Header(codegen.Base): class_definitions = [] - write = lambda str, **kwargs: class_definitions.append(str%dict({ - 'documentation': templates.multi_line_comment(documentation.description(kwargs['name']))}, **kwargs)) + write = lambda str, **kwargs: class_definitions.append( + str + % dict({"documentation": templates.multi_line_comment(documentation.description(kwargs["name"]))}, **kwargs) + ) emitted_entities = set() while len(emitted_entities) < len(mapping.schema.entities): for name, type in mapping.schema.entities.items(): - if name.lower() in emitted_entities: continue + if name.lower() in emitted_entities: + continue if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities: attr_lines = [] + def write_method(attr): if attr.optional: attr_lines.append(templates.optional_attribute_description % (attr.name, name)) - attr_lines.append("bool has%s() const;"%(attr.name)) - attr_lines.extend(["/// %s"%d for d in documentation.description(".".join((name, attr.name)))]) + attr_lines.append("bool has%s() const;" % (attr.name)) + attr_lines.extend( + ["/// %s" % d for d in documentation.description(".".join((name, attr.name)))] + ) type_str = mapping.get_parameter_type(attr, allow_optional=False, allow_entities=False) if mapping.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN": - attr_lines.append("%s %s() const;"%(type_str, attr.name)) - attr_lines.append("void set%s(%s v);"%(attr.name, type_str)) + attr_lines.append("%s %s() const;" % (type_str, attr.name)) + attr_lines.append("void set%s(%s v);" % (attr.name, type_str)) [write_method(attr) for attr in type.attributes] inv_lines = [] + def write_inverse(attr): - inv_lines.append(templates.inverse_attr%{'name':attr.name, 'entity':attr.entity, 'attribute':attr.attribute}) + inv_lines.append( + templates.inverse_attr + % {"name": attr.name, "entity": attr.entity, "attribute": attr.attribute} + ) if type.inverse: [write_inverse(attr) for attr in type.inverse] - attributes = "\n".join(["%s%s"%(' '*4, a) for a in attr_lines]) - if len(attributes): attributes += '\n' + attributes = "\n".join(["%s%s" % (" " * 4, a) for a in attr_lines]) + if len(attributes): + attributes += "\n" - inverse = "\n".join(["%s%s"%(' '*4, a) for a in inv_lines]) - if len(inverse): inverse += '\n' + inverse = "\n".join(["%s%s" % (" " * 4, a) for a in inv_lines]) + if len(inverse): + inverse += "\n" def case_norm(n): n = n.lower() return [k for k in mapping.schema.entities.keys() if k.lower() == n][0] - - supertypes = map(case_norm, type.supertypes) if len(type.supertypes) else ['IfcUtil::IfcBaseEntity'] - superclass = ": %s "%(", ".join(["public %s"%c for c in supertypes])) + + supertypes = map(case_norm, type.supertypes) if len(type.supertypes) else ["IfcUtil::IfcBaseEntity"] + superclass = ": %s " % (", ".join(["public %s" % c for c in supertypes])) argument_count = mapping.argument_count(type) argument_start = argument_count - len(type.attributes) - argument_name_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return "%s"; '%(i+argument_start, attr.name) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else "" - argument_name_function_body_tail = (" return %s::getArgumentName(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); ' + argument_name_function_body_switch_stmt = ( + " switch (i) {%s}" + % ( + "".join( + [ + 'case %d: return "%s"; ' % (i + argument_start, attr.name) + for i, attr in enumerate(type.attributes) + ] + ) + ) + if len(type.attributes) + else "" + ) + argument_name_function_body_tail = ( + (" return %s::getArgumentName(i); " % type.supertypes[0]) + if len(type.supertypes) == 1 + else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); ' + ) + + argument_name_function_body = ( + argument_name_function_body_switch_stmt + argument_name_function_body_tail + ) - argument_name_function_body = argument_name_function_body_switch_stmt + argument_name_function_body_tail - derived = mapping.derived_in_supertype(type) - attribute_names = list(map(operator.attrgetter('name'), mapping.arguments(type))) + attribute_names = list(map(operator.attrgetter("name"), mapping.arguments(type))) derived_in_supertype = set(derived) & set(attribute_names) derived_in_supertype_indices = sorted(attribute_names.index(nm) for nm in derived_in_supertype) - attribute_type_cases = ['case %d: return IfcUtil::Argument_DERIVED; ' % idx for idx in derived_in_supertype_indices] - attribute_type_cases += ['case %d: return %s; '%(i+argument_start, mapping.make_argument_type(attr)) for i, attr in enumerate(type.attributes)] - argument_type_function_body_switch_stmt = " switch (i) {%s}"%("".join(attribute_type_cases)) if len(type.attributes) else "" - argument_type_function_body_tail = (" return %s::getArgumentType(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); ' + attribute_type_cases = [ + "case %d: return IfcUtil::Argument_DERIVED; " % idx for idx in derived_in_supertype_indices + ] + attribute_type_cases += [ + "case %d: return %s; " % (i + argument_start, mapping.make_argument_type(attr)) + for i, attr in enumerate(type.attributes) + ] + argument_type_function_body_switch_stmt = ( + " switch (i) {%s}" % ("".join(attribute_type_cases)) if len(type.attributes) else "" + ) + argument_type_function_body_tail = ( + (" return %s::getArgumentType(i); " % type.supertypes[0]) + if len(type.supertypes) == 1 + else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); ' + ) - argument_type_function_body = argument_type_function_body_switch_stmt + argument_type_function_body_tail - - argument_entity_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return %s; '%(i+argument_start, mapping.make_argument_entity(attr)) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else "" - argument_entity_function_body_tail = (" return %s::getArgumentEntity(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); ' + argument_type_function_body = ( + argument_type_function_body_switch_stmt + argument_type_function_body_tail + ) - argument_entity_function_body = argument_entity_function_body_switch_stmt + argument_entity_function_body_tail + argument_entity_function_body_switch_stmt = ( + " switch (i) {%s}" + % ( + "".join( + [ + "case %d: return %s; " % (i + argument_start, mapping.make_argument_entity(attr)) + for i, attr in enumerate(type.attributes) + ] + ) + ) + if len(type.attributes) + else "" + ) + argument_entity_function_body_tail = ( + (" return %s::getArgumentEntity(i); " % type.supertypes[0]) + if len(type.supertypes) == 1 + else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); ' + ) - constructor_arguments = ", ".join("%(full_type)s v%(index)d_%(name)s"%a for a in mapping.get_assignable_arguments(type)) + argument_entity_function_body = ( + argument_entity_function_body_switch_stmt + argument_entity_function_body_tail + ) + + constructor_arguments = ", ".join( + "%(full_type)s v%(index)d_%(name)s" % a for a in mapping.get_assignable_arguments(type) + ) write(templates.entity, **locals()) emitted_entities.add(name) self.str = templates.header % { - 'schema_name_upper' : mapping.schema.name.upper(), - 'schema_name' : mapping.schema.name.capitalize(), - 'declarations' : ''.join(declarations), - 'forward_definitions' : forward_definitions, - 'class_definitions' : ''.join(class_definitions) + "schema_name_upper": mapping.schema.name.upper(), + "schema_name": mapping.schema.name.capitalize(), + "declarations": "".join(declarations), + "forward_definitions": forward_definitions, + "class_definitions": "".join(class_definitions), } self.schema_name = mapping.schema.name.capitalize() - - self.file_name = '%s.h'%self.schema_name - - + + self.file_name = "%s.h" % self.schema_name + def __repr__(self): return self.str + Generator = Header diff --git a/src/ifcopenshell-python/ifcopenshell/express/implementation.py b/src/ifcopenshell-python/ifcopenshell/express/implementation.py index 9f7275aa38..555331728b 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/implementation.py +++ b/src/ifcopenshell-python/ifcopenshell/express/implementation.py @@ -22,6 +22,7 @@ import templates from schema import OrderedCaseInsensitiveDict + class Implementation(codegen.Base): def __init__(self, mapping): enumeration_functions = [] @@ -31,230 +32,324 @@ class Implementation(codegen.Base): schema_name = mapping.schema.name.capitalize() schema_name_upper = mapping.schema.name.upper() - stringify = lambda s: '"%s"'%s + stringify = lambda s: '"%s"' % s cat = lambda vs: "".join(vs) catc = lambda vs: ", ".join(vs) catnl = lambda vs: "\n".join(vs) cator = lambda vs: " || ".join(vs) - nl = lambda s: "%s\n"%s if len(s) else s + nl = lambda s: "%s\n" % s if len(s) else s - write = lambda str, **kwargs: enumeration_functions.append(str%kwargs) + write = lambda str, **kwargs: enumeration_functions.append(str % kwargs) for name, enum in mapping.schema.enumerations.items(): short_name = name[:-4] if name.endswith("Enum") else name context = locals() write( templates.enumeration_function, - max_id = len(enum.values), - name = name, - schema_name = schema_name, - schema_name_upper = schema_name_upper, - values = catc(map(stringify, enum.values)), - from_string_statements = catnl(templates.enum_from_string_stmt%dict(context,**locals()) for value in enum.values) + max_id=len(enum.values), + name=name, + schema_name=schema_name, + schema_name_upper=schema_name_upper, + values=catc(map(stringify, enum.values)), + from_string_statements=catnl( + templates.enum_from_string_stmt % dict(context, **locals()) for value in enum.values + ), ) - write = lambda str, **kwargs: entity_implementations.append(str%kwargs) + write = lambda str, **kwargs: entity_implementations.append(str % kwargs) - for name, type in mapping.schema.entities.items(): - parent_type_test = "" if not type.supertypes or len(type.supertypes) != 1 \ - else templates.parent_type_test%(type.supertypes[0]) - - constructor_arguments = mapping.get_assignable_arguments(type, include_derived = True) - constructor_arguments_str = catc("%(full_type)s v%(index)d_%(name)s"%a for a in constructor_arguments if not a['is_derived']) + for name, type in mapping.schema.entities.items(): + parent_type_test = ( + "" + if not type.supertypes or len(type.supertypes) != 1 + else templates.parent_type_test % (type.supertypes[0]) + ) + + constructor_arguments = mapping.get_assignable_arguments(type, include_derived=True) + constructor_arguments_str = catc( + "%(full_type)s v%(index)d_%(name)s" % a for a in constructor_arguments if not a["is_derived"] + ) attributes = [] constructor_implementations = [] - write_attr = lambda str, **kwargs: attributes.append(str%kwargs) + write_attr = lambda str, **kwargs: attributes.append(str % kwargs) for arg in constructor_arguments: - if not arg['is_inherited'] and not arg['is_derived']: - if arg['is_optional']: + if not arg["is_inherited"] and not arg["is_derived"]: + if arg["is_optional"]: write_attr( templates.const_function, - class_name = name, - schema_name = schema_name, - schema_name_upper = schema_name_upper, - name = 'has%s'%arg['name'], - arguments = '', - return_type = 'bool', - body = templates.optional_attr_stmt % {'index':arg['index']-1} + class_name=name, + schema_name=schema_name, + schema_name_upper=schema_name_upper, + name="has%s" % arg["name"], + arguments="", + return_type="bool", + body=templates.optional_attr_stmt % {"index": arg["index"] - 1}, ) - + def find_template(arg): - simple = mapping.schema.is_simpletype(arg['list_instance_type']) - select = arg['list_instance_type'] == "IfcUtil::IfcBaseClass" - express = mapping.flatten_type_string(arg['list_instance_type']) in mapping.express_to_cpp_typemapping - if arg['is_enum']: return templates.get_attr_stmt_enum - elif arg['is_nested'] and arg['is_templated_list']: return templates.get_attr_stmt_nested_array - elif arg['is_templated_list'] and not (select or simple or express): return templates.get_attr_stmt_array - elif arg['non_optional_type'].endswith('*'): return templates.get_attr_stmt_entity - else: return templates.get_attr_stmt + simple = mapping.schema.is_simpletype(arg["list_instance_type"]) + select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass" + express = ( + mapping.flatten_type_string(arg["list_instance_type"]) in mapping.express_to_cpp_typemapping + ) + if arg["is_enum"]: + return templates.get_attr_stmt_enum + elif arg["is_nested"] and arg["is_templated_list"]: + return templates.get_attr_stmt_nested_array + elif arg["is_templated_list"] and not (select or simple or express): + return templates.get_attr_stmt_array + elif arg["non_optional_type"].endswith("*"): + return templates.get_attr_stmt_entity + else: + return templates.get_attr_stmt tmpl = find_template(arg) write_attr( templates.const_function, - class_name = name, - name = arg['name'], - arguments = '', - schema_name = schema_name, - schema_name_upper = schema_name_upper, - return_type = arg['non_optional_type'], - body = tmpl % {'index': arg['index']-1, - 'type' : arg['non_optional_type'].replace('::Value', ''), - 'list_instance_type' : arg['list_instance_type']} + class_name=name, + name=arg["name"], + arguments="", + schema_name=schema_name, + schema_name_upper=schema_name_upper, + return_type=arg["non_optional_type"], + body=tmpl + % { + "index": arg["index"] - 1, + "type": arg["non_optional_type"].replace("::Value", ""), + "list_instance_type": arg["list_instance_type"], + }, ) - + def find_template(arg): - simple = mapping.schema.is_simpletype(arg['list_instance_type']) - select = arg['list_instance_type'] == "IfcUtil::IfcBaseClass" - express = arg['list_instance_type'] in mapping.express_to_cpp_typemapping - if arg['is_enum']: return templates.set_attr_stmt_enum - elif arg['is_templated_list'] and not (select or simple or express): return templates.set_attr_stmt_array - else: return templates.set_attr_stmt + simple = mapping.schema.is_simpletype(arg["list_instance_type"]) + select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass" + express = arg["list_instance_type"] in mapping.express_to_cpp_typemapping + if arg["is_enum"]: + return templates.set_attr_stmt_enum + elif arg["is_templated_list"] and not (select or simple or express): + return templates.set_attr_stmt_array + else: + return templates.set_attr_stmt tmpl = find_template(arg) write_attr( templates.function, - class_name = name, - name = 'set%s'%arg['name'], - arguments = '%s v'%arg['non_optional_type'], - return_type = 'void', - schema_name = schema_name, - schema_name_upper = schema_name_upper, - body = tmpl % {'index': arg['index']-1, - 'type' : arg['non_optional_type'].replace('::Value', '')} + class_name=name, + name="set%s" % arg["name"], + arguments="%s v" % arg["non_optional_type"], + return_type="void", + schema_name=schema_name, + schema_name_upper=schema_name_upper, + body=tmpl + % {"index": arg["index"] - 1, "type": arg["non_optional_type"].replace("::Value", "")}, ) - if arg['is_derived']: - constructor_implementations.append(templates.constructor_stmt_derived % {'index' : arg['index']-1}) + if arg["is_derived"]: + constructor_implementations.append(templates.constructor_stmt_derived % {"index": arg["index"] - 1}) else: - is_optional_non_naked_ptr = arg['is_optional'] and not arg['non_optional_type'].endswith('*') - arg_name = "v%(index)d_%(name)s"%arg - deref_name = ("*%s"%arg_name) if is_optional_non_naked_ptr else arg_name + is_optional_non_naked_ptr = arg["is_optional"] and not arg["non_optional_type"].endswith("*") + arg_name = "v%(index)d_%(name)s" % arg + deref_name = ("*%s" % arg_name) if is_optional_non_naked_ptr else arg_name - tmpl = templates.constructor_stmt_array if arg['is_templated_list'] \ - else templates.constructor_stmt_enum if arg['is_enum'] \ + tmpl = ( + templates.constructor_stmt_array + if arg["is_templated_list"] + else templates.constructor_stmt_enum + if arg["is_enum"] else templates.constructor_stmt - impl = tmpl % {'name' : deref_name, - 'index' : arg['index']-1, - 'type' : arg['non_optional_type'].replace('::Value', '')} + ) + impl = tmpl % { + "name": deref_name, + "index": arg["index"] - 1, + "type": arg["non_optional_type"].replace("::Value", ""), + } if is_optional_non_naked_ptr: - impl = templates.constructor_stmt_optional%{'name' : arg_name, - 'index' : arg['index']-1, - 'stmt' : impl} + impl = templates.constructor_stmt_optional % { + "name": arg_name, + "index": arg["index"] - 1, + "stmt": impl, + } constructor_implementations.append(impl) - + def get_attribute_index(entity, attr_name): related_entity = mapping.schema.entities[entity] - return [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name.lower()) + return [ + a["name"].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True) + ].index(attr_name.lower()) - inverse = [templates.const_function % { - 'class_name' : name, - 'schema_name' : schema_name, - 'schema_name_upper' : schema_name_upper, - 'name' : i.name, - 'arguments' : '', - 'return_type' : '::%s::%s::list::ptr' % (schema_name, i.entity), - 'body' : templates.get_inverse % {'type': i.entity, 'index':get_attribute_index(i.entity, i.attribute), 'schema_name' : schema_name, 'schema_name_upper': schema_name_upper} - } for i in type.inverse] + inverse = [ + templates.const_function + % { + "class_name": name, + "schema_name": schema_name, + "schema_name_upper": schema_name_upper, + "name": i.name, + "arguments": "", + "return_type": "::%s::%s::list::ptr" % (schema_name, i.entity), + "body": templates.get_inverse + % { + "type": i.entity, + "index": get_attribute_index(i.entity, i.attribute), + "schema_name": schema_name, + "schema_name_upper": schema_name_upper, + }, + } + for i in type.inverse + ] - superclass = "%s((IfcEntityInstanceData*)0)" % type.supertypes[0] if len(type.supertypes) == 1 else 'IfcUtil::IfcBaseEntity()' + superclass = ( + "%s((IfcEntityInstanceData*)0)" % type.supertypes[0] + if len(type.supertypes) == 1 + else "IfcUtil::IfcBaseEntity()" + ) write( templates.entity_implementation, - name = name, - parent_type_test = parent_type_test, - constructor_arguments = constructor_arguments_str, - constructor_implementation = cat(constructor_implementations), - attributes = nl(catnl(attributes)), - inverse = nl(catnl(inverse)), - superclass = superclass, - schema_name = schema_name, - schema_name_upper = schema_name_upper + name=name, + parent_type_test=parent_type_test, + constructor_arguments=constructor_arguments_str, + constructor_implementation=cat(constructor_implementations), + attributes=nl(catnl(attributes)), + inverse=nl(catnl(inverse)), + superclass=superclass, + schema_name=schema_name, + schema_name_upper=schema_name_upper, ) - selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(map(str, mapping.schema.types.keys()))) - schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.simpletypes.items()] - schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.entities.items()] + selectable_simple_types = sorted( + set(sum([b.values for a, b in mapping.schema.selects.items()], [])) + & set(map(str, mapping.schema.types.keys())) + ) + schema_entity_statements += [ + templates.schema_entity_stmt % locals() for name, type in mapping.schema.simpletypes.items() + ] + schema_entity_statements += [ + templates.schema_entity_stmt % locals() for name, type in mapping.schema.entities.items() + ] - enumerable_types = sorted(set([name for name, type in mapping.schema.types.items()] + [name for name, type in mapping.schema.entities.items()])) + enumerable_types = sorted( + set( + [name for name, type in mapping.schema.types.items()] + + [name for name, type in mapping.schema.entities.items()] + ) + ) max_len = max(map(len, enumerable_types)) type_name_strings = catc(map(stringify, enumerable_types)) - string_map_statements = [templates.string_map_statement % { - 'uppercase_name' : name.upper(), - 'name' : name, - 'padding' : ' ' * (max_len - len(name)) - } for name in enumerable_types] - - enumeration_index_by_str = OrderedCaseInsensitiveDict((j,i) for i,j in enumerate(enumerable_types)) + string_map_statements = [ + templates.string_map_statement + % {"uppercase_name": name.upper(), "name": name, "padding": " " * (max_len - len(name))} + for name in enumerable_types + ] + + enumeration_index_by_str = OrderedCaseInsensitiveDict((j, i) for i, j in enumerate(enumerable_types)) + def get_parent_id(s): e = mapping.schema.entities.get(s) if e and e.supertypes: return enumeration_index_by_str[e.supertypes[0]] - else: return -1 + else: + return -1 parent_type_statements = ",".join(map(str, map(get_parent_id, enumerable_types))) max_id = len(enumerable_types) - simple_type_statements = cator("v == Type::%s"%name for name in selectable_simple_types) - + simple_type_statements = cator("v == Type::%s" % name for name in selectable_simple_types) + simple_type_impl = [] for class_name, type in mapping.schema.simpletypes.items(): type_str = mapping.make_type_string(mapping.flatten_type_string(type)) attr_type = mapping.make_argument_type(type) superclass = mapping.simple_type_parent(class_name) - - simpletype_impl_is = templates.simpletype_impl_is_with_supertype if superclass \ + + simpletype_impl_is = ( + templates.simpletype_impl_is_with_supertype + if superclass else templates.simpletype_impl_is_without_supertype - - constructor = templates.constructor_single_initlist if superclass \ - else templates.constructor - - simpletype_impl_cast = templates.simpletype_impl_cast_templated if mapping.is_templated_list(type) \ + ) + + constructor = templates.constructor_single_initlist if superclass else templates.constructor + + simpletype_impl_cast = ( + templates.simpletype_impl_cast_templated + if mapping.is_templated_list(type) else templates.simpletype_impl_cast - - simpletype_impl_constructor = templates.simpletype_impl_constructor_templated if mapping.is_templated_list(type) \ + ) + + simpletype_impl_constructor = ( + templates.simpletype_impl_constructor_templated + if mapping.is_templated_list(type) else templates.simpletype_impl_constructor - + ) + def compose(params, schema_name=schema_name, schema_name_upper=schema_name_upper): class_name, attr_type, superclass, superclass_init, name, tmpl, return_type, args, body = params underlying_type = mapping.list_instance_type(type) arguments = ",".join(args) body = body % locals() return tmpl % locals() - - simple_type_impl.append(templates.simpletype_impl_comment % {'name': class_name}) - simple_type_impl.extend(map(compose, map(lambda x: (class_name, attr_type, superclass, "(IfcEntityInstanceData*)0")+x, ( - ('Class', templates.function, 'const IfcParse::type_declaration&', (), templates.simpletype_impl_class ), - ('declaration', templates.const_function, 'const IfcParse::type_declaration&', (), templates.simpletype_impl_declaration ), - ('', constructor, '', ('IfcEntityInstanceData* e',), templates.simpletype_impl_explicit_constructor), - ('', constructor, '', ("%s v" % type_str,), simpletype_impl_constructor ), - ('', templates.cast_function, type_str, (), simpletype_impl_cast ) - )))) - simple_type_impl.append('') - - external_definitions = [("extern entity* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.entities.keys() ] + \ - [("extern type_declaration* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.simpletypes.keys()] + + simple_type_impl.append(templates.simpletype_impl_comment % {"name": class_name}) + simple_type_impl.extend( + map( + compose, + map( + lambda x: (class_name, attr_type, superclass, "(IfcEntityInstanceData*)0") + x, + ( + ( + "Class", + templates.function, + "const IfcParse::type_declaration&", + (), + templates.simpletype_impl_class, + ), + ( + "declaration", + templates.const_function, + "const IfcParse::type_declaration&", + (), + templates.simpletype_impl_declaration, + ), + ( + "", + constructor, + "", + ("IfcEntityInstanceData* e",), + templates.simpletype_impl_explicit_constructor, + ), + ("", constructor, "", ("%s v" % type_str,), simpletype_impl_constructor), + ("", templates.cast_function, type_str, (), simpletype_impl_cast), + ), + ), + ) + ) + simple_type_impl.append("") + + external_definitions = [ + ("extern entity* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.entities.keys() + ] + [ + ("extern type_declaration* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.simpletypes.keys() + ] self.str = templates.implementation % { - 'schema_name_upper' : schema_name_upper, - 'schema_name' : schema_name, - 'max_id' : max_id, - 'enumeration_functions' : cat(enumeration_functions), - 'schema_entity_statements' : catnl(schema_entity_statements), - 'type_name_strings' : type_name_strings, - 'string_map_statements' : catnl(string_map_statements), - 'simple_type_statement' : simple_type_statements, - 'parent_type_statements' : parent_type_statements, - 'entity_implementations' : catnl(entity_implementations), - 'simple_type_impl' : catnl(simple_type_impl), - 'external_definitions' : catnl(external_definitions) + "schema_name_upper": schema_name_upper, + "schema_name": schema_name, + "max_id": max_id, + "enumeration_functions": cat(enumeration_functions), + "schema_entity_statements": catnl(schema_entity_statements), + "type_name_strings": type_name_strings, + "string_map_statements": catnl(string_map_statements), + "simple_type_statement": simple_type_statements, + "parent_type_statements": parent_type_statements, + "entity_implementations": catnl(entity_implementations), + "simple_type_impl": catnl(simple_type_impl), + "external_definitions": catnl(external_definitions), } self.schema_name = mapping.schema.name.capitalize() - - self.file_name = '%s.cpp'%self.schema_name - - + + self.file_name = "%s.cpp" % self.schema_name + def __repr__(self): return self.str diff --git a/src/ifcopenshell-python/ifcopenshell/express/mapping.py b/src/ifcopenshell-python/ifcopenshell/express/mapping.py index 2ea3379c22..0c05413a2c 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/mapping.py +++ b/src/ifcopenshell-python/ifcopenshell/express/mapping.py @@ -23,57 +23,80 @@ import sys import nodes import templates + class Mapping: express_to_cpp_typemapping = { - 'boolean' : 'bool', - 'logical' : 'bool', - 'integer' : 'int', - 'real' : 'double', - 'number' : 'double', - 'string' : 'std::string', - 'binary' : 'boost::dynamic_bitset<>' + "boolean": "bool", + "logical": "bool", + "integer": "int", + "real": "double", + "number": "double", + "string": "std::string", + "binary": "boost::dynamic_bitset<>", } - - supported_argument_types = set([ - 'INT', 'BOOL', 'DOUBLE', 'STRING', 'BINARY', 'ENUMERATION', 'ENTITY_INSTANCE', - 'AGGREGATE_OF_INT', 'AGGREGATE_OF_DOUBLE', 'AGGREGATE_OF_STRING', 'AGGREGATE_OF_BINARY', 'AGGREGATE_OF_ENTITY_INSTANCE', - 'AGGREGATE_OF_AGGREGATE_OF_INT', 'AGGREGATE_OF_AGGREGATE_OF_DOUBLE', 'AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE', - ]) + + supported_argument_types = set( + [ + "INT", + "BOOL", + "DOUBLE", + "STRING", + "BINARY", + "ENUMERATION", + "ENTITY_INSTANCE", + "AGGREGATE_OF_INT", + "AGGREGATE_OF_DOUBLE", + "AGGREGATE_OF_STRING", + "AGGREGATE_OF_BINARY", + "AGGREGATE_OF_ENTITY_INSTANCE", + "AGGREGATE_OF_AGGREGATE_OF_INT", + "AGGREGATE_OF_AGGREGATE_OF_DOUBLE", + "AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE", + ] + ) def __init__(self, schema): self.schema = schema - + def flatten_type_string(self, type): return self.flatten_type_string(self.schema.types[type].type) if self.schema.is_simpletype(type) else type - + def flatten_type(self, type): res = self.flatten_type(self.schema.types[type].type) if self.schema.is_simpletype(type) else type return res - + def simple_type_parent(self, type): parent = self.schema.types[type].type - if isinstance(parent, (nodes.AggregationType, nodes.StringType)) or (isinstance(parent, nodes.SimpleType) and isinstance(parent.type, nodes.StringType)): + if isinstance(parent, (nodes.AggregationType, nodes.StringType)) or ( + isinstance(parent, nodes.SimpleType) and isinstance(parent.type, nodes.StringType) + ): return None if str(parent) in self.express_to_cpp_typemapping: return None return str(parent) def make_type_string(self, type): - if isinstance(type, nodes.StringType) or (isinstance(type, nodes.SimpleType) and isinstance(type.type, nodes.StringType)): + if isinstance(type, nodes.StringType) or ( + isinstance(type, nodes.SimpleType) and isinstance(type.type, nodes.StringType) + ): type = "string" if isinstance(type, (str, nodes.BinaryType, nodes.SimpleType, nodes.NamedType)): return self.express_to_cpp_typemapping.get(str(type), "::%s::%s" % (self.schema.name.capitalize(), type)) else: if type.bounds is None: - import pdb; pdb.set_trace() + import pdb + + pdb.set_trace() is_list = self.schema.is_entity(type.type) is_nested_list = isinstance(type.type, nodes.AggregationType) - tmpl = templates.list_list_type if is_nested_list else templates.list_type if is_list else templates.array_type + tmpl = ( + templates.list_list_type if is_nested_list else templates.list_type if is_list else templates.array_type + ) return tmpl % { - 'instance_type' : self.make_type_string(self.flatten_type_string(type.type)), - 'lower' : type.bounds.lower, - 'upper' : type.bounds.upper, + "instance_type": self.make_type_string(self.flatten_type_string(type.type)), + "lower": type.bounds.lower, + "upper": type.bounds.upper, } def is_array(self, type): @@ -83,12 +106,15 @@ class Mapping: return self.is_array(self.schema.types[type].type) else: return False - + def make_argument_entity(self, attr): - type = attr.type if hasattr(attr, 'type') else attr - while isinstance(type, nodes.AggregationType): type = type.type - if str(type) in self.express_to_cpp_typemapping: return "Type::UNDEFINED" - else: return "Type::%s" % type + type = attr.type if hasattr(attr, "type") else attr + while isinstance(type, nodes.AggregationType): + type = type.type + if str(type) in self.express_to_cpp_typemapping: + return "Type::UNDEFINED" + else: + return "Type::%s" % type def make_argument_type(self, attr): def _make_argument_type(type): @@ -104,18 +130,20 @@ class Mapping: return "ENUMERATION" elif isinstance(type, nodes.AggregationType): ty = _make_argument_type(type.type) - if ty == "UNKNOWN": return "UNKNOWN" + if ty == "UNKNOWN": + return "UNKNOWN" return "AGGREGATE_OF_" + ty elif str(type) in self.express_to_cpp_typemapping: - return self.express_to_cpp_typemapping.get(str(type), type).split('::')[-1].upper() + return self.express_to_cpp_typemapping.get(str(type), type).split("::")[-1].upper() elif self.schema.is_type(type): return _make_argument_type(self.schema.types[type].type) else: raise ValueError("Unable to map type %r for attribute %r" % (type, attr)) - ty = _make_argument_type(attr.type if hasattr(attr, 'type') else attr) + + ty = _make_argument_type(attr.type if hasattr(attr, "type") else attr) if ty not in self.supported_argument_types: print("Attribute %r mapped as 'unknown'" % (attr), file=sys.stderr) - ty = 'UNKNOWN' + ty = "UNKNOWN" return "IfcUtil::Argument_%s" % ty def get_type_dep(self, type): @@ -124,18 +152,20 @@ class Mapping: else: return self.get_type_dep(type.type) - def get_parameter_type(self, attr, allow_optional, allow_entities, allow_pointer = True): + def get_parameter_type(self, attr, allow_optional, allow_entities, allow_pointer=True): attr_type = self.flatten_type(attr.type) - - if (isinstance(attr_type, nodes.SimpleType) and isinstance(attr_type.type, nodes.StringType)) or isinstance(attr_type, nodes.StringType): + + if (isinstance(attr_type, nodes.SimpleType) and isinstance(attr_type.type, nodes.StringType)) or isinstance( + attr_type, nodes.StringType + ): type_str = self.express_to_cpp_typemapping["string"] else: type_str = self.express_to_cpp_typemapping.get(str(attr_type), attr_type) - + is_ptr = False - + if self.schema.is_enumeration(attr_type): - type_str = '::%s::%s::Value' % (self.schema.name.capitalize(), attr_type) + type_str = "::%s::%s::Value" % (self.schema.name.capitalize(), attr_type) elif isinstance(type_str, nodes.AggregationType): is_nested_list = isinstance(attr_type.type, nodes.AggregationType) ty = self.get_parameter_type(attr_type.type if is_nested_list else attr_type, False, allow_entities, False) @@ -144,18 +174,12 @@ class Mapping: elif self.schema.is_simpletype(ty) or str(ty) in self.express_to_cpp_typemapping.values(): tmpl = templates.nested_array_type if is_nested_list else templates.array_type bounds = (attr_type.bounds.lower, attr_type.bounds.upper) if attr_type.bounds else (-1, -1) - type_str = tmpl % { - 'instance_type' : ty, - 'lower' : bounds[0], - 'upper' : bounds[1] - } + type_str = tmpl % {"instance_type": ty, "lower": bounds[0], "upper": bounds[1]} else: tmpl = templates.list_list_type if is_nested_list else templates.list_type - type_str = tmpl % { - 'instance_type': ty - } - elif (self.schema.is_entity(type_str) or self.schema.is_select(type_str)): - type_str = '::%s::%s' % (self.schema.name.capitalize(), attr_type) + type_str = tmpl % {"instance_type": ty} + elif self.schema.is_entity(type_str) or self.schema.is_select(type_str): + type_str = "::%s::%s" % (self.schema.name.capitalize(), attr_type) if allow_pointer: type_str += "*" is_ptr = True @@ -163,7 +187,7 @@ class Mapping: type_str = "IfcUtil::IfcBaseClass*" is_ptr = True if allow_optional and attr.optional and not is_ptr: - type_str = "boost::optional< %s >"%type_str + type_str = "boost::optional< %s >" % type_str return type_str def argument_count(self, t): @@ -181,39 +205,49 @@ class Mapping: def list_instance_type(self, attr): attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr - if isinstance(attr_type, str): return None + if isinstance(attr_type, str): + return None + def f(v): v = self.flatten_type(v) - if isinstance(v, (nodes.AggregationType, nodes.StringType)) or (isinstance(v, nodes.SimpleType) and isinstance(v.type, nodes.StringType)): + if isinstance(v, (nodes.AggregationType, nodes.StringType)) or ( + isinstance(v, nodes.SimpleType) and isinstance(v.type, nodes.StringType) + ): return "string" if self.schema.is_select(v): - return 'IfcUtil::IfcBaseClass' + return "IfcUtil::IfcBaseClass" elif str(v) in self.schema.types or str(v) in self.schema.entities: return "::%s::%s" % (self.schema.name.capitalize(), v) - else: return str(v) + else: + return str(v) + if self.is_array(attr_type): if not isinstance(attr_type, str) and self.is_array(attr_type.type): if isinstance(attr_type.type, str): return f(attr_type.type) - else: return f(attr_type.type.type) + else: + return f(attr_type.type.type) else: if isinstance(attr_type, str): return f(attr_type) - else: return f(attr_type.type) + else: + return f(attr_type.type) return None def is_templated_list(self, attr): attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr - if isinstance(attr, str): return False + if isinstance(attr, str): + return False ty = self.list_instance_type(attr) - if ty is None: return False + if ty is None: + return False arr = self.is_array(attr_type) simple = self.schema.is_simpletype(ty) express = self.flatten_type_string(ty) in self.express_to_cpp_typemapping - select = ty == 'IfcUtil::IfcBaseClass' + select = ty == "IfcUtil::IfcBaseClass" return arr and not simple and not express and not select - def get_assignable_arguments(self, t, include_derived = False): + def get_assignable_arguments(self, t, include_derived=False): count = self.argument_count(t) num_inherited = count - len(t.attributes) derived = set(self.derived_in_supertype(t)) @@ -224,22 +258,27 @@ class Mapping: supported = self.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN" return not_derived and supported - return [{ - 'index' : i+1, - 'name' : attr.name, - 'full_type' : self.get_parameter_type(attr, allow_optional=True, allow_entities=True), - 'specialized_type' : self.get_parameter_type(attr, allow_optional=True, allow_entities=False), - 'non_optional_type' : self.get_parameter_type(attr, allow_optional=False, allow_entities=False), - 'list_instance_type' : self.list_instance_type(attr), - 'is_optional' : attr.optional, - 'is_inherited' : i < num_inherited, - 'is_enum' : attr.type in self.schema.enumerations, - 'is_array' : self.is_array(attr.type), - 'is_nested' : self.is_array(attr.type) and not isinstance(attr.type, str) and self.is_array(attr.type.type), - 'is_derived' : attr.name in derived, - 'is_templated_list' : self.is_templated_list(attr), - 'argument_type_enum' : self.make_argument_type(attr), - 'argument_entity' : self.make_argument_entity(attr), - 'argument_type' : attr.type - } for i, attr in attrs if include(attr)] - + return [ + { + "index": i + 1, + "name": attr.name, + "full_type": self.get_parameter_type(attr, allow_optional=True, allow_entities=True), + "specialized_type": self.get_parameter_type(attr, allow_optional=True, allow_entities=False), + "non_optional_type": self.get_parameter_type(attr, allow_optional=False, allow_entities=False), + "list_instance_type": self.list_instance_type(attr), + "is_optional": attr.optional, + "is_inherited": i < num_inherited, + "is_enum": attr.type in self.schema.enumerations, + "is_array": self.is_array(attr.type), + "is_nested": self.is_array(attr.type) + and not isinstance(attr.type, str) + and self.is_array(attr.type.type), + "is_derived": attr.name in derived, + "is_templated_list": self.is_templated_list(attr), + "argument_type_enum": self.make_argument_type(attr), + "argument_entity": self.make_argument_entity(attr), + "argument_type": attr.type, + } + for i, attr in attrs + if include(attr) + ] diff --git a/src/ifcopenshell-python/ifcopenshell/express/nodes.py b/src/ifcopenshell-python/ifcopenshell/express/nodes.py index bac00d6814..e888768ecc 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/nodes.py +++ b/src/ifcopenshell-python/ifcopenshell/express/nodes.py @@ -23,61 +23,77 @@ import io import string import collections + class Node: def __init__(self, s, loc, tokens, rule=None): self.rule = rule or (type(self).__name__) self.tokens = tokens.asDict() - self.flat = sum([getattr(t, 'flat', [t]) for t in tokens.asList()], []) + self.flat = sum([getattr(t, "flat", [t]) for t in tokens.asList()], []) if rule is None: self.init() + def __repr__(self): return "%s(%s)" % (self.rule, ",".join("%s:%s" % i for i in self.tokens.items())) + def __getattr__(self, k): return self.tokens.get(k) - def __getstate__(self): return self.__dict__ - def __setstate__(self, d): self.__dict__.update(d) - def init(self): pass + + def __getstate__(self): + return self.__dict__ + + def __setstate__(self, d): + self.__dict__.update(d) + + def init(self): + pass + def any(self): return next(iter(self.tokens.values())) + class ListNode: def __init__(self, s, loc, tokens, rule=None): self.rule = rule or (type(self).__name__) self.tokens = tokens.asList() - self.flat = sum([getattr(t, 'flat', [t]) for t in self.tokens], []) + self.flat = sum([getattr(t, "flat", [t]) for t in self.tokens], []) + def __repr__(self): return "%s[%s]" % (self.rule, ",".join("%s" % i for i in self.tokens)) + def __iter__(self): return iter(self.tokens) + def __getitem__(self, i): return self.tokens[i] - def init(self): pass - + + def init(self): + pass + class SimpleType(Node): - def get_type(self): t = self.any() - if (type(t) == Node): + if type(t) == Node: return t.any() else: t = t[0] - if (type(t) == Node): + if type(t) == Node: return t.any().any() else: return t - + type = property(get_type) - + def __repr__(self): return str(self.type) def format_clause(exp): def whitespace(t): - if t in {'=', '|', '<*', 'or', 'in', '<>', 'and'}: - return ' %s ' % t + if t in {"=", "|", "<*", "or", "in", "<>", "and"}: + return " %s " % t return t + return "".join(whitespace(term) for term in exp.flat) @@ -85,18 +101,18 @@ class TypeDeclaration(Node): name = property(lambda self: self.type_id[0]) utype = property(lambda self: self.underlying_type.any().any()) type = property(lambda self: self.utype[0] if isinstance(self.utype, list) else self.utype) - - def init(self): - + + def init(self): + assert hasattr(self, "TYPE") - + self.where = [] clause = self.where_clause if clause: clause = clause[0] - + self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]] - + def __repr__(self): s = "TYPE %s = %s;\n" % (self.name, self.type) if self.where: @@ -112,7 +128,7 @@ class EntityDeclaration(Node): supertype = property(lambda self: self.entity_head[0].subsuper[0].supertype_constraint) subtype = property(lambda self: self.entity_head[0].subsuper[0].subtype_declaration) supertypes = property(lambda self: [self.subtype.super_type] if self.subtype else []) - + def get_abstract(self): if self.entity_head[0].subsuper[0].supertype_constraint: return self.entity_head[0].subsuper[0].supertype_constraint.abstract @@ -120,72 +136,71 @@ class EntityDeclaration(Node): return False abstract = property(get_abstract) - + def init(self): - def redeclared_attribute(a): try: return ( a.attribute_decl.redeclared_attribute.qualified_attribute.group_qualifier.simple_id, - a.attribute_decl.redeclared_attribute.qualified_attribute.attribute_qualifier.simple_id + a.attribute_decl.redeclared_attribute.qualified_attribute.attribute_qualifier.simple_id, ) except: return a.attribute_decl.simple_id - - assert self.flat[0] == 'entity' - + + assert self.flat[0] == "entity" + self.attributes = [a for a in self.entity_body[0] if isinstance(a, ExplicitAttribute)] self.inverse = [] - alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == 'inverse'] + alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == "inverse"] if alist: - self.inverse = alist[0] - + self.inverse = alist[0] + self.derive = [] - alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == 'derive'] + alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == "derive"] if alist: alist = alist[0] self.derive = [(redeclared_attribute(a), format_clause(a.expression[0])) for a in alist] - + self.where = [] clause = [r for r in self.entity_body[0] if r.rule == "where_clause"] if clause: clause = clause[0] - + self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]] - + self.unique = [] clause = [r for r in self.entity_body[0] if r.rule == "unique_clause"] if clause: clause = clause[0] self.unique = [(r[0], r[2].simple_id) for r in clause[1::2]] - + def __repr__(self): strm = io.StringIO() - + print("ENTITY %s" % self.name, file=strm) - + if self.supertype: print("", self.supertype, file=strm) if self.subtype: print("", self.subtype, file=strm) - + strm.seek(strm.tell() - 1) print(";", file=strm) - + for a in self.attributes: - print(" ", a, ";", file=strm, sep='') - + print(" ", a, ";", file=strm, sep="") + if self.derive: print(" DERIVE", file=strm) for nm, exp in self.derive: if isinstance(nm, tuple): nm = "SELF\\%s.%s" % nm print(" %s : %s;" % (nm, exp), file=strm) - + if self.inverse: print(" INVERSE", file=strm) print(self.inverse, file=strm) - + if self.where: print(" WHERE", file=strm) for nm_exp in self.where: @@ -195,19 +210,21 @@ class EntityDeclaration(Node): print(" UNIQUE", file=strm) for nm_exp in self.unique: print(" %s : %s;" % nm_exp, file=strm) - + print("END_ENTITY;", file=strm) return strm.getvalue() class EnumerationType(Node): values = property(lambda self: self.enumeration_type[2][1::2]) + def __repr__(self): - return "ENUMERATION OF (" + ",".join(self.values) + ")" - - + return "ENUMERATION OF (" + ",".join(self.values) + ")" + + class NamedType(Node): type = property(lambda self: self.simple_id) + def __repr__(self): return self.type @@ -216,7 +233,7 @@ class AggregationType(Node): aggregate_type = property(lambda self: self.flat[0]) bounds = property(lambda self: (list(self.tokens.values())[0][0].bound_spec or [None])[0]) unique = property(lambda self: list(self.tokens.values())[0][0].UNIQUE is not None) - + def get_type(self): v = list(self.tokens.values())[0][0] if v.instantiable_type: @@ -231,51 +248,61 @@ class AggregationType(Node): elif v.parameter_type.generalized_types.general_aggregation_types: return v.parameter_type.generalized_types.general_aggregation_types else: - import pdb; pdb.set_trace() + import pdb + + pdb.set_trace() raise ValueError() - + type = property(get_type) - + def init(self): assert self.bounds is None or isinstance(self.bounds, BoundSpecification) - + def __repr__(self): - return "%s%s of %s%s"%(self.aggregate_type, self.bounds, "unique " if self.unique else "", self.type) + return "%s%s of %s%s" % (self.aggregate_type, self.bounds, "unique " if self.unique else "", self.type) class SelectType(Node): values = property(lambda self: self.select_type[1][1::2]) + def __repr__(self): return "SELECT (" + ",".join(map(str, self.values)) + ")" class SuperTypeExpression(Node): abstract = property(lambda self: self.abstract_supertype_declaration is not None) + def get_sub_types(self): if self.abstract: constraint = self.abstract_supertype_declaration[0] else: constraint = self.supertype_rule[0] - return [s[0][0].simple_id for s in constraint.subtype_constraint[0].supertype_expression[0][0][0].one_of[0][2::2]] + return [ + s[0][0].simple_id for s in constraint.subtype_constraint[0].supertype_expression[0][0][0].one_of[0][2::2] + ] sub_types = property(get_sub_types) - + def __repr__(self): - return "%sSUPERTYPE OF(ONEOF(%s))" % ("ABSTRACT " if self.abstract else "",",".join(self.sub_types)) + return "%sSUPERTYPE OF(ONEOF(%s))" % ("ABSTRACT " if self.abstract else "", ",".join(self.sub_types)) class SubTypeExpression(Node): super_type = property(lambda self: self.entity_ref[0]) - + def __repr__(self): return "SUBTYPE OF(%s)" % self.super_type + class AttributeList(ListNode): - type = property(lambda self: self.flat[0] if self.flat[0] in {'inverse', 'derive'} else 'explicit') + type = property(lambda self: self.flat[0] if self.flat[0] in {"inverse", "derive"} else "explicit") + def __repr__(self): - return "\n".join([" %s;"%s for s in self.tokens[1:]]) + return "\n".join([" %s;" % s for s in self.tokens[1:]]) + def __iter__(self): return iter(self.tokens[1:]) + def __len__(self): return len(self.tokens[1:]) @@ -286,6 +313,7 @@ class InverseAttribute(Node): bounds = property(lambda self: self.bound_spec[0] if self.bound_spec else None) entity = property(lambda self: self.entity_ref[0]) attribute = property(lambda self: self.attribute_ref[0]) + def __repr__(self): def _(): yield self.name @@ -298,8 +326,10 @@ class InverseAttribute(Node): yield self.entity yield "FOR" yield self.attribute + return " ".join(map(str, _())) + """ class DerivedAttribute(Node): def init(self): @@ -310,6 +340,7 @@ class DerivedAttribute(Node): return str(self.name) """ + class BinaryType(Node): def __repr__(self): return "binary" @@ -320,32 +351,32 @@ class BoundSpecification(Node): upper = property(lambda self: self.flat[3]) def __repr__(self): - return "[%s:%s]"%(self.lower, self.upper) + return "[%s:%s]" % (self.lower, self.upper) class ExplicitAttribute(Node): name = property(lambda self: self.attribute_decl.simple_id) optional = property(lambda self: self.OPTIONAL is not None) - + def get_type(self): v = next(iter(self.parameter_type.tokens.values())) if v.general_aggregation_types: return v.general_aggregation_types else: return v - + type = property(get_type) - + def __repr__(self): return "%s : %s%s" % (self.name, "optional " if self.optional else "", self.type) - + class WidthSpec(Node): fixed = property(lambda self: self.FIXED is not None) - + def init(self): - self.width = int(''.join(self.width[0].flat)) - + self.width = int("".join(self.width[0].flat)) + def __repr__(self): return "(%d)%s" % (self.width, " fixed" if self.fixed else "") diff --git a/src/ifcopenshell-python/ifcopenshell/express/schema.py b/src/ifcopenshell-python/ifcopenshell/express/schema.py index d7614b685b..4a812ecd51 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/schema.py +++ b/src/ifcopenshell-python/ifcopenshell/express/schema.py @@ -23,13 +23,15 @@ import collections if tuple(map(int, platform.python_version_tuple())) < (2, 7): import ordereddict + collections.OrderedDict = ordereddict.OrderedDict - -# According to ISO 10303-11 7.1.2: Letters: "... The case of + +# According to ISO 10303-11 7.1.2: Letters: "... The case of # letters is significant only within explicit string literals." class OrderedCaseInsensitiveDict_KeyObject(str): def __eq__(self, other): return self.lower() == other.lower() + def __hash__(self): return hash(self.lower()) @@ -39,53 +41,73 @@ class OrderedCaseInsensitiveDict(collections.OrderedDict): collections.OrderedDict.__init__(self) for key, value in collections.OrderedDict(*args, **kwargs).items(): self[OrderedCaseInsensitiveDict_KeyObject(key)] = value + def __setitem__(self, key, value): return collections.OrderedDict.__setitem__(self, OrderedCaseInsensitiveDict_KeyObject(key), value) + def __getitem__(self, key): return collections.OrderedDict.__getitem__(self, OrderedCaseInsensitiveDict_KeyObject(key)) + def get(self, key, *args, **kwargs): return collections.OrderedDict.get(self, OrderedCaseInsensitiveDict_KeyObject(key), *args, **kwargs) + def __contains__(self, key): return collections.OrderedDict.__contains__(self, OrderedCaseInsensitiveDict_KeyObject(key)) + def __delitem__(self, key): return collections.OrderedDict.__delitem__(self, OrderedCaseInsensitiveDict_KeyObject(key)) - class Schema: def is_enumeration(self, v): return str(v) in self.enumerations + def is_select(self, v): return str(v) in self.selects + def is_simpletype(self, v): return str(v) in self.simpletypes + def is_type(self, v): return str(v) in self.types + def is_entity(self, v): return str(v) in self.entities + def __len__(self): return len(self.types) + len(self.entities) + def __iter__(self): return iter(self.keys) + def __getitem__(self, key): return self.types_entities[key] + def __init__(self, parsetree): self.name = parsetree.syntax[0][0].simple_id - - sort = lambda d: OrderedCaseInsensitiveDict(sorted(d)) - - declarations = [d.any()[0] for d in parsetree.syntax[0][0].schema_body[0] if d.rule == 'declaration' and d.any()[0].rule != 'function_decl'] - self.types = sort([(t.name,t) for t in declarations if isinstance(t, nodes.TypeDeclaration)]) - self.entities = sort([(t.name,t) for t in declarations if isinstance(t, nodes.EntityDeclaration)]) - + sort = lambda d: OrderedCaseInsensitiveDict(sorted(d)) + + declarations = [ + d.any()[0] + for d in parsetree.syntax[0][0].schema_body[0] + if d.rule == "declaration" and d.any()[0].rule != "function_decl" + ] + + self.types = sort([(t.name, t) for t in declarations if isinstance(t, nodes.TypeDeclaration)]) + self.entities = sort([(t.name, t) for t in declarations if isinstance(t, nodes.EntityDeclaration)]) + self.keys = list(self.types.keys()) + list(self.entities.keys()) self.types_entities = {k: v for d in (self.types, self.entities) for k, v in d.items()} - of_type = lambda *types: sort([(a, b.type) for a,b in self.types.items() if any(isinstance(b.type, ty) for ty in types)]) + of_type = lambda *types: sort( + [(a, b.type) for a, b in self.types.items() if any(isinstance(b.type, ty) for ty in types)] + ) self.enumerations = of_type(nodes.EnumerationType) self.selects = of_type(nodes.SelectType) - self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType, nodes.StringType, nodes.SimpleType, nodes.NamedType) - + self.simpletypes = of_type( + str, nodes.AggregationType, nodes.BinaryType, nodes.StringType, nodes.SimpleType, nodes.NamedType + ) + assert len(self.enumerations) + len(self.selects) + len(self.simpletypes) == len(self.types) diff --git a/src/ifcopenshell-python/ifcopenshell/express/schema_class.py b/src/ifcopenshell-python/ifcopenshell/express/schema_class.py index 1fa75c5671..1ae24453f0 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/schema_class.py +++ b/src/ifcopenshell-python/ifcopenshell/express/schema_class.py @@ -27,109 +27,127 @@ from collections import defaultdict import ifcopenshell.ifcopenshell_wrapper as w + class LateBoundSchemaInstantiator: - def __init__(self, schema_name): self.schema_name = schema_name - self.schema_name_title = schema_name.capitalize() + self.schema_name_title = schema_name.capitalize() self.declarations = {} self.names = [] # We need to make sure anonymous types are not gc'ed. self.cache = [] - + def aggregation_type(self, aggr_type, bound1, bound2, decl_type): - self.cache.append(w.aggregation_type(getattr(w.aggregation_type, aggr_type + "_type"), bound1, bound2, decl_type)) + self.cache.append( + w.aggregation_type(getattr(w.aggregation_type, aggr_type + "_type"), bound1, bound2, decl_type) + ) return self.cache[-1] - + def simple_type(self, type): self.cache.append(w.simple_type(getattr(w.simple_type, type + "_type"))) return self.cache[-1] - + def named_type(self, type): self.cache.append(w.named_type(self.declarations[str(type)])) return self.cache[-1] - + def declare(self, definition_type, name): self.names.append(str(name)) - + def begin_schema(self): self.names.sort(key=str.lower) - + def typedef(self, name, declared_type): index_in_schema = self.names.index(str(name)) self.declarations[str(name)] = w.type_declaration(name, index_in_schema, declared_type) - + def enumeration(self, name, enum): schema_name = self.schema_name index_in_schema = self.names.index(str(name)) self.declarations[str(name)] = w.enumeration_type(name, index_in_schema, sorted(enum.values)) - + def entity(self, name, type): index_in_schema = self.names.index(str(name)) supertype = None if len(type.supertypes) == 0 else self.declarations[str(type.supertypes[0])] self.declarations[str(name)] = w.entity(name, type.abstract, index_in_schema, supertype) - + def select(self, name, type): index_in_schema = self.names.index(str(name)) children = [self.declarations[str(v)] for v in type.values] self.declarations[str(name)] = w.select_type(name, index_in_schema, children) - + def entity_attributes(self, name, attribute_definitions, is_derived): attributes = [] for attr_name, decl_type, optional in attribute_definitions: attributes.append(w.attribute(attr_name, decl_type, optional)) self.declarations[str(name)].set_attributes(attributes, is_derived) self.cache.append(attributes) - + def inverse_attributes(self, name, inv_attrs): attributes = [] for attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index in inv_attrs: en = self.declarations[str(entity_ref)] - attributes.append(w.inverse_attribute(attr_name, getattr(w.inverse_attribute, aggr_type + "_type"), bound1, bound2, en, en.attributes()[attribute_entity_index])) + attributes.append( + w.inverse_attribute( + attr_name, + getattr(w.inverse_attribute, aggr_type + "_type"), + bound1, + bound2, + en, + en.attributes()[attribute_entity_index], + ) + ) self.declarations[str(name)].set_inverse_attributes(attributes) - + def entity_subtypes(self, name, tys): self.declarations[str(name)].set_subtypes([self.declarations[str(v)] for v in tys]) - - def finalize(self, can_be_instantiated_set, override_schema_name = None): - self.schema = w.schema_definition(override_schema_name or self.schema_name, list(self.declarations.values()), None) - + + def finalize(self, can_be_instantiated_set, override_schema_name=None): + self.schema = w.schema_definition( + override_schema_name or self.schema_name, list(self.declarations.values()), None + ) + class EarlyBoundCodeWriter: - def __init__(self, schema_name): self.schema_name = schema_name self.schema_name_title = schema_name.capitalize() - - self.statements = ['', - '#include "../ifcparse/IfcSchema.h"', - '#include "../ifcparse/%(schema_name_title)s.h"' % self.__dict__, - '', - 'using namespace IfcParse;', - ''] - + + self.statements = [ + "", + '#include "../ifcparse/IfcSchema.h"', + '#include "../ifcparse/%(schema_name_title)s.h"' % self.__dict__, + "", + "using namespace IfcParse;", + "", + ] + self.names = [] - + def aggregation_type(self, aggr_type, bound1, bound2, decl_type): - return "new aggregation_type(aggregation_type::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(decl_type)s)" % locals() - + return ( + "new aggregation_type(aggregation_type::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(decl_type)s)" + % locals() + ) + def simple_type(self, type): return "new simple_type(simple_type::%s_type)" % type - + def named_type(self, type): return "new named_type(%s_%s_type)" % (self.schema_name, type) - + def declare(self, definition_type, name): schema_name = self.schema_name - self.statements.append('%(definition_type)s* %(schema_name)s_%(name)s_type = 0;' % locals()) + self.statements.append("%(definition_type)s* %(schema_name)s_%(name)s_type = 0;" % locals()) self.names.append(name) - + def begin_schema(self): self.names.sort(key=str.lower) - + self.statements.append("{factory_placeholder}") - - self.statements.append(""" + + self.statements.append( + """ #if defined(__clang__) __attribute__((optnone)) #elif defined(__GNUC__) || defined(__GNUG__) @@ -138,132 +156,182 @@ __attribute__((optnone)) #elif defined(_MSC_VER) #pragma optimize("", off) #endif - """) - self.statements.append('IfcParse::schema_definition* %s_populate_schema() {' % self.schema_name) - + """ + ) + self.statements.append("IfcParse::schema_definition* %s_populate_schema() {" % self.schema_name) + def typedef(self, name, declared_type): schema_name = self.schema_name index_in_schema = self.names.index(name) - self.statements.append(' %(schema_name)s_%(name)s_type = new type_declaration("%(name)s", %(index_in_schema)d, %(declared_type)s);' % locals()) - + self.statements.append( + ' %(schema_name)s_%(name)s_type = new type_declaration("%(name)s", %(index_in_schema)d, %(declared_type)s);' + % locals() + ) + def enumeration(self, name, enum): schema_name = self.schema_name index_in_schema = self.names.index(name) - self.statements.append(' {') - self.statements.append(' std::vector items; items.reserve(%d);' % len(enum.values)) + self.statements.append(" {") + self.statements.append(" std::vector items; items.reserve(%d);" % len(enum.values)) self.statements.extend(map(lambda v: ' items.push_back("%s");' % v, sorted(enum.values))) - self.statements.append(' %(schema_name)s_%(name)s_type = new enumeration_type("%(name)s", %(index_in_schema)d, items);' % locals()) - self.statements.append(' }') - + self.statements.append( + ' %(schema_name)s_%(name)s_type = new enumeration_type("%(name)s", %(index_in_schema)d, items);' + % locals() + ) + self.statements.append(" }") + def entity(self, name, type): schema_name = self.schema_name index_in_schema = self.names.index(name) - supertype = '0' if len(type.supertypes) == 0 else '%s_%s_type' % (self.schema_name, type.supertypes[0]) + supertype = "0" if len(type.supertypes) == 0 else "%s_%s_type" % (self.schema_name, type.supertypes[0]) is_abstract = "true" if type.abstract else "false" - self.statements.append(' %(schema_name)s_%(name)s_type = new entity("%(name)s", %(is_abstract)s, %(index_in_schema)d, %(supertype)s);' % locals()) - + self.statements.append( + ' %(schema_name)s_%(name)s_type = new entity("%(name)s", %(is_abstract)s, %(index_in_schema)d, %(supertype)s);' + % locals() + ) + def select(self, name, type): schema_name = self.schema_name index_in_schema = self.names.index(name) - self.statements.append(' {') - self.statements.append(' std::vector items; items.reserve(%d);' % len(type.values)) - self.statements.extend(map(lambda v: ' items.push_back(%s_%s_type);' % (self.schema_name, v), sorted(map(str, type.values)))) - self.statements.append(' %(schema_name)s_%(name)s_type = new select_type("%(name)s", %(index_in_schema)d, items);' % locals()) - self.statements.append(' }') - + self.statements.append(" {") + self.statements.append(" std::vector items; items.reserve(%d);" % len(type.values)) + self.statements.extend( + map(lambda v: " items.push_back(%s_%s_type);" % (self.schema_name, v), sorted(map(str, type.values))) + ) + self.statements.append( + ' %(schema_name)s_%(name)s_type = new select_type("%(name)s", %(index_in_schema)d, items);' + % locals() + ) + self.statements.append(" }") + def entity_attributes(self, name, attribute_definitions, is_derived): schema_name = self.schema_name - self.statements.append(' {') - self.statements.append(' std::vector attributes; attributes.reserve(%d);' % len(attribute_definitions)) + self.statements.append(" {") + self.statements.append( + " std::vector attributes; attributes.reserve(%d);" % len(attribute_definitions) + ) for attr_name, decl_type, optional in attribute_definitions: optional_cpp = str(optional).lower() - self.statements.append(' attributes.push_back(new attribute("%(attr_name)s", %(decl_type)s, %(optional_cpp)s));' % locals()) - self.statements.append(' std::vector derived; derived.reserve(%d);' % len(is_derived)) - self.statements.append(' ' + " ".join(map(lambda b: 'derived.push_back(%s);' % str(b).lower(), is_derived))) - self.statements.append(' %(schema_name)s_%(name)s_type->set_attributes(attributes, derived);' % locals()) - self.statements.append(' }') - + self.statements.append( + ' attributes.push_back(new attribute("%(attr_name)s", %(decl_type)s, %(optional_cpp)s));' + % locals() + ) + self.statements.append(" std::vector derived; derived.reserve(%d);" % len(is_derived)) + self.statements.append( + " " + " ".join(map(lambda b: "derived.push_back(%s);" % str(b).lower(), is_derived)) + ) + self.statements.append(" %(schema_name)s_%(name)s_type->set_attributes(attributes, derived);" % locals()) + self.statements.append(" }") + def inverse_attributes(self, name, inv_attrs): schema_name = self.schema_name - self.statements.append(' {') - self.statements.append(' std::vector attributes; attributes.reserve(%d);' % len(inv_attrs)) + self.statements.append(" {") + self.statements.append( + " std::vector attributes; attributes.reserve(%d);" % len(inv_attrs) + ) for attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index in inv_attrs: - self.statements.append(' attributes.push_back(new inverse_attribute("%(attr_name)s", inverse_attribute::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(schema_name)s_%(entity_ref)s_type, %(schema_name)s_%(attribute_entity)s_type->attributes()[%(attribute_entity_index)d]));' % locals()) - self.statements.append(' %(schema_name)s_%(name)s_type->set_inverse_attributes(attributes);' % locals()) - self.statements.append(' }') - + self.statements.append( + ' attributes.push_back(new inverse_attribute("%(attr_name)s", inverse_attribute::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(schema_name)s_%(entity_ref)s_type, %(schema_name)s_%(attribute_entity)s_type->attributes()[%(attribute_entity_index)d]));' + % locals() + ) + self.statements.append(" %(schema_name)s_%(name)s_type->set_inverse_attributes(attributes);" % locals()) + self.statements.append(" }") + def entity_subtypes(self, name, tys): schema_name = self.schema_name - self.statements.append(' {') - self.statements.append(' std::vector defs; defs.reserve(%d);' % len(tys)) - self.statements.append((' ' + "".join(map(lambda t: ("defs.push_back(%%(schema_name)s_%s_type);" % t), tys))) % locals()) - self.statements.append(' %(schema_name)s_%(name)s_type->set_subtypes(defs);' % locals()) - self.statements.append(' }') - + self.statements.append(" {") + self.statements.append(" std::vector defs; defs.reserve(%d);" % len(tys)) + self.statements.append( + (" " + "".join(map(lambda t: ("defs.push_back(%%(schema_name)s_%s_type);" % t), tys))) % locals() + ) + self.statements.append(" %(schema_name)s_%(name)s_type->set_subtypes(defs);" % locals()) + self.statements.append(" }") + def finalize(self, can_be_instantiated_set): schema_name = self.schema_name schema_name_title = self.schema_name.capitalize() - + num_declarations = len(self.names) - - self.statements.append('') - self.statements.append(' std::vector declarations; declarations.reserve(%(num_declarations)d);' % locals()) + + self.statements.append("") + self.statements.append( + " std::vector declarations; declarations.reserve(%(num_declarations)d);" % locals() + ) for type_name in self.names: - self.statements.append(' declarations.push_back(%(schema_name)s_%(type_name)s_type);' % locals()) - - self.statements.append(' return new schema_definition("%(schema_name)s", declarations, new %(schema_name)s_instance_factory());' % locals()) - - self.statements.extend(('}','')) - - self.statements.append(""" + self.statements.append(" declarations.push_back(%(schema_name)s_%(type_name)s_type);" % locals()) + + self.statements.append( + ' return new schema_definition("%(schema_name)s", declarations, new %(schema_name)s_instance_factory());' + % locals() + ) + + self.statements.extend(("}", "")) + + self.statements.append( + """ #if defined(__clang__) #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC pop_options #elif defined(_MSC_VER) #pragma optimize("", on) #endif - """) - - self.statements.extend(('const schema_definition& %s::get_schema() {' % schema_name_title, - '', - ' static const schema_definition* s = %(schema_name)s_populate_schema();' % locals(), - ' return *s;', - '}','','')) - + """ + ) + + self.statements.extend( + ( + "const schema_definition& %s::get_schema() {" % schema_name_title, + "", + " static const schema_definition* s = %(schema_name)s_populate_schema();" % locals(), + " return *s;", + "}", + "", + "", + ) + ) + def can_be_instantiated(idx_name): name = idx_name[1] return name in can_be_instantiated_set - + instance_mapping = """switch(data->type()->index_in_schema()) { %s default: throw IfcParse::IfcException(data->type()->name() + " cannot be instantiated"); } -""" % "\n ".join(map(lambda tup: ("case %%d: return new ::%s::%%s(data);" % schema_name_title) % tup, filter(can_be_instantiated, enumerate(self.names)))) +""" % "\n ".join( + map( + lambda tup: ("case %%d: return new ::%s::%%s(data);" % schema_name_title) % tup, + filter(can_be_instantiated, enumerate(self.names)), + ) + ) - self.statements[self.statements.index("{factory_placeholder}")] = """ + self.statements[self.statements.index("{factory_placeholder}")] = ( + """ class %(schema_name)s_instance_factory : public IfcParse::instance_factory { virtual IfcUtil::IfcBaseClass* operator()(IfcEntityInstanceData* data) const { %(instance_mapping)s } }; -""" % locals() +""" + % locals() + ) def __str__(self): - return "\n".join(self.statements) + return "\n".join(self.statements) class SchemaClass(codegen.Base): def __init__(self, mapping, code=EarlyBoundCodeWriter): - - class UnmetDependenciesException(Exception): pass - + class UnmetDependenciesException(Exception): + pass + schema_name = mapping.schema.name self.schema_name = schema_name_title = schema_name.capitalize() declared_types = [] - + x = code(schema_name) - + def get_declared_type(type, emitted_names=None): if isinstance(type, nodes.SimpleType): type = type.type @@ -272,7 +340,7 @@ class SchemaClass(codegen.Base): if isinstance(type, nodes.AggregationType): aggr_type = type.aggregate_type - make_bound = lambda b: -1 if b == '?' else int(b) + make_bound = lambda b: -1 if b == "?" else int(b) bound1, bound2 = map(make_bound, (type.bounds.lower, type.bounds.upper)) decl_type = get_declared_type(type.type, emitted_names) return x.aggregation_type(aggr_type, bound1, bound2, decl_type) @@ -295,38 +363,41 @@ class SchemaClass(codegen.Base): attributes_per_subtype = [] while True: entity = mapping.schema.entities[entity_name] - attr_names = list(map(operator.attrgetter('name'), entity.attributes)) + attr_names = list(map(operator.attrgetter("name"), entity.attributes)) if len(attr_names): attributes_per_subtype.append((entity_name, attr_names)) - if len(entity.supertypes) != 1: break + if len(entity.supertypes) != 1: + break entity_name = entity.supertypes[0] index = 0 for et, attrs in attributes_per_subtype[::-1]: - try: return et, attrs.index(attribute_name) - except: pass - + try: + return et, attrs.index(attribute_name) + except: + pass + else: raise Exception("No declared type for <%r>" % type) - - - collections_by_type = (('entity', mapping.schema.entities ), - ('type_declaration', mapping.schema.simpletypes ), - ('select_type', mapping.schema.selects ), - ('enumeration_type', mapping.schema.enumerations)) + collections_by_type = ( + ("entity", mapping.schema.entities), + ("type_declaration", mapping.schema.simpletypes), + ("select_type", mapping.schema.selects), + ("enumeration_type", mapping.schema.enumerations), + ) for definition_type, collection in collections_by_type: for name in collection.keys(): x.declare(definition_type, name) - + declarations_by_index = [] - + x.begin_schema() - + emitted = set() len_to_emit = len(mapping.schema) - - def write_simpletype(schema_name, name, type): + + def write_simpletype(schema_name, name, type): try: declared_type = get_declared_type(type, emitted) except UnmetDependenciesException: @@ -335,20 +406,22 @@ class SchemaClass(codegen.Base): return False x.typedef(name, declared_type) - + def write_enumeration(schema_name, name, enum): x.enumeration(name, enum) - + def write_entity(schema_name, name, type): if len(type.supertypes) == 0 or set(map(lambda s: s.lower(), type.supertypes)) < emitted: x.entity(name, type) - else: return False - + else: + return False + def write_select(schema_name, name, type): if set(map(lambda s: str(s).lower(), type.values)) < emitted: x.select(name, type) - else: return False - + else: + return False + def write(name): if mapping.schema.is_simpletype(name): fn = write_simpletype @@ -358,64 +431,69 @@ class SchemaClass(codegen.Base): fn = write_entity elif mapping.schema.is_select(name): fn = write_select - + decl = mapping.schema[name] if isinstance(decl, nodes.TypeDeclaration): decl = decl.type return fn(schema_name, name, decl) is not False - + while len(emitted) < len_to_emit: for name in mapping.schema: - if name.lower() in emitted: continue + if name.lower() in emitted: + continue if write(name): emitted.add(name.lower()) declarations_by_index.append(name) - declared_types.append('%(schema_name)s_%(name)s_type' % locals()) + declared_types.append("%(schema_name)s_%(name)s_type" % locals()) num_declarations = len(declared_types) - + for name, type in mapping.schema.entities.items(): derived = set(mapping.derived_in_supertype(type)) - attribute_names = list(map(operator.attrgetter('name'), mapping.arguments(type))) + attribute_names = list(map(operator.attrgetter("name"), mapping.arguments(type))) is_derived = [b in derived for b in attribute_names] attribute_definitions = [] for attr in type.attributes: decl_type = get_declared_type(attr.type) attribute_definitions.append((attr.name, decl_type, attr.optional)) x.entity_attributes(name, attribute_definitions, is_derived) - + for name, type in mapping.schema.entities.items(): if type.inverse: inv_attrs = [] for attr in type.inverse: if attr.bounds: - make_bound = lambda b: -1 if b == '?' else int(b) + make_bound = lambda b: -1 if b == "?" else int(b) bound1, bound2 = map(make_bound, (attr.bounds.lower, attr.bounds.upper)) else: bound1, bound2 = -1, -1 attr_name, aggr_type, entity_ref = attr.name, attr.type, attr.entity - if aggr_type is None: aggr_type = 'unspecified' + if aggr_type is None: + aggr_type = "unspecified" attribute_entity, attribute_entity_index = find_inverse_name_and_index(entity_ref, attr.attribute) - inv_attrs.append((attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index)) + inv_attrs.append( + (attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index) + ) x.inverse_attributes(name, inv_attrs) - + subtypes = defaultdict(list) - + for name, type in mapping.schema.entities.items(): for ty in type.supertypes: subtypes[ty].append(name) - + for name, tys in subtypes.items(): x.entity_subtypes(name, tys) - + can_be_instantiated_set = set(list(mapping.schema.entities.keys()) + list(mapping.schema.simpletypes.keys())) x.finalize(can_be_instantiated_set) - + self.str = str(x) - self.file_name = '%s-schema.cpp' % self.schema_name + self.file_name = "%s-schema.cpp" % self.schema_name self.code = x def __repr__(self): return self.str + Generator = SchemaClass diff --git a/src/ifcopenshell-python/ifcopenshell/express/templates.py b/src/ifcopenshell-python/ifcopenshell/express/templates.py index 1fee20d5d3..7b18a9cf03 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/templates.py +++ b/src/ifcopenshell-python/ifcopenshell/express/templates.py @@ -65,7 +65,7 @@ enum_header = """ lb_header = """""" -implementation= """ +implementation = """ #include "../ifcparse/%(schema_name)s.h" #include "../ifcparse/IfcSchema.h" #include "../ifcparse/IfcException.h" @@ -103,8 +103,8 @@ enumeration_descriptor = """ values.clear(); values.reserve(128); enumeration_descriptor_value = ' values.push_back("%(name)s");' -derived_field_statement = ' {std::set idxs; %(statements)sderived_map[Type::%(type)s] = idxs;}'; -derived_field_statement_attrs = 'idxs.insert(%d); ' +derived_field_statement = " {std::set idxs; %(statements)sderived_map[Type::%(type)s] = idxs;}" +derived_field_statement_attrs = "idxs.insert(%d); " simpletype = """%(documentation)s class IFC_PARSE_API %(name)s : public %(superclass)s { @@ -118,19 +118,24 @@ public: """ simpletype_impl_comment = "// Function implementations for %(name)s" -simpletype_impl_argument_type = "if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcAttributeOutOfRangeException(\"Argument index out of range\"); }" +simpletype_impl_argument_type = 'if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); }' simpletype_impl_argument = "return data_->getArgument(i);" simpletype_impl_is_with_supertype = "return v == %(class_name)s_type || %(superclass)s::is(v);" simpletype_impl_is_without_supertype = "return v == %(class_name)s_type;" simpletype_impl_type = "return *%(schema_name_upper)s_%(class_name)s_type;" simpletype_impl_class = "return *%(schema_name_upper)s_%(class_name)s_type;" simpletype_impl_explicit_constructor = "data_ = e;" -simpletype_impl_constructor = "data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(class_name)s_type); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v" +"); data_->setArgument(0, attr);}" +simpletype_impl_constructor = ( + "data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(class_name)s_type); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v" + + "); data_->setArgument(0, attr);}" +) simpletype_impl_constructor_templated = "data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(class_name)s_type); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v->generalize()); data_->setArgument(0, attr);}" simpletype_impl_cast = "return *data_->getArgument(0);" -simpletype_impl_cast_templated = "IfcEntityList::ptr es = *data_->getArgument(0); return es->as< %(underlying_type)s >();" +simpletype_impl_cast_templated = ( + "IfcEntityList::ptr es = *data_->getArgument(0); return es->as< %(underlying_type)s >();" +) simpletype_impl_declaration = "return *%(schema_name_upper)s_%(class_name)s_type;" - + select = """%(documentation)s typedef IfcUtil::IfcBaseClass %(name)s; """ @@ -154,7 +159,7 @@ public: }; """ -enumeration_function=""" +enumeration_function = """ const char* %(schema_name)s::%(name)s::ToString(Value v) { if ( v < 0 || v >= %(max_id)d ) throw IfcException("Unable to find find keyword in schema"); const char* names[] = { %(values)s }; @@ -181,7 +186,9 @@ optional_attribute_description = "/// Whether the optional attribute %s is defin function = "%(return_type)s %(schema_name)s::%(class_name)s::%(name)s(%(arguments)s) { %(body)s }" const_function = "%(return_type)s %(schema_name)s::%(class_name)s::%(name)s(%(arguments)s) const { %(body)s }" constructor = "%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) { %(body)s }" -constructor_single_initlist = "%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) : %(superclass)s(%(superclass_init)s) { %(body)s }" +constructor_single_initlist = ( + "%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) : %(superclass)s(%(superclass_init)s) { %(body)s }" +) cast_function = "%(schema_name)s::%(class_name)s::operator %(return_type)s() const { %(body)s }" array_type = "std::vector< %(instance_type)s > /*[%(lower)s:%(upper)s]*/" @@ -193,9 +200,9 @@ inverse_attr = "IfcTemplatedEntityList< %(entity)s >::ptr %(name)s() const; // I enum_from_string_stmt = ' if (s == "%(value)s") return ::%(schema_name)s::%(name)s::%(short_name)s_%(value)s;' -schema_entity_stmt = ' case Type::%(name)s: return new %(name)s(e); break;' +schema_entity_stmt = " case Type::%(name)s: return new %(name)s(e); break;" string_map_statement = ' string_map["%(uppercase_name)s"%(padding)s] = Type::%(name)s;' -parent_type_stmt = ' if(v==%(name)s%(padding)s) { return %(parent)s; }' +parent_type_stmt = " if(v==%(name)s%(padding)s) { return %(parent)s; }" parent_type_test = " || %s::is(v)" @@ -204,24 +211,46 @@ optional_attr_stmt = "return !data_->getArgument(%(index)d)->isNull();" get_attr_stmt = "return *data_->getArgument(%(index)d);" get_attr_stmt_enum = "return %(type)s::FromString(*data_->getArgument(%(index)d));" get_attr_stmt_entity = "return (%(type)s)((IfcUtil::IfcBaseClass*)(*data_->getArgument(%(index)d)));" -get_attr_stmt_array = "IfcEntityList::ptr es = *data_->getArgument(%(index)d); return es->as< %(list_instance_type)s >();" -get_attr_stmt_nested_array = "IfcEntityListList::ptr es = *data_->getArgument(%(index)d); return es->as< %(list_instance_type)s >();" +get_attr_stmt_array = ( + "IfcEntityList::ptr es = *data_->getArgument(%(index)d); return es->as< %(list_instance_type)s >();" +) +get_attr_stmt_nested_array = ( + "IfcEntityListList::ptr es = *data_->getArgument(%(index)d); return es->as< %(list_instance_type)s >();" +) get_inverse = "return data_->getInverse(%(schema_name_upper)s_%(type)s_type, %(index)d)->as<%(type)s>();" -set_attr_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v" +");data_->setArgument(%(index)d,attr);}" -set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,%(type)s::ToString(v)));data_->setArgument(%(index)d,attr);}" -set_attr_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v->generalize()" +");data_->setArgument(%(index)d,attr);}" +set_attr_stmt = ( + "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v" + + ");data_->setArgument(%(index)d,attr);}" +) +set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,%(type)s::ToString(v)));data_->setArgument(%(index)d,attr);}" +set_attr_stmt_array = ( + "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v->generalize()" + + ");data_->setArgument(%(index)d,attr);}" +) -constructor_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)" +");data_->setArgument(%(index)d,attr);}" -constructor_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((IfcWrite::IfcWriteArgument::EnumerationReference(%(name)s,%(type)s::ToString(%(name)s)))" +");data_->setArgument(%(index)d,attr);}" -constructor_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)->generalize()" +");data_->setArgument(%(index)d,attr);}" -constructor_stmt_derived = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::Derived()" +");data_->setArgument(%(index)d,attr);}" +constructor_stmt = ( + "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)" + + ");data_->setArgument(%(index)d,attr);}" +) +constructor_stmt_enum = ( + "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((IfcWrite::IfcWriteArgument::EnumerationReference(%(name)s,%(type)s::ToString(%(name)s)))" + + ");data_->setArgument(%(index)d,attr);}" +) +constructor_stmt_array = ( + "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)->generalize()" + + ");data_->setArgument(%(index)d,attr);}" +) +constructor_stmt_derived = ( + "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::Derived()" + + ");data_->setArgument(%(index)d,attr);}" +) constructor_stmt_optional = " if (%(name)s) {%(stmt)s } else { IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(boost::blank()); data_->setArgument(%(index)d, attr); }" -inverse_implementation = " inverse_map[Type::%(type)s].insert(std::make_pair(\"%(name)s\", std::make_pair(Type::%(related_type)s, %(index)d)));" +inverse_implementation = ' inverse_map[Type::%(type)s].insert(std::make_pair("%(name)s", std::make_pair(Type::%(related_type)s, %(index)d)));' + def multi_line_comment(li): - return ("/// %s"%("\n/// ".join(li))) if len(li) else "" - + return ("/// %s" % ("\n/// ".join(li))) if len(li) else "" diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 52ad1e99b7..9ada1529fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -51,6 +51,7 @@ class file(object): print(products[0] == ifc_file[122] == ifc_file['2XQ$n5SLP5MBLyL442paFx']) >>> True """ + def __init__(self, f=None, schema=None): if f is not None: self.wrapped_data = f @@ -82,14 +83,13 @@ class file(object): e = entity_instance((self.schema, type)) self.wrapped_data.add(e.wrapped_data) e.wrapped_data.this.disown() - attrs = list(enumerate(args)) + \ - [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] + attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] for idx, arg in attrs: e[idx] = arg return e def __getattr__(self, attr): - if attr[0:6] == 'create': + if attr[0:6] == "create": return functools.partial(self.create_entity, attr[6:]) else: return getattr(self.wrapped_data, attr) diff --git a/src/ifcopenshell-python/ifcopenshell/geom/__init__.py b/src/ifcopenshell-python/ifcopenshell/geom/__init__.py index 04db49a6e2..b0d85ec273 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/__init__.py @@ -21,15 +21,18 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function + def _has_occ(): try: import OCC.Core.BRepTools + return True except ImportError: pass try: import OCC.BRepTools + return True except ImportError: pass @@ -41,5 +44,5 @@ has_occ = _has_occ() if has_occ: from . import occ_utils as utils - + from .main import * diff --git a/src/ifcopenshell-python/ifcopenshell/geom/app.py b/src/ifcopenshell-python/ifcopenshell/geom/app.py index 308025b4ce..9490ca3923 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/app.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/app.py @@ -12,9 +12,10 @@ import multiprocessing import OCC.AIS from collections import defaultdict, OrderedDict + try: # python 3.3+ from collections.abc import Iterable -except ModuleNotFoundError: # python 2 +except ModuleNotFoundError: # python 2 from collections import Iterable try: @@ -23,7 +24,7 @@ except NameError: # Python 3 QString = str -os.environ['QT_API'] = 'pyqt5' +os.environ["QT_API"] = "pyqt5" try: from pyqode.qt import QtCore except BaseException: @@ -59,11 +60,13 @@ from .. import version as ifcopenshell_version if ifcopenshell_version < "0.6": # not yet ported from .. import get_supertype - + + class geometry_creation_signals(QtCore.QObject): - completed = QtCore.pyqtSignal('PyQt_PyObject') - progress = QtCore.pyqtSignal('PyQt_PyObject') - + completed = QtCore.pyqtSignal("PyQt_PyObject") + progress = QtCore.pyqtSignal("PyQt_PyObject") + + class geometry_creation_thread(QtCore.QThread): def __init__(self, signals, settings, f): QtCore.QThread.__init__(self) @@ -82,25 +85,27 @@ class geometry_creation_thread(QtCore.QThread): if not it.initialize(): self.signals.completed.emit([]) return - + def _(): old_progress = -1 while True: shape = it.get() - + if shape: yield shape - + if not it.next(): break self.signals.completed.emit((it, self.f, list(_()))) + class configuration(object): def __init__(self): try: import ConfigParser + Cfg = ConfigParser.RawConfigParser except BaseException: import configparser @@ -122,7 +127,11 @@ class configuration(object): if not os.path.exists(conf_file): config = Cfg() config.add_section("snippets") - config.set("snippets", "print all wall ids", self.config_encode(""" + config.set( + "snippets", + "print all wall ids", + self.config_encode( + """ ########################################################################### # A simple script that iterates over all walls in the current model # # and prints their Globally unique IDs (GUIDS) to the console window # @@ -130,9 +139,15 @@ class configuration(object): for wall in model.by_type("IfcWall"): print ("wall with global id: "+str(wall.GlobalId)) -""".lstrip())) +""".lstrip() + ), + ) - config.set("snippets", "print properties of current selection", self.config_encode(""" + config.set( + "snippets", + "print properties of current selection", + self.config_encode( + """ ########################################################################### # A simple script that iterates over all IfcPropertySets of the currently # # selected object and prints them to the console # @@ -147,8 +162,10 @@ if selection: for prop in relDefinesByProperties.RelatingPropertyDefinition.HasProperties: print ("{:<20} :{}".format(prop.Name,prop.NominalValue.wrappedValue)) print ("\\n") -""".lstrip())) - with open(conf_file, 'w') as configfile: +""".lstrip() + ), + ) + with open(conf_file, "w") as configfile: config.write(configfile) self.config = Cfg() @@ -191,7 +208,7 @@ class application(QtWidgets.QApplication): action = menu.exec_(self.mapToGlobal(event.pos())) index = self.selectionModel().currentIndex() inst = index.data(QtCore.Qt.UserRole) - if hasattr(inst, 'toPyObject'): + if hasattr(inst, "toPyObject"): inst = inst if action in visibility: self.instanceVisibilityChanged.emit(inst, visibility.index(action)) @@ -200,7 +217,7 @@ class application(QtWidgets.QApplication): def clicked_(self, index): inst = index.data(QtCore.Qt.UserRole) - if hasattr(inst, 'toPyObject'): + if hasattr(inst, "toPyObject"): inst = inst if inst: self.instanceSelected.emit(inst) @@ -209,14 +226,15 @@ class application(QtWidgets.QApplication): itm = self.product_to_item.get(product) if itm is None: return - self.selectionModel().setCurrentIndex(itm, - QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.Rows) + self.selectionModel().setCurrentIndex( + itm, QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.Rows + ) class decomposition_treeview(abstract_treeview): """Treeview with typical IFC decomposition relationships""" - ATTRIBUTES = ['Entity', 'GlobalId', 'Name'] + ATTRIBUTES = ["Entity", "GlobalId", "Name"] def parent(self, instance): if instance.is_a("IfcOpeningElement"): @@ -247,10 +265,10 @@ class application(QtWidgets.QApplication): if (parent is None or parent in items) and product not in items: sl = [] for attr in ATTRS: - if attr == 'Entity': + if attr == "Entity": sl.append(product.is_a()) else: - sl.append(getattr(product, attr) or '') + sl.append(getattr(product, attr) or "") itm = items[product] = QtWidgets.QTreeWidgetItem(items.get(parent, self), sl) itm.setData(0, QtCore.Qt.UserRole, product) self.children[parent].append(product) @@ -262,13 +280,14 @@ class application(QtWidgets.QApplication): """Treeview with typical IFC decomposition relationships""" - ATTRIBUTES = ['Name'] + ATTRIBUTES = ["Name"] def load_file(self, f, **kwargs): products = list(f.by_type("IfcProduct")) types = set(map(lambda i: i.is_a(), products)) items = {} for t in types: + def add(t): s = get_supertype(t) if s: @@ -284,7 +303,7 @@ class application(QtWidgets.QApplication): for p in products: t = QString(p.is_a()) - itm = items[p] = QtWidgets.QTreeWidgetItem(items.get(t, self), [p.Name or '']) + itm = items[p] = QtWidgets.QTreeWidgetItem(items.get(t, self), [p.Name or ""]) itm.setData(0, QtCore.Qt.UserRole, t) self.children[t].append(p) @@ -293,7 +312,6 @@ class application(QtWidgets.QApplication): self.expandAll() class property_table(QtWidgets.QWidget): - def __init__(self): QtWidgets.QWidget.__init__(self) self.layout = QtWidgets.QVBoxLayout(self) @@ -338,7 +356,7 @@ class application(QtWidgets.QApplication): value_str = value_str.wrappedValue if isinstance(value_str, unicode): - value_str = value_str.encode('utf-8') + value_str = value_str.encode("utf-8") else: value_str = str(value_str) @@ -392,6 +410,7 @@ class application(QtWidgets.QApplication): propsets.append(process_pset(propset)) except Exception as e: import traceback + print("failed to load properties: {}".format(e)) traceback.print_exc() @@ -408,7 +427,7 @@ class application(QtWidgets.QApplication): def ais_to_key(ais_handle): def yield_shapes(): ais = ais_handle.GetObject() - if hasattr(ais, 'Shape'): + if hasattr(ais, "Shape"): yield ais.Shape() return shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle) @@ -444,7 +463,7 @@ class application(QtWidgets.QApplication): def finished(self, file_shapes): it, f, shapes = file_shapes v = self._display - + t = {0: time.time()} def update(dt=None): @@ -453,29 +472,29 @@ class application(QtWidgets.QApplication): v.FitAll() v.Repaint() t[0] = t1 - + for shape in shapes: ais = display_shape(shape, viewer_handle=v) product = f[shape.data.id] - + ais.GetObject().SetSelectionPriority(self.counter) self.ais_to_product[self.counter] = product self.product_to_ais[product] = ais self.counter += 1 - + QtWidgets.QApplication.processEvents() - - if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}: + + if product.is_a() in {"IfcSpace", "IfcOpeningElement"}: v.Context.Erase(ais, True) - - update(1.) - + + update(1.0) + update() - + self.thread = None - + def load_file(self, f, setting=None): - + if self.thread is not None: return @@ -483,10 +502,10 @@ class application(QtWidgets.QApplication): setting = settings() setting.set(setting.INCLUDE_CURVES, True) setting.set(setting.USE_PYTHON_OPENCASCADE, True) - + self.signals = geometry_creation_signals() thread = self.thread = geometry_creation_thread(self.signals, setting, f) - self.window.window_closed.connect(lambda *args: thread.terminate()) + self.window.window_closed.connect(lambda *args: thread.terminate()) self.signals.completed.connect(self.finished) self.thread.start() @@ -509,23 +528,31 @@ class application(QtWidgets.QApplication): def toggle_visibility(self, product_or_products, flag): v = self._display.Context if flag: + def visibility(ais, last): v.Erase(ais, last) + else: + def visibility(ais, last): v.Display(ais, last) + self.toggle(product_or_products, visibility) def toggle_wireframe(self, product_or_products, flag): v = self._display.Context if flag: + def wireframe(ais, last): if v.IsDisplayed(ais): v.SetDisplayMode(ais, 0, last) + else: + def wireframe(ais, last): if v.IsDisplayed(ais): v.SetDisplayMode(ais, 1, last) + self.toggle(product_or_products, wireframe) def HandleSelection(self, X, Y): @@ -588,12 +615,12 @@ class application(QtWidgets.QApplication): self.window.resize(800, 600) splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal) splitter.addWidget(self.tabs) - self.tabs.addTab(self.tree, 'Decomposition') - self.tabs.addTab(self.tree2, 'Types') + self.tabs.addTab(self.tree, "Decomposition") + self.tabs.addTab(self.tree2, "Types") self.tabs.addTab(self.propview, "Properties") splitter2 = QtWidgets.QSplitter(QtCore.Qt.Vertical) splitter2.addWidget(self.canvas) - self.editor = code_edit(self.canvas, configuration().options('snippets')) + self.editor = code_edit(self.canvas, configuration().options("snippets")) splitter2.addWidget(self.editor) splitter.addWidget(splitter2) splitter.setSizes([200, 600]) @@ -603,9 +630,9 @@ class application(QtWidgets.QApplication): self.components = [self.tree, self.tree2, self.canvas, self.propview, self.editor] self.files = {} - self.window.add_menu_item('File', '&Open', self.browse, shortcut='CTRL+O') - self.window.add_menu_item('File', '&Close', self.clear, shortcut='CTRL+W') - self.window.add_menu_item('File', '&Exit', self.window.close, shortcut='ALT+F4') + self.window.add_menu_item("File", "&Open", self.browse, shortcut="CTRL+O") + self.window.add_menu_item("File", "&Close", self.clear, shortcut="CTRL+W") + self.window.add_menu_item("File", "&Exit", self.window.close, shortcut="ALT+F4") self.tree.instanceSelected.connect(self.makeSelectionHandler(self.tree)) self.tree2.instanceSelected.connect(self.makeSelectionHandler(self.tree2)) @@ -629,8 +656,9 @@ class application(QtWidgets.QApplication): sys.exit(self.exec_()) def browse(self): - filename = QtWidgets.QFileDialog.getOpenFileName(self.window, 'Open file', ".", - "Industry Foundation Classes (*.ifc)")[0] + filename = QtWidgets.QFileDialog.getOpenFileName( + self.window, "Open file", ".", "Industry Foundation Classes (*.ifc)" + )[0] self.load(filename) def clear(self): diff --git a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py index b64787f32b..a95248d1f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py @@ -70,7 +70,7 @@ class code_edit(QtWidgets.QWidget): sys.stderr = sys.__stderr__ def select(self, product): - self.c = self.Console({'model': self.model, 'viewer': self.viewer, 'selection': product}) + self.c = self.Console({"model": self.model, "viewer": self.viewer, "selection": product}) def __init__(self, viewer, snippets=None): self.model = None @@ -92,8 +92,7 @@ class code_edit(QtWidgets.QWidget): editor.backend.start(server.__file__) editor.panels.append(panels.FoldingPanel()) editor.panels.append(panels.LineNumberPanel()) - editor.panels.append(panels.SearchAndReplacePanel(), - panels.SearchAndReplacePanel.Position.BOTTOM) + editor.panels.append(panels.SearchAndReplacePanel(), panels.SearchAndReplacePanel.Position.BOTTOM) editor.panels.append(panels.EncodingPanel(), api.Panel.Position.TOP) editor.add_separator() editor.panels.append(pypanels.QuickDocPanel(), api.Panel.Position.BOTTOM) @@ -116,7 +115,7 @@ class code_edit(QtWidgets.QWidget): editor.modes.append(pymodes.PyIndenterMode()) editor.show() else: - editor.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier;') + editor.setStyleSheet("font-size: 10pt; font-family: Consolas, Courier;") self.editor = editor self.snippets = snippets @@ -131,7 +130,7 @@ class code_edit(QtWidgets.QWidget): self.layout.addWidget(self.editor) self.output = QtWidgets.QTextEdit() self.output.setReadOnly(True) - self.output.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier; background-color: #444;') + self.output.setStyleSheet("font-size: 10pt; font-family: Consolas, Courier; background-color: #444;") self.layout.addWidget(self.output) def replace_snippet(self, number=None): @@ -145,5 +144,5 @@ class code_edit(QtWidgets.QWidget): output = [] sys.stdout = StdoutRedirector(self.output) self.model = f - self.c = self.Console({'model': self.model, 'selection': None, 'viewer': self.viewer}) + self.c = self.Console({"model": self.model, "selection": None, "viewer": self.viewer}) sys.stdout = sys.__stdout__ diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 0514f04322..b4f2fbf41e 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -45,11 +45,12 @@ if has_occ: from OCC import TopoDS def wrap_shape_creation(settings, shape): - if getattr(settings, 'use_python_opencascade', False): + if getattr(settings, "use_python_opencascade", False): return utils.create_shape_from_serialization(shape) else: return shape + # Subclass the settings module to provide an additional # setting to enable pythonOCC when available class settings(ifcopenshell_wrapper.settings): @@ -73,60 +74,57 @@ _iterator = ifcopenshell_wrapper.iterator_double_precision # Make sure people are able to use python's platform agnostic paths class iterator(_iterator): - def __init__(self, settings, file_or_filename, num_threads = 1, include = None, exclude = None): + def __init__(self, settings, file_or_filename, num_threads=1, include=None, exclude=None): self.settings = settings if isinstance(file_or_filename, file): file_or_filename = file_or_filename.wrapped_data else: file_or_filename = os.path.abspath(file_or_filename) - + if include is not None and exclude is not None: raise ValueError("include and exclude cannot be specified simultaneously") - + if include is not None or exclude is not None: # Couldn't get the typemaps properly applied using %extend so we # replicate the SWIG-generated __init__ call on the output of a # free function. # @todo verify this works with SWIG 4 - + include_or_exclude = include if exclude is None else exclude include_or_exclude_type = set(x.__class__.__name__ for x in include_or_exclude) print(include_or_exclude_type) - + if include_or_exclude_type == {"entity_instance"}: if not all(inst.is_a("IfcProduct") for inst in include_or_exclude): raise ValueError("include and exclude need to be an aggregate of IfcProduct") - - initializer = ifcopenshell_wrapper.\ - construct_iterator_double_precision_with_include_exclude_globalid - - decode_unicode = lambda x: x.encode('ascii') if x.__class__.__name__ == "unicode" else x - include_or_exclude = list(map(decode_unicode, map(operator.attrgetter('GlobalId'), include_or_exclude))) + + initializer = ifcopenshell_wrapper.construct_iterator_double_precision_with_include_exclude_globalid + + decode_unicode = lambda x: x.encode("ascii") if x.__class__.__name__ == "unicode" else x + include_or_exclude = list(map(decode_unicode, map(operator.attrgetter("GlobalId"), include_or_exclude))) else: - initializer = ifcopenshell_wrapper.\ - construct_iterator_double_precision_with_include_exclude - + initializer = ifcopenshell_wrapper.construct_iterator_double_precision_with_include_exclude + self.this = initializer( - self.settings, - file_or_filename, - include_or_exclude, - include is not None, - num_threads) + self.settings, file_or_filename, include_or_exclude, include is not None, num_threads + ) else: _iterator.__init__(self, settings, file_or_filename, num_threads) if has_occ: + def get(self): return wrap_shape_creation(self.settings, _iterator.get(self)) - + def __iter__(self): if self.initialize(): while True: yield self.get() - if not self.next(): break + if not self.next(): + break + class tree(ifcopenshell_wrapper.tree): - def __init__(self, file=None, settings=None): args = [self] if file is not None: @@ -166,7 +164,7 @@ class tree(ifcopenshell_wrapper.tree): if "extend" in kwargs or "completely_within" in kwargs: args.append(kwargs.get("completely_within", False)) if "extend" in kwargs: - args.append(kwargs.get("extend", -1.e-5)) + args.append(kwargs.get("extend", -1.0e-5)) return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select_box(*args)] @@ -193,14 +191,11 @@ def create_shape(settings, inst, repr=None): """ return wrap_shape_creation( settings, - ifcopenshell_wrapper.create_shape( - settings, - inst.wrapped_data, - repr.wrapped_data if repr is not None else None - )) + ifcopenshell_wrapper.create_shape(settings, inst.wrapped_data, repr.wrapped_data if repr is not None else None), + ) -def iterate(settings, file_or_filename, num_threads = 1, include = None, exclude = None): +def iterate(settings, file_or_filename, num_threads=1, include=None, exclude=None): it = iterator(settings, file_or_filename, num_threads, include, exclude) if it.initialize(): while True: @@ -214,13 +209,17 @@ def make_shape_function(fn): return None if e is None else entity_instance(e) if has_occ: + def _(schema, string_or_shape, *args): if isinstance(string_or_shape, TopoDS.TopoDS_Shape): string_or_shape = utils.serialize_shape(string_or_shape) return entity_instance_or_none(fn(schema, string_or_shape, *args)) + else: + def _(schema, string, *args): return entity_instance_or_none(fn(schema, string, *args)) + return _ diff --git a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py index ec77ea7340..3f6ac7c6b7 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py @@ -26,35 +26,38 @@ import operator import warnings from collections import namedtuple + try: # python 3.3+ from collections.abc import Iterable -except ModuleNotFoundError: # python 2 +except ModuleNotFoundError: # python 2 from collections import Iterable try: from OCC.Core import V3d, TopoDS, gp, AIS, Quantity, BRepTools, Graphic3d + USE_OCCT_HANDLE = False except ImportError: from OCC import V3d, TopoDS, gp, AIS, Quantity, BRepTools, Graphic3d + USE_OCCT_HANDLE = True -shape_tuple = namedtuple('shape_tuple', ('data', 'geometry', 'styles')) +shape_tuple = namedtuple("shape_tuple", ("data", "geometry", "styles")) handle, main_loop, add_menu, add_function_to_menu = None, None, None, None DEFAULT_STYLES = { - "DEFAULT": (.7, .7, .7), - "IfcWall": (.8, .8, .8), - "IfcSite": (.75, .8, .65), - "IfcSlab": (.4, .4, .4), - "IfcWallStandardCase": (.9, .9, .9), - "IfcWall": (.9, .9, .9), - "IfcWindow": (.75, .8, .75, .3), - "IfcDoor": (.55, .3, .15), - "IfcBeam": (.75, .7, .7), - "IfcRailing": (.65, .6, .6), - "IfcMember": (.65, .6, .6), - "IfcPlate": (.8, .8, .8) + "DEFAULT": (0.7, 0.7, 0.7), + "IfcWall": (0.8, 0.8, 0.8), + "IfcSite": (0.75, 0.8, 0.65), + "IfcSlab": (0.4, 0.4, 0.4), + "IfcWallStandardCase": (0.9, 0.9, 0.9), + "IfcWall": (0.9, 0.9, 0.9), + "IfcWindow": (0.75, 0.8, 0.75, 0.3), + "IfcDoor": (0.55, 0.3, 0.15), + "IfcBeam": (0.75, 0.7, 0.7), + "IfcRailing": (0.65, 0.6, 0.6), + "IfcMember": (0.65, 0.6, 0.6), + "IfcPlate": (0.8, 0.8, 0.8), } @@ -82,7 +85,7 @@ def initialize_display(): for l in lights: viewer.DelLight(l) - if hasattr(V3d, 'V3d_TypeOfOrientation_Yup_AxoRight'): + if hasattr(V3d, "V3d_TypeOfOrientation_Yup_AxoRight"): dirs = [[V3d.V3d_TypeOfOrientation_Yup_AxoRight], [V3d.V3d_TypeOfOrientation_Zup_AxoRight]] else: dirs = [(3, 2, 1), (-1, -2, -3)] @@ -117,7 +120,7 @@ def display_shape(shape, clr=None, viewer_handle=None): if representation and not clr: if len(set(representation.styles)) == 1: clr = representation.styles[0] - if min(clr) < 0. or max(clr) > 1.: + if min(clr) < 0.0 or max(clr) > 1.0: clr = DEFAULT_STYLES.get(representation.data.type, DEFAULT_STYLES["DEFAULT"]) if clr: @@ -125,8 +128,9 @@ def display_shape(shape, clr=None, viewer_handle=None): ais.SetMaterial(material) if isinstance(clr, str): - qclr = getattr(Quantity, "Quantity_NOC_%s" % clr.upper(), - getattr(Quantity, "Quantity_NOC_%s1" % clr.upper(), None)) + qclr = getattr( + Quantity, "Quantity_NOC_%s" % clr.upper(), getattr(Quantity, "Quantity_NOC_%s1" % clr.upper(), None) + ) if qclr is None: raise Exception("No color named '%s'" % clr.upper()) elif isinstance(clr, Iterable): @@ -140,8 +144,8 @@ def display_shape(shape, clr=None, viewer_handle=None): raise Exception("Object of type %r cannot be used as a color." % type(clr)) ais.SetColor(qclr) - if isinstance(clr, tuple) and len(clr) == 4 and clr[3] < 1.: - ais.SetTransparency(1. - clr[3]) + if isinstance(clr, tuple) and len(clr) == 4 and clr[3] < 1.0: + ais.SetTransparency(1.0 - clr[3]) elif representation and hasattr(AIS, "AIS_MultipleConnectedShape"): default_style_applied = None @@ -155,13 +159,14 @@ def display_shape(shape, clr=None, viewer_handle=None): else: for shp, stl in zip(subshapes, representation.styles): subshape = AIS.AIS_Shape(shp) - if min(stl) < 0. or max(stl) > 1.: - default_style_applied = stl = DEFAULT_STYLES.get(representation.data.type, - DEFAULT_STYLES["DEFAULT"]) + if min(stl) < 0.0 or max(stl) > 1.0: + default_style_applied = stl = DEFAULT_STYLES.get( + representation.data.type, DEFAULT_STYLES["DEFAULT"] + ) subshape.SetColor(Quantity.Quantity_Color(stl[0], stl[1], stl[2], Quantity.Quantity_TOC_RGB)) subshape.SetMaterial(material) - if len(stl) == 4 and stl[3] < 1.: - subshape.SetTransparency(1. - stl[3]) + if len(stl) == 4 and stl[3] < 1.0: + subshape.SetTransparency(1.0 - stl[3]) ais.Connect(subshape.GetHandle()) # For some reason it is necessary to set transparency here again @@ -169,14 +174,14 @@ def display_shape(shape, clr=None, viewer_handle=None): applied_styles = representation.styles if default_style_applied: if len(default_style_applied) == 3: - default_style_applied += (1.,) + default_style_applied += (1.0,) applied_styles += (default_style_applied,) if len(applied_styles): # The only way for this not to be true if is the entire shape is NULL min_transp = min(map(operator.itemgetter(3), applied_styles)) - if min_transp < 1.: - ais.SetTransparency(1.) + if min_transp < 1.0: + ais.SetTransparency(1.0) else: ais = AIS.AIS_Shape(shape) @@ -199,10 +204,10 @@ def set_shape_transparency(ais, t): def get_bounding_box_center(bbox): - bbmin = [0.] * 3 - bbmax = [0.] * 3 + bbmin = [0.0] * 3 + bbmax = [0.0] * 3 bbmin[0], bbmin[1], bbmin[2], bbmax[0], bbmax[1], bbmax[2] = bbox.Get() - return gp.gp_Pnt(*map(lambda xy: (xy[0] + xy[1]) / 2., zip(bbmin, bbmax))) + return gp.gp_Pnt(*map(lambda xy: (xy[0] + xy[1]) / 2.0, zip(bbmin, bbmax))) def serialize_shape(shape): @@ -226,7 +231,7 @@ def create_shape_from_serialization(brep_object): except BaseException: pass - styles = tuple(styles[i:i + 4] for i in range(0, len(styles), 4)) + styles = tuple(styles[i : i + 4] for i in range(0, len(styles), 4)) if not brep_data: return shape_tuple(brep_object, None, styles) diff --git a/src/ifcopenshell-python/ifcopenshell/guid.py b/src/ifcopenshell-python/ifcopenshell/guid.py index b14ce43f4e..aeebb6835f 100644 --- a/src/ifcopenshell-python/ifcopenshell/guid.py +++ b/src/ifcopenshell-python/ifcopenshell/guid.py @@ -26,16 +26,16 @@ import string from functools import reduce -chars = string.digits + string.ascii_uppercase + string.ascii_lowercase + '_$' +chars = string.digits + string.ascii_uppercase + string.ascii_lowercase + "_$" def compress(g): - bs = [int(g[i:i + 2], 16) for i in range(0, len(g), 2)] + bs = [int(g[i : i + 2], 16) for i in range(0, len(g), 2)] def b64(v, l=4): - return ''.join([chars[(v // (64 ** i)) % 64] for i in range(l)][::-1]) + return "".join([chars[(v // (64 ** i)) % 64] for i in range(l)][::-1]) - return ''.join([b64(bs[0], 2)] + [b64((bs[i] << 16) + (bs[i + 1] << 8) + bs[i + 2]) for i in range(1, 16, 3)]) + return "".join([b64(bs[0], 2)] + [b64((bs[i] << 16) + (bs[i + 1] << 8) + bs[i + 2]) for i in range(1, 16, 3)]) def expand(g): @@ -44,13 +44,13 @@ def expand(g): bs = [b64(g[0:2])] for i in range(5): - d = b64(g[2 + 4 * i:6 + 4 * i]) + d = b64(g[2 + 4 * i : 6 + 4 * i]) bs += [(d >> (8 * (2 - j))) % 256 for j in range(3)] - return ''.join(['%02x' % b for b in bs]) + return "".join(["%02x" % b for b in bs]) def split(g): - return '{%s-%s-%s-%s-%s}' % (g[:8], g[8:12], g[12:16], g[16:20], g[20:]) + return "{%s-%s-%s-%s-%s}" % (g[:8], g[8:12], g[12:16], g[16:20], g[20:]) def new(): diff --git a/src/ifcopenshell-python/ifcopenshell/template.py b/src/ifcopenshell-python/ifcopenshell/template.py index ec253ad414..20d23aca33 100644 --- a/src/ifcopenshell-python/ifcopenshell/template.py +++ b/src/ifcopenshell-python/ifcopenshell/template.py @@ -62,24 +62,33 @@ END-ISO-10303-21; """ DEFAULTS = { - "application": lambda d: 'IfcOpenShell-%s' % main.version, + "application": lambda d: "IfcOpenShell-%s" % main.version, "application_version": lambda d: main.version, "project_globalid": lambda d: compress(uuid.uuid4().hex), "schema_identifier": lambda d: main.schema_identifier, "timestamp": lambda d: int(time.time()), - "timestring": lambda d: time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(d.get('timestamp') or time.time())) + "timestring": lambda d: time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(d.get("timestamp") or time.time())), } -def create(filename=None, timestring=None, organization=None, creator=None, - schema_identifier=None, application_version=None, timestamp=None, - application=None, project_globalid=None, project_name=None): +def create( + filename=None, + timestring=None, + organization=None, + creator=None, + schema_identifier=None, + application_version=None, + timestamp=None, + application=None, + project_globalid=None, + project_name=None, +): d = dict(locals()) def _(): for var, value in d.items(): if value is None: - yield var, DEFAULTS.get(var, lambda *args: '')(d) + yield var, DEFAULTS.get(var, lambda *args: "")(d) d.update(dict(_())) diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index c28271967d..87d5991646 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -1,18 +1,19 @@ def get_psets(element): psets = {} try: - if element.is_a('IfcTypeObject'): + if element.is_a("IfcTypeObject"): if element.HasPropertySets: for definition in element.HasPropertySets: psets[definition.Name] = get_property_definition(definition) else: for relationship in element.IsDefinedBy: - if relationship.is_a('IfcRelDefinesByProperties'): + if relationship.is_a("IfcRelDefinesByProperties"): definition = relationship.RelatingPropertyDefinition psets[definition.Name] = get_property_definition(definition) except Exception as e: import traceback - print('failed to load properties: {}'.format(e)) + + print("failed to load properties: {}".format(e)) traceback.print_exc() return psets @@ -20,9 +21,9 @@ def get_psets(element): def get_property_definition(definition): if definition is not None: props = {} - if definition.is_a('IfcElementQuantity'): + if definition.is_a("IfcElementQuantity"): props.update(get_quantities(definition.Quantities)) - elif definition.is_a('IfcPropertySet'): + elif definition.is_a("IfcPropertySet"): props.update(get_properties(definition.HasProperties)) else: # Entity introduced in IFC4 @@ -35,7 +36,7 @@ def get_property_definition(definition): def get_quantities(quantities): results = {} for quantity in quantities: - if quantity.is_a('IfcPhysicalSimpleQuantity'): + if quantity.is_a("IfcPhysicalSimpleQuantity"): results[quantity.Name] = quantity[3] return results @@ -43,22 +44,22 @@ def get_quantities(quantities): def get_properties(properties): results = {} for prop in properties: - if prop.is_a('IfcPropertySingleValue'): + if prop.is_a("IfcPropertySingleValue"): results[prop.Name] = prop.NominalValue.wrappedValue - elif prop.is_a('IfcComplexProperty'): + elif prop.is_a("IfcComplexProperty"): data = prop.get_info() - data['properties'] = get_properties(prop.HasProperties) - del(data['HasProperties']) + data["properties"] = get_properties(prop.HasProperties) + del data["HasProperties"] results[prop.Name] = data return results def get_type(element): - if hasattr(element, 'IsTypedBy') and element.IsTypedBy: + if hasattr(element, "IsTypedBy") and element.IsTypedBy: return element.IsTypedBy[0].RelatingType - elif hasattr(element, 'IsDefinedBy') and element.IsDefinedBy: # IFC2X3 + elif hasattr(element, "IsDefinedBy") and element.IsDefinedBy: # IFC2X3 for relationship in element.IsDefinedBy: - if relationship.is_a('IfcRelDefinesByType'): + if relationship.is_a("IfcRelDefinesByType"): return relationship.RelatingType diff --git a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py index 5455f7e3ec..7e1dcd8b96 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py +++ b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py @@ -1,16 +1,18 @@ import math + def dms2dd(degrees, minutes, seconds, ms=0): - dd = float(degrees) + float(minutes)/60.0 + float(seconds)/(3600.0) + float(ms/3600000000.0) + dd = float(degrees) + float(minutes) / 60.0 + float(seconds) / (3600.0) + float(ms / 3600000000.0) return dd + def dd2dms(dd, use_ms=False): dd = float(dd) sign = 1 if dd >= 0 else -1 dd = abs(dd) if use_ms: - seconds, ms = divmod(dd*60*60*1000000, 1000000) - minutes, seconds = divmod(dd*60*60, 60) + seconds, ms = divmod(dd * 60 * 60 * 1000000, 1000000) + minutes, seconds = divmod(dd * 60 * 60, 60) degrees, minutes = divmod(minutes, 60) if dd < 0: degrees = -degrees @@ -18,9 +20,10 @@ def dd2dms(dd, use_ms=False): return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign, int(ms) * sign) return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign) + def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_axis_ordinate, scale=None): if scale is None: - scale = 1. + scale = 1.0 rotation = math.atan2(x_axis_ordinate, x_axis_abscissa) a = scale * math.cos(rotation) b = scale * math.sin(rotation) @@ -29,6 +32,7 @@ def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_ height = z + orthogonal_height return (eastings, northings, height) + # Used for converting the X and Y vectors of the X Axis in IFC geolocation def xy2angle(x, y): return math.degrees(math.atan2(y, x)) diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index 0291e2cb35..29a5b75077 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -3,65 +3,67 @@ import ifcopenshell.util.element import lark cobie_type_assets = [ - 'IfcDoorStyle', - 'IfcBuildingElementProxyType', - 'IfcChimneyType', - 'IfcCoveringType', - 'IfcDoorType', - 'IfcFootingType', - 'IfcPileType', - 'IfcRoofType', - 'IfcShadingDeviceType', - 'IfcWindowType', - 'IfcDistributionControlElementType', - 'IfcDistributionChamberElementType', - 'IfcEnergyConversionDeviceType', - 'IfcFlowControllerType', - 'IfcFlowMovingDeviceType', - 'IfcFlowStorageDeviceType', - 'IfcFlowTerminalType', - 'IfcFlowTreatmentDeviceType', - 'IfcElementAssemblyType', - 'IfcBuildingElementPartType', - 'IfcDiscreteAccessoryType', - 'IfcMechanicalFastenerType', - 'IfcReinforcingElementType', - 'IfcVibrationIsolatorType', - 'IfcFurnishingElementType', - 'IfcGeographicElementType', - 'IfcTransportElementType', - 'IfcSpatialZoneType', - 'IfcWindowStyle', + "IfcDoorStyle", + "IfcBuildingElementProxyType", + "IfcChimneyType", + "IfcCoveringType", + "IfcDoorType", + "IfcFootingType", + "IfcPileType", + "IfcRoofType", + "IfcShadingDeviceType", + "IfcWindowType", + "IfcDistributionControlElementType", + "IfcDistributionChamberElementType", + "IfcEnergyConversionDeviceType", + "IfcFlowControllerType", + "IfcFlowMovingDeviceType", + "IfcFlowStorageDeviceType", + "IfcFlowTerminalType", + "IfcFlowTreatmentDeviceType", + "IfcElementAssemblyType", + "IfcBuildingElementPartType", + "IfcDiscreteAccessoryType", + "IfcMechanicalFastenerType", + "IfcReinforcingElementType", + "IfcVibrationIsolatorType", + "IfcFurnishingElementType", + "IfcGeographicElementType", + "IfcTransportElementType", + "IfcSpatialZoneType", + "IfcWindowStyle", ] cobie_component_assets = [ - 'IfcBuildingElementProxy', - 'IfcChimney', - 'IfcCovering', - 'IfcDoor', - 'IfcShadingDevice', - 'IfcWindow', - 'IfcDistributionControlElement', - 'IfcDistributionChamberElement', - 'IfcEnergyConversionDevice', - 'IfcFlowController', - 'IfcFlowMovingDevice', - 'IfcFlowStorageDevice', - 'IfcFlowTerminal', - 'IfcFlowTreatmentDevice', - 'IfcDiscreteAccessory', - 'IfcTendon', - 'IfcTendonAnchor', - 'IfcVibrationIsolator', - 'IfcFurnishingElement', - 'IfcGeographicElement', - 'IfcTransportElement', + "IfcBuildingElementProxy", + "IfcChimney", + "IfcCovering", + "IfcDoor", + "IfcShadingDevice", + "IfcWindow", + "IfcDistributionControlElement", + "IfcDistributionChamberElement", + "IfcEnergyConversionDevice", + "IfcFlowController", + "IfcFlowMovingDevice", + "IfcFlowStorageDevice", + "IfcFlowTerminal", + "IfcFlowTreatmentDevice", + "IfcDiscreteAccessory", + "IfcTendon", + "IfcTendonAnchor", + "IfcVibrationIsolator", + "IfcFurnishingElement", + "IfcGeographicElement", + "IfcTransportElement", ] -class Selector(): + +class Selector: def parse(self, ifc_file, query): self.file = ifc_file - l = lark.Lark('''start: query (lfunction query)* + l = lark.Lark( + """start: query (lfunction query)* query: selector | group group: "(" query (lfunction query)* ")" selector: (inverse_relationship)? guid_selector | (inverse_relationship)? class_selector @@ -111,7 +113,8 @@ class Selector(): NEWLINE: (CR? LF)+ %ignore WS // Disregard spaces in text - ''') + """ + ) start = l.parse(query) return self.get_group(start) @@ -119,24 +122,24 @@ class Selector(): def get_group(self, group): lfunction = None for child in group.children: - if child.data == 'query': + if child.data == "query": new_results = self.get_query(child) if not lfunction: results = new_results - elif lfunction == 'or': + elif lfunction == "or": results.extend(new_results) - elif lfunction == 'and': + elif lfunction == "and": results = list(set(results).intersection(new_results)) results = list(set(results)) - elif child.data == 'lfunction': + elif child.data == "lfunction": lfunction = child.children[0].data return results def get_query(self, query): for child in query.children: - if child.data == 'selector': + if child.data == "selector": return self.get_selector(child) - elif child.data == 'group': + elif child.data == "group": return self.get_group(child) def get_selector(self, selector): @@ -147,9 +150,9 @@ class Selector(): inverse_relationship = selector.children[0] class_or_guid_selector = selector.children[1] - if class_or_guid_selector.data == 'class_selector': + if class_or_guid_selector.data == "class_selector": results = self.get_class_selector(class_or_guid_selector) - elif class_or_guid_selector.data == 'guid_selector': + elif class_or_guid_selector.data == "guid_selector": results = self.get_guid_selector(class_or_guid_selector) if not inverse_relationship: @@ -159,26 +162,25 @@ class Selector(): def parse_inverse_relationship(self, elements, inverse_relationship): results = [] for element in elements: - if inverse_relationship == 'types': - if hasattr(element, 'Types') and element.Types: + if inverse_relationship == "types": + if hasattr(element, "Types") and element.Types: results.extend(element.Types[0].RelatedObjects) - elif hasattr(element, 'ObjectTypeOf') and element.ObjectTypeOf: + elif hasattr(element, "ObjectTypeOf") and element.ObjectTypeOf: results.extend(element.ObjectTypeOf[0].RelatedObjects) - elif inverse_relationship == 'contains_elements' \ - and hasattr(element, 'ContainsElements'): + elif inverse_relationship == "contains_elements" and hasattr(element, "ContainsElements"): for relationship in element.ContainsElements: results.extend(relationship.RelatedElements) return results def get_class_selector(self, class_selector): - if class_selector.children[0] == 'COBie': + if class_selector.children[0] == "COBie": elements = [] for ifc_class in cobie_component_assets: try: elements += self.file.by_type(ifc_class) except: pass - elif class_selector.children[0] == 'COBieType': + elif class_selector.children[0] == "COBieType": elements = [] for ifc_class in cobie_type_assets: try: @@ -187,8 +189,7 @@ class Selector(): pass else: elements = self.file.by_type(class_selector.children[0]) - if len(class_selector.children) > 1 \ - and class_selector.children[1].data == 'filter': + if len(class_selector.children) > 1 and class_selector.children[1].data == "filter": return self.filter_elements(elements, class_selector.children[1]) return elements @@ -196,7 +197,7 @@ class Selector(): results = [] key = filter_rule.children[0].children[0] if not isinstance(key, str): - key = key.children[0] + '.' + key.children[1] + key = key.children[0] + "." + key.children[1] comparison = value = None if len(filter_rule.children) > 1: comparison = filter_rule.children[1].children[0].data @@ -205,42 +206,40 @@ class Selector(): element_value = self.get_element_value(element, key) if not element_value: continue - if not comparison \ - or self.filter_element(element, element_value, comparison, value): + if not comparison or self.filter_element(element, element_value, comparison, value): results.append(element) return results def get_element_value(self, element, key): - if '.' in key \ - and key.split('.')[0] == 'type': + if "." in key and key.split(".")[0] == "type": try: element = ifcopenshell.util.element.get_type(element) if not element: return None except: return - key = '.'.join(key.split('.')[1:]) + key = ".".join(key.split(".")[1:]) info = element.get_info() if key in info: return info[key] - elif '.' in key: - pset_name, prop = key.split('.') + elif "." in key: + pset_name, prop = key.split(".") psets = ifcopenshell.util.element.get_psets(element) if pset_name in psets and prop in psets[pset_name]: return psets[pset_name][prop] def filter_element(self, element, element_value, comparison, value): - if comparison == 'equal': + if comparison == "equal": return str(element_value) == value - elif comparison == 'contains': + elif comparison == "contains": return value in str(element_value) - elif comparison == 'morethan': + elif comparison == "morethan": return element_value > float(value) - elif comparison == 'lessthan': + elif comparison == "lessthan": return element_value < float(value) - elif comparison == 'morethanequalto': + elif comparison == "morethanequalto": return element_value >= float(value) - elif comparison == 'lessthanequalto': + elif comparison == "lessthanequalto": return element_value <= float(value) return False diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 20a29ca4ac..2937f8213c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -1,55 +1,99 @@ from math import pi -prefixes = {'EXA': 1e18, 'PETA': 1e15, 'TERA': 1e12, 'GIGA': 1e9, 'MEGA': - 1e6, 'KILO': 1e3, 'HECTO': 1e2, 'DECA': 1e1, 'DECI': 1e-1, 'CENTI': - 1e-2, 'MILLI': 1e-3, 'MICRO': 1e-6, 'NANO': 1e-9, 'PICO': 1e-12, - 'FEMTO': 1e-15, 'ATTO': 1e-18} +prefixes = { + "EXA": 1e18, + "PETA": 1e15, + "TERA": 1e12, + "GIGA": 1e9, + "MEGA": 1e6, + "KILO": 1e3, + "HECTO": 1e2, + "DECA": 1e1, + "DECI": 1e-1, + "CENTI": 1e-2, + "MILLI": 1e-3, + "MICRO": 1e-6, + "NANO": 1e-9, + "PICO": 1e-12, + "FEMTO": 1e-15, + "ATTO": 1e-18, +} -unit_names = ['AMPERE', 'BECQUEREL', 'CANDELA', 'COULOMB', - 'CUBIC_METRE', 'DEGREE CELSIUS', 'FARAD', 'GRAM', 'GRAY', 'HENRY', - 'HERTZ', 'JOULE', 'KELVIN', 'LUMEN', 'LUX', 'MOLE', 'NEWTON', 'OHM', - 'PASCAL', 'RADIAN', 'SECOND', 'SIEMENS', 'SIEVERT', 'SQUARE METRE', - 'METRE', 'STERADIAN', 'TESLA', 'VOLT', 'WATT', 'WEBER'] +unit_names = [ + "AMPERE", + "BECQUEREL", + "CANDELA", + "COULOMB", + "CUBIC_METRE", + "DEGREE CELSIUS", + "FARAD", + "GRAM", + "GRAY", + "HENRY", + "HERTZ", + "JOULE", + "KELVIN", + "LUMEN", + "LUX", + "MOLE", + "NEWTON", + "OHM", + "PASCAL", + "RADIAN", + "SECOND", + "SIEMENS", + "SIEVERT", + "SQUARE METRE", + "METRE", + "STERADIAN", + "TESLA", + "VOLT", + "WATT", + "WEBER", +] si_conversions = { - 'inch': 0.0254, - 'foot': 0.3048, - 'yard': 0.914, - 'mile': 1609, - 'square inch': 0.0006452, - 'square foot': 0.09290304, - 'square yard': 0.83612736, - 'acre': 4046.86, - 'square mile': 2588881, - 'cubic inch': 0.00001639, - 'cubic foot': 0.02831684671168849, - 'cubic yard': 0.7636, - 'litre': 0.001, - 'fluid ounce UK': 0.0000284130625, - 'fluid ounce US': 0.00002957353, - 'pint UK': 0.000568, - 'pint US': 0.000473, - 'gallon UK': 0.004546, - 'gallon US': 0.003785, - 'degree': pi/180, - 'ounce': 0.02835, - 'pound': 0.454, - 'ton UK': 1016.0469088, - 'ton US': 907.18474, - 'lbf': 4.4482216153, - 'kip': 4448.2216153, - 'psi': 6894.7572932, - 'ksi': 6894757.2932, - 'minute': 60, - 'hour': 3600, - 'day': 86400, - 'btu': 1055.056} + "inch": 0.0254, + "foot": 0.3048, + "yard": 0.914, + "mile": 1609, + "square inch": 0.0006452, + "square foot": 0.09290304, + "square yard": 0.83612736, + "acre": 4046.86, + "square mile": 2588881, + "cubic inch": 0.00001639, + "cubic foot": 0.02831684671168849, + "cubic yard": 0.7636, + "litre": 0.001, + "fluid ounce UK": 0.0000284130625, + "fluid ounce US": 0.00002957353, + "pint UK": 0.000568, + "pint US": 0.000473, + "gallon UK": 0.004546, + "gallon US": 0.003785, + "degree": pi / 180, + "ounce": 0.02835, + "pound": 0.454, + "ton UK": 1016.0469088, + "ton US": 907.18474, + "lbf": 4.4482216153, + "kip": 4448.2216153, + "psi": 6894.7572932, + "ksi": 6894757.2932, + "minute": 60, + "hour": 3600, + "day": 86400, + "btu": 1055.056, +} + def get_prefix(text): for prefix in prefixes.keys(): if prefix in text.upper(): return prefix + def get_prefix_multiplier(text): if not text: return 1 @@ -58,11 +102,13 @@ def get_prefix_multiplier(text): return prefixes[prefix] return 1 + def get_unit_name(text): for name in unit_names: - if name in text.upper().replace('METER', 'METRE'): + if name in text.upper().replace("METER", "METRE"): return name + def convert(value, from_prefix, from_unit, to_prefix, to_unit): """Converts between length, area, and volume units @@ -81,18 +127,18 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit): value *= si_conversions[from_unit] elif from_prefix: value *= get_prefix_multiplier(from_prefix) - if 'SQUARE' in from_unit: + if "SQUARE" in from_unit: value *= get_prefix_multiplier(from_prefix) - elif 'CUBIC' in from_unit: + elif "CUBIC" in from_unit: value *= get_prefix_multiplier(from_prefix) value *= get_prefix_multiplier(from_prefix) if to_unit in si_conversions: return value * (1 / si_conversions[to_unit]) elif to_prefix: - value *= (1 / get_prefix_multiplier(to_prefix)) - if 'SQUARE' in from_unit: - value *= (1 / get_prefix_multiplier(to_prefix)) - elif 'CUBIC' in from_unit: - value *= (1 / get_prefix_multiplier(to_prefix)) - value *= (1 / get_prefix_multiplier(to_prefix)) + value *= 1 / get_prefix_multiplier(to_prefix) + if "SQUARE" in from_unit: + value *= 1 / get_prefix_multiplier(to_prefix) + elif "CUBIC" in from_unit: + value *= 1 / get_prefix_multiplier(to_prefix) + value *= 1 / get_prefix_multiplier(to_prefix) return value diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py index d7234bed00..d044366b4e 100644 --- a/src/ifcopenshell-python/ifcopenshell/validate.py +++ b/src/ifcopenshell-python/ifcopenshell/validate.py @@ -8,31 +8,34 @@ from collections import namedtuple import ifcopenshell -named_type = ifcopenshell.ifcopenshell_wrapper.named_type +named_type = ifcopenshell.ifcopenshell_wrapper.named_type aggregation_type = ifcopenshell.ifcopenshell_wrapper.aggregation_type -simple_type = ifcopenshell.ifcopenshell_wrapper.simple_type +simple_type = ifcopenshell.ifcopenshell_wrapper.simple_type type_declaration = ifcopenshell.ifcopenshell_wrapper.type_declaration enumeration_type = ifcopenshell.ifcopenshell_wrapper.enumeration_type -entity_type = ifcopenshell.ifcopenshell_wrapper.entity -select_type = ifcopenshell.ifcopenshell_wrapper.select_type -attribute = ifcopenshell.ifcopenshell_wrapper.attribute +entity_type = ifcopenshell.ifcopenshell_wrapper.entity +select_type = ifcopenshell.ifcopenshell_wrapper.select_type +attribute = ifcopenshell.ifcopenshell_wrapper.attribute -class ValidationError(Exception): pass -log_entry_type = namedtuple('log_entry_type', ("level", "message", "instance")) +class ValidationError(Exception): + pass + + +log_entry_type = namedtuple("log_entry_type", ("level", "message", "instance")) + class json_logger: - def __init__(self): self.statements = [] self.instance = None - + def set_instance(self, instance): self.instance = instance - + def log(self, level, message, instance): self.statements.append(log_entry_type(level, message, instance)._asdict()) - + def __getattr__(self, level): return functools.partial(self.log, level, instance=self.instance) @@ -44,10 +47,11 @@ simple_type_python_mapping = { "real": float, "number": float, "boolean": bool, - "logical": bool, # still not implemented in IfcOpenShell - "binary": str # maps to a str of "0" and "1" + "logical": bool, # still not implemented in IfcOpenShell + "binary": str, # maps to a str of "0" and "1" } + def assert_valid_inverse(attr, val): b1, b2 = attr.bound1(), attr.bound2() invalid = len(val) < b1 or (b2 != -1 and len(val) > b2) @@ -55,24 +59,25 @@ def assert_valid_inverse(attr, val): raise ValidationError("%r not valid for %s" % (val, attr)) return True + def assert_valid(attr, val): if isinstance(attr, attribute): attr_type = attr.type_of_attribute() else: attr_type = attr - + type_wrappers = (named_type,) if not isinstance(val, ifcopenshell.entity_instance): - # If val is not an entity instance we need to + # If val is not an entity instance we need to # flatten the type declaration to something that # maps to the python types - type_wrappers += (type_declaration,) - + type_wrappers += (type_declaration,) + while isinstance(attr_type, type_wrappers): attr_type = attr_type.declared_type() - + if isinstance(attr_type, simple_type): - invalid = type(val) != simple_type_python_mapping[attr_type.declared_type()] + invalid = type(val) != simple_type_python_mapping[attr_type.declared_type()] elif isinstance(attr_type, (entity_type, type_declaration)): invalid = not isinstance(val, ifcopenshell.entity_instance) or not val.is_a(attr_type.name()) elif isinstance(attr_type, select_type): @@ -85,77 +90,79 @@ def assert_valid(attr, val): invalid = len(val) < b1 or (b2 != -1 and len(val) > b2) or not all(assert_valid(ty, v) for v in val) else: raise NotImplementedError("Not impl %s %s" % (type(attr_type), attr_type)) - + if invalid: raise ValidationError("%r not valid for %s" % (val, attr)) - + return True - + + def try_valid(attr, val): try: return assert_valid(attr, val) except ValidationError as e: return False - + + def validate(f, logger): schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema) for inst in f: - if hasattr(logger, 'set_instance'): + if hasattr(logger, "set_instance"): logger.set_instance(inst) - + entity = schema.declaration_by_name(inst.is_a()) - + if entity.is_abstract(): e = "Entity %s is abstract" % entity.name() - if hasattr(logger, 'set_instance'): + if hasattr(logger, "set_instance"): logger.error(e) else: - logger.error('In %s\n%s', inst, e) + logger.error("In %s\n%s", inst, e) for attr, val, is_derived in zip(entity.all_attributes(), inst, entity.derived()): - + if val is None and not (is_derived or attr.optional()): - logger.error("Attribute %s.%s not optional", entity, attr) + logger.error("Attribute %s.%s not optional", entity, attr) if val is not None: attr_type = attr.type_of_attribute() - try: + try: assert_valid(attr, val) except ValidationError as e: - if hasattr(logger, 'set_instance'): + if hasattr(logger, "set_instance"): logger.error(str(e)) else: - logger.error('In %s\n%s', inst, e) - + logger.error("In %s\n%s", inst, e) + for attr in entity.all_inverse_attributes(): val = getattr(inst, attr.name()) try: assert_valid_inverse(attr, val) except ValidationError as e: - if hasattr(logger, 'set_instance'): + if hasattr(logger, "set_instance"): logger.error(str(e)) else: - logger.error('In %s\n%s', inst, e) + logger.error("In %s\n%s", inst, e) + - if __name__ == "__main__": import sys import logging - - filenames = [x for x in sys.argv[1:] if not x.startswith('--')] - flags = set(x for x in sys.argv[1:] if x.startswith('--')) - + + filenames = [x for x in sys.argv[1:] if not x.startswith("--")] + flags = set(x for x in sys.argv[1:] if x.startswith("--")) + for fn in filenames: - if '--json' in flags: + if "--json" in flags: logger = json_logger() else: - logger = logging.getLogger('validate') + logger = logging.getLogger("validate") logger.setLevel(logging.DEBUG) - + f = ifcopenshell.open(fn) - + print("Validating", fn, file=sys.stderr) validate(f, logger) - - if '--json' in flags: + + if "--json" in flags: print("\n".join(json.dumps(x, default=str) for x in logger.statements))