compatibility 2.1.0__tar.gz → 2.2.0__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 (20) hide show
  1. {compatibility-2.1.0 → compatibility-2.2.0}/PKG-INFO +6 -6
  2. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/__main__.py +62 -19
  3. compatibility-2.2.0/compatibility/locales/de/LC_MESSAGES/compatibility.mo +0 -0
  4. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/locales/de/LC_MESSAGES/compatibility.po +20 -0
  5. compatibility-2.2.0/compatibility/locales/es/LC_MESSAGES/compatibility.mo +0 -0
  6. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/locales/es/LC_MESSAGES/compatibility.po +20 -0
  7. compatibility-2.2.0/compatibility/locales/fr/LC_MESSAGES/compatibility.mo +0 -0
  8. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/locales/fr/LC_MESSAGES/compatibility.po +20 -0
  9. compatibility-2.2.0/compatibility/locales/nl/LC_MESSAGES/compatibility.mo +0 -0
  10. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/locales/nl/LC_MESSAGES/compatibility.po +20 -0
  11. {compatibility-2.1.0 → compatibility-2.2.0}/pyproject.toml +6 -6
  12. compatibility-2.1.0/compatibility/locales/de/LC_MESSAGES/compatibility.mo +0 -0
  13. compatibility-2.1.0/compatibility/locales/es/LC_MESSAGES/compatibility.mo +0 -0
  14. compatibility-2.1.0/compatibility/locales/fr/LC_MESSAGES/compatibility.mo +0 -0
  15. compatibility-2.1.0/compatibility/locales/nl/LC_MESSAGES/compatibility.mo +0 -0
  16. {compatibility-2.1.0 → compatibility-2.2.0}/LICENSE +0 -0
  17. {compatibility-2.1.0 → compatibility-2.2.0}/README.md +0 -0
  18. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/__init__.py +0 -0
  19. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/err.py +0 -0
  20. {compatibility-2.1.0 → compatibility-2.2.0}/compatibility/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compatibility
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: A library that checks whether the running version of Python is compatible and tested. Remind the user to check for updates of the library.
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -26,11 +26,11 @@ Classifier: Topic :: Software Development :: Quality Assurance
26
26
  Classifier: Topic :: Utilities
27
27
  Classifier: Typing :: Typed
28
28
  Provides-Extra: dev
29
- Requires-Dist: coverage[toml] (>=7.14,<8) ; extra == "dev"
30
- Requires-Dist: mypy (>=2.1,<3) ; extra == "dev"
31
- Requires-Dist: pytest (>=9.0,<10) ; extra == "dev"
32
- Requires-Dist: pytest-cov (>=7.1,<8) ; extra == "dev"
33
- Requires-Dist: ruff (>=0.15,<0.16) ; extra == "dev"
29
+ Requires-Dist: coverage[toml] (>=7.14.1,<8) ; extra == "dev"
30
+ Requires-Dist: mypy (>=2.1.0,<3) ; extra == "dev"
31
+ Requires-Dist: pytest (>=9.0.3,<10) ; extra == "dev"
32
+ Requires-Dist: pytest-cov (>=7.1.0,<8) ; extra == "dev"
33
+ Requires-Dist: ruff (>=0.15.16,<0.16) ; extra == "dev"
34
34
  Project-URL: Bug Tracker, https://github.com/RuedigerVoigt/compatibility/issues
35
35
  Project-URL: Changelog, https://github.com/RuedigerVoigt/compatibility/blob/main/CHANGELOG.md
36
36
  Project-URL: Documentation, https://github.com/RuedigerVoigt/compatibility
@@ -20,7 +20,7 @@ import platform
20
20
  import random
21
21
  import re
22
22
  import sys
23
- from typing import Literal, Optional, TypedDict, Union
23
+ from typing import Literal, TypedDict
24
24
 
25
25
  from compatibility import err
26
26
 
@@ -54,7 +54,7 @@ class SystemSupport(TypedDict, total=False):
54
54
  incompatible: set[str]
55
55
 
56
56
 
57
- class Check():
57
+ class Check:
58
58
  """Main Class of the compatibility package: check Python version
59
59
  and time since release."""
60
60
 
@@ -63,14 +63,21 @@ class Check():
63
63
  VERSION_REGEX = re.compile(
64
64
  r"(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<releaselevel>\b(alpha|beta|candidate|final)\b))?")
65
65
 
