Skip to content

Commit

Permalink
add option to ignore datatypes while validating (#265)
Browse files Browse the repository at this point in the history
* teach the `bluepysnap validate-circuit` and `bluepysnap validate-simulation`
the ability to `--ignore-datatype-errors` so that mismatches of datatypes to
the specification are ignored
  • Loading branch information
mgeplf authored Apr 18, 2024
1 parent dc2793c commit 06d9e52
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 69 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Improvements
- Added the possibility to query Edge IDs and Node IDs based on edge/node population type using query key ``population_type``

- the types conform to `node types <https://sonata-extension.readthedocs.io/en/latest/sonata_config.html#populations>`_ and `edge types <https://sonata-extension.readthedocs.io/en/latest/sonata_config.html#id4>`_ defined in the sonata specification
- teach the `bluepysnap validate-circuit` and `bluepysnap validate-simulation` the ability to `--ignore-datatype-errors` so that mismatches of datatypes to the specification are ignored


Version v3.0.1
Expand Down
26 changes: 16 additions & 10 deletions bluepysnap/circuit_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ def validate_edge_population(edges_file, name, nodes):
return []


def validate_edges_dict(edges_dict, nodes, skip_slow):
def validate_edges_dict(edges_dict, nodes, skip_slow, ignore_datatype_errors):
"""Validate an item in the "edges" list.
Args:
Expand Down Expand Up @@ -518,7 +518,9 @@ def _is_source_node_virtual(edges_dict, edge_population, nodes):
virtual = False
if pop_type == "chemical":
virtual = _is_source_node_virtual(edges_dict, name, nodes)
errors += schemas.validate_edges_schema(edges_file, pop_type, virtual)
errors += schemas.validate_edges_schema(
edges_file, pop_type, virtual, ignore_datatype_errors
)
if not skip_slow:
errors += validate_edge_population(edges_file, name, nodes)
else:
Expand All @@ -527,7 +529,7 @@ def _is_source_node_virtual(edges_dict, edge_population, nodes):
return errors


def validate_nodes_dict(nodes_dict, components):
def validate_nodes_dict(nodes_dict, components, ignore_datatype_errors):
"""Validate an item in the "nodes" list.
Args:
Expand All @@ -544,15 +546,17 @@ def validate_nodes_dict(nodes_dict, components):
nodes_file = nodes_dict["nodes_file"]

if Path(nodes_file).is_file():
errors = schemas.validate_nodes_schema(nodes_file, population["type"])
errors = schemas.validate_nodes_schema(
nodes_file, population["type"], ignore_datatype_errors
)
errors += validate_node_population(nodes_file, population, pop_name)
else:
errors.append(BluepySnapValidationError.fatal(f'Invalid "nodes_file": {nodes_file}'))

return errors


def validate_networks(config, skip_slow):
def validate_networks(config, skip_slow, ignore_datatype_errors):
"""Validate "networks" part of the config.
Acts as a starting point of validation.
Expand All @@ -566,15 +570,17 @@ def validate_networks(config, skip_slow):

for nodes_dict in nodes:
if "nodes_file" in nodes_dict:
errors += validate_nodes_dict(nodes_dict, components)
errors += validate_nodes_dict(nodes_dict, components, ignore_datatype_errors)
for edges_dict in config["networks"].get("edges", []):
if "edges_file" in edges_dict:
errors += validate_edges_dict(edges_dict, nodes, skip_slow)
errors += validate_edges_dict(edges_dict, nodes, skip_slow, ignore_datatype_errors)

return errors


def validate(config_file, skip_slow, only_errors=False, print_errors=True):
def validate(
config_file, skip_slow, only_errors=False, print_errors=True, ignore_datatype_errors=False
):
"""Validates Sonata circuit.
Args:
Expand All @@ -587,10 +593,10 @@ def validate(config_file, skip_slow, only_errors=False, print_errors=True):
set: set of errors, empty if no errors
"""
config = Parser.parse(load_json(config_file), str(Path(config_file).parent))
errors = schemas.validate_circuit_schema(config_file, config)
errors = schemas.validate_circuit_schema(config_file, config, ignore_datatype_errors)

if "networks" in config:
errors += validate_networks(config, skip_slow)
errors += validate_networks(config, skip_slow, ignore_datatype_errors)

if _check_partial_circuit_config(config):
message = (
Expand Down
54 changes: 28 additions & 26 deletions bluepysnap/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""The project's command line launcher."""

import functools
import logging

import click
Expand All @@ -22,45 +21,48 @@ def cli(verbose):
)


def circuit_validation_params(func):
"""Small helper to have shared params."""

@click.argument("config_file", type=CLICK_EXISTING_FILE)
@click.option(
"--skip-slow/--no-skip-slow",
default=True,
help=(
"Skip slow checks; checking all edges refer to existing node ids, "
"edge indices are correct, etc"
),
)
@click.option("--only-errors", is_flag=True, help="Only print fatal errors (ignore warnings)")
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)

return wrapper


