Repository: BlueBrain/snap (language: python)

Issue:
Fix tests with new libsonata
Opening as draft until a version of libsonata is released and the skipped tests are restored.


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 20e3d621..e99e5aa7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,7 +6,6 @@ Version v1.0.0
 
 New Features
 ~~~~~~~~~~~~
-- First version of the Nexus Forge integration
 - jsonschema based validation of h5 files and circuit configuration
   - checks for required attributes and data types of attributes (in h5 files)
 
diff --git a/README.rst b/README.rst
index 4329f6fe..bd96b8fd 100644
--- a/README.rst
+++ b/README.rst
@@ -73,19 +73,6 @@ Or a python free function:
     errors = validate("my/circuit/path/circuit_config.json")
 
 
-Nexus Forge integration
-~~~~~~~~~~~~~~~~~~~~~~~
-
-There is also a `Nexus Forge <https://github.com/BlueBrain/nexus-forge>`__ integration in Blue brain SNAP.
-It is meant to help users search and acquire data from Nexus and instantiate the acquired data.
-
-To use it, all that is needed, is to instantiate the Nexus Helper class:
-
-.. code-block:: python3
-
-    from bluepysnap.nexus import NexusHelper
-    nexus = NexusHelper(nexus_access_token, bucket)
-
 Acknowledgements
 ----------------
 
