Repository: joke2k/faker
Pull request #2068 (2024-06-28 19:05:15)

feat: Add Swahili provider for generating Swahili names.


Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 99e8e993..3aa2ab21 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 26.0.0
+current_version = 26.1.0
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8599f1e..13a4d4f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 ## Changelog
 
+### [v26.1.0 - 2024-08-01](https://github.com/joke2k/faker/compare/v26.0.0...v26.1.0)
+
+* Add more entries to `sk_SK` Geo provider. Thanks @george0st.
+
 ### [v26.0.0 - 2024-06-26](https://github.com/joke2k/faker/compare/v25.9.2...v26.0.0)
 
 * Fix upper limit of nb_elements. Thanks @mileswatsonbjss.
diff --git a/VERSION b/VERSION
index 1e212a91..2aaedf99 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-26.0.0
+26.1.0
diff --git a/docs/conf.py b/docs/conf.py
index 9f267530..81d91711 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = f"2014-{datetime.now().year}, Daniele Faraglia"
 # built documents.
 #
 # The short X.Y version.
-version = "26.0.0"
+version = "26.1.0"
 # The full version, including alpha/beta/rc tags.
-release = "26.0.0"
+release = "26.1.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 f238d099..f3ec5c0a 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 = "26.0.0"
+VERSION = "26.1.0"
 
 __all__ = ("Factory", "Generator", "Faker")
diff --git a/faker/providers/color/color.py b/faker/providers/color/color.py
index 366a073a..47b64e0c 100644
--- a/faker/providers/color/color.py
+++ b/faker/providers/color/color.py
@@ -16,12 +16,12 @@ import math
 import random
 import sys
 
-from typing import TYPE_CHECKING, Dict, Hashable, Literal, Optional, Sequence, Tuple
+from typing import TYPE_CHECKING, Dict, Literal, Optional, Sequence, Tuple
 
 if TYPE_CHECKING:
     from ...factory import Generator
 
-from ...typing import HueType
+from ...typing import HueType, SeedType
 
 ColorFormat = Literal["hex", "hsl", "hsv", "rgb"]
 
