From e846ef5a94a146d9ddc21e699e77cd8bd735b082 Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 29 Nov 2023 15:32:14 +0100 Subject: [PATCH 1/3] fix missing abstract methods in entity linker (#363) --- .../entity_linking/entity_linker_elastic.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py b/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py index 92b51430..03471b62 100644 --- a/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py +++ b/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py @@ -11,8 +11,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with Blue Brain Nexus Forge. If not, see . -from typing import Callable, Dict, Optional +from pathlib import Path +from typing import Callable, Dict, Optional, Union, Any +from kgforge.core.commons.execution import not_supported from kgforge.specializations.mappers import DictionaryMapper from kgforge.specializations.mappings import DictionaryMapping from kgforge.specializations.resolvers.entity_linking import EntityLinker @@ -21,6 +23,15 @@ class EntityLinkerElastic(EntityLinker): + @staticmethod + def _service_from_directory(dirpath: Path, targets: Dict[str, Union[str, Dict]], + **source_config) -> Any: + raise not_supported() + + @staticmethod + def _service_from_web_service(endpoint: str, targets: Dict[str, Union[str, Dict]]) -> Any: + raise not_supported() + @property def mapping(self) -> Callable: return DictionaryMapping From 55ba0cefb67663926e359102688f6a839512692a Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 29 Nov 2023 15:41:03 +0100 Subject: [PATCH 2/3] fix missing abstract methods in entity linking sklearn (#364) --- kgentitylinkingsklearn/entity_linking_sklearn.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/kgentitylinkingsklearn/entity_linking_sklearn.py b/kgentitylinkingsklearn/entity_linking_sklearn.py index 64c38c1c..d8647e12 100644 --- a/kgentitylinkingsklearn/entity_linking_sklearn.py +++ b/kgentitylinkingsklearn/entity_linking_sklearn.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Blue Brain Nexus Forge. If not, see . from pathlib import Path -from typing import Any, Dict, List, Callable, Optional, Tuple +from typing import Any, Dict, List, Callable, Optional, Tuple, Union from kgentitylinkingsklearn import EntityLinkerServiceSkLearn from kgforge.core.commons.actions import LazyAction @@ -23,6 +23,15 @@ class EntityLinkerSkLearn(EntityLinker): + @staticmethod + def _service_from_web_service(endpoint: str, targets: Dict[str, Union[str, Dict]]) -> Any: + pass + + @staticmethod + def _service_from_store(store: 'Store', targets: Dict[str, Union[str, Dict]], + **store_config) -> Any: + pass + def __init__(self, source: str, targets: List[Dict[str, Any]], result_resource_mapping: str, **source_config) -> None: super().__init__(source, targets, result_resource_mapping, **source_config) From 2e616cd393f3415580bd267eb6466e1c3cb22bef Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 29 Nov 2023 16:11:22 +0100 Subject: [PATCH 3/3] Fix cyclic dependency between Mapping and KnowledgeGraphForge (#362) * attempt fix cyclic dependency * undo * full imports within forge * default mapper and mapping from store --- kgforge/core/archetypes/dataset_store.py | 4 +--- kgforge/core/archetypes/read_only_store.py | 2 +- kgforge/core/forge.py | 15 +++++++++------ kgforge/specializations/models/rdf_model.py | 2 +- .../specializations/resolvers/agent_resolver.py | 4 ++-- .../specializations/resolvers/demo_resolver.py | 4 ++-- .../entity_linking/entity_linker_elastic.py | 5 +++-- .../service/entity_linking_elastic_service.py | 4 ++-- .../resolvers/ontology_resolver.py | 4 ++-- kgforge/specializations/stores/bluebrain_nexus.py | 4 ++-- kgforge/specializations/stores/demo_store.py | 2 +- .../stores/sparql/sparql_service.py | 2 +- kgforge/specializations/stores/sparql_store.py | 6 +++--- tests/conftest.py | 3 ++- tests/core/archetypes/test_read_only_store.py | 3 ++- tests/core/archetypes/test_store.py | 3 ++- tests/core/commons/test_actions.py | 2 +- tests/core/conversions/conftest.py | 3 ++- tests/core/conversions/test_dataframe.py | 2 +- tests/core/conversions/test_rdf.py | 2 +- tests/core/test_forge.py | 2 +- tests/core/test_reshaping.py | 3 ++- tests/core/test_resource.py | 2 +- tests/specializations/mappers/test_mappers.py | 7 ++++--- tests/specializations/models/test_rdf_model.py | 2 +- tests/specializations/resources/test_datasets.py | 3 ++- .../stores/test_bluebrain_nexus.py | 4 ++-- 27 files changed, 54 insertions(+), 45 deletions(-) diff --git a/kgforge/core/archetypes/dataset_store.py b/kgforge/core/archetypes/dataset_store.py index 2dac7fcf..4896e0f9 100644 --- a/kgforge/core/archetypes/dataset_store.py +++ b/kgforge/core/archetypes/dataset_store.py @@ -15,15 +15,13 @@ from abc import abstractmethod, ABC from typing import Optional, Union, List, Type, Dict -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.archetypes.read_only_store import ReadOnlyStore from kgforge.core.archetypes.resolver import Resolver -from kgforge.core.archetypes.model import Model from kgforge.core.archetypes.mapping import Mapping from kgforge.core.archetypes.mapper import Mapper from kgforge.core.commons.imports import import_class from kgforge.core.conversions.json import as_json, from_json -from kgforge.core.commons.execution import not_supported from kgforge.core.wrappings import Filter diff --git a/kgforge/core/archetypes/read_only_store.py b/kgforge/core/archetypes/read_only_store.py index 390b8774..a16c70f6 100644 --- a/kgforge/core/archetypes/read_only_store.py +++ b/kgforge/core/archetypes/read_only_store.py @@ -16,7 +16,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.archetypes.model import Model from kgforge.core.archetypes.resolver import Resolver from kgforge.core.commons.attributes import repr_class diff --git a/kgforge/core/forge.py b/kgforge/core/forge.py index 401f1dfa..747fa98a 100644 --- a/kgforge/core/forge.py +++ b/kgforge/core/forge.py @@ -13,7 +13,7 @@ # along with Blue Brain Nexus Forge. If not, see . from copy import deepcopy -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Any, Callable, Dict, List, Optional, Tuple, Union, Type import numpy as np import yaml @@ -21,11 +21,12 @@ from rdflib import Graph from kgforge.core.resource import Resource -from kgforge.core.commons.files import load_file_as_byte from kgforge.core.archetypes.mapping import Mapping from kgforge.core.archetypes.model import Model from kgforge.core.archetypes.resolver import Resolver +from kgforge.core.archetypes.mapper import Mapper from kgforge.core.archetypes.store import Store +from kgforge.core.commons.files import load_file_as_byte from kgforge.core.commons.actions import LazyAction from kgforge.core.commons.dictionaries import with_defaults from kgforge.core.commons.exceptions import ResolvingError @@ -44,8 +45,6 @@ ) from kgforge.core.reshaping import Reshaper from kgforge.core.wrappings.paths import PathsWrapper, wrap_paths, Filter -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping class KnowledgeGraphForge: @@ -548,7 +547,7 @@ def mappings( @catch def mapping( - self, entity: str, source: str, type: Callable = DictionaryMapping + self, entity: str, source: str, type: Type[Mapping] = None ) -> Mapping: """ Return a Mapping object of type 'type' for a resource type 'entity' and a source. @@ -558,6 +557,8 @@ def mapping( :param type: a Mapping class :return: Mapping """ + if type is None: + type = self._store.mapping return self._model.mapping(entity, source, type) @catch @@ -565,7 +566,7 @@ def map( self, data: Any, mapping: Union[Mapping, List[Mapping]], - mapper: Callable = DictionaryMapper, + mapper: Type[Mapper] = None, na: Union[Any, List[Any]] = None, ) -> Union[Resource, List[Resource]]: """ @@ -578,6 +579,8 @@ def map( :param na: represents missing values :return: Union[Resource, List[Resource]] """ + if mapper is None: + mapper = self._store.mapper return mapper(self).map(data, mapping, na) # Reshaping User Interface. diff --git a/kgforge/specializations/models/rdf_model.py b/kgforge/specializations/models/rdf_model.py index 8f00e3d0..edf40dd5 100644 --- a/kgforge/specializations/models/rdf_model.py +++ b/kgforge/specializations/models/rdf_model.py @@ -20,7 +20,7 @@ from rdflib import URIRef, Literal from rdflib.namespace import XSD -from kgforge.core.archetypes import Mapping +from kgforge.core.archetypes.mapping import Mapping from kgforge.core.resource import Resource from kgforge.core.archetypes.store import Store from kgforge.core.archetypes.model import Model diff --git a/kgforge/specializations/resolvers/agent_resolver.py b/kgforge/specializations/resolvers/agent_resolver.py index 197cbf01..02c3eb38 100644 --- a/kgforge/specializations/resolvers/agent_resolver.py +++ b/kgforge/specializations/resolvers/agent_resolver.py @@ -19,8 +19,8 @@ from kgforge.core.commons.execution import not_supported from kgforge.core.commons.sparql_query_builder import SPARQLQueryBuilder from kgforge.core.commons.strategies import ResolvingStrategy -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping from kgforge.specializations.resolvers.store_service import StoreService diff --git a/kgforge/specializations/resolvers/demo_resolver.py b/kgforge/specializations/resolvers/demo_resolver.py index 73860d6b..e094cd76 100644 --- a/kgforge/specializations/resolvers/demo_resolver.py +++ b/kgforge/specializations/resolvers/demo_resolver.py @@ -21,8 +21,8 @@ from kgforge.core.commons.exceptions import ConfigurationError from kgforge.core.commons.execution import not_supported from kgforge.core.commons.strategies import ResolvingStrategy -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping class DemoResolver(Resolver): diff --git a/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py b/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py index 03471b62..9c7cff03 100644 --- a/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py +++ b/kgforge/specializations/resolvers/entity_linking/entity_linker_elastic.py @@ -15,8 +15,9 @@ from typing import Callable, Dict, Optional, Union, Any from kgforge.core.commons.execution import not_supported -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping + from kgforge.specializations.resolvers.entity_linking import EntityLinker from kgforge.specializations.resolvers.entity_linking.service.entity_linking_elastic_service import EntityLinkerElasticService diff --git a/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py b/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py index d16b8b25..012c318a 100644 --- a/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py +++ b/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py @@ -22,8 +22,8 @@ from kgforge.core.conversions.json import as_json from kgforge.core.resource import encode from kgforge.core.wrappings import Filter, FilterOperator -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping from kgforge.specializations.resolvers.entity_linking.service.entity_linking_service import ( EntityLinkerService, ) diff --git a/kgforge/specializations/resolvers/ontology_resolver.py b/kgforge/specializations/resolvers/ontology_resolver.py index 9320a890..82a2ac2c 100644 --- a/kgforge/specializations/resolvers/ontology_resolver.py +++ b/kgforge/specializations/resolvers/ontology_resolver.py @@ -19,8 +19,8 @@ from kgforge.core.commons.execution import not_supported from kgforge.core.commons.sparql_query_builder import SPARQLQueryBuilder from kgforge.core.commons.strategies import ResolvingStrategy -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping from kgforge.specializations.resolvers.store_service import StoreService diff --git a/kgforge/specializations/stores/bluebrain_nexus.py b/kgforge/specializations/stores/bluebrain_nexus.py index 118986d7..f68396d3 100644 --- a/kgforge/specializations/stores/bluebrain_nexus.py +++ b/kgforge/specializations/stores/bluebrain_nexus.py @@ -59,8 +59,8 @@ from kgforge.core.conversions.rdf import as_jsonld from kgforge.core.wrappings.dict import DictWrapper from kgforge.core.wrappings.paths import Filter, create_filters_from_dict -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping from kgforge.specializations.stores.nexus.service import BatchAction, Service, _error_message diff --git a/kgforge/specializations/stores/demo_store.py b/kgforge/specializations/stores/demo_store.py index 281b6925..d2a9805d 100644 --- a/kgforge/specializations/stores/demo_store.py +++ b/kgforge/specializations/stores/demo_store.py @@ -16,7 +16,7 @@ from pathlib import Path from typing import Dict, List, Optional, Union, Type, Tuple, Any from uuid import uuid4 -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.archetypes.resolver import Resolver from kgforge.core.archetypes.store import Store from kgforge.core.archetypes.mapper import Mapper diff --git a/kgforge/specializations/stores/sparql/sparql_service.py b/kgforge/specializations/stores/sparql/sparql_service.py index 5e04fa2e..dca8d07b 100644 --- a/kgforge/specializations/stores/sparql/sparql_service.py +++ b/kgforge/specializations/stores/sparql/sparql_service.py @@ -15,7 +15,7 @@ import copy from typing import Dict, List, Optional, Union, Tuple -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.commons.context import Context from kgforge.core.wrappings.dict import wrap_dict diff --git a/kgforge/specializations/stores/sparql_store.py b/kgforge/specializations/stores/sparql_store.py index cc354370..828908fd 100644 --- a/kgforge/specializations/stores/sparql_store.py +++ b/kgforge/specializations/stores/sparql_store.py @@ -15,13 +15,13 @@ import requests from typing import Dict, List, Optional, Union, Any, Type, Tuple -from kgforge.core import Resource -from kgforge.core.archetypes import Mapper +from kgforge.core.resource import Resource +from kgforge.core.archetypes.mapper import Mapper from kgforge.core.archetypes.resolver import Resolver from kgforge.core.archetypes.model import Model from kgforge.core.archetypes.dataset_store import DatasetStore from kgforge.core.commons import Context -from kgforge.specializations.mappers import DictionaryMapper +from kgforge.specializations.mappers.dictionaries import DictionaryMapper from kgforge.specializations.stores.sparql.sparql_service import SPARQLService from kgforge.core.wrappings.paths import create_filters_from_dict, Filter from kgforge.core.wrappings.dict import DictWrapper diff --git a/tests/conftest.py b/tests/conftest.py index 940262ca..d472e046 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,7 +18,8 @@ import pytest from pytest_bdd import given, parsers, then, when -from kgforge.core import Resource, KnowledgeGraphForge +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge from kgforge.core.commons.actions import Action from kgforge.core.commons.context import Context from kgforge.core.conversions.rdf import _merge_jsonld, Form diff --git a/tests/core/archetypes/test_read_only_store.py b/tests/core/archetypes/test_read_only_store.py index b22fed08..12227912 100644 --- a/tests/core/archetypes/test_read_only_store.py +++ b/tests/core/archetypes/test_read_only_store.py @@ -15,7 +15,8 @@ # Placeholder for the test suite for actions. import pytest -from kgforge.core import Resource, KnowledgeGraphForge +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge from kgforge.core.commons.exceptions import DownloadingError diff --git a/tests/core/archetypes/test_store.py b/tests/core/archetypes/test_store.py index 23b08a3f..b9359ea2 100644 --- a/tests/core/archetypes/test_store.py +++ b/tests/core/archetypes/test_store.py @@ -15,7 +15,8 @@ # Placeholder for the test suite for actions. import pytest -from kgforge.core import Resource, KnowledgeGraphForge +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge from kgforge.specializations.resources import Dataset from kgforge.core.wrappings.dict import wrap_dict import json diff --git a/tests/core/commons/test_actions.py b/tests/core/commons/test_actions.py index db94b6a5..d477dfcd 100644 --- a/tests/core/commons/test_actions.py +++ b/tests/core/commons/test_actions.py @@ -14,7 +14,7 @@ # Placeholder for the test suite for actions. -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.commons.actions import LazyAction, collect_lazy_actions, execute_lazy_actions diff --git a/tests/core/conversions/conftest.py b/tests/core/conversions/conftest.py index 6d60e224..d3817518 100644 --- a/tests/core/conversions/conftest.py +++ b/tests/core/conversions/conftest.py @@ -18,7 +18,8 @@ from urllib.parse import urljoin from urllib.request import pathname2url -from kgforge.core import KnowledgeGraphForge, Resource +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge from kgforge.core.commons.context import Context from kgforge.core.conversions.rdf import _merge_jsonld from kgforge.core.wrappings.dict import wrap_dict diff --git a/tests/core/conversions/test_dataframe.py b/tests/core/conversions/test_dataframe.py index 22b7a1be..38e4e870 100644 --- a/tests/core/conversions/test_dataframe.py +++ b/tests/core/conversions/test_dataframe.py @@ -19,7 +19,7 @@ # Test suite for conversion of resource to / from Pandas DataFrame. -from kgforge.core import Resource +from kgforge.core.resource import Resource @pytest.fixture diff --git a/tests/core/conversions/test_rdf.py b/tests/core/conversions/test_rdf.py index a4b020e6..2567071d 100644 --- a/tests/core/conversions/test_rdf.py +++ b/tests/core/conversions/test_rdf.py @@ -24,7 +24,7 @@ from rdflib import Graph, BNode, term from rdflib.namespace import RDF, Namespace -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.commons.exceptions import NotSupportedError from kgforge.core.conversions.rdf import _merge_jsonld, _resolve_iri, from_jsonld, as_jsonld, Form, as_graph, from_graph, LD_KEYS diff --git a/tests/core/test_forge.py b/tests/core/test_forge.py index 81d1e9f8..34c40066 100644 --- a/tests/core/test_forge.py +++ b/tests/core/test_forge.py @@ -16,7 +16,7 @@ import pytest -from kgforge.core import KnowledgeGraphForge +from kgforge.core.forge import KnowledgeGraphForge SCOPE = "terms" MODEL = "DemoModel" diff --git a/tests/core/test_reshaping.py b/tests/core/test_reshaping.py index e77ff269..4a000886 100644 --- a/tests/core/test_reshaping.py +++ b/tests/core/test_reshaping.py @@ -14,7 +14,8 @@ # Placeholder for the test suite for reshaping. import pytest -from kgforge.core import Resource, KnowledgeGraphForge +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge from kgforge.core.reshaping import collect_values, Reshaper diff --git a/tests/core/test_resource.py b/tests/core/test_resource.py index 7e1835e9..7da385a3 100644 --- a/tests/core/test_resource.py +++ b/tests/core/test_resource.py @@ -15,7 +15,7 @@ import pytest from pytest_bdd import given, scenarios, then, when -from kgforge.core import Resource +from kgforge.core.resource import Resource # TODO To be port to the generic parameterizable test suite for resources in test_resources.py. # DKE-135. diff --git a/tests/specializations/mappers/test_mappers.py b/tests/specializations/mappers/test_mappers.py index 0dbf0508..5d0f83a1 100644 --- a/tests/specializations/mappers/test_mappers.py +++ b/tests/specializations/mappers/test_mappers.py @@ -17,9 +17,10 @@ import pytest from contextlib import nullcontext as does_not_raise -from kgforge.core import KnowledgeGraphForge, Resource -from kgforge.specializations.mappers import DictionaryMapper -from kgforge.specializations.mappings import DictionaryMapping +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge +from kgforge.specializations.mappers.dictionaries import DictionaryMapper +from kgforge.specializations.mappings.dictionaries import DictionaryMapping @pytest.fixture diff --git a/tests/specializations/models/test_rdf_model.py b/tests/specializations/models/test_rdf_model.py index c3895c26..099169f7 100644 --- a/tests/specializations/models/test_rdf_model.py +++ b/tests/specializations/models/test_rdf_model.py @@ -14,7 +14,7 @@ import json import pytest -from kgforge.core import Resource +from kgforge.core.resource import Resource from kgforge.core.commons.exceptions import ValidationError from kgforge.specializations.models import RdfModel from tests.specializations.models.data import * diff --git a/tests/specializations/resources/test_datasets.py b/tests/specializations/resources/test_datasets.py index 0a332faa..423fe070 100644 --- a/tests/specializations/resources/test_datasets.py +++ b/tests/specializations/resources/test_datasets.py @@ -15,7 +15,8 @@ # Placeholder for the generic parameterizable test suite for resources. from typing import List -from kgforge.core import KnowledgeGraphForge, Resource +from kgforge.core.resource import Resource +from kgforge.core.forge import KnowledgeGraphForge from kgforge.specializations.resources import Dataset diff --git a/tests/specializations/stores/test_bluebrain_nexus.py b/tests/specializations/stores/test_bluebrain_nexus.py index 429fea81..91684e5a 100644 --- a/tests/specializations/stores/test_bluebrain_nexus.py +++ b/tests/specializations/stores/test_bluebrain_nexus.py @@ -22,8 +22,8 @@ import pytest from typing import Callable, Union, List -from kgforge.core import Resource -from kgforge.core.archetypes import Store +from kgforge.core.resource import Resource +from kgforge.core.archetypes.store import Store from kgforge.core.commons.context import Context from kgforge.core.conversions.rdf import _merge_jsonld from kgforge.core.wrappings.dict import wrap_dict