Repository: joke2k/faker
Pull request #1643 (2022-04-28 03:59:46)

Add zh-CN datetime provider
Added datetime provider for zh-CN locale.


Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index f90cdd16..a432bd77 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 13.6.0
+current_version = 13.7.0
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/.github/workflows/gh-release.yml b/.github/workflows/gh-release.yml
index b0e2ca94..45f8242b 100644
--- a/.github/workflows/gh-release.yml
+++ b/.github/workflows/gh-release.yml
@@ -6,8 +6,13 @@ on:
 
 name: Create Release
 
+permissions:
+  contents: read
+
 jobs:
   build:
+    permissions:
+      contents: write  # for actions/create-release to create a release
     name: Create Release
     runs-on: ubuntu-latest
     steps:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5848d54d..ea72ec59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 ## Changelog
 
+### [v13.7.0 - 2022-05-02](https://github.com/joke2k/faker/compare/v13.6.0...v13.7.0)
+
+* Add automotive provider for `fi_FI`,  `et_EE`, `lt_LT` and `vi_VN`. Thanks @zerogonal.
+
 ### [v13.6.0 - 2022-04-27](https://github.com/joke2k/faker/compare/v13.5.0...v13.6.0)
 
 * Add `ar_BH` automotive provider. Thanks @zerogonal.
diff --git a/VERSION b/VERSION
index fc9c00c1..d6ef9982 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-13.6.0
+13.7.0
diff --git a/docs/conf.py b/docs/conf.py
index 4beadaee..8256ae92 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 = "13.6.0"
+version = "13.7.0"
 # The full version, including alpha/beta/rc tags.
-release = "13.6.0"
+release = "13.7.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 3d24ad36..951f285f 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 = "13.6.0"
+VERSION = "13.7.0"
 
 __all__ = ("Factory", "Generator", "Faker")
diff --git a/faker/generator.py b/faker/generator.py
index 613c44dc..8e26c21a 100644
--- a/faker/generator.py
+++ b/faker/generator.py
@@ -3,6 +3,8 @@ import re
 
 from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Optional
 
+from .typing import SeedType
+
 if TYPE_CHECKING:
     from .providers import BaseProvider
 
@@ -65,7 +67,7 @@ class Generator:
     def random(self, value: random_module.Random) -> None:
         self.__random = value
 
-    def seed_instance(self, seed: Optional[Hashable] = None) -> "Generator":
+    def seed_instance(self, seed: Optional[SeedType] = None) -> "Generator":
         """Calls random.seed"""
         if self.__random == random:
             # create per-instance random obj when first time seed_instance() is
@@ -76,7 +78,7 @@ class Generator:
         return self
 
     @classmethod
-    def seed(cls, seed: Optional[Hashable] = None) -> None:
+    def seed(cls, seed: Optional[SeedType] = None) -> None:
         random.seed(seed)
         cls._global_seed = seed
         cls._is_seeded = True
