We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Raises RecursionError under certain conditions
* Faker version: 8.4.0
* OS: Arch Linux with kernel 5.12.6

Certain conditions can trigger a `RecursionError: maximum recursion depth exceeded`. I encountered this error in a django project, see below the steps to reproduce this error.

### Steps to reproduce

1. `pip install django faker`
1. `django-admin startproject mysite`
1. In the app mysite create a file called _test_example.py_, the source code is listed below
1. And in the app mysite create a file called _my_faker.py_, the source code is listed below

```python
# test_example.py
from django.test import TestCase
from faker import Faker

from .my_faker import MyFaker


class ExampleTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.faker_example = MyFaker()

    def setUp(self):
        self.fake = Faker()

        self.example = self.faker_example.example()

    def test_example(self):
        self.assertEqual(1, 1)
```

```python
# my_faker.py
from faker import Faker


class MyFaker:
    def __init__(self):
        self.fake = Faker()

    def example(self):
        pass
```


### Expected behavior

No RecursionError, it worked before. Last time about 2 month ago with `faker==5.6.5`, however this version does not work on updated systems as of the `AttributeError: 'PosixPath' object has no attribute 'startswith'`error but which was fixed recently.

### Actual behavior
```
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_example (mysite.test_example.ExampleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/username/Downloads/mysite/mysite/test_example.py", line 15, in setUp
    self.example = self.faker_example.example()
  File "/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/django/test/testcases.py", line 1124, in __get__
    data = deepcopy(self.data, memo)
  File "/usr/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/lib/python3.9/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/lib/python3.9/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.9/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/lib/python3.9/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/lib/python3.9/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.9/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/lib/python3.9/copy.py", line 271, in _reconstruct
    if hasattr(y, '__setstate__'):
  File "/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/faker/proxy.py", line 263, in __getattr__
    obj = getattr(self._proxy, name)
  File "/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/faker/proxy.py", line 263, in __getattr__
    obj = getattr(self._proxy, name)
  File "/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/faker/proxy.py", line 263, in __getattr__
    obj = getattr(self._proxy, name)
  [Previous line repeated 964 more times]
RecursionError: maximum recursion depth exceeded
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `faker/proxy.py`
Content:
```
1 import functools
2 import random
3 import re
4 
5 from collections import OrderedDict
6 
7 from faker.config import DEFAULT_LOCALE
8 from faker.exceptions import UniquenessException
9 from faker.factory import Factory
10 from faker.generator import Generator
11 from faker.utils.distribution import choices_distribution
12 
13 _UNIQUE_ATTEMPTS = 1000
14 
15 
16 class Faker:
17     """Proxy class capable of supporting multiple locales"""
18 
19     cache_pattern = re.compile(r'^_cached_\w*_mapping$')
20     generator_attrs = [
21         attr for attr in dir(Generator)
22         if not attr.startswith('__')
23         and attr not in ['seed', 'seed_instance', 'random']
24     ]
25 
26     def __init__(self, locale=None, providers=None,
27                  generator=None, includes=None,
28                  use_weighting=True, **config):
29         self._factory_map = OrderedDict()
30         self._weights = None
31         self._unique_proxy = UniqueProxy(self)
32 
33         if isinstance(locale, str):
34             locales = [locale.replace('-', '_')]
35 
36         # This guarantees a FIFO ordering of elements in `locales` based on the final
37         # locale string while discarding duplicates after processing
38         elif isinstance(locale, (list, tuple, set)):
39             locales = []
40             for code in locale:
41                 if not isinstance(code, str):
42                     raise TypeError('The locale "%s" must be a string.' % str(code))
43                 final_locale = code.replace('-', '_')
44                 if final_locale not in locales:
45                     locales.append(final_locale)
46 
47         elif isinstance(locale, OrderedDict):
48             assert all(isinstance(v, (int, float)) for v in locale.values())
49             odict = OrderedDict()
50             for k, v in locale.items():
51                 key = k.replace('-', '_')
52                 odict[key] = v
53             locales = list(odict.keys())
54             self._weights = list(odict.values())
55 
56         else:
57             locales = [DEFAULT_LOCALE]
58 
59         for locale in locales:
60             self._factory_map[locale] = Factory.create(locale, providers, generator, includes,
61                                                        use_weighting=use_weighting,
62                                                        **config)
63 
64         self._locales = locales
65         self._factories = list(self._factory_map.values())
66 
67     def __dir__(self):
68         attributes = set(super(Faker, self).__dir__())
69         for factory in self.factories:
70             attributes |= {
71                 attr for attr in dir(factory) if not attr.startswith('_')
72             }
73         return sorted(attributes)
74 
75     def __getitem__(self, locale):
76         return self._factory_map[locale.replace('-', '_')]
77 
78     def __getattribute__(self, attr):
79         """
80         Handles the "attribute resolution" behavior for declared members of this proxy class
81 
82         The class method `seed` cannot be called from an instance.
83 
84         :param attr: attribute name
85         :return: the appropriate attribute
86         """
87         if attr == 'seed':
88             msg = (
89                 'Calling `.seed()` on instances is deprecated. '
90                 'Use the class method `Faker.seed()` instead.'
91             )
92             raise TypeError(msg)
93         else:
94             return super().__getattribute__(attr)
95 
96     def __getattr__(self, attr):
97         """
98         Handles cache access and proxying behavior
99 
100         :param attr: attribute name
101         :return: the appropriate attribute
102         """
103 
104         if len(self._factories) == 1:
105             return getattr(self._factories[0], attr)
106         elif attr in self.generator_attrs:
107             msg = 'Proxying calls to `%s` is not implemented in multiple locale mode.' % attr
108             raise NotImplementedError(msg)
109         elif self.cache_pattern.match(attr):
110             msg = 'Cached attribute `%s` does not exist' % attr
111             raise AttributeError(msg)
112         else:
113             factory = self._select_factory(attr)
114             return getattr(factory, attr)
115 
116     @property
117     def unique(self):
118         return self._unique_proxy
119 
120     def _select_factory(self, method_name):
121         """
122         Returns a random factory that supports the provider method
123 
124         :param method_name: Name of provider method
125         :return: A factory that supports the provider method
126         """
127 
128         factories, weights = self._map_provider_method(method_name)
129         if len(factories) == 0:
130             msg = f'No generator object has attribute {method_name!r}'
131             raise AttributeError(msg)
132         elif len(factories) == 1:
133             return factories[0]
134 
135         if weights:
136             factory = choices_distribution(factories, weights, length=1)[0]
137         else:
138             factory = random.choice(factories)
139         return factory
140 
141     def _map_provider_method(self, method_name):
142         """
143         Creates a 2-tuple of factories and weights for the given provider method name
144 
145         The first element of the tuple contains a list of compatible factories.
146         The second element of the tuple contains a list of distribution weights.
147 
148         :param method_name: Name of provider method
149         :return: 2-tuple (factories, weights)
150         """
151 
152         # Return cached mapping if it exists for given method
153         attr = f'_cached_{method_name}_mapping'
154         if hasattr(self, attr):
155             return getattr(self, attr)
156 
157         # Create mapping if it does not exist
158         if self._weights:
159             value = [
160                 (factory, weight)
161                 for factory, weight in zip(self.factories, self._weights)
162                 if hasattr(factory, method_name)
163             ]
164             factories, weights = zip(*value)
165             mapping = list(factories), list(weights)
166         else:
167             value = [
168                 factory
169                 for factory in self.factories
170                 if hasattr(factory, method_name)
171             ]
172             mapping = value, None
173 
174         # Then cache and return results
175         setattr(self, attr, mapping)
176         return mapping
177 
178     @classmethod
179     def seed(cls, seed=None):
180         """
181         Seeds the shared `random.Random` object across all factories
182 
183         :param seed: seed value
184         """
185         Generator.seed(seed)
186 
187     def seed_instance(self, seed=None):
188         """
189         Creates and seeds a new `random.Random` object for each factory
190 
191         :param seed: seed value
192         """
193         for factory in self._factories:
194             factory.seed_instance(seed)
195 
196     def seed_locale(self, locale, seed=None):
197         """
198         Creates and seeds a new `random.Random` object for the factory of the specified locale
199 
200         :param locale: locale string
201         :param seed: seed value
202         """
203         self._factory_map[locale.replace('-', '_')].seed_instance(seed)
204 
205     @property
206     def random(self):
207         """
208         Proxies `random` getter calls
209 
210         In single locale mode, this will be proxied to the `random` getter
211         of the only internal `Generator` object. Subclasses will have to
212         implement desired behavior in multiple locale mode.
213         """
214 
215         if len(self._factories) == 1:
216             return self._factories[0].random
217         else:
218             msg = 'Proxying `random` getter calls is not implemented in multiple locale mode.'
219             raise NotImplementedError(msg)
220 
221     @random.setter
222     def random(self, value):
223         """
224         Proxies `random` setter calls
225 
226         In single locale mode, this will be proxied to the `random` setter
227         of the only internal `Generator` object. Subclasses will have to
228         implement desired behavior in multiple locale mode.
229         """
230 
231         if len(self._factories) == 1:
232             self._factories[0].random = value
233         else:
234             msg = 'Proxying `random` setter calls is not implemented in multiple locale mode.'
235             raise NotImplementedError(msg)
236 
237     @property
238     def locales(self):
239         return list(self._locales)
240 
241     @property
242     def weights(self):
243         return self._weights
244 
245     @property
246     def factories(self):
247         return self._factories
248 
249     def items(self):
250         return self._factory_map.items()
251 
252 
253 class UniqueProxy:
254     def __init__(self, proxy):
255         self._proxy = proxy
256         self._seen = {}
257         self._sentinel = object()
258 
259     def clear(self):
260         self._seen = {}
261 
262     def __getattr__(self, name: str):
263         obj = getattr(self._proxy, name)
264         if callable(obj):
265             return self._wrap(name, obj)
266         else:
267             raise TypeError("Accessing non-functions through .unique is not supported.")
268 
269     def _wrap(self, name, function):
270         @functools.wraps(function)
271         def wrapper(*args, **kwargs):
272             key = (name, args, tuple(sorted(kwargs.items())))
273 
274             generated = self._seen.setdefault(key, {self._sentinel})
275 
276             # With use of a sentinel value rather than None, we leave
277             # None open as a valid return value.
278             retval = self._sentinel
279 
280             for i in range(_UNIQUE_ATTEMPTS):
281                 if retval not in generated:
282                     break
283                 retval = function(*args, **kwargs)
284             else:
285                 raise UniquenessException(f'Got duplicated values after {_UNIQUE_ATTEMPTS:,} iterations.')
286 
287             generated.add(retval)
288 
289             return retval
290 
291         return wrapper
292
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/faker/proxy.py b/faker/proxy.py
--- a/faker/proxy.py
+++ b/faker/proxy.py
@@ -1,3 +1,4 @@
+import copy
 import functools
 import random
 import re
@@ -100,7 +101,6 @@
         :param attr: attribute name
         :return: the appropriate attribute
         """
-
         if len(self._factories) == 1:
             return getattr(self._factories[0], attr)
         elif attr in self.generator_attrs:
@@ -113,6 +113,20 @@
             factory = self._select_factory(attr)
             return getattr(factory, attr)
 
+    def __deepcopy__(self, memodict={}):
+        cls = self.__class__
+        result = cls.__new__(cls)
+        result._locales = copy.deepcopy(self._locales)
+        result._factories = copy.deepcopy(self._factories)
+        result._factory_map = copy.deepcopy(self._factory_map)
+        result._weights = copy.deepcopy(self._weights)
+        result._unique_proxy = UniqueProxy(self)
+        result._unique_proxy._seen = {
+            k: {result._unique_proxy._sentinel}
+            for k in self._unique_proxy._seen.keys()
+        }
+        return result
+
     @property
     def unique(self):
         return self._unique_proxy

{"golden_diff": "diff --git a/faker/proxy.py b/faker/proxy.py\n--- a/faker/proxy.py\n+++ b/faker/proxy.py\n@@ -1,3 +1,4 @@\n+import copy\n import functools\n import random\n import re\n@@ -100,7 +101,6 @@\n         :param attr: attribute name\n         :return: the appropriate attribute\n         \"\"\"\n-\n         if len(self._factories) == 1:\n             return getattr(self._factories[0], attr)\n         elif attr in self.generator_attrs:\n@@ -113,6 +113,20 @@\n             factory = self._select_factory(attr)\n             return getattr(factory, attr)\n \n+    def __deepcopy__(self, memodict={}):\n+        cls = self.__class__\n+        result = cls.__new__(cls)\n+        result._locales = copy.deepcopy(self._locales)\n+        result._factories = copy.deepcopy(self._factories)\n+        result._factory_map = copy.deepcopy(self._factory_map)\n+        result._weights = copy.deepcopy(self._weights)\n+        result._unique_proxy = UniqueProxy(self)\n+        result._unique_proxy._seen = {\n+            k: {result._unique_proxy._sentinel}\n+            for k in self._unique_proxy._seen.keys()\n+        }\n+        return result\n+\n     @property\n     def unique(self):\n         return self._unique_proxy\n", "issue": "Raises RecursionError under certain conditions\n* Faker version: 8.4.0\r\n* OS: Arch Linux with kernel 5.12.6\r\n\r\nCertain conditions can trigger a `RecursionError: maximum recursion depth exceeded`. I encountered this error in a django project, see below the steps to reproduce this error.\r\n\r\n### Steps to reproduce\r\n\r\n1. `pip install django faker`\r\n1. `django-admin startproject mysite`\r\n1. In the app mysite create a file called _test_example.py_, the source code is listed below\r\n1. And in the app mysite create a file called _my_faker.py_, the source code is listed below\r\n\r\n```python\r\n# test_example.py\r\nfrom django.test import TestCase\r\nfrom faker import Faker\r\n\r\nfrom .my_faker import MyFaker\r\n\r\n\r\nclass ExampleTest(TestCase):\r\n    @classmethod\r\n    def setUpTestData(cls):\r\n        cls.faker_example = MyFaker()\r\n\r\n    def setUp(self):\r\n        self.fake = Faker()\r\n\r\n        self.example = self.faker_example.example()\r\n\r\n    def test_example(self):\r\n        self.assertEqual(1, 1)\r\n```\r\n\r\n```python\r\n# my_faker.py\r\nfrom faker import Faker\r\n\r\n\r\nclass MyFaker:\r\n    def __init__(self):\r\n        self.fake = Faker()\r\n\r\n    def example(self):\r\n        pass\r\n```\r\n\r\n\r\n### Expected behavior\r\n\r\nNo RecursionError, it worked before. Last time about 2 month ago with `faker==5.6.5`, however this version does not work on updated systems as of the `AttributeError: 'PosixPath' object has no attribute 'startswith'`error but which was fixed recently.\r\n\r\n### Actual behavior\r\n```\r\n$ python manage.py test\r\nCreating test database for alias 'default'...\r\nSystem check identified no issues (0 silenced).\r\nE\r\n======================================================================\r\nERROR: test_example (mysite.test_example.ExampleTest)\r\n----------------------------------------------------------------------\r\nTraceback (most recent call last):\r\n  File \"/home/username/Downloads/mysite/mysite/test_example.py\", line 15, in setUp\r\n    self.example = self.faker_example.example()\r\n  File \"/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/django/test/testcases.py\", line 1124, in __get__\r\n    data = deepcopy(self.data, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 172, in deepcopy\r\n    y = _reconstruct(x, memo, *rv)\r\n  File \"/usr/lib/python3.9/copy.py\", line 270, in _reconstruct\r\n    state = deepcopy(state, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 146, in deepcopy\r\n    y = copier(x, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 230, in _deepcopy_dict\r\n    y[deepcopy(key, memo)] = deepcopy(value, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 172, in deepcopy\r\n    y = _reconstruct(x, memo, *rv)\r\n  File \"/usr/lib/python3.9/copy.py\", line 270, in _reconstruct\r\n    state = deepcopy(state, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 146, in deepcopy\r\n    y = copier(x, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 230, in _deepcopy_dict\r\n    y[deepcopy(key, memo)] = deepcopy(value, memo)\r\n  File \"/usr/lib/python3.9/copy.py\", line 172, in deepcopy\r\n    y = _reconstruct(x, memo, *rv)\r\n  File \"/usr/lib/python3.9/copy.py\", line 271, in _reconstruct\r\n    if hasattr(y, '__setstate__'):\r\n  File \"/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/faker/proxy.py\", line 263, in __getattr__\r\n    obj = getattr(self._proxy, name)\r\n  File \"/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/faker/proxy.py\", line 263, in __getattr__\r\n    obj = getattr(self._proxy, name)\r\n  File \"/home/username/.virtualenvs/faker-test/lib/python3.9/site-packages/faker/proxy.py\", line 263, in __getattr__\r\n    obj = getattr(self._proxy, name)\r\n  [Previous line repeated 964 more times]\r\nRecursionError: maximum recursion depth exceeded\r\n```\r\n\n", "before_files": [{"content": "import functools\nimport random\nimport re\n\nfrom collections import OrderedDict\n\nfrom faker.config import DEFAULT_LOCALE\nfrom faker.exceptions import UniquenessException\nfrom faker.factory import Factory\nfrom faker.generator import Generator\nfrom faker.utils.distribution import choices_distribution\n\n_UNIQUE_ATTEMPTS = 1000\n\n\nclass Faker:\n    \"\"\"Proxy class capable of supporting multiple locales\"\"\"\n\n    cache_pattern = re.compile(r'^_cached_\\w*_mapping$')\n    generator_attrs = [\n        attr for attr in dir(Generator)\n        if not attr.startswith('__')\n        and attr not in ['seed', 'seed_instance', 'random']\n    ]\n\n    def __init__(self, locale=None, providers=None,\n                 generator=None, includes=None,\n                 use_weighting=True, **config):\n        self._factory_map = OrderedDict()\n        self._weights = None\n        self._unique_proxy = UniqueProxy(self)\n\n        if isinstance(locale, str):\n            locales = [locale.replace('-', '_')]\n\n        # This guarantees a FIFO ordering of elements in `locales` based on the final\n        # locale string while discarding duplicates after processing\n        elif isinstance(locale, (list, tuple, set)):\n            locales = []\n            for code in locale:\n                if not isinstance(code, str):\n                    raise TypeError('The locale \"%s\" must be a string.' % str(code))\n                final_locale = code.replace('-', '_')\n                if final_locale not in locales:\n                    locales.append(final_locale)\n\n        elif isinstance(locale, OrderedDict):\n            assert all(isinstance(v, (int, float)) for v in locale.values())\n            odict = OrderedDict()\n            for k, v in locale.items():\n                key = k.replace('-', '_')\n                odict[key] = v\n            locales = list(odict.keys())\n            self._weights = list(odict.values())\n\n        else:\n            locales = [DEFAULT_LOCALE]\n\n        for locale in locales:\n            self._factory_map[locale] = Factory.create(locale, providers, generator, includes,\n                                                       use_weighting=use_weighting,\n                                                       **config)\n\n        self._locales = locales\n        self._factories = list(self._factory_map.values())\n\n    def __dir__(self):\n        attributes = set(super(Faker, self).__dir__())\n        for factory in self.factories:\n            attributes |= {\n                attr for attr in dir(factory) if not attr.startswith('_')\n            }\n        return sorted(attributes)\n\n    def __getitem__(self, locale):\n        return self._factory_map[locale.replace('-', '_')]\n\n    def __getattribute__(self, attr):\n        \"\"\"\n        Handles the \"attribute resolution\" behavior for declared members of this proxy class\n\n        The class method `seed` cannot be called from an instance.\n\n        :param attr: attribute name\n        :return: the appropriate attribute\n        \"\"\"\n        if attr == 'seed':\n            msg = (\n                'Calling `.seed()` on instances is deprecated. '\n                'Use the class method `Faker.seed()` instead.'\n            )\n            raise TypeError(msg)\n        else:\n            return super().__getattribute__(attr)\n\n    def __getattr__(self, attr):\n        \"\"\"\n        Handles cache access and proxying behavior\n\n        :param attr: attribute name\n        :return: the appropriate attribute\n        \"\"\"\n\n        if len(self._factories) == 1:\n            return getattr(self._factories[0], attr)\n        elif attr in self.generator_attrs:\n            msg = 'Proxying calls to `%s` is not implemented in multiple locale mode.' % attr\n            raise NotImplementedError(msg)\n        elif self.cache_pattern.match(attr):\n            msg = 'Cached attribute `%s` does not exist' % attr\n            raise AttributeError(msg)\n        else:\n            factory = self._select_factory(attr)\n            return getattr(factory, attr)\n\n    @property\n    def unique(self):\n        return self._unique_proxy\n\n    def _select_factory(self, method_name):\n        \"\"\"\n        Returns a random factory that supports the provider method\n\n        :param method_name: Name of provider method\n        :return: A factory that supports the provider method\n        \"\"\"\n\n        factories, weights = self._map_provider_method(method_name)\n        if len(factories) == 0:\n            msg = f'No generator object has attribute {method_name!r}'\n            raise AttributeError(msg)\n        elif len(factories) == 1:\n            return factories[0]\n\n        if weights:\n            factory = choices_distribution(factories, weights, length=1)[0]\n        else:\n            factory = random.choice(factories)\n        return factory\n\n    def _map_provider_method(self, method_name):\n        \"\"\"\n        Creates a 2-tuple of factories and weights for the given provider method name\n\n        The first element of the tuple contains a list of compatible factories.\n        The second element of the tuple contains a list of distribution weights.\n\n        :param method_name: Name of provider method\n        :return: 2-tuple (factories, weights)\n        \"\"\"\n\n        # Return cached mapping if it exists for given method\n        attr = f'_cached_{method_name}_mapping'\n        if hasattr(self, attr):\n            return getattr(self, attr)\n\n        # Create mapping if it does not exist\n        if self._weights:\n            value = [\n                (factory, weight)\n                for factory, weight in zip(self.factories, self._weights)\n                if hasattr(factory, method_name)\n            ]\n            factories, weights = zip(*value)\n            mapping = list(factories), list(weights)\n        else:\n            value = [\n                factory\n                for factory in self.factories\n                if hasattr(factory, method_name)\n            ]\n            mapping = value, None\n\n        # Then cache and return results\n        setattr(self, attr, mapping)\n        return mapping\n\n    @classmethod\n    def seed(cls, seed=None):\n        \"\"\"\n        Seeds the shared `random.Random` object across all factories\n\n        :param seed: seed value\n        \"\"\"\n        Generator.seed(seed)\n\n    def seed_instance(self, seed=None):\n        \"\"\"\n        Creates and seeds a new `random.Random` object for each factory\n\n        :param seed: seed value\n        \"\"\"\n        for factory in self._factories:\n            factory.seed_instance(seed)\n\n    def seed_locale(self, locale, seed=None):\n        \"\"\"\n        Creates and seeds a new `random.Random` object for the factory of the specified locale\n\n        :param locale: locale string\n        :param seed: seed value\n        \"\"\"\n        self._factory_map[locale.replace('-', '_')].seed_instance(seed)\n\n    @property\n    def random(self):\n        \"\"\"\n        Proxies `random` getter calls\n\n        In single locale mode, this will be proxied to the `random` getter\n        of the only internal `Generator` object. Subclasses will have to\n        implement desired behavior in multiple locale mode.\n        \"\"\"\n\n        if len(self._factories) == 1:\n            return self._factories[0].random\n        else:\n            msg = 'Proxying `random` getter calls is not implemented in multiple locale mode.'\n            raise NotImplementedError(msg)\n\n    @random.setter\n    def random(self, value):\n        \"\"\"\n        Proxies `random` setter calls\n\n        In single locale mode, this will be proxied to the `random` setter\n        of the only internal `Generator` object. Subclasses will have to\n        implement desired behavior in multiple locale mode.\n        \"\"\"\n\n        if len(self._factories) == 1:\n            self._factories[0].random = value\n        else:\n            msg = 'Proxying `random` setter calls is not implemented in multiple locale mode.'\n            raise NotImplementedError(msg)\n\n    @property\n    def locales(self):\n        return list(self._locales)\n\n    @property\n    def weights(self):\n        return self._weights\n\n    @property\n    def factories(self):\n        return self._factories\n\n    def items(self):\n        return self._factory_map.items()\n\n\nclass UniqueProxy:\n    def __init__(self, proxy):\n        self._proxy = proxy\n        self._seen = {}\n        self._sentinel = object()\n\n    def clear(self):\n        self._seen = {}\n\n    def __getattr__(self, name: str):\n        obj = getattr(self._proxy, name)\n        if callable(obj):\n            return self._wrap(name, obj)\n        else:\n            raise TypeError(\"Accessing non-functions through .unique is not supported.\")\n\n    def _wrap(self, name, function):\n        @functools.wraps(function)\n        def wrapper(*args, **kwargs):\n            key = (name, args, tuple(sorted(kwargs.items())))\n\n            generated = self._seen.setdefault(key, {self._sentinel})\n\n            # With use of a sentinel value rather than None, we leave\n            # None open as a valid return value.\n            retval = self._sentinel\n\n            for i in range(_UNIQUE_ATTEMPTS):\n                if retval not in generated:\n                    break\n                retval = function(*args, **kwargs)\n            else:\n                raise UniquenessException(f'Got duplicated values after {_UNIQUE_ATTEMPTS:,} iterations.')\n\n            generated.add(retval)\n\n            return retval\n\n        return wrapper\n", "path": "faker/proxy.py"}], "after_files": [{"content": "import copy\nimport functools\nimport random\nimport re\n\nfrom collections import OrderedDict\n\nfrom faker.config import DEFAULT_LOCALE\nfrom faker.exceptions import UniquenessException\nfrom faker.factory import Factory\nfrom faker.generator import Generator\nfrom faker.utils.distribution import choices_distribution\n\n_UNIQUE_ATTEMPTS = 1000\n\n\nclass Faker:\n    \"\"\"Proxy class capable of supporting multiple locales\"\"\"\n\n    cache_pattern = re.compile(r'^_cached_\\w*_mapping$')\n    generator_attrs = [\n        attr for attr in dir(Generator)\n        if not attr.startswith('__')\n        and attr not in ['seed', 'seed_instance', 'random']\n    ]\n\n    def __init__(self, locale=None, providers=None,\n                 generator=None, includes=None,\n                 use_weighting=True, **config):\n        self._factory_map = OrderedDict()\n        self._weights = None\n        self._unique_proxy = UniqueProxy(self)\n\n        if isinstance(locale, str):\n            locales = [locale.replace('-', '_')]\n\n        # This guarantees a FIFO ordering of elements in `locales` based on the final\n        # locale string while discarding duplicates after processing\n        elif isinstance(locale, (list, tuple, set)):\n            locales = []\n            for code in locale:\n                if not isinstance(code, str):\n                    raise TypeError('The locale \"%s\" must be a string.' % str(code))\n                final_locale = code.replace('-', '_')\n                if final_locale not in locales:\n                    locales.append(final_locale)\n\n        elif isinstance(locale, OrderedDict):\n            assert all(isinstance(v, (int, float)) for v in locale.values())\n            odict = OrderedDict()\n            for k, v in locale.items():\n                key = k.replace('-', '_')\n                odict[key] = v\n            locales = list(odict.keys())\n            self._weights = list(odict.values())\n\n        else:\n            locales = [DEFAULT_LOCALE]\n\n        for locale in locales:\n            self._factory_map[locale] = Factory.create(locale, providers, generator, includes,\n                                                       use_weighting=use_weighting,\n                                                       **config)\n\n        self._locales = locales\n        self._factories = list(self._factory_map.values())\n\n    def __dir__(self):\n        attributes = set(super(Faker, self).__dir__())\n        for factory in self.factories:\n            attributes |= {\n                attr for attr in dir(factory) if not attr.startswith('_')\n            }\n        return sorted(attributes)\n\n    def __getitem__(self, locale):\n        return self._factory_map[locale.replace('-', '_')]\n\n    def __getattribute__(self, attr):\n        \"\"\"\n        Handles the \"attribute resolution\" behavior for declared members of this proxy class\n\n        The class method `seed` cannot be called from an instance.\n\n        :param attr: attribute name\n        :return: the appropriate attribute\n        \"\"\"\n        if attr == 'seed':\n            msg = (\n                'Calling `.seed()` on instances is deprecated. '\n                'Use the class method `Faker.seed()` instead.'\n            )\n            raise TypeError(msg)\n        else:\n            return super().__getattribute__(attr)\n\n    def __getattr__(self, attr):\n        \"\"\"\n        Handles cache access and proxying behavior\n\n        :param attr: attribute name\n        :return: the appropriate attribute\n        \"\"\"\n        if len(self._factories) == 1:\n            return getattr(self._factories[0], attr)\n        elif attr in self.generator_attrs:\n            msg = 'Proxying calls to `%s` is not implemented in multiple locale mode.' % attr\n            raise NotImplementedError(msg)\n        elif self.cache_pattern.match(attr):\n            msg = 'Cached attribute `%s` does not exist' % attr\n            raise AttributeError(msg)\n        else:\n            factory = self._select_factory(attr)\n            return getattr(factory, attr)\n\n    def __deepcopy__(self, memodict={}):\n        cls = self.__class__\n        result = cls.__new__(cls)\n        result._locales = copy.deepcopy(self._locales)\n        result._factories = copy.deepcopy(self._factories)\n        result._factory_map = copy.deepcopy(self._factory_map)\n        result._weights = copy.deepcopy(self._weights)\n        result._unique_proxy = UniqueProxy(self)\n        result._unique_proxy._seen = {\n            k: {result._unique_proxy._sentinel}\n            for k in self._unique_proxy._seen.keys()\n        }\n        return result\n\n    @property\n    def unique(self):\n        return self._unique_proxy\n\n    def _select_factory(self, method_name):\n        \"\"\"\n        Returns a random factory that supports the provider method\n\n        :param method_name: Name of provider method\n        :return: A factory that supports the provider method\n        \"\"\"\n\n        factories, weights = self._map_provider_method(method_name)\n        if len(factories) == 0:\n            msg = f'No generator object has attribute {method_name!r}'\n            raise AttributeError(msg)\n        elif len(factories) == 1:\n            return factories[0]\n\n        if weights:\n            factory = choices_distribution(factories, weights, length=1)[0]\n        else:\n            factory = random.choice(factories)\n        return factory\n\n    def _map_provider_method(self, method_name):\n        \"\"\"\n        Creates a 2-tuple of factories and weights for the given provider method name\n\n        The first element of the tuple contains a list of compatible factories.\n        The second element of the tuple contains a list of distribution weights.\n\n        :param method_name: Name of provider method\n        :return: 2-tuple (factories, weights)\n        \"\"\"\n\n        # Return cached mapping if it exists for given method\n        attr = f'_cached_{method_name}_mapping'\n        if hasattr(self, attr):\n            return getattr(self, attr)\n\n        # Create mapping if it does not exist\n        if self._weights:\n            value = [\n                (factory, weight)\n                for factory, weight in zip(self.factories, self._weights)\n                if hasattr(factory, method_name)\n            ]\n            factories, weights = zip(*value)\n            mapping = list(factories), list(weights)\n        else:\n            value = [\n                factory\n                for factory in self.factories\n                if hasattr(factory, method_name)\n            ]\n            mapping = value, None\n\n        # Then cache and return results\n        setattr(self, attr, mapping)\n        return mapping\n\n    @classmethod\n    def seed(cls, seed=None):\n        \"\"\"\n        Seeds the shared `random.Random` object across all factories\n\n        :param seed: seed value\n        \"\"\"\n        Generator.seed(seed)\n\n    def seed_instance(self, seed=None):\n        \"\"\"\n        Creates and seeds a new `random.Random` object for each factory\n\n        :param seed: seed value\n        \"\"\"\n        for factory in self._factories:\n            factory.seed_instance(seed)\n\n    def seed_locale(self, locale, seed=None):\n        \"\"\"\n        Creates and seeds a new `random.Random` object for the factory of the specified locale\n\n        :param locale: locale string\n        :param seed: seed value\n        \"\"\"\n        self._factory_map[locale.replace('-', '_')].seed_instance(seed)\n\n    @property\n    def random(self):\n        \"\"\"\n        Proxies `random` getter calls\n\n        In single locale mode, this will be proxied to the `random` getter\n        of the only internal `Generator` object. Subclasses will have to\n        implement desired behavior in multiple locale mode.\n        \"\"\"\n\n        if len(self._factories) == 1:\n            return self._factories[0].random\n        else:\n            msg = 'Proxying `random` getter calls is not implemented in multiple locale mode.'\n            raise NotImplementedError(msg)\n\n    @random.setter\n    def random(self, value):\n        \"\"\"\n        Proxies `random` setter calls\n\n        In single locale mode, this will be proxied to the `random` setter\n        of the only internal `Generator` object. Subclasses will have to\n        implement desired behavior in multiple locale mode.\n        \"\"\"\n\n        if len(self._factories) == 1:\n            self._factories[0].random = value\n        else:\n            msg = 'Proxying `random` setter calls is not implemented in multiple locale mode.'\n            raise NotImplementedError(msg)\n\n    @property\n    def locales(self):\n        return list(self._locales)\n\n    @property\n    def weights(self):\n        return self._weights\n\n    @property\n    def factories(self):\n        return self._factories\n\n    def items(self):\n        return self._factory_map.items()\n\n\nclass UniqueProxy:\n    def __init__(self, proxy):\n        self._proxy = proxy\n        self._seen = {}\n        self._sentinel = object()\n\n    def clear(self):\n        self._seen = {}\n\n    def __getattr__(self, name: str):\n        obj = getattr(self._proxy, name)\n        if callable(obj):\n            return self._wrap(name, obj)\n        else:\n            raise TypeError(\"Accessing non-functions through .unique is not supported.\")\n\n    def _wrap(self, name, function):\n        @functools.wraps(function)\n        def wrapper(*args, **kwargs):\n            key = (name, args, tuple(sorted(kwargs.items())))\n\n            generated = self._seen.setdefault(key, {self._sentinel})\n\n            # With use of a sentinel value rather than None, we leave\n            # None open as a valid return value.\n            retval = self._sentinel\n\n            for i in range(_UNIQUE_ATTEMPTS):\n                if retval not in generated:\n                    break\n                retval = function(*args, **kwargs)\n            else:\n                raise UniquenessException(f'Got duplicated values after {_UNIQUE_ATTEMPTS:,} iterations.')\n\n            generated.add(retval)\n\n            return retval\n\n        return wrapper\n", "path": "faker/proxy.py"}]}