def __init__(self, *args: Union[FunOwlBase, IRI.types()], **kwargs: Dict[str, FunOwlBase]) -> None: args = list(args) if args and isinstance(args[0], IRI) and not isinstance_(args[0], Axiom): self.iri = args.pop(0) if args and isinstance(args[0], IRI) and not isinstance_(args[0], Axiom): self.version = args.pop(0) self.directlyImportsDocuments = cast(List[Import], []) while args and isinstance(args[0], Import): self.directlyImportsDocuments.append(args.pop(0)) self.axioms = cast(List[Axiom], []) while args and isinstance_(args[0], Axiom): self.axioms.append(args.pop(0)) self.annotations = kwargs.get('annotations', []) for k, v in kwargs.items(): cur_v = getattr(self, k, MISSING) if cur_v is MISSING: raise ValueError(f"Unknown argument to Ontology: {k}") if cur_v is None: setattr(self, k, v) elif k != 'annotations': setattr(self, k, cur_v + v) if args: raise ValueError(f"Unrecognized arguments to Ontology: {args}") self._naxioms = 0
def cast(typ: Type, v: Any, _coercion_allowed: Optional[bool] = None) -> Any: """ Convert value v to type typ. Raises TypeError if conversion is not possible. Note that None and empty lists are treated as universal types :param typ: type to convert v to :param v: value to convert to type :param _coercion_allowed: True means type coercion is allowed. False means only matching types work :return: instance of type """ from funowl.base.fun_owl_choice import FunOwlChoice if v is None or v == []: # Null and empty list are always allowed (a bit too permissive but...) return v # Union[...] if is_union(typ): for t in get_args(typ): if type(v) is t: return v elif _coercion_allowed is not False and isinstance_(v, t): return cast(t, v) raise TypeError(f"Type mismatch between {v} (type: {type(v)} and {typ}") # List[...] if is_iterable(typ): list_type = get_args(typ)[0] if isinstance(v, str) or not isinstance(v, Iterable): # You can assign a singleton directly v = [v] return [cast(list_type, vi) for vi in v] if issubclass(type(v), typ): # conversion is treated as idempotent (typ(typ(v)) = typ(v) return copy(v) if isinstance(typ, type) and issubclass(typ, FunOwlChoice): hints = typ.hints() pos_types = ', '.join([t.__name__ for t in hints]) logging.debug(f"value: {v} (type: {type(v)}) testing against {typ}[{pos_types}]") for poss_type in hints: if issubclass(type(v), poss_type) or (_coercion_allowed is not False and isinstance(v, poss_type)): logging.debug(f" Matches {poss_type.__name__}") if getattr(poss_type, '_parse_input', None): return typ(poss_type(*poss_type._parse_input(v))) return typ(poss_type(v)) logging.debug(' No match') # Determine whether v can be cooreced into type if _coercion_allowed is False or not isinstance_(v, typ): raise TypeError(f"value: {v} (type: {type(v)}) cannot be converted to {typ}") # Vanilla typing return typ(*(getattr(typ, '_parse_input', lambda e: e))(v))
def add_arg(self, arg: [IRI.types(), Import, Axiom, Annotation]): if isinstance_(arg, Axiom): self.axioms.append(arg) self._naxioms += 1 if not self._naxioms % 100000: print('H') elif not self._naxioms % 10000: print('K') elif not self._naxioms % 1000: print('k', end='') sys.stdout.flush() elif not self._naxioms % 100: print('.', end='') sys.stdout.flush() elif isinstance(arg, IRI): if not self.iri: self.iri = arg elif not self.version: self.version = arg else: raise ValueError(f"Raw IRI is not a valid argument {arg}") elif isinstance(arg, Import): self.directlyImportsDocuments.append(arg) elif isinstance(arg, Annotation): self.annotations.append(arg) else: raise ValueError(f"Unrecognized argument to Ontology: {arg}")
def set_v(self, value: Any) -> bool: """ Default setter -- can be invoked from more elaborate coercion routines :param value: value to set :return: True if v was set """ for choice_type in self.hints(): if issubclass( type(value), choice_type) or (self._coercion_allowed and isinstance_(value, choice_type)): super().__setattr__('v', value) logging.debug( f"{type(self).__name__}: value = {str(self.v)} (type: {type(self.v).__name__})" ) return True return False
def remove_exclusions(typ: Union[Field, Type]) -> Union[List[Type], Type]: """ Convert typ into an ordered list of possible types, removing any exclusions :param typ: Field, FunOwlChoice instance or Type definition :return: Ordered list of types with exclusions removed """ from funowl.base.fun_owl_choice import FunOwlChoice if isinstance(typ, type) and issubclass(typ, FunOwlChoice): return typ.real_types() if isinstance_(typ, Field): # If it is a field, the actual type is Field.type. if is_union(typ.type): exclusions = typ.metadata.get('exclude', []) return [t for t in get_args(typ.type) if t not in exclusions] else: typ = typ.type return [typ]
def cast(cast_to: Union[Type, Field], v: Any, _coercion_allowed: Optional[bool] = None) -> Any: """ Convert value v to type cast_to. Raises TypeError if conversion is not possible. Note that None and empty lists are treated as universal types :param cast_to: Field, FunOwlChoice instance or Type definition we want to cast v to :param v: value to cast. Note that None and empty lists are treated as always cast. :param _coercion_allowed: True means type coercion is allowed. False means only matching types work :return: instance of cast_to """ from funowl.base.fun_owl_choice import FunOwlChoice def cast_to_choice(choice: FunOwlChoice, v: Any) -> Any: """ Process a choice """ hints = choice.real_types() for poss_type in hints: if issubclass(type(v), poss_type): return choice_match(poss_type) for poss_type in hints: if issubclass(type(v), poss_type) or (_coercion_allowed is not False and isinstance(v, poss_type)): return choice_match(poss_type) logging.debug(' No match') def choice_match(matched_type: Type) -> Any: if hasattr(matched_type, '_parse_input'): rval = typ(matched_type(*matched_type._parse_input(v))) else: rval = typ(matched_type(v)) return rval # TODO: this should be a parameterized type for return def do_cast(target_type: Type, target_value: Any) -> Any: return target_type( *(getattr(target_type, '_parse_input', lambda e: e))(target_value)) # None and empty lists are universal types. If already cast, we're done if v is None or v == [] or (isinstance_(cast_to, Field) and type(v) is cast_to.type) or type(v) is cast_to: return v # Create an ordered list of target types -- these are the types IN cast_to type_list = remove_exclusions(cast_to) # If we already match the list, no coercion is necessary for typ in type_list: if type(v) is typ: return v # Iterate through the list to determine whether we can coerce v to any of the targets if _coercion_allowed is not False: for typ in type_list: # Note: This parallels the code in TypingHelper.isinstance_ -- it may be worth considering merging these # with a visitor idiom # Any / unrealized TypeVar is the identity function # TODO: not all TypeVar situations will work here if typ is Any or isinstance(typ, TypeVar): return do_cast(typ, v) elif isinstance_(typ, FunOwlChoice): return cast_to_choice(typ, v) elif is_union(typ): for t in get_args(typ): if isinstance_(v, t): if type(v) is t: return v else: return do_cast(t, v) elif is_dict(typ): if isinstance(v, dict): dict_args = get_args(typ) return { cast(dict_args[0], dk): cast(dict_args[1], dv) for dk, dv in v.items() } if dict_args else v elif is_list(typ): # casting x == [x] if isinstance(v, str) or not isinstance(v, Iterable): v = [v] if isinstance(v, list) or isinstance(v, UserList): list_type = get_args(typ) return [cast(list_type[0], vi) for vi in v] if list_type else v elif is_tuple(typ): if isinstance(v, tuple): tuple_args = get_args(typ) return tuple( cast(tt, vt) for tt, vt in zip(tuple_args, v)) if tuple_args else v elif is_set(typ): if isinstance_(v, set): set_type = get_args(typ) return set(cast(set_type[0], e) for e in v) if set_type else v elif isinstance_(v, typ): return do_cast(typ, v if issubclass(typ, str) else copy(v)) raise TypeError( f"Type mismatch between {v} (type: {type(v)} and {type_list}")
def __instancecheck__(self, instance) -> bool: return instance is not None and \ isinstance_(instance, self.python_type) and (self.pattern is None or self.pattern.matches(str(instance)))
def test_isinstance(self): self.assertTrue(isinstance_(int, Any)) self.assertTrue(isinstance_(None, Any)) self.assertTrue(isinstance_(Any, Any)) self.assertTrue(isinstance_(17, inst_union)) self.assertTrue(isinstance_("fred", inst_union)) self.assertTrue(isinstance_(False, inst_union)) self.assertTrue(isinstance_(Class("http://example.org"), inst_union)) self.assertTrue(isinstance_({"k": 42}, inst_union)) self.assertFalse(isinstance_({"k": 42}, inst_union_2)) self.assertTrue(isinstance_({"k": "abc"}, inst_union_2)) self.assertTrue(isinstance_({"k": "abc", "l": "def"}, inst_union_2)) self.assertFalse(isinstance_({"k": "abc", "l": True}, inst_union_2)) self.assertTrue(isinstance_({"k": "abc", "l": True}, Union[Dict])) self.assertTrue(isinstance_({"k": "abc", "l": True}, Union[dict])) self.assertTrue(isinstance_({}, inst_union_2)) self.assertTrue(isinstance_((17, "a"), inst_union)) self.assertFalse(isinstance_(("a", "a"), inst_union)) self.assertTrue( isinstance_(([1, 2, 3], [True, False, True]), Tuple[List[int], List[bool]]))