human-input-validator 0.1.0__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: human-input-validator
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Small validators for common human-entered data.
5
5
  Author: Snorlena
6
6
  License-Expression: MIT
@@ -41,7 +41,8 @@ invalid input.
41
41
 
42
42
  - `email(value)` — returns a trimmed, lower-cased email address.
43
43
  - `country(value)` — returns the official ISO 3166-1 country name, given a
44
- name or code.
44
+ name or code. Also accepts localized country names (e.g. "Deutschland",
45
+ "España") via pycountry's bundled translations.
45
46
  - `username(value, max_length=12)` — returns a lower-cased username, checking
46
47
  length and allowed characters.
47
48
  - `phonenumber(value, pattern)` — checks a phone number against one or more
@@ -31,7 +31,8 @@ invalid input.
31
31
 
32
32
  - `email(value)` — returns a trimmed, lower-cased email address.
33
33
  - `country(value)` — returns the official ISO 3166-1 country name, given a
34
- name or code.
34
+ name or code. Also accepts localized country names (e.g. "Deutschland",
35
+ "España") via pycountry's bundled translations.
35
36
  - `username(value, max_length=12)` — returns a lower-cased username, checking
36
37
  length and allowed characters.
37
38
  - `phonenumber(value, pattern)` — checks a phone number against one or more
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "human-input-validator"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Small validators for common human-entered data."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -5,6 +5,7 @@ Each function returns a normalized value or raises :class:`ValidationError`.
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import difflib
8
9
  import functools
9
10
  import gettext
10
11
  import os
@@ -31,7 +32,12 @@ def _localized_country_names() -> dict[str, str]:
31
32
  except FileNotFoundError:
32
33
  continue
33
34
  for entry in pycountry.countries:
34
- for candidate in filter(None, (entry.name, getattr(entry, "official_name", None))):
35
+ names = (
36
+ entry.name,
37
+ getattr(entry, "official_name", None),
38
+ getattr(entry, "common_name", None),
39
+ )
40
+ for candidate in filter(None, names):
35
41
  translated = translation.gettext(candidate).strip().lower()
36
42
  index.setdefault(translated, entry.alpha_2)
37
43
  return index
@@ -45,22 +51,40 @@ def email(value: str) -> str:
45
51
  return normalized
46
52
 
47
53
 
54
+ _FUZZY_MATCH_CUTOFF = 0.8
55
+
56
+
57
+ def _display_name(entry: object) -> str:
58
+ """Prefer the short common name (e.g. 'Tanzania') over the formal ISO name."""
59
+ common_name: str | None = getattr(entry, "common_name", None)
60
+ name: str = getattr(entry, "name")
61
+ return common_name or name
62
+
63
+
48
64
  def country(value: str) -> str:
49
- """Return the official name of an ISO 3166-1 country, given its name or code in any supported language."""
65
+ """Return the common name of an ISO 3166-1 country, given its name or code in any supported language."""
50
66
  normalized = value.strip()
51
67
  try:
52
68
  result = pycountry.countries.lookup(normalized)
53
- return result.name
69
+ return _display_name(result)
54
70
  except LookupError:
55
71
  pass
56
72
 
57
- alpha_2 = _localized_country_names().get(normalized.lower())
73
+ index = _localized_country_names()
74
+ lowered = normalized.lower()
75
+ alpha_2 = index.get(lowered)
76
+ if alpha_2 is None:
77
+ # Fall back to fuzzy matching for close spelling variants (e.g. hyphenation
78
+ # or transliteration differences) not covered by the exact translation.
79
+ close_matches = difflib.get_close_matches(lowered, index.keys(), n=1, cutoff=_FUZZY_MATCH_CUTOFF)
80
+ if close_matches:
81
+ alpha_2 = index[close_matches[0]]
58
82
  if alpha_2 is None:
59
83
  raise ValidationError("Please enter a valid country")
60
84
  result = pycountry.countries.get(alpha_2=alpha_2)
61
85
  if result is None:
62
86
  raise ValidationError("Please enter a valid country")
63
- return result.name
87
+ return _display_name(result)
64
88
 
65
89
 
66
90
  def username(value: str, max_length: int = 12) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: human-input-validator
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Small validators for common human-entered data.
5
5
  Author: Snorlena
6
6
  License-Expression: MIT
@@ -41,7 +41,8 @@ invalid input.
41
41
 
42
42
  - `email(value)` — returns a trimmed, lower-cased email address.
43
43
  - `country(value)` — returns the official ISO 3166-1 country name, given a
44
- name or code.
44
+ name or code. Also accepts localized country names (e.g. "Deutschland",
45
+ "España") via pycountry's bundled translations.
45
46
  - `username(value, max_length=12)` — returns a lower-cased username, checking
46
47
  length and allowed characters.
47
48
  - `phonenumber(value, pattern)` — checks a phone number against one or more