|
121 | 121 | warnings.warn("pkg_resources is deprecated as an API", DeprecationWarning)
|
122 | 122 |
|
123 | 123 |
|
| 124 | +_PEP440_FALLBACK = re.compile(r"^v?(?P<safe>(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) |
| 125 | + |
| 126 | + |
124 | 127 | class PEP440Warning(RuntimeWarning):
|
125 | 128 | """
|
126 | 129 | Used when there is an issue with a version or specifier not complying with
|
@@ -1396,6 +1399,38 @@ def safe_version(version):
|
1396 | 1399 | return re.sub('[^A-Za-z0-9.]+', '-', version)
|
1397 | 1400 |
|
1398 | 1401 |
|
| 1402 | +def _forgiving_version(version): |
| 1403 | + """Fallback when ``safe_version`` is not safe enough |
| 1404 | + >>> parse_version(_forgiving_version('0.23ubuntu1')) |
| 1405 | + <Version('0.23.dev0+sanitized.ubuntu1')> |
| 1406 | + >>> parse_version(_forgiving_version('0.23-')) |
| 1407 | + <Version('0.23.dev0+sanitized')> |
| 1408 | + >>> parse_version(_forgiving_version('0.-_')) |
| 1409 | + <Version('0.dev0+sanitized')> |
| 1410 | + >>> parse_version(_forgiving_version('42.+?1')) |
| 1411 | + <Version('42.dev0+sanitized.1')> |
| 1412 | + >>> parse_version(_forgiving_version('hello world')) |
| 1413 | + <Version('0.dev0+sanitized.hello.world')> |
| 1414 | + """ |
| 1415 | + version = version.replace(' ', '.') |
| 1416 | + match = _PEP440_FALLBACK.search(version) |
| 1417 | + if match: |
| 1418 | + safe = match["safe"] |
| 1419 | + rest = version[len(safe):] |
| 1420 | + else: |
| 1421 | + safe = "0" |
| 1422 | + rest = version |
| 1423 | + local = f"sanitized.{_safe_segment(rest)}".strip(".") |
| 1424 | + return f"{safe}.dev0+{local}" |
| 1425 | + |
| 1426 | + |
| 1427 | +def _safe_segment(segment): |
| 1428 | + """Convert an arbitrary string into a safe segment""" |
| 1429 | + segment = re.sub('[^A-Za-z0-9.]+', '-', segment) |
| 1430 | + segment = re.sub('-[^A-Za-z0-9]+', '-', segment) |
| 1431 | + return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-") |
| 1432 | + |
| 1433 | + |
1399 | 1434 | def safe_extra(extra):
|
1400 | 1435 | """Convert an arbitrary string to a standard 'extra' name
|
1401 | 1436 |
|
@@ -2642,7 +2677,7 @@ def _reload_version(self):
|
2642 | 2677 | @property
|
2643 | 2678 | def hashcmp(self):
|
2644 | 2679 | return (
|
2645 |
| - self.parsed_version, |
| 2680 | + self._forgiving_parsed_version, |
2646 | 2681 | self.precedence,
|
2647 | 2682 | self.key,
|
2648 | 2683 | self.location,
|
@@ -2700,6 +2735,32 @@ def parsed_version(self):
|
2700 | 2735 |
|
2701 | 2736 | return self._parsed_version
|
2702 | 2737 |
|
| 2738 | + @property |
| 2739 | + def _forgiving_parsed_version(self): |
| 2740 | + try: |
| 2741 | + return self.parsed_version |
| 2742 | + except packaging.version.InvalidVersion as ex: |
| 2743 | + self._parsed_version = parse_version(_forgiving_version(self.version)) |
| 2744 | + |
| 2745 | + notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 |
| 2746 | + msg = f"""!!\n\n |
| 2747 | + ************************************************************************* |
| 2748 | + {str(ex)}\n{notes} |
| 2749 | +
|
| 2750 | + This is a long overdue deprecation. |
| 2751 | + For the time being, `pkg_resources` will use `{self._parsed_version}` |
| 2752 | + as a replacement to avoid breaking existing environments, |
| 2753 | + but no future compatibility is guaranteed. |
| 2754 | +
|
| 2755 | + If you maintain package {self.project_name} you should implement |
| 2756 | + the relevant changes to adequate the project to PEP 440 immediately. |
| 2757 | + ************************************************************************* |
| 2758 | + \n\n!! |
| 2759 | + """ |
| 2760 | + warnings.warn(msg, DeprecationWarning) |
| 2761 | + |
| 2762 | + return self._parsed_version |
| 2763 | + |
2703 | 2764 | @property
|
2704 | 2765 | def version(self):
|
2705 | 2766 | try:
|
|
0 commit comments