Repository: joke2k/faker

Issue:
Default to Safe Email Addresses
By default `faker.email()` will generate email addresses that are potentially valid, especially considering `@gmail.com`, `@yahoo.com` and other free providers are included in the data. 

This can be problematic considering there are a few potential outcomes that can come of this that can affect the developers using `faker` or the people who have unwittingly had their emails generated as part of a test suite.

While there is the argument that developers should be configuring mailers in test environments to not send outbound, using the in-built `ascii_safe_email()` or provide the optional `domain` argument, I also think that `faker` would benefit from having the `email()` provider use the safe domains by default and require developers to enable unsafe email address generation.

### Expected behavior

Here was something I very quickly put together, to demonstrate what I'd like to see happen, with how it could be used by a developer:

```python
@lowercase
def email(self, unsafe=False, domain=None):
    if domain:
      email = f'{self.user_name()}@{domain}'
    if unsafe:
      pattern = self.random_element(self.email_formats)
      email = "".join(self.generator.parse(pattern).split(" "))
    else:
      domain = self.random_element(self.safe_domain_name)
      email = f'{self.user_name()}@{domain}'
    return email
```

Which when used by the developer would look something like:
```
fake = Faker()

# safe by default
print(fake.email())

# enable unsafe mode
print(fake.email(unsafe=True))

# use a domain
print(fake.email(domain="mycooldomain.com"))
```

```
>>> justinramos@domain.com
>>> justinramos@jackson-cuevas.net
>>> justinramos@mycooldomain.com
```

### Actual behavior

Currently, the default behaviour is for `faker` to generate email addresses that may be valid. 

I'd be more than happy to open a PR to make these changes if you'd like to pursue this approach!

Thanks!

Discussion:

Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index f6df28d0..81209b95 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 8.14.1
+current_version = 8.16.0
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0806599b..e872b8a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
 ## Changelog
 
+### [v8.16.0 - 2021-10-04](https://github.com/joke2k/faker/compare/v8.15.0...v8.16.0)
+
+* Add ``date_time`` provider for ``nl_NL``. Thanks @MarcelRobeer.
+
+### [v8.15.0 - 2021-10-04](https://github.com/joke2k/faker/compare/v8.14.2...v8.15.0)
+
+* Added ``bg_BG`` color provider. Thanks @iradonov.
+
+### [v8.14.2 - 2021-10-04](https://github.com/joke2k/faker/compare/v8.14.1...v8.14.2)
+
+* Fix bug in ``pydecimal`` when returning max/min. Thanks @wilbertom.
+
 ### [v8.14.1 - 2021-10-01](https://github.com/joke2k/faker/compare/v8.14.0...v8.14.1)
 
 * Fix `pydecimal` precision. Thanks @joidegn.
diff --git a/VERSION b/VERSION
index 6092827e..894aa0bc 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-8.14.1
+8.16.0
diff --git a/docs/conf.py b/docs/conf.py
index dfec42af..51937f3c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = '2014, Daniele Faraglia'
 # built documents.
 #
 # The short X.Y version.
-version = '8.14.1'
+version = '8.16.0'
 # The full version, including alpha/beta/rc tags.
-release = '8.14.1'
+release = '8.16.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/faker/__init__.py b/faker/__init__.py
index a122980a..e81e59ae 100644
--- a/faker/__init__.py
+++ b/faker/__init__.py
@@ -2,6 +2,6 @@ from faker.factory import Factory
 from faker.generator import Generator
 from faker.proxy import Faker
 
-VERSION = '8.14.1'
+VERSION = '8.16.0'
 
 __all__ = ('Factory', 'Generator', 'Faker')