@@ -136,7 +136,7 @@ class RandomColor:
     :meth:`color() <faker.providers.color.Provider.color>` method.
     """
 
-    def __init__(self, generator: Optional["Generator"] = None, seed: Optional[Hashable] = None) -> None:
+    def __init__(self, generator: Optional["Generator"] = None, seed: Optional[SeedType] = None) -> None:
         self.colormap = COLOR_MAP
 
         # Option to specify a seed was not removed so this class
diff --git a/faker/providers/geo/sk_SK/__init__.py b/faker/providers/geo/sk_SK/__init__.py
index 886f3046..38dc71cd 100644
--- a/faker/providers/geo/sk_SK/__init__.py
+++ b/faker/providers/geo/sk_SK/__init__.py
@@ -77,4 +77,28 @@ class Provider(GeoProvider):
         ("48.44858", "18.91003", "Banská Štiavnica", "SK", "Europe/Banská Štiavnica"),
         ("49.337", "19.556", "Tvrdošín", "SK", "Europe/Tvrdošín"),
         ("48.61428", "20.99957", "Moldava nad Bodvou", "SK", "Europe/Moldava nad Bodvou"),
+        ("48.24371", "18.30846", "Vráble", "SK", "Europe/Vráble"),
+        ("47.85798", "17.76884", "Nagymegyer", "SK", "Europe/Nagymegyer"),
+        ("48.91447", "20.87514", "Krompachy", "SK", "Europe/Krompachy"),
+        ("48.33397", "17.30711", "Modra", "SK", "Europe/Modra"),
+        ("48.57787", "19.52574", "Hriňová", "SK", "Europe/Hriňová"),
+        ("49.03962", "19.72335", "Liptovský Hrádok", "SK", "Europe/Liptovský Hrádok"),
+        ("47.86984", "18.19233", "Stará ďala", "SK", "Europe/Stará ďala"),
+        ("49.4079", "19.48032", "Námestovo", "SK", "Europe/Námestovo"),
+        ("48.27474", "17.03173", "Stampfen", "SK", "Europe/Stampfen"),
+        ("48.07408", "18.94946", "Šahy", "SK", "Europe/Šahy"),
+        ("48.88628", "21.9393", "Chlmec", "SK", "Europe/Chlmec"),
+        ("48.3554", "19.06474", "Krupina", "SK", "Europe/Krupina"),
+        ("49.40429", "18.62258", "Turzovka", "SK", "Europe/Turzovka"),
+        ("48.70074", "21.66104", "Sečovce", "SK", "Europe/Sečovce"),
+        ("48.05075", "18.65421", "Želiezovce", "SK", "Europe/Želiezovce"),
+        ("48.42305", "18.64037", "Nová Baňa", "SK", "Europe/Nová Baňa"),
+        ("49.36101", "19.61249", "Trstená", "SK", "Europe/Trstená"),
+        ("49.06014", "20.20695", "Svit", "SK", "Europe/Svit"),
+        ("49.11328", "18.91714", "Vrútky", "SK", "Europe/Vrútky"),
+        ("49.27195", "21.90073", "Medzilaborce", "SK", "Europe/Medzilaborce"),
+        ("48.48123", "18.71565", "Žarnovica", "SK", "Europe/Žarnovica"),
+        ("48.85584", "20.93713", "Gelnica", "SK", "Europe/Gelnica"),
+        ("49.08899", "18.64007", "Rajec", "SK", "Europe/Rajec"),
+        ("48.74455", "22.18136", "Sobrance", "SK", "Europe/Sobrance"),
     )
diff --git a/faker/providers/person/sw/__init__.py b/faker/providers/person/sw/__init__.py
new file mode 100644
index 00000000..6557496c
--- /dev/null
+++ b/faker/providers/person/sw/__init__.py
@@ -0,0 +1,409 @@
+from .. import Provider as PersonProvider
+
+
+class Provider(PersonProvider):
+    """
+    A Faker provider for generating fake Swahili.
+    """
+
+    formats = (
+        "{{first_name_male}} {{last_name_male}}",
+        "{{first_name_male}} {{last_name_male}}",
+        "{{first_name_male}} {{last_name_male}}",
+        "{{first_name_male}} {{last_name_male}}",
+        "{{first_name_male}} {{last_name_male}} {{last_name_male}}",
+        "{{first_name_female}} {{last_name_female}}",
+        "{{first_name_female}} {{last_name_female}}",
+        "{{first_name_female}} {{last_name_female}}",
+        "{{first_name_female}} {{last_name_female}}",
+        "{{first_name_female}} {{last_name_female}} {{last_name_female}}",
+        "{{prefix_male}} {{first_name_male}} {{last_name_male}}",
+        "{{prefix_female}} {{first_name_female}} {{last_name_female}}",
+        "{{prefix_male}} {{first_name_male}} {{last_name_male}}",
+        "{{prefix_female}} {{first_name_female}} {{last_name_female}}",
+    )
+
+    # first names sourced from:
+    # 1. https://www.behindthename.com/submit/names/gender/masculine/usage/swahili
+    # 2. https://github.com/faker-js/faker/blob/next/src/locales/yo_NG/person/male_first_name.ts
+
+    first_names_male = (
+        "Abdu",
+        "Aijuka",
+        "Amri",
+        "Andwele",
+        "Angalia",
+        "Angavu",
+        "Anoni",
+        "Asani",
+        "Asanti",
+        "Athumani",
+        "Azizi",
+        "Bahari",
+        "Bale",
+        "Balinda",
+        "Beshte",
+        "Bibuwa",
+        "Boma",
+        "Cheusi",
+        "Chuki",
+        "Dai",
+        "Daudi",
+        "Duma",
+        "Dunia",
+        "Ëakumbu",
+        "Ekundu",
+        "Eliakimu",
+        "Enzi",
+        "Evance",
+        "Fahari",
+        "Fanaka",
+        "Faraja",
+        "Hadithi",
+        "Hamis",
+        "Harambee",
+        "Hekima",
+        "Isaya",
+        "Issack",
+        "Ituri",
+        "Jalia",
+        "Jangwa",
+        "Jelani",
+        "Jua",
+        "Jumaane",
+        "Justiniani",
+        "Kaombwe",
+        "Kashangaki",
+        "Kenyangi",
+        "Khamani",
+        "Khamisi",
+        "Kiapo",
+        "Kiburi",
+        "Kijana",
+        "Kijani",
+        "Kimbilio",
+        "Kinubi",
+        "Kipenzi",
+        "Kiume",
+        "Kondo",
+        "Konradi",
+        "Kovu",
+        "Kurunzi",
+        "Kusiima",
+        "Makini",
+        "Makunga",
+        "Makuu",
+        "Matunda",
+        "Mavuno",
+        "Mohamedi",
+        "Mulele",
+        "Mwezi",
+        "Ngamia",
+        "Ngeni",
+        "Ntimi",
+        "Nuhu",
+        "Nuriat",
+        "Nwabudike",
+        "Osogo",
+        "Pambe",
+        "Pelaji",
+        "Popobawa",
+        "Pumbaa",
+        "Rashidi",
+        "Reshoni",
+        "Risasi",
+        "Rua",
+        "Rubani",
+        "Ruhiu",
+        "Rungo",
+        "Sabari",
+        "Sadaka",
+        "Sadiki",
+        "Safari",
+        "Samweli",
+        "Seif",
+        "Shida",
+        "Sifa",
+        "Siku",
+        "Takatifu",
+        "Thabiti",
+        "Tisa",
+        "Tufani",
+        "Tukufu",
+        "Ushindi",
+        "Usiku",
+        "Uzima",
+        "Wamwema",
+        "Yakobo",
+        "Yohana",
+        "Yohane",
+        "Zahur",
+        "Zende",
+        "Zuba",
+        "Zuhri",
+        "Zwatie",
+    )
+    first_names_female = (
+        "Abigaili",
+        "Adhra",
+        "Adia",
+        "Adimu",
+        "Akumu",
+        "Almasi",
+        "Amani",
+        "Amondi",
+        "Anasa",
+        "Angalia",
+        "Arusi",
+        "Asali",
+        "Asanti",
+        "Asatira",
+        "Asmini",
+        "Atiena",
+        "Bahari",
+        "Boma",
+        "Busara",
+        "Chaniya",
+        "Chausiki",
+        "Chipukizi",
+        "Chuki",
+        "Dainess",
+        "Dalili",
+        "Enzi",
+        "Evance",
+        "Fahari",
+        "Faisa",
+        "Fanaka",
+        "Faraja",
+        "Farhiya",
+        "Farijika",
+        "Gethera",
+        "Goma",
+        "Haiba",
+        "Halisi",
+        "Hanja",
+        "Hashiki",
+        "Hatima",
+        "Hawehindi",
+        "Hekima",
+        "Hidaya",
+        "Hodari",
+        "Humaiya",
+        "Imany",
+        "Imara",
+        "Itanya",
+        "Jahi",
+        "Jana",
+        "Jasiri",
+        "Jina",
+        "Jua",
+        "Kaluwa",
+        "Kaombwe",
+        "Karama",
+        "Kaskazi",
+        "Kiah",
+        "Kibafupia",
+        "Kibibi",
+        "Kiburi",
+        "Kijana",
+        "Kimya",
+        "Kinaya",
+        "Kiojah",
+        "Kipenzi",
+        "Kipepeo",
+        "Kisima",
+        "Kiwara",
+        "Kuchanua",
+        "Kweli",
+        "Lailati",
+        "Laini",
+        "Madaha",
+        "Madini",
+        "Madoa",
+        "Mahali",
+        "Maisha",
+        "Majani",
+        "Makini",
+        "Maliza",
+        "Marini",
+        "Marjani",
+        "Matunda",
+        "Maua",
+        "Misuli",
+        "Mkarkara",
+        "Mrihani",
+        "Muhima",
+        "Musila",
+        "Mwamini",
+        "Mwasaa",
+        "Najuma",
+        "Naki",
+        "Nashipie",
+        "Nasra",
+        "Nathari",
+        "Nayfa",
+        "Nelah",
+        "Niara",
+        "Nigesa",
+        "Njozi",
+        "Nula",
+        "Nyasi",
+        "Nyoka",
+        "Nyoni",
+        "Nyota",
+        "Nyuki",
+        "Opwonya",
+        "Panya",
+        "Paskalia",
+        "Reshoni",
+        "Rua",
+        "Sabari",
+        "Sadao",
+        "Safari",
+        "Safiri",
+        "Sarabi",
+        "Sarafina",
+        "Sauti",
+        "Serafina",
+        "Shani",
+        "Shawana",
+        "Shida",
+        "Sifa",
+        "Siku",
+        "Skolastika",
+        "Sungara",
+        "Swala",
+        "Tambika",
+        "Tamu",
+        "Ta-tanisha",
+        "Tisa",
+        "Tuere",
+        "Tufani",
+        "Udeera",
+        "Ujamaa",
+        "Umande",
+        "Umoja",
+        "Uzima",
+        "Waceera",
+        "Wamwema",
+        "Waridi",
+        "Waseme",
+        "Yasinta",
+        "Zahnya",
+        "Zaituni",
+        "Zumaridi",
+        "Zuwena",
+    )
+
+    first_names = first_names_male + first_names_female
+
+    # last names sourced from :
+    # 1.https://www.familyeducation.com/baby-names/surname/origin/kenyan
+    last_names_male = (
+        "Abwao",
+        "Adamu",
+        "Baharia",
+        "Dhadho",
+        "Fuli",
+        "Hassani",
+        "Juma",
+        "Kahinu",
+        "Kimachu",
+        "Kitumaini",
+        "Madhubuti",
+        "Magombo",
+        "Mathenge",
+        "Msuya",
+        "Naomi",
+        "Nazari",
+        "Rikke",
+        "Sayyid",
+        "Simba",
+        "Sinema",
+        "Wario",
+        "Yudas",
+        "Abdi",
+        "Ali",
+        "Akinyi",
+        "Anyango",
+        "Juma",
+        "Kamau",
+        "Kibet",
+        "Kimani",
+        "Maina",
+        "Mwangi",
+        "Obama",
+        "Ochieng",
+        "Onyango",
+        "Otieno",
+        "Mohamed",
+        "Hassan",
+        "Wafula",
+        "Wanjala",
+        "Atieno",
+        "Kariuki",
+        "Kimutai",
+        "Kipkorir",
+        "Kipkirui",
+        "Kipkemei",
+        "Kiplagat",
+        "Kiprono",
+        "Kipsang",
+        "Kiptoo",
+        "Kipruto",
+        "Mumbi",
+        "Muthoni",
+        "Njeri",
+        "Njoroge",
+        "Odhiambo",
+        "Omondi",
+        "Owuor",
+        "Wanijiku",
+        "Wambui",
+        "Abdullahi",
+        "Adan",
+        "Ahmed",
+        "Auma",
+        "Barasa",
+        "Hussein",
+        "Ibrahim",
+        "John",
+        "Mutai",
+        "Omar",
+        "Ouma",
+        "Waweru",
+    )
+
+    # last names are not sex dependant
+    last_names_female = last_names_male
+    last_names = last_names_male + last_names_female
+
+    prefixes_female = (
+        "Mrs.",
+        "Ms.",
+        "Dr.",
+        "Bi.",
+        "Mama",
+        "Bibi",
+        "Madam",
+        "Chief",
+        "Dkt.",
+        "Mheshimiwa",
+        "Mwalimu",
+        "Mtukufu",
+        "Malkia",
+        "Mwanamke",
+    )
+
+    prefixes_male = (
+        "Mr.",
+        "Dr.",
+        "Bwana",
+        "Mzee",
+        "Bw.",
+        "Dkt.",
+        "Mheshimiwa",
+        "Mwalimu",
+        "Mtukufu",
+        "Mfalme",
+    )


Test patch:
diff --git a/tests/providers/test_person.py b/tests/providers/test_person.py
index ee5ac157..5cece79e 100644
--- a/tests/providers/test_person.py
+++ b/tests/providers/test_person.py
@@ -32,6 +32,7 @@ from faker.providers.person.ru_RU import Provider as RuProvider
 from faker.providers.person.ru_RU import translit
 from faker.providers.person.sk_SK import Provider as SkSKProvider
 from faker.providers.person.sv_SE import Provider as SvSEProvider
+from faker.providers.person.sw import Provider as SwProvider
 from faker.providers.person.ta_IN import Provider as TaINProvider
 from faker.providers.person.th_TH import Provider as ThThProvider
 from faker.providers.person.uk_UA import Provider as UkUAProvider
@@ -1477,6 +1478,85 @@ class TestYoNG(unittest.TestCase):
             raise AssertionError("Invalid number of name parts. Expected 2 or 3.")
 
 
+class TestSw(unittest.TestCase):
+    def setUp(self):
+        self.fake = Faker("sw")
+        Faker.seed(0)
+
+    def test_last_name(self):
+        """
+        Test the generation of Swahili last names.
+        """
+        # There's no gender-specific last name in Swahili.
+        self.assertTrue(hasattr(SwProvider, "last_names_male"))
+        self.assertTrue(hasattr(SwProvider, "last_names_female"))
+
+        # All last names apply to all genders.
+        self.assertTrue(hasattr(SwProvider, "last_names"))
+
+        # General last name.
+        name = self.fake.last_name()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, SwProvider.last_names)
+
+        # Females last name.
+        name = self.fake.last_name_female()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, SwProvider.last_names)
+
+        # Male last name.
+        name = self.fake.last_name_male()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, SwProvider.last_names)
+
+    def test_first_name(self):
+        """
+        Test the generation of Swahili first names.
+        """
+        # General first name.
+        name = self.fake.first_name()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, SwProvider.first_names)
+
+        # Female first name.
+        name = self.fake.first_name_female()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, SwProvider.first_names)
+        self.assertIn(name, SwProvider.first_names_female)
+
+        # Male first name.
+        name = self.fake.first_name_male()
+        self.assertIsInstance(name, str)
+        self.assertIn(name, SwProvider.first_names)
+        self.assertIn(name, SwProvider.first_names_male)
+
+    def test_full_name(self):
+        """
+        Test the generation of full Swahili names.
+        """
+        # Full name.
+        name = self.fake.name()
+        self.assertIsInstance(name, str)
+
+        full_name_parts = name.split()
+
+        if len(full_name_parts) == 2:
+            first_name = full_name_parts[0]
+            last_name = full_name_parts[1]
+            self.assertIn(first_name, SwProvider.first_names)
+            self.assertIn(last_name, SwProvider.last_names)
+        elif len(full_name_parts) == 3:
+            prefix = full_name_parts[0]
+            first_name = full_name_parts[1]
+            last_name = full_name_parts[2]
+
+            self.assertIn(prefix, SwProvider.prefixes_female + SwProvider.prefixes_male)
+            self.assertIn(first_name, SwProvider.first_names)
+            self.assertIn(last_name, SwProvider.last_names)
+        else:
+            raise AssertionError("Invalid number of name parts. Expected 2 or 3.")
+
+
 class TestZuZa(unittest.TestCase):
     def setUp(self):
         self.fake = Faker("zu_ZA")