def parse_list( self, manager: ImportManager, schema: Dict, entry_name: EntryName, att_name: str, att_type: str, desc: str) -> ListProperty: if SchemaKeywords.element_type not in schema: raise TypeNotDeclaredException( f"Item type for the attribute {att_name} of the entry " f"[{entry_name.class_name}] not declared. This attribute is " f"a composite type: {att_type}, it should have a " f"{SchemaKeywords.element_type}.") item_type = schema[SchemaKeywords.element_type] if is_composite_type(item_type): # Case of nested. raise UnsupportedTypeException( f"Item type {item_type} for entry {entry_name.name}'s " f"attribute {att_name} is a composite type, we do not support " f"nested composite type.") if not manager.is_known_name(item_type): # Case of unknown. raise TypeNotDeclaredException( f"Item type {item_type} for the entry " f"{entry_name.name} of the attribute {att_name} " f"not declared in ontology.") # Make sure the import of these related types are handled. manager.add_object_to_import(item_type) self_ref = entry_name.class_name == item_type return ListProperty( manager, att_name, item_type, description=desc, default_val=[], self_ref=self_ref)
def parse_dict( self, manager: ImportManager, schema: Dict, entry_name: EntryName, att_name: str, att_type: str, desc: str, ): if (SchemaKeywords.dict_key_type not in schema or SchemaKeywords.dict_value_type not in schema): raise TypeNotDeclaredException( f"Item type of the attribute {att_name} for the entry " f" {entry_name.class_name} not declared. This attribute is " f"a composite type: {att_type}, it should have a " f"{SchemaKeywords.dict_key_type} and " f"{SchemaKeywords.dict_value_type}.") key_type = schema[SchemaKeywords.dict_key_type] if not valid_composite_key(key_type): raise UnsupportedTypeException( f"Key type {key_type} for entry {entry_name.name}'s " f"attribute {att_name} is not supported, we only support a " f"limited set of keys.") value_type = schema[SchemaKeywords.dict_value_type] if is_composite_type(value_type): # Case of nested. raise UnsupportedTypeException( f"Item type {value_type} for entry {entry_name.name}'s " f"attribute {att_name} is a composite type, we do not support " f"nested composite type.") if not manager.is_known_name(value_type): # Case of unknown. raise TypeNotDeclaredException( f"Item type {value_type} for the entry " f"{entry_name.name} of the attribute {att_name} " f"not declared in ontology.") # Make sure the import of these related types are handled. manager.add_object_to_import(value_type) self_ref = entry_name.class_name == value_type default_val: Dict = {} return DictProperty( manager, att_name, key_type, value_type, description=desc, default_val=default_val, self_ref=self_ref, )
def parse_property(self, entry_name: EntryName, schema: Dict) -> Property: """ Parses instance and class properties defined in an entry schema and checks for the constraints allowed by the ontology generation system. Args: entry_name: Entry Name object that contains various form of the entry's name. schema: Entry definition schema Returns: An object of class `code_generation_util.FileItem` containing the generated code. """ att_name = schema[SchemaKeywords.attribute_name] att_type = schema[SchemaKeywords.attribute_type] manager: ImportManager = self.import_managers.get( entry_name.module_name) # schema type should be present in the validation tree # TODO: Remove this hack if not manager.is_known_name(att_type): raise TypeNotDeclaredException( f"Attribute type '{att_type}' for the entry " f"'{entry_name.name}' of the attribute '{att_name}' not " f"declared in the ontology") desc = schema.get(SchemaKeywords.description, None) default_val = schema.get(SchemaKeywords.default_value, None) # element type should be present in the validation tree if att_type in COMPOSITES: if att_type == "List": return self.parse_list(manager, schema, entry_name, att_name, att_type, desc) elif att_type == "Dict": return self.parse_dict(manager, schema, entry_name, att_name, att_type, desc) elif att_type in NON_COMPOSITES or manager.is_imported(att_type): self_ref = entry_name.class_name == att_type return self.parse_non_composite( manager, att_name, att_type, desc, default_val, self_ref=self_ref, ) raise UnsupportedTypeException(f"{att_type} is not a supported type.")
def parse_entry(self, entry_name: EntryName, schema: Dict) -> Tuple[EntryDefinition, List[str]]: """ Args: entry_name: Object holds various name form of the entry. schema: Dictionary containing specifications for an entry. Returns: extracted entry information: entry package string, entry filename, entry class entry_name, generated entry code and entry attribute names. """ this_manager = self.import_managers.get(entry_name.module_name) # Determine the parent entry of this entry. parent_entry: str = schema[SchemaKeywords.parent_entry] if parent_entry.startswith(TOP_MOST_MODULE_NAME): raise ParentEntryNotSupportedException( f"The parent entry {parent_entry} cannot be directly inherited," f" please inherit a type from {top.__name__} or your own" f" ontology.") if not this_manager.is_imported(parent_entry): raise ParentEntryNotDeclaredException( f"The parent entry {parent_entry} is not declared. It is " f"neither in the base entries nor in custom entries. " f"Please check them ontology specification, and make sure the " f"entry is defined before this.") base_entry: Optional[str] = self.find_base_entry( entry_name.class_name, parent_entry) if base_entry is None: raise OntologySpecError( f"Cannot find the base entry for entry " f"{entry_name.class_name} and {parent_entry}") if base_entry not in self.top_init_args: raise ParentEntryNotSupportedException( f"Cannot add {entry_name.class_name} to the ontology as " f"it's parent entry {parent_entry} is not supported. This is " f"likely that the entries are not inheriting the allowed types." ) # Take the property definitions of this entry. properties: List[Dict] = schema.get(SchemaKeywords.attributes, []) this_manager = self.import_managers.get(entry_name.module_name) # Validate if the parent entry is present. if not this_manager.is_known_name(parent_entry): raise ParentEntryNotDeclaredException( f"Cannot add {entry_name.class_name} to the ontology as " f"it's parent entry {parent_entry} is not present " f"in the ontology.") parent_entry_use_name = this_manager.get_name_to_use(parent_entry) property_items, property_names = [], [] for prop_schema in properties: # TODO: add test prop_name = prop_schema["name"] if prop_name in RESERVED_ATTRIBUTE_NAMES: raise InvalidIdentifierException( f"The attribute name {prop_name} is reserved and cannot be " f"used, please consider changed the name. The list of " f"reserved name strings are " f"{RESERVED_ATTRIBUTE_NAMES}") property_names.append(prop_schema["name"]) property_items.append(self.parse_property(entry_name, prop_schema)) # For special classes that requires a constraint. core_bases: Set[str] = self.top_to_core_entries[base_entry] entry_constraint_keys: Dict[str, str] = {} if any(item == "BaseLink" for item in core_bases): entry_constraint_keys = DEFAULT_CONSTRAINTS_KEYS["BaseLink"] elif any(item == "BaseGroup" for item in core_bases): entry_constraint_keys = DEFAULT_CONSTRAINTS_KEYS["BaseGroup"] class_att_items: List[ClassTypeDefinition] = [] for schema_key, class_key in entry_constraint_keys.items(): if schema_key in schema: constraint_type_ = schema[schema_key] constraint_type_name = this_manager.get_name_to_use( constraint_type_) if constraint_type_name is None: raise TypeNotDeclaredException( f"The type {constraint_type_} is not defined but it is " f"specified in {schema_key} of the definition of " f"{schema['entry_name']}. Please define them before " f"this entry type.") # TODO: cannot handle constraints that contain self-references. # self_ref = entry_name.class_name == constraint_type_ class_att_items.append( ClassTypeDefinition(class_key, constraint_type_name)) # TODO: Can assign better object type to Link and Group objects custom_init_arg_str: str = self.construct_init(entry_name, base_entry) entry_item = EntryDefinition( name=entry_name.name, class_type=parent_entry_use_name, init_args=custom_init_arg_str, properties=property_items, class_attributes=class_att_items, description=schema.get(SchemaKeywords.description, None), ) return entry_item, property_names