diff --git a/faker/providers/color/bg_BG/__init__.py b/faker/providers/color/bg_BG/__init__.py
new file mode 100644
index 00000000..325dbce0
--- /dev/null
+++ b/faker/providers/color/bg_BG/__init__.py
@@ -0,0 +1,74 @@
+from collections import OrderedDict
+
+from .. import Provider as ColorProvider
+
+localized = True
+
+
+class Provider(ColorProvider):
+    """Implement color provider for ``bg_BG`` locale."""
+
+    all_colors = OrderedDict((
+        ("Антично-бяло", "#FAEBD7"),
+        ("Аквамарин", "#7FFFD4"),
+        ("Лазурно", "#F0FFFF"),
+        ("Бежово", "#F5F5DC"),
+        ("Черно", "#000000"),
+        ("Синьо", "#0000FF"),
+        ("Синьо-виолетовo", "#8A2BE2"),
+        ("Кафявo", "#A52A2A"),
+        ("Шоколадовo", "#D2691E"),
+        ("Корал", "#FF7F50"),
+        ("Метличино синьо", "#6495ED"),
+        ("Малиново", "#DC143C"),
+        ("Тъмносиньо", "#00008B"),
+        ("Тъмноциан", "#008B8B"),
+        ("Тъмносуво", "#A9A9A9"),
+        ("Тъмнозелено", "#006400"),
+        ("Тъмно маслинено зелено", "#BDB76B"),
+        ("Тъмнооранжево", "#FF8C00"),
+        ("Тъмночервено", "#8B0000"),
+        ("Тъмнотюркоазено", "#00CED1"),
+        ("Тъмновиолетово", "#9400D3"),
+        ("Тъмнорозово", "#FF1493"),
+        ("Бледо сиво", "#696969"),
+        ("Фуксия", "#FF00FF"),
+        ("Златно", "#FFD700"),
+        ("Сребърно", "#808080"),
+        ("Зелено", "#008000"),
+        ("Жълто-зелено", "#ADFF2F"),
+        ("Ярко-розово", "#FF69B4"),
+        ("Индиго", "#4B0082"),
+        ("Слонова кост", "#FFFFF0"),
+        ("Маслинено зелено", "#F0E68C"),
+        ("Розовато-лавандулово", "#FFF0F5"),
+        ("Светлосиньо", "#ADD8E6"),
+        ("Светло-небесносино", "#E0FFFF"),
+        ("Светлосиво", "#D3D3D3"),
+        ("Светлозелено", "#90EE90"),
+        ("Светлорозово", "#FFB6C1"),
+        ("Небесносиньо", "#87CEFA"),
+        ("Светложълто", "#FFFFE0"),
+        ("Кестеняво", "#800000"),
+        ("Оранжево", "#FFA500"),
+        ("Оранжево-червено", "#FF4500"),
+        ("Бледозелено", "#98FB98"),
+        ("Бледотюркоазно", "#AFEEEE"),
+        ("Розово", "#FFC0CB"),
+        ("Сливово розово", "#DDA0DD"),
+        ("Пурпурно", "#800080"),
+        ("Червено", "#FF0000"),
+        ("Циан", "#2E8B57"),
+        ("Сребърно", "#C0C0C0"),
+        ("Тюркоазено", "#40E0D0"),
+        ("Виолетово", "#EE82EE"),
+        ("Бяло", "#FFFFFF"),
+        ("Жълто", "#FFFF00"),
+        ("Жълто-зелено", "#9ACD32"),
+    ))
+
+    safe_colors = (
+        'черно', 'бордо', 'зелено', 'маслинено зелено',
+        'пурпурно', 'циан', 'лайм', 'синьо', 'сребърно',
+        'сиво', 'жълто', 'фуксия', 'бяло',
+    )
diff --git a/faker/providers/date_time/nl_NL/__init__.py b/faker/providers/date_time/nl_NL/__init__.py
new file mode 100644
index 00000000..e7cc5854
--- /dev/null
+++ b/faker/providers/date_time/nl_NL/__init__.py
@@ -0,0 +1,37 @@
+from .. import Provider as DateTimeProvider
+
+
+class Provider(DateTimeProvider):
+
+    DAY_NAMES = {
+        "0": "zondag",
+        "1": "maandag",
+        "2": "dinsdag",
+        "3": "woensdag",
+        "4": "donderdag",
+        "5": "vrijdag",
+        "6": "zaterdag",
+    }
+
+    MONTH_NAMES = {
+        "01": "januari",
+        "02": "februari",
+        "03": "maart",
+        "04": "april",
+        "05": "mei",
+        "06": "juni",
+        "07": "juli",
+        "08": "augustus",
+        "09": "september",
+        "10": "oktober",
+        "11": "november",
+        "12": "december",
+    }
+
+    def day_of_week(self):
+        day = self.date("%w")
+        return self.DAY_NAMES[day]
+
+    def month_name(self):
+        month = self.month()
+        return self.MONTH_NAMES[month]
diff --git a/faker/providers/internet/__init__.py b/faker/providers/internet/__init__.py
index f5ebc38b..e1b4f921 100644
--- a/faker/providers/internet/__init__.py
+++ b/faker/providers/internet/__init__.py
@@ -119,9 +119,11 @@ class Provider(BaseProvider):
         return string
 
     @lowercase
-    def email(self, domain=None):
+    def email(self, safe=True, domain=None):
         if domain:
             email = f'{self.user_name()}@{domain}'
+        elif safe:
+            email = f'{self.user_name()}@{self.safe_domain_name()}'
         else:
             pattern = self.random_element(self.email_formats)
             email = "".join(self.generator.parse(pattern).split(" "))
diff --git a/faker/providers/python/__init__.py b/faker/providers/python/__init__.py
index 595e69fb..b056d186 100644
--- a/faker/providers/python/__init__.py
+++ b/faker/providers/python/__init__.py
@@ -198,9 +198,9 @@ class Provider(BaseProvider):
         # Because the random result might have the same number of decimals as max_value the random number
         # might be above max_value or below min_value
         if max_value is not None and result > max_value:
-            result = max_value
+            result = Decimal(max_value)
         if min_value is not None and result < min_value:
-            result = min_value
+            result = Decimal(min_value)
 
         return result
 


Test patch:
diff --git a/tests/providers/test_color.py b/tests/providers/test_color.py
index 328e7217..52d15336 100644
--- a/tests/providers/test_color.py
+++ b/tests/providers/test_color.py
@@ -5,6 +5,7 @@ import re
 import pytest
 
 from faker.providers.color import RandomColor
