Repository: joke2k/faker

Issue:
'Generator' object has no attribute 'locale' when calling user_agent()
The user_agent method intermittently will fail with the message `AttributeError: 'Generator' object has no attribute 'locale'`

### Steps to reproduce
#### Systems bug has been reproduced on
Python: Python 3.7.0 & 3.7.1
Faker version: git and pypi 1.0.2 both tested.
Platform: Windows 10x64 & Arch Linux 4.20.0-arch1-1-ARCH

```
for _ in range(10):
     Faker(providers=['faker.providers.user_agent', 
                      'faker.providers.date_time']).user_agent()
```
Manually specifed `locale` has no effect. The snippet below is effectively the same in the case of this bug as the one above:
```
for _ in range(10):
     Faker(providers=['faker.providers.user_agent', 
                      'faker.providers.date_time'], locale='en_US').user_agent()
```
### Expected behavior

`user_agent()` should work 100% of the time.

### Actual behavior

A error is raised with the following traceback:

```
Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3265, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-319-5c64504eb0d2>", line 1, in <module>
    Faker(providers=['faker.providers.user_agent', 'faker.providers.date_time', 'faker.providers.internet']).user_agent()
  File "C:\Program Files\Python37\lib\site-packages\faker\providers\user_agent\__init__.py", line 34, in user_agent
    return getattr(self, name)()
  File "C:\Program Files\Python37\lib\site-packages\faker\providers\user_agent\__init__.py", line 114, in safari/firefox/opera
    locale = self.generator.locale().replace('_', '-')
AttributeError: 'Generator' object has no attribute 'locale'
```

Note: only occurs in `firefox`,`safari`, and `opera`. Am I missing a provider to import?

'Generator' object has no attribute 'locale' when calling user_agent()
The user_agent method intermittently will fail with the message `AttributeError: 'Generator' object has no attribute 'locale'`

### Steps to reproduce
#### Systems bug has been reproduced on
Python: Python 3.7.0 & 3.7.1
Faker version: git and pypi 1.0.2 both tested.
Platform: Windows 10x64 & Arch Linux 4.20.0-arch1-1-ARCH

```
for _ in range(10):
     Faker(providers=['faker.providers.user_agent', 
                      'faker.providers.date_time']).user_agent()
```
Manually specifed `locale` has no effect. The snippet below is effectively the same in the case of this bug as the one above:
```
for _ in range(10):
     Faker(providers=['faker.providers.user_agent', 
                      'faker.providers.date_time'], locale='en_US').user_agent()
```
### Expected behavior

`user_agent()` should work 100% of the time.

### Actual behavior

A error is raised with the following traceback:

```
Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3265, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-319-5c64504eb0d2>", line 1, in <module>
    Faker(providers=['faker.providers.user_agent', 'faker.providers.date_time', 'faker.providers.internet']).user_agent()
  File "C:\Program Files\Python37\lib\site-packages\faker\providers\user_agent\__init__.py", line 34, in user_agent
    return getattr(self, name)()
  File "C:\Program Files\Python37\lib\site-packages\faker\providers\user_agent\__init__.py", line 114, in safari/firefox/opera
    locale = self.generator.locale().replace('_', '-')
AttributeError: 'Generator' object has no attribute 'locale'
```

Note: only occurs in `firefox`,`safari`, and `opera`. Am I missing a provider to import?



Discussion:
The issue is that the `user_agent` providers is depending on the `misc` provider, which you are not loading. I'm going to move the required methods to our base provider, but for the time being you should be able to call:
```
Faker(providers=[
    'faker.providers.user_agent', 'faker.providers.date_time', 'faker.providers.misc',
]).user_agent()`
```
The issue is that the `user_agent` providers is depending on the `misc` provider, which you are not loading. I'm going to move the required methods to our base provider, but for the time being you should be able to call:
```
Faker(providers=[
    'faker.providers.user_agent', 'faker.providers.date_time', 'faker.providers.misc',
]).user_agent()`
```

Resolving patch:
diff --git a/faker/providers/__init__.py b/faker/providers/__init__.py
--- a/faker/providers/__init__.py
+++ b/faker/providers/__init__.py
@@ -19,9 +19,80 @@ class BaseProvider(object):
     __provider__ = 'base'
     __lang__ = None
 