diff --git a/faker/providers/automotive/et_EE/__init__.py b/faker/providers/automotive/et_EE/__init__.py
new file mode 100644
index 00000000..dab55164
--- /dev/null
+++ b/faker/providers/automotive/et_EE/__init__.py
@@ -0,0 +1,12 @@
+from .. import Provider as AutomotiveProvider
+
+
+class Provider(AutomotiveProvider):
+    """Implement automotive provider for ``et_EE`` locale.
+
+    Source:
+
+    - https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Estonia
+    """
+
+    license_formats = ("### ???",)
diff --git a/faker/providers/automotive/fi_FI/__init__.py b/faker/providers/automotive/fi_FI/__init__.py
new file mode 100644
index 00000000..aa9420bf
--- /dev/null
+++ b/faker/providers/automotive/fi_FI/__init__.py
@@ -0,0 +1,12 @@
+from .. import Provider as AutomotiveProvider
+
+
+class Provider(AutomotiveProvider):
+    """Implement automotive provider for ``fi_FI`` locale.
+
+    Source:
+
+    - https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Finland
+    """
+
+    license_formats = ("???-###",)
diff --git a/faker/providers/automotive/lt_LT/__init__.py b/faker/providers/automotive/lt_LT/__init__.py
new file mode 100644
index 00000000..6e10f8d1
--- /dev/null
+++ b/faker/providers/automotive/lt_LT/__init__.py
@@ -0,0 +1,12 @@
+from .. import Provider as AutomotiveProvider
+
+
+class Provider(AutomotiveProvider):
+    """Implement automotive provider for ``lt_LT`` locale.
+
+    Source:
+
+    - https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Lithuania
+    """
+
+    license_formats = ("??? ###",)
diff --git a/faker/providers/automotive/vi_VN/__init__.py b/faker/providers/automotive/vi_VN/__init__.py
new file mode 100644
index 00000000..f877249a
--- /dev/null
+++ b/faker/providers/automotive/vi_VN/__init__.py
@@ -0,0 +1,24 @@
+import re
+
+from .. import Provider as AutomotiveProvider
+
+
+class Provider(AutomotiveProvider):
+    """Implement automotive provider for ``vi_VN`` locale.
+
+    Sources:
+
+    - https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Vietnam
+    """
+
+    license_formats = ("##?-#####",)
+    ascii_uppercase_vietnamese = "ABCDĐEFGHKLMNPSTUVXYZ"
+
+    def license_plate(self) -> str:
+        """Generate a license plate."""
+        temp = re.sub(
+            r"\?",
+            lambda x: self.random_element(self.ascii_uppercase_vietnamese),
+            self.random_element(self.license_formats),
+        )
+        return self.numerify(temp)
diff --git a/faker/providers/date_time/zh_CN/__init__.py b/faker/providers/date_time/zh_CN/__init__.py
new file mode 100644
index 00000000..a69e6d00
--- /dev/null
+++ b/faker/providers/date_time/zh_CN/__init__.py
@@ -0,0 +1,35 @@
+from .. import Provider as DateTimeProvider
+
+
+class Provider(DateTimeProvider):
+    MONTH_NAMES = {
+        "01": "一月",
+        "02": "二月",
+        "03": "三月",
+        "04": "四月",
+        "05": "五月",
+        "06": "六月",
+        "07": "七月",
+        "08": "八月",
+        "09": "九月",
+        "10": "十月",
+        "11": "十一月",
+        "12": "十二月",
+    }
+    DAY_NAMES = {
+        "0": "星期日",
+        "1": "星期一",
+        "2": "星期二",
+        "3": "星期三",
+        "4": "星期四",
+        "5": "星期五",
+        "6": "星期六",
+    }
+
+    def day_of_week(self) -> str:
+        day = self.date("%w")
+        return self.DAY_NAMES[day]
+
+    def month_name(self) -> str:
+        month = self.month()
+        return self.MONTH_NAMES[month]
diff --git a/faker/proxy.py b/faker/proxy.py
index d3ebdf8a..eee266a4 100644
--- a/faker/proxy.py
+++ b/faker/proxy.py
@@ -4,12 +4,13 @@ import re
 
 from collections import OrderedDict
 from random import Random
-from typing import Any, Callable, Dict, Hashable, List, Optional, Pattern, Sequence, Tuple, Union
+from typing import Any, Callable, Dict, List, Optional, Pattern, Sequence, Tuple, Union
 
 from .config import DEFAULT_LOCALE
 from .exceptions import UniquenessException
 from .factory import Factory
 from .generator import Generator, Sentinel, random
+from .typing import SeedType
 from .utils.distribution import choices_distribution
 
 _UNIQUE_ATTEMPTS = 1000
@@ -153,7 +154,7 @@ class Faker:
             return factories[0]
 
         if Generator._global_seed is not Sentinel:
-            random.seed(Generator._global_seed)
+            random.seed(Generator._global_seed)  # type: ignore
         if weights:
             factory = self._select_factory_distribution(factories, weights)
         else:
@@ -200,7 +201,7 @@ class Faker:
         return mapping
 
     @classmethod
-    def seed(cls, seed: Optional[Hashable] = None) -> None:
+    def seed(cls, seed: Optional[SeedType] = None) -> None:
         """
         Hashables the shared `random.Random` object across all factories
 
@@ -208,7 +209,7 @@ class Faker:
         """
         Generator.seed(seed)
 
