We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Wrong annotated return type in Registry class
https://github.com/openvinotoolkit/datumaro/blob/0d4a73d3bbe3a93585af7a0148a0e344fd1106b3/datumaro/components/environment.py#L41-L42
In the referenced code the return type of the method appears to be wrong. 

Either it should be `Iterator[str]` since iteration over a dict returns its keys which are of type `str` or the return statement should be `return iter(self.items.values())`.

When using the library with static type checkers this annotation causes type check errors. When removing the annotation, type checkers correctly infer the type `Iterator[str]`.
Wrong annotated return type in Registry class
https://github.com/openvinotoolkit/datumaro/blob/0d4a73d3bbe3a93585af7a0148a0e344fd1106b3/datumaro/components/environment.py#L41-L42
In the referenced code the return type of the method appears to be wrong. 

Either it should be `Iterator[str]` since iteration over a dict returns its keys which are of type `str` or the return statement should be `return iter(self.items.values())`.

When using the library with static type checkers this annotation causes type check errors. When removing the annotation, type checkers correctly infer the type `Iterator[str]`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `datumaro/components/environment.py`
Content:
```
1 # Copyright (C) 2020-2022 Intel Corporation
2 #
3 # SPDX-License-Identifier: MIT
4 
5 import glob
6 import importlib
7 import logging as log
8 import os.path as osp
9 from functools import partial
10 from inspect import isclass
11 from typing import Callable, Dict, Generic, Iterable, Iterator, List, Optional, Type, TypeVar
12 
13 from datumaro.components.cli_plugin import CliPlugin, plugin_types
14 from datumaro.components.format_detection import RejectionReason, detect_dataset_format
15 from datumaro.util.os_util import import_foreign_module, split_path
16 
17 T = TypeVar("T")
18 
19 
20 class Registry(Generic[T]):
21     def __init__(self):
22         self.items: Dict[str, T] = {}
23 
24     def register(self, name: str, value: T) -> T:
25         self.items[name] = value
26         return value
27 
28     def unregister(self, name: str) -> Optional[T]:
29         return self.items.pop(name, None)
30 
31     def get(self, key: str):
32         """Returns a class or a factory function"""
33         return self.items[key]
34 
35     def __getitem__(self, key: str) -> T:
36         return self.get(key)
37 
38     def __contains__(self, key) -> bool:
39         return key in self.items
40 
41     def __iter__(self) -> Iterator[T]:
42         return iter(self.items)
43 
44 
45 class PluginRegistry(Registry[Type[CliPlugin]]):
46     def __init__(
47         self, filter: Callable[[Type[CliPlugin]], bool] = None
48     ):  # pylint: disable=redefined-builtin
49         super().__init__()
50         self._filter = filter
51 
52     def batch_register(self, values: Iterable[CliPlugin]):
53         for v in values:
54             if self._filter and not self._filter(v):
55                 continue
56 
57             self.register(v.NAME, v)
58 
59 
60 class Environment:
61     _builtin_plugins = None
62 
63     @classmethod
64     def _make_filter(cls, accept, skip=None):
65         accept = (accept,) if isclass(accept) else tuple(accept)
66         skip = {skip} if isclass(skip) else set(skip or [])
67         skip = tuple(skip | set(accept))
68         return partial(cls._check_type, accept=accept, skip=skip)
69 
70     @staticmethod
71     def _check_type(t, *, accept, skip):
72         return issubclass(t, accept) and t not in skip
73 
74     def __init__(self):
75         from datumaro.components.converter import Converter
76         from datumaro.components.dataset_generator import DatasetGenerator
77         from datumaro.components.extractor import (
78             Extractor,
79             Importer,
80             ItemTransform,
81             SourceExtractor,
82             Transform,
83         )
84         from datumaro.components.launcher import Launcher
85         from datumaro.components.validator import Validator
86 
87         _filter = self._make_filter
88         self._extractors = PluginRegistry(_filter(Extractor, skip=SourceExtractor))
89         self._importers = PluginRegistry(_filter(Importer))
90         self._launchers = PluginRegistry(_filter(Launcher))
91         self._converters = PluginRegistry(_filter(Converter))
92         self._generators = PluginRegistry(_filter(DatasetGenerator))
93         self._transforms = PluginRegistry(_filter(Transform, skip=ItemTransform))
94         self._validators = PluginRegistry(_filter(Validator))
95         self._builtins_initialized = False
96 
97     def _get_plugin_registry(self, name):
98         if not self._builtins_initialized:
99             self._builtins_initialized = True
100             self._register_builtin_plugins()
101         return getattr(self, name)
102 
103     @property
104     def extractors(self) -> PluginRegistry:
105         return self._get_plugin_registry("_extractors")
106 
107     @property
108     def importers(self) -> PluginRegistry:
109         return self._get_plugin_registry("_importers")
110 
111     @property
112     def launchers(self) -> PluginRegistry:
113         return self._get_plugin_registry("_launchers")
114 
115     @property
116     def converters(self) -> PluginRegistry:
117         return self._get_plugin_registry("_converters")
118 
119     @property
120     def generators(self) -> PluginRegistry:
121         return self._get_plugin_registry("_generators")
122 
123     @property
124     def transforms(self) -> PluginRegistry:
125         return self._get_plugin_registry("_transforms")
126 
127     @property
128     def validators(self) -> PluginRegistry:
129         return self._get_plugin_registry("_validators")
130 
131     @staticmethod
132     def _find_plugins(plugins_dir):
133         plugins = []
134 
135         for pattern in ("*.py", "*/*.py"):
136             for path in glob.glob(osp.join(glob.escape(plugins_dir), pattern)):
137                 if not osp.isfile(path):
138                     continue
139 
140                 path_rel = osp.relpath(path, plugins_dir)
141                 name_parts = split_path(osp.splitext(path_rel)[0])
142 
143                 # a module with a dot in the name won't load correctly
144                 if any("." in part for part in name_parts):
145                     log.warning(
146                         "Python file '%s' in directory '%s' can't be imported "
147                         "due to a dot in the name; skipping.",
148                         path_rel,
149                         plugins_dir,
150                     )
151                     continue
152                 plugins.append(".".join(name_parts))
153 
154         return plugins
155 
156     @classmethod
157     def _get_plugin_exports(cls, module, types):
158         exports = []
159         if hasattr(module, "exports"):
160             exports = module.exports
161         else:
162             for symbol in dir(module):
163                 if symbol.startswith("_"):
164                     continue
165                 exports.append(getattr(module, symbol))
166 
167         exports = [s for s in exports if isclass(s) and issubclass(s, types) and not s in types]
168 
169         return exports
170 
171     @classmethod
172     def _load_plugins(cls, module_names, *, importer, types=None):
173         types = tuple(types or plugin_types())
174 
175         all_exports = []
176         for module_name in module_names:
177             try:
178                 module = importer(module_name)
179                 exports = cls._get_plugin_exports(module, types)
180             except Exception as e:
181                 module_search_error = ModuleNotFoundError
182 
183                 message = ["Failed to import module '%s': %s", module_name, e]
184                 if isinstance(e, module_search_error):
185                     log.debug(*message)
186                 else:
187                     log.warning(*message)
188                 continue
189 
190             log.debug(
191                 "Imported the following symbols from %s: %s"
192                 % (module_name, ", ".join(s.__name__ for s in exports))
193             )
194             all_exports.extend(exports)
195 
196         return all_exports
197 
198     @classmethod
199     def _load_builtin_plugins(cls):
200         if cls._builtin_plugins is None:
201             import datumaro.plugins
202 
203             plugins_dir = osp.dirname(datumaro.plugins.__file__)
204             module_names = [
205                 datumaro.plugins.__name__ + "." + name for name in cls._find_plugins(plugins_dir)
206             ]
207             cls._builtin_plugins = cls._load_plugins(module_names, importer=importlib.import_module)
208         return cls._builtin_plugins
209 
210     def load_plugins(self, plugins_dir):
211         module_names = self._find_plugins(plugins_dir)
212         plugins = self._load_plugins(
213             module_names, importer=partial(import_foreign_module, path=plugins_dir)
214         )
215         self._register_plugins(plugins)
216 
217     def _register_builtin_plugins(self):
218         self._register_plugins(self._load_builtin_plugins())
219 
220     def _register_plugins(self, plugins):
221         self.extractors.batch_register(plugins)
222         self.importers.batch_register(plugins)
223         self.launchers.batch_register(plugins)
224         self.converters.batch_register(plugins)
225         self.generators.batch_register(plugins)
226         self.transforms.batch_register(plugins)
227         self.validators.batch_register(plugins)
228 
229     def make_extractor(self, name, *args, **kwargs):
230         return self.extractors.get(name)(*args, **kwargs)
231 
232     def make_importer(self, name, *args, **kwargs):
233         return self.importers.get(name)(*args, **kwargs)
234 
235     def make_launcher(self, name, *args, **kwargs):
236         return self.launchers.get(name)(*args, **kwargs)
237 
238     def make_converter(self, name, *args, **kwargs):
239         result = self.converters.get(name)
240         if isclass(result):
241             result = result.convert
242         return partial(result, *args, **kwargs)
243 
244     def make_transform(self, name, *args, **kwargs):
245         return partial(self.transforms.get(name), *args, **kwargs)
246 
247     def is_format_known(self, name):
248         return name in self.importers or name in self.extractors
249 
250     def detect_dataset(
251         self,
252         path: str,
253         depth: int = 1,
254         rejection_callback: Optional[Callable[[str, RejectionReason, str], None]] = None,
255     ) -> List[str]:
256         ignore_dirs = {"__MSOSX", "__MACOSX"}
257         matched_formats = set()
258         for _ in range(depth + 1):
259             detected_formats = detect_dataset_format(
260                 (
261                     (format_name, importer.detect)
262                     for format_name, importer in self.importers.items.items()
263                 ),
264                 path,
265                 rejection_callback=rejection_callback,
266             )
267 
268             if detected_formats and len(detected_formats) == 1:
269                 return detected_formats
270             elif detected_formats:
271                 matched_formats |= set(detected_formats)
272 
273             paths = glob.glob(osp.join(path, "*"))
274             path = "" if len(paths) != 1 else paths[0]
275             if not osp.isdir(path) or osp.basename(path) in ignore_dirs:
276                 break
277 
278         return list(matched_formats)
279
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/datumaro/components/environment.py b/datumaro/components/environment.py
--- a/datumaro/components/environment.py
+++ b/datumaro/components/environment.py
@@ -38,7 +38,7 @@
     def __contains__(self, key) -> bool:
         return key in self.items
 
-    def __iter__(self) -> Iterator[T]:
+    def __iter__(self) -> Iterator[str]:
         return iter(self.items)

{"golden_diff": "diff --git a/datumaro/components/environment.py b/datumaro/components/environment.py\n--- a/datumaro/components/environment.py\n+++ b/datumaro/components/environment.py\n@@ -38,7 +38,7 @@\n     def __contains__(self, key) -> bool:\n         return key in self.items\n \n-    def __iter__(self) -> Iterator[T]:\n+    def __iter__(self) -> Iterator[str]:\n         return iter(self.items)\n", "issue": "Wrong annotated return type in Registry class\nhttps://github.com/openvinotoolkit/datumaro/blob/0d4a73d3bbe3a93585af7a0148a0e344fd1106b3/datumaro/components/environment.py#L41-L42\r\nIn the referenced code the return type of the method appears to be wrong. \r\n\r\nEither it should be `Iterator[str]` since iteration over a dict returns its keys which are of type `str` or the return statement should be `return iter(self.items.values())`.\r\n\r\nWhen using the library with static type checkers this annotation causes type check errors. When removing the annotation, type checkers correctly infer the type `Iterator[str]`.\nWrong annotated return type in Registry class\nhttps://github.com/openvinotoolkit/datumaro/blob/0d4a73d3bbe3a93585af7a0148a0e344fd1106b3/datumaro/components/environment.py#L41-L42\r\nIn the referenced code the return type of the method appears to be wrong. \r\n\r\nEither it should be `Iterator[str]` since iteration over a dict returns its keys which are of type `str` or the return statement should be `return iter(self.items.values())`.\r\n\r\nWhen using the library with static type checkers this annotation causes type check errors. When removing the annotation, type checkers correctly infer the type `Iterator[str]`.\n", "before_files": [{"content": "# Copyright (C) 2020-2022 Intel Corporation\n#\n# SPDX-License-Identifier: MIT\n\nimport glob\nimport importlib\nimport logging as log\nimport os.path as osp\nfrom functools import partial\nfrom inspect import isclass\nfrom typing import Callable, Dict, Generic, Iterable, Iterator, List, Optional, Type, TypeVar\n\nfrom datumaro.components.cli_plugin import CliPlugin, plugin_types\nfrom datumaro.components.format_detection import RejectionReason, detect_dataset_format\nfrom datumaro.util.os_util import import_foreign_module, split_path\n\nT = TypeVar(\"T\")\n\n\nclass Registry(Generic[T]):\n    def __init__(self):\n        self.items: Dict[str, T] = {}\n\n    def register(self, name: str, value: T) -> T:\n        self.items[name] = value\n        return value\n\n    def unregister(self, name: str) -> Optional[T]:\n        return self.items.pop(name, None)\n\n    def get(self, key: str):\n        \"\"\"Returns a class or a factory function\"\"\"\n        return self.items[key]\n\n    def __getitem__(self, key: str) -> T:\n        return self.get(key)\n\n    def __contains__(self, key) -> bool:\n        return key in self.items\n\n    def __iter__(self) -> Iterator[T]:\n        return iter(self.items)\n\n\nclass PluginRegistry(Registry[Type[CliPlugin]]):\n    def __init__(\n        self, filter: Callable[[Type[CliPlugin]], bool] = None\n    ):  # pylint: disable=redefined-builtin\n        super().__init__()\n        self._filter = filter\n\n    def batch_register(self, values: Iterable[CliPlugin]):\n        for v in values:\n            if self._filter and not self._filter(v):\n                continue\n\n            self.register(v.NAME, v)\n\n\nclass Environment:\n    _builtin_plugins = None\n\n    @classmethod\n    def _make_filter(cls, accept, skip=None):\n        accept = (accept,) if isclass(accept) else tuple(accept)\n        skip = {skip} if isclass(skip) else set(skip or [])\n        skip = tuple(skip | set(accept))\n        return partial(cls._check_type, accept=accept, skip=skip)\n\n    @staticmethod\n    def _check_type(t, *, accept, skip):\n        return issubclass(t, accept) and t not in skip\n\n    def __init__(self):\n        from datumaro.components.converter import Converter\n        from datumaro.components.dataset_generator import DatasetGenerator\n        from datumaro.components.extractor import (\n            Extractor,\n            Importer,\n            ItemTransform,\n            SourceExtractor,\n            Transform,\n        )\n        from datumaro.components.launcher import Launcher\n        from datumaro.components.validator import Validator\n\n        _filter = self._make_filter\n        self._extractors = PluginRegistry(_filter(Extractor, skip=SourceExtractor))\n        self._importers = PluginRegistry(_filter(Importer))\n        self._launchers = PluginRegistry(_filter(Launcher))\n        self._converters = PluginRegistry(_filter(Converter))\n        self._generators = PluginRegistry(_filter(DatasetGenerator))\n        self._transforms = PluginRegistry(_filter(Transform, skip=ItemTransform))\n        self._validators = PluginRegistry(_filter(Validator))\n        self._builtins_initialized = False\n\n    def _get_plugin_registry(self, name):\n        if not self._builtins_initialized:\n            self._builtins_initialized = True\n            self._register_builtin_plugins()\n        return getattr(self, name)\n\n    @property\n    def extractors(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_extractors\")\n\n    @property\n    def importers(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_importers\")\n\n    @property\n    def launchers(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_launchers\")\n\n    @property\n    def converters(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_converters\")\n\n    @property\n    def generators(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_generators\")\n\n    @property\n    def transforms(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_transforms\")\n\n    @property\n    def validators(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_validators\")\n\n    @staticmethod\n    def _find_plugins(plugins_dir):\n        plugins = []\n\n        for pattern in (\"*.py\", \"*/*.py\"):\n            for path in glob.glob(osp.join(glob.escape(plugins_dir), pattern)):\n                if not osp.isfile(path):\n                    continue\n\n                path_rel = osp.relpath(path, plugins_dir)\n                name_parts = split_path(osp.splitext(path_rel)[0])\n\n                # a module with a dot in the name won't load correctly\n                if any(\".\" in part for part in name_parts):\n                    log.warning(\n                        \"Python file '%s' in directory '%s' can't be imported \"\n                        \"due to a dot in the name; skipping.\",\n                        path_rel,\n                        plugins_dir,\n                    )\n                    continue\n                plugins.append(\".\".join(name_parts))\n\n        return plugins\n\n    @classmethod\n    def _get_plugin_exports(cls, module, types):\n        exports = []\n        if hasattr(module, \"exports\"):\n            exports = module.exports\n        else:\n            for symbol in dir(module):\n                if symbol.startswith(\"_\"):\n                    continue\n                exports.append(getattr(module, symbol))\n\n        exports = [s for s in exports if isclass(s) and issubclass(s, types) and not s in types]\n\n        return exports\n\n    @classmethod\n    def _load_plugins(cls, module_names, *, importer, types=None):\n        types = tuple(types or plugin_types())\n\n        all_exports = []\n        for module_name in module_names:\n            try:\n                module = importer(module_name)\n                exports = cls._get_plugin_exports(module, types)\n            except Exception as e:\n                module_search_error = ModuleNotFoundError\n\n                message = [\"Failed to import module '%s': %s\", module_name, e]\n                if isinstance(e, module_search_error):\n                    log.debug(*message)\n                else:\n                    log.warning(*message)\n                continue\n\n            log.debug(\n                \"Imported the following symbols from %s: %s\"\n                % (module_name, \", \".join(s.__name__ for s in exports))\n            )\n            all_exports.extend(exports)\n\n        return all_exports\n\n    @classmethod\n    def _load_builtin_plugins(cls):\n        if cls._builtin_plugins is None:\n            import datumaro.plugins\n\n            plugins_dir = osp.dirname(datumaro.plugins.__file__)\n            module_names = [\n                datumaro.plugins.__name__ + \".\" + name for name in cls._find_plugins(plugins_dir)\n            ]\n            cls._builtin_plugins = cls._load_plugins(module_names, importer=importlib.import_module)\n        return cls._builtin_plugins\n\n    def load_plugins(self, plugins_dir):\n        module_names = self._find_plugins(plugins_dir)\n        plugins = self._load_plugins(\n            module_names, importer=partial(import_foreign_module, path=plugins_dir)\n        )\n        self._register_plugins(plugins)\n\n    def _register_builtin_plugins(self):\n        self._register_plugins(self._load_builtin_plugins())\n\n    def _register_plugins(self, plugins):\n        self.extractors.batch_register(plugins)\n        self.importers.batch_register(plugins)\n        self.launchers.batch_register(plugins)\n        self.converters.batch_register(plugins)\n        self.generators.batch_register(plugins)\n        self.transforms.batch_register(plugins)\n        self.validators.batch_register(plugins)\n\n    def make_extractor(self, name, *args, **kwargs):\n        return self.extractors.get(name)(*args, **kwargs)\n\n    def make_importer(self, name, *args, **kwargs):\n        return self.importers.get(name)(*args, **kwargs)\n\n    def make_launcher(self, name, *args, **kwargs):\n        return self.launchers.get(name)(*args, **kwargs)\n\n    def make_converter(self, name, *args, **kwargs):\n        result = self.converters.get(name)\n        if isclass(result):\n            result = result.convert\n        return partial(result, *args, **kwargs)\n\n    def make_transform(self, name, *args, **kwargs):\n        return partial(self.transforms.get(name), *args, **kwargs)\n\n    def is_format_known(self, name):\n        return name in self.importers or name in self.extractors\n\n    def detect_dataset(\n        self,\n        path: str,\n        depth: int = 1,\n        rejection_callback: Optional[Callable[[str, RejectionReason, str], None]] = None,\n    ) -> List[str]:\n        ignore_dirs = {\"__MSOSX\", \"__MACOSX\"}\n        matched_formats = set()\n        for _ in range(depth + 1):\n            detected_formats = detect_dataset_format(\n                (\n                    (format_name, importer.detect)\n                    for format_name, importer in self.importers.items.items()\n                ),\n                path,\n                rejection_callback=rejection_callback,\n            )\n\n            if detected_formats and len(detected_formats) == 1:\n                return detected_formats\n            elif detected_formats:\n                matched_formats |= set(detected_formats)\n\n            paths = glob.glob(osp.join(path, \"*\"))\n            path = \"\" if len(paths) != 1 else paths[0]\n            if not osp.isdir(path) or osp.basename(path) in ignore_dirs:\n                break\n\n        return list(matched_formats)\n", "path": "datumaro/components/environment.py"}], "after_files": [{"content": "# Copyright (C) 2020-2022 Intel Corporation\n#\n# SPDX-License-Identifier: MIT\n\nimport glob\nimport importlib\nimport logging as log\nimport os.path as osp\nfrom functools import partial\nfrom inspect import isclass\nfrom typing import Callable, Dict, Generic, Iterable, Iterator, List, Optional, Type, TypeVar\n\nfrom datumaro.components.cli_plugin import CliPlugin, plugin_types\nfrom datumaro.components.format_detection import RejectionReason, detect_dataset_format\nfrom datumaro.util.os_util import import_foreign_module, split_path\n\nT = TypeVar(\"T\")\n\n\nclass Registry(Generic[T]):\n    def __init__(self):\n        self.items: Dict[str, T] = {}\n\n    def register(self, name: str, value: T) -> T:\n        self.items[name] = value\n        return value\n\n    def unregister(self, name: str) -> Optional[T]:\n        return self.items.pop(name, None)\n\n    def get(self, key: str):\n        \"\"\"Returns a class or a factory function\"\"\"\n        return self.items[key]\n\n    def __getitem__(self, key: str) -> T:\n        return self.get(key)\n\n    def __contains__(self, key) -> bool:\n        return key in self.items\n\n    def __iter__(self) -> Iterator[str]:\n        return iter(self.items)\n\n\nclass PluginRegistry(Registry[Type[CliPlugin]]):\n    def __init__(\n        self, filter: Callable[[Type[CliPlugin]], bool] = None\n    ):  # pylint: disable=redefined-builtin\n        super().__init__()\n        self._filter = filter\n\n    def batch_register(self, values: Iterable[CliPlugin]):\n        for v in values:\n            if self._filter and not self._filter(v):\n                continue\n\n            self.register(v.NAME, v)\n\n\nclass Environment:\n    _builtin_plugins = None\n\n    @classmethod\n    def _make_filter(cls, accept, skip=None):\n        accept = (accept,) if isclass(accept) else tuple(accept)\n        skip = {skip} if isclass(skip) else set(skip or [])\n        skip = tuple(skip | set(accept))\n        return partial(cls._check_type, accept=accept, skip=skip)\n\n    @staticmethod\n    def _check_type(t, *, accept, skip):\n        return issubclass(t, accept) and t not in skip\n\n    def __init__(self):\n        from datumaro.components.converter import Converter\n        from datumaro.components.dataset_generator import DatasetGenerator\n        from datumaro.components.extractor import (\n            Extractor,\n            Importer,\n            ItemTransform,\n            SourceExtractor,\n            Transform,\n        )\n        from datumaro.components.launcher import Launcher\n        from datumaro.components.validator import Validator\n\n        _filter = self._make_filter\n        self._extractors = PluginRegistry(_filter(Extractor, skip=SourceExtractor))\n        self._importers = PluginRegistry(_filter(Importer))\n        self._launchers = PluginRegistry(_filter(Launcher))\n        self._converters = PluginRegistry(_filter(Converter))\n        self._generators = PluginRegistry(_filter(DatasetGenerator))\n        self._transforms = PluginRegistry(_filter(Transform, skip=ItemTransform))\n        self._validators = PluginRegistry(_filter(Validator))\n        self._builtins_initialized = False\n\n    def _get_plugin_registry(self, name):\n        if not self._builtins_initialized:\n            self._builtins_initialized = True\n            self._register_builtin_plugins()\n        return getattr(self, name)\n\n    @property\n    def extractors(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_extractors\")\n\n    @property\n    def importers(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_importers\")\n\n    @property\n    def launchers(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_launchers\")\n\n    @property\n    def converters(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_converters\")\n\n    @property\n    def generators(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_generators\")\n\n    @property\n    def transforms(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_transforms\")\n\n    @property\n    def validators(self) -> PluginRegistry:\n        return self._get_plugin_registry(\"_validators\")\n\n    @staticmethod\n    def _find_plugins(plugins_dir):\n        plugins = []\n\n        for pattern in (\"*.py\", \"*/*.py\"):\n            for path in glob.glob(osp.join(glob.escape(plugins_dir), pattern)):\n                if not osp.isfile(path):\n                    continue\n\n                path_rel = osp.relpath(path, plugins_dir)\n                name_parts = split_path(osp.splitext(path_rel)[0])\n\n                # a module with a dot in the name won't load correctly\n                if any(\".\" in part for part in name_parts):\n                    log.warning(\n                        \"Python file '%s' in directory '%s' can't be imported \"\n                        \"due to a dot in the name; skipping.\",\n                        path_rel,\n                        plugins_dir,\n                    )\n                    continue\n                plugins.append(\".\".join(name_parts))\n\n        return plugins\n\n    @classmethod\n    def _get_plugin_exports(cls, module, types):\n        exports = []\n        if hasattr(module, \"exports\"):\n            exports = module.exports\n        else:\n            for symbol in dir(module):\n                if symbol.startswith(\"_\"):\n                    continue\n                exports.append(getattr(module, symbol))\n\n        exports = [s for s in exports if isclass(s) and issubclass(s, types) and not s in types]\n\n        return exports\n\n    @classmethod\n    def _load_plugins(cls, module_names, *, importer, types=None):\n        types = tuple(types or plugin_types())\n\n        all_exports = []\n        for module_name in module_names:\n            try:\n                module = importer(module_name)\n                exports = cls._get_plugin_exports(module, types)\n            except Exception as e:\n                module_search_error = ModuleNotFoundError\n\n                message = [\"Failed to import module '%s': %s\", module_name, e]\n                if isinstance(e, module_search_error):\n                    log.debug(*message)\n                else:\n                    log.warning(*message)\n                continue\n\n            log.debug(\n                \"Imported the following symbols from %s: %s\"\n                % (module_name, \", \".join(s.__name__ for s in exports))\n            )\n            all_exports.extend(exports)\n\n        return all_exports\n\n    @classmethod\n    def _load_builtin_plugins(cls):\n        if cls._builtin_plugins is None:\n            import datumaro.plugins\n\n            plugins_dir = osp.dirname(datumaro.plugins.__file__)\n            module_names = [\n                datumaro.plugins.__name__ + \".\" + name for name in cls._find_plugins(plugins_dir)\n            ]\n            cls._builtin_plugins = cls._load_plugins(module_names, importer=importlib.import_module)\n        return cls._builtin_plugins\n\n    def load_plugins(self, plugins_dir):\n        module_names = self._find_plugins(plugins_dir)\n        plugins = self._load_plugins(\n            module_names, importer=partial(import_foreign_module, path=plugins_dir)\n        )\n        self._register_plugins(plugins)\n\n    def _register_builtin_plugins(self):\n        self._register_plugins(self._load_builtin_plugins())\n\n    def _register_plugins(self, plugins):\n        self.extractors.batch_register(plugins)\n        self.importers.batch_register(plugins)\n        self.launchers.batch_register(plugins)\n        self.converters.batch_register(plugins)\n        self.generators.batch_register(plugins)\n        self.transforms.batch_register(plugins)\n        self.validators.batch_register(plugins)\n\n    def make_extractor(self, name, *args, **kwargs):\n        return self.extractors.get(name)(*args, **kwargs)\n\n    def make_importer(self, name, *args, **kwargs):\n        return self.importers.get(name)(*args, **kwargs)\n\n    def make_launcher(self, name, *args, **kwargs):\n        return self.launchers.get(name)(*args, **kwargs)\n\n    def make_converter(self, name, *args, **kwargs):\n        result = self.converters.get(name)\n        if isclass(result):\n            result = result.convert\n        return partial(result, *args, **kwargs)\n\n    def make_transform(self, name, *args, **kwargs):\n        return partial(self.transforms.get(name), *args, **kwargs)\n\n    def is_format_known(self, name):\n        return name in self.importers or name in self.extractors\n\n    def detect_dataset(\n        self,\n        path: str,\n        depth: int = 1,\n        rejection_callback: Optional[Callable[[str, RejectionReason, str], None]] = None,\n    ) -> List[str]:\n        ignore_dirs = {\"__MSOSX\", \"__MACOSX\"}\n        matched_formats = set()\n        for _ in range(depth + 1):\n            detected_formats = detect_dataset_format(\n                (\n                    (format_name, importer.detect)\n                    for format_name, importer in self.importers.items.items()\n                ),\n                path,\n                rejection_callback=rejection_callback,\n            )\n\n            if detected_formats and len(detected_formats) == 1:\n                return detected_formats\n            elif detected_formats:\n                matched_formats |= set(detected_formats)\n\n            paths = glob.glob(osp.join(path, \"*\"))\n            path = \"\" if len(paths) != 1 else paths[0]\n            if not osp.isdir(path) or osp.basename(path) in ignore_dirs:\n                break\n\n        return list(matched_formats)\n", "path": "datumaro/components/environment.py"}]}