Repository: joke2k/faker

Issue:
Sometimes generated person names end with a space (in it_IT)
* Faker version: `Faker==4.14.0`
* OS: `Linux`

Sometimes the names it generates in Italian end with a space (e.g. `'Sig.ra Loretta Pagliaro '`), I suspect it's due to the fact that there are no suffixes defined.

Maybe the regression is introduced by https://github.com/joke2k/faker/pull/1030 (just a claim, it's the first time I use this library):

https://github.com/joke2k/faker/blob/299f6b6bbc18fc576642069ccfbf279160b7be54/faker/providers/person/it_IT/__init__.py#L13-L14

AFAIK Italian names do not have suffixes, and for sure no suffix is defined in that provider.

### Steps to reproduce

```python
>>> from faker import Faker
>>> fake = Faker("it_IT")
>>> for _ in range(1000):
...     name = fake.name()
...     if name.endswith(" "):
...         print(f"Invalid name: '{name}'")
...         break
... else:
...     print("Probably ok")
...
Invalid name: 'Sig. Goffredo Morellato '
```

### Expected behavior

All the generated names should not start or end with a space.

### Actual behavior

Sometimes the generated names end with a space.

Discussion:

Patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 19c07de5..1b9927e0 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 4.14.0
+current_version = 4.14.1
 files = VERSION faker/__init__.py docs/conf.py
 commit = True
 tag = True
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 0ecc0f22..1d9210c5 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,11 @@
 Changelog
 =========
 
+`4.14.1 - 2020-11-04 <https://github.com/joke2k/faker/compare/v4.14.0...v4.14.1>`_
+----------------------------------------------------------------------------------
+
+* Add relative frequencies for japanese last names. Thanks @TianyiShi2001.
+
 `4.14.0 - 2020-10-13 <https://github.com/joke2k/faker/compare/v4.13.0...v4.14.0>`_
 ----------------------------------------------------------------------------------
 
diff --git a/VERSION b/VERSION
index c412a4e2..d2b9909a 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.14.0
+4.14.1
diff --git a/docs/conf.py b/docs/conf.py
index 199fc84b..3dbec411 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = '2014, Daniele Faraglia'
 # built documents.
 #
 # The short X.Y version.
-version = '4.14.0'
+version = '4.14.1'
 # The full version, including alpha/beta/rc tags.
-release = '4.14.0'
+release = '4.14.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 85e9f7b4..4695f72a 100644
--- a/faker/__init__.py
+++ b/faker/__init__.py
@@ -2,4 +2,4 @@ from faker.factory import Factory  # noqa F401
 from faker.generator import Generator  # noqa F401
 from faker.proxy import Faker  # noqa F401
 
-VERSION = '4.14.0'
+VERSION = '4.14.1'
diff --git a/faker/providers/address/hy_AM/__init__.py b/faker/providers/address/hy_AM/__init__.py
index 988a9b4f..6ae62229 100644
--- a/faker/providers/address/hy_AM/__init__.py
+++ b/faker/providers/address/hy_AM/__init__.py
@@ -3,6 +3,7 @@ from .. import Provider as AddressProvider
 
 class Provider(AddressProvider):
 
+    city_formats = ('{{first_name}}',)
     city_prefixes = ('ք.',)
     city_suffixes = ('',)
     street_prefixes = ('փողոց', 'պողոտա')
diff --git a/faker/providers/company/pt_PT/__init__.py b/faker/providers/company/pt_PT/__init__.py
index 93ab7959..f7954929 100644
--- a/faker/providers/company/pt_PT/__init__.py
+++ b/faker/providers/company/pt_PT/__init__.py
@@ -9,9 +9,6 @@ class Provider(CompanyProvider):
         '{{last_name}}',
     )
 
-    catch_phrase_formats = (
-        '{{catch_phrase_noun}} {{catch_phrase_verb}} {{catch_phrase_attribute}}', )
-
     nouns = (
         'a segurança', 'o prazer', 'o conforto', 'a simplicidade', 'a certeza',
         'a arte', 'o poder', 'o direito', 'a possibilidade', 'a vantagem',
diff --git a/faker/providers/person/de_AT/__init__.py b/faker/providers/person/de_AT/__init__.py
index 009f1309..fc103da4 100644
--- a/faker/providers/person/de_AT/__init__.py
+++ b/faker/providers/person/de_AT/__init__.py
@@ -10,8 +10,6 @@ class Provider(PersonProvider):
         '{{first_name_male}} {{last_name}}',
         '{{first_name_male}} {{last_name}}-{{last_name}}',
         '{{prefix_male}} {{first_name_male}} {{last_name}}',
-        '{{first_name_male}} {{last_name}} {{suffix_male}}',
-        '{{prefix_male}} {{first_name_male}} {{last_name}} {{suffix_male}}',
     )
 
     formats_female = (
@@ -21,8 +19,6 @@ class Provider(PersonProvider):
         '{{first_name_female}} {{last_name}}',
         '{{first_name_female}} {{last_name}}-{{last_name}}',
         '{{prefix_female}} {{first_name_female}} {{last_name}}',
-        '{{first_name_female}} {{last_name}} {{suffix_female}}',
-        '{{prefix_female}} {{first_name_female}} {{last_name}} {{suffix_female}}',
     )
 
     formats = formats_male + formats_female
diff --git a/faker/providers/person/fa_IR/__init__.py b/faker/providers/person/fa_IR/__init__.py
index 830959b9..3eae8f37 100644
--- a/faker/providers/person/fa_IR/__init__.py
+++ b/faker/providers/person/fa_IR/__init__.py
@@ -9,8 +9,6 @@ class Provider(PersonProvider):
         '{{first_name_female}} {{last_name}}',
         '{{first_name_female}} {{last_name}}',
         '{{prefix_female}} {{first_name_female}} {{last_name}}',
-        '{{first_name_female}} {{last_name}} {{suffix}}',
-        '{{prefix_female}} {{first_name_female}} {{last_name}} {{suffix}}',
     )
 
     formats_male = (
@@ -20,8 +18,6 @@ class Provider(PersonProvider):
         '{{first_name_male}} {{last_name}}',
         '{{first_name_male}} {{last_name}}',
         '{{prefix_male}} {{first_name_male}} {{last_name}}',
-        '{{first_name_male}} {{last_name}} {{suffix}}',
-        '{{prefix_male}} {{first_name_male}} {{last_name}} {{suffix}}',
     )
 
     formats = formats_female + formats_male
diff --git a/faker/providers/person/it_IT/__init__.py b/faker/providers/person/it_IT/__init__.py
index ec743cc6..43c26d52 100644
--- a/faker/providers/person/it_IT/__init__.py
+++ b/faker/providers/person/it_IT/__init__.py
@@ -10,8 +10,6 @@ class Provider(PersonProvider):
         '{{first_name_male}} {{last_name}}',
         '{{first_name_male}} {{last_name}}-{{last_name}}',
         '{{prefix_male}} {{first_name_male}} {{last_name}}',
-        '{{first_name_male}} {{last_name}} {{suffix_male}}',
-        '{{prefix_male}} {{first_name_male}} {{last_name}} {{suffix_male}}',
     )
 
     formats_female = (
@@ -21,8 +19,6 @@ class Provider(PersonProvider):
         '{{first_name_female}} {{last_name}}',
         '{{first_name_female}} {{last_name}}-{{last_name}}',
         '{{prefix_female}} {{first_name_female}} {{last_name}}',
-        '{{first_name_female}} {{last_name}} {{suffix_female}}',
-        '{{prefix_female}} {{first_name_female}} {{last_name}} {{suffix_female}}',
     )
 
     formats = formats_male + formats_female
diff --git a/faker/providers/person/ja_JP/__init__.py b/faker/providers/person/ja_JP/__init__.py
index e19075da..28cd36ab 100644
--- a/faker/providers/person/ja_JP/__init__.py
+++ b/faker/providers/person/ja_JP/__init__.py
@@ -1,3 +1,5 @@
+from collections import OrderedDict
+
 from .. import Provider as PersonProvider
 
 
@@ -31,16 +33,59 @@ class Provider(PersonProvider):
 
     first_names = first_names_male + first_names_female
 
-    # link: https://dic.nicovideo.jp/a/日本の苗字(名字)の一覧
-    last_names = (
-        '青田', '青山', '石田', '井高', '伊藤', '井上', '宇野', '江古田',
-        '大垣', '加藤', '加納', '喜嶋', '木村', '桐山', '工藤', '小泉',
-        '小林', '近藤', '斉藤', '坂本', '佐々木', '佐藤', '笹田', '鈴木',
-        '杉山', '高橋', '田中', '田辺', '津田', '中島', '中村', '渚',
-        '中津川', '西之園', '野村', '原田', '浜田', '廣川', '藤本', '松本',
-        '三宅', '宮沢', '村山', '山岸', '山口', '山田', '山本', '吉田',
-        '吉本', '若松', '渡辺',
-    )
+    # based on: https://www2.nipponsoft.co.jp/bldoko/index.asp
+    last_names = OrderedDict((
+        ("佐藤", 366803),
+        ("鈴木", 321135),
+        ("高橋", 266782),
+        ("田中", 245821),
+        ("伊藤", 203357),
+        ("渡辺", 200504),
+        ("山本", 200134),
+        ("中村", 195219),
+        ("小林", 191819),
+        ("加藤", 160283),
+        ("吉田", 154461),
+        ("山田", 151675),
+        ("佐々木", 135927),
+        ("山口", 119501),
+        ("松本", 116490),
+        ("井上", 111287),
+        ("木村", 107446),
+        ("林", 101826),
+        ("斎藤", 101774),
+        ("清水", 97826),
+        ("山崎", 90781),
+        ("阿部", 86833),
+        ("森", 86507),
+        ("池田", 84860),
+        ("橋本", 82836),
+        ("山下", 80588),
+        ("石川", 77471),
+        ("中島", 74106),
+        ("前田", 72930),
+        ("藤田", 72375),
+        ("後藤", 71629),
+        ("小川", 71179),
+        ("岡田", 70347),
+        ("長谷川", 69201),
+        ("村上", 68606),
+        ("近藤", 68297),
+        ("石井", 67079),
+        ("遠藤", 62620),
+        ("斉藤", 62540),
+        ("坂本", 62308),
+        ("青木", 59516),
+        ("藤井", 59204),
+        ("西村", 58821),
+        ("福田", 58714),
+        ("太田", 58439),
+        ("三浦", 58006),
+        ("藤原", 57742),
+        ("松田", 55883),
+        ("岡本", 55539),
+        ("中川", 55221),
+    ))
 
     kana_formats = (
         '{{last_kana_name}} {{first_kana_name_female}}',


Test patch:
diff --git a/tests/test_providers_formats.py b/tests/test_providers_formats.py
new file mode 100644
index 00000000..e4ebd4c2
--- /dev/null
+++ b/tests/test_providers_formats.py
@@ -0,0 +1,65 @@
+import re
+
+import pytest
+
+from faker import Factory
+from faker.config import AVAILABLE_LOCALES, PROVIDERS
+
+locales = AVAILABLE_LOCALES
+
+# searches {{group}} and capture the _group_
+find_group = re.compile(r"\{\{(\w+)\}\}")
+
+
+@pytest.mark.parametrize("locale", locales)
+def test_no_invalid_formats(locale):
+    """
+    For each locale, for each provider, search all the definitions of "formats"
+    and make sure that all the providers in there (e.g. {{group}}) are valid
+    and do not emit empty strings. Empty strings are allowed only if the group
+    is not surrounded by spaces. This is a quick way to make sure that no
+    string is generated with "double spaces", starting spaces or ending spaces.
+    """
+    faker = Factory.create(locale)
+    errors = []
+
+    for provider in PROVIDERS:
+        if provider == "faker.providers":
+            continue
+        prov_cls, lang = Factory._get_provider_class(provider, locale)
+        assert lang == locale
+
+        attributes = set(dir(prov_cls))
+
+        for attribute in attributes:
+            # consider only the format attributes
+            if not attribute.endswith("formats"):
+                continue
+            formats = getattr(prov_cls, attribute)
+            # may be a function or some other bizarre types
+            if not isinstance(formats, (list, tuple)):
+                continue
+            for format in formats:
+                # search all the {{groups}} in the format
+                for match in find_group.finditer(format):
+                    group = match.group(1)
+                    try:
+                        attr = faker.format(group)
+                    except AttributeError as e:
+                        errors.append(str(e))
+                        continue
+                    # touching = True if the group is touching sometime on at
+                    # least one side, i.e. it's not surrounded by spaces
+                    touching = False
+                    if match.start() != 0 and format[match.start() - 1] != " ":
+                        touching = True
+                    if match.end() != len(format) and format[match.end()] != " ":
+                        touching = True
+
+                    if not attr and not touching:
+                        errors.append(
+                            "Attribute {{%s}} provided an invalid value in format '%s' from %s.%s.%s"
+                            % (group, format, provider, locale, attribute),
+                        )
+    # group errors reporting all the ones from the same locale
+    assert not errors, "Errors:\n - " + "\n - ".join(errors)