66
+ # Plausibility bounds for release_date. A date well into the future is
67
+ # almost always a typo (e.g. a wrong year); a very old date suggests
68
+ # release_date was never updated. Both only warn -- a near-future date is
69
+ # legitimate while developing toward a planned release.
70
+ FUTURE_LIMIT_DAYS = 60
71
+ PAST_LIMIT_YEARS = 8
72
+
66
73
  def __init__(self,
67
74
  package_name: str,
68
75
  package_version: str,
69
- release_date: Union[datetime.date, str],
70
- python_version_support: Optional[PythonVersionSupport] = None,
71
- nag_over_update: Optional[NagOverUpdate] = None,
76
+ release_date: datetime.date | str,
77
+ python_version_support: PythonVersionSupport | None = None,
78
+ nag_over_update: NagOverUpdate | None = None,
72
79
  language_messages: str = 'en',
73
- system_support: Optional[SystemSupport] = None,
80
+ system_support: SystemSupport | None = None,
74
81
  on_incompatible: Literal['raise', 'warn', 'ignore'] = 'raise'):
75
82
  """Initialize compatibility checker and perform all validation checks.
76
83
 
@@ -134,6 +141,7 @@ class Check():
134
141
  self._ = self._translation.gettext
135
142
  self.release_date = self.__coerce_date(release_date)
136
143
  self.check_params()
144
+ self.__warn_implausible_release_date()
137
145
  self.check_python_version(python_version_support)
138
146
  self.check_system(system_support)
139
147
  self.log_version_info()
@@ -141,7 +149,7 @@ class Check():
141
149
  self.check_version_age(nag_over_update)
142
150
 
143
151
  def __coerce_date(
144
- self, date_to_coerce: Union[str, datetime.date]) -> datetime.date:
152
+ self, date_to_coerce: str | datetime.date) -> datetime.date:
145
153
  """Convert a string to datetime.date and validate.
146
154
 
147
155
  Accepts either a datetime.date object (returned as-is) or a string
@@ -172,6 +180,25 @@ class Check():
172
180
  raise err.BadDateType(
173
181
  self._('date_to_coerce must be either string or datetime.date'))
174
182
 
183
+ def __warn_implausible_release_date(self) -> None:
184
+ """Warn (never raise) if release_date is implausibly far off.
185
+
186
+ A date more than ``FUTURE_LIMIT_DAYS`` ahead is almost always a typo
187
+ (e.g. a wrong year), and a date more than ``PAST_LIMIT_YEARS`` old
188
+ suggests release_date was not updated. Neither is fatal: a near-future
189
+ date is legitimate while developing toward a planned release, so both
190
+ cases only log a warning and leave the date usable.
191
+ """
192
+ days_from_today = (self.release_date - datetime.date.today()).days
193
+ if days_from_today > self.FUTURE_LIMIT_DAYS:
194
+ logger.warning(
195
+ self._("The release_date %s is more than %s days in the future. Please check it is correct."),
196
+ self.release_date, self.FUTURE_LIMIT_DAYS)
197
+ elif days_from_today < -(self.PAST_LIMIT_YEARS * 365):
198
+ logger.warning(
199
+ self._("The release_date %s is more than %s years in the past. Please check it is correct."),
200
+ self.release_date, self.PAST_LIMIT_YEARS)
201
+
175
202
  def check_params(self) -> None:
176
203
  """Validate that required parameters are non-empty and valid.
177
204
 
@@ -199,7 +226,7 @@ class Check():
199
226
  raise ValueError(self._('Invalid value for on_incompatible!'))
200
227
 
201
228
  def check_python_version(self,
202
- python_version_support: Optional[PythonVersionSupport] = None
229
+ python_version_support: PythonVersionSupport | None = None
203
230
  ) -> None:
204
231
  """Validate that the running Python version is supported.
205
232
 
@@ -213,8 +240,8 @@ class Check():
213
240
  no version checking is performed.
214
241
 
215
242
  Raises:
216
- ValueError: If python_version_support dict is malformed or
217
- contains invalid version strings.
243
+ ValueError: If python_version_support is not a dict, is malformed,
244
+ or contains invalid version strings.
218
245
  err.ParameterContradiction: If 'min_version' is higher than
219
246
  'max_tested_version'.
220
247
  RuntimeError: If running Python version is below minimum or is
@@ -224,6 +251,9 @@ class Check():
224
251
  if not python_version_support:
225
252
  # Setting python_version_support is not required
226
253
  return None
