Repository: sdv-dev/Copulas

Issue:
AttributeError raised when an entry point fails to load
### Problem Description

Currently if an `EntryPoint` fails to load, instead of raising a warning this crashes with an `AttributeError` as follows:

```
AttributeError: 'EntryPoint' object has no attribute 'module_name'
```

### Expected behavior

If an `EntryPoint` fails to load, we should raise the proper warning.

### Additional context

The code should be updated to match [SDV's __init__.py](https://github.com/sdv-dev/SDV/blob/2ec744c694255377f65b74bb5d708f95eb125fc8/sdv/__init__.py#L99)

Hints from issue comments:

Solution patch:
diff --git a/copulas/__init__.py b/copulas/__init__.py
index 5d787c1..788a110 100644
--- a/copulas/__init__.py
+++ b/copulas/__init__.py
@@ -13,6 +13,7 @@ import warnings
 from copy import deepcopy
 from importlib.metadata import entry_points
 from operator import attrgetter
+from types import ModuleType
 
 import numpy as np
 import pandas as pd
@@ -319,8 +320,11 @@ def _find_addons():
     for entry_point in eps:
         try:
             addon = entry_point.load()
-        except Exception:  # pylint: disable=broad-exception-caught
-            msg = f'Failed to load "{entry_point.name}" from "{entry_point.value}".'
+        except Exception as e:  # pylint: disable=broad-exception-caught
+            msg = (
+                f'Failed to load "{entry_point.name}" from "{entry_point.value}" '
+                f'with error:\n{e}'
+            )
             warnings.warn(msg)
             continue
 
@@ -331,6 +335,11 @@ def _find_addons():
             warnings.warn(msg)
             continue
 
+        if isinstance(addon, ModuleType):
+            addon_module_name = f'{addon_target.__name__}.{addon_name}'
+            if addon_module_name not in sys.modules:
+                sys.modules[addon_module_name] = addon
+
         setattr(addon_target, addon_name, addon)
 
 


Test patch:
diff --git a/tests/unit/test___init__.py b/tests/unit/test___init__.py
index 2d9a4d6..0aee850 100644
--- a/tests/unit/test___init__.py
+++ b/tests/unit/test___init__.py
@@ -1,6 +1,6 @@
 import sys
 from unittest import TestCase
-from unittest.mock import MagicMock, call, patch
+from unittest.mock import MagicMock, Mock, call, patch
 
 import numpy as np
 import pandas as pd
@@ -464,14 +464,14 @@ def test__find_addons_bad_addon(entry_points_mock, warning_mock):
 
     # Setup
     def entry_point_error():
-        raise ValueError()
+        raise ValueError('bad value')
 
-    bad_entry_point = MagicMock()
+    bad_entry_point = Mock()
     bad_entry_point.name = 'bad_entry_point'
     bad_entry_point.value = 'bad_module'
     bad_entry_point.load.side_effect = entry_point_error
     entry_points_mock.return_value = [bad_entry_point]
-    msg = 'Failed to load "bad_entry_point" from "bad_module".'
+    msg = 'Failed to load "bad_entry_point" from "bad_module" with error:\nbad value'
 
     # Run
     _find_addons()