def prefixedNameQname(self, prefixedName): """Returns ModelValue.QName of prefixedName using this element and its ancestors' xmlns. :param prefixedName: A prefixed name string :type prefixedName: str :returns: QName -- the resolved prefixed name, or None if no prefixed name was provided """ if prefixedName: # passing None would return element qname, not prefixedName None Qname return qnameEltPfxName(self, prefixedName) else: return None
def validateValue(modelXbrl, elt, attrTag, baseXsdType, value, isNillable=False, isNil=False, facets=None): if baseXsdType: try: ''' if (len(value) == 0 and attrTag is None and not isNillable and baseXsdType not in ("anyType", "string", "normalizedString", "token", "NMTOKEN", "anyURI", "noContent")): raise ValueError("missing value for not nillable element") ''' xValid = VALID whitespaceReplace = (baseXsdType == "normalizedString") whitespaceCollapse = (not whitespaceReplace and baseXsdType != "string") isList = baseXsdType in {"IDREFS", "ENTITIES", "NMTOKENS"} if isList: baseXsdType = baseXsdType[:-1] # remove plural if facets: if "minLength" not in facets: facets = facets.copy() facets["minLength"] = 1 else: facets = {"minLength": 1} pattern = baseXsdTypePatterns.get(baseXsdType) if facets: if "pattern" in facets: pattern = facets["pattern"] # note multiple patterns are or'ed togetner, which isn't yet implemented! if "whiteSpace" in facets: whitespaceReplace, whitespaceCollapse = { "preserve": (False, False), "replace": (True, False), "collapse": (False, True) }[facets["whiteSpace"]] if whitespaceReplace: value = normalizeWhitespacePattern.sub( ' ', value) # replace tab, line feed, return with space elif whitespaceCollapse: value = collapseWhitespacePattern.sub(' ', value).strip( ' ' ) # collapse multiple spaces, tabs, line feeds and returns to single space if baseXsdType == "noContent": if len(value) > 0 and not entirelyWhitespacePattern.match( value): # only xml schema pattern whitespaces removed raise ValueError("value content not permitted") # note that sValue and xValue are not innerText but only text elements on specific element (or attribute) xValue = sValue = None xValid = VALID_NO_CONTENT # notify others that element may contain subelements (for stringValue needs) elif not value and isNil and isNillable: # rest of types get None if nil/empty value xValue = sValue = None else: if pattern is not None: if ((isList and any( pattern.match(v) is None for v in value.split())) or (not isList and pattern.match(value) is None)): raise ValueError( "pattern facet " + facets["pattern"].pattern if facets and "pattern" in facets else "pattern mismatch") if facets: if "enumeration" in facets and value not in facets[ "enumeration"]: raise ValueError("{0} is not in {1}".format( value, facets["enumeration"].keys())) if "length" in facets and len(value) != facets["length"]: raise ValueError("length {0}, expected {1}".format( len(value), facets["length"])) if "minLength" in facets and len( value) < facets["minLength"]: raise ValueError("length {0}, minLength {1}".format( len(value), facets["minLength"])) if "maxLength" in facets and len( value) > facets["maxLength"]: raise ValueError("length {0}, maxLength {1}".format( len(value), facets["maxLength"])) if baseXsdType in { "string", "normalizedString", "language", "languageOrEmpty", "token", "NMTOKEN", "Name", "NCName", "IDREF", "ENTITY" }: xValue = sValue = value elif baseXsdType == "ID": xValue = sValue = value xValid = VALID_ID elif baseXsdType == "anyURI": if value: # allow empty strings to be valid anyURIs if UrlUtil.relativeUrlPattern.match(value) is None: raise ValueError("IETF RFC 2396 4.3 syntax") # encode PSVI xValue similarly to Xerces and other implementations xValue = anyURI(UrlUtil.anyUriQuoteForPSVI(value)) sValue = value elif baseXsdType in ("decimal", "float", "double", "XBRLI_NONZERODECIMAL"): if baseXsdType in ("decimal", "XBRLI_NONZERODECIMAL"): if decimalPattern.match(value) is None: raise ValueError("lexical pattern mismatch") xValue = Decimal(value) sValue = float( value ) # s-value uses Number (float) representation if sValue == 0 and baseXsdType == "XBRLI_NONZERODECIMAL": raise ValueError("zero is not allowed") else: if floatPattern.match(value) is None: raise ValueError("lexical pattern mismatch") xValue = sValue = float(value) if facets: if "totalDigits" in facets and len( value.replace(".", "")) > facets["totalDigits"]: raise ValueError("totalDigits facet {0}".format( facets["totalDigits"])) if "fractionDigits" in facets and ( '.' in value and len(value[value.index('.') + 1:]) > facets["fractionDigits"]): raise ValueError( "fraction digits facet {0}".format( facets["fractionDigits"])) if "maxInclusive" in facets and xValue > facets[ "maxInclusive"]: raise ValueError(" > maxInclusive {0}".format( facets["maxInclusive"])) if "maxExclusive" in facets and xValue >= facets[ "maxExclusive"]: raise ValueError(" >= maxInclusive {0}".format( facets["maxExclusive"])) if "minInclusive" in facets and xValue < facets[ "minInclusive"]: raise ValueError(" < minInclusive {0}".format( facets["minInclusive"])) if "minExclusive" in facets and xValue <= facets[ "minExclusive"]: raise ValueError(" <= minExclusive {0}".format( facets["minExclusive"])) elif baseXsdType in { "integer", "nonPositiveInteger", "negativeInteger", "nonNegativeInteger", "positiveInteger", "long", "unsignedLong", "int", "unsignedInt", "short", "unsignedShort", "byte", "unsignedByte" }: xValue = sValue = _INT(value) if ((baseXsdType in { "nonNegativeInteger", "unsignedLong", "unsignedInt" } and xValue < 0) or (baseXsdType == "nonPositiveInteger" and xValue > 0) or (baseXsdType == "positiveInteger" and xValue <= 0) or (baseXsdType == "byte" and not -128 <= xValue < 127) or (baseXsdType == "unsignedByte" and not 0 <= xValue < 255) or (baseXsdType == "short" and not -32768 <= xValue < 32767) or (baseXsdType == "unsignedShort" and not 0 <= xValue < 65535) or (baseXsdType == "positiveInteger" and xValue <= 0)): raise ValueError("{0} is not {1}".format( value, baseXsdType)) if facets: if "totalDigits" in facets and len( value.replace(".", "")) > facets["totalDigits"]: raise ValueError("totalDigits facet {0}".format( facets["totalDigits"])) if "fractionDigits" in facets and ( '.' in value and len(value[value.index('.') + 1:]) > facets["fractionDigits"]): raise ValueError( "fraction digits facet {0}".format( facets["fractionDigits"])) if "maxInclusive" in facets and xValue > facets[ "maxInclusive"]: raise ValueError(" > maxInclusive {0}".format( facets["maxInclusive"])) if "maxExclusive" in facets and xValue >= facets[ "maxExclusive"]: raise ValueError(" >= maxInclusive {0}".format( facets["maxExclusive"])) if "minInclusive" in facets and xValue < facets[ "minInclusive"]: raise ValueError(" < minInclusive {0}".format( facets["minInclusive"])) if "minExclusive" in facets and xValue <= facets[ "minExclusive"]: raise ValueError(" <= minExclusive {0}".format( facets["minExclusive"])) elif baseXsdType == "boolean": if value in ("true", "1"): xValue = sValue = True elif value in ("false", "0"): xValue = sValue = False else: raise ValueError elif baseXsdType == "QName": xValue = qnameEltPfxName(elt, value, prefixException=ValueError) #xValue = qname(elt, value, castException=ValueError, prefixException=ValueError) sValue = value ''' not sure here, how are explicitDimensions validated, but bad units not? if xValue.namespaceURI in modelXbrl.namespaceDocs: if (xValue not in modelXbrl.qnameConcepts and xValue not in modelXbrl.qnameTypes and xValue not in modelXbrl.qnameAttributes and xValue not in modelXbrl.qnameAttributeGroups): raise ValueError("qname not defined " + str(xValue)) ''' elif baseXsdType == "enumerationHrefs": xValue = [qnameHref(href) for href in value.split()] sValue = value elif baseXsdType == "enumerationQNames": xValue = [ qnameEltPfxName(elt, qn, prefixException=ValueError) for qn in value.split() ] sValue = value elif baseXsdType in ("XBRLI_DECIMALSUNION", "XBRLI_PRECISIONUNION"): xValue = sValue = value if value == "INF" else _INT(value) elif baseXsdType in ("XBRLI_NONZERODECIMAL"): xValue = sValue = _INT(value) if xValue == 0: raise ValueError("invalid value") elif baseXsdType == "regex-pattern": # for facet compiling try: sValue = value if value in xmlSchemaPatterns: xValue = xmlSchemaPatterns[value] else: if r"\i" in value or r"\c" in value: value = value.replace(r"[\i-[:]]", iNameChar).replace(r"\i", iNameChar) \ .replace(r"[\c-[:]]", cMinusCNameChar).replace(r"\c", cNameChar) xValue = re_compile(value + "$") # must match whole string except Exception as err: raise ValueError(err) elif baseXsdType == "fraction": numeratorStr, denominatorStr = elt.fractionValue if numeratorStr == INVALIDixVALUE or denominatorStr == INVALIDixVALUE: sValue = xValue = INVALIDixVALUE xValid = INVALID else: sValue = value numeratorNum = float(numeratorStr) denominatorNum = float(denominatorStr) if numeratorNum.is_integer( ) and denominatorNum.is_integer(): xValue = Fraction(int(numeratorNum), int(denominatorNum)) else: xValue = Fraction(numeratorNum / denominatorNum) else: if baseXsdType in lexicalPatterns: match = lexicalPatterns[baseXsdType].match(value) if match is None: raise ValueError("lexical pattern mismatch") if baseXsdType == "XBRLI_DATEUNION": xValue = dateTime(value, type=DATEUNION, castException=ValueError) sValue = value elif baseXsdType == "dateTime": xValue = dateTime(value, type=DATETIME, castException=ValueError) sValue = value elif baseXsdType == "date": xValue = dateTime(value, type=DATE, castException=ValueError) sValue = value elif baseXsdType == "gMonthDay": month, day, zSign, zHrMin, zHr, zMin = match.groups( ) if int(day) > { 2: 29, 4: 30, 6: 30, 9: 30, 11: 30, 1: 31, 3: 31, 5: 31, 7: 31, 8: 31, 10: 31, 12: 31 }[int(month)]: raise ValueError( "invalid day {0} for month {1}".format( day, month)) xValue = gMonthDay(month, day) elif baseXsdType == "gYearMonth": year, month, zSign, zHrMin, zHr, zMin = match.groups( ) xValue = gYearMonth(year, month) elif baseXsdType == "gYear": year, zSign, zHrMin, zHr, zMin = match.groups() xValue = gYear(year) elif baseXsdType == "gMonth": month, zSign, zHrMin, zHr, zMin = match.groups() xValue = gMonth(month) elif baseXsdType == "gDay": day, zSign, zHrMin, zHr, zMin = match.groups() xValue = gDay(day) elif baseXsdType == "duration": xValue = isoDuration(value) else: xValue = value else: # no lexical pattern, forget compiling value xValue = value sValue = value except (ValueError, InvalidOperation) as err: if ModelInlineValueObject is not None and isinstance( elt, ModelInlineValueObject): errElt = "{0} fact {1}".format(elt.elementQname, elt.qname) else: errElt = elt.elementQname if attrTag: modelXbrl.error( "xmlSchema:valueError", _("Element %(element)s attribute %(attribute)s type %(typeName)s value error: %(value)s, %(error)s" ), modelObject=elt, element=errElt, attribute=XmlUtil.clarkNotationToPrefixedName( elt, attrTag, isAttribute=True), typeName=baseXsdType, value=strTruncate(value, 30), error=err) else: modelXbrl.error( "xmlSchema:valueError", _("Element %(element)s type %(typeName)s value error: %(value)s, %(error)s" ), modelObject=elt, element=errElt, typeName=baseXsdType, value=strTruncate(value, 30), error=err) xValue = None sValue = value xValid = INVALID else: xValue = sValue = None xValid = UNKNOWN if attrTag: try: # dynamically allocate attributes (otherwise given shared empty set) xAttributes = elt.xAttributes except AttributeError: elt.xAttributes = xAttributes = {} xAttributes[attrTag] = ModelAttribute(elt, attrTag, xValid, xValue, sValue, value) else: elt.xValid = xValid elt.xValue = xValue elt.sValue = sValue
def validateValue(modelXbrl, elt, attrTag, baseXsdType, value, isNillable=False, isNil=False, facets=None): if baseXsdType: try: ''' if (len(value) == 0 and attrTag is None and not isNillable and baseXsdType not in ("anyType", "string", "normalizedString", "token", "NMTOKEN", "anyURI", "noContent")): raise ValueError("missing value for not nillable element") ''' xValid = VALID whitespaceReplace = (baseXsdType == "normalizedString") whitespaceCollapse = (not whitespaceReplace and baseXsdType != "string") isList = baseXsdType in {"IDREFS", "ENTITIES", "NMTOKENS"} if isList: baseXsdType = baseXsdType[:-1] # remove plural pattern = baseXsdTypePatterns.get(baseXsdType) if facets: if "pattern" in facets: pattern = facets["pattern"] # note multiple patterns are or'ed togetner, which isn't yet implemented! if "whiteSpace" in facets: whitespaceReplace, whitespaceCollapse = {"preserve":(False,False), "replace":(True,False), "collapse":(False,True)}[facets["whiteSpace"]] if whitespaceReplace: value = normalizeWhitespacePattern.sub(' ', value) elif whitespaceCollapse: value = collapseWhitespacePattern.sub(' ', value.strip()) if baseXsdType == "noContent": if len(value) > 0 and not value.isspace(): raise ValueError("value content not permitted") # note that sValue and xValue are not innerText but only text elements on specific element (or attribute) xValue = sValue = None xValid = VALID_NO_CONTENT # notify others that element may contain subelements (for stringValue needs) elif not value and isNil and isNillable: # rest of types get None if nil/empty value xValue = sValue = None else: if pattern is not None: if ((isList and any(pattern.match(v) is None for v in value.split())) or (not isList and pattern.match(value) is None)): raise ValueError("pattern facet " + facets["pattern"].pattern if facets and "pattern" in facets else "pattern mismatch") if facets: if "enumeration" in facets and value not in facets["enumeration"]: raise ValueError("{0} is not in {1}".format(value, facets["enumeration"])) if "length" in facets and len(value) != facets["length"]: raise ValueError("length {0}, expected {1}".format(len(value), facets["length"])) if "minLength" in facets and len(value) < facets["minLength"]: raise ValueError("length {0}, minLength {1}".format(len(value), facets["minLength"])) if "maxLength" in facets and len(value) > facets["maxLength"]: raise ValueError("length {0}, maxLength {1}".format(len(value), facets["maxLength"])) if baseXsdType in {"string", "normalizedString", "language", "token", "NMTOKEN","Name","NCName","IDREF","ENTITY"}: xValue = sValue = value elif baseXsdType == "ID": xValue = sValue = value xValid = VALID_ID elif baseXsdType == "anyURI": if value: # allow empty strings to be valid anyURIs if UrlUtil.relativeUrlPattern.match(value) is None: raise ValueError("IETF RFC 2396 4.3 syntax") # encode PSVI xValue similarly to Xerces and other implementations xValue = anyURI(UrlUtil.anyUriQuoteForPSVI(value)) sValue = value elif baseXsdType in ("decimal", "float", "double"): if baseXsdType == "decimal": if decimalPattern.match(value) is None: raise ValueError("lexical pattern mismatch") xValue = Decimal(value) sValue = float(value) # s-value uses Number (float) representation else: if floatPattern.match(value) is None: raise ValueError("lexical pattern mismatch") xValue = sValue = float(value) if facets: if "totalDigits" in facets and len(value.replace(".","")) > facets["totalDigits"]: raise ValueError("totalDigits facet {0}".format(facets["totalDigits"])) if "fractionDigits" in facets and ( '.' in value and len(value[value.index('.') + 1:]) > facets["fractionDigits"]): raise ValueError("fraction digits facet {0}".format(facets["fractionDigits"])) if "maxInclusive" in facets and xValue > facets["maxInclusive"]: raise ValueError(" > maxInclusive {0}".format(facets["maxInclusive"])) if "maxExclusive" in facets and xValue >= facets["maxExclusive"]: raise ValueError(" >= maxInclusive {0}".format(facets["maxExclusive"])) if "minInclusive" in facets and xValue < facets["minInclusive"]: raise ValueError(" < minInclusive {0}".format(facets["minInclusive"])) if "minExclusive" in facets and xValue <= facets["minExclusive"]: raise ValueError(" <= minExclusive {0}".format(facets["minExclusive"])) elif baseXsdType in {"integer", "nonPositiveInteger","negativeInteger","nonNegativeInteger","positiveInteger", "long","unsignedLong", "int","unsignedInt", "short","unsignedShort", "byte","unsignedByte"}: xValue = sValue = _INT(value) if ((baseXsdType in {"nonNegativeInteger","unsignedLong","unsignedInt"} and xValue < 0) or (baseXsdType == "nonPositiveInteger" and xValue > 0) or (baseXsdType == "positiveInteger" and xValue <= 0) or (baseXsdType == "byte" and not -128 <= xValue < 127) or (baseXsdType == "unsignedByte" and not 0 <= xValue < 255) or (baseXsdType == "short" and not -32768 <= xValue < 32767) or (baseXsdType == "unsignedShort" and not 0 <= xValue < 65535) or (baseXsdType == "positiveInteger" and xValue <= 0)): raise ValueError("{0} is not {1}".format(value, baseXsdType)) if facets: if "totalDigits" in facets and len(value.replace(".","")) > facets["totalDigits"]: raise ValueError("totalDigits facet {0}".format(facets["totalDigits"])) if "fractionDigits" in facets and ( '.' in value and len(value[value.index('.') + 1:]) > facets["fractionDigits"]): raise ValueError("fraction digits facet {0}".format(facets["fractionDigits"])) if "maxInclusive" in facets and xValue > facets["maxInclusive"]: raise ValueError(" > maxInclusive {0}".format(facets["maxInclusive"])) if "maxExclusive" in facets and xValue >= facets["maxExclusive"]: raise ValueError(" >= maxInclusive {0}".format(facets["maxExclusive"])) if "minInclusive" in facets and xValue < facets["minInclusive"]: raise ValueError(" < minInclusive {0}".format(facets["minInclusive"])) if "minExclusive" in facets and xValue <= facets["minExclusive"]: raise ValueError(" <= minExclusive {0}".format(facets["minExclusive"])) elif baseXsdType == "boolean": if value in ("true", "1"): xValue = sValue = True elif value in ("false", "0"): xValue = sValue = False else: raise ValueError elif baseXsdType == "QName": xValue = qnameEltPfxName(elt, value, prefixException=ValueError) #xValue = qname(elt, value, castException=ValueError, prefixException=ValueError) sValue = value ''' not sure here, how are explicitDimensions validated, but bad units not? if xValue.namespaceURI in modelXbrl.namespaceDocs: if (xValue not in modelXbrl.qnameConcepts and xValue not in modelXbrl.qnameTypes and xValue not in modelXbrl.qnameAttributes and xValue not in modelXbrl.qnameAttributeGroups): raise ValueError("qname not defined " + str(xValue)) ''' elif baseXsdType in ("XBRLI_DECIMALSUNION", "XBRLI_PRECISIONUNION"): xValue = sValue = value if value == "INF" else _INT(value) elif baseXsdType in ("XBRLI_NONZERODECIMAL"): xValue = sValue = _INT(value) if xValue == 0: raise ValueError("invalid value") elif baseXsdType == "XBRLI_DATEUNION": xValue = dateTime(value, type=DATEUNION, castException=ValueError) sValue = value elif baseXsdType == "dateTime": xValue = dateTime(value, type=DATETIME, castException=ValueError) sValue = value elif baseXsdType == "date": xValue = dateTime(value, type=DATE, castException=ValueError) sValue = value elif baseXsdType == "regex-pattern": # for facet compiling try: sValue = value if value in xmlSchemaPatterns: xValue = xmlSchemaPatterns[value] else: if r"\i" in value or r"\c" in value: value = value.replace(r"\i", iNameChar).replace(r"\c", cNameChar) xValue = re_compile(value + "$") # must match whole string except Exception as err: raise ValueError(err) elif baseXsdType == "fraction": sValue = value xValue = Fraction("/".join(elt.fractionValue)) else: if baseXsdType in lexicalPatterns: match = lexicalPatterns[baseXsdType].match(value) if match is None: raise ValueError("lexical pattern mismatch") if baseXsdType == "gMonthDay": month, day, zSign, zHrMin, zHr, zMin = match.groups() if int(day) > {2:29, 4:30, 6:30, 9:30, 11:30, 1:31, 3:31, 5:31, 7:31, 8:31, 10:31, 12:31}[int(month)]: raise ValueError("invalid day {0} for month {1}".format(day, month)) xValue = gMonthDay(month, day) elif baseXsdType == "gYearMonth": year, month, zSign, zHrMin, zHr, zMin = match.groups() xValue = gYearMonth(year, month) elif baseXsdType == "gYear": year, zSign, zHrMin, zHr, zMin = match.groups() xValue = gYear(year) elif baseXsdType == "gMonth": month, zSign, zHrMin, zHr, zMin = match.groups() xValue = gMonth(month) elif baseXsdType == "gDay": day, zSign, zHrMin, zHr, zMin = match.groups() xValue = gDay(day) else: xValue = value else: # no lexical pattern, forget compiling value xValue = value sValue = value except (ValueError, InvalidOperation) as err: if ModelInlineValueObject is not None and isinstance(elt, ModelInlineValueObject): errElt = "{0} fact {1}".format(elt.elementQname, elt.qname) else: errElt = elt.elementQname if attrTag: modelXbrl.error("xmlSchema:valueError", _("Element %(element)s attribute %(attribute)s type %(typeName)s value error: %(value)s, %(error)s"), modelObject=elt, element=errElt, attribute=XmlUtil.clarkNotationToPrefixedName(elt,attrTag,isAttribute=True), typeName=baseXsdType, value=value if len(value) < 31 else value[:30] + '...', error=err) else: modelXbrl.error("xmlSchema:valueError", _("Element %(element)s type %(typeName)s value error: %(value)s, %(error)s"), modelObject=elt, element=errElt, typeName=baseXsdType, value=value if len(value) < 31 else value[:30] + '...', error=err) xValue = None sValue = value xValid = INVALID else: xValue = sValue = None xValid = UNKNOWN if attrTag: try: # dynamically allocate attributes (otherwise given shared empty set) xAttributes = elt.xAttributes except AttributeError: elt.xAttributes = xAttributes = {} xAttributes[attrTag] = ModelAttribute(elt, attrTag, xValid, xValue, sValue, value) else: elt.xValid = xValid elt.xValue = xValue elt.sValue = sValue
def validateFacts(val, factsToCheck): # may be called in streaming batches or all at end (final) if not streaming modelXbrl = val.modelXbrl modelDocument = modelXbrl.modelDocument # note EBA 2.1 is in ModelDocument.py timelessDatePattern = re.compile(r"\s*([0-9]{4})-([0-9]{2})-([0-9]{2})\s*$") for cntx in modelXbrl.contexts.values(): if getattr(cntx, "_batchChecked", False): continue # prior streaming batch already checked cntx._batchChecked = True val.cntxEntities.add(cntx.entityIdentifier) dateElts = XmlUtil.descendants(cntx, XbrlConst.xbrli, ("startDate","endDate","instant")) if any(not timelessDatePattern.match(e.textValue) for e in dateElts): modelXbrl.error(("EBA.2.10","EIOPA.2.10"), _('Period dates must be whole dates without time or timezone: %(dates)s.'), modelObject=cntx, dates=", ".join(e.text for e in dateElts)) if cntx.isForeverPeriod: modelXbrl.error(("EBA.2.11","EIOPA.N.2.11"), _('Forever context period is not allowed.'), modelObject=cntx) elif cntx.isStartEndPeriod: modelXbrl.error(("EBA.2.13","EIOPA.N.2.11"), _('Start-End (flow) context period is not allowed.'), modelObject=cntx) elif cntx.isInstantPeriod: # cannot pass context object to final() below, for error logging, if streaming mode val.cntxDates[cntx.instantDatetime].add(modelXbrl if getattr(val.modelXbrl, "isStreamingMode", False) else cntx) if cntx.hasSegment: modelXbrl.error(("EBA.2.14","EIOPA.N.2.14"), _("Contexts MUST NOT contain xbrli:segment values: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id) if cntx.nonDimValues("scenario"): modelXbrl.error(("EBA.2.15","EIOPA.S.2.15" if val.isEIOPAfullVersion else "EIOPA.N.2.15"), _("Contexts MUST NOT contain non-dimensional xbrli:scenario values: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id, messageCodes=("EBA.2.15","EIOPA.N.2.15","EIOPA.S.2.15")) val.unusedCntxIDs.add(cntx.id) for unit in modelXbrl.units.values(): if getattr(unit, "_batchChecked", False): continue # prior streaming batch already checked unit._batchChecked = True val.unusedUnitIDs.add(unit.id) factsByQname = defaultdict(set) # top level for this for f in factsToCheck: factsByQname[f.qname].add(f) val.unusedCntxIDs.discard(f.contextID) val.unusedUnitIDs.discard(f.unitID) if f.objectIndex < val.firstFactObjectIndex: val.firstFactObjectIndex = f.objectIndex val.firstFact = f for fIndicators in factsByQname[qnFIndicators]: val.numFilingIndicatorTuples += 1 for fIndicator in fIndicators.modelTupleFacts: _value = (getattr(fIndicator, "xValue", None) or fIndicator.value) # use validated xValue if DTS else value for skipDTS if _value in val.filingIndicators: modelXbrl.error(("EBA.1.6.1", "EIOPA.1.6.1"), _('Multiple filing indicators facts for indicator %(filingIndicator)s.'), modelObject=(fIndicator, val.filingIndicators[_value]), filingIndicator=_value) val.filingIndicators[_value] = fIndicator.get("{http://www.eurofiling.info/xbrl/ext/filing-indicators}filed", "true") in ("true", "1") val.unusedCntxIDs.discard(fIndicator.contextID) cntx = fIndicator.context if cntx is not None and (cntx.hasSegment or cntx.hasScenario): modelXbrl.error("EIOPA.N.1.6.d" if val.isEIOPAfullVersion else "EIOPA.S.1.6.d", _('Filing indicators must not contain segment or scenario elements %(filingIndicator)s.'), modelObject=fIndicator, filingIndicator=_value) if fIndicators.objectIndex > val.firstFactObjectIndex: modelXbrl.warning("EIOPA.1.6.2", _('Filing indicators should precede first fact %(firstFact)s.'), modelObject=(fIndicators, val.firstFact), firstFact=val.firstFact.qname) if val.isEIOPAfullVersion: for fIndicator in factsByQname[qnFilingIndicator]: if fIndicator.getparent().qname == XbrlConst.qnXbrliXbrl: _isPos = fIndicator.get("{http://www.eurofiling.info/xbrl/ext/filing-indicators}filed", "true") in ("true", "1") _value = (getattr(fIndicator, "xValue", None) or fIndicator.value) # use validated xValue if DTS else value for skipDTS modelXbrl.error("EIOPA.1.6.a" if _isPos else "EIOPA.1.6.b", _('Filing indicators must be in a tuple %(filingIndicator)s.'), modelObject=fIndicator, filingIndicator=_value, messageCodes=("EIOPA.1.6.a", "EIOPA.1.6.b")) otherFacts = {} # (contextHash, unitHash, xmlLangHash) : fact nilFacts = [] # removed in current draft: stringFactsWithoutXmlLang = [] nonMonetaryNonPureFacts = [] for qname, facts in factsByQname.items(): for f in facts: if modelXbrl.skipDTS: c = f.qname.localName[0] isNumeric = c in ('m', 'p', 'r', 'i') isMonetary = c == 'm' isInteger = c == 'i' isPercent = c == 'p' isString = c == 's' isEnum = c == 'e' else: concept = f.concept if concept is not None: isNumeric = concept.isNumeric isMonetary = concept.isMonetary isInteger = concept.baseXbrliType in integerItemTypes isPercent = concept.typeQname in (qnPercentItemType, qnPureItemType) isString = concept.baseXbrliType in ("stringItemType", "normalizedStringItemType") isEnum = concept.typeQname == qnEnumerationItemType else: isNumeric = isString = isEnum = False # error situation k = (f.getparent().objectIndex, f.qname, f.context.contextDimAwareHash if f.context is not None else None, f.unit.hash if f.unit is not None else None, hash(f.xmlLang)) if f.qname == qnFIndicators and val.validateEIOPA: pass elif k not in otherFacts: otherFacts[k] = {f} else: matches = [o for o in otherFacts[k] if (f.getparent().objectIndex == o.getparent().objectIndex and f.qname == o.qname and f.context.isEqualTo(o.context) if f.context is not None and o.context is not None else True) and # (f.unit.isEqualTo(o.unit) if f.unit is not None and o.unit is not None else True) and (f.xmlLang == o.xmlLang)] if matches: contexts = [f.contextID] + [o.contextID for o in matches] modelXbrl.error(("EBA.2.16", "EIOPA.S.2.16" if val.isEIOPAfullVersion else "EIOPA.S.2.16.a"), _('Facts are duplicates %(fact)s contexts %(contexts)s.'), modelObject=[f] + matches, fact=f.qname, contexts=', '.join(contexts), messageCodes=("EBA.2.16", "EIOPA.S.2.16", "EIOPA.S.2.16.a")) else: otherFacts[k].add(f) if isNumeric: if f.precision: modelXbrl.error(("EBA.2.17", "EIOPA.2.18.a"), _("Numeric fact %(fact)s of context %(contextID)s has a precision attribute '%(precision)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, precision=f.precision) if f.decimals and not f.isNil: if f.decimals == "INF": if not val.isEIOPAfullVersion: modelXbrl.error("EIOPA.S.2.18.f", _("Monetary fact %(fact)s of context %(contextID)s has a decimal attribute INF: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: try: xValue = f.xValue dec = int(f.decimals) if isMonetary: if dec < -3: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.c"), _("Monetary fact %(fact)s of context %(contextID)s has a decimals attribute < -3: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: # apply dynamic decimals check if -.1 < xValue < .1: dMin = 2 elif -1 < xValue < 1: dMin = 1 elif -10 < xValue < 10: dMin = 0 elif -100 < xValue < 100: dMin = -1 elif -1000 < xValue < 1000: dMin = -2 else: dMin = -3 if dMin > dec: modelXbrl.warning("EIOPA:factDecimalsWarning", _("Monetary fact %(fact)s of context %(contextID)s value %(value)s has an imprecise decimals attribute: %(decimals)s, minimum is %(mindec)s"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue, decimals=f.decimals, mindec=dMin) elif isInteger: if dec != 0: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.d"), _("Integer fact %(fact)s of context %(contextID)s has a decimals attribute \u2260 0: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) elif isPercent: if dec < 4: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.e"), _("Percent fact %(fact)s of context %(contextID)s has a decimals attribute < 4: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: if -.001 < xValue < .001: dMin = 4 elif -.01 < xValue < .01: dMin = 3 elif -.1 < xValue < .1: dMin = 2 elif -1 < xValue < 1: dMin = 1 else: dMin = 0 if dMin > dec: modelXbrl.warning("EIOPA:factDecimalsWarning", _("Numeric fact %(fact)s of context %(contextID)s value %(value)s has an imprecise decimals attribute: %(decimals)s, minimum is %(mindec)s"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue, decimals=f.decimals, mindec=dMin) except (AttributeError, ValueError): pass # should have been reported as a schema error by loader '''' (not intended by EBA 2.18, paste here is from EFM) if not f.isNil and getattr(f,"xValid", 0) == 4: try: insignificance = insignificantDigits(f.xValue, decimals=f.decimals) if insignificance: # if not None, returns (truncatedDigits, insiginficantDigits) modelXbrl.error(("EFM.6.05.37", "GFM.1.02.26"), _("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s has nonzero digits in insignificant portion %(insignificantDigits)s."), modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.xValue, truncatedDigits=insignificance[0], insignificantDigits=insignificance[1]) except (ValueError,TypeError): modelXbrl.error(("EBA.2.18"), _("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s causes Value Error exception."), modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.value) ''' unit = f.unit if unit is not None: if isMonetary: if unit.measures[0]: val.currenciesUsed[unit.measures[0][0]] = unit elif not unit.isSingleMeasure or unit.measures[0][0] != XbrlConst.qnXbrliPure: nonMonetaryNonPureFacts.append(f) if isEnum: _eQn = getattr(f,"xValue", None) or qnameEltPfxName(f, f.value) if _eQn: val.namespacePrefixesUsed[_eQn.namespaceURI].add(_eQn.prefix) val.prefixesUnused.discard(_eQn.prefix) ''' removed in current draft elif isString: if not f.xmlLang: stringFactsWithoutXmlLang.append(f) ''' if f.isNil: nilFacts.append(f) if val.footnotesRelationshipSet.fromModelObject(f): modelXbrl.warning("EIOPA.S.19", _("Fact %(fact)s of context %(contextID)s has footnotes.'"), modelObject=f, fact=f.qname, contextID=f.contextID) if nilFacts: modelXbrl.error(("EBA.2.19", "EIOPA.S.2.19"), _('Nil facts MUST NOT be present in the instance: %(nilFacts)s.'), modelObject=nilFacts, nilFacts=", ".join(str(f.qname) for f in nilFacts)) ''' removed in current draft if stringFactsWithoutXmlLang: modelXbrl.error("EBA.2.20", _("String facts need to report xml:lang: '%(langLessFacts)s'"), modelObject=stringFactsWithoutXmlLang, langLessFacts=", ".join(set(str(f.qname) for f in stringFactsWithoutXmlLang))) ''' if nonMonetaryNonPureFacts: modelXbrl.error(("EBA.3.2","EIOPA.3.2.a"), _("Non monetary (numeric) facts MUST use the pure unit: '%(langLessFacts)s'"), modelObject=nonMonetaryNonPureFacts, langLessFacts=", ".join(set(str(f.qname) for f in nonMonetaryNonPureFacts))) val.utrValidator.validateFacts() # validate facts for UTR at logLevel WARNING unitHashes = {} for unit in modelXbrl.units.values(): h = unit.hash if h in unitHashes and unit.isEqualTo(unitHashes[h]): modelXbrl.warning("EBA.2.21", _("Duplicate units SHOULD NOT be reported, units %(unit1)s and %(unit2)s have same measures.'"), modelObject=(unit, unitHashes[h]), unit1=unit.id, unit2=unitHashes[h].id) if not getattr(modelXbrl, "isStreamingMode", False): modelXbrl.error("EIOPA.2.21", _("Duplicate units MUST NOT be reported, units %(unit1)s and %(unit2)s have same measures.'"), modelObject=(unit, unitHashes[h]), unit1=unit.id, unit2=unitHashes[h].id) else: unitHashes[h] = unit for _measures in unit.measures: for _measure in _measures: val.namespacePrefixesUsed[_measure.namespaceURI].add(_measure.prefix) val.prefixesUnused.discard(_measure.prefix) del unitHashes cntxHashes = {} for cntx in modelXbrl.contexts.values(): h = cntx.contextDimAwareHash if h in cntxHashes and cntx.isEqualTo(cntxHashes[h]): if not getattr(modelXbrl, "isStreamingMode", False): modelXbrl.log("WARNING" if val.isEIOPAfullVersion else "ERROR", "EIOPA.S.2.7.b", _("Duplicate contexts MUST NOT be reported, contexts %(cntx1)s and %(cntx2)s are equivalent.'"), modelObject=(cntx, cntxHashes[h]), cntx1=cntx.id, cntx2=cntxHashes[h].id) else: cntxHashes[h] = cntx for _dim in cntx.qnameDims.values(): _dimQn = _dim.dimensionQname val.namespacePrefixesUsed[_dimQn.namespaceURI].add(_dimQn.prefix) val.prefixesUnused.discard(_dimQn.prefix) if _dim.isExplicit: _memQn = _dim.memberQname else: _memQn = _dim.typedMember.qname if _memQn: val.namespacePrefixesUsed[_memQn.namespaceURI].add(_memQn.prefix) val.prefixesUnused.discard(_memQn.prefix) for elt in modelDocument.xmlRootElement.iter(): if isinstance(elt, ModelObject): # skip comments and processing instructions val.namespacePrefixesUsed[elt.qname.namespaceURI].add(elt.qname.prefix) val.prefixesUnused.discard(elt.qname.prefix) for attrTag in elt.keys(): if attrTag.startswith("{"): _prefix, _NS, _localName = XmlUtil.clarkNotationToPrefixNsLocalname(elt, attrTag, isAttribute=True) if _prefix: val.namespacePrefixesUsed[_NS].add(_prefix) val.prefixesUnused.discard(_prefix)
def validateFacts(val, factsToCheck): # may be called in streaming batches or all at end (final) if not streaming modelXbrl = val.modelXbrl modelDocument = modelXbrl.modelDocument # note EBA 2.1 is in ModelDocument.py timelessDatePattern = re.compile(r"\s*([0-9]{4})-([0-9]{2})-([0-9]{2})\s*$") for cntx in modelXbrl.contexts.values(): if getattr(cntx, "_batchChecked", False): continue # prior streaming batch already checked cntx._batchChecked = True val.cntxEntities.add(cntx.entityIdentifier) dateElts = XmlUtil.descendants(cntx, XbrlConst.xbrli, ("startDate","endDate","instant")) if any(not timelessDatePattern.match(e.textValue) for e in dateElts): modelXbrl.error(("EBA.2.10","EIOPA.2.10"), _('Period dates must be whole dates without time or timezone: %(dates)s.'), modelObject=cntx, dates=", ".join(e.text for e in dateElts)) if cntx.isForeverPeriod: modelXbrl.error(("EBA.2.11","EIOPA.N.2.11"), _('Forever context period is not allowed.'), modelObject=cntx) elif cntx.isStartEndPeriod: modelXbrl.error(("EBA.2.13","EIOPA.N.2.11"), _('Start-End (flow) context period is not allowed.'), modelObject=cntx) elif cntx.isInstantPeriod: # cannot pass context object to final() below, for error logging, if streaming mode val.cntxDates[cntx.instantDatetime].add(modelXbrl if getattr(val.modelXbrl, "isStreamingMode", False) else cntx) if cntx.hasSegment: modelXbrl.error(("EBA.2.14","EIOPA.N.2.14"), _("Contexts MUST NOT contain xbrli:segment values: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id) if cntx.nonDimValues("scenario"): modelXbrl.error(("EBA.2.15","EIOPA.S.2.15" if val.isEIOPAfullVersion else "EIOPA.N.2.15"), _("Contexts MUST NOT contain non-dimensional xbrli:scenario values: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id, messageCodes=("EBA.2.15","EIOPA.N.2.15","EIOPA.S.2.15")) val.unusedCntxIDs.add(cntx.id) if val.isEIOPA_2_0_1 and len(cntx.id) > 128: modelXbrl.warning("EIOPA.S.2.6", _("Contexts IDs SHOULD be short: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id) for unit in modelXbrl.units.values(): if getattr(unit, "_batchChecked", False): continue # prior streaming batch already checked unit._batchChecked = True val.unusedUnitIDs.add(unit.id) factsByQname = defaultdict(set) # top level for this for f in factsToCheck: factsByQname[f.qname].add(f) val.unusedCntxIDs.discard(f.contextID) val.unusedUnitIDs.discard(f.unitID) if f.objectIndex < val.firstFactObjectIndex: val.firstFactObjectIndex = f.objectIndex val.firstFact = f for fIndicators in factsByQname[qnFIndicators]: val.numFilingIndicatorTuples += 1 for fIndicator in fIndicators.modelTupleFacts: _value = (getattr(fIndicator, "xValue", None) or fIndicator.value) # use validated xValue if DTS else value for skipDTS _filed = fIndicator.get("{http://www.eurofiling.info/xbrl/ext/filing-indicators}filed", "true") in ("true", "1") if _value in val.filingIndicators: modelXbrl.error(("EBA.1.6.1", "EIOPA.1.6.1"), _('Multiple filing indicators facts for indicator %(filingIndicator)s.'), modelObject=(fIndicator, val.filingIndicators[_value]), filingIndicator=_value) if _filed and not val.filingIndicators[_value]: val.filingIndicators[_value] = _filed #set to filed if any of the multiple indicators are filed=true else: # not a duplicate filing indicator val.filingIndicators[_value] = _filed val.unusedCntxIDs.discard(fIndicator.contextID) cntx = fIndicator.context if cntx is not None and (cntx.hasSegment or cntx.hasScenario): modelXbrl.error("EIOPA.N.1.6.d" if val.isEIOPAfullVersion else "EIOPA.S.1.6.d", _('Filing indicators must not contain segment or scenario elements %(filingIndicator)s.'), modelObject=fIndicator, filingIndicator=_value) if fIndicators.objectIndex > val.firstFactObjectIndex: modelXbrl.warning("EIOPA.1.6.2", _('Filing indicators should precede first fact %(firstFact)s.'), modelObject=(fIndicators, val.firstFact), firstFact=val.firstFact.qname) if val.isEIOPAfullVersion: for fIndicator in factsByQname[qnFilingIndicator]: if fIndicator.getparent().qname == XbrlConst.qnXbrliXbrl: _isPos = fIndicator.get("{http://www.eurofiling.info/xbrl/ext/filing-indicators}filed", "true") in ("true", "1") _value = (getattr(fIndicator, "xValue", None) or fIndicator.value) # use validated xValue if DTS else value for skipDTS modelXbrl.error("EIOPA.1.6.a" if _isPos else "EIOPA.1.6.b", _('Filing indicators must be in a tuple %(filingIndicator)s.'), modelObject=fIndicator, filingIndicator=_value, messageCodes=("EIOPA.1.6.a", "EIOPA.1.6.b")) otherFacts = {} # (contextHash, unitHash, xmlLangHash) : fact nilFacts = [] stringFactsWithXmlLang = [] nonMonetaryNonPureFacts = [] for qname, facts in factsByQname.items(): for f in facts: if f.qname == qnFilingIndicator: continue # skip erroneous root-level filing indicators if modelXbrl.skipDTS: c = f.qname.localName[0] isNumeric = c in ('m', 'p', 'r', 'i') isMonetary = c == 'm' isInteger = c == 'i' isPercent = c == 'p' isString = c == 's' isEnum = c == 'e' else: concept = f.concept if concept is not None: isNumeric = concept.isNumeric isMonetary = concept.isMonetary isInteger = concept.baseXbrliType in integerItemTypes isPercent = concept.typeQname in (qnPercentItemType, qnPureItemType) isString = concept.baseXbrliType in ("stringItemType", "normalizedStringItemType") isEnum = concept.typeQname == qnEnumerationItemType else: isNumeric = isString = isEnum = False # error situation k = (f.getparent().objectIndex, f.qname, f.context.contextDimAwareHash if f.context is not None else None, f.unit.hash if f.unit is not None else None, hash(f.xmlLang)) if f.qname == qnFIndicators and val.validateEIOPA: pass elif k not in otherFacts: otherFacts[k] = {f} else: matches = [o for o in otherFacts[k] if (f.getparent().objectIndex == o.getparent().objectIndex and f.qname == o.qname and f.context.isEqualTo(o.context) if f.context is not None and o.context is not None else True) and # (f.unit.isEqualTo(o.unit) if f.unit is not None and o.unit is not None else True) and (f.xmlLang == o.xmlLang)] if matches: contexts = [f.contextID] + [o.contextID for o in matches] modelXbrl.error(("EBA.2.16", "EIOPA.S.2.16" if val.isEIOPAfullVersion else "EIOPA.S.2.16.a"), _('Facts are duplicates %(fact)s contexts %(contexts)s.'), modelObject=[f] + matches, fact=f.qname, contexts=', '.join(contexts), messageCodes=("EBA.2.16", "EIOPA.S.2.16", "EIOPA.S.2.16.a")) else: otherFacts[k].add(f) if isNumeric: if f.precision: modelXbrl.error(("EBA.2.17", "EIOPA.2.18.a"), _("Numeric fact %(fact)s of context %(contextID)s has a precision attribute '%(precision)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, precision=f.precision) if f.decimals and not f.isNil: # in XbrlDpmSqlDB for 2_0_1 if f.decimals == "INF": if not val.isEIOPAfullVersion: modelXbrl.error("EIOPA.S.2.18.f", _("Monetary fact %(fact)s of context %(contextID)s has a decimal attribute INF: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: try: xValue = f.xValue dec = int(f.decimals) if isMonetary: if val.isEIOPA_2_0_1: _absXvalue = abs(xValue) if str(f.qname) in s_2_18_c_a_met: dMin = 2 elif _absXvalue >= 100000000: dMin = -4 elif 100000000 > _absXvalue >= 1000000: dMin = -3 elif 1000000 > _absXvalue >= 1000: dMin = -2 else: dMin = -1 if dMin > dec: modelXbrl.error("EIOPA.S.2.18.c", _("Monetary fact %(fact)s of context %(contextID)s has a decimals attribute less than minimum %(minimumDecimals)s: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, minimumDecimals=dMin, decimals=f.decimals) elif dec < -3: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.c"), _("Monetary fact %(fact)s of context %(contextID)s has a decimals attribute < -3: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: # apply dynamic decimals check if -.1 < xValue < .1: dMin = 2 elif -1 < xValue < 1: dMin = 1 elif -10 < xValue < 10: dMin = 0 elif -100 < xValue < 100: dMin = -1 elif -1000 < xValue < 1000: dMin = -2 else: dMin = -3 if dMin > dec: modelXbrl.warning("EIOPA:factDecimalsWarning", _("Monetary fact %(fact)s of context %(contextID)s value %(value)s has an imprecise decimals attribute: %(decimals)s, minimum is %(mindec)s"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue, decimals=f.decimals, mindec=dMin) elif isInteger: if dec != 0: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.d"), _("Integer fact %(fact)s of context %(contextID)s has a decimals attribute \u2260 0: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) elif isPercent: if dec < 4: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.e"), _("Percent fact %(fact)s of context %(contextID)s has a decimals attribute < 4: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) if val.isEIOPA_2_0_1 and xValue > 1: modelXbrl.warning(("EIOPA.3.2.b"), _("Percent fact %(fact)s of context %(contextID)s appears to be over 100% = 1.0: '%(value)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue) else: if -.001 < xValue < .001: dMin = 4 elif -.01 < xValue < .01: dMin = 3 elif -.1 < xValue < .1: dMin = 2 elif -1 < xValue < 1: dMin = 1 else: dMin = 0 if dMin > dec: modelXbrl.warning("EIOPA:factDecimalsWarning", _("Numeric fact %(fact)s of context %(contextID)s value %(value)s has an imprecise decimals attribute: %(decimals)s, minimum is %(mindec)s"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue, decimals=f.decimals, mindec=dMin) except (AttributeError, ValueError): pass # should have been reported as a schema error by loader '''' (not intended by EBA 2.18, paste here is from EFM) if not f.isNil and getattr(f,"xValid", 0) == 4: try: insignificance = insignificantDigits(f.xValue, decimals=f.decimals) if insignificance: # if not None, returns (truncatedDigits, insiginficantDigits) modelXbrl.error(("EFM.6.05.37", "GFM.1.02.26"), _("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s has nonzero digits in insignificant portion %(insignificantDigits)s."), modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.xValue, truncatedDigits=insignificance[0], insignificantDigits=insignificance[1]) except (ValueError,TypeError): modelXbrl.error(("EBA.2.18"), _("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s causes Value Error exception."), modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.value) ''' unit = f.unit if unit is not None: if isMonetary: if unit.measures[0]: _currencyMeasure = unit.measures[0][0] if val.isEIOPA_2_0_1 and f.context is not None: if f.context.dimMemberQname(val.qnDimAF) == val.qnCAx1 and val.qnDimOC in f.context.qnameDims: _ocCurrency = f.context.dimMemberQname(val.qnDimOC).localName if _currencyMeasure.localName != _ocCurrency: modelXbrl.error("EIOPA.3.1", _("There MUST be only one currency but metric %(metric)s reported OC dimension currency %(ocCurrency)s differs from unit currency: %(unitCurrency)s."), modelObject=f, metric=f.qname, ocCurrency=_ocCurrency, unitCurrency=_currencyMeasure.localName) else: val.currenciesUsed[_currencyMeasure] = unit else: val.currenciesUsed[_currencyMeasure] = unit elif not unit.isSingleMeasure or unit.measures[0][0] != XbrlConst.qnXbrliPure: nonMonetaryNonPureFacts.append(f) if isEnum: _eQn = getattr(f,"xValue", None) or qnameEltPfxName(f, f.value) if _eQn: prefixUsed(val, _eQn.namespaceURI, _eQn.prefix) if val.isEIOPA_2_0_1 and f.qname.localName == "ei1930": val.reportingCurrency = _eQn.localName elif isString: if f.xmlLang: # requires disclosureSystem to NOT specify default language stringFactsWithXmlLang.append(f) if f.isNil: nilFacts.append(f) if val.footnotesRelationshipSet.fromModelObject(f): modelXbrl.warning("EIOPA.S.19", _("Fact %(fact)s of context %(contextID)s has footnotes.'"), modelObject=f, fact=f.qname, contextID=f.contextID) if nilFacts: modelXbrl.error(("EBA.2.19", "EIOPA.S.2.19"), _('Nil facts MUST NOT be present in the instance: %(nilFacts)s.'), modelObject=nilFacts, nilFacts=", ".join(str(f.qname) for f in nilFacts)) if stringFactsWithXmlLang: modelXbrl.warning("EIOPA.2.20", # not reported for EBA _("String facts reporting xml:lang (not saved by T4U, not round-tripped): '%(factsWithLang)s'"), modelObject=stringFactsWithXmlLang, factsWithLang=", ".join(set(str(f.qname) for f in stringFactsWithXmlLang))) if nonMonetaryNonPureFacts: modelXbrl.error(("EBA.3.2","EIOPA.3.2.a"), _("Non monetary (numeric) facts MUST use the pure unit: '%(langLessFacts)s'"), modelObject=nonMonetaryNonPureFacts, langLessFacts=", ".join(set(str(f.qname) for f in nonMonetaryNonPureFacts))) val.utrValidator.validateFacts() # validate facts for UTR at logLevel WARNING unitHashes = {} for unit in modelXbrl.units.values(): h = unit.hash if h in unitHashes and unit.isEqualTo(unitHashes[h]): modelXbrl.warning("EBA.2.21", _("Duplicate units SHOULD NOT be reported, units %(unit1)s and %(unit2)s have same measures.'"), modelObject=(unit, unitHashes[h]), unit1=unit.id, unit2=unitHashes[h].id) if not getattr(modelXbrl, "isStreamingMode", False): modelXbrl.error("EIOPA.2.21", _("Duplicate units MUST NOT be reported, units %(unit1)s and %(unit2)s have same measures.'"), modelObject=(unit, unitHashes[h]), unit1=unit.id, unit2=unitHashes[h].id) else: unitHashes[h] = unit for _measures in unit.measures: for _measure in _measures: prefixUsed(val, _measure.namespaceURI, _measure.prefix) del unitHashes cntxHashes = {} for cntx in modelXbrl.contexts.values(): h = cntx.contextDimAwareHash if h in cntxHashes and cntx.isEqualTo(cntxHashes[h]): if not getattr(modelXbrl, "isStreamingMode", False): modelXbrl.log("WARNING" if val.isEIOPAfullVersion else "ERROR", "EIOPA.S.2.7.b", _("Duplicate contexts MUST NOT be reported, contexts %(cntx1)s and %(cntx2)s are equivalent.'"), modelObject=(cntx, cntxHashes[h]), cntx1=cntx.id, cntx2=cntxHashes[h].id) else: cntxHashes[h] = cntx for _dim in cntx.qnameDims.values(): _dimQn = _dim.dimensionQname prefixUsed(val, _dimQn.namespaceURI, _dimQn.prefix) if _dim.isExplicit: _memQn = _dim.memberQname else: _memQn = _dim.typedMember.qname if _memQn: prefixUsed(val, _memQn.namespaceURI, _memQn.prefix) for elt in modelDocument.xmlRootElement.iter(): if isinstance(elt, ModelObject): # skip comments and processing instructions prefixUsed(val, elt.qname.namespaceURI, elt.qname.prefix) for attrTag in elt.keys(): if attrTag.startswith("{"): _prefix, _NS, _localName = XmlUtil.clarkNotationToPrefixNsLocalname(elt, attrTag, isAttribute=True) if _prefix: prefixUsed(val, _NS, _prefix) elif val.isEIOPA_2_0_1: if elt.tag in ("{http://www.w3.org/2001/XMLSchema}documentation", "{http://www.w3.org/2001/XMLSchema}annotation"): modelXbrl.error("EIOPA.2.5", _("xs:documentation element found, all relevant business data MUST only be contained in contexts, units, schemaRef and facts."), modelObject=modelDocument) elif isinstance(elt, etree._Comment): modelXbrl.error("EIOPA.2.5", _("XML comment found, all relevant business data MUST only be contained in contexts, units, schemaRef and facts: %(comment)s"), modelObject=modelDocument, comment=elt.text)
def validateFacts(val, factsToCheck): # may be called in streaming batches or all at end (final) if not streaming modelXbrl = val.modelXbrl modelDocument = modelXbrl.modelDocument isStreamingMode = getattr(modelXbrl, "isStreamingMode", False) # note EBA 2.1 is in ModelDocument.py timelessDatePattern = re.compile(r"\s*([0-9]{4})-([0-9]{2})-([0-9]{2})\s*$") for cntx in modelXbrl.contexts.values(): if cntx is not None: if getattr(cntx, "_batchChecked", False) and isStreamingMode: continue # prior streaming batch already checked cntx._batchChecked = True val.cntxEntities.add(cntx.entityIdentifier) dateElts = XmlUtil.descendants(cntx, XbrlConst.xbrli, ("startDate","endDate","instant")) if any(not timelessDatePattern.match(e.textValue) for e in dateElts): modelXbrl.error(("EBA.2.10","EIOPA.2.10"), _('Period dates must be whole dates without time or timezone: %(dates)s.'), modelObject=cntx, dates=", ".join(e.text for e in dateElts)) if cntx.isForeverPeriod: modelXbrl.error(("EBA.2.11","EIOPA.N.2.11"), _('Forever context period is not allowed.'), modelObject=cntx) elif cntx.isStartEndPeriod: modelXbrl.error(("EBA.2.13","EIOPA.N.2.11"), _('Start-End (flow) context period is not allowed.'), modelObject=cntx) elif cntx.isInstantPeriod: # cannot pass context object to final() below, for error logging, if streaming mode val.cntxDates[cntx.instantDatetime].add(modelXbrl if getattr(val.modelXbrl, "isStreamingMode", False) else cntx) if cntx.hasSegment: modelXbrl.error(("EBA.2.14","EIOPA.N.2.14"), _("Contexts MUST NOT contain xbrli:segment values: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id) if cntx.nonDimValues("scenario"): modelXbrl.error(("EBA.2.15","EIOPA.N.2.15"), _("Contexts MUST NOT contain non-dimensional xbrli:scenario values: %(cntx)s.'"), modelObject=cntx, cntx=cntx.id) val.unusedCntxIDs.add(cntx.id) for unit in modelXbrl.units.values(): if unit is None or (getattr(unit, "_batchChecked", False) and isStreamingMode): continue # prior streaming batch already checked unit._batchChecked = True val.unusedUnitIDs.add(unit.id) factsByQname = defaultdict(set) # top level for this for f in factsToCheck: factsByQname[f.qname].add(f) val.unusedCntxIDs.discard(f.contextID) val.unusedUnitIDs.discard(f.unitID) if f.objectIndex < val.firstFactObjectIndex: val.firstFactObjectIndex = f.objectIndex val.firstFact = f for fIndicators in factsByQname[qnFIndicators]: val.numFilingIndicatorTuples += 1 for fIndicator in fIndicators.modelTupleFacts: _value = (getattr(fIndicator, "xValue", None) or fIndicator.value) # use validated xValue if DTS else value for skipDTS if _value in val.filingIndicators: modelXbrl.error(("EBA.1.6.1", "EIOPA.1.6.1"), _('Multiple filing indicators facts for indicator %(filingIndicator)s.'), modelObject=(fIndicator, val.filingIndicators[_value]), filingIndicator=_value) val.filingIndicators[_value] = fIndicator.get("{http://www.eurofiling.info/xbrl/ext/filing-indicators}filed", "true") in ("true", "1") val.unusedCntxIDs.discard(fIndicator.contextID) cntx = fIndicator.context if cntx is not None and (cntx.hasSegment or cntx.hasScenario): modelXbrl.error("EIOPA.S.1.6.d", _('Filing indicators must not contain segment or scenario elements %(filingIndicator)s.'), modelObject=fIndicator, filingIndicator=_value) if fIndicators.objectIndex > val.firstFactObjectIndex: modelXbrl.warning("EIOPA.1.6.2", _('Filing indicators should precede first fact %(firstFact)s.'), modelObject=(fIndicators, val.firstFact), firstFact=val.firstFact.qname) otherFacts = {} # (contextHash, unitHash, xmlLangHash) : fact nilFacts = [] # removed in current draft: stringFactsWithoutXmlLang = [] nonMonetaryNonPureFacts = [] for qname, facts in factsByQname.items(): for f in facts: if modelXbrl.skipDTS: c = f.qname.localName[0] isNumeric = c in ('m', 'p', 'i') isMonetary = c == 'm' isInteger = c == 'i' isPercent = c == 'p' isString = c == 's' isEnum = c == 'e' else: concept = f.concept if concept is not None: isNumeric = concept.isNumeric isMonetary = concept.isMonetary isInteger = concept.baseXbrliType in integerItemTypes isPercent = concept.typeQname in (qnPercentItemType, qnPureItemType) isString = concept.baseXbrliType in ("stringItemType", "normalizedStringItemType") isEnum = concept.typeQname == qnEnumerationItemType else: isNumeric = isString = isEnum = False # error situation k = (f.getparent().objectIndex, f.qname, f.context.contextDimAwareHash if f.context is not None else None, f.unit.hash if f.unit is not None else None, hash(f.xmlLang)) if f.qname == qnFIndicators and val.validateEIOPA: pass elif k not in otherFacts: otherFacts[k] = {f} else: matches = [o for o in otherFacts[k] if (f.getparent().objectIndex == o.getparent().objectIndex and f.qname == o.qname and f.context.isEqualTo(o.context) if f.context is not None and o.context is not None else True) and # (f.unit.isEqualTo(o.unit) if f.unit is not None and o.unit is not None else True) and (f.xmlLang == o.xmlLang)] if matches: contexts = [f.contextID] + [o.contextID for o in matches] modelXbrl.error(("EBA.2.16", "EIOPA.S.2.16.a"), _('Facts are duplicates %(fact)s contexts %(contexts)s.'), modelObject=[f] + matches, fact=f.qname, contexts=', '.join(contexts)) else: otherFacts[k].add(f) if isNumeric: if f.precision: modelXbrl.error(("EBA.2.17", "EIOPA.2.18.a"), _("Numeric fact %(fact)s of context %(contextID)s has a precision attribute '%(precision)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, precision=f.precision) if f.decimals and not f.isNil: if f.decimals == "INF": modelXbrl.error("EIOPA.S.2.18.f", _("Monetary fact %(fact)s of context %(contextID)s has a decimal attribute INF: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: try: xValue = f.xValue dec = int(f.decimals) # will only work if getattr(f,"xValid", XmlValidate.UNVALIDATED) >= XmlValidate.VALID: if isMonetary: if dec < -3: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.c"), _("Monetary fact %(fact)s of context %(contextID)s has a decimals attribute < -3: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: # apply dynamic decimals check if -.1 < xValue < .1: dMin = 2 elif -1 < xValue < 1: dMin = 1 elif -10 < xValue < 10: dMin = 0 elif -100 < xValue < 100: dMin = -1 elif -1000 < xValue < 1000: dMin = -2 else: dMin = -3 if dMin > dec: modelXbrl.warning("EIOPA:factDecimalsWarning", _("Monetary fact %(fact)s of context %(contextID)s value %(value)s has an imprecise decimals attribute: %(decimals)s, minimum is %(mindec)s"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue, decimals=f.decimals, mindec=dMin) elif isInteger: if dec != 0: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.d"), _("Integer fact %(fact)s of context %(contextID)s has a decimals attribute \u2260 0: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) elif isPercent: if dec < 4: modelXbrl.error(("EBA.2.18","EIOPA.S.2.18.e"), _("Percent fact %(fact)s of context %(contextID)s has a decimals attribute < 4: '%(decimals)s'"), modelObject=f, fact=f.qname, contextID=f.contextID, decimals=f.decimals) else: if -.001 < xValue < .001: dMin = 4 elif -.01 < xValue < .01: dMin = 3 elif -.1 < xValue < .1: dMin = 2 elif -1 < xValue < 1: dMin = 1 else: dMin = 0 if dMin > dec: modelXbrl.warning("EIOPA:factDecimalsWarning", _("Numeric fact %(fact)s of context %(contextID)s value %(value)s has an imprecise decimals attribute: %(decimals)s, minimum is %(mindec)s"), modelObject=f, fact=f.qname, contextID=f.contextID, value=xValue, decimals=f.decimals, mindec=dMin) except (AttributeError, ValueError, TypeError): pass # should have been reported as a schema error by loader '''' (not intended by EBA 2.18, paste here is from EFM) if not f.isNil and getattr(f,"xValid", 0) == 4: try: insignificance = insignificantDigits(f.xValue, decimals=f.decimals) if insignificance: # if not None, returns (truncatedDigits, insiginficantDigits) modelXbrl.error(("EFM.6.05.37", "GFM.1.02.26"), _("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s has nonzero digits in insignificant portion %(insignificantDigits)s."), modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.xValue, truncatedDigits=insignificance[0], insignificantDigits=insignificance[1]) except (ValueError,TypeError): modelXbrl.error(("EBA.2.18"), _("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s causes Value Error exception."), modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.value) ''' unit = f.unit if unit is not None: if isMonetary: if unit.measures[0]: val.currenciesUsed[unit.measures[0][0]] = unit elif not unit.isSingleMeasure or unit.measures[0][0] != XbrlConst.qnXbrliPure: nonMonetaryNonPureFacts.append(f) if isEnum: _eQn = getattr(f,"xValue", None) or qnameEltPfxName(f, f.value) if _eQn: val.namespacePrefixesUsed[_eQn.namespaceURI].add(_eQn.prefix) val.prefixesUnused.discard(_eQn.prefix) ''' removed in current draft elif isString: if not f.xmlLang: stringFactsWithoutXmlLang.append(f) ''' if f.isNil: nilFacts.append(f) if val.footnotesRelationshipSet.fromModelObject(f): modelXbrl.warning("EIOPA.S.19", _("Fact %(fact)s of context %(contextID)s has footnotes.'"), modelObject=f, fact=f.qname, contextID=f.contextID) if nilFacts: nilFactNames = [str(f.qname) for f in nilFacts] nilFactNames = sorted(nilFactNames) modelXbrl.error(("EBA.2.19", "EIOPA.S.2.19"), _('Nil facts MUST NOT be present in the instance: %(nilFacts)s.'), modelObject=nilFacts, nilFacts=", ".join(fname for fname in nilFactNames)) ''' removed in current draft if stringFactsWithoutXmlLang: modelXbrl.error("EBA.2.20", _("String facts need to report xml:lang: '%(langLessFacts)s'"), modelObject=stringFactsWithoutXmlLang, langLessFacts=", ".join(set(str(f.qname) for f in stringFactsWithoutXmlLang))) ''' if nonMonetaryNonPureFacts: modelXbrl.error(("EBA.3.2","EIOPA.3.2.a"), _("Non monetary (numeric) facts MUST use the pure unit: '%(langLessFacts)s'"), modelObject=nonMonetaryNonPureFacts, langLessFacts=", ".join(set(str(f.qname) for f in nonMonetaryNonPureFacts))) val.utrValidator.validateFacts() # validate facts for UTR at logLevel WARNING unitHashes = {} for unit in modelXbrl.units.values(): if unit is not None: h = unit.hash if h in unitHashes and unit.isEqualTo(unitHashes[h]): modelXbrl.warning("EBA.2.21", _("Duplicate units SHOULD NOT be reported, units %(unit1)s and %(unit2)s have same measures.'"), modelObject=(unit, unitHashes[h]), unit1=unit.id, unit2=unitHashes[h].id) if not getattr(modelXbrl, "isStreamingMode", False): modelXbrl.error("EIOPA.2.21", _("Duplicate units MUST NOT be reported, units %(unit1)s and %(unit2)s have same measures.'"), modelObject=(unit, unitHashes[h]), unit1=unit.id, unit2=unitHashes[h].id) else: unitHashes[h] = unit for _measures in unit.measures: for _measure in _measures: val.namespacePrefixesUsed[_measure.namespaceURI].add(_measure.prefix) val.prefixesUnused.discard(_measure.prefix) del unitHashes cntxHashes = {} for cntx in modelXbrl.contexts.values(): if cntx is not None: h = cntx.contextDimAwareHash if h in cntxHashes and cntx.isEqualTo(cntxHashes[h]): if not getattr(modelXbrl, "isStreamingMode", False): modelXbrl.error("EIOPA.S.2.7.b", _("Duplicate contexts MUST NOT be reported, contexts %(cntx1)s and %(cntx2)s are equivalent.'"), modelObject=(cntx, cntxHashes[h]), cntx1=cntx.id, cntx2=cntxHashes[h].id) else: cntxHashes[h] = cntx for _dim in cntx.qnameDims.values(): _dimQn = _dim.dimensionQname val.namespacePrefixesUsed[_dimQn.namespaceURI].add(_dimQn.prefix) val.prefixesUnused.discard(_dimQn.prefix) if _dim.isExplicit: _memQn = _dim.memberQname else: _memQn = _dim.typedMember.qname if _memQn: val.namespacePrefixesUsed[_memQn.namespaceURI].add(_memQn.prefix) val.prefixesUnused.discard(_memQn.prefix) for elt in modelDocument.xmlRootElement.iter(): if isinstance(elt, ModelObject): # skip comments and processing instructions val.namespacePrefixesUsed[elt.qname.namespaceURI].add(elt.qname.prefix) val.prefixesUnused.discard(elt.qname.prefix) for attrTag in elt.keys(): if attrTag.startswith("{"): _prefix, _NS, _localName = XmlUtil.clarkNotationToPrefixNsLocalname(elt, attrTag, isAttribute=True) if _prefix: val.namespacePrefixesUsed[_NS].add(_prefix) val.prefixesUnused.discard(_prefix)
def validateValue(modelXbrl, elt, attrTag, baseXsdType, value, isNillable=False, isNil=False, facets=None): if baseXsdType: try: u''' if (len(value) == 0 and attrTag is None and not isNillable and baseXsdType not in ("anyType", "string", "normalizedString", "token", "NMTOKEN", "anyURI", "noContent")): raise ValueError("missing value for not nillable element") ''' xValid = VALID whitespaceReplace = (baseXsdType == u"normalizedString") whitespaceCollapse = (not whitespaceReplace and baseXsdType != u"string") isList = baseXsdType in set([u"IDREFS", u"ENTITIES", u"NMTOKENS"]) if isList: baseXsdType = baseXsdType[:-1] # remove plural pattern = baseXsdTypePatterns.get(baseXsdType) if facets: if u"pattern" in facets: pattern = facets[u"pattern"] # note multiple patterns are or'ed togetner, which isn't yet implemented! if u"whiteSpace" in facets: whitespaceReplace, whitespaceCollapse = {u"preserve":(False,False), u"replace":(True,False), u"collapse":(False,True)}[facets[u"whiteSpace"]] if whitespaceReplace: value = normalizeWhitespacePattern.sub(u' ', value) elif whitespaceCollapse: value = collapseWhitespacePattern.sub(u' ', value.strip()) if baseXsdType == u"noContent": if len(value) > 0 and not value.isspace(): raise ValueError(u"value content not permitted") # note that sValue and xValue are not innerText but only text elements on specific element (or attribute) xValue = sValue = None xValid = VALID_NO_CONTENT # notify others that element may contain subelements (for stringValue needs) elif not value and isNil and isNillable: # rest of types get None if nil/empty value xValue = sValue = None else: if pattern is not None: if ((isList and any(pattern.match(v) is None for v in value.split())) or (not isList and pattern.match(value) is None)): raise ValueError(u"pattern facet " + facets[u"pattern"].pattern if facets and u"pattern" in facets else u"pattern mismatch") if facets: if u"enumeration" in facets and value not in facets[u"enumeration"]: raise ValueError(u"{0} is not in {1}".format(value, facets[u"enumeration"])) if u"length" in facets and len(value) != facets[u"length"]: raise ValueError(u"length {0}, expected {1}".format(len(value), facets[u"length"])) if u"minLength" in facets and len(value) < facets[u"minLength"]: raise ValueError(u"length {0}, minLength {1}".format(len(value), facets[u"minLength"])) if u"maxLength" in facets and len(value) > facets[u"maxLength"]: raise ValueError(u"length {0}, maxLength {1}".format(len(value), facets[u"maxLength"])) if baseXsdType in set([u"string", u"normalizedString", u"language", u"token", u"NMTOKEN",u"Name",u"NCName",u"IDREF",u"ENTITY"]): xValue = sValue = value elif baseXsdType == u"ID": xValue = sValue = value xValid = VALID_ID elif baseXsdType == u"anyURI": if value: # allow empty strings to be valid anyURIs if UrlUtil.relativeUrlPattern.match(value) is None: raise ValueError(u"IETF RFC 2396 4.3 syntax") # encode PSVI xValue similarly to Xerces and other implementations xValue = anyURI(UrlUtil.anyUriQuoteForPSVI(value)) sValue = value elif baseXsdType in (u"decimal", u"float", u"double"): if baseXsdType == u"decimal": if decimalPattern.match(value) is None: raise ValueError(u"lexical pattern mismatch") xValue = Decimal(value) sValue = float(value) # s-value uses Number (float) representation else: if floatPattern.match(value) is None: raise ValueError(u"lexical pattern mismatch") xValue = sValue = float(value) if facets: if u"totalDigits" in facets and len(value.replace(u".",u"")) > facets[u"totalDigits"]: raise ValueError(u"totalDigits facet {0}".format(facets[u"totalDigits"])) if u"fractionDigits" in facets and ( u'.' in value and len(value[value.index(u'.') + 1:]) > facets[u"fractionDigits"]): raise ValueError(u"fraction digits facet {0}".format(facets[u"fractionDigits"])) if u"maxInclusive" in facets and xValue > facets[u"maxInclusive"]: raise ValueError(u" > maxInclusive {0}".format(facets[u"maxInclusive"])) if u"maxExclusive" in facets and xValue >= facets[u"maxExclusive"]: raise ValueError(u" >= maxInclusive {0}".format(facets[u"maxExclusive"])) if u"minInclusive" in facets and xValue < facets[u"minInclusive"]: raise ValueError(u" < minInclusive {0}".format(facets[u"minInclusive"])) if u"minExclusive" in facets and xValue <= facets[u"minExclusive"]: raise ValueError(u" <= minExclusive {0}".format(facets[u"minExclusive"])) elif baseXsdType in set([u"integer", u"nonPositiveInteger",u"negativeInteger",u"nonNegativeInteger",u"positiveInteger", u"long",u"unsignedLong", u"int",u"unsignedInt", u"short",u"unsignedShort", u"byte",u"unsignedByte"]): xValue = sValue = _INT(value) if ((baseXsdType in set([u"nonNegativeInteger",u"unsignedLong",u"unsignedInt"]) and xValue < 0) or (baseXsdType == u"nonPositiveInteger" and xValue > 0) or (baseXsdType == u"positiveInteger" and xValue <= 0) or (baseXsdType == u"byte" and not -128 <= xValue < 127) or (baseXsdType == u"unsignedByte" and not 0 <= xValue < 255) or (baseXsdType == u"short" and not -32768 <= xValue < 32767) or (baseXsdType == u"unsignedShort" and not 0 <= xValue < 65535) or (baseXsdType == u"positiveInteger" and xValue <= 0)): raise ValueError(u"{0} is not {1}".format(value, baseXsdType)) if facets: if u"totalDigits" in facets and len(value.replace(u".",u"")) > facets[u"totalDigits"]: raise ValueError(u"totalDigits facet {0}".format(facets[u"totalDigits"])) if u"fractionDigits" in facets and ( u'.' in value and len(value[value.index(u'.') + 1:]) > facets[u"fractionDigits"]): raise ValueError(u"fraction digits facet {0}".format(facets[u"fractionDigits"])) if u"maxInclusive" in facets and xValue > facets[u"maxInclusive"]: raise ValueError(u" > maxInclusive {0}".format(facets[u"maxInclusive"])) if u"maxExclusive" in facets and xValue >= facets[u"maxExclusive"]: raise ValueError(u" >= maxInclusive {0}".format(facets[u"maxExclusive"])) if u"minInclusive" in facets and xValue < facets[u"minInclusive"]: raise ValueError(u" < minInclusive {0}".format(facets[u"minInclusive"])) if u"minExclusive" in facets and xValue <= facets[u"minExclusive"]: raise ValueError(u" <= minExclusive {0}".format(facets[u"minExclusive"])) elif baseXsdType == u"boolean": if value in (u"true", u"1"): xValue = sValue = True elif value in (u"false", u"0"): xValue = sValue = False else: raise ValueError elif baseXsdType == u"QName": xValue = qnameEltPfxName(elt, value, prefixException=ValueError) #xValue = qname(elt, value, castException=ValueError, prefixException=ValueError) sValue = value u''' not sure here, how are explicitDimensions validated, but bad units not? if xValue.namespaceURI in modelXbrl.namespaceDocs: if (xValue not in modelXbrl.qnameConcepts and xValue not in modelXbrl.qnameTypes and xValue not in modelXbrl.qnameAttributes and xValue not in modelXbrl.qnameAttributeGroups): raise ValueError("qname not defined " + str(xValue)) ''' elif baseXsdType in (u"XBRLI_DECIMALSUNION", u"XBRLI_PRECISIONUNION"): xValue = sValue = value if value == u"INF" else _INT(value) elif baseXsdType in (u"XBRLI_NONZERODECIMAL"): xValue = sValue = _INT(value) if xValue == 0: raise ValueError(u"invalid value") elif baseXsdType == u"XBRLI_DATEUNION": xValue = dateTime(value, type=DATEUNION, castException=ValueError) sValue = value elif baseXsdType == u"dateTime": xValue = dateTime(value, type=DATETIME, castException=ValueError) sValue = value elif baseXsdType == u"date": xValue = dateTime(value, type=DATE, castException=ValueError) sValue = value elif baseXsdType == u"regex-pattern": # for facet compiling try: sValue = value if value in xmlSchemaPatterns: xValue = xmlSchemaPatterns[value] else: if ur"\i" in value or ur"\c" in value: value = value.replace(ur"\i", iNameChar).replace(ur"\c", cNameChar) xValue = re_compile(value + u"$") # must match whole string except Exception, err: raise ValueError(err) else: if baseXsdType in lexicalPatterns: match = lexicalPatterns[baseXsdType].match(value) if match is None: raise ValueError(u"lexical pattern mismatch") if baseXsdType == u"gMonthDay": month, day, zSign, zHrMin, zHr, zMin = match.groups() if int(day) > {2:29, 4:30, 6:30, 9:30, 11:30, 1:31, 3:31, 5:31, 7:31, 8:31, 10:31, 12:31}[int(month)]: raise ValueError(u"invalid day {0} for month {1}".format(day, month)) xValue = gMonthDay(month, day) elif baseXsdType == u"gYearMonth": year, month, zSign, zHrMin, zHr, zMin = match.groups() xValue = gYearMonth(year, month) elif baseXsdType == u"gYear": year, zSign, zHrMin, zHr, zMin = match.groups() xValue = gYear(year) elif baseXsdType == u"gMonth": month, zSign, zHrMin, zHr, zMin = match.groups() xValue = gMonth(month) elif baseXsdType == u"gDay": day, zSign, zHrMin, zHr, zMin = match.groups() xValue = gDay(day) else: xValue = value else: # no lexical pattern, forget compiling value xValue = value sValue = value