Skip to content

Commit

Permalink
Add Typing [Fixed #1489!] (#1536)
Browse files Browse the repository at this point in the history
* Create nl_NL

* Delete nl_NL

* Add date_time provider for nl_NL

* Added date_time nl_NL test case

* Added nl_NL provider

* Added nl_NL test case

* Added nl_NL automotive provider

* Added tests for nl_NL automotive provider

* flake8 fixes

* flake8 fixes in test_automotive

* import order fixes

* Added type annotations

* mypy fixes

* isort fixes

* minor flake8 fixes

* Fixed circular import error in generator.py

* Circular import error fix

* Typing fixes for OrderedDict

* OrderedDict Python 3.6 fix

* OrderedDictType fix in proxy

* Added mypy test to tox.ini

* Update tox.ini

* Update tox.ini

* PyPy is incompatible with mypy

* Update tox.ini

* Removed `automotive` provider `nl_NL`

* Typo

* Many typing fixes

- New mypy version in tox.ini
- Replaced Any with TypeVar('T')
- Added List[str] pr Tuple[str, ...] in many places (e.g. base classes, so that inherited classes would not throw errors)
- Fully fixed `providers.color.color.py`

* Added ElementsType (input for `BaseProvider.random_element()`)

* First typing fixes in date_time

* Minor Typing fixes to date_time

* Typing fixes in `providers.company`

* Minor typing fixes in `providers.barcode`

* Minor typing fixes in `providers.address`

* Minor typing fixes in `providers.person`

* Update decorators.py

* Minor typing fixes in `providers.automotive`

* Import typing.Pattern in test files

* Added type: ignore[attr-defined] hotfix

* Added final missing Pattern imports

* Optional distrib, flake8 fix in test_user_agent

* Minor typing fixes

* Literal not supported before Python 3.8

* Bug fix in datetime.fromtimestamp

* Fixed broken tests

Except TestInternetProvider.test_ipv4_distribution_selection!

* Fixed test_ipv4_distribution_selection

* Update decimal in `geo.el_GR`

* Add mypy config and improve command for tox

* Fix last type errors

- remove OrderedDictType and use Dict instead
- remove type checking for Literal and use try catch instead
- add Any for *args and **kwargs arguments

* Updated MANIFEST.in to include mypy.ini

* Renamed typing.py to typing.pyi

* Some Python versions don't like .pyi

* Updated coding_style.rst

- Removed redundant TypeVar from Generator
- Ordered typing.py in alphabetical order

* Added mypy to .github/workflows/ci.yml

* Added dependency order to ci.yml

* Python 3.10 to "3.10" in ci.yml

* Unused ignores across versions

python/mypy#8823

* Show error codes

* Python 3.6 mypy fix

* Final changes

Co-authored-by: Nico Carl <nicocarl@protonmail.com>
  • Loading branch information
MarcelRobeer and nicarl committed Oct 19, 2021
1 parent 4f48a5a commit 9a382ed
Show file tree
Hide file tree
Showing 245 changed files with 2,276 additions and 2,097 deletions.
31 changes: 25 additions & 6 deletions .github/workflows/ci.yml
Expand Up @@ -29,14 +29,34 @@ jobs:
run: tox
env:
TOXENV: checkmanifest
- name: isort
- name: Import order checking with isort
run: tox
env:
TOXENV: isort

typing:
runs-on: ubuntu-latest
strategy:
matrix:
python: [3.6, 3.8, "3.10"]

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Install dependencies
run: |
python -m pip install tox
- name: Static type checking with mypy
run: tox
env:
TOXENV: mypy

test_ubuntu:
runs-on: ubuntu-latest
needs: [lint]
needs: [lint, typing]
strategy:
matrix:
python: [3.6, 3.7, 3.8, 3.9, "pypy3"]
Expand All @@ -62,7 +82,7 @@ jobs:

test_windows:
runs-on: windows-latest
needs: [lint]
needs: [lint, typing]
strategy:
matrix:
python: [3.6, 3.7, 3.8, 3.9]
Expand All @@ -89,7 +109,7 @@ jobs:

test_alpine:
runs-on: ubuntu-latest
needs: [lint]
needs: [lint, typing]

steps:
- uses: actions/checkout@v2
Expand All @@ -108,7 +128,7 @@ jobs:

test_32bit:
runs-on: ubuntu-latest
needs: [lint]
needs: [lint, typing]

steps:
- uses: actions/checkout@v2
Expand All @@ -125,7 +145,6 @@ jobs:
TOXENV: 32bit
TEST_32BIT: 1


finish:
needs: [test_ubuntu, test_windows, test_alpine, test_32bit]
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Expand Up @@ -5,6 +5,7 @@ include CHANGELOG.md
include RELEASE_PROCESS.rst
include VERSION
include CITATION.cff
include mypy.ini
recursive-include tests *.json
recursive-include tests *.py
recursive-exclude faker/sphinx *.py
Expand Down
16 changes: 15 additions & 1 deletion docs/coding_style.rst
Expand Up @@ -3,7 +3,7 @@ Coding Style

Lines length should not exceed 120 characters. Please use trailing commas.

Plese include `type hints`_ for every provider method you write.
Please include `type hints`_ for every provider method you write. An overview of generic types is included below.

You can find our complete flake8 configuration in the tox.ini_ file.

Expand All @@ -22,6 +22,20 @@ Name Lists

When you have long lists of names, please order them alphabetically. Keep the lines length as close as possible to 120 characters, without exceeding the limit.


Type Hints
----------
`typing.py` includes generic types that can be re-used throughout the codebase. Moreover, some type definitions are
included in other parts of the code. If you add a generic type, please specify its usage below:

| Type | Used for |
|------|----------|
| `providers.ElementsType` | When creating a variable in a `Provider` (e.g. for a specific locale), which is not defined in the superclass, `self.random_element()`, `self.random_elements()`, `self.random_choices()` and `self.random_sample()` assume this to be the input format. |
| `typing.DateParseType` | Input for various `faker.providers.date_time` functions that parse (relative) dates/times. |
| `typing.HueType` | Hue name, float value or integer range. |
| `typing.GenderType` | String variable that can only have values `'F'` (female) and `'M'` (male) |

.. _`tox.ini`: https://github.com/joke2k/faker/blob/master/tox.ini
.. _`pep 8`: https://python.org/dev/peps/pep-0008
.. _`pep 263`: https://python.org/dev/peps/pep-0263
Expand Down
2 changes: 1 addition & 1 deletion faker/__main__.py
@@ -1,3 +1,3 @@
if __name__ == "__main__":
from faker.cli import execute_from_command_line
from .cli import execute_from_command_line
execute_from_command_line()
45 changes: 27 additions & 18 deletions faker/cli.py
Expand Up @@ -4,22 +4,27 @@
import random
import sys

from io import TextIOWrapper
from pathlib import Path
from typing import Any, Dict, List, Optional, TextIO
from typing import Dict, List, Optional, TextIO, TypeVar, Union

from faker import VERSION, Faker, documentor, exceptions
from faker.config import AVAILABLE_LOCALES, DEFAULT_LOCALE, META_PROVIDERS_MODULES
from . import VERSION, Faker, documentor, exceptions
from .config import AVAILABLE_LOCALES, DEFAULT_LOCALE, META_PROVIDERS_MODULES
from .documentor import Documentor
from .providers import BaseProvider

__author__ = 'joke2k'

T = TypeVar('T')

def print_provider(doc,
provider: List[str],
formatters: Dict[str, Any],
excludes=None,
output: Optional[TextIO] = None) -> None:

output = output or sys.stdout
def print_provider(doc: Documentor,
provider: BaseProvider,
formatters: Dict[str, T],
excludes: Optional[List[str]] = None,
output: Optional[TextIO] = None) -> None:
if output is None:
output = sys.stdout
if excludes is None:
excludes = []

Expand Down Expand Up @@ -48,11 +53,16 @@ def print_provider(doc,
signature = separator = ' '


def print_doc(provider_or_field=None,
args=None, lang: str = DEFAULT_LOCALE, output=None, seed=None,
includes=None) -> None:
args = args or []
output = output or sys.stdout
def print_doc(provider_or_field: Optional[str] = None,
args: Optional[List[T]] = None,
lang: str = DEFAULT_LOCALE,
output: Optional[Union[TextIO, TextIOWrapper]] = None,
seed: Optional[float] = None,
includes: Optional[List[str]] = None) -> None:
if args is None:
args = []
if output is None:
output = sys.stdout
fake = Faker(locale=lang, includes=includes)
fake.seed_instance(seed)

Expand Down Expand Up @@ -86,7 +96,7 @@ def print_doc(provider_or_field=None,

else:
doc = documentor.Documentor(fake)
unsupported = []
unsupported: List[str] = []

while True:
try:
Expand All @@ -97,7 +107,6 @@ def print_doc(provider_or_field=None,
break

for provider, fakers in formatters:

print_provider(doc, provider, fakers, output=output)

for language in AVAILABLE_LOCALES:
Expand All @@ -117,7 +126,7 @@ def print_doc(provider_or_field=None,

class Command:

def __init__(self, argv=None) -> None:
def __init__(self, argv: Optional[str] = None) -> None:
self.argv = argv or sys.argv[:]
self.prog_name = Path(self.argv[0]).name

Expand Down Expand Up @@ -259,7 +268,7 @@ def execute(self) -> None:
break


def execute_from_command_line(argv=None) -> None:
def execute_from_command_line(argv: Optional[str] = None) -> None:
"""A simple method that runs a Command."""
if sys.stdout.encoding is None:
print('please set python env PYTHONIOENCODING=UTF-8, example: '
Expand Down
2 changes: 1 addition & 1 deletion faker/config.py
@@ -1,6 +1,6 @@
from importlib import import_module

from faker.utils.loading import find_available_locales, find_available_providers
from .utils.loading import find_available_locales, find_available_providers

DEFAULT_LOCALE = 'en_US'

Expand Down
31 changes: 21 additions & 10 deletions faker/documentor.py
@@ -1,24 +1,33 @@
import inspect
import warnings

from typing import Any, Dict, List, Optional, Tuple, Union

from .generator import Generator
from .providers import BaseProvider
from .proxy import Faker


class Documentor:

def __init__(self, generator):
def __init__(self, generator: Union[Generator, Faker]) -> None:
"""
:param generator: a localized Generator with providers filled,
for which to write the documentation
:type generator: faker.Generator()
"""
self.generator = generator
self.max_name_len = 0
self.already_generated = []
self.max_name_len: int = 0
self.already_generated: List[str] = []

def get_formatters(self, locale=None, excludes=None, **kwargs):
def get_formatters(self,
locale: Optional[str] = None,
excludes: Optional[List[str]] = None,
**kwargs: Any) -> List[Tuple[BaseProvider, Dict[str, str]]]:
self.max_name_len = 0
self.already_generated = [] if excludes is None else excludes[:]
formatters = []
providers = self.generator.get_providers()
providers: List[BaseProvider] = self.generator.get_providers()
for provider in providers[::-1]: # reverse
if locale and provider.__lang__ != locale:
continue
Expand All @@ -27,9 +36,11 @@ def get_formatters(self, locale=None, excludes=None, **kwargs):
)
return formatters

def get_provider_formatters(self, provider, prefix='fake.',
with_args=True, with_defaults=True):

def get_provider_formatters(self,
provider: BaseProvider,
prefix: str = 'fake.',
with_args: bool = True,
with_defaults: bool = True) -> Dict[str, str]:
formatters = {}

for name, method in inspect.getmembers(provider, inspect.ismethod):
Expand All @@ -38,7 +49,7 @@ def get_provider_formatters(self, provider, prefix='fake.',
continue

arguments = []
faker_args = []
faker_args: List[str] = []
faker_kwargs = {}

if name == 'binary':
Expand Down Expand Up @@ -98,5 +109,5 @@ def get_provider_formatters(self, provider, prefix='fake.',
return formatters

@staticmethod
def get_provider_name(provider_class):
def get_provider_name(provider_class: BaseProvider) -> str:
return provider_class.__provider__
3 changes: 2 additions & 1 deletion faker/exceptions.py
Expand Up @@ -10,6 +10,7 @@ class UniquenessException(BaseFakerException):

class UnsupportedFeature(BaseFakerException):
"""The requested feature is not available on this system."""
def __init__(self, msg, name):

def __init__(self, msg: str, name: str) -> None:
self.name = name
super().__init__(msg)
25 changes: 13 additions & 12 deletions faker/factory.py
Expand Up @@ -3,10 +3,11 @@
import sys

from importlib import import_module
from typing import Any, List, Optional, Tuple

from faker.config import AVAILABLE_LOCALES, DEFAULT_LOCALE, PROVIDERS
from faker.generator import Generator
from faker.utils.loading import list_module
from .config import AVAILABLE_LOCALES, DEFAULT_LOCALE, PROVIDERS
from .generator import Generator
from .utils.loading import list_module

logger = logging.getLogger(__name__)

Expand All @@ -23,14 +24,14 @@ class Factory:
@classmethod
def create(
cls,
locale=None,
providers=None,
generator=None,
includes=None,
locale: Optional[str] = None,
providers: Optional[List[str]] = None,
generator: Generator = None,
includes: Optional[List[str]] = None,
# Should we use weightings (more realistic) or weight every element equally (faster)?
# By default, use weightings for backwards compatibility & realism
use_weighting=True,
**config):
use_weighting: bool = True,
**config: Any) -> Generator:
if includes is None:
includes = []

Expand Down Expand Up @@ -63,7 +64,7 @@ def create(
return faker

@classmethod
def _get_provider_class(cls, provider, locale=''):
def _get_provider_class(cls, provider: str, locale: Optional[str] = '') -> Tuple[Any, Optional[str]]:

provider_class = cls._find_provider_class(provider, locale)

Expand All @@ -85,7 +86,7 @@ def _get_provider_class(cls, provider, locale=''):
raise ValueError(msg)

@classmethod
def _find_provider_class(cls, provider_path, locale=None):
def _find_provider_class(cls, provider_path: str, locale: Optional[str] = None) -> Any:

provider_module = import_module(provider_path)

Expand Down Expand Up @@ -117,4 +118,4 @@ def _find_provider_class(cls, provider_path, locale=None):
if locale is not None:
provider_module = import_module(provider_path)

return provider_module.Provider
return provider_module.Provider # type: ignore

0 comments on commit 9a382ed

Please sign in to comment.