Repository: joke2k/faker

Issue:
Unicity and localization
* Faker version: 17.4.0
* OS: Archlinux

It seems that uniqueness and localization cannot be used together:

```python
>>> from faker import Faker
... fake = Faker(['it_IT', 'en_US', 'ja_JP'])
>>> fake['it_IT'].name()
'Giuseppina Juvara'
>>> fake['it_IT'].unique.name()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Generator' object has no attribute 'unique'

>>> fake.unique.name()
'石井 翔太'
>>> fake.unique['it_IT'].name()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'UniqueProxy' object is not subscriptable
```

I would lo to be able to use both features at the same time!



Comments:

Patch:
diff --git a/faker/proxy.py b/faker/proxy.py
index 653a5943..03a6e42e 100644
--- a/faker/proxy.py
+++ b/faker/proxy.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import copy
 import functools
 import re
@@ -35,7 +37,7 @@ class Faker:
         use_weighting: bool = True,
         **config: Any,
     ) -> None:
-        self._factory_map = OrderedDict()
+        self._factory_map: OrderedDict[str, Generator | Faker] = OrderedDict()
         self._weights = None
         self._unique_proxy = UniqueProxy(self)
         self._optional_proxy = OptionalProxy(self)
@@ -54,7 +56,7 @@ class Faker:
                 if final_locale not in locales:
                     locales.append(final_locale)
 
-        elif isinstance(locale, OrderedDict):
+        elif isinstance(locale, (OrderedDict, dict)):
             assert all(isinstance(v, (int, float)) for v in locale.values())
             odict = OrderedDict()
             for k, v in locale.items():
@@ -66,15 +68,25 @@ class Faker:
         else:
             locales = [DEFAULT_LOCALE]
 
-        for locale in locales:
-            self._factory_map[locale] = Factory.create(
-                locale,
+        if len(locales) == 1:
+            self._factory_map[locales[0]] = Factory.create(
+                locales[0],
                 providers,
                 generator,
                 includes,
                 use_weighting=use_weighting,
                 **config,
             )
+        else:
+            for locale in locales:
+                self._factory_map[locale] = Faker(
+                    locale,
+                    providers,
+                    generator,
+                    includes,
+                    use_weighting=use_weighting,
+                    **config,
+                )
 
         self._locales = locales
         self._factories = list(self._factory_map.values())
@@ -85,8 +97,12 @@ class Faker:
             attributes |= {attr for attr in dir(factory) if not attr.startswith("_")}
         return sorted(attributes)
 
-    def __getitem__(self, locale: str) -> Generator:
-        return self._factory_map[locale.replace("-", "_")]
+    def __getitem__(self, locale: str) -> Faker:
+        if locale.replace("-", "_") in self.locales and len(self.locales) == 1:
+            return self
+        instance = self._factory_map[locale.replace("-", "_")]
+        assert isinstance(instance, Faker)  # for mypy
+        return instance
 
     def __getattribute__(self, attr: str) -> Any:
         """
@@ -273,10 +289,10 @@ class Faker:
         return self._weights
 
     @property
-    def factories(self) -> List[Generator]:
+    def factories(self) -> List[Generator | Faker]:
         return self._factories
 
-    def items(self) -> List[Tuple[str, Generator]]:
+    def items(self) -> List[Tuple[str, Generator | Faker]]:
         return list(self._factory_map.items())
 
 


Test patch:
diff --git a/tests/providers/test_misc.py b/tests/providers/test_misc.py
index ec9fe57a..df735eee 100644
--- a/tests/providers/test_misc.py
+++ b/tests/providers/test_misc.py
@@ -423,7 +423,7 @@ class TestMiscProvider:
     def test_dsv_data_columns(self, faker):
         num_rows = 10
         data_columns = ["{{name}}", "#??-####", "{{address}}", "{{phone_number}}"]
-        with patch.object(faker["en_US"], "pystr_format") as mock_pystr_format:
+        with patch.object(faker["en_US"].factories[0], "pystr_format") as mock_pystr_format:
             mock_pystr_format.assert_not_called()
             faker.dsv(data_columns=data_columns, num_rows=num_rows)
 
diff --git a/tests/providers/test_python.py b/tests/providers/test_python.py
index 6342bfb9..4935528e 100644
--- a/tests/providers/test_python.py
+++ b/tests/providers/test_python.py
@@ -523,7 +523,7 @@ class TestPystrFormat(unittest.TestCase):
         Faker.seed(0)
 
     def test_formatter_invocation(self):
-        with patch.object(self.fake["en_US"], "foo") as mock_foo:
+        with patch.object(self.fake["en_US"].factories[0], "foo") as mock_foo:
             with patch("faker.providers.BaseProvider.bothify", wraps=self.fake.bothify) as mock_bothify:
                 mock_foo.return_value = "barbar"
                 value = self.fake.pystr_format("{{foo}}?#?{{foo}}?#?{{foo}}", letters="abcde")
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
index 322bc396..8b4b6d63 100644
--- a/tests/test_proxy.py
+++ b/tests/test_proxy.py
@@ -103,14 +103,14 @@ class TestFakerProxyClass:
         fake = Faker(locale)
         for locale_name, factory in fake.items():
             assert locale_name in processed_locale
-            assert isinstance(factory, Generator)
+            assert isinstance(factory, (Generator, Faker))
 
     def test_dunder_getitem(self):
         locale = ["de_DE", "en-US", "en-PH", "ja_JP"]
         fake = Faker(locale)
 
         for code in locale:
-            assert isinstance(fake[code], Generator)
+            assert isinstance(fake[code], (Generator, Faker))
 
         with pytest.raises(KeyError):
             fake["en_GB"]