@cli.command()
@circuit_validation_params
def validate_circuit(config_file, skip_slow, only_errors):
@click.argument("config_file", type=CLICK_EXISTING_FILE)
@click.option(
"--skip-slow/--no-skip-slow",
default=True,
help=(
"Skip slow checks; checking all edges refer to existing node ids, "
"edge indices are correct, etc"
),
)
@click.option("--only-errors", is_flag=True, help="Only print fatal errors (ignore warnings)")
@click.option(
"--ignore-datatype-errors",
is_flag=True,
help="Ignore errors related to mismatch of datatypes: ie: float64 used instead of float32",
)
def validate_circuit(config_file, skip_slow, only_errors, ignore_datatype_errors):
"""Validate Sonata circuit based on config file.
Args:
config_file (str): path to Sonata circuit config file
skip_slow (bool): skip slow tests
only_errors (bool): only print fatal errors
ignore_datatype_errors (bool): ignore checks related to datatypes
"""
circuit_validation.validate(config_file, skip_slow, only_errors)
circuit_validation.validate(
config_file, skip_slow, only_errors, ignore_datatype_errors=ignore_datatype_errors
)


@cli.command()
@click.argument("config_file", type=CLICK_EXISTING_FILE)
def validate_simulation(config_file):
@click.option(
"--ignore-datatype-errors",
is_flag=True,
help="Ignore errors related to mismatch of datatypes: ie: float64 used instead of float32",
)
def validate_simulation(config_file, ignore_datatype_errors):
"""Validate Sonata simulation based on config file.
Args:
config_file (str): path to Sonata simulation config file
ignore_datatype_errors (bool): ignore checks related to datatypes
"""
simulation_validation.validate(config_file)
simulation_validation.validate(config_file, ignore_datatype_errors=ignore_datatype_errors)
23 changes: 12 additions & 11 deletions bluepysnap/schemas/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def _parse_path(path, join_str):
return join_str.join(error_path)


def _wrap_errors(filepath, schema_errors, join_str):
def _wrap_errors(filepath, schema_errors, join_str, ignore_datatype_errors):
"""Handles parsing of schema errors into more meaningful messages.
Also wraps all the warngings and errors to single Error instances.
Expand All @@ -55,8 +55,9 @@ def _wrap_errors(filepath, schema_errors, join_str):
if not e.path:
errors.append(e.message)
elif e.path[-1] == "datatype":
path = _parse_path(list(e.path)[:-1], join_str)
warnings.append(f"incorrect datatype '{e.instance}' for '{path}': {e.message}")
if not ignore_datatype_errors:
path = _parse_path(list(e.path)[:-1], join_str)
warnings.append(f"incorrect datatype '{e.instance}' for '{path}': {e.message}")
else:
if e.schema_path[-1] in e.schema.get("messages", {}):
path = _parse_path(e.path, join_str)
Expand Down Expand Up @@ -165,7 +166,7 @@ def get_dataset_dtype(item):
return properties


def validate_simulation_schema(path, config):
def validate_simulation_schema(path, config, ignore_datatype_errors):
"""Validates a simulation config against a schema.
Args:
Expand All @@ -177,10 +178,10 @@ def validate_simulation_schema(path, config):
"""
errors = _validate_schema_for_dict(_parse_schema("simulation"), config)

return _wrap_errors(path, errors, ".")
return _wrap_errors(path, errors, ".", ignore_datatype_errors)


def validate_circuit_schema(path, config):
def validate_circuit_schema(path, config, ignore_datatype_errors):
"""Validates a circuit config against a schema.
Args:
Expand All @@ -192,10 +193,10 @@ def validate_circuit_schema(path, config):
"""
errors = _validate_schema_for_dict(_parse_schema("circuit"), config)

return _wrap_errors(path, errors, ".")
return _wrap_errors(path, errors, ".", ignore_datatype_errors)


def validate_nodes_schema(path, nodes_type):
def validate_nodes_schema(path, nodes_type, ignore_datatype_errors):
"""Validates a nodes file against a schema.
Args:
Expand All @@ -210,10 +211,10 @@ def validate_nodes_schema(path, nodes_type):

errors = _validate_schema_for_dict(_parse_schema("node", nodes_type), nodes_h5_dict)

return _wrap_errors(path, errors, "/")
return _wrap_errors(path, errors, "/", ignore_datatype_errors)


def validate_edges_schema(path, edges_type, virtual):
def validate_edges_schema(path, edges_type, virtual, ignore_datatype_errors):
"""Validates an edges file against a schema.
Args:
Expand All @@ -232,7 +233,7 @@ def validate_edges_schema(path, edges_type, virtual):

errors = _validate_schema_for_dict(_parse_schema("edge", edges_type), edges_h5_dict)

return _wrap_errors(path, errors, "/")
return _wrap_errors(path, errors, "/", ignore_datatype_errors)


def _resolve_types(resolver, types):
Expand Down
4 changes: 2 additions & 2 deletions bluepysnap/simulation_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def validate_config(config):
return [error for section in sorted(VALIDATORS) for error in VALIDATORS[section](config)]


def validate(config_file, print_errors=True):
def validate(config_file, print_errors=True, ignore_datatype_errors=False):
"""Validate Sonata simulation config.
Args:
Expand All @@ -502,7 +502,7 @@ def validate(config_file, print_errors=True):
set: set of errors, empty if no errors
"""
config = _parse_config(config_file)
errors = schemas.validate_simulation_schema(config_file, config)
errors = schemas.validate_simulation_schema(config_file, config, ignore_datatype_errors)

config = _add_validation_parameters(config, config_file)
errors += validate_config(config)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_schema_validation_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def _validate(config):
Schema mocked to not have to parse it from file for every validation.
"""
return test_module.validate_circuit_schema(CONFIG_FILE, config)
return test_module.validate_circuit_schema(CONFIG_FILE, config, ignore_datatype_errors=False)


def _remove_from_config(config, to_remove):
Expand Down
Loading

0 comments on commit 06d9e52

Please sign in to comment.