def __init__(self, schema: Union[str, TextIO, SchemaDefinition, "Generator"], fmt: Optional[str] = None, emit_metadata: bool = False) -> None: """ Constructor :param schema: metamodel compliant schema. Can be URI, file name, actual schema, another generator, an open file or a pre-parsed schema. :param fmt: expected output format :param emit_metadata: True means include date, generator, etc. information in source header if appropriate """ if fmt is None: fmt = self.valid_formats[0] assert fmt in self.valid_formats, f"Unrecognized format: {fmt}" self.format = fmt self.emit_metadata = emit_metadata if isinstance(schema, Generator): gen = schema self.schema = gen.schema self.synopsis = gen.synopsis self.namespaces = gen.namespaces self.base_dir = gen.base_dir self.schema_location = gen.schema_location else: loader = SchemaLoader(schema, self.base_dir) loader.resolve() self.schema = loader.schema self.synopsis = loader.synopsis self.namespaces = loader.namespaces self.base_dir = loader.base_dir self.schema_location = loader.schema_location
def test_error_paths(self): """ Test various loader error situations""" fn = env.input_path('loadererror1.yaml') with self.assertRaises(ValueError, msg="Unknown slot domain should fail") as e: SchemaLoader(fn).resolve() self.assertIn('loadererror1.yaml", line 11, col 13', str(e.exception)) fn = env.input_path('loadererror2.yaml') with self.assertRaises(ValueError, msg="No Type URI") as e: SchemaLoader(fn).resolve() self.assertIn('type "string" does not declare a URI', str(e.exception)) fn = env.input_path('loadererror2a.yaml') with self.assertRaises(ValueError, msg="Optional key slot should fail") as e: SchemaLoader(fn).resolve() self.assertIn('slot: s1 - key and identifier slots cannot be optional', str(e.exception)) fn = env.input_path('loadertest1.yaml') schema = SchemaLoader(fn).resolve() self.assertEqual('string', schema.slots['s1'].range) fn = env.input_path('loadererror4.yaml') with self.assertRaises(ValueError, msg="Default prefix is not defined") as e: SchemaLoader(fn).resolve() self.assertIn('loadererror4.yaml", line 6, col 17', str(e.exception))
def __init__(self, schema: Union[str, TextIO, SchemaDefinition], **kwargs) -> None: super().__init__(schema, **kwargs) self.graph: Optional[Graph] = None self.metamodel = SchemaLoader(LOCAL_METAMODEL_YAML_FILE) if os.path.exists(LOCAL_METAMODEL_YAML_FILE) else\ SchemaLoader(METAMODEL_YAML_URI, base_dir=META_BASE_URI) self.metamodel.resolve() self.top_value_uri: Optional[URIRef] = None
def __init__(self, schema: Union[str, TextIO, SchemaDefinition], fmt: str = 'ttl') -> None: super().__init__(schema, fmt) self.graph: Graph = None self.metamodel = SchemaLoader(LOCAL_YAML_PATH) self.metamodel.resolve() self.top_value_uri: URIRef = None
def test_missing_type_uri(self): """ A type with neither a typeof or uri is an error """ fn = os.path.join(datadir, 'loadererror10.yaml') with self.assertRaises(ValueError, msg="A non-typeof type has to have a URI"): _ = SchemaLoader(fn).resolve() fn = os.path.join(datadir, 'loaderpass11.yaml') _ = SchemaLoader(fn).resolve()
def __init__(self, schema: Union[str, TextIO, SchemaDefinition, "Generator"], format: Optional[str] = None, emit_metadata: bool = False, useuris: Optional[bool] = None, importmap: Optional[str] = None, log_level: int = DEFAULT_LOG_LEVEL_INT, **kwargs) -> None: """ Constructor :param schema: metamodel compliant schema. Can be URI, file name, actual schema, another generator, an open file or a pre-parsed schema. :param format: expected output format :param emit_metadata: True means include date, generator, etc. information in source header if appropriate :param useuris: True means declared class slot uri's are used. False means use model uris :param importmap: File name of import mapping file -- maps import name/uri to target :param log_level: Logging level :param logger: pre-set logger (hidden in kwargs) """ if 'logger' in kwargs: self.logger = kwargs.pop('logger') else: logging.basicConfig() self.logger = logging.getLogger(self.__class__.__name__) self.logger.setLevel(log_level) if format is None: format = self.valid_formats[0] assert format in self.valid_formats, f"Unrecognized format: {format}" self.format = format self.emit_metadata = emit_metadata if isinstance(schema, Generator): gen = schema self.schema = gen.schema self.synopsis = gen.synopsis self.loaded = gen.loaded self.namespaces = gen.namespaces self.base_dir = gen.base_dir self.importmap = gen.importmap self.schema_location = gen.schema_location self.schema_defaults = gen.schema_defaults self.logger = gen.logger else: loader = SchemaLoader(schema, self.base_dir, useuris=useuris, importmap=importmap, logger=self.logger) loader.resolve() self.schema = loader.schema self.synopsis = loader.synopsis self.loaded = loader.loaded self.namespaces = loader.namespaces self.base_dir = loader.base_dir self.importmap = loader.importmap self.schema_location = loader.schema_location self.schema_defaults = loader.schema_defaults
def test_missing_type_uri(self): """ A type with neither a typeof or uri is an error """ fn = env.input_path('loadererror10.yaml') with self.assertRaises(ValueError, msg="A non-typeof type has to have a URI") as e: _ = SchemaLoader(fn).resolve() self.assertIn('loadererror10.yaml", line 12, col 3', str(e.exception)) fn = env.input_path('loaderpass11.yaml') _ = SchemaLoader(fn).resolve()
def test_biolink_model(self): """ Make sure the biolink model is valid """ schema = SchemaLoader(BIOLINK_MODEL_YAML) errors = [] try: schema.resolve() except ValueError as e: errors.append(str(e)) if not errors: errors = schema.synopsis.errors() self.assertEqual([], errors, "biolink-model.yaml - errors detected")
def test_key_and_id(self): """ A slot cannot be both a key and an identifier """ fn = os.path.join(datadir, 'loadererror8.yaml') with self.assertRaises( ValueError, msg="A slot cannot be both a key and identifier"): _ = SchemaLoader(fn).resolve() fn = os.path.join(datadir, 'loadererror9.yaml') with self.assertRaises( ValueError, msg="A slot cannot be both a key and identifier"): _ = SchemaLoader(fn).resolve()
def test_multi_key(self): """ Multiple keys are not supported """ fn = os.path.join(datadir, 'loadererror6.yaml') with self.assertRaises(ValueError, msg="Two or more keys are not allowed"): _ = SchemaLoader(fn).resolve() fn = os.path.join(datadir, 'loadererror7.yaml') with self.assertRaises(ValueError, msg="Two or more keys are not allowed"): _ = SchemaLoader(fn).resolve()
def eval_loader(self, base_name: str, is_sourcedir: bool = False, source: Optional[str] = None) -> None: fn = os.path.join(sourcedir if is_sourcedir else datadir, base_name + '.yaml') if not source else source loader = SchemaLoader(fn) schema = as_json(self.fix_schema_metadata(loader.resolve())) self.eval_output(schema, base_name + '.json', loads) errors = '\n'.join(loader.synopsis.errors()) self.eval_output(errors, base_name + '.errs') self.assertFalse(update_all_files, "Updating base files -- rerun")
def test_default_range(self): """ Validate default slot range settings """ schema = SchemaLoader(env.input_path('resolver1.yaml')).resolve() self.assertEqual({ 's1': 't1', 's2': 't2' }, {slot.name: slot.range for slot in schema.slots.values()}) schema = SchemaLoader(env.input_path('resolver2.yaml')).resolve() self.assertEqual({ 's1': 'string', 's2': 't2' }, {slot.name: slot.range for slot in schema.slots.values()})
def test_representation_errors(self): """ Test misformed schema elements """ fn = env.input_path('typeerror1.yaml') with self.assertRaises(ValueError): SchemaLoader(fn) fn = env.input_path('typeerror2.yaml') with self.assertRaises(ValueError): SchemaLoader(fn) fn = env.input_path('typeerror3.yaml') with self.assertRaises(ValueError): SchemaLoader(fn) fn = env.input_path('typeerror4.yaml') with self.assertRaises(ValueError): SchemaLoader(fn)
def test_representation_errors(self): """ Test misformed schema elements """ fn = os.path.join(datadir, 'typeerror1.yaml') with self.assertRaises(ValueError): SchemaLoader(fn) fn = os.path.join(datadir, 'typeerror2.yaml') with self.assertRaises(ValueError): SchemaLoader(fn) fn = os.path.join(datadir, 'typeerror3.yaml') with self.assertRaises(ValueError): SchemaLoader(fn) fn = os.path.join(datadir, 'typeerror4.yaml') with self.assertRaises(ValueError): SchemaLoader(fn)
def test_multi_key(self): """ Multiple keys are not supported """ fn = env.input_path('loadererror6.yaml') with self.assertRaises( ValueError, msg='Multiple keys/identifiers not allowed') as e: _ = SchemaLoader(fn).resolve() self.assertIn('multiple keys/identifiers not allowed', str(e.exception)) fn = env.input_path('loadererror7.yaml') with self.assertRaises(ValueError, msg="Two or more keys are not allowed") as e: _ = SchemaLoader(fn).resolve() self.assertIn('multiple keys/identifiers not allowed', str(e.exception))
def test_no_inverse_domain(self): with self.assertRaises(ValueError) as e: env.generate_single_file('issue_18_error3.yaml', lambda: as_yaml(SchemaLoader(env.input_path('issue_18_error3.yaml')).resolve()), value_is_returned=True) self.assertEqual("Unable to determine the range of slot `s1'. Its inverse (s2) has no declared domain", str(e.exception).strip())
def test_inverse_mismatch(self): """ Test error detection when inverses don't match """ with self.assertRaises(ValueError) as e: env.generate_single_file('issue_18_error1.yaml', lambda: as_yaml(SchemaLoader(env.input_path('issue_18_error1.yaml')).resolve()), value_is_returned=True) self.assertEqual('Slot s1.inverse (s2) does not match slot s2.inverse (s3)', str(e.exception).strip())
def eval_synopsis(self, base_name: str, source: Optional[str] = None) -> None: schema = SchemaLoader(source if source else env.input_path(base_name + '.yaml'), importmap=env.import_map) schema.resolve() self.summary = schema.synopsis.summary() self.env.generate_single_file( base_name + '.errs', lambda: '\n'.join(schema.synopsis.errors()), value_is_returned=True) self.env.generate_single_file(base_name + '.synopsis', lambda: self.summary, value_is_returned=True)
def test_undefined_subset(self): """ Throw an error on an undefined subset reference """ fn = env.input_path('loadererror11.yaml') with self.assertRaises(ValueError, msg="Subset references must be valid") as e: _ = SchemaLoader(fn).resolve() self.assertIn('loadererror11.yaml", line 22, col 16', str(e.exception))
def eval_loader(self, base_name: str, logger: Optional[logging.Logger] = None, source: Optional[str] = None) -> None: loader = SchemaLoader(source or self.env.input_path(base_name + '.yaml'), logger=logger) self.env.generate_single_file(base_name + '.json', lambda: as_json(loader.resolve()), filtr=json_metadata_filter, value_is_returned=True) self.env.generate_single_file( base_name + '.errs', lambda: '\n'.join(loader.synopsis.errors()), filtr=json_metadata_filter, value_is_returned=True)
def test_empty_range(self): """ A type must have either a base or a parent """ fn = env.input_path('loadererror5.yaml') with self.assertRaises(ValueError, msg="Range error should be raised") as e: _ = SchemaLoader(fn).resolve() self.assertIn('loadererror5.yaml", line 9, col 3', str(e.exception))
class SchemaSynopsisTestCase(Base): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.loader: SchemaLoader = None def loadit(self, source: str) -> None: self.loader = SchemaLoader(source) self.loader.resolve() """ Tests for various parts of the schema synopsis file """ def eval_synopsis(self, base_name: str, *, is_sourcedir: bool = False, source: Optional[str] = None) -> None: fn = os.path.join(sourcedir if is_sourcedir else inputdir, base_name + '.yaml') if not source else source self.loadit(fn) errors = '\n'.join(self.loader.synopsis.errors()) self.eval_output(errors, base_name + '.errs') synopsis = self.loader.synopsis.summary() self.eval_output(synopsis, base_name + '.synopsis') self.assertFalse(update_all_files, "Updating base files -- rerun") def assert_warning(self, text: str) -> bool: return f'* {text}' in self.loader.synopsis.summary() def assert_error(self, text: Union[str, List[str]]) -> None: if isinstance(text, str): text = [text] self.assertEqual(text, [e.strip() for e in self.loader.synopsis.errors()]) def test_meta_synopsis(self): """ Raise a flag if the number of classes, slots, types or other elements change in the model """ self.eval_synopsis('meta', source=LOCAL_METAMODEL_YAML_FILE) def test_unitialized_domain(self): self.loadit(os.path.join(inputdir, 'synopsis1.yaml')) # Owners check no longer occurs # self.assert_error('Slot s1 has no owners') self.assert_warning('Unspecified domain: s1') def test_applyto(self): self.eval_synopsis('synopsis2')
def test_multi_domains(self): with self.redirect_logstream() as logger: env.generate_single_file('issue_18_warning1.yaml', lambda: as_yaml(SchemaLoader(env.input_path('issue_18_warning1.yaml'), logger=logger).resolve()), filtr=yaml_filter, value_is_returned=True) self.assertIn('Slot s2.inverse (s1), has multi domains (c1, c2) Multi ranges not yet implemented', logger.result)
def test_issue_167(self): """ Test extensions to the four basic types """ env.generate_single_file( 'issue_167.yaml', lambda: as_yaml( SchemaLoader(env.input_path('issue_167.yaml')).resolve()), value_is_returned=True, filtr=yaml_filter)
def test_multi_usages_2(self): """ Slot usage chain with starting alias """ schema = SchemaLoader(env.input_path('multi_usages_2.yaml')).resolve() self._eval_expected(schema, 's1', 'value', 'root_class', None, None, 'string') self._eval_expected(schema, 'child_class1_s1', 'value', 'child_class1', 's1', 's1', 'boolean') self._eval_expected(schema, 'child_class2_s1', 'value', 'child_class2', 'child_class1_s1', 's1', 'integer') self._eval_expected(schema, 'child_class3_s1', 'value', 'child_class3', 'child_class2_s1', 's1', 'integer') env.eval_single_file(env.expected_path('multi_usages_2.yaml'), as_yaml(schema), filtr=yaml_filter)
def test_mergeerror1(self): """ Test conflicting definitions path """ fn = env.input_path('mergeerror1.yaml') with self.assertRaises(ValueError) as ve: SchemaLoader(fn) self.assertEqual( "Conflicting URIs (http://example.org/schema2, http://example.org/schema1) for item: c1", str(ve.exception))
def test_issue_177_dup(self): env.generate_single_file( 'issue_177_error.yaml', lambda: as_yaml( SchemaLoader(env.input_path('issue_177_error.yaml')).resolve() ), value_is_returned=True, filtr=yaml_filter)
def test_issue_167b_yaml(self): """ Annotations yaml example """ env.generate_single_file( 'issue_167b.yaml', lambda: as_yaml( SchemaLoader(env.input_path('issue_167b.yaml'), importmap=env.import_map).resolve()), value_is_returned=True, filtr=yaml_filter)
def test_key_and_id(self): """ A slot cannot be both a key and an identifier """ fn = env.input_path('loadererror8.yaml') with self.assertRaises( ValueError, msg="A slot cannot be both a key and identifier") as e: _ = SchemaLoader(fn).resolve() self.assertIn( 'A slot cannot be both a key and identifier at the same time', str(e.exception)) fn = env.input_path('loadererror9.yaml') with self.assertRaises( ValueError, msg="A slot cannot be both a key and identifier") as e: _ = SchemaLoader(fn).resolve() self.assertIn( 'A slot cannot be both a key and identifier at the same time', str(e.exception))
def test_issue_58(self): """ Reject non NSNAME model names""" with self.assertRaises(ValueError) as ve: env.generate_single_file( 'issue_58_error1.yaml', lambda: as_yaml( SchemaLoader(env.input_path('issue_58_error1.yaml')). resolve()), value_is_returned=True) self.assertIn('issue 58: Not a valid NCName', str(ve.exception))