python-iso639 2025.1.27__tar.gz → 2025.2.8__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.
Files changed (23) hide show
  1. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/CHANGELOG.md +10 -0
  2. {python_iso639-2025.1.27/src/python_iso639.egg-info → python_iso639-2025.2.8}/PKG-INFO +15 -5
  3. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/README.md +12 -2
  4. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/pyproject.toml +4 -4
  5. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/__init__.py +1 -8
  6. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/language.py +48 -30
  7. python_iso639-2025.2.8/src/iso639/py.typed +0 -0
  8. {python_iso639-2025.1.27 → python_iso639-2025.2.8/src/python_iso639.egg-info}/PKG-INFO +15 -5
  9. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/python_iso639.egg-info/SOURCES.txt +1 -0
  10. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/python_iso639.egg-info/requires.txt +2 -2
  11. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/LICENSE.txt +0 -0
  12. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/MANIFEST.in +0 -0
  13. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/setup.cfg +0 -0
  14. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/_data/__init__.py +0 -0
  15. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/_data/iso-639-3-macrolanguages.tab +0 -0
  16. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/_data/iso-639-3.tab +0 -0
  17. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/_data/iso-639-3_Name_Index.tab +0 -0
  18. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/iso639/_data/iso-639-3_Retirements.tab +0 -0
  19. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/python_iso639.egg-info/dependency_links.txt +0 -0
  20. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/python_iso639.egg-info/not-zip-safe +0 -0
  21. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/src/python_iso639.egg-info/top_level.txt +0 -0
  22. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/tests/test_language.py +0 -0
  23. {python_iso639-2025.1.27 → python_iso639-2025.2.8}/tests/test_version_number.py +0 -0