+    # Locales supported by Linux Mint from `/usr/share/i18n/SUPPORTED`
+    language_locale_codes = {
+        'aa': ('DJ', 'ER', 'ET'), 'af': ('ZA',), 'ak': ('GH',), 'am': ('ET',),
+        'an': ('ES',), 'apn': ('IN',),
+        'ar': ('AE', 'BH', 'DJ', 'DZ', 'EG', 'EH', 'ER', 'IL', 'IN',
+               'IQ', 'JO', 'KM', 'KW', 'LB', 'LY', 'MA', 'MR', 'OM',
+               'PS', 'QA', 'SA', 'SD', 'SO', 'SS', 'SY', 'TD', 'TN',
+               'YE'),
+        'as': ('IN',), 'ast': ('ES',), 'ayc': ('PE',), 'az': ('AZ', 'IN'),
+        'be': ('BY',), 'bem': ('ZM',), 'ber': ('DZ', 'MA'), 'bg': ('BG',),
+        'bhb': ('IN',), 'bho': ('IN',), 'bn': ('BD', 'IN'), 'bo': ('CN', 'IN'),
+        'br': ('FR',), 'brx': ('IN',), 'bs': ('BA',), 'byn': ('ER',),
+        'ca': ('AD', 'ES', 'FR', 'IT'), 'ce': ('RU',), 'ckb': ('IQ',),
+        'cmn': ('TW',), 'crh': ('UA',), 'cs': ('CZ',), 'csb': ('PL',),
+        'cv': ('RU',), 'cy': ('GB',), 'da': ('DK',),
+        'de': ('AT', 'BE', 'CH', 'DE', 'LI', 'LU'), 'doi': ('IN',),
+        'dv': ('MV',), 'dz': ('BT',), 'el': ('GR', 'CY'),
+        'en': ('AG', 'AU', 'BW', 'CA', 'DK', 'GB', 'HK', 'IE', 'IN', 'NG',
+               'NZ', 'PH', 'SG', 'US', 'ZA', 'ZM', 'ZW'),
+        'eo': ('US',),
+        'es': ('AR', 'BO', 'CL', 'CO', 'CR', 'CU', 'DO', 'EC', 'ES', 'GT',
+               'HN', 'MX', 'NI', 'PA', 'PE', 'PR', 'PY', 'SV', 'US', 'UY', 'VE',
+               ), 'et': ('EE',), 'eu': ('ES', 'FR'), 'fa': ('IR',),
+        'ff': ('SN',), 'fi': ('FI',), 'fil': ('PH',), 'fo': ('FO',),
+        'fr': ('CA', 'CH', 'FR', 'LU'), 'fur': ('IT',), 'fy': ('NL', 'DE'),
+        'ga': ('IE',), 'gd': ('GB',), 'gez': ('ER', 'ET'), 'gl': ('ES',),
+        'gu': ('IN',), 'gv': ('GB',), 'ha': ('NG',), 'hak': ('TW',),
+        'he': ('IL',), 'hi': ('IN',), 'hne': ('IN',), 'hr': ('HR',),
+        'hsb': ('DE',), 'ht': ('HT',), 'hu': ('HU',), 'hy': ('AM',),
+        'ia': ('FR',), 'id': ('ID',), 'ig': ('NG',), 'ik': ('CA',),
+        'is': ('IS',), 'it': ('CH', 'IT'), 'iu': ('CA',), 'iw': ('IL',),
+        'ja': ('JP',), 'ka': ('GE',), 'kk': ('KZ',), 'kl': ('GL',),
+        'km': ('KH',), 'kn': ('IN',), 'ko': ('KR',), 'kok': ('IN',),
+        'ks': ('IN',), 'ku': ('TR',), 'kw': ('GB',), 'ky': ('KG',),
+        'lb': ('LU',), 'lg': ('UG',), 'li': ('BE', 'NL'), 'lij': ('IT',),
+        'ln': ('CD',), 'lo': ('LA',), 'lt': ('LT',), 'lv': ('LV',),
+        'lzh': ('TW',), 'mag': ('IN',), 'mai': ('IN',), 'mg': ('MG',),
+        'mhr': ('RU',), 'mi': ('NZ',), 'mk': ('MK',), 'ml': ('IN',),
+        'mn': ('MN',), 'mni': ('IN',), 'mr': ('IN',), 'ms': ('MY',),
+        'mt': ('MT',), 'my': ('MM',), 'nan': ('TW',), 'nb': ('NO',),
+        'nds': ('DE', 'NL'), 'ne': ('NP',), 'nhn': ('MX',),
+        'niu': ('NU', 'NZ'), 'nl': ('AW', 'BE', 'NL'), 'nn': ('NO',),
+        'nr': ('ZA',), 'nso': ('ZA',), 'oc': ('FR',), 'om': ('ET', 'KE'),
+        'or': ('IN',), 'os': ('RU',), 'pa': ('IN', 'PK'),
+        'pap': ('AN', 'AW', 'CW'), 'pl': ('PL',), 'ps': ('AF',),
+        'pt': ('BR', 'PT'), 'quz': ('PE',), 'raj': ('IN',), 'ro': ('RO',),
+        'ru': ('RU', 'UA'), 'rw': ('RW',), 'sa': ('IN',), 'sat': ('IN',),
+        'sc': ('IT',), 'sd': ('IN', 'PK'), 'se': ('NO',), 'shs': ('CA',),
+        'si': ('LK',), 'sid': ('ET',), 'sk': ('SK',), 'sl': ('SI',),
+        'so': ('DJ', 'ET', 'KE', 'SO'), 'sq': ('AL', 'ML'), 'sr': ('ME', 'RS'),
+        'ss': ('ZA',), 'st': ('ZA',), 'sv': ('FI', 'SE'), 'sw': ('KE', 'TZ'),
+        'szl': ('PL',), 'ta': ('IN', 'LK'), 'tcy': ('IN',), 'te': ('IN',),
+        'tg': ('TJ',), 'th': ('TH',), 'the': ('NP',), 'ti': ('ER', 'ET'),
+        'tig': ('ER',), 'tk': ('TM',), 'tl': ('PH',), 'tn': ('ZA',),
+        'tr': ('CY', 'TR'), 'ts': ('ZA',), 'tt': ('RU',), 'ug': ('CN',),
+        'uk': ('UA',), 'unm': ('US',), 'ur': ('IN', 'PK'), 'uz': ('UZ',),
+        've': ('ZA',), 'vi': ('VN',), 'wa': ('BE',), 'wae': ('CH',),
+        'wal': ('ET',), 'wo': ('SN',), 'xh': ('ZA',), 'yi': ('US',),
+        'yo': ('NG',), 'yue': ('HK',), 'zh': ('CN', 'HK', 'SG', 'TW'),
+        'zu': ('ZA',),
+    }
+
     def __init__(self, generator):
         self.generator = generator
 
