Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to ignore datatypes while validating #265

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading