Repository: joke2k/faker
Pull request #2182 (2025-03-07 09:46:35)

Add ng_NG currency provider
### What does this change
Added Nigerian currency provider


### Checklist

- [x] I have read the documentation about [CONTRIBUTING](https://github.com/joke2k/faker/blob/master/CONTRIBUTING.rst)
- [x] I have read the documentation about [Coding style](https://github.com/joke2k/faker/blob/master/docs/coding_style.rst)
- [x] I have run `make lint`



Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 0a4a0760..fb181dbd 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 36.2.2
+current_version = 37.0.2
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3a60bb9..3dbe873d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
 ## Changelog
 
+### [v37.0.2 - 2025-03-19](https://github.com/joke2k/faker/compare/v37.0.1...v37.0.2)
+
+* Fix type annotiation
+
+### [v37.0.1 - 2025-03-18](https://github.com/joke2k/faker/compare/v37.0.0...v37.0.1)
+
+* Fix last names from `en_PK` provider. Thanks @bradenkwebb.
+* fix Belgium IBAN incorrect checksum. Thanks @xeirzo.
+
+### [v37.0.0 - 2025-03-07](https://github.com/joke2k/faker/compare/v36.2.3...v37.0.0)
+
+* Fix: `es_ES` `doi()` to use standard DOI format. Thanks @jasur-py.
+
+### [v36.2.3 - 2025-03-07](https://github.com/joke2k/faker/compare/v36.2.2...v36.2.3)
+
+* Fix typing for hash methods. Thanks @lindycoder.
+
 ### [v36.2.2 - 2025-03-05](https://github.com/joke2k/faker/compare/v36.2.1...v36.2.2)
 
 * Fix male prefix in `gu_IN`. Thanks @Hitesh1122.
diff --git a/VERSION b/VERSION
index 69cc2e64..38f15178 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-36.2.2
+37.0.2
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 60d7d456..7b52ab39 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -10,5 +10,6 @@ mypy>=1.15.0
 packaging>=24.2
 pytest>=6.0.1
 setuptools>=75.8.2
+tox>=4.24.1
 twine>=6.1.0
 wheel>=0.45.1
diff --git a/docs/conf.py b/docs/conf.py
index 35c91f36..d708c00c 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 = "36.2.2"
+version = "37.0.2"
 # The full version, including alpha/beta/rc tags.
-release = "36.2.2"
+release = "37.0.2"
 
 # 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 18a90855..8d9a852d 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 = "36.2.2"
+VERSION = "37.0.2"
 
 __all__ = ("Factory", "Generator", "Faker")
diff --git a/faker/cli.py b/faker/cli.py
index 05db32dd..52badf17 100644
--- a/faker/cli.py
+++ b/faker/cli.py
@@ -197,7 +197,7 @@ examples:
             choices=AVAILABLE_LOCALES,
             default=default_locale,
             metavar="LOCALE",
-            help="specify the language for a localized " "provider (e.g. de_DE)",
+            help="specify the language for a localized provider (e.g. de_DE)",
         )
         parser.add_argument(
             "-r",
@@ -210,7 +210,7 @@ examples:
             "-s",
             "--sep",
             default="\n",
-            help="use the specified separator after each " "output",
+            help="use the specified separator after each output",
         )
 
         parser.add_argument(
@@ -237,7 +237,7 @@ examples:
             "fake",
             action="store",
             nargs="?",
-            help="name of the fake to generate output for " "(e.g. profile)",
+            help="name of the fake to generate output for (e.g. profile)",
         )
 
         parser.add_argument(
diff --git a/faker/providers/bank/nl_BE/__init__.py b/faker/providers/bank/nl_BE/__init__.py
index 54334866..2d586478 100644
--- a/faker/providers/bank/nl_BE/__init__.py
+++ b/faker/providers/bank/nl_BE/__init__.py
@@ -85,12 +85,12 @@ class Provider(BankProvider):
 
     def _calculate_mod97(self, account_number: str) -> str:
         """Calculate the mod 97 check digits for a given account number."""
-        check_digits = 97 - (int(account_number) % 97)
-        return str(check_digits).zfill(2)
+        remainder = int(account_number) % 97
+        return str(remainder).zfill(2) if remainder != 0 else "97"
 
     def _calculate_iban_check_digits(self, bban: str) -> str:
         """Calculate the IBAN check digits using mod 97 algorithm."""
-        raw_iban = f"{self.country_code}00{bban}"
+        raw_iban = f"{bban}{self.country_code}00"
         numeric_iban = "".join(str(ord(char) - 55) if char.isalpha() else char for char in raw_iban)
         check_digits = 98 - (int(numeric_iban) % 97)
         return str(check_digits).zfill(2)
diff --git a/faker/providers/credit_card/__init__.py b/faker/providers/credit_card/__init__.py
index da820289..38e923d7 100644
--- a/faker/providers/credit_card/__init__.py
+++ b/faker/providers/credit_card/__init__.py
@@ -143,7 +143,7 @@ class Provider(BaseProvider):
         """Generate a set of credit card details."""
         card = self._credit_card_type(card_type)
 
-        tpl = "{provider}\n" "{owner}\n" "{number} {expire_date}\n" "{security}: {security_nb}\n"
+        tpl = "{provider}\n{owner}\n{number} {expire_date}\n{security}: {security_nb}\n"
 
         tpl = tpl.format(
             provider=card.name,
diff --git a/faker/providers/credit_card/ru_RU/__init__.py b/faker/providers/credit_card/ru_RU/__init__.py
index e68b151d..1447dc1f 100644
--- a/faker/providers/credit_card/ru_RU/__init__.py
+++ b/faker/providers/credit_card/ru_RU/__init__.py
@@ -91,7 +91,7 @@ class Provider(CreditCardProvider):
         """Generate a set of credit card details."""
         card = self._credit_card_type(card_type)
 
-        tpl = "{provider}\n" "{owner}\n" "{number} {expire_date}\n" "{security}: {security_nb}\n" "{issuer}"
+        tpl = "{provider}\n{owner}\n{number} {expire_date}\n{security}: {security_nb}\n{issuer}"
 
         tpl = tpl.format(
             provider=card.name,
diff --git a/faker/providers/credit_card/uk_UA/__init__.py b/faker/providers/credit_card/uk_UA/__init__.py
index 1607d27f..99bc9ebf 100644
--- a/faker/providers/credit_card/uk_UA/__init__.py
+++ b/faker/providers/credit_card/uk_UA/__init__.py
@@ -35,7 +35,7 @@ class Provider(CreditCardProvider):
         :sample: card_type="mastercard"
         """
         card = self._credit_card_type(card_type)
-        tpl = "{provider}\n" "{owner}\n" "{number} {expire_date}\n" "{security}: {security_nb}\n" "{issuer}"
+        tpl = "{provider}\n{owner}\n{number} {expire_date}\n{security}: {security_nb}\n{issuer}"
         tpl = tpl.format(
             provider=card.name,
             owner=translit(
diff --git a/faker/providers/currency/ng_NG/__init__.py b/faker/providers/currency/ng_NG/__init__.py
new file mode 100644
index 00000000..38e839b6
--- /dev/null
+++ b/faker/providers/currency/ng_NG/__init__.py
@@ -0,0 +1,8 @@
+from .. import Provider as CurrencyProvider
+
+
+class Provider(CurrencyProvider):
+    price_formats = ["#.##", "%#.##", "%##.##", "%,###.##"]
+
+    def pricetag(self) -> str:
+        return "\N{NAIRA SIGN}" + "\N{NO-BREAK SPACE}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/doi/__init__.py b/faker/providers/doi/__init__.py
new file mode 100644
index 00000000..2ab98ce2
--- /dev/null
+++ b/faker/providers/doi/__init__.py
@@ -0,0 +1,22 @@
+from faker.providers import BaseProvider
+
+
+class Provider(BaseProvider):
+    """
+    Provider for Digital Object Identifier (DOI)
+    Source of info: https://en.wikipedia.org/wiki/Digital_object_identifier (English)
+    """
+
+    def doi(self) -> str:
+        """
+        Generate a valid Digital Object Identifier (DOI).
+        Format: 10.{4-9 digits}/{alphanumeric string}
+        Eg: 10.1000/xyz123
+
+        :sample:
+        """
+        prefix = "10"
+        registrant = str(self.generator.random.randint(1000, 99999999))
+        suffix = self.generator.bothify("?#?#?##").lower()
+
+        return f"{prefix}.{registrant}/{suffix}"
diff --git a/faker/providers/misc/__init__.py b/faker/providers/misc/__init__.py
index 70f20ec6..dca3363e 100644
--- a/faker/providers/misc/__init__.py
+++ b/faker/providers/misc/__init__.py
@@ -60,6 +60,9 @@ class Provider(BaseProvider):
         # Generator is unseeded anyway, just use urandom
         return os.urandom(length)
 
+    @overload
+    def md5(self) -> str: ...
+
     @overload
     def md5(self, raw_output: Literal[True]) -> bytes: ...
 
@@ -80,6 +83,9 @@ class Provider(BaseProvider):
             return res.digest()
         return res.hexdigest()
 
+    @overload
+    def sha1(self) -> str: ...
+
     @overload
     def sha1(self, raw_output: Literal[True]) -> bytes: ...
 
@@ -100,6 +106,9 @@ class Provider(BaseProvider):
             return res.digest()
         return res.hexdigest()
 
+    @overload
+    def sha256(self) -> str: ...
+
     @overload
     def sha256(self, raw_output: Literal[True]) -> bytes: ...
 
diff --git a/faker/providers/person/en_PK/__init__.py b/faker/providers/person/en_PK/__init__.py
index 38ee0a08..1b3511a3 100644
--- a/faker/providers/person/en_PK/__init__.py
+++ b/faker/providers/person/en_PK/__init__.py
@@ -115,7 +115,6 @@ class Provider(PersonProvider):
         "Zewad",
         "Zimran",
         "Zuwayhir",
-        "Name",
         "Adil",
         "Aaaqil",
         "Aaban",
@@ -155,7 +154,6 @@ class Provider(PersonProvider):
         "Aayan",
         "Aazim",
         "Abaan",
-        "Name",
         "Baahir",
         "Baaizeed",
         "Baaqee",
@@ -195,7 +193,6 @@ class Provider(PersonProvider):
         "Bazam",
         "Bilaal",
         "Bilal",
-        "Name",
         "Dawud",
         "Daamin",
         "Daanish",
@@ -210,7 +207,6 @@ class Provider(PersonProvider):
         "Damurah",
         "Daniel",
         "Danish",
-        "Name",
         "Eesaa",
         "Ehan",
         "Ehsaas",
@@ -220,7 +216,6 @@ class Provider(PersonProvider):
         "El-Amin",
         "Emran",
         "Eshan",
-        "Name",
         "Ghaalib",
         "Ghaazi",
         "Ghaffaar",
@@ -254,7 +249,6 @@ class Provider(PersonProvider):
         "Gulfam",
         "Gulshan",
         "Gulzar",
-        "Name",
         "Izaaz",
         "Ibaad",
         "Ibn",
@@ -282,7 +276,6 @@ class Provider(PersonProvider):
         "Ikrimah",
         "Ikrimah",
         "Ilan",
-        "Name",
         "Jafar",
         "Jaabir",
         "Jaabir",
@@ -311,7 +304,6 @@ class Provider(PersonProvider):
         "Jamal",
         "Jameel",
         "Jameel",
-        "Name",
         "Kaamil",
         "Kaamil",
         "Kaamil",
@@ -347,7 +339,6 @@ class Provider(PersonProvider):
         "Kazim",
         "Keyaan",
         "Khaalid",
-        "Name",
         "Laeeq",
         "Labeeb",
         "Labeeb",
@@ -375,7 +366,6 @@ class Provider(PersonProvider):
         "Lutf",
         "Lutfi",
         "Lutfi",
-        "Name",
         "Maawiya",
         "Mad",
         "Mamun",
@@ -415,7 +405,6 @@ class Provider(PersonProvider):
         "Majdy",
         "Majeed",
         "Makeen",
-        "Name",
         "Nail",
         "Naail",
         "Naadir",
@@ -455,7 +444,6 @@ class Provider(PersonProvider):
         "Najeeb",
         "Najeeb",
         "Najeeb",
-        "Name",
         "Obaid",
         "Omair",
         "Omar",
@@ -465,11 +453,9 @@ class Provider(PersonProvider):
         "Osama",
         "Ossama",
         "Owais",
-        "Name",
         "Parsa",
         "Parvez",
         "Pervaiz",
-        "Name",
         "Qaadir",
         "Qaadir",
         "Qaasim",
@@ -507,7 +493,6 @@ class Provider(PersonProvider):
         "Qutaybah",
         "Qutb",
         "Qutub",
-        "Name",
         "Raed",
         "Raid",
         "Raaghib",
@@ -545,7 +530,6 @@ class Provider(PersonProvider):
         "Rahat",
         "Raheel",
         "Raheem",
-        "Name",
         "Sad",
         "Sadan",
         "Said",
@@ -585,7 +569,6 @@ class Provider(PersonProvider):
         "Sadan",
         "Sadaqat",
         "Sadeed",
-        "Name",
         "Taahaa",
         "Taahir",
         "Taahir",
@@ -623,7 +606,6 @@ class Provider(PersonProvider):
         "Talib",
         "Tamam",
         "Tamanna",
-        "Name",
         "Ubaadah",
         "Ubaadah",
         "Ubaadah",
@@ -656,7 +638,6 @@ class Provider(PersonProvider):
         "Umar",
         "Umar",
         "Umar",
-        "Name",
         "Waail",
         "Waail",
         "Waahid",
@@ -676,7 +657,6 @@ class Provider(PersonProvider):
         "Waheed",
         "Wahhaab",
         "Wahhaaj",
-        "Name",
         "Yaaseen",
         "Yafi",
         "Yaghnam",
@@ -697,7 +677,6 @@ class Provider(PersonProvider):
         "Yathrib",
         "Yawar",
         "Yawer",
-        "Name",
         "Zaafir",
         "Zaahid",
         "Zaahid",
@@ -721,304 +700,296 @@ class Provider(PersonProvider):
     )
 
     last_names = (
-        "Lajlaj"
-        "Aarif"
-        "Urrab"
-        "Tabassum"
-        "Ubadah"
-        "Daniel"
-        "Umaarah"
-        "Omair"
-        "Jalil"
-        "Aatiq"
-        "Karaamat"
-        "Lut"
-        "Karam"
-        "Aasif"
-        "Aadam"
-        "Mahbeer"
-        "Saalim"
-        "Ubayd"
-        "Naail"
-        "Mahfuz"
-        "Ghazzal"
-        "Aamir"
-        "Ubaydullah"
-        "Umaarah"
-        "Rabiah"
-        "Maawiya"
-        "Yasir"
-        "Raaghib"
-        "Daamin"
-        "Rabb"
-        "Bashaar"
-        "Taanish"
-        "Yafir"
-        "Baaree"
-        "Talib"
-        "Rafi"
-        "Luqman"
-        "Qaasim"
-        "Ubaidah"
-        "Saajid"
-        "Yaman"
-        "Ubaadah"
-        "Baaqir"
-        "Sadan"
-        "Zarar"
-        "Saafir"
-        "Zafar"
-        "Mahmoud"
-        "Zayyir"
-        "Ubay"
-        "Fidvi"
-        "Mahfuj"
-        "Awmar"
-        "Yawer"
-        "Ayaan"
-        "Taimur"
-        "Rabbani"
-        "Ayyubi"
-        "Waahid"
-        "Ijli"
-        "Baleegh"
-        "Bilaal"
-        "Radi"
-        "Ali"
-        "Tadeen"
-        "Souma"
-        "Layth"
-        "Kashif"
-        "Labeeb"
-        "Talhah"
-        "Sabir"
-        "Dabir"
-        "Yaghnam"
-        "Zackariya"
-        "Ibrahim"
-        "Rafeek"
-        "Qadeer"
-        "Luqmaan"
-        "Jahdari"
-        "Qabeel"
-        "Kaamil"
-        "Ilan"
-        "Omeir"
-        "Ubaid"
-        "Majd"
-        "Aadil"
-        "Ghafoor"
-        "Zahrun"
-        "Tabassum"
-        "Lutf"
-        "Aamir"
-        "Iftikhaar"
-        "Naeem"
-        "Ghauth"
-        "Eshan"
-        "Raid"
-        "Qasif"
-        "Ihsaan"
-        "Bambad"
-        "Aaaqil"
-        "Nabeel"
-        "Jamaal"
-        "Awj"
-        "Wahhaaj"
-        "Nabih"
-        "Jalaal"
-        "Yahyaa"
-        "Aalam"
-        "Ghayoor"
-        "Aarif"
-        "Tahir"
-        "Batal"
-        "Talha"
-        "Uhban"
-        "Aryan"
-        "Najam"
-        "Darain"
-        "Qusay"
-        "Vahar"
-        "Aabid"
-        "Ihtiram"
-        "Umar"
-        "Mahbub"
-        "Qaim"
-        "Name"
-        "Saajid"
-        "Owais"
-        "Maheen"
-        "Raashid"
-        "Limazah"
-        "Zaafir"
-        "Wadood"
-        "Aariz"
-        "Aalam"
-        "Ihab"
-        "Umair"
-        "Zahri"
-        "Aazim"
-        "Jad"
-        "Omar"
-        "Majeed"
-        "Qaseem"
-        "Rafay"
-        "Ghanee"
-        "Gulshan"
-        "Babar"
-        "Baasim"
-        "Ghunayn"
-        "Jaabir"
-        "Nadeem"
-        "Lahiah"
-        "Sair"
-        "Saaqib"
-        "Esfandyar"
-        "Zaheer"
-        "Sabil"
-        "Qutaybah"
-        "Azban"
-        "Zafrul"
-        "Awani"
-        "Tajammul"
-        "Auraq"
-        "Man"
-        "Name"
-        "Tafazal"
-        "Raed"
-        "Baseer"
-        "Quadir"
-        "Dawud"
-        "Talal"
-        "Sabah"
-        "Baashir"
-        "Damurah"
-        "Ibraaheem"
-        "Faizan"
-        "Zaakir"
-        "Ghutayf"
-        "Ehsaas"
-        "Sadeed"
-        "Mad"
-        "Jabir"
-        "Mourib"
-        "Aamil"
-        "Sabeeh"
-        "Bizhan"
-        "Barr"
-        "Basaam"
-        "Ghasaan"
-        "Nail"
-        "Kasim"
-        "Taaj"
-        "Omran"
-        "Madiyan"
-        "Taheem"
-        "Saad"
-        "Kamal"
-        "Raatib"
-        "Taj"
-        "Yadid"
-        "Basheerah"
-        "Aasim"
-        "Zahur"
-        "Saabir"
-        "Kasam"
-        "Naeem"
-        "Tawkeel"
-        "Ghannam"
-        "Tahmaseb"
-        "Awadil"
-        "Liyaaqat"
-        "Tahaw-wur"
-        "Tamanna"
-        "Zafir"
-        "Ghauth"
-        "Ubay"
-        "Zaahid"
-        "Awamil"
-        "Talat"
-        "Maalik"
-        "Name"
-        "Qadar"
-        "Waajid"
-        "Aamirah"
-        "Ayamin"
-        "Kamran"
-        "Kaleem"
-        "Wadi"
-        "Zaahid"
-        "Umar"
-        "Bashaarat"
-        "Saal"
-        "Najeeb"
-        "Kachela"
-        "Sabur"
-        "Buraid"
-        "Rabee"
-        "Najeeb"
-        "Yar"
-        "Umar"
-        "Ossama"
-        "Tahawwur"
-        "Zaahir"
-        "Raashid"
-        "Name"
-        "Tali"
-        "Batool"
-        "Umair"
-        "Ihsaan"
-        "Name"
-        "Majd Udeen"
-        "Kaamil"
-        "Raheel"
-        "Abaan"
-        "Rabah"
-        "Jameel"
-        "Gohar"
-        "Aabid"
-        "Zuwayhir"
-        "Name"
-        "Sadan"
-        "Idris"
-        "Qais"
-        "Sadaqat"
-        "Barraq"
-        "Ejlaal"
-        "Luay"
-        "Jahdami"
-        "Wafeeq"
-        "Wafa"
-        "Rabar"
-        "Aasif"
-        "Dakhil"
-        "Jalaal"
-        "Gulfam"
-        "Saahir"
-        "Name"
-        "Maroof"
-        "Baasit"
-        "Kabeer"
-        "Jameel"
-        "Latif"
-        "Badr Udeen"
-        "Qahtan"
-        "Liyaqat"
-        "Jabr"
-        "Kaleema"
-        "Fazli"
-        "Name"
-        "Huzaifa"
-        "Man"
-        "Rohaan"
-        "Ubadah"
-        "Saburah"
-        "Saariyah"
-        "Kaysan"
-        "Raakin"
-        "Sabiq"
-        "Saboor"
-        "Zahaar"
-        "Jaabir"
+        "Lajlaj",
+        "Aarif",
+        "Urrab",
+        "Tabassum",
+        "Ubadah",
+        "Daniel",
+        "Umaarah",
+        "Omair",
+        "Jalil",
+        "Aatiq",
+        "Karaamat",
+        "Lut",
+        "Karam",
+        "Aasif",
+        "Aadam",
+        "Mahbeer",
+        "Saalim",
+        "Ubayd",
+        "Naail",
+        "Mahfuz",
+        "Ghazzal",
+        "Aamir",
+        "Ubaydullah",
+        "Umaarah",
+        "Rabiah",
+        "Maawiya",
+        "Yasir",
+        "Raaghib",
+        "Daamin",
+        "Rabb",
+        "Bashaar",
+        "Taanish",
+        "Yafir",
+        "Baaree",
+        "Talib",
+        "Rafi",
+        "Luqman",
+        "Qaasim",
+        "Ubaidah",
+        "Saajid",
+        "Yaman",
+        "Ubaadah",
+        "Baaqir",
+        "Sadan",
+        "Zarar",
+        "Saafir",
+        "Zafar",
+        "Mahmoud",
+        "Zayyir",
+        "Ubay",
+        "Fidvi",
+        "Mahfuj",
+        "Awmar",
+        "Yawer",
+        "Ayaan",
+        "Taimur",
+        "Rabbani",
+        "Ayyubi",
+        "Waahid",
+        "Ijli",
+        "Baleegh",
+        "Bilaal",
+        "Radi",
+        "Ali",
+        "Tadeen",
+        "Souma",
+        "Layth",
+        "Kashif",
+        "Labeeb",
+        "Talhah",
+        "Sabir",
+        "Dabir",
+        "Yaghnam",
+        "Zackariya",
+        "Ibrahim",
+        "Rafeek",
+        "Qadeer",
+        "Luqmaan",
+        "Jahdari",
+        "Qabeel",
+        "Kaamil",
+        "Ilan",
+        "Omeir",
+        "Ubaid",
+        "Majd",
+        "Aadil",
+        "Ghafoor",
+        "Zahrun",
+        "Tabassum",
+        "Lutf",
+        "Aamir",
+        "Iftikhaar",
+        "Naeem",
+        "Ghauth",
+        "Eshan",
+        "Raid",
+        "Qasif",
+        "Ihsaan",
+        "Bambad",
+        "Aaaqil",
+        "Nabeel",
+        "Jamaal",
+        "Awj",
+        "Wahhaaj",
+        "Nabih",
+        "Jalaal",
+        "Yahyaa",
+        "Aalam",
+        "Ghayoor",
+        "Aarif",
+        "Tahir",
+        "Batal",
+        "Talha",
+        "Uhban",
+        "Aryan",
+        "Najam",
+        "Darain",
+        "Qusay",
+        "Vahar",
+        "Aabid",
+        "Ihtiram",
+        "Umar",
+        "Mahbub",
+        "Qaim",
+        "Saajid",
+        "Owais",
+        "Maheen",
+        "Raashid",
+        "Limazah",
+        "Zaafir",
+        "Wadood",
+        "Aariz",
+        "Aalam",
+        "Ihab",
+        "Umair",
+        "Zahri",
+        "Aazim",
+        "Jad",
+        "Omar",
+        "Majeed",
+        "Qaseem",
+        "Rafay",
+        "Ghanee",
+        "Gulshan",
+        "Babar",
+        "Baasim",
+        "Ghunayn",
+        "Jaabir",
+        "Nadeem",
+        "Lahiah",
+        "Sair",
+        "Saaqib",
+        "Esfandyar",
+        "Zaheer",
+        "Sabil",
+        "Qutaybah",
+        "Azban",
+        "Zafrul",
+        "Awani",
+        "Tajammul",
+        "Auraq",
+        "Man",
+        "Tafazal",
+        "Raed",
+        "Baseer",
+        "Quadir",
+        "Dawud",
+        "Talal",
+        "Sabah",
+        "Baashir",
+        "Damurah",
+        "Ibraaheem",
+        "Faizan",
+        "Zaakir",
+        "Ghutayf",
+        "Ehsaas",
+        "Sadeed",
+        "Mad",
+        "Jabir",
+        "Mourib",
+        "Aamil",
+        "Sabeeh",
+        "Bizhan",
+        "Barr",
+        "Basaam",
+        "Ghasaan",
+        "Nail",
+        "Kasim",
+        "Taaj",
+        "Omran",
+        "Madiyan",
+        "Taheem",
+        "Saad",
+        "Kamal",
+        "Raatib",
+        "Taj",
+        "Yadid",
+        "Basheerah",
+        "Aasim",
+        "Zahur",
+        "Saabir",
+        "Kasam",
+        "Naeem",
+        "Tawkeel",
+        "Ghannam",
+        "Tahmaseb",
+        "Awadil",
+        "Liyaaqat",
+        "Tahaw-wur",
+        "Tamanna",
+        "Zafir",
+        "Ghauth",
+        "Ubay",
+        "Zaahid",
+        "Awamil",
+        "Talat",
+        "Maalik",
+        "Qadar",
+        "Waajid",
+        "Aamirah",
+        "Ayamin",
+        "Kamran",
+        "Kaleem",
+        "Wadi",
+        "Zaahid",
+        "Umar",
+        "Bashaarat",
+        "Saal",
+        "Najeeb",
+        "Kachela",
+        "Sabur",
+        "Buraid",
+        "Rabee",
+        "Najeeb",
+        "Yar",
+        "Umar",
+        "Ossama",
+        "Tahawwur",
+        "Zaahir",
+        "Raashid",
+        "Tali",
+        "Batool",
+        "Umair",
+        "Ihsaan",
+        "Majd Udeen",
+        "Kaamil",
+        "Raheel",
+        "Abaan",
+        "Rabah",
+        "Jameel",
+        "Gohar",
+        "Aabid",
+        "Zuwayhir",
+        "Sadan",
+        "Idris",
+        "Qais",
+        "Sadaqat",
+        "Barraq",
+        "Ejlaal",
+        "Luay",
+        "Jahdami",
+        "Wafeeq",
+        "Wafa",
+        "Rabar",
+        "Aasif",
+        "Dakhil",
+        "Jalaal",
+        "Gulfam",
+        "Saahir",
+        "Maroof",
+        "Baasit",
+        "Kabeer",
+        "Jameel",
+        "Latif",
+        "Badr Udeen",
+        "Qahtan",
+        "Liyaqat",
+        "Jabr",
+        "Kaleema",
+        "Fazli",
+        "Huzaifa",
+        "Man",
+        "Rohaan",
+        "Ubadah",
+        "Saburah",
+        "Saariyah",
+        "Kaysan",
+        "Raakin",
+        "Sabiq",
+        "Saboor",
+        "Zahaar",
+        "Jaabir",
     )
diff --git a/faker/providers/python/__init__.py b/faker/providers/python/__init__.py
index 28b43562..8f14c9b0 100644
--- a/faker/providers/python/__init__.py
+++ b/faker/providers/python/__init__.py
@@ -149,9 +149,9 @@ class Provider(BaseProvider):
         max_value: Optional[Union[float, int]] = None,
     ) -> float:
         if left_digits is not None and left_digits < 0:
-            raise ValueError("A float number cannot have less than 0 digits in its " "integer part")
+            raise ValueError("A float number cannot have less than 0 digits in its integer part")
         if right_digits is not None and right_digits < 0:
-            raise ValueError("A float number cannot have less than 0 digits in its " "fractional part")
+            raise ValueError("A float number cannot have less than 0 digits in its fractional part")
         if left_digits == 0 and right_digits == 0:
             raise ValueError("A float number cannot have less than 0 digits in total")
         if min_value is not None and max_value is not None:
@@ -290,9 +290,9 @@ class Provider(BaseProvider):
         max_value: Optional[BasicNumber] = None,
     ) -> Decimal:
         if left_digits is not None and left_digits < 0:
-            raise ValueError("A decimal number cannot have less than 0 digits in its " "integer part")
+            raise ValueError("A decimal number cannot have less than 0 digits in its integer part")
         if right_digits is not None and right_digits < 0:
-            raise ValueError("A decimal number cannot have less than 0 digits in its " "fractional part")
+            raise ValueError("A decimal number cannot have less than 0 digits in its fractional part")
         if (left_digits is not None and left_digits == 0) and (right_digits is not None and right_digits == 0):
             raise ValueError("A decimal number cannot have 0 digits in total")
         if min_value is not None and max_value is not None and min_value > max_value:
diff --git a/faker/providers/sbn/__init__.py b/faker/providers/sbn/__init__.py
index f09eab07..e816d159 100644
--- a/faker/providers/sbn/__init__.py
+++ b/faker/providers/sbn/__init__.py
@@ -43,7 +43,7 @@ class Provider(BaseProvider):
                 reg_len = rule.registrant_length
                 break
         else:
-            raise Exception("Registrant/Publication not found in registrant " "rule list.")
+            raise Exception("Registrant/Publication not found in registrant rule list.")
         registrant, publication = reg_pub[:reg_len], reg_pub[reg_len:]
         return registrant, publication
 
diff --git a/faker/providers/ssn/en_US/__init__.py b/faker/providers/ssn/en_US/__init__.py
index 66f2a323..4f9d5a98 100644
--- a/faker/providers/ssn/en_US/__init__.py
+++ b/faker/providers/ssn/en_US/__init__.py
@@ -231,5 +231,5 @@ class Provider(BaseProvider):
 
         else:
             raise ValueError(
-                "taxpayer_identification_number_type must be one of 'SSN', 'EIN', 'ITIN'," " or 'INVALID_SSN'."
+                "taxpayer_identification_number_type must be one of 'SSN', 'EIN', 'ITIN', or 'INVALID_SSN'."
             )
diff --git a/faker/providers/ssn/es_ES/__init__.py b/faker/providers/ssn/es_ES/__init__.py
index 0d321897..f8f9b9fb 100644
--- a/faker/providers/ssn/es_ES/__init__.py
+++ b/faker/providers/ssn/es_ES/__init__.py
@@ -18,6 +18,8 @@ class Provider(BaseProvider):
         """
         http://ec.europa.eu/taxation_customs/vies/faq.html#item_11
         :return: a random Spanish VAT ID
+
+        :sample:
         """
 
         return self.bothify(self.random_element(self.vat_id_formats))
@@ -26,6 +28,8 @@ class Provider(BaseProvider):
         """
         https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero
         :return: a random Spanish NIE
+
+        :sample:
         """
 
         first_chr = random.randrange(0, 3)
@@ -37,6 +41,8 @@ class Provider(BaseProvider):
         """
         https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal
         :return: NIF
+
+        :sample:
         """
 
         nie_body = str(random.randrange(0, 100000000))  # generate a number of a maximum of 8 characters long
@@ -46,6 +52,8 @@ class Provider(BaseProvider):
         """
         https://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal
         :return: a random Spanish CIF
+
+        :sample:
         """
 
         first_chr = random.choice("ABCDEFGHJNPQRSUVW")
@@ -53,18 +61,13 @@ class Provider(BaseProvider):
         cif = first_chr + doi_body
         return cif + self._calculate_control_cif(cif)
 
-    def doi(self) -> str:
-        """
-        https://es.wikipedia.org/wiki/Identificador_de_objeto_digital
-        :return: a random Spanish CIF or NIE or NIF
-        """
-
-        return random.choice([self.cif, self.nie, self.nif])()
-
     def nuss(self, company: bool = False) -> str:
         """
         :param company: flag to indicate if we should generate a company NUSS
         :return: a random Spanish Social Security Number (Número de la Seguridad Social)
+
+        :sample:
+        :sample: company=True
         """
         nuss_body_length = 8
         if company:
diff --git a/faker/providers/user_agent/__init__.py b/faker/providers/user_agent/__init__.py
index 29ee6806..f2fa4342 100644
--- a/faker/providers/user_agent/__init__.py
+++ b/faker/providers/user_agent/__init__.py
@@ -177,8 +177,8 @@ class Provider(BaseProvider):
         """Generate a Chrome web browser user agent string."""
         saf: str = f"{self.generator.random.randint(531, 536)}.{self.generator.random.randint(0, 2)}"
         bld: str = self.lexify(self.numerify("##?###"), string.ascii_uppercase)
-        tmplt: str = "({0}) AppleWebKit/{1} (KHTML, like Gecko)" " Chrome/{2}.0.{3}.0 Safari/{4}"
-        tmplt_ios: str = "({0}) AppleWebKit/{1} (KHTML, like Gecko)" " CriOS/{2}.0.{3}.0 Mobile/{4} Safari/{1}"
+        tmplt: str = "({0}) AppleWebKit/{1} (KHTML, like Gecko) Chrome/{2}.0.{3}.0 Safari/{4}"
+        tmplt_ios: str = "({0}) AppleWebKit/{1} (KHTML, like Gecko) CriOS/{2}.0.{3}.0 Mobile/{4} Safari/{1}"
         platforms: ElementsType[str] = (
             tmplt.format(
                 self.linux_platform_token(),
@@ -283,8 +283,8 @@ class Provider(BaseProvider):
             else f"{self.generator.random.randint(4, 5)}.0.{self.generator.random.randint(1, 5)}"
         )
 
-        tmplt_win: str = "(Windows; U; {0}) AppleWebKit/{1} (KHTML, like Gecko)" " Version/{2} Safari/{3}"
-        tmplt_mac: str = "({0} rv:{1}.0; {2}) AppleWebKit/{3} (KHTML, like Gecko)" " Version/{4} Safari/{5}"
+        tmplt_win: str = "(Windows; U; {0}) AppleWebKit/{1} (KHTML, like Gecko) Version/{2} Safari/{3}"
+        tmplt_mac: str = "({0} rv:{1}.0; {2}) AppleWebKit/{3} (KHTML, like Gecko) Version/{4} Safari/{5}"
         tmplt_ipod: str = (
             "(iPod; U; CPU iPhone OS {0}_{1} like Mac OS X; {2})"
             " AppleWebKit/{3} (KHTML, like Gecko) Version/{4}.0.5"
diff --git a/faker/proxy.py b/faker/proxy.py
index ac39e455..fc72a158 100644
--- a/faker/proxy.py
+++ b/faker/proxy.py
@@ -114,7 +114,7 @@ class Faker:
         :return: the appropriate attribute
         """
         if attr == "seed":
-            msg = "Calling `.seed()` on instances is deprecated. " "Use the class method `Faker.seed()` instead."
+            msg = "Calling `.seed()` on instances is deprecated. Use the class method `Faker.seed()` instead."
             raise TypeError(msg)
         else:
             return super().__getattribute__(attr)
diff --git a/faker/proxy.pyi b/faker/proxy.pyi
index ddae96a2..859f2eec 100644
--- a/faker/proxy.pyi
+++ b/faker/proxy.pyi
@@ -1432,6 +1432,16 @@ class Faker:
         """
         ...
 
+    def doi(self) -> str:
+        """
+        Generate a valid Digital Object Identifier (DOI).
+        Format: 10.{4-9 digits}/{alphanumeric string}
+        Eg: 10.1000/xyz123
+
+        :sample:
+        """
+        ...
+
     def emoji(self) -> str:
         """
         :example: '😉'
@@ -2202,6 +2212,19 @@ class Faker:
         """
         ...
 
+    @overload
+    def md5(self) -> str:
+        """
+        Generate a random MD5 hash.
+
+        If ``raw_output`` is ``False`` (default), a hexadecimal string representation of the MD5 hash
+        will be returned. If ``True``, a ``bytes`` object representation will be returned instead.
+
+        :sample: raw_output=False
+        :sample: raw_output=True
+        """
+        ...
+
     @overload
     def md5(self, raw_output: Literal[True]) -> bytes:
         """
@@ -2277,6 +2300,19 @@ class Faker:
         """
         ...
 
+    @overload
+    def sha1(self) -> str:
+        """
+        Generate a random SHA-1 hash.
+
+        If ``raw_output`` is ``False`` (default), a hexadecimal string representation of the SHA-1 hash
+        will be returned. If ``True``, a ``bytes`` object representation will be returned instead.
+
+        :sample: raw_output=False
+        :sample: raw_output=True
+        """
+        ...
+
     @overload
     def sha1(self, raw_output: Literal[True]) -> bytes:
         """
@@ -2303,6 +2339,19 @@ class Faker:
         """
         ...
 
+    @overload
+    def sha256(self) -> str:
+        """
+        Generate a random SHA-256 hash.
+
+        If ``raw_output`` is ``False`` (default), a hexadecimal string representation of the SHA-256 hash
+        will be returned. If ``True``, a ``bytes`` object representation will be returned instead.
+
+        :sample: raw_output=False
+        :sample: raw_output=True
+        """
+        ...
+
     @overload
     def sha256(self, raw_output: Literal[True]) -> bytes:
         """
@@ -3409,13 +3458,8 @@ class Faker:
         """
         https://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal
         :return: a random Spanish CIF
-        """
-        ...
 
-    def doi(self) -> str:
-        """
-        https://es.wikipedia.org/wiki/Identificador_de_objeto_digital
-        :return: a random Spanish CIF or NIE or NIF
+        :sample:
         """
         ...
 
@@ -3423,6 +3467,8 @@ class Faker:
         """
         https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero
         :return: a random Spanish NIE
+
+        :sample:
         """
         ...
 
@@ -3430,6 +3476,8 @@ class Faker:
         """
         https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal
         :return: NIF
+
+        :sample:
         """
         ...
 
@@ -3437,6 +3485,9 @@ class Faker:
         """
         :param company: flag to indicate if we should generate a company NUSS
         :return: a random Spanish Social Security Number (Número de la Seguridad Social)
+
+        :sample:
+        :sample: company=True
         """
         ...
 
diff --git a/generate_stubs.py b/generate_stubs.py
index cbe64c2c..b5dfb6aa 100644
--- a/generate_stubs.py
+++ b/generate_stubs.py
@@ -34,7 +34,7 @@ def get_module_and_member_to_import(cls: Type, locale: Optional[str] = None) ->
             if imports[qualified_type[0][0]] is None or qualified_type[0][1] not in imports[qualified_type[0][0]]:
                 module, member = qualified_type[0]
         else:
-            unqualified_type = re.findall(r"[^\.a-zA-Z0-9_]([A-Z][a-zA-Z0-9_]+)[^\.a-zA-Z0-9_]", " " + str(cls) + " ")
+            unqualified_type = re.findall(r"[^\.a-zA-Z0-9_]([A-Z][a-zA-Z0-9_]+)[^\.a-zA-Z0-9_]", f" {cls} ")
             if len(unqualified_type) > 0 and unqualified_type[0] != "NoneType":
                 cls_str = str(cls).replace(".en_US", "").replace("faker.", ".")
                 if "<class '" in cls_str:


Test patch:
diff --git a/tests/providers/test_bank.py b/tests/providers/test_bank.py
index a0692e68..12ccff56 100644
--- a/tests/providers/test_bank.py
+++ b/tests/providers/test_bank.py
@@ -467,17 +467,16 @@ class TestNlBe:
             assert re.fullmatch(r"\d{12}", bban)
             account_number = bban[:-2]
             check_digits = int(bban[-2:])
-            assert (97 - (int(account_number) % 97)) == check_digits or check_digits == 97
+            assert (int(account_number) % 97) == check_digits or check_digits == 97
 
     def test_iban(self, faker, num_samples):
         for _ in range(num_samples):
             iban = faker.iban()
             assert iban[:2] == NlBeBankProvider.country_code
             assert re.fullmatch(r"\d{2}\d{12}", iban[2:])
-            bban = iban[4:]
-            account_number = bban[:-2]
-            check_digits = int(bban[-2:])
-            assert (97 - (int(account_number) % 97)) == check_digits or check_digits == 97
+            rearranged_iban = iban[4:] + iban[:4]
+            numeric_iban = "".join(str(ord(char) - 55) if char.isalpha() else char for char in rearranged_iban)
+            assert int(numeric_iban) % 97 == 1
 
     def test_swift8_use_dataset(self, faker, num_samples):
         for _ in range(num_samples):
diff --git a/tests/providers/test_currency.py b/tests/providers/test_currency.py
index ec3e6431..b9ca42c4 100644
--- a/tests/providers/test_currency.py
+++ b/tests/providers/test_currency.py
@@ -391,6 +391,23 @@ class TestItIt:
             assert isinstance(pricetag, str)
 
 
+class TestNgNg:
+    """Test ng_NG currency provider"""
+
+    num_samples = 100
+
+    @classmethod
+    def setup_class(cls):
+        from faker.providers.currency.ng_NG import Provider as NgNgCurrencyProvider
+
+        cls.provider = NgNgCurrencyProvider
+
+    def test_pricetag(self, faker, num_samples):
+        for _ in range(num_samples):
+            pricetag = faker.pricetag()
+            assert isinstance(pricetag, str)
+
+
 class TestPlPl:
     """Test pl_PL currency provider"""
 
diff --git a/tests/providers/test_doi.py b/tests/providers/test_doi.py
new file mode 100644
index 00000000..8063a012
--- /dev/null
+++ b/tests/providers/test_doi.py
@@ -0,0 +1,26 @@
+import re
+
+from faker import Faker
+
+
+def test_doi():
+    fake = Faker()
+
+    # Test standard DOI
+    doi = fake.doi()
+    assert doi.startswith("10.")
+    # DOI format: 10.{registrant}/{suffix}
+    assert re.match(r"^10\.\d{4,9}/[a-z0-9]+$", doi)
+
+
+def test_doi_es_ES():
+    # Test Spanish locale no longer returns Spanish IDs
+    fake = Faker("es_ES")
+    doi = fake.doi()
+
+    # Should follow DOI format, not Spanish ID format
+    assert doi.startswith("10.")
+    assert re.match(r"^10\.\d{4,9}/[a-z0-9]+$", doi)
+    # Make sure it's not returning Spanish IDs
+    assert not re.match(r"^[XYZ]\d{7}[A-Z]$", doi)  # NIE format
+    assert not re.match(r"^\d{8}[A-Z]$", doi)  # NIF format
diff --git a/tests/providers/test_person.py b/tests/providers/test_person.py
index a47c6cca..64a10dfc 100644
--- a/tests/providers/test_person.py
+++ b/tests/providers/test_person.py
@@ -434,6 +434,7 @@ class TestEnPk(unittest.TestCase):
     def test_last_name(self):
         """Test if the last name is from the predefined list."""
         last_name = self.fake.last_name()
+        self.assertGreater(len(last_name), 1, "Last name should have more than 1 character.")
         self.assertIn(last_name, EnPKprovider.last_names)
 
     def test_full_name(self):
@@ -448,8 +449,20 @@ class TestEnPk(unittest.TestCase):
         name = self.fake.name()
         name_parts = name.split()
         self.assertGreaterEqual(len(name_parts), 2, "Full name should have at least a first and last name.")
-        self.assertIn(name_parts[0], EnPKprovider.first_names)
-        self.assertIn(name_parts[-1], EnPKprovider.last_names)
+        if len(name_parts) == 2:
+            self.assertIn(name_parts[0], EnPKprovider.first_names)
+            self.assertIn(name_parts[-1], EnPKprovider.last_names)
+        elif len(name_parts) == 4:
+            self.assertIn(name_parts[:2], EnPKprovider.first_names)
+            self.assertIn(name_parts[2:], EnPKprovider.last_names)
+        elif len(name_parts) == 3:
+            assert (
+                " ".join(name_parts[:2]) in EnPKprovider.first_names
+                and " ".join(name_parts[2]) in EnPKprovider.last_names
+            ) or (
+                " ".join(name_parts[:1]) in EnPKprovider.first_names
+                and " ".join(name_parts[1:]) in EnPKprovider.last_names
+            ), "Either first two name parts should be in first names, or last two should be in last names."
 
 
 class TestEnUS(unittest.TestCase):
diff --git a/tests/providers/test_ssn.py b/tests/providers/test_ssn.py
index a99b2224..45455ba3 100644
--- a/tests/providers/test_ssn.py
+++ b/tests/providers/test_ssn.py
@@ -666,9 +666,6 @@ class TestEsES(unittest.TestCase):
         for _ in range(100):
             assert is_cif(self.fake.cif())
 
-    def test_doi(self):
-        assert len(self.fake.doi()) == 9
-
     def test_nuss(self):
         for _ in range(50):
             nuss = self.fake.nuss()
diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py
index f0f69dfa..bf9c3d32 100644
--- a/tests/utils/test_utils.py
+++ b/tests/utils/test_utils.py
@@ -79,6 +79,7 @@ class UtilsTestCase(unittest.TestCase):
                     "faker.providers.credit_card",
                     "faker.providers.currency",
                     "faker.providers.date_time",
+                    "faker.providers.doi",
                     "faker.providers.emoji",
                     "faker.providers.file",
                     "faker.providers.geo",