254
+ if not isinstance(python_version_support, dict):
255
+ raise ValueError(
256
+ self._('Parameter python_version_support must be a dictionary'))
227
257
 
228
258
  min_version, max_version = self.__validate_python_version_support(
229
259
  python_version_support)
@@ -256,7 +286,8 @@ class Check():
256
286
  re-parse (and assert-guard) the strings in the comparison helpers.
257
287
 
258
288
  Raises:
259
- ValueError: If the dict has the wrong keys, or any of the version
289
+ ValueError: If the dict has the wrong keys, if
290
+ 'incompatible_versions' is not a list, or any of the version
260
291
  strings ('min_version', 'max_tested_version', or any entry of
261
292
  'incompatible_versions') cannot be parsed.
262
293
  err.ParameterContradiction: If 'min_version' is higher than
@@ -287,6 +318,12 @@ class Check():
287
318
  python_version_support['max_tested_version'])
288
319
  if not max_match:
289
320
  raise ValueError(self._('Value for key max_tested_version incorrect.'))
321
+ # A bare string here would otherwise be iterated character by character
322
+ # (yielding a misleading "cannot be parsed" error), and an empty string
323
+ # would pass silently. Require an actual list.
324
+ if not isinstance(python_version_support['incompatible_versions'], list):
325
+ raise ValueError(
326
+ self._('Value for key incompatible_versions must be a list.'))
290
327
  for version_string in python_version_support['incompatible_versions']:
291
328
  if not re.fullmatch(self.VERSION_REGEX, version_string):
292
329
  raise ValueError(
@@ -368,7 +405,7 @@ class Check():
368
405
  )
369
406
 
370
407
  def check_system(self,
371
- system_support: Optional[SystemSupport]) -> None:
408
+ system_support: SystemSupport | None) -> None:
372
409
  """Validate operating system compatibility.
373
410
 
374
411
  Checks whether the current OS (Linux, MacOS, or Windows) is fully
@@ -527,10 +564,13 @@ class Check():
527
564
  (int 0-100, percentage chance of showing nag message).
528
565
 
529
566
  Raises:
530
- ValueError: If a key is missing, if a value is not an int, if
531
- nag_days_after_release is negative, or if nag_in_hundred is
532
- not between 0 and 100.
567
+ ValueError: If nag_over_update is not a dict, a key is missing, a
568
+ value is not an int, nag_days_after_release is negative, or
569
+ nag_in_hundred is not between 0 and 100.
533
570
  """
571
+ if not isinstance(nag_over_update, dict):
572
+ raise ValueError(
573
+ self._('Parameter nag_over_update must be a dictionary'))
534
574
  nag_days_after_release, nag_in_hundred = self.__validate_nag_over_update(
535
575
  nag_over_update)
536
576
  if nag_in_hundred == 0:
@@ -557,8 +597,8 @@ class Check():
557
597
  A tuple of (nag_days_after_release, nag_in_hundred).
558
598
 
559
599
  Raises:
560
- ValueError: If a key is missing, if a value is not an int (floats
561
- and strings are rejected rather than coerced), if
600
+ ValueError: If a key is missing, if a value is not an int (bool,
601
+ floats, and strings are rejected rather than coerced), if
562
602
  nag_days_after_release is negative, or if nag_in_hundred is
563
603
  not between 0 and 100.