diff --git a/bluepysnap/nexus/__init__.py b/bluepysnap/nexus/__init__.py
deleted file mode 100644
index e775d25f..00000000
--- a/bluepysnap/nexus/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2022, EPFL/Blue Brain Project
-
-# This file is part of BlueBrain SNAP library <https://github.com/BlueBrain/snap>
-
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License version 3.0 as published
-# by the Free Software Foundation.
-
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""Nexus-forge API integration."""
-from .core import NexusHelper
diff --git a/bluepysnap/nexus/connector.py b/bluepysnap/nexus/connector.py
deleted file mode 100644
index 1ec068ff..00000000
--- a/bluepysnap/nexus/connector.py
+++ /dev/null
@@ -1,200 +0,0 @@
-# Copyright (c) 2022, EPFL/Blue Brain Project
-
-# This file is part of BlueBrain SNAP library <https://github.com/BlueBrain/snap>
-
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License version 3.0 as published
-# by the Free Software Foundation.
-
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""Implementation of Nexus connector for the kgforge API."""
-import logging
-from pathlib import Path
-
-L = logging.getLogger(__name__)
-
-PROJECTS_NAMESPACE = "https://bbp.epfl.ch/nexus/v1/projects/"
-USERS_NAMESPACE = "https://bbp.epfl.ch/nexus/v1/realms/bbp/users/"
-
-# Keys that need to be prefixed with the nexus namespace.
-# FIXME: should it be managed automatically in kgforge.core.archetypes.store.rewrite_sparql?
-_NEXUS_KEYS = {
-    "constrainedBy",
-    "createdAt",
-    "createdBy",
-    "deprecated",
-    "incoming",
-    "outgoing",
-    "project",
-    "schemaProject",
-    "rev",
-    "self",
-    "updatedAt",
-    "updatedBy",
-}
-
-_DATE_KEYS = {
-    "createdAt",
-    "updatedAt",
-}
-
-NAMESPACE_MAPPING = {
-    "createdBy": USERS_NAMESPACE,
-    "updatedBy": USERS_NAMESPACE,
-    "project": PROJECTS_NAMESPACE,
-}
-
-DATETIME_SUFFIX = "^^xsd:dateTime"
-
-
-def _build_search_filters(type_, filters):
-    """Build search filters in the format expected by nexusforge.
-
-    Args:
-        type_ (str): Resource type (e.g., ``"DetailedCircuit"``).
-        filters (dict): Search filters to use.
-
-    Returns:
-        dict: Search filters in the format expected by nexusforge.
-    """
-    search_filters = {}
-
-    def add_prefix_suffix(key, value):
-        # Omit non-string values (concerns at least keys: 'rev', 'deprecated')
-        if not isinstance(value, str):
-            return value
-
-        fixed_value = value
-        namespace = NAMESPACE_MAPPING.get(key, "")
-
-        if not fixed_value.startswith(namespace):
-            fixed_value = namespace + fixed_value
-
-        if key in _DATE_KEYS and not fixed_value.endswith(DATETIME_SUFFIX):
-            fixed_value += DATETIME_SUFFIX
-
-        return fixed_value
-
-    if type_:
-        search_filters["type"] = type_
-
-    for k, v in filters.items():
-        if k in _NEXUS_KEYS:
-            search_filters[f"_{k}"] = add_prefix_suffix(k, v)
-        else:
-            search_filters[k] = v
-
-    return search_filters
-
-
-class NexusConnector:
-    """Handles communication with Nexus."""
-
-    def __init__(self, forge, debug=False):
-        """Instantiate a new NexusConnector.
-
-        Args:
-            forge (KnowledgeGraphForge): A KnowledgeGraphForge instance.
-            debug (bool): A flag that enables more verbose output.
-        """
-        self._forge = forge
-        self._debug = debug
-
-    def search(self, type_, filters, **kwargs):
-        """Search for resources in Nexus.
-
-        Args:
-            type_ (str): Resource type (e.g., ``"DetailedCircuit"``).
-            filters (dict): Search filters to use.
-            kwargs (dict): See KnowledgeGraphForge.search.
-
-        Returns:
-            list: An array of found (kgforge.core.Resource) resources.
-        """
-        search_filters = _build_search_filters(type_, filters)
-        kwargs["debug"] = kwargs.get("debug", self._debug)
-        kwargs["search_in_graph"] = kwargs.get("search_in_graph", False)
-
-        return self._forge.search(search_filters, **kwargs)
-
-    def query(self, query, **kwargs):
-        """Query resources using SparQL as defined in KnowledgeGraphForge.
-
-        Args:
-            query (str): Query string to be passed to KnowledgeGraphForge.sparql.
-            kwargs (dict): See KnowledgeGraphForge.sparql.
-
-        Returns:
-            list: An array of found (kgforge.core.Resource) resources.
-        """
-        kwargs["debug"] = kwargs.get("debug", self._debug)
-        return self._forge.sparql(query, **kwargs)
-
-    def get_resource_by_id(self, resource_id, **kwargs):
-        """Fetch a resource based on its ID.
-
-        Args:
-            resource_id (str): ID of a Nexus resource.
-            kwargs (dict): See KnowledgeGraphForge.retrieve.
-
-        Returns:
-            kgforge.core.Resource: Desired resource.
-        """
-        kwargs["cross_bucket"] = kwargs.get("cross_bucket", True)
-        return self._forge.retrieve(resource_id, **kwargs)
-
-    def get_resources_by_query(self, query, **kwargs):
-        """Query for resources and fetch them.
-
-        Args:
-            query (str): SparQL query string.
-            kwargs (dict): See KnowledgeGraphForge.sparql.
-
-        Returns:
-            list: An array of found (kgforge.core.Resource) resources.
-        """
-        result = self.query(query, **kwargs)
-        # TODO: execute requests concurrently, or pass a list of ids if possible,
-        #  to avoid calling the nexus endpoint for each resource.
-        return [self.get_resource_by_id(r.id) for r in result]
-
-    def get_resources(self, resource_type, resource_filter=None, **kwargs):
-        """Search for resources and fetch them.
-
-        Args:
-            resource_type (str): Resource type (e.g., ``"DetailedCircuit"``).
-            resource_filter (dict): Search filters to use.
-            kwargs (dict): See KnowledgeGraphForge.search.
-
-        Returns:
-            list: An array of found (kgforge.core.Resource) resources.
-        """
-        resource_filter = resource_filter or {}
-        kwargs["limit"] = kwargs.get("limit", 100)
-        resources = self.search(resource_type, resource_filter, **kwargs)
-
-        return [self.get_resource_by_id(r.id) for r in resources]
-
-    def download_resource(self, resource, path):
-        """Download a resource.
-
-        Args:
-            resource (kgforge.core.Resource): A downloadable resource.
-            path (str): The path to the directory into which the data is downloaded.
-        """
-        file_path = Path(path, resource.name)
-        if file_path.is_file():
-            L.warning("File %s already exists, not downloading...", file_path)
-            return
-        if resource.type == "DataDownload" and hasattr(resource, "contentUrl"):
-            self._forge.download(resource, "contentUrl", path)
-        else:
-            raise RuntimeError(f"resource {resource.type} can not be downloaded.")
diff --git a/bluepysnap/nexus/core.py b/bluepysnap/nexus/core.py
deleted file mode 100644
index 5ace33cf..00000000
--- a/bluepysnap/nexus/core.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright (c) 2022, EPFL/Blue Brain Project
-
-# This file is part of BlueBrain SNAP library <https://github.com/BlueBrain/snap>
-
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License version 3.0 as published
-# by the Free Software Foundation.
-
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""Nexus-forge API integration."""
-import logging
-
-from kgforge.core import KnowledgeGraphForge
-
-from bluepysnap.nexus.connector import NexusConnector
-from bluepysnap.nexus.factory import EntityFactory
-
-L = logging.getLogger(__name__)
-NEXUS_CONFIG_DEFAULT = (
-    "https://raw.githubusercontent.com/BlueBrain/nexus-forge/"
-    "master/examples/notebooks/use-cases/prod-forge-nexus.yml"
-)
-
-
-class NexusHelper:
-    """The "main" class for the nexus-forge integration."""
-
-    def __init__(self, bucket, token, nexus_config=None, debug=False, **kwargs):
-        """Instantiate a new NexusHelper class.
-
-        Args:
-            bucket (str): Name of the bucket to use (as: ``"ORGANIZATON/PROJECT"``).
-            token (str): A base64 encoded Nexus access token.
-            nexus_config (str): Path to the nexus config to use.
-            debug (bool): A flag that enables more verbose output.
-            kwargs (dict): See KnowledgeGraphForge.
-        """
-        self._forge = KnowledgeGraphForge(
-            nexus_config or NEXUS_CONFIG_DEFAULT,
-            bucket=bucket,
-            token=token,
-            debug=debug,
-            **kwargs,
-        )
-        self._connector = NexusConnector(forge=self._forge, debug=debug)
-        self._factory = EntityFactory(helper=self, connector=self._connector)
-
-    @property
-    def factory(self):
-        """:py:class:`~bluepysnap.nexus.factory.EntityFactory` instance creating the entities."""
-        return self._factory
-
-    def get_entity_by_id(self, resource_id, tool=None, **kwargs):
-        """Retrieve and return a single entity based on the id.
-
-        Args:
-            resource_id (str): ID of a Nexus resource.
-            tool (str): Name of the tool to open the resource with, or None to use the default tool
-                        (see :py:class:`~bluepysnap.nexus.factory.EntityFactory.open`).
-            kwargs (dict): See KnowledgeGraphForge.retrieve.
-
-        Returns:
-            Entity: Desired resource wrapped as an entity.
-        """
-        resource = self._connector.get_resource_by_id(resource_id, tool=tool, **kwargs)
-        return self._factory.open(resource, tool=tool)
-
-    def get_entities_by_query(self, query, tool=None, **kwargs):
-        """Retrieve and return a list of entities based on a SPARQL query.
-
-        Args:
-            query (str): Query string to be passed to KnowledgeGraphForge.sparql
-            tool (str): Name of the tool to open the resource with, or None to use the default tool
-                        (see :py:class:`~bluepysnap.nexus.factory.EntityFactory.open`).
-            kwargs (dict): See KnowledgeGraphForge.sparql.
-
-        Returns:
-            list: An array of found entities (py:class:~bluepysnap.nexus.entity.Entity`).
-        """
-        resources = self._connector.get_resources_by_query(query, tool=tool, **kwargs)
-        return [self._factory.open(r, tool=tool) for r in resources]
-
-    def get_entities(self, type_, filters=None, tool=None, **kwargs):
-        """Retrieve and return a list of entities based on the resource type and a filter.
-
-        Args:
-            type_ (str): Resource type (e.g., ``"DetailedCircuit"``).
-            filters (dict): Search filters to use.
-            tool (str): Name of the tool to open the resource with, or None to use the default tool
-                        (see :py:class:`~bluepysnap.nexus.factory.EntityFactory.open`).
-            kwargs (dict): See KnowledgeGraphForge.search.
-
-        Returns:
-            list: An array of found (kgforge.core.Resource) resources.
-
-        Examples:
-            >>> helper.get_entities(
-            ...     "DetailedCircuit",
-            ...     {"brainLocation": {"brainRegion": {"label": "Thalamus"}}},
-            ...     tool="snap",
-            ...     limit=10)
-        """
-        resources = self._connector.get_resources(type_, resource_filter=filters, **kwargs)
-        return [self._factory.open(r, tool=tool) for r in resources]
-
-    def as_dataframe(self, data, store_metadata=True, **kwargs):
-        """Return a pandas dataframe representing the list of entities.
-
-        Args:
-            data (list): List of :py:class:`~bluepysnap.nexus.entity.Entity` objects.
-            store_metadata(bool): A flag indicating whether or not to include metadata in the
-                                  output.
-            kwargs (dict): See KnowledgeGraphForge.as_dataframe.
-
-        Returns:
-            pandas.DataFrame: A dataframe containing the data of the entity list.
-        """
-        data = [e.resource for e in data]
-        return self._forge.as_dataframe(data, store_metadata=store_metadata, **kwargs)
-
-    def to_dict(self, entity, store_metadata=True, **kwargs):
-        """Return a dictionary or a list of dictionaries representing the entities.
-
-        Args:
-            entity (Entity): A single entity.
-            store_metadata(bool): A flag indicating whether or not to include metadata in the
-                                  output.
-            kwargs (dict): See KnowledgeGraphForge.as_json.
-
-        Returns:
-            dict: A dictionary containing the data of the entity.
-        """
-        return self._forge.as_json(entity.resource, store_metadata=store_metadata, **kwargs)
-
-    def reopen(self, entity, tool=None):
-        """Return a new entity to be opened with a different tool.
-
-        Args:
-            entity (Entity): Entity to be opened.
-            tool (str): Name of the tool to open the resource with, or None to use the default tool
-                        (see :py:class:`~bluepysnap.nexus.factory.EntityFactory.open`).
-
-        Returns:
-            Entity: An entity binding the resource and the opener.
-        """
-        return self._factory.open(entity.resource, tool=tool)
diff --git a/bluepysnap/nexus/entity.py b/bluepysnap/nexus/entity.py
deleted file mode 100644
index e179ca45..00000000
--- a/bluepysnap/nexus/entity.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# Copyright (c) 2022, EPFL/Blue Brain Project
-
-# This file is part of BlueBrain SNAP library <https://github.com/BlueBrain/snap>
-
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License version 3.0 as published
-# by the Free Software Foundation.
-
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""Classes that implement the resource and entity handling."""
-from functools import partial
-from pathlib import Path
-
-from kgforge.core import Resource
-from lazy_object_proxy import Proxy
-from more_itertools import always_iterable
-
-# user defined or tmp would be better
-DOWNLOADED_CONTENT_PATH = Path(".downloaded_content")
-
-
-class ResolvingResource:
-    """Class implementing traversing the resources attributes."""
-
-    def __init__(self, resource, retriever=None):
-        """Instantiate a new wrapper class.
-
-        Wraps kgforge Resource object and provides easier access (traversing) to linked objects
-        and store metadata.
-
-        Args:
-            resource (kgforge.core.Resource): The wrapped resource.
-            retriever (callable): A function implementing the communication with Nexus.
-        """
-        self._wrapped = resource
-        self._retriever = retriever
-
-    @property
-    def wrapped(self):
-        """The wrapped resource."""
-        return self._wrapped
-
-    def _sync_resource(self):
-        """Retrieve the resource if it's not synchronized."""
-        if (
-            self._retriever
-            and getattr(self._wrapped, "_synchronized", None) is False
-            and hasattr(self._wrapped, "id")
-        ):
-            self._wrapped = self._retriever(self._wrapped.id)
-            assert self._wrapped._synchronized is True  # pylint:disable = protected-access
-            return True
-        return False
-
-    def __getattr__(self, name):
-        """Get an attribute from the metadata or from the wrapped resource.
-
-        It could call the retriever if the resource is not synchronized.
-        """
-        # ignore private attributes to avoid unnecessary lookups (for example using notebooks)
-        if name.startswith("_"):
-            raise AttributeError(f"{type(self).__name__!r} object has no attribute {name!r}")
-
-        # get the attribute from _store_metadata
-        _name = "_" + name
-        meta = getattr(self._wrapped, "_store_metadata", None)
-        if meta and _name in meta:
-            return meta[_name]
-
-        if not hasattr(self._wrapped, name):
-            self._sync_resource()
-
-        try:
-            result = getattr(self._wrapped, name)
-        except AttributeError as error:
-            raise AttributeError(
-                f"{type(self).__name__!r} object has no attribute {name!r}"
-            ) from error
-
-        # In some cases 'distribution' contains list, in some cases 'DataDownload'.
-        # In either case, don't wrap it as it raises an error with kgforge.
-        if isinstance(result, Resource) and (
-            not hasattr(result, "type") or result.type != "DataDownload"
-        ):
-            # Wrap the Resource to resolve nested attributes.
-            # The retrieved resource is not saved to avoid modifying the wrapped resource.
-            result = ResolvingResource(result, retriever=self._retriever)
-
-        return result
-
-    def __repr__(self):
-        """Proxies the __repr__ call to the wrapped resource."""
-        return self._wrapped.__repr__()
-
-
-class Entity:
-    """Implements the instantiation and downloading of a resource."""
-
-    def __init__(self, resource: Resource, helper=None, connector=None, opener=None):
-        """Instantiate a new entity.
-
-        Args:
-            resource (kgforge.core.Resource): The resource to be wrapped.
-            helper (NexusHelper): NexusHelper instance.
-            connector (NexusConnector): Connector instance.
-            opener (callable): A function used to open the instance associated to the resource.
-        """
-        if connector is None:
-            retriever = downloader = None
-        else:
-            retriever = connector.get_resource_by_id
-            downloader = connector.download_resource
-
-        self._helper = helper
-        self._connector = connector
-        self._resolving_resource = ResolvingResource(resource, retriever=retriever)
-        self._instance = Proxy(partial(opener, self)) if opener else None
-        self._downloader = downloader
-
-    @property
-    def resource(self):
-        """The wrapped resource."""
-        return self._resolving_resource.wrapped
-
-    @property
-    def instance(self):
-        """The instantiated object."""
-        return self._instance
-
-    def download(self, items=None, path=None):
-        """Downloads the wrapped resource.
-
-        Args:
-            items (list, kgforge.core.Resource): Item(s) to download.
-            path (str): The path to the directory into which the data is downloaded.
-        """
-        path = path or DOWNLOADED_CONTENT_PATH
-        items = always_iterable(items or self.resource.distribution)
-
-        for item in items:
-            self._downloader(item, path)
-
-    def to_dict(self, store_metadata=True):
-        """Returns the entity as a dictionary.
-
-        Args:
-            store_metadata(bool): A flag indicating whether or not to include metadata in the
-                                  output.
-
-        Returns:
-            dict: A dictionary containing the data of the entity.
-        """
-        return self._helper.to_dict(self, store_metadata=store_metadata)
-
-    def __repr__(self):
-        """Overwrite the default __repr__ implementation."""
-        resource_id = getattr(self._resolving_resource, "id", None)
-        resource_type = getattr(self._resolving_resource, "type", None)
-        instance_type = type(self._instance).__name__
-        return (
-            f"Entity(resource_id=<{resource_id}>,"
-            f" resource_type=<{resource_type}>,"
-            f" instance_type=<{instance_type}>)"
-        )
-
-    def __getattr__(self, name):
-        """Proxy getter requests to the wrapped resource."""
-        try:
-            return getattr(self._resolving_resource, name)
-        except AttributeError as error:
-            raise AttributeError(
-                f"{type(self).__name__!r} object has no attribute {name!r}"
-            ) from error
diff --git a/bluepysnap/nexus/factory.py b/bluepysnap/nexus/factory.py
deleted file mode 100644
index c1807fea..00000000
--- a/bluepysnap/nexus/factory.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (c) 2022, EPFL/Blue Brain Project
-
-# This file is part of BlueBrain SNAP library <https://github.com/BlueBrain/snap>
-
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License version 3.0 as published
-# by the Free Software Foundation.
-
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""Functions and classes used for instantiating Nexus resources."""
-import logging
-from collections import defaultdict
-from functools import partial
-
-from kgforge.core import Resource
-from more_itertools import all_equal, always_iterable, first
-
-from bluepysnap.nexus import tools
-from bluepysnap.nexus.entity import Entity
-
-L = logging.getLogger(__name__)
-
-
-class EntityFactory:
-    """Factory class for instantiating Nexus resources."""
-
-    def __init__(self, helper, connector):
-        """Instantiate a new EntityFactory.
-
-        Args:
-            helper (NexusHelper): NexusHelper instance.
-            connector (NexusConnector): NexusConnector instance.
-        """
-        self._helper = helper
-        self._connector = connector
-        self._function_registry = defaultdict(dict)
-        self.register("DetailedCircuit", "snap", tools.open_circuit_snap)
-        self.register("DetailedCircuit", "bluepy", tools.open_circuit_bluepy)
-        self.register("Simulation", "snap", tools.open_simulation_snap)
-        self.register("Simulation", "bluepy", tools.open_simulation_bluepy)
-        self.register("Simulation", "bglibpy", tools.open_simulation_bglibpy)
-        self.register("MorphologyRelease", "morph-tool", tools.open_morphology_release)
-        self.register(
-            [
-                "NeuronMorphology",
-                "ReconstructedCell",
-                "ReconstructedPatchedCell",
-                "ReconstructedWholeBrainCell",
-            ],
-            "neurom",
-            tools.open_morphology_neurom,
-        )
-        self.register(
-            [
-                "BrainAtlasRelease",
-                "AtlasRelease",
-            ],
-            "voxcell",
-            tools.open_atlas_voxcell,
-        )
-        self.register("EModelConfiguration", "custom-wrapper", tools.open_emodelconfiguration)
-
-    def register(self, resource_types, tool, func):
-        """Register a tool to open the given resource type.
-
-        Args:
-            resource_types (str, list): Type(s) of resources handled by tool.
-            tool (str): Name of the tool to register.
-            func (callable): Any callable accepting an entity as parameter.
-        """
-        for resource_type in always_iterable(resource_types):
-            L.info("Registering tool %s for resource type %s", tool, resource_type)
-            # The first registered tool for a type will be used as default.
-            self._function_registry[resource_type][tool] = func
-
-    def get_registered_types(self):
-        """Return the registered resource types.
-
-        Returns:
-            set: Registered resource types.
-        """
-        return set(self._function_registry.keys())
-
-    def get_available_tools(self, resource_type):
-        """Return the available tools for a given resource type.
-
-        Args:
-            resource_type (str): Type of a nexus resource.
-
-        Returns:
-            list: Available tool names.
-        """
-        return list(self._function_registry.get(resource_type, {}))
-
-    def open(self, resource: Resource, tool=None):
-        """Open the resource and return an entity (resource, proxy).
-
-        Args:
-            resource (kgforge.core.Resource): The resource to be opened.
-            tool (str): Name of the tool to open the resource with, or None to use the default tool.
-
-        Returns:
-            Entity: An entity binding the resource and the opener.
-        """
-        return Entity(
-            resource,
-            helper=self._helper,
-            connector=self._connector,
-            opener=partial(self._open_entity, tool=tool),
-        )
-
-    def _open_entity(self, entity, tool=None):
-        """Open the entity and return the associated instance."""
-        types = entity.type  # type or list of types
-        tool_functions = self._get_tool_functions(types)
-        if tool is None:
-            tool, func = first(tool_functions.items())
-            L.info("Using the default tool %s to open %s", tool, types)
-        elif tool in tool_functions:
-            func = tool_functions[tool]
-            L.info("Using the specified tool %s to open %s", tool, types)
-        else:
-            raise RuntimeError(f"Tool {tool} not found for {types}")
-
-        try:
-            if func is tools.open_emodelconfiguration:
-                # TODO: EModelConfiguration in nexus demo/emodel_pipeline
-                # only have the morphology.name, and can't be auto downloaded
-                return func(entity, self._connector)
-            return func(entity)
-        except Exception as ex:
-            raise RuntimeError(f"Unable to open {types}") from ex
-
-    def _get_tool_functions(self, types):
-        """Iterate over types and return the available functions to open the resource."""
-        available_tool_functions = {
-            resource_type: self._function_registry[resource_type]
-            for resource_type in always_iterable(types)
-            if resource_type in self._function_registry
-        }
-        if not available_tool_functions:
-            raise RuntimeError(f"No available tools to open {types}")
-        if not all_equal(available_tool_functions.values()):
-            raise RuntimeError(f"Multiple tools to open {types}")
-        # available_tool_functions contains only one item, or all the values are identical,
-        # so the first value can be returned
-        return first(available_tool_functions.values())
diff --git a/bluepysnap/nexus/tools.py b/bluepysnap/nexus/tools.py
deleted file mode 100644
index 670d54e8..00000000
--- a/bluepysnap/nexus/tools.py
+++ /dev/null
@@ -1,421 +0,0 @@
-# Copyright (c) 2022, EPFL/Blue Brain Project
-
-# This file is part of BlueBrain SNAP library <https://github.com/BlueBrain/snap>
-
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License version 3.0 as published
-# by the Free Software Foundation.
-
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-"""Some wrappers to simplify using external packages."""
-# TODO: the emodel wrappers are very tailored to the mechanims we're are storing in the notebook
-# example. We need more of the logic from bluepyemodel/model/model.py, but for that we need a
-# config, which gets added then deprecated in Nexus, according to Tanguy. Once it's not PoC, then we
-# can integrate it.
-
-import logging
-import os
-import subprocess
-from pathlib import Path
-
-from more_itertools import always_iterable
-
-from bluepysnap.nexus.entity import DOWNLOADED_CONTENT_PATH
-
-L = logging.getLogger(__name__)
-
-try:
-    from bluepyopt import ephys
-except ImportError:
-    L.warning("Need to have bluepyopt installed")
-
-try:
-    from bluepyemodel.model.model import (
-        define_distributions,
-        define_mechanisms,
-        define_morphology,
-        define_parameters,
-    )
-except ImportError:
-    L.warning("Need to have bluepyemodel installed")
-
-
-def _get_path(p):
-    return Path(p.replace("file://", ""))
-
-
-def _get_path_for_item(item, entity):
-    if hasattr(item, "atLocation") and hasattr(item.atLocation, "location"):
-        path = _get_path(item.atLocation.location)
-        if os.access(path, os.R_OK):
-            return path
-    if hasattr(item, "contentUrl"):
-        entity.download(items=item, path=DOWNLOADED_CONTENT_PATH)
-        path = DOWNLOADED_CONTENT_PATH / item.name
-        return path
-
-    return None
-
-
-def open_circuit_snap(entity):
-    """Open SNAP circuit.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        Circuit: A SNAP Circuit instance.
-    """
-    import bluepysnap
-
-    if hasattr(entity, "circuitConfigPath"):
-        config_path = _get_path(entity.circuitConfigPath.url)
-    else:
-        # TODO: we should abstain from any hard coded paths (even if partial).
-        # This was used for a demo (can also be seen elsewhere). The config path should be a
-        # property of the resource.
-        config_path = _get_path(entity.circuitBase.url) / "sonata/circuit_config.json"
-    return bluepysnap.Circuit(str(config_path))
-
-
-def open_circuit_bluepy(entity):  # pragma: no cover
-    """Open bluepy circuit.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        bluepy.Circuit: A bluepy circuit instance.
-    """
-    import bluepy  # pylint: disable=import-error
-
-    config_path = _get_path(entity.circuitBase.url) / "CircuitConfig"
-    return bluepy.Circuit(str(config_path))
-
-
-def open_simulation_snap(entity):
-    """Open SNAP simulation.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        Simulation: A SNAP simulation instance.
-    """
-    import bluepysnap
-
-    # TODO: Same as with open_circuit_snap: should abstain from hard coded paths
-    config_path = _get_path(entity.path) / "sonata/simulation_config.json"
-    return bluepysnap.Simulation(str(config_path))
-
-
-def open_simulation_bluepy(entity):  # pragma: no cover
-    """Open bluepy simulation.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        bluepy.Simulation: A bluepy simulation instance.
-    """
-    import bluepy  # pylint: disable=import-error
-
-    config_path = _get_path(entity.path) / "BlueConfig"
-    return bluepy.Simulation(str(config_path))
-
-
-def open_simulation_bglibpy(entity):  # pragma: no cover
-    """Open bluepy simulation with bglibpy.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        bglibpy.SSim: A bglibpy SSim instance.
-    """
-    from bglibpy import SSim  # pylint: disable=import-error
-
-    config_path = _get_path(entity.path) / "BlueConfig"
-    return SSim(str(config_path))
-
-
-def open_morphology_release(entity):
-    """Open morphology release with morph-tool.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        morph_tool.morphdb.MorphDB: A morphology release as a MorpDB instance.
-    """
-    from morph_tool.morphdb import MorphDB
-
-    config_path = _get_path(entity.morphologyIndex.distribution.url)
-    return MorphDB.from_neurondb(str(config_path))
-
-
-def open_emodelconfiguration(entity, connector):  # pragma: no cover
-    """Open emodel configuration.
-
-    Args:
-        entity (Entity): Entity to open.
-        connector (NexusConnector): Nexus connector instance.
-
-    Returns:
-        EModelConfiguration: EModel configuration wrapper.
-    """
-    # TODO: we need the connector here, since the
-    # morphology/SubCellularModelScript (mod file) only exists as text;
-    # it's not 'connected'/'linked' to anything in nexus
-
-    def _get_entity_by_filter(type_, filter_):
-        resources = connector.get_resources(type_, filter_)
-        assert len(resources) == 1, f"Wanted 1 entity, got {len(resources)}"
-        ret = resources[0]
-
-        def download(path):
-            connector.download_resource(ret.distribution, path)
-            return Path(path) / ret.distribution.name
-
-        ret.download = download
-
-        return ret
-
-    def _get_named_entity(type_, name):
-        return _get_entity_by_filter(type_, {"name": name})
-
-    def _get_distribution(type_, name):
-        return _get_entity_by_filter(type_, {"channelDistribution": name})
-
-    def _get_distribution_for_parameter(param):
-        if param.location.startswith("distribution_"):
-            return _get_distribution(
-                "ElectrophysiologyFeatureOptimisationChannelDistribution",
-                param.location.split("distribution_")[1],
-            )
-
-        return None
-
-    morphology = _get_named_entity("NeuronMorphology", name=entity.morphology.name)
-    mod_file = _get_named_entity("SubCellularModelScript", name=entity.mechanisms.name)
-    distributions = {p.name: _get_distribution_for_parameter(p) for p in entity.parameters}
-
-    return EModelConfiguration(
-        entity.parameters, entity.mechanisms, distributions, morphology, mod_file
-    )
-
-
-def open_morphology_neurom(entity):
-    """Open morphology with NeuroM.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        neurom.core.morphology.Morphology: A neurom Morphology instance.
-    """
-    import neurom
-
-    supported_formats = {"text/plain", "application/swc", "application/h5"}
-    unsupported_formats = set()
-
-    for item in always_iterable(entity.distribution):
-        if item.type == "DataDownload":
-            encoding_format = getattr(item, "encodingFormat", "").lower()
-            if encoding_format in supported_formats:
-                path = _get_path_for_item(item, entity)
-                if path:
-                    return neurom.io.utils.load_morphology(path)
-            if encoding_format:
-                unsupported_formats.add(encoding_format)
-
-    if unsupported_formats:
-        raise RuntimeError(f"Unsupported morphology formats: {unsupported_formats}")
-
-    raise RuntimeError("Missing morphology location")
-
-
-def open_atlas_voxcell(entity):  # pragma: no cover
-    """Open atlas with voxcell.
-
-    Args:
-        entity (Entity): Entity to open.
-
-    Returns:
-        voxcell.nexus.voxcelbrain.Atlas: A voxcell Atlas instance.
-    """
-    from voxcell.nexus.voxelbrain import Atlas  # pylint: disable=import-error
-
-    path = _get_path(entity.distribution.url)
-    return Atlas.open(str(path))
-
-
-def wrap_morphology_dataframe_as_entities(df, helper, tool=None):
-    """Wraps morphology dataframe as entities.
-
-    Args:
-        df (pandas.DataFrame): A morphology dataframe.
-        helper (NexusHelper): NexusHelper instance.
-        tool (callable): The function to use to open the morphologies.
-
-    Returns:
-        tuple: An array of entities (:py:class:`~bluepysnap.nexus.entity.Entity`).
-    """
-    from kgforge.core import Resource
-
-    from bluepysnap.nexus.entity import Entity
-
-    df = df[["name", "path"]]
-    return tuple(
-        helper.reopen(
-            Entity(
-                Resource(
-                    type="NeuronMorphology",
-                    distribution=[
-                        Resource(
-                            type="DataDownload",
-                            name=name,
-                            atLocation=Resource(location=str(path)),
-                            encodingFormat=f"application/{path.suffix.lstrip('.')}",
-                        )
-                    ],
-                )
-            ),
-            tool=tool,
-        )
-        for name, path in df.values
-    )
-
-
-class DistrWrapper:  # pragma: no cover
-    """Wrapper for distributions."""
-
-    def __init__(self, parameter, distribution):
-        """Instantiate a new DistrWrapper.
-
-        Args:
-            parameter (kgforge.core.Resource): A parameter resource.
-            distribution (kgforge.core.Resource): A distribution resource.
-        """
-        self.parameters = parameter if isinstance(parameter, list) else [parameter]
-
-        if distribution is None:
-            self.name = "uniform"
-            self.function = None
-            self.soma_ref_location = None
-        else:
-            self.name = distribution.channelDistribution
-            self.function = distribution.function
-            self.soma_ref_location = distribution.somaReferenceLocation
-
-
-class ParamWrapper:  # pragma: no cover
-    """Wrapper for parameters."""
-
-    def __init__(self, parameter):
-        """Instantiate a new ParamWrapper.
-
-        Args:
-            parameter (kgforge.core.Resource): A parameter resource.
-        """
-        self.param = parameter
-
-    def __getattr__(self, name):
-        """Wrap the attribute getter for the parameters."""
-        if name == "distribution" and not hasattr(self.param, "distribution"):
-            # check which distribution should be returned but default to 'uniform'
-            if self.param.location.startswith("distribution_"):
-                return self.param.location.split("distribution_")[1]
-            return "uniform"
-        return getattr(self.param, name)
-
-
-class EmodelMorphWrapper:  # pragma: no cover
-    """Wrapper for emodels."""
-
-    MORPHOLOGY_PATH = DOWNLOADED_CONTENT_PATH / "morphologies"
-
-    class DummyMorph:
-        """Dummy Morphology class."""
-
-        def __init__(self, path):
-            """Instantiate a new DummyMorph.
-
-            Args:
-                path (str): The path to the morphology file.
-            """
-            self.path = path
-
-    def __init__(self, morphology):
-        """Instantiate a new EmodelMorphWrapper.
-
-        Args:
-            morphology (Entity): A morphology entity.
-        """
-        self.path = morphology.download(self.MORPHOLOGY_PATH)
-        self.morphology = self.DummyMorph(str(self.path))
-
-
-class EModelConfiguration:  # pragma: no cover
-    """EModelConfiguration wrapper class."""
-
-    MECHANISM_PATH = DOWNLOADED_CONTENT_PATH / "mechanisms_source"
-
-    def __init__(self, parameters, mechanisms, distributions, morphology, mod_file):
-        """Instantiate a new EModelConfiguration.
-
-        Args:
-            parameters (list): An array of parameters.
-            mechanisms (list): An array of mechanisms.
-            distributions (list): An array of distributions.
-            morphology (Entity): A morphology entity.
-            mod_file (object): Entity-like downloadable resource
-        """
-        self._parameters = [ParamWrapper(p) for p in parameters]
-        self._mechanisms = mechanisms if isinstance(mechanisms, list) else [mechanisms]
-        self._distributions = [DistrWrapper(p, d) for p, d in distributions.items()]
-        self._morphology = EmodelMorphWrapper(morphology)
-        self._mod_file = mod_file
-
-    def _compile_mod_file(self):
-        """Get the mod file, and compile it.
-
-        Returns:
-            str: path to the downloaded mod file
-        """
-        path = self._mod_file.download(self.MECHANISM_PATH)
-        subprocess.check_call(
-            ["nrnivmodl", str(self.MECHANISM_PATH)],
-            stderr=subprocess.DEVNULL,
-            stdout=subprocess.DEVNULL,
-        )
-        return path
-
-    def build_cell_model(self):
-        """Build the cell model.
-
-        Returns:
-            bluepyopt.ephys.models.CellModel: Cell model
-        """
-        self._compile_mod_file()
-        morphology = define_morphology(self._morphology)
-        mechanisms = define_mechanisms(self._mechanisms, None)
-        distributions = define_distributions(self._distributions)
-        parameters = define_parameters(self._parameters, distributions, None)
-
-        cell_model = ephys.models.CellModel(
-            name=self._morphology.path.stem,
-            morph=morphology,
-            mechs=mechanisms,
-            params=parameters,
-        )
-
-        return cell_model
diff --git a/doc/source/api.rst b/doc/source/api.rst
index 1d9c0a2d..c280deb6 100644
--- a/doc/source/api.rst
+++ b/doc/source/api.rst
@@ -9,6 +9,5 @@ API Documentation
 
    circuits
    simulations
-   nexus
    utilities
    changelog
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 3f7f26d1..0f616588 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -6,7 +6,6 @@
    :maxdepth: 2
 
    Home <self>
-   nexusforge
    api
 
 .. |banner| image:: /_images/BlueBrainSNAP.jpg
diff --git a/doc/source/nexus.rst b/doc/source/nexus.rst
deleted file mode 100644
index 1523d94e..00000000
--- a/doc/source/nexus.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-Nexus integration
-=================
-
-.. currentmodule:: bluepysnap.nexus
-
-.. autosummary::
-   :toctree: submodules
-
-   core
-   factory
-   entity
-   connector
-   tools
diff --git a/doc/source/nexusforge.rst b/doc/source/nexusforge.rst
deleted file mode 100644
index 25fad112..00000000
--- a/doc/source/nexusforge.rst
+++ /dev/null
@@ -1,109 +0,0 @@
-Nexus Forge Integration
-=======================
-
-The Nexus Forge integration consists of the following submodules/classes:
-
-* ``bluepysnap.nexus.core.NexusHelper``
-   * The class that user would instantiate to begin getting data from Nexus
-* ``bluepysnap.nexus.connector.NexusConnector``
-   * contains core functionalities used to communicate with Nexus
-* ``bluepysnap.nexus.factory.EntityFactory``
-   * factory class that creates entities and links them to tools that they are instantiated with
-* ``bluepysnap.nexus.entity.Entity``
-   * a single item from Nexus wrapped into a class that allows traversal of linked objects
-   * can be instantiated with a tool linked by ``EntityFactory``
-* ``bluepysnap.nexus.tools``
-   * collection of functions used for instantiating nexus entities
-
-For further details, please see :ref:`api-documentation`.
-
-NexusHelper
------------
-
-To set up SNAP NexusHelper, the following is needed:
-
-* **BUCKET**: Nexus project data is acquired from (as ``"ORGANIZATION/PROJECT"``)
-* **TOKEN**: Nexus token copied from `Nexus <https://bbp.epfl.ch/nexus/web/>`_.
-
-Then it's just a matter of instantiating the helper:
-
-.. code-block:: python
-
-    from bluepysnap.nexus import NexusHelper
-
-    BUCKET = "nse/test"
-    TOKEN = "<insert_token_here>"
-    nexus = NexusHelper(BUCKET, TOKEN)
-
-    entity = nexus.get_entity_by_id('<nexus_id>')
-
-EntityFactory
--------------
-
-As mentioned above, ``EntityFactory`` links tools with entities.
-To be more exact, while creating entities, it assigns them to a tool that will be used to open/instantiate them.
-SNAP is equipped with several tools but it is also entirely possible to add new tools to the factory.
-
-Tools
-~~~~~
-
-Tools are basically functions that take only one ``Entity`` as an argument and returns an instance of a desired tool.
-Let's illuminate this with a short example:
-
-.. code-block:: python
-
-    def open_entity_with_some_tool(entity):
-        """Example of how to open entities."""
-        from pathlib import Path
-        from somelib import SomeClass
-
-        # If the entity has a reference to file path, it can be used directly:
-        if hasattr(entity, "attribute_containing_path"):
-            file_path = entity.path_to_file
-        # Otherwise, resort to downloading the resource data files:
-        else:
-            download_path = Path('/path/to/directory')
-            entity.download(items=entity.distribution[0], path=download_path)
-
-            # construct the file path
-            file_name = entity.distribution[0].name
-            file_path = download_path / file_name
-
-        # Open the entity with the desired class
-        return SomeClass(entity.name, file_path)
-
-
-Registering a Tool
-~~~~~~~~~~~~~~~~~~
-
-Before any of the custom tools can be used, they need to be registered to the ``EntityFactory`` which is responsible for creating the Entities.
-The factory instance used by ``NexusHelper`` is available through ``NexusHelper.factory``.
-
-E.g., to register a tool to open neuron morphologies:
-
-.. code-block:: python
-
-    nexus.factory.register(
-        resource_types = ['NeuronMorphology'], # list of resource types that the function can handle
-        tool = 'morph_opener',                 # arbitrary name for the tool
-        func = open_entity_with_some_tool,     # the function to open the entity with
-    )
-
-Entity
-------
-
-Instantiating
-~~~~~~~~~~~~~
-
-Now that the tool is registered, it can be linked to entities fetched from Nexus and used to instantiate them:
-
-.. code-block:: python
-
-    entities = nexus.get_entities('NeuronMorphology', tool='morph_opener', limit=1)
-    morphology = entities[0].instance
-
-Where to put the tools?
------------------------
-
-Currently available tools are located in ``bluepysnap.nexus.tools``.
-However, these tools are not automatically registered to ``EntityFactory`` unless they are also added to the ``EntityFactory.__init__`` method.
diff --git a/pyproject.toml b/pyproject.toml
index 04b8d2df..deafe145 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -82,8 +82,3 @@ extension-pkg-whitelist = [
 ignored-modules = [
     'numpy',
 ]
-
-[tool.coverage.run]
-omit = [
-    'bluepysnap/nexus/wrappers.py',
-]
diff --git a/setup.py b/setup.py
index 67420c0c..77c49de6 100644
--- a/setup.py
+++ b/setup.py
@@ -50,7 +50,6 @@ def __init__(self, *args, **kwargs):
         "morphio>=3.0.0,<4.0.0",
         "morph-tool>=2.4.3,<3.0.0",
         "neurom>=1.6",
-        "nexusforge>=0.7.0,<1.0.0",
         "numpy>=1.8",
         "pandas>=1.0.0",
         "click>=7.0",


Test patch:
diff --git a/tests/nexus/test_connector.py b/tests/nexus/test_connector.py
deleted file mode 100644
index 6113b9aa..00000000
--- a/tests/nexus/test_connector.py
+++ /dev/null
@@ -1,125 +0,0 @@
-import logging
-import os
-from unittest.mock import MagicMock
-
-import pytest
-from kgforge.core import KnowledgeGraphForge, Resource
-
-from bluepysnap.nexus import connector as test_module
-
-
-def test_nexus_connector_init():
-    forge = MagicMock()
-    connector = test_module.NexusConnector(forge=forge)
-
-    assert isinstance(connector, test_module.NexusConnector)
-
-
-def test_nexus_connector_query():
-    resources = [Resource(id="id1"), Resource(id="id2")]
-    forge = MagicMock(KnowledgeGraphForge)
-    forge.sparql.return_value = resources
-    connector = test_module.NexusConnector(forge=forge)
-
-    result = connector.query("FAKE QUERY")
-
-    assert result == resources
-    forge.sparql.assert_called_once()
-
-
-def test_nexus_connector_get_resource_by_id():
-    resource = Resource(id="id1")
-    forge = MagicMock(KnowledgeGraphForge)
-    forge.retrieve.return_value = resource
-    connector = test_module.NexusConnector(forge=forge)
-
-    result = connector.get_resource_by_id("id1")
-
-    assert result == resource
-    forge.retrieve.assert_called_once()
-
-
-def test_nexus_connector_get_resources_by_query():
-    resource = Resource(id="id1")
-    forge = MagicMock(KnowledgeGraphForge)
-    forge.sparql.return_value = [resource]
-    forge.retrieve.return_value = resource
-    connector = test_module.NexusConnector(forge=forge)
-
-    result = connector.get_resources_by_query("FAKE QUERY")
-
-    assert result == [resource]
-    forge.sparql.assert_called_once()
-    forge.retrieve.assert_called_once()
-
-
-def test_nexus_connector_get_resources():
-    resource = Resource(id="id1")
-    forge = MagicMock(KnowledgeGraphForge)
-    forge.search.return_value = [resource]
-    forge.retrieve.return_value = resource
-    connector = test_module.NexusConnector(forge=forge)
-    resource_type = "DetailedCircuit"
-    resource_filter = {}
-
-    result = connector.get_resources(resource_type, resource_filter, limit=1)
-
-    assert result == [resource]
-    forge.search.assert_called_once()
-    forge.retrieve.assert_called_once()
-
-
-def test_nexus_connector_download_resource(caplog):
-    forge = MagicMock(KnowledgeGraphForge)
-    forge.download.return_value = None
-    connector = test_module.NexusConnector(forge=forge)
-
-    class MockResource:
-        def __init__(self, type_=""):
-            self.name = ""
-            self.contentUrl = ""
-            self.type = type_
-
-    with caplog.at_level(logging.WARNING):
-        connector.download_resource(MockResource(), __file__)
-        forge.download.assert_not_called()
-        assert "already exists, not downloading..." in caplog.text
-
-    connector.download_resource(MockResource("DataDownload"), "")
-    forge.download.assert_called_once()
-
-    with pytest.raises(RuntimeError, match="can not be downloaded"):
-        connector.download_resource(MockResource("invalid_type"), "")
-
-    with pytest.raises(RuntimeError, match="can not be downloaded"):
-        resource = MockResource("DataDownload")
-        del resource.contentUrl
-        connector.download_resource(resource, "")
-
-
-@pytest.mark.parametrize(
-    "type_, filter_, expected",
-    [
-        (None, {}, {}),
-        ("test_type", {}, {"type": "test_type"}),
-        (None, {"test_filter": "test_value"}, {"test_filter": "test_value"}),
-        (None, {"deprecated": True}, {"_deprecated": True}),
-        (None, {"project": "test"}, {"_project": test_module.PROJECTS_NAMESPACE + "test"}),
-        (None, {"createdBy": "test"}, {"_createdBy": test_module.USERS_NAMESPACE + "test"}),
-        (None, {"updatedBy": "test"}, {"_updatedBy": test_module.USERS_NAMESPACE + "test"}),
-        (None, {"createdAt": "test"}, {"_createdAt": "test" + test_module.DATETIME_SUFFIX}),
-        (
-            None,
-            {"updatedBy": test_module.USERS_NAMESPACE + "test"},
-            {"_updatedBy": test_module.USERS_NAMESPACE + "test"},
-        ),
-        (
-            None,
-            {"createdAt": "test" + test_module.DATETIME_SUFFIX},
-            {"_createdAt": "test" + test_module.DATETIME_SUFFIX},
-        ),
-    ],
-)
-def test_search_builder(type_, filter_, expected):
-    result = test_module._build_search_filters(type_, filter_)
-    assert result == expected
diff --git a/tests/nexus/test_core.py b/tests/nexus/test_core.py
deleted file mode 100644
index 26e4b69f..00000000
--- a/tests/nexus/test_core.py
+++ /dev/null
@@ -1,119 +0,0 @@
-from unittest.mock import patch
-
-import pandas as pd
-from kgforge.core import Resource
-
-from bluepysnap.nexus import core as test_module
-from bluepysnap.nexus.entity import Entity
-from bluepysnap.nexus.factory import EntityFactory
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_init(mocked_forge):
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-    assert isinstance(helper, test_module.NexusHelper)
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test__factory(mocked_forge):
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-    assert isinstance(helper.factory, EntityFactory)
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_get_entity_by_id(mocked_forge):
-    mocked_forge.return_value.retrieve.return_value = Resource(id="id1", type="DetailedCircuit")
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-
-    result = helper.get_entity_by_id("id1")
-
-    assert isinstance(result, Entity)
-    assert result.id == "id1"
-    assert result.type == "DetailedCircuit"
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_get_entities_by_query(mocked_forge):
-    mocked_forge.return_value.sparql.return_value = [Resource(id="id1")]
-    mocked_forge.return_value.retrieve.return_value = Resource(id="id1", type="DetailedCircuit")
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-
-    result = helper.get_entities_by_query("FAKE QUERY")
-
-    assert isinstance(result, list)
-    assert len(result) == 1
-    assert isinstance(result[0], Entity)
-    assert result[0].id == "id1"
-    assert result[0].type == "DetailedCircuit"
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_get_entities(mocked_forge):
-    mocked_forge.return_value.search.return_value = [Resource(id="id1")]
-    mocked_forge.return_value.retrieve.return_value = Resource(id="id1", type="DetailedCircuit")
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-
-    result = helper.get_entities("DetailedCircuit")
-
-    assert isinstance(result, list)
-    assert len(result) == 1
-    assert isinstance(result[0], Entity)
-    assert result[0].id == "id1"
-    assert result[0].type == "DetailedCircuit"
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_as_dataframe(mocked_forge):
-    # KnowledgeGraphForge.as_dataframe is patched so we can only mock the result
-    df = pd.DataFrame(
-        {
-            "id": ["id1", "id2"],
-            "type": ["DetailedCircuit", "DetailedCircuit"],
-            "name": ["fake_name_1", "fake_name_2"],
-        }
-    )
-    mocked_forge.return_value.as_dataframe.return_value = df
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-    resources = [
-        Resource(id="id1", type="DetailedCircuit", name="fake_name_1"),
-        Resource(id="id2", type="DetailedCircuit", name="fake_name_2"),
-    ]
-    entities = [Entity(res) for res in resources]
-
-    result = helper.as_dataframe(entities)
-
-    assert result is df
-    mocked_forge.return_value.as_dataframe.assert_called_once_with(resources, store_metadata=True)
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_to_dict(mocked_forge):
-    # KnowledgeGraphForge.as_json is patched so we can only mock the result
-    resource_dict = {
-        "id": "id1",
-        "type": "DetailedCircuit",
-        "name": "fake_name",
-    }
-    mocked_forge.return_value.as_json.return_value = resource_dict
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-    resource = Resource(id="id1", type="DetailedCircuit", name="fake_name")
-    entity = Entity(resource)
-
-    result = helper.to_dict(entity)
-
-    assert result is resource_dict
-    mocked_forge.return_value.as_json.assert_called_once_with(resource, store_metadata=True)
-
-
-@patch(test_module.__name__ + ".KnowledgeGraphForge")
-def test_nexushelper_reopen(mocked_forge):
-    helper = test_module.NexusHelper(bucket="fake/project", token="fake_token")
-    resource = Resource(id="id1", type="DetailedCircuit", name="fake_name")
-    entity = Entity(resource)
-
-    result = helper.reopen(entity)
-    assert isinstance(result, Entity)
-    # only the wrapped resource is the same
-    assert result.resource is entity.resource
-    # while the instance is different
-    assert result.instance is not entity.instance
diff --git a/tests/nexus/test_entity.py b/tests/nexus/test_entity.py
deleted file mode 100644
index 88aee17d..00000000
--- a/tests/nexus/test_entity.py
+++ /dev/null
@@ -1,119 +0,0 @@
-from unittest.mock import MagicMock, call
-
-import pytest
-from kgforge.core import Resource
-
-from bluepysnap.nexus import entity as test_module
-
-
-def test_resolving_resource_simple():
-    resource = Resource(id="id1", type="DetailedCircuit", name="fake_name")
-
-    rr = test_module.ResolvingResource(resource)
-
-    assert isinstance(rr, test_module.ResolvingResource)
-    assert rr.name == "fake_name"
-    assert rr.__repr__() == resource.__repr__()
-
-    with pytest.raises(AttributeError, match="object has no attribute"):
-        rr._non_existent
-
-    with pytest.raises(AttributeError, match="object has no attribute"):
-        rr.non_existent
-
-
-def test_resolving_resource_nested():
-    resource = Resource(
-        id="id1",
-        type="MorphologyRelease",
-        morphologyIndex=Resource(id="id2", type="ModelReleaseIndex"),
-    )
-    # nested resource that will be returned by the mocked retriever
-    nested = Resource(id="id2", type="ModelReleaseIndex", name="nested")
-    nested._synchronized = True
-    retriever = MagicMock(return_value=nested)
-
-    rr = test_module.ResolvingResource(resource, retriever=retriever)
-
-    assert isinstance(rr, test_module.ResolvingResource)
-    assert rr.morphologyIndex.name == "nested"
-    retriever.assert_called_once_with("id2")
-
-
-def test_resolving_resource_metadata():
-    resource = Resource(
-        id="id1",
-        type="MorphologyRelease",
-    )
-    resource._store_metadata = {"_meta_attr": "meta_value"}
-
-    rr = test_module.ResolvingResource(resource)
-    assert rr.meta_attr == "meta_value"
-
-
-def test_entity():
-    resource = Resource(
-        id="id1", type="DetailedCircuit", name="fake_name", distribution="distribution"
-    )
-
-    def opener(res):
-        return f"Instance of {res.id}"
-
-    entity = test_module.Entity(resource, opener=opener)
-
-    assert isinstance(entity, test_module.Entity)
-    assert entity.resource == resource
-    assert entity.instance == "Instance of id1"
-    assert entity.id == "id1"
-    with pytest.raises(AttributeError, match="object has no attribute"):
-        entity.non_existent
-
-    assert (
-        entity.__repr__()
-        == "Entity(resource_id=<id1>, resource_type=<DetailedCircuit>, instance_type=<Proxy>)"
-    )
-
-    entity = test_module.Entity(Resource())
-    assert (
-        entity.__repr__()
-        == "Entity(resource_id=<None>, resource_type=<None>, instance_type=<NoneType>)"
-    )
-
-
-def test_entity_download():
-    # Can't download
-    download_resource_mock = MagicMock()
-    connector = MagicMock(download_resource=download_resource_mock)
-    resource = Resource(id="id1", type="DetailedCircuit", name="fake_name", distribution=None)
-    entity = test_module.Entity(resource, connector=connector)
-    entity.download()
-    connector.assert_not_called()
-
-    # downloadable item in resource.distribution, default path
-    resource = Resource(
-        id="id1", type="DetailedCircuit", name="fake_name", distribution=["fake_item"]
-    )
-    entity = test_module.Entity(resource, connector=connector)
-    entity.download()
-    download_resource_mock.assert_called_once_with("fake_item", test_module.DOWNLOADED_CONTENT_PATH)
-
-    # specify path and item to download
-    download_resource_mock.reset_mock()
-    entity.download(items=[1, 2], path="fake_path")
-    download_resource_mock.has_calls((call(1, "fake_path"), call(2, "fake_path")))
-
-
-def test_entity_to_dict():
-    resource = Resource(id="id1", type="DetailedCircuit", name="fake_name", distribution=None)
-    to_dict_mock = MagicMock()
-    helper = MagicMock(to_dict=to_dict_mock)
-    entity = test_module.Entity(resource, helper=helper)
-
-    entity.to_dict()
-    entity.to_dict(store_metadata=False)
-    to_dict_mock.has_calls(
-        (
-            call(entity, store_metadata=True),
-            call(entity, store_metadata=False),
-        )
-    )
diff --git a/tests/nexus/test_factory.py b/tests/nexus/test_factory.py
deleted file mode 100644
index 1e0cbf27..00000000
--- a/tests/nexus/test_factory.py
+++ /dev/null
@@ -1,143 +0,0 @@
-from unittest.mock import MagicMock, patch
-
-import pytest
-from kgforge.core import Resource
-from lazy_object_proxy import Proxy
-
-from bluepysnap.nexus import factory as test_module
-
-
-def test_entity_factory_init():
-    factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-
-    assert isinstance(factory, test_module.EntityFactory)
-
-
-def test_entity_factory_get_registered_types():
-    factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-
-    result = factory.get_registered_types()
-
-    assert result == {
-        "DetailedCircuit",
-        "Simulation",
-        "MorphologyRelease",
-        "NeuronMorphology",
-        "ReconstructedCell",
-        "ReconstructedPatchedCell",
-        "ReconstructedWholeBrainCell",
-        "BrainAtlasRelease",
-        "AtlasRelease",
-        "EModelConfiguration",
-    }
-
-
-def test_entity_factory_get_available_tools():
-    factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-    tools_by_type = {
-        "DetailedCircuit": ["snap", "bluepy"],
-        "Simulation": ["snap", "bluepy", "bglibpy"],
-        "MorphologyRelease": ["morph-tool"],
-        "NeuronMorphology": ["neurom"],
-        "ReconstructedPatchedCell": ["neurom"],
-        "ReconstructedWholeBrainCell": ["neurom"],
-        "BrainAtlasRelease": ["voxcell"],
-        "AtlasRelease": ["voxcell"],
-    }
-    for resource_type, tools in tools_by_type.items():
-        result = factory.get_available_tools(resource_type)
-
-        assert (resource_type, result) == (resource_type, tools)
-
-
-def test_entity_factory_open():
-    factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-    resource = Resource()
-
-    result = factory.open(resource)
-
-    assert result.resource is resource
-    # no need to patch the opener in this test,
-    # because the proxy is not resolved when calling `isinstance`
-    assert isinstance(result.instance, Proxy)
-
-
-def test_entity_factory_open_instance():
-    mocked_instance = MagicMock()
-    with patch(
-        "bluepysnap.nexus.tools.open_circuit_snap", return_value=mocked_instance
-    ) as mocked_opener:
-        factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-        resource = Resource(type="DetailedCircuit")
-
-        result = factory.open(resource, tool="snap")
-
-        assert isinstance(result.instance, Proxy)
-        assert result.instance == mocked_instance
-        mocked_opener.assert_called_once_with(result)
-
-
-def test_entity_factory_open_instance_with_default_tool():
-    mocked_instance = MagicMock()
-    with patch(
-        "bluepysnap.nexus.tools.open_circuit_snap", return_value=mocked_instance
-    ) as mocked_opener:
-        factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-        resource = Resource(type="DetailedCircuit")
-
-        result = factory.open(resource)
-
-        assert isinstance(result.instance, Proxy)
-        assert result.instance == mocked_instance
-        mocked_opener.assert_called_once_with(result)
-
-    with patch(
-        "bluepysnap.nexus.tools.open_emodelconfiguration", return_value=mocked_instance
-    ) as mocked_opener:
-        connector = MagicMock()
-        resource = Resource(type="EModelConfiguration")
-        factory = test_module.EntityFactory(helper=MagicMock(), connector=connector)
-        result = factory.open(resource)
-        assert result.instance == mocked_instance
-        mocked_opener.assert_called_once_with(result, connector)
-
-
-def test_entity_factory_open_instance_with_non_default_tool():
-    mocked_instance = MagicMock()
-    factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-    tool = MagicMock()
-    tool.return_value = mocked_instance
-
-    factory.register("TestType", "test_tool", tool)
-    resource = Resource(type="TestType")
-
-    result = factory.open(resource)
-    assert isinstance(result.instance, Proxy)
-    assert result.instance == mocked_instance
-    tool.assert_called_once_with(result)
-
-    tool.reset_mock()
-    with pytest.raises(RuntimeError, match="Tool .* not found for"):
-        result = factory.open(resource, tool="fake_tool")
-        print(result.instance)
-
-
-def test_entity_factory_fail_to_open_instance():
-    with patch("bluepysnap.nexus.tools.open_circuit_snap", side_effect=Exception("error")):
-        factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-        resource = Resource(type="DetailedCircuit")
-
-        with pytest.raises(RuntimeError, match="Unable to open"):
-            result = factory.open(resource)
-
-            assert isinstance(result.instance, Proxy)
-            print(result.instance)
-
-
-def test__get_tool_functions_errors():
-    factory = test_module.EntityFactory(helper=MagicMock(), connector=MagicMock())
-    with pytest.raises(RuntimeError, match="No available tools to open"):
-        factory._get_tool_functions(("fake_type"))
-
-    with pytest.raises(RuntimeError, match="Multiple tools to open"):
-        factory._get_tool_functions(("DetailedCircuit", "EModelConfiguration"))
diff --git a/tests/nexus/test_tools.py b/tests/nexus/test_tools.py
deleted file mode 100644
index ef0f0564..00000000
--- a/tests/nexus/test_tools.py
+++ /dev/null
@@ -1,116 +0,0 @@
-from pathlib import Path
-from unittest.mock import MagicMock, patch
-
-import pandas as pd
-import pytest
-
-import bluepysnap.nexus.tools as test_module
-from bluepysnap.nexus.entity import Entity
-
-
-def test_wrap_morphology_dataframe_as_entities():
-    df = pd.DataFrame(
-        [
-            ["fake_name_1", Path("/fake/path/1.fake")],
-            ["fake_name_2", Path("/fake/path/2.fake")],
-        ],
-        columns=["name", "path"],
-    )
-
-    def reopen(*args, **_):
-        return args[0]
-
-    helper = MagicMock(reopen=reopen)
-    res = test_module.wrap_morphology_dataframe_as_entities(df, helper)
-    assert len(res) == len(df)
-    for i, r in enumerate(res):
-        assert isinstance(r, Entity)
-        assert r.type == "NeuronMorphology"
-        assert len(r.distribution) == 1
-        assert r.distribution[0].name == df.iloc[i]["name"]
-        assert r.distribution[0].type == "DataDownload"
-        assert r.distribution[0].encodingFormat == "application/fake"
-        assert r.distribution[0].atLocation.location == str(df.iloc[i]["path"])
-
-
-def test_open_circuit_snap():
-    class MockEntity:
-        def __init__(self):
-            self.circuitConfigPath = MagicMock(url="file:///fake/config/path")
-            self.circuitBase = MagicMock(url="file:///fake/base/path")
-
-    with patch("bluepysnap.Circuit") as patched:
-        entity = MockEntity()
-
-        test_module.open_circuit_snap(entity)
-        patched.assert_called_once_with("/fake/config/path")
-        patched.reset_mock()
-
-        del entity.circuitConfigPath
-        test_module.open_circuit_snap(entity)
-        patched.assert_called_once_with("/fake/base/path/sonata/circuit_config.json")
-
-
-def test_open_simulation_snap():
-    with patch("bluepysnap.Simulation") as patched:
-        entity = MagicMock()
-        entity.path = "file:///fake/path"
-        test_module.open_simulation_snap(entity)
-        patched.assert_called_once_with("/fake/path/sonata/simulation_config.json")
-
-
-def test_open_morphology_release():
-    with patch("morph_tool.morphdb.MorphDB") as patched:
-        entity = MagicMock()
-        entity.morphologyIndex.distribution.url = "file:///fake/path"
-        test_module.open_morphology_release(entity)
-        patched.from_neurondb.assert_called_once_with("/fake/path")
-
-
-def test__get_path_for_item():
-    class MockItem:
-        def __init__(self):
-            self.name = "fake_name"
-            self.contentUrl = MagicMock()
-            self.atLocation = MagicMock(location=__file__)
-
-    res = test_module._get_path_for_item(MockItem(), MagicMock())
-    assert str(res) == __file__
-
-    item = MockItem()
-    entity = MagicMock()
-
-    del item.atLocation
-    res = test_module._get_path_for_item(item, entity)
-    assert str(res) == str(test_module.DOWNLOADED_CONTENT_PATH / "fake_name")
-    entity.download.assert_called_once_with(items=item, path=test_module.DOWNLOADED_CONTENT_PATH)
-
-    del item.contentUrl
-    res = test_module._get_path_for_item(item, entity)
-    assert res is None
-
-
-def test_open_morphology_neurom():
-    entity = MagicMock()
-    entity.distribution = [MagicMock(type="DataDownload", encodingFormat="application/swc")]
-    with patch("neurom.io.utils.load_morphology") as neurom_patched:
-        neurom_patched.return_value = "done"
-
-        with patch(
-            test_module.__name__ + "._get_path_for_item", MagicMock(return_value="/fake/path")
-        ) as mock_get_path:
-            result = test_module.open_morphology_neurom(entity)
-            assert result == "done"
-            mock_get_path.assert_called_once()
-
-        neurom_patched.assert_called_once_with("/fake/path")
-
-    entity.distribution = [MagicMock(type="fake_type")]
-
-    with pytest.raises(RuntimeError, match="Missing morphology location"):
-        test_module.open_morphology_neurom(entity)
-
-    entity.distribution = [MagicMock(type="DataDownload", encodingFormat="unsupported/fmt")]
-
-    with pytest.raises(RuntimeError, match="Unsupported morphology formats: {'unsupported/fmt'}"):
-        test_module.open_morphology_neurom(entity)