+    def locale(self):
+        language_code = self.language_code()
+        return language_code + '_' + self.random_element(
+            BaseProvider.language_locale_codes[language_code],
+        )
+
+    def language_code(self):
+        return self.random_element(BaseProvider.language_locale_codes.keys())
+
     def random_int(self, min=0, max=9999):
         """
         Returns a random integer between two values.
diff --git a/faker/providers/misc/__init__.py b/faker/providers/misc/__init__.py
--- a/faker/providers/misc/__init__.py
+++ b/faker/providers/misc/__init__.py
@@ -10,68 +10,6 @@
 
 
 class Provider(BaseProvider):
-    # Locales supported by Linux Mint from `/usr/share/i18n/SUPPORTED`
-    language_locale_codes = {
-        'aa': ('DJ', 'ER', 'ET'), 'af': ('ZA',), 'ak': ('GH',), 'am': ('ET',),
-        'an': ('ES',), 'apn': ('IN',),
-        'ar': ('AE', 'BH', 'DJ', 'DZ', 'EG', 'EH', 'ER', 'IL', 'IN',
-               'IQ', 'JO', 'KM', 'KW', 'LB', 'LY', 'MA', 'MR', 'OM',
-               'PS', 'QA', 'SA', 'SD', 'SO', 'SS', 'SY', 'TD', 'TN',
-               'YE'),
-        'as': ('IN',), 'ast': ('ES',), 'ayc': ('PE',), 'az': ('AZ', 'IN'),
-        'be': ('BY',), 'bem': ('ZM',), 'ber': ('DZ', 'MA'), 'bg': ('BG',),
-        'bhb': ('IN',), 'bho': ('IN',), 'bn': ('BD', 'IN'), 'bo': ('CN', 'IN'),
-        'br': ('FR',), 'brx': ('IN',), 'bs': ('BA',), 'byn': ('ER',),
-        'ca': ('AD', 'ES', 'FR', 'IT'), 'ce': ('RU',), 'ckb': ('IQ',),
-        'cmn': ('TW',), 'crh': ('UA',), 'cs': ('CZ',), 'csb': ('PL',),
-        'cv': ('RU',), 'cy': ('GB',), 'da': ('DK',),
-        'de': ('AT', 'BE', 'CH', 'DE', 'LI', 'LU'), 'doi': ('IN',),
-        'dv': ('MV',), 'dz': ('BT',), 'el': ('GR', 'CY'),
-        'en': ('AG', 'AU', 'BW', 'CA', 'DK', 'GB', 'HK', 'IE', 'IN', 'NG',
-               'NZ', 'PH', 'SG', 'US', 'ZA', 'ZM', 'ZW'),
-        'eo': ('US',),
-        'es': ('AR', 'BO', 'CL', 'CO', 'CR', 'CU', 'DO', 'EC', 'ES', 'GT',
-               'HN', 'MX', 'NI', 'PA', 'PE', 'PR', 'PY', 'SV', 'US', 'UY', 'VE',
-               ), 'et': ('EE',), 'eu': ('ES', 'FR'), 'fa': ('IR',),
-        'ff': ('SN',), 'fi': ('FI',), 'fil': ('PH',), 'fo': ('FO',),
-        'fr': ('CA', 'CH', 'FR', 'LU'), 'fur': ('IT',), 'fy': ('NL', 'DE'),
-        'ga': ('IE',), 'gd': ('GB',), 'gez': ('ER', 'ET'), 'gl': ('ES',),
-        'gu': ('IN',), 'gv': ('GB',), 'ha': ('NG',), 'hak': ('TW',),
-        'he': ('IL',), 'hi': ('IN',), 'hne': ('IN',), 'hr': ('HR',),
-        'hsb': ('DE',), 'ht': ('HT',), 'hu': ('HU',), 'hy': ('AM',),
-        'ia': ('FR',), 'id': ('ID',), 'ig': ('NG',), 'ik': ('CA',),
-        'is': ('IS',), 'it': ('CH', 'IT'), 'iu': ('CA',), 'iw': ('IL',),
-        'ja': ('JP',), 'ka': ('GE',), 'kk': ('KZ',), 'kl': ('GL',),
-        'km': ('KH',), 'kn': ('IN',), 'ko': ('KR',), 'kok': ('IN',),
-        'ks': ('IN',), 'ku': ('TR',), 'kw': ('GB',), 'ky': ('KG',),
-        'lb': ('LU',), 'lg': ('UG',), 'li': ('BE', 'NL'), 'lij': ('IT',),
-        'ln': ('CD',), 'lo': ('LA',), 'lt': ('LT',), 'lv': ('LV',),
-        'lzh': ('TW',), 'mag': ('IN',), 'mai': ('IN',), 'mg': ('MG',),
-        'mhr': ('RU',), 'mi': ('NZ',), 'mk': ('MK',), 'ml': ('IN',),
-        'mn': ('MN',), 'mni': ('IN',), 'mr': ('IN',), 'ms': ('MY',),
-        'mt': ('MT',), 'my': ('MM',), 'nan': ('TW',), 'nb': ('NO',),
-        'nds': ('DE', 'NL'), 'ne': ('NP',), 'nhn': ('MX',),
-        'niu': ('NU', 'NZ'), 'nl': ('AW', 'BE', 'NL'), 'nn': ('NO',),
-        'nr': ('ZA',), 'nso': ('ZA',), 'oc': ('FR',), 'om': ('ET', 'KE'),
-        'or': ('IN',), 'os': ('RU',), 'pa': ('IN', 'PK'),
-        'pap': ('AN', 'AW', 'CW'), 'pl': ('PL',), 'ps': ('AF',),
-        'pt': ('BR', 'PT'), 'quz': ('PE',), 'raj': ('IN',), 'ro': ('RO',),
-        'ru': ('RU', 'UA'), 'rw': ('RW',), 'sa': ('IN',), 'sat': ('IN',),
-        'sc': ('IT',), 'sd': ('IN', 'PK'), 'se': ('NO',), 'shs': ('CA',),
-        'si': ('LK',), 'sid': ('ET',), 'sk': ('SK',), 'sl': ('SI',),
-        'so': ('DJ', 'ET', 'KE', 'SO'), 'sq': ('AL', 'ML'), 'sr': ('ME', 'RS'),
-        'ss': ('ZA',), 'st': ('ZA',), 'sv': ('FI', 'SE'), 'sw': ('KE', 'TZ'),
-        'szl': ('PL',), 'ta': ('IN', 'LK'), 'tcy': ('IN',), 'te': ('IN',),
-        'tg': ('TJ',), 'th': ('TH',), 'the': ('NP',), 'ti': ('ER', 'ET'),
-        'tig': ('ER',), 'tk': ('TM',), 'tl': ('PH',), 'tn': ('ZA',),
-        'tr': ('CY', 'TR'), 'ts': ('ZA',), 'tt': ('RU',), 'ug': ('CN',),
-        'uk': ('UA',), 'unm': ('US',), 'ur': ('IN', 'PK'), 'uz': ('UZ',),
-        've': ('ZA',), 'vi': ('VN',), 'wa': ('BE',), 'wae': ('CH',),
-        'wal': ('ET',), 'wo': ('SN',), 'xh': ('ZA',), 'yi': ('US',),
-        'yo': ('NG',), 'yue': ('HK',), 'zh': ('CN', 'HK', 'SG', 'TW'),
-        'zu': ('ZA',),
-    }
-
     def boolean(self, chance_of_getting_true=50):
         return self.generator.random.randint(1, 100) <= chance_of_getting_true
 
@@ -121,15 +59,6 @@ def sha256(self, raw_output=False):
             return res.digest()
         return res.hexdigest()
 
-    def locale(self):
-        language_code = self.language_code()
-        return language_code + '_' + self.random_element(
-            Provider.language_locale_codes[language_code],
-        )
-
-    def language_code(self):
-        return self.random_element(Provider.language_locale_codes.keys())
-
     def uuid4(self, cast_to=str):
         """
         Generates a random UUID4 string.


Test patch: