Repository: joke2k/faker
Pull request #2166 (2025-02-05 15:07:22)

feat: Add fake-security to list of community providers
### What does this change

Adds `faker-security` to the list of community providers

This was actually released quite a while back, but I only noticed this community providers page today

### What was wrong

Nothing 

### How this fixes it

### 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`


MichaelAquilina: From what I can tell, the errors seem unrelated to my changes? 🤔 

Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index b63c18e4..2fa6f33a 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 35.2.0
+current_version = 36.1.1
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 54f185f8..ba18dcd3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -139,7 +139,6 @@ jobs:
     strategy:
       matrix:
         python:
-        - "3.8"
         - "3.9"
         - "3.10"
         - "3.11"
@@ -175,7 +174,6 @@ jobs:
     strategy:
       matrix:
         python:
-        - "3.8"
         - "3.9"
         - "3.10"
         - "3.11"
@@ -198,7 +196,7 @@ jobs:
         with:
           python-version: ${{ matrix.python }}
       - name: Install Tox and any other packages
-        run: python -m pip install tox coveralls
+        run: python -m pip install tox
       - name: pytest
         uses: liskin/gh-problem-matcher-wrap@v3
         with:
@@ -209,21 +207,13 @@ jobs:
           COVERALLS_FLAG_NAME: run-ubuntu-${{ matrix.python }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           COVERALLS_SERVICE_NAME: github
-      - name: Publish coverage
-        run: coveralls --service=github
-        env:
-          COVERALLS_PARALLEL: true
-          COVERALLS_FLAG_NAME: run-ubuntu-${{ matrix.python }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          COVERALLS_SERVICE_NAME: github
-
+ 
   test_windows:
     runs-on: windows-latest
     needs: [flake8, isort, black, doc8, checkmanifest, typing]
     strategy:
       matrix:
         python:
-        - "3.8"
         - "3.9"
         - "3.10"
         - "3.11"
@@ -246,17 +236,10 @@ jobs:
         with:
           python-version: ${{ matrix.python }}
       - name: Install Tox and any other packages
-        run: python -m pip install tox coveralls
+        run: python -m pip install tox
       - name: Run Tox
         # Run tox using the version of Python in `PATH`
         run: tox -e py
-      - name: Publish coverage
-        run: coveralls --service=github
-        env:
-          COVERALLS_PARALLEL: true
-          COVERALLS_FLAG_NAME: run-ubuntu-${{ matrix.python }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          COVERALLS_SERVICE_NAME: github
 
   test_alpine:
     runs-on: ubuntu-latest
@@ -284,13 +267,3 @@ jobs:
       env:
         TOXENV: alpine
         TEST_ALPINE: 1
-
-  finish:
-    needs: [test_ubuntu, test_windows, test_alpine]
-    runs-on: ubuntu-latest
-    steps:
-    - name: Coveralls Finished
-      uses: AndreMiras/coveralls-python-action@develop
-      with:
-        github-token: ${{ secrets.GITHUB_TOKEN }}
-        parallel-finished: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 522cf4cb..7d23295c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
 ## Changelog
 
+### [v36.1.1 - 2025-02-13](https://github.com/joke2k/faker/compare/v36.1.0...v36.1.1)
+
+* Fix typing for `uuid` provider. Thanks @evenicoulddoit.
+
+### [v36.1.0 - 2025-02-10](https://github.com/joke2k/faker/compare/v36.0.0...v36.1.0)
+
+* Drop `python-dateutil` dependency. Thanks @knyghty.
+* Add dependency on `tzdata`.
+
+### [v36.0.0 - 2025-02-10](https://github.com/joke2k/faker/compare/v35.2.1...v36.0.0)
+
+* Drop Python 3.8 support. Thanks @knyghty.
+
+### [v35.2.2 - 2025-02-10](https://github.com/joke2k/faker/compare/v35.2.1...v35.2.2)
+
+* Revert accidental deprecation of Python 3.8.
+
+### [v35.2.1 - 2025-02-10](https://github.com/joke2k/faker/compare/v35.2.0...v35.2.1)
+
+* Remove invalid word from `ja_JP` lorem provider. Thanks @kymckay
+* Fix typing for `tar` provider.
+
 ### [v35.2.0 - 2025-01-30](https://github.com/joke2k/faker/compare/v35.1.0...v35.2.0)
 
 * Add various German localization. Thanks @ChristinaRau.
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index bd47b86f..1ddcd73e 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -34,7 +34,7 @@ Making Changes
 -  Check for unnecessary whitespace with ``git diff --check`` before
    committing.
 -  Make sure you have added the necessary tests for your changes.
--  Run ``make lint`` in the repository directory and commit any changes it makes.
+-  Run ``make lint`` in the repository directory and commit any changes it makes. Note: requires Python 3.11.
 -  Run *all* the tests to assure nothing else was accidentally broken:
 
    .. code:: bash
@@ -59,4 +59,3 @@ Additional Resources
 
 .. _`coding style`: https://github.com/joke2k/faker/blob/master/docs/coding_style.rst
 .. _`community providers`: https://github.com/joke2k/faker/blob/master/docs/communityproviders.rst
-
diff --git a/Makefile b/Makefile
index 1e6a9442..b4f3e407 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ isort:
 	isort --atomic .
 
 generate-stubs: 
-	python3.10 generate_stubs.py
+	python3.11 generate_stubs.py
 
 lint: generate-stubs isort black mypy flake8
 
diff --git a/README.rst b/README.rst
index 9bceeecc..77addc49 100644
--- a/README.rst
+++ b/README.rst
@@ -23,7 +23,7 @@ Compatibility
 -------------
 
 Starting from version ``4.0.0``, ``Faker`` dropped support for Python 2 and from version ``5.0.0``
-only supports Python 3.7 and above. If you still need Python 2 compatibility, please install version ``3.0.1`` in the
+only supports Python 3.8 and above. If you still need Python 2 compatibility, please install version ``3.0.1`` in the
 meantime, and please consider updating your codebase to support Python 3 so you can enjoy the
 latest features ``Faker`` has to offer. Please see the `extended docs`_ for more details, especially
 if you are upgrading from version ``2.0.4`` and below as there might be breaking changes.
diff --git a/VERSION b/VERSION
index 6ee5ed10..2e1659a7 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-35.2.0
+36.1.1
diff --git a/docs/communityproviders.rst b/docs/communityproviders.rst
index e568acb3..3bf517ef 100644
--- a/docs/communityproviders.rst
+++ b/docs/communityproviders.rst
@@ -65,6 +65,9 @@ Here's a list of Providers written by the community:
 | Faker         | Recognition models        |                                  |
 |               | using Faker.              |                                  |
 +---------------+---------------------------+----------------------------------+
+| Security      | Fake data related to      | `faker-security`_                |
+|               | security e.g. CVSS, CVE   |                                  |
++-------------------------------------------------------------------------------
 
 If you want to add your own provider to this list, please submit a Pull Request to our `repo`_.
 
@@ -96,3 +99,4 @@ In order to be included, your provider must satisfy these requirements:
 .. _faker_wifi_essid: https://pypi.org/project/faker-wifi-essid/
 .. _optional_faker: https://pypi.org/project/optional_faker
 .. _presidio-evaluator: https://pypi.org/project/presidio-evaluator
+.. _faker-security: https://pypi.org/project/faker-security/
diff --git a/docs/conf.py b/docs/conf.py
index 96b0993d..5bffb0f5 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 = "35.2.0"
+version = "36.1.1"
 # The full version, including alpha/beta/rc tags.
-release = "35.2.0"
+release = "36.1.1"
 
 # 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 8d1e8a79..68003d8e 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 = "35.2.0"
+VERSION = "36.1.1"
 
 __all__ = ("Factory", "Generator", "Faker")
diff --git a/faker/providers/address/en_BD/__init__.py b/faker/providers/address/en_BD/__init__.py
index 1fd4ca52..49e0c074 100644
--- a/faker/providers/address/en_BD/__init__.py
+++ b/faker/providers/address/en_BD/__init__.py
@@ -1,5 +1,5 @@
 """
-    Contributed by: @aamibhoot 🇧🇩
+Contributed by: @aamibhoot 🇧🇩
 """
 
 from .. import Provider as AddressProvider
diff --git a/faker/providers/address/en_IN/__init__.py b/faker/providers/address/en_IN/__init__.py
index 4004bcb4..c517e6c4 100644
--- a/faker/providers/address/en_IN/__init__.py
+++ b/faker/providers/address/en_IN/__init__.py
@@ -1,10 +1,8 @@
 from typing import Dict, List, Optional, Tuple
 
-from typing_extensions import TypeAlias
-
 from faker.providers.address import Provider as AddressProvider
 
-Range: TypeAlias = Tuple[int, int]
+Range = Tuple[int, int]
 
 
 class Provider(AddressProvider):
diff --git a/faker/providers/company/ru_RU/__init__.py b/faker/providers/company/ru_RU/__init__.py
index 23cde258..ca2a574e 100644
--- a/faker/providers/company/ru_RU/__init__.py
+++ b/faker/providers/company/ru_RU/__init__.py
@@ -12,6 +12,11 @@ def calculate_checksum(value: str) -> str:
     return str((check_sum % 11) % 10)
 
 
+def calculate_snils_checksum(numbers: str) -> str:
+    checksum = sum(int(v) * (9 - i) for i, v in enumerate(numbers)) % 101
+    return "%02d" % (checksum if checksum < 100 else 0)
+
+
 class Provider(CompanyProvider):
     formats = (
         "{{company_prefix}} «{{last_name}}»",
@@ -1168,3 +1173,10 @@ class Provider(CompanyProvider):
         tail: str = "%03d" % self.random_int(min=1, max=999)
 
         return region + inspection + reason + tail
+
+    def snils(self) -> str:
+        """
+        Returns SNILS number (ru. СНИЛС).
+        """
+        numbers: str = "%09d" % self.random_int(min=1, max=999999999)
+        return numbers + calculate_snils_checksum(numbers)
diff --git a/faker/providers/currency/__init__.py b/faker/providers/currency/__init__.py
index 215eadc8..c101f7ac 100644
--- a/faker/providers/currency/__init__.py
+++ b/faker/providers/currency/__init__.py
@@ -225,9 +225,9 @@ class Provider(BaseProvider):
     # source: https://en.wikipedia.org/wiki/Currency_symbol
     currency_symbols: Dict[str, str] = {
         "AED": "\u002e\u062f\u002e\u0625",
-        "AFN": "\u060B",
+        "AFN": "\u060b",
         "ALL": "Lek",
-        "AMD": "\u058F",
+        "AMD": "\u058f",
         "ANG": "\u0192",
         "AOA": "Kz",
         "ARS": "\u0024",
@@ -236,9 +236,9 @@ class Provider(BaseProvider):
         "AZN": "\u20bc",
         "BAM": "KM",
         "BBD": "\u0024",
-        "BDT": "\u09F3",
+        "BDT": "\u09f3",
         "BGN": "Lev",
-        "BHD": "\u062F\u0628",
+        "BHD": "\u062f\u0628",
         "BIF": "Fr",
         "BMD": "\u0024",
         "BND": "\u0024",
@@ -253,9 +253,9 @@ class Provider(BaseProvider):
         "CDF": "Fr",
         "CHF": "Fr",
         "CLP": "\u0024",
-        "CNY": "\u00A5",
+        "CNY": "\u00a5",
         "COP": "\u0024",
-        "CRC": "\u20A1",
+        "CRC": "\u20a1",
         "CUC": "\u0024",
         "CUP": "\u0024",
         "CVE": "\u0024",
@@ -264,17 +264,17 @@ class Provider(BaseProvider):
         "DKK": "kr",
         "DOP": "\u0024",
         "DZD": "\u062f\u062c\u200e",
-        "EGP": "\u00A3",
+        "EGP": "\u00a3",
         "ERN": "Nfk",
         "ETB": "Br",
-        "EUR": "\u20AC",
+        "EUR": "\u20ac",
         "FJD": "\u0024",
-        "FKP": "\u00A3",
-        "GBP": "\u00A3",
-        "GEL": "\u20BE",
-        "GGP": "\u00A3",
-        "GHS": "\u20B5",
-        "GIP": "\u00A3",
+        "FKP": "\u00a3",
+        "GBP": "\u00a3",
+        "GEL": "\u20be",
+        "GGP": "\u00a3",
+        "GHS": "\u20b5",
+        "GIP": "\u00a3",
         "GMD": "D",
         "GNF": "FG",
         "GTQ": "Q",
@@ -285,28 +285,28 @@ class Provider(BaseProvider):
         "HTG": "G",
         "HUF": "Ft",
         "IDR": "Rp",
-        "ILS": "\u20AA",
-        "IMP": "\u00A3",
-        "INR": "\u20B9",
-        "IQD": "\u062F\u0639",
-        "IRR": "\uFDFC",
+        "ILS": "\u20aa",
+        "IMP": "\u00a3",
+        "INR": "\u20b9",
+        "IQD": "\u062f\u0639",
+        "IRR": "\ufdfc",
         "ISK": "kr",
-        "JEP": "\u00A3",
+        "JEP": "\u00a3",
         "JMD": "\u0024",
         "JOD": "JD",
-        "JPY": "\u00A5",
+        "JPY": "\u00a5",
         "KES": "KSh",
-        "KGS": "\u20C0",
-        "KHR": "\u17DB",
+        "KGS": "\u20c0",
+        "KHR": "\u17db",
         "KMF": "FC",
-        "KPW": "\u20A9",
-        "KRW": "\u20A9",
+        "KPW": "\u20a9",
+        "KRW": "\u20a9",
         "KWD": "KD",
         "KYD": "\u0024",
-        "KZT": "\u20B8",
-        "LAK": "\u20AD",
-        "LBP": "\u00A3",
-        "LKR": "\u20A8",
+        "KZT": "\u20b8",
+        "LAK": "\u20ad",
+        "LBP": "\u00a3",
+        "LKR": "\u20a8",
         "LRD": "\u0024",
         "LSL": "M",
         "LTL": "L",
@@ -316,67 +316,67 @@ class Provider(BaseProvider):
         "MGA": "Ar",
         "MKD": "DEN",
         "MMK": "Ks",
-        "MNT": "\u20AE",
+        "MNT": "\u20ae",
         "MOP": "\u0024",
         "MRO": "UM",
-        "MUR": "\u20A8",
+        "MUR": "\u20a8",
         "MVR": "\u0078",
         "MWK": "K",
         "MXN": "\u0024",
         "MYR": "RM",
         "MZN": "Mt",
         "NAD": "\u0024",
-        "NGN": "\u20A6",
+        "NGN": "\u20a6",
         "NIO": "\u0024",
-        "NIS": "\u20AA",
+        "NIS": "\u20aa",
         "NOK": "kr",
-        "NPR": "\u20A8",
+        "NPR": "\u20a8",
         "NZD": "\u0024",
-        "OMR": "\uFDFC",
+        "OMR": "\ufdfc",
         "PAB": "B/",
         "PEN": "S/",
         "PGK": "K",
-        "PHP": "\u20B1",
-        "PKR": "\u20A8",
+        "PHP": "\u20b1",
+        "PKR": "\u20a8",
         "PLN": "\u007a\u0142",
-        "PYG": "\u20B2",
-        "QAR": "\uFDFC",
+        "PYG": "\u20b2",
+        "QAR": "\ufdfc",
         "RON": "leu",
         "RSD": "\u0434\u0438\u043d",
-        "RUB": "\u20BD",
+        "RUB": "\u20bd",
         "RWF": "F",
-        "SAR": "\uFDFC",
+        "SAR": "\ufdfc",
         "SBD": "\u0024",
-        "SCR": "\u20A8",
-        "SDG": "\u00A3",
+        "SCR": "\u20a8",
+        "SDG": "\u00a3",
         "SEK": "kr",
         "SGD": "\u0024",
-        "SHP": "\u00A3",
+        "SHP": "\u00a3",
         "SLL": "Le",
         "SOS": "Sh.So.",
         "SPL": "L",
         "SRD": "\u0024",
         "STD": "Db",
-        "SVC": "\u20A1",
-        "SYP": "\u00A3",
+        "SVC": "\u20a1",
+        "SYP": "\u00a3",
         "SZL": "E",
-        "THB": "\u0E3F",
+        "THB": "\u0e3f",
         "TJS": "SM",
         "TMT": "m",
         "TND": "DT",
         "TOP": "\u00a2",
-        "TRY": "\u20BA",
+        "TRY": "\u20ba",
         "TTD": "\u0024",
         "TVD": "\u0024",
         "TWD": "\u0024",
         "TZS": "Tsh",
-        "UAH": "\u20B4",
+        "UAH": "\u20b4",
         "UGX": "USh",
         "USD": "\u0024",
         "UYU": "\u0024",
         "UZS": "\u043b\u0432",
         "VEF": "\u0042\u0073",
-        "VND": "\u20AB",
+        "VND": "\u20ab",
         "VUV": "VT",
         "WST": "\u0024",
         "XAF": "Fr",
@@ -384,7 +384,7 @@ class Provider(BaseProvider):
         "XDR": "SDR",
         "XOF": "Fr",
         "XPF": "Fr",
-        "YER": "\uFDFC",
+        "YER": "\ufdfc",
         "ZAR": "R",
         "ZMW": "K",
         "ZWD": "\u0024",
@@ -409,7 +409,7 @@ class Provider(BaseProvider):
             code = self.random_element(self.currency_symbols.keys())
         elif code not in [currency[0] for currency in self.currencies]:
             raise KeyError("The supplied code is not valid")
-        return self.currency_symbols.get(code, "\u00A4")
+        return self.currency_symbols.get(code, "\u00a4")
 
     def cryptocurrency(self) -> Tuple[str, str]:
         return self.random_element(self.cryptocurrencies)
@@ -422,4 +422,4 @@ class Provider(BaseProvider):
 
     def pricetag(self) -> str:
         currency: Tuple[str, str] = self.random_element(self.currencies)
-        return currency[0] + "\N{no-break space}" + self.numerify(self.random_element(self.price_formats))
+        return currency[0] + "\N{NO-BREAK SPACE}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/currency/az_AZ/__init__.py b/faker/providers/currency/az_AZ/__init__.py
index b8a37865..4a4638cb 100644
--- a/faker/providers/currency/az_AZ/__init__.py
+++ b/faker/providers/currency/az_AZ/__init__.py
@@ -175,4 +175,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self):
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}AZN"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}AZN"
diff --git a/faker/providers/currency/cs_CZ/__init__.py b/faker/providers/currency/cs_CZ/__init__.py
index ad67125c..5af4f01b 100644
--- a/faker/providers/currency/cs_CZ/__init__.py
+++ b/faker/providers/currency/cs_CZ/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,#0", "%#,#0", "%##,#0", "%.###,#0", "%#.###,#0"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}Kč"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}Kč"
diff --git a/faker/providers/currency/de_AT/__init__.py b/faker/providers/currency/de_AT/__init__.py
index 0371f9d6..2a6bccb5 100644
--- a/faker/providers/currency/de_AT/__init__.py
+++ b/faker/providers/currency/de_AT/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/de_CH/__init__.py b/faker/providers/currency/de_CH/__init__.py
index af04dff5..a453ec6c 100644
--- a/faker/providers/currency/de_CH/__init__.py
+++ b/faker/providers/currency/de_CH/__init__.py
@@ -3,7 +3,7 @@ from ..de import Provider as CurrencyProvider
 
 class Provider(CurrencyProvider):
     # source: https://de.wikipedia.org/wiki/Schreibweise_von_Zahlen#Dezimaltrennzeichen_2
-    price_formats = ["\N{figure dash}.##", "%.##", "%#.##", "%##.##", "% ###.##", "%# ###.##"]
+    price_formats = ["\N{FIGURE DASH}.##", "%.##", "%#.##", "%##.##", "% ###.##", "%# ###.##"]
 
     def pricetag(self):
-        return "Fr.\N{no-break space}" + self.numerify(self.random_element(self.price_formats))
+        return "Fr.\N{NO-BREAK SPACE}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/currency/de_DE/__init__.py b/faker/providers/currency/de_DE/__init__.py
index 28e0dcfe..a49621b8 100644
--- a/faker/providers/currency/de_DE/__init__.py
+++ b/faker/providers/currency/de_DE/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self):
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/el_GR/__init__.py b/faker/providers/currency/el_GR/__init__.py
index a16afce4..a24bf250 100644
--- a/faker/providers/currency/el_GR/__init__.py
+++ b/faker/providers/currency/el_GR/__init__.py
@@ -155,4 +155,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/en_AU/__init__.py b/faker/providers/currency/en_AU/__init__.py
index 7f0736ed..6735f0a2 100644
--- a/faker/providers/currency/en_AU/__init__.py
+++ b/faker/providers/currency/en_AU/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#.##", "%#.##", "%##.##", "%,###.##", "%#,###.##"]
 
     def pricetag(self) -> str:
-        return "$\N{no-break space}" + self.numerify(self.random_element(self.price_formats))
+        return "$\N{NO-BREAK SPACE}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/currency/en_CA/__init__.py b/faker/providers/currency/en_CA/__init__.py
index 96be3bd0..3171b0aa 100644
--- a/faker/providers/currency/en_CA/__init__.py
+++ b/faker/providers/currency/en_CA/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#.##", "%#.##", "%##.##", "%,###.##", "%#,###.##"]
 
     def pricetag(self):
-        return "$\N{no-break space}" + self.numerify(self.random_element(self.price_formats))
+        return "$\N{NO-BREAK SPACE}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/currency/es_CL/__init__.py b/faker/providers/currency/es_CL/__init__.py
index 3cf0f5aa..084b7446 100644
--- a/faker/providers/currency/es_CL/__init__.py
+++ b/faker/providers/currency/es_CL/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["%##", "%.###", "%#.##0", "%##.##0", "%##.##0", "%.###.##0"]
 
     def pricetag(self) -> str:
-        return "\N{dollar sign}\N{no-break space}" + self.numerify(self.random_element(self.price_formats))
+        return "\N{DOLLAR SIGN}\N{NO-BREAK SPACE}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/currency/es_ES/__init__.py b/faker/providers/currency/es_ES/__init__.py
index c4513052..5781a991 100644
--- a/faker/providers/currency/es_ES/__init__.py
+++ b/faker/providers/currency/es_ES/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/fa_IR/__init__.py b/faker/providers/currency/fa_IR/__init__.py
index a15d7a1d..95de6dae 100644
--- a/faker/providers/currency/fa_IR/__init__.py
+++ b/faker/providers/currency/fa_IR/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["###,###,000", "#,###,000,000", "%,###,###,###,###", "%,###,###,###,000,000"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\uFDFC"
+        return self.numerify(self.random_element(self.price_formats)) + "\ufdfc"
diff --git a/faker/providers/currency/fr_CA/__init__.py b/faker/providers/currency/fr_CA/__init__.py
index 94b138ba..d570e238 100644
--- a/faker/providers/currency/fr_CA/__init__.py
+++ b/faker/providers/currency/fr_CA/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}$"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}$"
diff --git a/faker/providers/currency/fr_FR/__init__.py b/faker/providers/currency/fr_FR/__init__.py
index 93d2014f..61971648 100644
--- a/faker/providers/currency/fr_FR/__init__.py
+++ b/faker/providers/currency/fr_FR/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self):
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/it_IT/__init__.py b/faker/providers/currency/it_IT/__init__.py
index 93d2014f..61971648 100644
--- a/faker/providers/currency/it_IT/__init__.py
+++ b/faker/providers/currency/it_IT/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self):
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/nl_NL/__init__.py b/faker/providers/currency/nl_NL/__init__.py
index f155b6e9..b3f8de49 100644
--- a/faker/providers/currency/nl_NL/__init__.py
+++ b/faker/providers/currency/nl_NL/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return "\N{euro sign}" + self.numerify(self.random_element(self.price_formats))
+        return "\N{EURO SIGN}" + self.numerify(self.random_element(self.price_formats))
diff --git a/faker/providers/currency/pl_PL/__init__.py b/faker/providers/currency/pl_PL/__init__.py
index 84de96b0..82d4db29 100644
--- a/faker/providers/currency/pl_PL/__init__.py
+++ b/faker/providers/currency/pl_PL/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}zł"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}zł"
diff --git a/faker/providers/currency/ro_RO/__init__.py b/faker/providers/currency/ro_RO/__init__.py
index a040bfa4..8cbe0451 100644
--- a/faker/providers/currency/ro_RO/__init__.py
+++ b/faker/providers/currency/ro_RO/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}Lei"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}Lei"
diff --git a/faker/providers/currency/ru_RU/__init__.py b/faker/providers/currency/ru_RU/__init__.py
index 36d796ad..00dd5d08 100644
--- a/faker/providers/currency/ru_RU/__init__.py
+++ b/faker/providers/currency/ru_RU/__init__.py
@@ -175,5 +175,5 @@ class Provider(CurrencyProvider):
 
     def pricetag(self) -> str:
         return (
-            self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{cyrillic small letter er}."
+            self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{CYRILLIC SMALL LETTER ER}."
         )
diff --git a/faker/providers/currency/sk_SK/__init__.py b/faker/providers/currency/sk_SK/__init__.py
index 93d2014f..61971648 100644
--- a/faker/providers/currency/sk_SK/__init__.py
+++ b/faker/providers/currency/sk_SK/__init__.py
@@ -5,4 +5,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"]
 
     def pricetag(self):
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{euro sign}"
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{EURO SIGN}"
diff --git a/faker/providers/currency/uk_UA/__init__.py b/faker/providers/currency/uk_UA/__init__.py
index c77b5a66..07fc175b 100644
--- a/faker/providers/currency/uk_UA/__init__.py
+++ b/faker/providers/currency/uk_UA/__init__.py
@@ -170,4 +170,4 @@ class Provider(CurrencyProvider):
     price_formats = ["#,##", "%#,##", "%##,##", "% ###,##", "%# ###,##"]
 
     def pricetag(self) -> str:
-        return self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}грн."
+        return self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}грн."
diff --git a/faker/providers/currency/uz_UZ/__init__.py b/faker/providers/currency/uz_UZ/__init__.py
index 418ff138..79bae34e 100644
--- a/faker/providers/currency/uz_UZ/__init__.py
+++ b/faker/providers/currency/uz_UZ/__init__.py
@@ -174,5 +174,5 @@ class Provider(CurrencyProvider):
 
     def pricetag(self) -> str:
         return (
-            self.numerify(self.random_element(self.price_formats)) + "\N{no-break space}\N{cyrillic small letter er}."
+            self.numerify(self.random_element(self.price_formats)) + "\N{NO-BREAK SPACE}\N{CYRILLIC SMALL LETTER ER}."
         )
diff --git a/faker/providers/date_time/__init__.py b/faker/providers/date_time/__init__.py
index 69dfa5f9..f05538ed 100644
--- a/faker/providers/date_time/__init__.py
+++ b/faker/providers/date_time/__init__.py
@@ -1,18 +1,16 @@
 import platform
 import re
+import zoneinfo
 
 from calendar import timegm
 from datetime import MAXYEAR
 from datetime import date as dtdate
 from datetime import datetime
 from datetime import time as dttime
-from datetime import timedelta
+from datetime import timedelta, timezone
 from datetime import tzinfo as TzInfo
 from typing import Any, Callable, Dict, Iterator, Optional, Tuple, Union
 
-from dateutil import relativedelta
-from dateutil.tz import gettz, tzlocal, tzutc
-
 from faker.typing import Country, DateParseType
 
 from .. import BaseProvider, ElementsType
@@ -20,16 +18,26 @@ from .. import BaseProvider, ElementsType
 localized = True
 
 
+def _get_local_timezone():
+    datetime.now().astimezone().tzinfo
+
+
+def _get_next_month_start(dt: Union[dtdate, datetime]) -> Union[dtdate, datetime]:
+    if dt.month == 12:
+        return dt.replace(year=dt.year + 1, month=1)
+    return dt.replace(month=dt.month + 1)
+
+
 def datetime_to_timestamp(dt: Union[dtdate, datetime]) -> int:
     if isinstance(dt, datetime) and getattr(dt, "tzinfo", None) is not None:
-        dt = dt.astimezone(tzutc())
+        dt = dt.astimezone(timezone.utc)
     return timegm(dt.timetuple())
 
 
 def timestamp_to_datetime(timestamp: Union[int, float], tzinfo: Optional[TzInfo]) -> datetime:
     if tzinfo is None:
-        pick = convert_timestamp_to_datetime(timestamp, tzlocal())
-        return pick.astimezone(tzutc()).replace(tzinfo=None)
+        pick = convert_timestamp_to_datetime(timestamp, _get_local_timezone())
+        return pick.astimezone(timezone.utc).replace(tzinfo=None)
     return convert_timestamp_to_datetime(timestamp, tzinfo)
 
 
@@ -2100,7 +2108,7 @@ class Provider(BaseProvider):
         if tzinfo is None:
             return datetime(1970, 1, 1, tzinfo=tzinfo) + timedelta(seconds=ts)
         else:
-            return (datetime(1970, 1, 1, tzinfo=tzutc()) + timedelta(seconds=ts)).astimezone(tzinfo)
+            return (datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(seconds=ts)).astimezone(tzinfo)
 
     def date_between(self, start_date: DateParseType = "-30y", end_date: DateParseType = "today") -> dtdate:
         """
@@ -2202,9 +2210,9 @@ class Provider(BaseProvider):
         timestamp = self._rand_seconds(datetime_start_, datetime_end_)
         try:
             if tzinfo is None:
-                pick = convert_timestamp_to_datetime(timestamp, tzlocal())
+                pick = convert_timestamp_to_datetime(timestamp, _get_local_timezone())
                 try:
-                    pick = pick.astimezone(tzutc()).replace(tzinfo=None)
+                    pick = pick.astimezone(timezone.utc).replace(tzinfo=None)
                 except OSError:
                     pass
             else:
@@ -2336,8 +2344,8 @@ class Provider(BaseProvider):
         """
         now = datetime.now(tzinfo)
         this_month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