@@ -17,6 +17,16 @@ major/minor/micro version numbers like `05` (it'd have to be just `5`).
17
17
  ### Fixed
18
18
  ### Security
19
19
 
20
+ ## [2025.2.8]
21
+
22
+ ### Added
23
+ * Explcitly indicated that the library is type-annotated.
24
+
25
+ ## [2025.1.28]
26
+
27
+ ### Changed
28
+ * Sped up the retrieval of `Language` objects during runtime.
29
+
20
30
  ## [2025.1.27]
21
31
 
22
32
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: python-iso639
3
- Version: 2025.1.27
3
+ Version: 2025.2.8
4
4
  Summary: ISO 639 language codes, names, and other associated information
5
5
  Author-email: "Jackson L. Lee" <jacksonlunlee@gmail.com>
6
6
  License: Apache 2.0
@@ -28,10 +28,10 @@ Requires-Python: >=3.9
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE.txt
30
30
  Provides-Extra: dev
31
- Requires-Dist: black==24.10.0; extra == "dev"
31
+ Requires-Dist: black==25.1.0; extra == "dev"
32
32
  Requires-Dist: build==1.2.2; extra == "dev"
33
33
  Requires-Dist: flake8==7.1.1; extra == "dev"
34
- Requires-Dist: mypy==1.14.1; extra == "dev"
34
+ Requires-Dist: mypy==1.15.0; extra == "dev"
35
35
  Requires-Dist: pytest==8.3.4; extra == "dev"
36
36
  Requires-Dist: requests==2.32.3; extra == "dev"
37
37
  Requires-Dist: twine==6.1.0; extra == "dev"
@@ -48,9 +48,10 @@ other associated information.
48
48
 
49
49
  Current features:
50
50
 
51
- * A representation of languages mapped across ISO 639-1, 639-2, and 639-3.
52
- * Functionality to "guess" what a language is for a given
51
+ * 🌐 A representation of languages mapped across ISO 639-1, 639-2, and 639-3.
52
+ * 🔎 Functionality to "guess" what a language is for a given
53
53
  unknown language code or name.
54
+ * 🚀 Optimized for speed in retrieving language information.
54
55
 
55
56
  ## Installation
56
57
 
@@ -82,6 +83,15 @@ Create a `Language` instance by one of the class methods.
82
83
  Language(part3='fra', part2b='fre', part2t='fra', part1='fr', scope='I', type='L', name='French', comment=None, other_names=None, macrolanguage=None, retire_reason=None, retire_change_to=None, retire_remedy=None, retire_date=None)
83
84
  ```
84
85
 
86
+ Fast object instantiation for retrieving language information (run on Python 3.13, macOS 15.2, Apple M1 Pro)
87
+
88
+ ```python
89
+ In [1]: import iso639
90
+
91
+ In [2]: %timeit iso639.Language.from_part3("fra")
92
+ 220 ns ± 0.658 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
93
+ ```
94
+
85
95
  #### From Another ISO 639 Code Set or a Reference Name
86
96
 
87
97
  ```python
@@ -10,9 +10,10 @@ other associated information.
10
10
 
11
11
  Current features:
12
12
 
13
- * A representation of languages mapped across ISO 639-1, 639-2, and 639-3.
14
- * Functionality to "guess" what a language is for a given
13
+ * 🌐 A representation of languages mapped across ISO 639-1, 639-2, and 639-3.
14
+ * 🔎 Functionality to "guess" what a language is for a given
15
15
  unknown language code or name.
16
+ * 🚀 Optimized for speed in retrieving language information.
16
17
 
17
18
  ## Installation
18
19
 
@@ -44,6 +45,15 @@ Create a `Language` instance by one of the class methods.
44
45
  Language(part3='fra', part2b='fre', part2t='fra', part1='fr', scope='I', type='L', name='French', comment=None, other_names=None, macrolanguage=None, retire_reason=None, retire_change_to=None, retire_remedy=None, retire_date=None)
45
46
  ```
46
47
 
48
+ Fast object instantiation for retrieving language information (run on Python 3.13, macOS 15.2, Apple M1 Pro)
49
+
50
+ ```python
51
+ In [1]: import iso639
52
+
53
+ In [2]: %timeit iso639.Language.from_part3("fra")
54
+ 220 ns ± 0.658 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
55
+ ```
56
+
47
57
  #### From Another ISO 639 Code Set or a Reference Name
48
58
 
49
59
  ```python
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-iso639"
7
- version = "2025.1.27"
7
+ version = "2025.2.8"
8
8
  description = "ISO 639 language codes, names, and other associated information"
9
9
  readme = "README.md"
10
10
  requires-python = ">= 3.9"
@@ -34,10 +34,10 @@ classifiers = [
34
34
 
35
35
  [project.optional-dependencies]
36
36
  dev = [
37
- "black == 24.10.0",
37
+ "black == 25.1.0",
38
38
  "build == 1.2.2",
39
39
  "flake8 == 7.1.1",
40
- "mypy == 1.14.1",
40
+ "mypy == 1.15.0",
41
41
  "pytest == 8.3.4",
42
42
  "requests == 2.32.3",
43
43
  "twine == 6.1.0",
@@ -52,7 +52,7 @@ where = [ "src" ]
52
52
  [tool.setuptools]
53
53
  zip-safe = false
54
54
  package-dir = { "" = "src" }
55
- package-data = { "iso639" = ["_data/*.tab"] }
55
+ package-data = { "iso639" = ["_data/*.tab", "py.typed"] }
56
56
 
57
57
  [tool.pytest.ini_options]
58
58
  addopts = "-vv --durations=0 --strict-markers"
@@ -1,7 +1,7 @@
1
1
  import datetime
2
2
  from importlib.metadata import version
3
3
 
4
- from .language import Language, LanguageNotFoundError, _get_all_languages
4
+ from .language import ALL_LANGUAGES, Language, LanguageNotFoundError
5
5
 
6
6
 
7
7
  # __version__ is based on calendar versioning (https://calver.org/).
@@ -12,13 +12,6 @@ __version__ = version("python-iso639")
12
12
  DATA_LAST_UPDATED = datetime.date(2025, 1, 15)
13
13
 
14
14
 
15
- def __getattr__(name):
16
- if name == "ALL_LANGUAGES":
17
- return _get_all_languages()
18
- else:
19
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
20
-
21
-
22
15
  __all__ = [
23
16
  "__version__",
24
17
  "ALL_LANGUAGES",
@@ -3,10 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import datetime
6
- import functools
7
6
  from dataclasses import dataclass
8
7
 
9
- from typing import List, Union, Set, Tuple
8
+ from typing import Dict, List, Union, Set
10
9
 
11
10
  from ._data import (
12
11
  _PART3_TO_CODES,
@@ -101,7 +100,7 @@ class Language:
101
100
  return isinstance(other, Language) and self.part3 == other.part3
102
101
 
103
102
  @classmethod
104
- def match(cls, user_input) -> Language:
103
+ def match(cls, user_input: str, /) -> Language:
105
104
  """Return a ``Language`` instance by matching on the user input.
106
105
 
107
106
  Parameters
@@ -131,7 +130,7 @@ class Language:
131
130
  # Order of columns to query the data tables.
132
131
  # Bias towards (and therefore prioritize) the user input being
133
132
  # a language code rather than a language name.
134
- query_order = (
133
+ query_order: List[_COLUMN_TYPE] = [
135
134
  _CodesColumn.ID,
136
135
  _CodesColumn.PART2B,
137
136
  _CodesColumn.PART2T,
@@ -140,55 +139,55 @@ class Language:
140
139
  _CodesColumn.REF_NAME,
141
140
  _NameIndexColumn.PRINT_NAME,
142
141
  _NameIndexColumn.INVERTED_NAME,
143
- )
144
- return _get_language(user_input, query_order)
142
+ ]
143
+ return _PART3_TO_LANGUAGES[_get_part3(user_input, query_order)]
145
144
 
146
145
  @classmethod
147
- def from_part3(cls, user_input) -> Language:
146
+ def from_part3(cls, user_input: str, /) -> Language:
148
147
  """Return a ``Language`` instance from an ISO 639-3 code."""
149
- return _get_language(user_input, (_CodesColumn.ID, _RetirementsColumn.ID))
148
+ return _PART3_TO_LANGUAGES[
149
+ _get_part3(user_input, [_CodesColumn.ID, _RetirementsColumn.ID])
150
+ ]
150
151
 
151
152
  @classmethod
152
- def from_part2b(cls, user_input) -> Language:
153
+ def from_part2b(cls, user_input: str, /) -> Language:
153
154
  """Return a ``Language`` instance from an ISO 639-2 (bibliographic) code."""
154
- return _get_language(user_input, (_CodesColumn.PART2B,))
155
+ return _PART3_TO_LANGUAGES[_get_part3(user_input, [_CodesColumn.PART2B])]
155
156
 
156
157
  @classmethod
157
- def from_part2t(cls, user_input) -> Language:
158
+ def from_part2t(cls, user_input: str, /) -> Language:
158
159
  """Return a ``Language`` instance from an ISO 639-2 (terminological) code."""
159
- return _get_language(user_input, (_CodesColumn.PART2T,))
160
+ return _PART3_TO_LANGUAGES[_get_part3(user_input, [_CodesColumn.PART2T])]
160
161
 
161
162
  @classmethod
162
- def from_part1(cls, user_input) -> Language:
163
+ def from_part1(cls, user_input: str, /) -> Language:
163
164
  """Return a ``Language`` instance from an ISO 639-1 code."""
164
- return _get_language(user_input, (_CodesColumn.PART1,))
165
+ return _PART3_TO_LANGUAGES[_get_part3(user_input, [_CodesColumn.PART1])]
165
166
 
166
167
  @classmethod
167
- def from_name(cls, user_input) -> Language:
168
+ def from_name(cls, user_input: str, /) -> Language:
168
169
  """Return a ``Language`` instance from an ISO 639-3 reference language name."""
169
- query_order = (
170
+ query_order: List[_COLUMN_TYPE] = [
170
171
  _CodesColumn.REF_NAME,
171
172
  _NameIndexColumn.PRINT_NAME,
172
173
  _NameIndexColumn.INVERTED_NAME,
173
- )
174
- return _get_language(user_input, query_order)
174
+ ]
175
+ return _PART3_TO_LANGUAGES[_get_part3(user_input, query_order)]
175
176
 
176
177
 
177
- @functools.lru_cache()
178
- def _get_language(user_input: str, query_order: Tuple[_COLUMN_TYPE]) -> Language:
179
- """Create a ``Language`` instance.
178
+ def _get_part3(user_input: str, query_order: List[_COLUMN_TYPE]) -> str:
179
+ """Get the part 3 code of a language.
180
180
 
181
181
  Parameters
182
182
  ----------
183
183
  user_input : str
184
184
  The user-provided language code or name.
185
- query_order : Tuple[_COLUMN_TYPE]
186
- A tuple of columns to specify query order.
187
- A tuple but not a list because this argument needs to be hashable for lru_cache.
185
+ query_order : List[_COLUMN_TYPE]
186
+ A list of columns to specify query order.
188
187
 
189
188
  Returns
190
189
  -------
191
- Language
190
+ str
192
191
 
193
192
  Raises
194
193
  ------
@@ -227,6 +226,21 @@ def _get_language(user_input: str, query_order: Tuple[_COLUMN_TYPE]) -> Language
227
226
  f"{user_input!r} isn't an ISO language code or name"
228
227
  )
229
228
 
229
+ return part3
230
+
231
+
232
+ def _get_language(part3: str) -> Language:
233
+ """Create a ``Language`` instance.
234
+
235
+ Parameters
236
+ ----------
237
+ part3 : str
238
+ Part 3 code of the language.
239
+
240
+ Returns
241
+ -------
242
+ Language
243
+ """
230
244
  from_codes = _PART3_TO_CODES.get(part3)
231
245
  from_macrolanguages = _PART3_TO_MACROLANGUAGES.get(part3)
232
246
  from_retirements = _PART3_TO_RETIREMENTS.get(part3)
@@ -300,11 +314,15 @@ def _get_language(user_input: str, query_order: Tuple[_COLUMN_TYPE]) -> Language
300
314
  return language
301
315
 
302
316
 
303
- @functools.lru_cache()
304
- def _get_all_languages() -> Set[Language]:
305
- languages = set()
317
+ def _get_all_languages() -> Dict[str, Language]:
318
+ languages = {}
306
319
  for part3 in _PART3_TO_CODES:
307
- languages.add(_get_language(part3, (_CodesColumn.ID,)))
320
+ languages[part3] = _get_language(part3)
308
321
  for part3 in _PART3_TO_RETIREMENTS:
309
- languages.add(_get_language(part3, (_RetirementsColumn.ID,)))
322
+ languages[part3] = _get_language(part3)
310
323
  return languages
324
+
325
+
326
+ _PART3_TO_LANGUAGES: Dict[str, Language] = _get_all_languages()
327
+
328
+ ALL_LANGUAGES: Set[Language] = set(_PART3_TO_LANGUAGES.values())
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: python-iso639
3
- Version: 2025.1.27
3
+ Version: 2025.2.8
4
4
  Summary: ISO 639 language codes, names, and other associated information
5
5
  Author-email: "Jackson L. Lee" <jacksonlunlee@gmail.com>
6
6
  License: Apache 2.0
@@ -28,10 +28,10 @@ Requires-Python: >=3.9
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE.txt
30
30
  Provides-Extra: dev
31
- Requires-Dist: black==24.10.0; extra == "dev"
31
+ Requires-Dist: black==25.1.0; extra == "dev"
32
32
  Requires-Dist: build==1.2.2; extra == "dev"
33
33
  Requires-Dist: flake8==7.1.1; extra == "dev"
34
- Requires-Dist: mypy==1.14.1; extra == "dev"
34
+ Requires-Dist: mypy==1.15.0; extra == "dev"
35
35
  Requires-Dist: pytest==8.3.4; extra == "dev"
36
36
  Requires-Dist: requests==2.32.3; extra == "dev"
37
37
  Requires-Dist: twine==6.1.0; extra == "dev"
@@ -48,9 +48,10 @@ other associated information.
48
48
 
49
49
  Current features:
50
50
 
51
- * A representation of languages mapped across ISO 639-1, 639-2, and 639-3.
52
- * Functionality to "guess" what a language is for a given
51
+ * 🌐 A representation of languages mapped across ISO 639-1, 639-2, and 639-3.
52
+ * 🔎 Functionality to "guess" what a language is for a given
53
53
  unknown language code or name.
54
+ * 🚀 Optimized for speed in retrieving language information.
54
55
 
55
56
  ## Installation
56
57
 
@@ -82,6 +83,15 @@ Create a `Language` instance by one of the class methods.
82
83
  Language(part3='fra', part2b='fre', part2t='fra', part1='fr', scope='I', type='L', name='French', comment=None, other_names=None, macrolanguage=None, retire_reason=None, retire_change_to=None, retire_remedy=None, retire_date=None)
83
84
  ```
84
85
 
86
+ Fast object instantiation for retrieving language information (run on Python 3.13, macOS 15.2, Apple M1 Pro)
87
+
88
+ ```python
89
+ In [1]: import iso639
90
+
91
+ In [2]: %timeit iso639.Language.from_part3("fra")
92
+ 220 ns ± 0.658 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
93
+ ```
94
+
85
95
  #### From Another ISO 639 Code Set or a Reference Name
86
96
 
87
97
  ```python
@@ -5,6 +5,7 @@ README.md
5
5
  pyproject.toml
6
6
  src/iso639/__init__.py
7
7
  src/iso639/language.py
8
+ src/iso639/py.typed
8
9
  src/iso639/_data/__init__.py
9
10
  src/iso639/_data/iso-639-3-macrolanguages.tab
10
11
  src/iso639/_data/iso-639-3.tab
@@ -1,9 +1,9 @@
1
1
 
2
2
  [dev]
3
- black==24.10.0
3
+ black==25.1.0
4
4
  build==1.2.2
5
5
  flake8==7.1.1
6
- mypy==1.14.1
6
+ mypy==1.15.0
7
7
  pytest==8.3.4
8
8
  requests==2.32.3
9
9
  twine==6.1.0