+from faker.providers.color.bg_BG import Provider as BgBgColorProvider
 from faker.providers.color.es_ES import Provider as EsEsColorProvider
 from faker.providers.color.fa_IR import Provider as FaIrColorProvider
 from faker.providers.color.he_IL import Provider as HeILColorProvider
@@ -278,6 +279,22 @@ class TestFaIr:
             assert safe_color_name in FaIrColorProvider.safe_colors
 
 
+class TestBgBg:
+    """Test bg_BG color provider methods"""
+
+    def test_color_name(self, faker, num_samples):
+        for _ in range(num_samples):
+            color_name = faker.color_name()
+            assert isinstance(color_name, str)
+            assert color_name in BgBgColorProvider.all_colors.keys()
+
+    def test_safe_color_name(self, faker, num_samples):
+        for _ in range(num_samples):
+            safe_color_name = faker.safe_color_name()
+            assert isinstance(safe_color_name, str)
+            assert safe_color_name in BgBgColorProvider.safe_colors
+
+
 class TestEsEs:
     """Test es_ES color provider methods"""
 
diff --git a/tests/providers/test_date_time.py b/tests/providers/test_date_time.py
index 62ee396c..608f05fe 100644
--- a/tests/providers/test_date_time.py
+++ b/tests/providers/test_date_time.py
@@ -25,6 +25,7 @@ from faker.providers.date_time.de_DE import Provider as DeDeProvider
 from faker.providers.date_time.es_ES import Provider as EsEsProvider
 from faker.providers.date_time.hy_AM import Provider as HyAmProvider
 from faker.providers.date_time.it_IT import Provider as ItItProvider
+from faker.providers.date_time.nl_NL import Provider as NlProvider
 from faker.providers.date_time.pl_PL import Provider as PlProvider
 from faker.providers.date_time.pt_BR import Provider as PtBrProvider
 from faker.providers.date_time.pt_PT import Provider as PtPtProvider
@@ -1163,3 +1164,18 @@ class TestBnBd(unittest.TestCase):
     def test_month(self):
         month = self.fake.month_name()
         assert month in BnBdProvider.MONTH_NAMES.values()
+
+
+class TestNlNl(unittest.TestCase):
+
+    def setUp(self):
+        self.fake = Faker('nl_NL')
+        Faker.seed(0)
+
+    def test_day(self):
+        day = self.fake.day_of_week()
+        assert day in NlProvider.DAY_NAMES.values()
+
+    def test_month(self):
+        month = self.fake.month_name()
+        assert month in NlProvider.MONTH_NAMES.values()
diff --git a/tests/providers/test_internet.py b/tests/providers/test_internet.py
index 2f9c37d9..28d5b1b3 100644
--- a/tests/providers/test_internet.py
+++ b/tests/providers/test_internet.py
@@ -39,6 +39,18 @@ class TestInternetProvider:
             email = faker.email()
             assert '@' in email
 
+    def test_safe_default_email(self, faker, num_samples):
+        expected_domains = ['example.com', 'example.org', 'example.net']
+        for _ in range(num_samples):
+            email = faker.email()
+            assert email.split('@')[1] in expected_domains
+
+    def test_unsafe_email(self, faker, num_samples):
+        not_expected_domains = ['example.com', 'example.org', 'example.net']
+        for _ in range(num_samples):
+            email = faker.email(safe=False)
+            assert email.split('@')[1] not in not_expected_domains
+
     def test_email_with_domain(self, faker):
         domain = 'example.com'
         email = faker.email(domain=domain)
diff --git a/tests/providers/test_python.py b/tests/providers/test_python.py
index 6da60260..9742c9a0 100644
--- a/tests/providers/test_python.py
+++ b/tests/providers/test_python.py
@@ -256,6 +256,13 @@ class TestPydecimal(unittest.TestCase):
             result = self.fake.pydecimal(min_value=min_value)
             self.assertGreaterEqual(result, min_value)
 
+    def test_min_value_always_returns_a_decimal(self):
+        min_values = (0, 10, -1000, 1000, 999999)
+
+        for min_value in min_values:
+            result = self.fake.pydecimal(min_value=min_value)
+            self.assertIsInstance(result, decimal.Decimal)
+
     def test_min_value_and_left_digits(self):
         """
         Combining the min_value and left_digits keyword arguments produces
@@ -273,6 +280,13 @@ class TestPydecimal(unittest.TestCase):
             result = self.fake.pydecimal(max_value=max_value)
             self.assertLessEqual(result, max_value)
 
+    def test_max_value_always_returns_a_decimal(self):
+        max_values = (0, 10, -1000, 1000, 999999)
+
+        for max_value in max_values:
+            result = self.fake.pydecimal(max_value=max_value)
+            self.assertIsInstance(result, decimal.Decimal)
+
     def test_max_value_zero_and_left_digits(self):
         """
         Combining the max_value and left_digits keyword arguments produces