564
604
  """
@@ -568,8 +608,11 @@ class Check():
568
608
  nag_days_after_release = nag_over_update['nag_days_after_release']
569
609
  nag_in_hundred = nag_over_update['nag_in_hundred']
570
610
  # Reject non-int values explicitly: int() would silently truncate a
571
- # float (3.7 -> 3) or accept a numeric string.
572
- if (not isinstance(nag_days_after_release, int)
611
+ # float (3.7 -> 3) or accept a numeric string. bool is checked first
612
+ # because it is a subclass of int (True would otherwise pass as 1).
613
+ if (isinstance(nag_days_after_release, bool)
614
+ or isinstance(nag_in_hundred, bool)
615
+ or not isinstance(nag_days_after_release, int)
573
616
  or not isinstance(nag_in_hundred, int)):
574
617
  raise ValueError(self._('Some key in nag_over_update has wrong type!'))
575
618
  if nag_days_after_release < 0:
@@ -60,6 +60,10 @@ msgstr "Wert für Schlüssel min_version ist inkorrekt."
60
60
  msgid "Value for key max_tested_version incorrect."
61
61
  msgstr "Wert für Schlüssel max_tested_version ist inkorrekt."
62
62
 
63
+ #: compatibility/__main__.py:326
64
+ msgid "Value for key incompatible_versions must be a list."
65
+ msgstr "Wert für Schlüssel incompatible_versions muss eine Liste sein."
66
+
63
67
  #: compatibility/__main__.py:294
64
68
  msgid "min_version (%(min)s) is higher than max_tested_version (%(max)s)."
65
69
  msgstr "min_version (%(min)s) ist höher als max_tested_version (%(max)s)."
@@ -143,3 +147,19 @@ msgstr "nag_days_after_release darf nicht negativ sein."
143
147
  #: compatibility/__main__.py:546
144
148
  msgid "nag_in_hundred must be int between 0 and 100."
145
149
  msgstr "nag_in_hundred muss ein Integer zwischen 0 und 100 sein."
150
+
151
+ #: compatibility/__main__.py:195
152
+ msgid "The release_date %s is more than %s days in the future. Please check it is correct."
153
+ msgstr "Das release_date %s liegt mehr als %s Tage in der Zukunft. Bitte prüfen Sie, ob es korrekt ist."
154
+
155
+ #: compatibility/__main__.py:199
156
+ msgid "The release_date %s is more than %s years in the past. Please check it is correct."
157
+ msgstr "Das release_date %s liegt mehr als %s Jahre in der Vergangenheit. Bitte prüfen Sie, ob es korrekt ist."
158
+
159
+ #: compatibility/__main__.py:256
160
+ msgid "Parameter python_version_support must be a dictionary"
161
+ msgstr "Parameter python_version_support muss ein Dictionary sein"
162
+
163
+ #: compatibility/__main__.py:566
164
+ msgid "Parameter nag_over_update must be a dictionary"
165
+ msgstr "Parameter nag_over_update muss ein Dictionary sein"
@@ -62,6 +62,10 @@ msgstr "El valor de la clave min_version es incorrecto."
62
62
  msgid "Value for key max_tested_version incorrect."
63
63
  msgstr "El valor de la clave max_tested_version es incorrecto."
64
64
 
65
+ #: compatibility/__main__.py:326
66
+ msgid "Value for key incompatible_versions must be a list."
67
+ msgstr "El valor de la clave incompatible_versions debe ser una lista."
68
+
65
69
  #: compatibility/__main__.py:294
66
70
  msgid "min_version (%(min)s) is higher than max_tested_version (%(max)s)."
67
71
  msgstr "min_version (%(min)s) es mayor que max_tested_version (%(max)s)."
@@ -145,3 +149,19 @@ msgstr "nag_days_after_release no debe ser negativo."
145
149
  #: compatibility/__main__.py:546
146
150
  msgid "nag_in_hundred must be int between 0 and 100."
147
151
  msgstr "nag_in_hundred debe ser un entero entre 0 y 100."
152
+
153
+ #: compatibility/__main__.py:195
154
+ msgid "The release_date %s is more than %s days in the future. Please check it is correct."
155
+ msgstr "La release_date %s está a más de %s días en el futuro. Compruebe que sea correcta."
156
+
157
+ #: compatibility/__main__.py:199
158
+ msgid "The release_date %s is more than %s years in the past. Please check it is correct."
159
+ msgstr "La release_date %s es de hace más de %s años. Compruebe que sea correcta."
160
+
161
+ #: compatibility/__main__.py:256
162
+ msgid "Parameter python_version_support must be a dictionary"
163
+ msgstr "El parámetro python_version_support debe ser un diccionario"
164
+
165
+ #: compatibility/__main__.py:566
166
+ msgid "Parameter nag_over_update must be a dictionary"
167
+ msgstr "El parámetro nag_over_update debe ser un diccionario"
@@ -62,6 +62,10 @@ msgstr "La valeur de la clé min_version est incorrecte."
62
62
  msgid "Value for key max_tested_version incorrect."
63
63
  msgstr "La valeur de la clé max_tested_version est incorrecte."
64
64
 
65
+ #: compatibility/__main__.py:326
66
+ msgid "Value for key incompatible_versions must be a list."
67
+ msgstr "La valeur de la clé incompatible_versions doit être une liste."
68
+
65
69
  #: compatibility/__main__.py:294
66
70
  msgid "min_version (%(min)s) is higher than max_tested_version (%(max)s)."
67
71
  msgstr "min_version (%(min)s) est supérieur à max_tested_version (%(max)s)."
@@ -145,3 +149,19 @@ msgstr "nag_days_after_release ne doit pas être négatif."
145
149
  #: compatibility/__main__.py:546
146
150
  msgid "nag_in_hundred must be int between 0 and 100."
147
151
  msgstr "nag_in_hundred doit être un entier entre 0 et 100."
152
+
153
+ #: compatibility/__main__.py:195
154
+ msgid "The release_date %s is more than %s days in the future. Please check it is correct."
155
+ msgstr "La release_date %s est à plus de %s jours dans le futur. Veuillez vérifier qu'elle est correcte."
156
+
157
+ #: compatibility/__main__.py:199
158
+ msgid "The release_date %s is more than %s years in the past. Please check it is correct."
159
+ msgstr "La release_date %s remonte à plus de %s ans. Veuillez vérifier qu'elle est correcte."
160
+
161
+ #: compatibility/__main__.py:256
162
+ msgid "Parameter python_version_support must be a dictionary"
163
+ msgstr "Le paramètre python_version_support doit être un dictionnaire"
164
+
165
+ #: compatibility/__main__.py:566
166
+ msgid "Parameter nag_over_update must be a dictionary"
167
+ msgstr "Le paramètre nag_over_update doit être un dictionnaire"
@@ -62,6 +62,10 @@ msgstr "De waarde voor sleutel min_version is onjuist."
62
62
  msgid "Value for key max_tested_version incorrect."
63
63
  msgstr "De waarde voor sleutel max_tested_version is onjuist."
64
64
 
65
+ #: compatibility/__main__.py:326
66
+ msgid "Value for key incompatible_versions must be a list."
67
+ msgstr "De waarde voor sleutel incompatible_versions moet een lijst zijn."
68
+
65
69
  #: compatibility/__main__.py:294
66
70
  msgid "min_version (%(min)s) is higher than max_tested_version (%(max)s)."
67
71
  msgstr "min_version (%(min)s) is hoger dan max_tested_version (%(max)s)."
@@ -145,3 +149,19 @@ msgstr "nag_days_after_release mag niet negatief zijn."
145
149
  #: compatibility/__main__.py:546
146
150
  msgid "nag_in_hundred must be int between 0 and 100."
147
151
  msgstr "nag_in_hundred moet een geheel getal tussen 0 en 100 zijn."
152
+
153
+ #: compatibility/__main__.py:195
154
+ msgid "The release_date %s is more than %s days in the future. Please check it is correct."
155
+ msgstr "De release_date %s ligt meer dan %s dagen in de toekomst. Controleer of deze juist is."
156
+
157
+ #: compatibility/__main__.py:199
158
+ msgid "The release_date %s is more than %s years in the past. Please check it is correct."
159
+ msgstr "De release_date %s ligt meer dan %s jaar in het verleden. Controleer of deze juist is."
160
+
161
+ #: compatibility/__main__.py:256
162
+ msgid "Parameter python_version_support must be a dictionary"
163
+ msgstr "Parameter python_version_support moet een dictionary zijn"
164
+
165
+ #: compatibility/__main__.py:566
166
+ msgid "Parameter nag_over_update must be a dictionary"
167
+ msgstr "Parameter nag_over_update moet een dictionary zijn"
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
6
6
  # [tool.poetry] below.
7
7
  [project]
8
8
  name = "compatibility"
9
- version = "2.1.0"
9
+ version = "2.2.0"
10
10
  description = "A library that checks whether the running version of Python is compatible and tested. Remind the user to check for updates of the library."
11
11
  readme = "README.md"
12
12
  license = "Apache-2.0"
@@ -41,11 +41,11 @@ dependencies = []
41
41
  # same set so local and CI tooling stay aligned.
42
42
  [project.optional-dependencies]
43
43
  dev = [
44
- "pytest>=9.0,<10",
45
- "pytest-cov>=7.1,<8",
46
- "mypy>=2.1,<3",
47
- "ruff>=0.15,<0.16",
48
- "coverage[toml]>=7.14,<8",
44
+ "pytest>=9.0.3,<10",
45
+ "pytest-cov>=7.1.0,<8",
46
+ "mypy>=2.1.0,<3",
47
+ "ruff>=0.15.16,<0.16",
48
+ "coverage[toml]>=7.14.1,<8",
49
49
  ]
50
50
 
51
51
  [project.urls]
File without changes
File without changes