def equalityHash(elt, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED): if isinstance(elt, ModelObject): try: if equalMode == S_EQUAL: return elt._hashSEqual else: return elt._hashXpathEqual except AttributeError: dts = elt.modelXbrl if not hasattr(elt,"xValid"): xmlValidate(dts, elt) hashableValue = elt.sValue if equalMode == S_EQUAL else elt.xValue if isinstance(hashableValue,float) and math.isnan(hashableValue): hashableValue = (hashableValue,elt) # ensure this NaN only compares to itself and no other NaN _hash = hash((elt.elementQname, hashableValue, tuple(sorted(attributeDict(dts, elt, (), equalMode, excludeIDs, distinguishNaNs=True).items(), key=lambda item: item[0])), # must sort so attrs always hashed in same order tuple(equalityHash(child,equalMode,excludeIDs) for child in childElements(elt)) )) if equalMode == S_EQUAL: elt._hashSEqual = _hash else: elt._hashXpathEqual = _hash return _hash elif isinstance(elt, (tuple,list,set)): return hash( tuple(equalityHash(i) for i in elt) ) else: return hash(None)
def sEqual(dts1, elt1, elt2, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, dts2=None, ns2ns1Tbl=None): if dts2 is None: dts2 = dts1 if elt1.localName != elt2.localName: return False if ns2ns1Tbl and elt2.namespaceURI in ns2ns1Tbl: if elt1.namespaceURI != ns2ns1Tbl[elt2.namespaceURI]: return False elif elt1.namespaceURI != elt2.namespaceURI: return False if not hasattr(elt1,"xValid"): xmlValidate(dts1, elt1) if not hasattr(elt2,"xValid"): xmlValidate(dts2, elt2) children1 = childElements(elt1) children2 = childElements(elt2) if len(children1) != len(children2): return False if (not xEqual(elt1, elt2, # must use stringValue for nested contents of mixed content # ... this is now in xValue for mixed content # VALIDATE_BY_STRING_VALUE if len(children1) and elt1.xValid == VALID else equalMode ) or attributeDict(dts1, elt1, (), equalMode, excludeIDs) != attributeDict(dts2, elt2, (), equalMode, excludeIDs, ns2ns1Tbl)): return False excludeChildIDs = excludeIDs if excludeIDs != TOP_IDs_EXCLUDED else NO_IDs_EXCLUDED for i in range( len(children1) ): if not sEqual(dts1, children1[i], children2[i], equalMode, excludeChildIDs, dts2, ns2ns1Tbl): return False return True
def attributeDict(modelXbrl, elt, exclusions=set(), equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, ns2ns1Tbl=None, keyByTag=False, distinguishNaNs=False): if not hasattr(elt,"xValid"): xmlValidate(modelXbrl, elt) attrs = {} # TBD: replace with validated attributes for modelAttribute in elt.xAttributes.values(): attrTag = modelAttribute.attrTag ns, sep, localName = attrTag.partition('}') attrNsURI = ns[1:] if sep else None if ns2ns1Tbl and attrNsURI in ns2ns1Tbl: attrNsURI = ns2ns1Tbl[attrNsURI] if (attrTag not in exclusions and (attrNsURI is None or attrNsURI not in exclusions)): if keyByTag: qname = attrTag elif attrNsURI is not None: qname = QName(None, attrNsURI, localName) else: qname = QName(None, None, attrTag) try: if excludeIDs and getattr(modelAttribute, "xValid", 0) == VALID_ID: continue if modelAttribute.xValid != UNKNOWN: value = modelAttribute.sValue if equalMode <= S_EQUAL2 else modelAttribute.xValue else: # unable to validate, no schema definition, use string value of attribute value = modelAttribute.text if distinguishNaNs and isinstance(value,float) and math.isnan(value): value = (value,elt) attrs[qname] = value except KeyError: pass # what should be done if attribute failed to have psvi value return attrs
def xEqual(elt1, elt2, equalMode=S_EQUAL): if not hasattr(elt1,"xValid"): xmlValidate(elt1.modelXbrl, elt1) if not hasattr(elt2,"xValid"): xmlValidate(elt2.modelXbrl, elt2) if equalMode == VALIDATE_BY_STRING_VALUE: return elt1.stringValue == elt2.stringValue elif equalMode == S_EQUAL or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)): return elt1.sValue == elt2.sValue else: return elt1.xValue == elt2.xValue
def xEqual(elt1, elt2, equalMode=S_EQUAL): if not hasattr(elt1, "xValid"): xmlValidate(elt1.modelXbrl, elt1) if not hasattr(elt2, "xValid"): xmlValidate(elt2.modelXbrl, elt2) if equalMode == VALIDATE_BY_STRING_VALUE: return elt1.stringValue == elt2.stringValue elif equalMode == S_EQUAL: # formula WG e-mail 2018-09-06: or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)): return elt1.sValue == elt2.sValue else: # includes dimension S-equal2, use xpath-2 equality. return elt1.xValue == elt2.xValue
def xEqual(elt1, elt2, equalMode=S_EQUAL): if not hasattr(elt1,"xValid"): xmlValidate(elt1.modelXbrl, elt1) if not hasattr(elt2,"xValid"): xmlValidate(elt2.modelXbrl, elt2) if equalMode == VALIDATE_BY_STRING_VALUE: return elt1.stringValue == elt2.stringValue elif equalMode == S_EQUAL: # formula WG e-mail 2018-09-06: or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)): return elt1.sValue == elt2.sValue else: # includes dimension S-equal2, use xpath-2 equality. return elt1.xValue == elt2.xValue
def xEqual(elt1, elt2, equalMode=S_EQUAL): if not hasattr(elt1, "xValid"): xmlValidate(elt1.modelXbrl, elt1) if not hasattr(elt2, "xValid"): xmlValidate(elt2.modelXbrl, elt2) if equalMode == VALIDATE_BY_STRING_VALUE: return elt1.stringValue == elt2.stringValue elif equalMode == S_EQUAL or (equalMode == S_EQUAL2 and not isinstance(elt1.sValue, QName)): return elt1.sValue == elt2.sValue else: return elt1.xValue == elt2.xValue
def typedValue(dts, element, attrQname=None): try: if attrQname: # PSVI attribute value modelAttribute = element.xAttributes[attrQname.clarkNotation] if modelAttribute.xValid >= VALID: return modelAttribute.xValue else: # PSVI element value (of text) if element.xValid >= VALID: return element.xValue except (AttributeError, KeyError): if dts: xmlValidate(dts, element, recurse=False, attrQname=attrQname) return typedValue(None, element, attrQname=attrQname) return None
def attributeDict(modelXbrl, elt, exclusions=set(), equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, ns2ns1Tbl=None, keyByTag=False, distinguishNaNs=False): if not hasattr(elt, "xValid"): xmlValidate(modelXbrl, elt) attrs = {} # TBD: replace with validated attributes for modelAttribute in elt.xAttributes.values(): attrTag = modelAttribute.attrTag ns, sep, localName = attrTag.partition('}') attrNsURI = ns[1:] if sep else None if ns2ns1Tbl and attrNsURI in ns2ns1Tbl: attrNsURI = ns2ns1Tbl[attrNsURI] if (attrTag not in exclusions and (attrNsURI is None or attrNsURI not in exclusions)): if keyByTag: qname = attrTag elif attrNsURI is not None: qname = QName(None, attrNsURI, localName) else: qname = QName(None, None, attrTag) try: if excludeIDs and getattr(modelAttribute, "xValid", 0) == VALID_ID: continue if modelAttribute.xValid != UNKNOWN: value = modelAttribute.sValue if equalMode <= S_EQUAL2 else modelAttribute.xValue else: # unable to validate, no schema definition, use string value of attribute value = modelAttribute.text if distinguishNaNs and isinstance(value, float) and math.isnan(value): value = (value, elt) attrs[qname] = value except KeyError: pass # what should be done if attribute failed to have psvi value return attrs
def sEqual(dts1, elt1, elt2, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, dts2=None, ns2ns1Tbl=None): if dts2 is None: dts2 = dts1 if elt1.localName != elt2.localName: return False if ns2ns1Tbl and elt2.namespaceURI in ns2ns1Tbl: if elt1.namespaceURI != ns2ns1Tbl[elt2.namespaceURI]: return False elif elt1.namespaceURI != elt2.namespaceURI: return False if not hasattr(elt1, "xValid"): xmlValidate(dts1, elt1) if not hasattr(elt2, "xValid"): xmlValidate(dts2, elt2) children1 = childElements(elt1) children2 = childElements(elt2) if len(children1) != len(children2): return False if (not xEqual( elt1, elt2, # must use stringValue for nested contents of mixed content # ... this is now in xValue for mixed content # VALIDATE_BY_STRING_VALUE if len(children1) and elt1.xValid == VALID else equalMode) or attributeDict(dts1, elt1, (), equalMode, excludeIDs) != attributeDict(dts2, elt2, (), equalMode, excludeIDs, ns2ns1Tbl)): return False excludeChildIDs = excludeIDs if excludeIDs != TOP_IDs_EXCLUDED else NO_IDs_EXCLUDED for i in range(len(children1)): if not sEqual(dts1, children1[i], children2[i], equalMode, excludeChildIDs, dts2, ns2ns1Tbl): return False return True
def equalityHash(elt, equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED): if isinstance(elt, ModelObject): try: if equalMode == S_EQUAL: return elt._hashSEqual else: return elt._hashXpathEqual except AttributeError: dts = elt.modelXbrl if not hasattr(elt, "xValid"): xmlValidate(dts, elt) hashableValue = elt.sValue if equalMode == S_EQUAL else elt.xValue if isinstance(hashableValue, float) and math.isnan(hashableValue): hashableValue = ( hashableValue, elt ) # ensure this NaN only compares to itself and no other NaN _hash = hash(( elt.elementQname, hashableValue, tuple( sorted(attributeDict(dts, elt, (), equalMode, excludeIDs, distinguishNaNs=True).items(), key=lambda item: item[0]) ), # must sort so attrs always hashed in same order tuple( equalityHash(child, equalMode, excludeIDs) for child in childElements(elt)))) if equalMode == S_EQUAL: elt._hashSEqual = _hash else: elt._hashXpathEqual = _hash return _hash elif isinstance(elt, (tuple, list, set)): return hash(tuple(equalityHash(i) for i in elt)) else: return hash(None)
def vEqual(elt1, elt2): if not hasattr(elt1,"xValid"): xmlValidate(elt1.modelXbrl, elt1) if not hasattr(elt2,"xValid"): xmlValidate(elt2.modelXbrl, elt2) return elt1.sValue == elt2.sValue
def vEqual(elt1, elt2): if not hasattr(elt1, "xValid"): xmlValidate(elt1.modelXbrl, elt1) if not hasattr(elt2, "xValid"): xmlValidate(elt2.modelXbrl, elt2) return elt1.sValue == elt2.sValue
def stepAxis(self, op, p, sourceSequence): targetSequence = [] for node in sourceSequence: if not isinstance(node,(ModelObject, etree._ElementTree, ModelAttribute)): raise XPathException(self.progHeader, 'err:XPTY0020', _('Axis step {0} context item is not a node: {1}').format(op, node)) targetNodes = [] if isinstance(p,QNameDef): ns = p.namespaceURI; localname = p.localName; axis = p.axis if p.isAttribute: if isinstance(node,ModelObject): attrTag = p.localName if p.unprefixed else p.clarkNotation modelAttribute = None try: modelAttribute = node.xAttributes[attrTag] except (AttributeError, TypeError, IndexError, KeyError): # may be lax or deferred validated try: xmlValidate(node.modelXbrl, node, p) modelAttribute = node.xAttributes[attrTag] except (AttributeError, TypeError, IndexError, KeyError): pass if modelAttribute is None: value = node.get(attrTag) if value is not None: targetNodes.append(ModelAttribute(node,p.clarkNotation,UNKNOWN,value,value,value)) elif modelAttribute.xValid >= VALID: targetNodes.append(modelAttribute) elif op == '/' or op is None: if axis is None or axis == "child": if isinstance(node,(ModelObject, etree._ElementTree)): targetNodes = XmlUtil.children(node, ns, localname) elif axis == "parent": if isinstance(node,ModelAttribute): parentNode = [ node.modelElement ] else: parentNode = [ XmlUtil.parent(node) ] if (isinstance(node,ModelObject) and (not ns or ns == parentNode.namespaceURI or ns == "*") and (localname == parentNode.localName or localname == "*")): targetNodes = [ parentNode ] elif axis == "self": if (isinstance(node,ModelObject) and (not ns or ns == node.namespaceURI or ns == "*") and (localname == node.localName or localname == "*")): targetNodes = [ node ] elif axis.startswith("descendant"): if isinstance(node,(ModelObject, etree._ElementTree)): targetNodes = XmlUtil.descendants(node, ns, localname) if (axis.endswith("-or-self") and isinstance(node,ModelObject) and (not ns or ns == node.namespaceURI or ns == "*") and (localname == node.localName or localname == "*")): targetNodes.append(node) elif axis.startswith("ancestor"): if isinstance(node,ModelObject): targetNodes = [ancestor for ancestor in XmlUtil.ancestors(node) if ((not ns or ns == ancestor.namespaceURI or ns == "*") and (localname == ancestor.localName or localname == "*"))] if (axis.endswith("-or-self") and isinstance(node,ModelObject) and (not ns or ns == node.namespaceURI or ns == "*") and (localname == node.localName or localname == "*")): targetNodes.insert(0, node) elif axis.endswith("-sibling"): if isinstance(node,ModelObject): targetNodes = [sibling for sibling in node.itersiblings(preceding=axis.startswith("preceding")) if ((not ns or ns == sibling.namespaceURI or ns == "*") and (localname == sibling.localName or localname == "*"))] elif axis == "preceding": if isinstance(node,ModelObject): for preceding in node.getroottree().iter(): if preceding == node: break elif ((not ns or ns == preceding.namespaceURI or ns == "*") and (localname == preceding.localName or localname == "*")): targetNodes.append(preceding) elif axis == "following": if isinstance(node,ModelObject): foundNode = False for following in node.getroottree().iter(): if following == node: foundNode = True elif (foundNode and (not ns or ns == following.namespaceURI or ns == "*") and (localname == following.localName or localname == "*")): targetNodes.append(following) elif op == '//': if isinstance(node,(ModelObject, etree. _ElementTree)): targetNodes = XmlUtil.descendants(node, ns, localname) elif op == '..': if isinstance(node,ModelAttribute): targetNodes = [ node.modelElement ] else: targetNodes = [ XmlUtil.parent(node) ] elif isinstance(p, OperationDef) and isinstance(p.name,QNameDef): if isinstance(node,ModelObject): if p.name.localName == "text": # note this is not string value, just child text targetNodes = [node.textValue] # todo: add element, attribute, node, etc... elif p == '*': # wildcard if op == '/' or op is None: if isinstance(node,(ModelObject, etree._ElementTree)): targetNodes = XmlUtil.children(node, '*', '*') elif op == '//': if isinstance(node,(ModelObject, etree._ElementTree)): targetNodes = XmlUtil.descendants(node, '*', '*') targetSequence.extend(targetNodes) return targetSequence
def stepAxis(self, op, p, sourceSequence): targetSequence = [] for node in sourceSequence: if not isinstance( node, (ModelObject, etree._ElementTree, ModelAttribute)): raise XPathException( self.progHeader, 'err:XPTY0020', _('Axis step {0} context item is not a node: {1}').format( op, node)) targetNodes = [] if isinstance(p, QNameDef): ns = p.namespaceURI localname = p.localName axis = p.axis if p.isAttribute: if isinstance(node, ModelObject): attrTag = p.localName if p.unprefixed else p.clarkNotation modelAttribute = None try: modelAttribute = node.xAttributes[attrTag] except (AttributeError, TypeError, IndexError, KeyError): # may be lax or deferred validated try: xmlValidate(node.modelXbrl, node, p) modelAttribute = node.xAttributes[attrTag] except (AttributeError, TypeError, IndexError, KeyError): pass if modelAttribute is None: value = node.get(attrTag) if value is not None: targetNodes.append( ModelAttribute(node, p.clarkNotation, UNKNOWN, value, value, value)) elif modelAttribute.xValid >= VALID: targetNodes.append(modelAttribute) elif op == '/' or op is None: if axis is None or axis == "child": if isinstance(node, (ModelObject, etree._ElementTree)): targetNodes = XmlUtil.children(node, ns, localname) elif axis == "parent": if isinstance(node, ModelAttribute): parentNode = [node.modelElement] else: parentNode = [XmlUtil.parent(node)] if (isinstance(node, ModelObject) and (not ns or ns == parentNode.namespaceURI or ns == "*") and (localname == parentNode.localName or localname == "*")): targetNodes = [parentNode] elif axis == "self": if (isinstance(node, ModelObject) and (not ns or ns == node.namespaceURI or ns == "*") and (localname == node.localName or localname == "*")): targetNodes = [node] elif axis.startswith("descendant"): if isinstance(node, (ModelObject, etree._ElementTree)): targetNodes = XmlUtil.descendants( node, ns, localname) if (axis.endswith("-or-self") and isinstance(node, ModelObject) and (not ns or ns == node.namespaceURI or ns == "*") and (localname == node.localName or localname == "*")): targetNodes.append(node) elif axis.startswith("ancestor"): if isinstance(node, ModelObject): targetNodes = [ ancestor for ancestor in XmlUtil.ancestors(node) if ((not ns or ns == ancestor.namespaceURI or ns == "*") and ( localname == ancestor.localName or localname == "*")) ] if (axis.endswith("-or-self") and isinstance(node, ModelObject) and (not ns or ns == node.namespaceURI or ns == "*") and (localname == node.localName or localname == "*")): targetNodes.insert(0, node) elif axis.endswith("-sibling"): if isinstance(node, ModelObject): targetNodes = [ sibling for sibling in node.itersiblings( preceding=axis.startswith("preceding")) if ((not ns or ns == sibling.namespaceURI or ns == "*") and ( localname == sibling.localName or localname == "*")) ] elif axis == "preceding": if isinstance(node, ModelObject): for preceding in node.getroottree().iter(): if preceding == node: break elif ((not ns or ns == preceding.namespaceURI or ns == "*") and (localname == preceding.localName or localname == "*")): targetNodes.append(preceding) elif axis == "following": if isinstance(node, ModelObject): foundNode = False for following in node.getroottree().iter(): if following == node: foundNode = True elif (foundNode and (not ns or ns == following.namespaceURI or ns == "*") and (localname == following.localName or localname == "*")): targetNodes.append(following) elif op == '//': if isinstance(node, (ModelObject, etree._ElementTree)): targetNodes = XmlUtil.descendants(node, ns, localname) elif op == '..': if isinstance(node, ModelAttribute): targetNodes = [node.modelElement] else: targetNodes = [XmlUtil.parent(node)] elif isinstance(p, OperationDef) and isinstance(p.name, QNameDef): if isinstance(node, ModelObject): if p.name.localName == "text": # note this is not string value, just child text targetNodes = [node.textValue] # todo: add element, attribute, node, etc... elif p == '*': # wildcard if op == '/' or op is None: if isinstance(node, (ModelObject, etree._ElementTree)): targetNodes = XmlUtil.children(node, '*', '*') elif op == '//': if isinstance(node, (ModelObject, etree._ElementTree)): targetNodes = XmlUtil.descendants(node, '*', '*') targetSequence.extend(targetNodes) return targetSequence
def createModelFact(fact, parentModelFact, topTupleFact): aspects = fact.get("aspects", EMPTYDICT) if oimConcept not in aspects: modelXbrl.error("{}:conceptQName".format(errPrefix), _("The concept QName could not be determined"), modelObject=modelXbrl) return conceptQn = qname(aspects[oimConcept], prefixes) concept = modelXbrl.qnameConcepts.get(conceptQn) attrs = {} if concept.isItem: missingAspects = [] if oimEntity not in aspects: missingAspects.append(oimEntity) if oimPeriod not in aspects and (oimPeriodStart not in aspects or oimPeriodEnd not in aspects): missingAspects.append(oimPeriod) if missingAspects: modelXbrl.error("{}:missingAspects".format(errPrefix), _("The concept %(element)s is missing aspects %(missingAspects)s"), modelObject=modelXbrl, element=conceptQn, missingAspects=", ".join(missingAspects)) return entityAsQn = qname(aspects[oimEntity], prefixes) or qname("error",fact[oimEntity]) if oimPeriod in aspects: periodStart = periodEnd = aspects[oimPeriod] if oimPeriodStart in aspects and oimPeriodEnd in aspects: periodStart = aspects[oimPeriodStart] periodEnd = aspects[oimPeriodEnd] cntxKey = ( # hashable context key ("periodType", concept.periodType), ("entity", entityAsQn), ("periodStart", periodStart), ("periodEnd", periodEnd)) + tuple(sorted( (dimName, dimVal["value"] if isinstance(dimVal,dict) else dimVal) for dimName, dimVal in aspects.items() if ":" in dimName and not dimName.startswith(oimPrefix))) if cntxKey in cntxTbl: _cntx = cntxTbl[cntxKey] else: cntxId = 'c-{:02}'.format(len(cntxTbl) + 1) qnameDims = {} for dimName, dimVal in aspects.items(): if ":" in dimName and not dimName.startswith(oimPrefix) and dimVal: dimQname = qname(dimName, prefixes) dimConcept = modelXbrl.qnameConcepts.get(dimQname) if dimConcept is None: modelXbrl.error("{}:taxonomyDefinedAspectQName".format(errPrefix), _("The taxonomy defined aspect concept QName %(qname)s could not be determined"), modelObject=modelXbrl, qname=dimQname) continue if isinstance(dimVal, dict): dimVal = dimVal["value"] else: dimVal = str(dimVal) # may be int or boolean if isinstance(dimVal,str) and ":" in dimVal and dimVal.partition(':')[0] in prefixes: mem = qname(dimVal, prefixes) # explicit dim elif dimConcept.isTypedDimension: # a modelObject xml element is needed for all of the instance functions to manage the typed dim mem = addChild(modelXbrl.modelDocument, dimConcept.typedDomainElement.qname, text=dimVal, appendChild=False) qnameDims[dimQname] = DimValuePrototype(modelXbrl, None, dimQname, mem, "segment") _cntx = modelXbrl.createContext( entityAsQn.namespaceURI, entityAsQn.localName, concept.periodType, None if concept.periodType == "instant" else dateTime(periodStart, type=DATETIME), dateTime(periodEnd, type=DATETIME), None, # no dimensional validity checking (like formula does) qnameDims, [], [], id=cntxId, beforeSibling=topTupleFact) cntxTbl[cntxKey] = _cntx if oimUnit in aspects and concept.isNumeric: unitKey = aspects[oimUnit] if unitKey in unitTbl: _unit = unitTbl[unitKey] else: _unit = None # validate unit unitKeySub = PrefixedQName.sub(UnitPrefixedQNameSubstitutionChar, unitKey) if not UnitPattern.match(unitKeySub): modelXbrl.error("oime:unitStringRepresentation", _("Unit string representation is lexically invalid, %(unit)s"), modelObject=modelXbrl, unit=unitKey) else: _mul, _sep, _div = unitKey.partition('/') if _mul.startswith('('): _mul = _mul[1:-1] _muls = [u for u in _mul.split('*') if u] if _div.startswith('('): _div = _div[1:-1] _divs = [u for u in _div.split('*') if u] if _muls != sorted(_muls) or _divs != sorted(_divs): modelXbrl.error("oime:unitStringRepresentation", _("Unit string representation measures are not in alphabetical order, %(unit)s"), modelObject=modelXbrl, unit=unitKey) try: mulQns = [qname(u, prefixes, prefixException=OIMException("oime:unitPrefix", _("Unit prefix is not declared: %(unit)s"), unit=u)) for u in _muls] divQns = [qname(u, prefixes, prefixException=OIMException("oime:unitPrefix", _("Unit prefix is not declared: %(unit)s"), unit=u)) for u in _divs] unitId = 'u-{:02}'.format(len(unitTbl) + 1) for _measures in mulQns, divQns: for _measure in _measures: addQnameValue(modelXbrl.modelDocument, _measure) _unit = modelXbrl.createUnit(mulQns, divQns, id=unitId, beforeSibling=topTupleFact) except OIMException as ex: modelXbrl.error(ex.code, ex.message, modelObject=modelXbrl, **ex.msgArgs) unitTbl[unitKey] = _unit else: _unit = None attrs["contextRef"] = _cntx.id if fact.get("value") is None: attrs[XbrlConst.qnXsiNil] = "true" text = None else: text = fact["value"] if concept.isNumeric: if _unit is None: return # skip creating fact because unit was invalid attrs["unitRef"] = _unit.id if "accuracy" in attrs or attrs.get(XbrlConst.qnXsiNil, "false") != "true": attrs["decimals"] = fact.get("accuracy", "INF") else: text = None #tuple id = fact.get("id") if id is not None: attrs["id"] = fact["id"] # is value a QName? if concept.baseXbrliType == "QName": addQnameValue(modelXbrl.modelDocument, qname(text.strip(), prefixes)) f = modelXbrl.createFact(conceptQn, attributes=attrs, text=text, parent=parentModelFact, validate=False) if id is not None and id in parentedFacts: # create child facts for i in sorted(parentedFacts[id], key=lambda j: facts[j].get("aspects", EMPTYDICT).get(oimTupleOrder,0)): createModelFact(facts[i], f, f if topTupleFact is None else topTupleFact) # validate after creating tuple contents xmlValidate(modelXbrl, f)