-    def seed_instance(self, seed: Optional[Hashable] = None) -> None:
+    def seed_instance(self, seed: Optional[SeedType] = None) -> None:
         """
         Creates and seeds a new `random.Random` object for each factory
 
@@ -217,7 +218,7 @@ class Faker:
         for factory in self._factories:
             factory.seed_instance(seed)
 
-    def seed_locale(self, locale: str, seed: Optional[Hashable] = None) -> None:
+    def seed_locale(self, locale: str, seed: Optional[SeedType] = None) -> None:
         """
         Creates and seeds a new `random.Random` object for the factory of the specified locale
 
diff --git a/faker/typing.py b/faker/typing.py
index 0e68df62..75be5a7b 100644
--- a/faker/typing.py
+++ b/faker/typing.py
@@ -9,3 +9,4 @@ except ImportError:
 DateParseType = Union[date, datetime, timedelta, str, int]
 HueType = TypeVar("HueType", str, float, Sequence[int])
 GenderType = TypeVar("GenderType", bound=Literal["M", "F"])
+SeedType = Union[int, float, str, bytes, bytearray, None]


Test patch:
diff --git a/tests/providers/test_automotive.py b/tests/providers/test_automotive.py
index 98a9e006..fc55ba2e 100644
--- a/tests/providers/test_automotive.py
+++ b/tests/providers/test_automotive.py
@@ -20,10 +20,16 @@ class _SimpleAutomotiveTestMixin:
         for _ in range(num_samples):
             license_plate = faker.license_plate()
             match = self.license_plate_pattern.fullmatch(license_plate)
-            assert match
+            assert match is not None
             self.perform_extra_checks(license_plate, match)
 
 
+class TestArBh(_SimpleAutomotiveTestMixin):
+    """Test ar_BH automotive provider methods"""
+
+    license_plate_pattern: Pattern = re.compile(r"\d{6}")
+
+
 class TestAzAz(_SimpleAutomotiveTestMixin):
     """Test az_AZ automotive provider methods"""
 
@@ -282,7 +288,13 @@ class TestNlNl(_SimpleAutomotiveTestMixin):
             assert self.license_plate_motorbike_pattern.match(plate)
 
 
-class TestArBh(_SimpleAutomotiveTestMixin):
-    """Test ar_BH automotive provider methods"""
+class TestViVn(_SimpleAutomotiveTestMixin):
+    """Test vi_VN automotive provider methods"""
 
-    license_plate_pattern: Pattern = re.compile(r"\d{6}")
+    license_plate_pattern: Pattern = re.compile(r"\d{2}[ABCDĐEFGHKLMNPSTUVXYZ]-\d{5}")
+
+
+class TestFiFi(_SimpleAutomotiveTestMixin):
+    """Test fi_FI automotive provider methods"""
+
+    license_plate_pattern: Pattern = re.compile(r"[A-Z]{3}-\d{3}")
diff --git a/tests/providers/test_date_time.py b/tests/providers/test_date_time.py
index fb476b4c..21b6e5e1 100644
--- a/tests/providers/test_date_time.py
+++ b/tests/providers/test_date_time.py
@@ -36,6 +36,7 @@ from faker.providers.date_time.ru_RU import Provider as RuProvider
 from faker.providers.date_time.sk_SK import Provider as SkSkProvider
 from faker.providers.date_time.ta_IN import Provider as TaInProvider
 from faker.providers.date_time.tr_TR import Provider as TrTrProvider
+from faker.providers.date_time.zh_CN import Provider as ZhCnProvider
 
 
 def is64bit():
@@ -1151,3 +1152,17 @@ class TestElGr(unittest.TestCase):
     def test_month(self):
         month = self.fake.month_name()
         assert month in ElGrProvider.MONTH_NAMES.values()
+
+
+class TestZhCn(unittest.TestCase):
+    def setUp(self):
+        self.fake = Faker("zh-CN")
+        Faker.seed(0)
+
+    def test_day(self):
+        day = self.fake.day_of_week()
+        assert day in ZhCnProvider.DAY_NAMES.values()
+
+    def test_month(self):
+        day = self.fake.month_name()
+        assert day in ZhCnProvider.MONTH_NAMES.values()