Repository: CS-SI/eodag
Pull request #176 (2021-03-17 16:33:03)

feat: auto load ext plugins providers config, fixes GH-172


Patch:
diff --git a/eodag/config.py b/eodag/config.py
index 87c59286..2a9e45d7 100644
--- a/eodag/config.py
+++ b/eodag/config.py
@@ -203,11 +203,22 @@ class PluginConfig(yaml.YAMLObject):
 def load_default_config():
     """Build the providers configuration into a dictionnary
 
+    :returns: The default provider's configuration
+    :rtype: dict
+    """
+    return load_config(resource_filename("eodag", "resources/providers.yml"))
+
+
+def load_config(config_path):
+    """Build providers configuration into a dictionnary from a given file
+
+    :param config_path: The path to the provider config file
+    :type config_path: str
     :returns: The default provider's configuration
     :rtype: dict
     """
     config = {}
-    with open(resource_filename("eodag", "resources/providers.yml"), "r") as fh:
+    with open(config_path, "r") as fh:
         try:
             # Providers configs are stored in this file as separated yaml documents
             # Load all of it
@@ -225,7 +236,7 @@ def provider_config_init(provider_config):
     """Applies some default values to provider config
 
     :param provider_config: An eodag provider configuration
-    :type file_path: str
+    :type provider_config: :class:`~eodag.config.ProviderConfig`
     """
     # For the provider, set the default outputs_prefix of its download plugin
     # as tempdir in a portable way
diff --git a/eodag/plugins/manager.py b/eodag/plugins/manager.py
index f99a5b1e..8abf723b 100644
--- a/eodag/plugins/manager.py
+++ b/eodag/plugins/manager.py
@@ -17,9 +17,11 @@
 # limitations under the License.
 import logging
 from operator import attrgetter
+from pathlib import Path
 
 import pkg_resources
 
+from eodag.config import load_config
 from eodag.plugins.apis.base import Api
 from eodag.plugins.authentication.base import Authentication
 from eodag.plugins.base import EODAGPluginMount
@@ -77,8 +79,20 @@ class PluginManager(object):
                         "Check that the plugin module (%s) is importable",
                         entry_point.module_name,
                     )
+                if entry_point.dist.project_name != "eodag":
+                    # use plugin providers if any
+                    plugin_providers_config_path = [
+                        x
+                        for x in Path(entry_point.dist.location).rglob("providers.yml")
+                        if all(i not in str(x) for i in ["build", ".tox"])
+                    ]
+                    if plugin_providers_config_path:
+                        self.providers_config.update(
+                            load_config(plugin_providers_config_path[0])
+                        )
+
         self.product_type_to_provider_config_map = {}
-        for provider_config in providers_config.values():
+        for provider_config in self.providers_config.values():
             for product_type in provider_config.products:
                 product_type_providers = (
                     self.product_type_to_provider_config_map.setdefault(  # noqa


Test patch:
diff --git a/tests/integration/test_config_ext_plugin.py b/tests/integration/test_config_ext_plugin.py
new file mode 100644
index 00000000..7a8fc66f
--- /dev/null
+++ b/tests/integration/test_config_ext_plugin.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021, CS GROUP - France, http://www.c-s.fr
+#
+# This file is part of EODAG project
+#     https://www.github.com/CS-SI/EODAG
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import os
+import unittest
+
+import pkg_resources
+
+from tests import TEST_RESOURCES_PATH
+from tests.context import EODataAccessGateway
+
+
+class TestExternalPluginConfig(unittest.TestCase):
+    def setUp(self):
+        super(TestExternalPluginConfig, self).setUp()
+        self.dag = EODataAccessGateway()
+
+    def tearDown(self):
+        super(TestExternalPluginConfig, self).tearDown()
+
+        self.dag.providers_config.pop("fakeplugin_provider", None)
+
+        # remove entry point manually, see https://github.com/pypa/setuptools/issues/1759
+        distpath = pkg_resources.working_set.by_key.pop("eodag-fakeplugin").location
+        pkg_resources.working_set.entry_keys.pop(distpath)
+        pkg_resources.working_set.entries.remove(distpath)
+
+    def test_update_providers_from_ext_plugin(self):
+        """Load fake external plugin and check if it updates providers config"""
+
+        default_providers_count = len(self.dag.providers_config)
+
+        fakeplugin_location = os.path.join(TEST_RESOURCES_PATH, "fake_ext_plugin")
+
+        # Create the fake entry point definition
+        ep = pkg_resources.EntryPoint.parse(
+            "FakePluginAPI = eodag_fakeplugin:FakePluginAPI"
+        )
+        # Create a fake distribution to insert into the global working_set
+        d = pkg_resources.Distribution(
+            location=fakeplugin_location,
+            project_name="eodag_fakeplugin",
+            version="0.1",
+        )
+        ep.dist = d
+        # Add the mapping to the fake EntryPoint
+        d._ep_map = {"eodag.plugins.api": {"FakePluginAPI": ep}}
+        # Add the fake distribution to the global working_set
+        pkg_resources.working_set.add(d, fakeplugin_location)
+
+        # New EODataAccessGateway instance, check if new conf has been loaded
+        self.dag = EODataAccessGateway()
+        self.assertEqual(len(self.dag.providers_config), default_providers_count + 1)
diff --git a/tests/resources/fake_ext_plugin/providers.yml b/tests/resources/fake_ext_plugin/providers.yml
new file mode 100644
index 00000000..2e4963e5
--- /dev/null
+++ b/tests/resources/fake_ext_plugin/providers.yml
@@ -0,0 +1,10 @@
+---
+!provider
+  name: fakeplugin_provider
+  priority: 0
+  api: !plugin
+    type: FakePluginAPI
+    endpoint: 'https://fakeplugin-endpoint'
+  products:
+    GENERIC_PRODUCT_TYPE:
+      productType: '{productType}'