+        next_month_start = _get_next_month_start(this_month_start)
 
-        next_month_start = this_month_start + relativedelta.relativedelta(months=1)
         if before_now and after_now:
             return self.date_time_between_dates(this_month_start, next_month_start, tzinfo)
         elif not before_now and after_now:
@@ -2428,8 +2436,8 @@ class Provider(BaseProvider):
         """
         today = dtdate.today()
         this_month_start = today.replace(day=1)
+        next_month_start = _get_next_month_start(this_month_start)
 
-        next_month_start = this_month_start + relativedelta.relativedelta(months=1)
         if before_today and after_today:
             return self.date_between_dates(this_month_start, next_month_start)
         elif not before_today and after_today:
@@ -2540,7 +2548,7 @@ class Provider(BaseProvider):
 
         :sample:
         """
-        return gettz(self.timezone(*args, **kwargs))  # type: ignore
+        return zoneinfo.ZoneInfo(self.timezone(*args, **kwargs))  # type: ignore
 
     def date_of_birth(
         self,
diff --git a/faker/providers/misc/__init__.py b/faker/providers/misc/__init__.py
index 8a83ad6f..69612b83 100644
--- a/faker/providers/misc/__init__.py
+++ b/faker/providers/misc/__init__.py
@@ -9,9 +9,7 @@ import tarfile
 import uuid
 import zipfile
 
-from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Set, Tuple, Type, Union
-
-from typing_extensions import TypeAlias
+from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Set, Tuple, Type, Union, overload
 
 from faker.exceptions import UnsupportedFeature
 
@@ -23,8 +21,8 @@ localized = True
 csv.register_dialect("faker-csv", csv.excel, quoting=csv.QUOTE_ALL)  # type: ignore
 
 
-ColumnSpec: TypeAlias = Union[Tuple[int, str], Tuple[int, str, Dict[str, Any]]]
-DataColumns: TypeAlias = List[ColumnSpec]
+ColumnSpec = Union[Tuple[int, str], Tuple[int, str, Dict[str, Any]]]
+DataColumns = List[ColumnSpec]
 
 
 class Provider(BaseProvider):
@@ -104,6 +102,18 @@ class Provider(BaseProvider):
             return res.digest()
         return res.hexdigest()
 
+    @overload
+    def uuid4(self) -> str: ...
+
+    @overload
+    def uuid4(self, cast_to: None) -> uuid.UUID: ...
+
+    @overload
+    def uuid4(self, cast_to: Callable[[uuid.UUID], str]) -> str: ...
+
+    @overload
+    def uuid4(self, cast_to: Callable[[uuid.UUID], bytes]) -> bytes: ...
+
     def uuid4(
         self,
         cast_to: Optional[Union[Callable[[uuid.UUID], str], Callable[[uuid.UUID], bytes]]] = str,
diff --git a/faker/providers/ssn/es_MX/__init__.py b/faker/providers/ssn/es_MX/__init__.py
index 02e64816..c45d5059 100644
--- a/faker/providers/ssn/es_MX/__init__.py
+++ b/faker/providers/ssn/es_MX/__init__.py
@@ -8,16 +8,10 @@ Registry Code (CURP) and Federal Taxpayer Registry ID (RFC).
 import random
 import string
 
-from typing import Optional
+from typing import Literal, Optional
 
 from .. import Provider as BaseProvider
 
-try:
-    from typing import Literal  # type: ignore
-except ImportError:
-    from typing_extensions import Literal  # type: ignore
-
-
 ALPHABET = string.ascii_uppercase
 ALPHANUMERIC = string.digits + ALPHABET
 VOWELS = "AEIOU"
diff --git a/faker/proxy.pyi b/faker/proxy.pyi
index a20a1b4b..dd303479 100644
--- a/faker/proxy.pyi
+++ b/faker/proxy.pyi
@@ -23,6 +23,7 @@ from typing import (
     Type,
     TypeVar,
     Union,
+    overload,
 )
 from uuid import UUID
 
@@ -2335,9 +2336,50 @@ class Faker:
         """
         ...
 
-    def uuid4(
-        self, cast_to: Union[Callable[[UUID], str], Callable[[UUID], bytes], None] = ...
-    ) -> Union[bytes, str, UUID]:
+    @overload
+    def uuid4(self) -> str:
+        """
+        Generate a random UUID4 object and cast it to another type if specified using a callable ``cast_to``.
+
+        By default, ``cast_to`` is set to ``str``.
+
+        May be called with ``cast_to=None`` to return a full-fledged ``UUID``.
+
+        :sample:
+        :sample: cast_to=None
+        """
+        ...
+
+    @overload
+    def uuid4(self, cast_to: None) -> UUID:
+        """
+        Generate a random UUID4 object and cast it to another type if specified using a callable ``cast_to``.
+
+        By default, ``cast_to`` is set to ``str``.
+
+        May be called with ``cast_to=None`` to return a full-fledged ``UUID``.
+
+        :sample:
+        :sample: cast_to=None
+        """
+        ...
+
+    @overload
+    def uuid4(self, cast_to: Callable[[UUID], str]) -> str:
+        """
+        Generate a random UUID4 object and cast it to another type if specified using a callable ``cast_to``.
+
+        By default, ``cast_to`` is set to ``str``.
+
+        May be called with ``cast_to=None`` to return a full-fledged ``UUID``.
+
+        :sample:
+        :sample: cast_to=None
+        """
+        ...
+
+    @overload
+    def uuid4(self, cast_to: Callable[[UUID], bytes]) -> bytes:
         """
         Generate a random UUID4 object and cast it to another type if specified using a callable ``cast_to``.
 
diff --git a/faker/typing.py b/faker/typing.py
index 7d020943..87f4fdc7 100644
--- a/faker/typing.py
+++ b/faker/typing.py
@@ -1,20 +1,8 @@
 import dataclasses
-import sys
 
+from collections import OrderedDict as OrderedDictType
 from datetime import date, datetime, timedelta
-from typing import Sequence, Union
-
-try:
-    from typing import List, Literal, TypeVar  # type: ignore
-except ImportError:
-    from typing import List
-
-    from typing_extensions import Literal, TypeVar  # type: ignore
-
-if sys.version_info >= (3, 9):
-    from collections import OrderedDict as OrderedDictType
-else:
-    from typing import OrderedDict as OrderedDictType
+from typing import List, Literal, Sequence, TypeVar, Union
 
 
 class CreditCard:
diff --git a/generate_stubs.py b/generate_stubs.py
index 8a3c8538..cbe64c2c 100644
--- a/generate_stubs.py
+++ b/generate_stubs.py
@@ -3,7 +3,7 @@ import pathlib
 import re
 
 from collections import defaultdict
-from typing import Any, Dict, List, Optional, Set, Tuple, Type, get_type_hints
+from typing import Any, Dict, List, Optional, Set, Tuple, Type, get_overloads, get_type_hints
 
 import faker.proxy
 
@@ -18,7 +18,7 @@ MODULES_TO_FULLY_QUALIFY = ["datetime"]
 imports: Dict[str, Optional[Set[str]]] = defaultdict(lambda: None)
 imports["collections"] = {"OrderedDict"}
 imports["json"] = {"encoder"}
-imports["typing"] = {"Callable", "Collection", "TypeVar"}
+imports["typing"] = {"Callable", "Collection", "TypeVar", "overload"}
 imports["uuid"] = {"UUID"}
 imports["enum"] = {"Enum"}
 imports["faker.typing"] = {"*"}
@@ -90,6 +90,86 @@ def get_member_functions_and_variables(cls: object, include_mangled: bool = Fals
     return UniqueMemberFunctionsAndVariables(cls, funcs, vars)
 
 
+def get_signatures_for_func(func_value, func_name, locale, is_overload: bool = False, comment: Optional[str] = None):
+    """Return the signatures for the given function, recursing as necessary to handle overloads."""
+    signatures = []
+
+    if comment is None:
+        comment = inspect.getdoc(func_value)
+
+    if not is_overload:
+        try:
+            overloads = get_overloads(func_value)
+        except Exception as e:
+            raise TypeError(f"Can't parse overloads for {func_name}{sig}.") from e
+
+        if overloads:
+            for overload in overloads:
+                signatures.extend(
+                    get_signatures_for_func(overload, func_name, locale, is_overload=True, comment=comment)
+                )
+            return signatures
+
+    sig = inspect.signature(func_value)
+    try:
+        hints = get_type_hints(func_value)
+    except Exception as e:
+        raise TypeError(f"Can't parse {func_name}{sig}.") from e
+    ret_annot_module = getattr(sig.return_annotation, "__module__", None)
+    if sig.return_annotation not in [
+        None,
+        inspect.Signature.empty,
+        prov_cls.__name__,
+    ] and ret_annot_module not in [
+        None,
+        *BUILTIN_MODULES_TO_IGNORE,
+    ]:
+        module, member = get_module_and_member_to_import(sig.return_annotation, locale)
+        if module not in [None, "types"]:
+            if imports[module] is None:
+                imports[module] = set() if member is None else {member}
+            elif member is not None:
+                imports[module].add(member)
+
+    new_parms = []
+    for key, parm_val in sig.parameters.items():
+        new_parm = parm_val
+        annotation = hints.get(key, new_parm.annotation)
+        if parm_val.default is not inspect.Parameter.empty:
+            new_parm = parm_val.replace(default=...)
+        if annotation is not inspect.Parameter.empty and annotation.__module__ not in BUILTIN_MODULES_TO_IGNORE:
+            module, member = get_module_and_member_to_import(annotation, locale)
+            if module not in [None, "types"]:
+                if imports[module] is None:
+                    imports[module] = set() if member is None else {member}
+                elif member is not None:
+                    imports[module].add(member)
+        new_parms.append(new_parm)
+
+    sig = sig.replace(parameters=new_parms)
+    sig_str = str(sig).replace("Ellipsis", "...").replace("NoneType", "None").replace("~", "")
+    for module in imports.keys():
+        if module in MODULES_TO_FULLY_QUALIFY:
+            continue
+        sig_str = sig_str.replace(f"{module}.", "")
+
+    decorator = ""
+    if is_overload:
+        decorator += "@overload\n"
+    if list(sig.parameters)[0] == "cls":
+        decorator += "@classmethod\n"
+    elif list(sig.parameters)[0] != "self":
+        decorator += "@staticmethod\n"
+    signatures.append(
+        (
+            f"{decorator}def {func_name}{sig_str}: ...",
+            None if comment == "" else comment,
+            False,
+        )
+    )
+    return signatures
+
+
 classes_and_locales_to_use_for_stub: List[Tuple[object, str]] = []
 for locale in AVAILABLE_LOCALES:
     for provider in PROVIDERS:
@@ -115,54 +195,7 @@ signatures_with_comments: List[Tuple[str, str, bool]] = []
 
 for mbr_funcs_and_vars, locale in all_members:
     for func_name, func_value in mbr_funcs_and_vars.funcs.items():
-        sig = inspect.signature(func_value)
-        try:
-            hints = get_type_hints(func_value)
-        except Exception as e:
-            raise TypeError(f"Can't parse {func_name}{sig}.") from e
-        ret_annot_module = getattr(sig.return_annotation, "__module__", None)
-        if sig.return_annotation not in [None, inspect.Signature.empty, prov_cls.__name__] and ret_annot_module not in [
-            None,
-            *BUILTIN_MODULES_TO_IGNORE,
-        ]:
-            module, member = get_module_and_member_to_import(sig.return_annotation, locale)
-            if module is not None:
-                if imports[module] is None:
-                    imports[module] = set() if member is None else {member}
-                elif member is not None:
-                    imports[module].add(member)
-
-        new_parms = []
-        for key, parm_val in sig.parameters.items():
-            new_parm = parm_val
-            annotation = hints.get(key, new_parm.annotation)
-            if parm_val.default is not inspect.Parameter.empty:
-                new_parm = parm_val.replace(default=...)
-            if annotation is not inspect.Parameter.empty and annotation.__module__ not in BUILTIN_MODULES_TO_IGNORE:
-                module, member = get_module_and_member_to_import(annotation, locale)
-                if module is not None:
-                    if imports[module] is None:
-                        imports[module] = set() if member is None else {member}
-                    elif member is not None:
-                        imports[module].add(member)
-            new_parms.append(new_parm)
-
-        sig = sig.replace(parameters=new_parms)
-        sig_str = str(sig).replace("Ellipsis", "...").replace("NoneType", "None").replace("~", "")
-        for module in imports.keys():
-            if module in MODULES_TO_FULLY_QUALIFY:
-                continue
-            sig_str = sig_str.replace(f"{module}.", "")
-
-        decorator = ""
-        if list(sig.parameters)[0] == "cls":
-            decorator = "@classmethod\n"
-        elif list(sig.parameters)[0] != "self":
-            decorator = "@staticmethod\n"
-        comment = inspect.getdoc(func_value)
-        signatures_with_comments.append(
-            (f"{decorator}def {func_name}{sig_str}: ...", None if comment == "" else comment, False)
-        )
+        signatures_with_comments.extend(get_signatures_for_func(func_value, func_name, locale))
 
 signatures_with_comments_as_str = []
 for sig, comment, is_preceding_comment in signatures_with_comments:
diff --git a/setup.py b/setup.py
index 97b13c1d..830d2aa5 100644
--- a/setup.py
+++ b/setup.py
@@ -38,7 +38,6 @@ setup(
         "Development Status :: 5 - Production/Stable",
         "Environment :: Console",
         "Intended Audience :: Developers",
-        "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
@@ -68,9 +67,6 @@ setup(
     },
     platforms=["any"],
     zip_safe=zip_safe,
-    python_requires=">=3.8",
-    install_requires=[
-        "python-dateutil>=2.4",
-        "typing_extensions",
-    ],
+    install_requires=["tzdata"],
+    python_requires=">=3.9",
 )
diff --git a/tox.ini b/tox.ini
index 8e435672..550cd35d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist=py{38,39,310,311,312,313,py3},alpine,flake8,checkmanifest,isort,mypy,doc8
+envlist=py{39,310,311,312,313,py3},alpine,flake8,checkmanifest,isort,mypy,doc8
 skip_missing_interpreters = true
 
 [testenv]
@@ -41,7 +41,7 @@ commands =
 [testenv:mypy]
 basepython = python
 deps =
-    mypy==0.910
+    mypy==1.15.0
 commands =
     mypy --install-types --non-interactive --config mypy.ini faker
 


Test patch:
diff --git a/tests/providers/test_company.py b/tests/providers/test_company.py
index c6285eae..c193df91 100644
--- a/tests/providers/test_company.py
+++ b/tests/providers/test_company.py
@@ -24,7 +24,7 @@ from faker.providers.company.pl_PL import company_vat_checksum, local_regon_chec
 from faker.providers.company.pt_BR import company_id_checksum
 from faker.providers.company.ro_RO import Provider as RoRoCompanyProvider
 from faker.providers.company.ru_RU import Provider as RuRuCompanyProvider
-from faker.providers.company.ru_RU import calculate_checksum
+from faker.providers.company.ru_RU import calculate_checksum, calculate_snils_checksum
 from faker.providers.company.th_TH import Provider as ThThCompanyProvider
 from faker.providers.company.tr_TR import Provider as TrTrCompanyProvider
 from faker.providers.company.vi_VN import Provider as ViVnCompanyProvider
@@ -396,6 +396,12 @@ class TestRuRu:
             assert isinstance(bs, str)
             assert bs_words[0] in RuRuCompanyProvider.bsWords[0]
 
+    def test_snils(self, faker, num_samples):
+        for _ in range(num_samples):
+            snils = faker.snils()
+            assert len(snils) == 11
+            assert snils[-2:] == calculate_snils_checksum(snils[:10])
+
 
 class TestItIt:
     """Test it_IT company provider methods"""
diff --git a/tests/providers/test_date_time.py b/tests/providers/test_date_time.py
index 8c8b8f3c..f0862c5c 100644
--- a/tests/providers/test_date_time.py
+++ b/tests/providers/test_date_time.py
@@ -5,6 +5,7 @@ import re
 import sys
 import time
 import unittest
+import zoneinfo
 
 from datetime import date, datetime
 from datetime import time as datetime_time
@@ -167,10 +168,8 @@ class TestDateTime(unittest.TestCase):
         assert today == today_back
 
     def test_pytimezone(self):
-        import dateutil
-
         pytz = self.fake.pytimezone()
-        assert isinstance(pytz, dateutil.tz.tz.tzfile)
+        assert isinstance(pytz, zoneinfo.ZoneInfo)
 
     def test_pytimezone_usable(self):
         pytz = self.fake.pytimezone()