Repository: openvinotoolkit/datumaro

Issue:
custom importer/extractor not loading?
Hi, 
I saw there's a comment about how to add a custom importer/extractor, but it seems not working.

https://github.com/openvinotoolkit/datumaro/blob/d75c2343b747440cc6703adb7b9a1efc89c4509d/datumaro/cli/contexts/project/__init__.py#L116


I tried putting `myimporter.py` to `.datumaro/importers` folder 

```myimporter.py
from datumaro.components.extractor import Importer

class MyImporter(Importer):
    @classmethod
    def find_sources(cls, path):
        return []

```

then run following code in python interactive under project root.
```
>>> from datumaro.components.environment import Environment
>>> env = Environment()
>>> print([importer for importer in env.importers])
>>> ['imagenet_txt', 'yolo', 'cityscapes', 'label_me', 'coco_captions', 'coco_image_info', 'coco', 'coco_instances', 'coco_labels', 'coco_panoptic', 
'coco_person_keypoints', 'coco_stuff', 'cifar', 'image_dir', 'wider_face', 'lfw', 'sly_pointcloud', 'market1501', 'image_zip', 'mot_seq', 'kitti_
detection', 'kitti', 'kitti_segmentation', 'kitti_raw', 'cvat', 'mots', 'imagenet', 'open_images', 'mnist_csv', 'datumaro', 'vgg_face2', 'mnist',
 'voc_action', 'voc_classification', 'voc_detection', 'voc', 'voc_layout', 'voc_segmentation', 'icdar_text_localization', 'icdar_text_segmentatio
n', 'icdar_word_recognition', 'camvid']
```

Then I tried force loading plugin from the specific directory
```
>>>  env.load_plugins('.datumaro/importers')
>>> print([importer for importer in env.importers])
['imagenet_txt', 'yolo', 'cityscapes', 'label_me', 'coco_captions', 'coco_image_info', 'coco', 'coco_instances', 'coco_labels', 'coco_panoptic', 
'coco_person_keypoints', 'coco_stuff', 'cifar', 'image_dir', 'wider_face', 'lfw', 'sly_pointcloud', 'market1501', 'image_zip', 'mot_seq', 'kitti_
detection', 'kitti', 'kitti_segmentation', 'kitti_raw', 'cvat', 'mots', 'imagenet', 'open_images', 'mnist_csv', 'datumaro', 'vgg_face2', 'mnist',
 'voc_action', 'voc_classification', 'voc_detection', 'voc', 'voc_layout', 'voc_segmentation', 'icdar_text_localization', 'icdar_text_segmentatio
n', 'icdar_word_recognition', 'camvid', 'my']
````





Discussion:
Hi, it looks like the plugin directory was overlooked in the last implementation update. It should be called here:
https://github.com/openvinotoolkit/datumaro/blob/develop/datumaro/components/project.py#L417

`<project_dir>/.datumaro/extractors` should be `<project_dir>/.datumaro/plugins` now.

Thank you for reporting the issue!

Resolving patch:
diff --git a/datumaro/components/project.py b/datumaro/components/project.py
--- a/datumaro/components/project.py
+++ b/datumaro/components/project.py
@@ -415,6 +415,8 @@ def __init__(self, config=None, env=None):
             env = Environment(self.config)
             env.models.batch_register(self.config.models)
             env.sources.batch_register(self.config.sources)
+            env.load_plugins(osp.join(self.config.project_dir,
+                self.config.env_dir, self.config.plugins_dir))
         elif config is not None:
             raise ValueError("env can only be provided when no config provided")
         self.env = env


Test patch:
diff --git a/tests/requirements.py b/tests/requirements.py
--- a/tests/requirements.py
+++ b/tests/requirements.py
@@ -30,6 +30,7 @@ class Requirements:
     DATUM_BUG_219 = "Return format is not uniform"
     DATUM_BUG_257 = "Dataset.filter doesn't count removed items"
     DATUM_BUG_259 = "Dataset.filter fails on merged datasets"
+    DATUM_BUG_404 = "custom importer/extractor not loading"
 
 
 class SkipMessages:
diff --git a/tests/test_project.py b/tests/test_project.py
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -1,6 +1,7 @@
 from unittest import TestCase
 import os
 import os.path as osp
+import textwrap
 
 import numpy as np
 
@@ -428,3 +429,35 @@ def __iter__(self):
         dataset = project.make_dataset()
 
         compare_datasets(self, CustomExtractor(), dataset)
+
+    @mark_requirement(Requirements.DATUM_BUG_404)
+    def test_can_add_plugin(self):
+        with TestDir() as test_dir:
+            Project.generate(test_dir)
+
+            plugin_dir = osp.join(test_dir, '.datumaro', 'plugins')
+            os.makedirs(plugin_dir)
+            with open(osp.join(plugin_dir, '__init__.py'), 'w') as f:
+                f.write(textwrap.dedent("""
+                    from datumaro.components.extractor import (SourceExtractor,
+                        DatasetItem)
+
+                    class MyExtractor(SourceExtractor):
+                        def __iter__(self):
+                            yield from [
+                                DatasetItem('1'),
+                                DatasetItem('2'),
+                            ]
+                """))
+
+            project = Project.load(test_dir)
+            project.add_source('src', {
+                'url': '',
+                'format': 'my'
+            })
+
+            expected = Dataset.from_iterable([
+                DatasetItem('1'),
+                DatasetItem('2')
+            ])
+            compare_datasets(self, expected, project.make_dataset())