Repository: lk-geimfari/mimesis

Issue:
Auto add builtin provider to Generic based on passed locale
# Feature request

An idea is very simple:

```python
generic = Generic('ru', auto_add_builtin=True)
generic.russia_provider.inn()
```

Instead of this:

```python
from mimesis import Generic
from mimesis.builtins import RussiaSpecProvider

generic = Generic('ru')
generic.add_provider(RussiaSpecProvider)
generic.russia_provider.inn()
```

Optionally we can make builtin's name customizable: 

```python
generic = Generic('ru', auto_add_builtin=True, builtin_custom_name='russia')
generic.russia.inn()
```





Discussion:
@ceccoemi @sobolevn What do you, guys, think?
I do not see any problems with these three lines of code.
What problem are you trying to solve?
@sobolevn I'm trying to make it easier to add country-specific data to Generic. For example, in common cases when you need to generate Personal info for locale `ru`, you will exactly need some data from `RussiaSpecProvider`, such as `inn`, `patronymic` and etc.

It's just an easier way to add builtin provider to `Generic`. We can do get all params we need from `**kwargs`.
@lk-geimfari I think that is a good idea.

What do you think about adding the builtin provider by default? The generic provider generates all the data from a locale so I think that is reasonable to add the builtin too:

    g = Generic('ru')
    g.russia_provider.inn()

It's ok to add the parameter `builtin_custom_name` if you think that it will not add unnecessary complexity.

I could work on this next week. Let me know!

Resolving patch:
diff --git a/mimesis/providers/generic.py b/mimesis/providers/generic.py
--- a/mimesis/providers/generic.py
+++ b/mimesis/providers/generic.py
@@ -5,6 +5,17 @@
 import inspect
 from typing import Any, List, Type
 
+from mimesis.builtins import (
+    BrazilSpecProvider,
+    DenmarkSpecProvider,
+    GermanySpecProvider,
+    ItalySpecProvider,
+    NetherlandsSpecProvider,
+    PolandSpecProvider,
+    RussiaSpecProvider,
+    UkraineSpecProvider,
+    USASpecProvider,
+)
 from mimesis.providers.address import Address
 from mimesis.providers.base import BaseDataProvider, BaseProvider
 from mimesis.providers.business import Business
@@ -48,6 +59,21 @@ def __init__(self, *args, **kwargs) -> None:
         self._text = Text
         self._food = Food
         self._science = Science
+
+        _spec_providers = {
+            'de': DenmarkSpecProvider,
+            'ge': GermanySpecProvider,
+            'en': USASpecProvider,
+            'it': ItalySpecProvider,
+            'nl': NetherlandsSpecProvider,
+            'pl': PolandSpecProvider,
+            'pt-br': BrazilSpecProvider,
+            'ru': RussiaSpecProvider,
+            'uk': UkraineSpecProvider,
+        }
+        if self.locale in _spec_providers:
+            self.add_provider(_spec_providers[self.locale])
+
         self.transport = Transport(seed=self.seed)
         self.code = Code(seed=self.seed)
         self.unit_system = UnitSystem(seed=self.seed)
@@ -108,7 +134,8 @@ def add_provider(self, cls: Type[BaseProvider]) -> None:
 
         :param cls: Custom provider.
         :return: None
-        :raises TypeError: if cls is not class.
+        :raises TypeError: if cls is not class or is not a subclass
+            of BaseProvider.
         """
         if inspect.isclass(cls):
             if not issubclass(cls, BaseProvider):


Test patch:
diff --git a/tests/test_providers/test_generic.py b/tests/test_providers/test_generic.py
--- a/tests/test_providers/test_generic.py
+++ b/tests/test_providers/test_generic.py
@@ -50,6 +50,15 @@ def test_base_code(self, generic):
         result = generic.code.isbn()
         assert result is not None
 
+    def test_base_spec_provider(self):
+        generic = Generic()
+        result = generic.usa_provider.ssn()
+        assert result is not None
+
+        generic = Generic(locale='it')
+        result = generic.italy_provider.fiscal_code()
+        assert result is not None
+
     def test_bad_argument(self, generic):
         with pytest.raises(AttributeError):
             _ = generic.bad_argument  # noqa
@@ -86,6 +95,9 @@ def empty():
         with pytest.raises(TypeError):
             generic.add_providers(Provider4)
 
+        with pytest.raises(TypeError):
+            generic.add_providers(3)
+
         class UnnamedProvider(BaseProvider):
             @staticmethod
             def nothing():
@@ -94,9 +106,13 @@ def nothing():
         generic.add_provider(UnnamedProvider)
         assert generic.unnamedprovider.nothing() is None
 
+    def test_dir(self, generic):
+        providers = generic.__dir__()
+        for p in providers:
+            assert not p.startswith('_')
+
 
 class TestSeededGeneric(object):
-    TIMES = 5
 
     @pytest.fixture
     def g1(self, seed):