| 53 | |
| 54 | |
| 55 | def get_name_version(info): |
| 56 | if not (name := info.get("name")): |
| 57 | raise ValueError("Name is empty") |
| 58 | if not (version := info.get("version")): |
| 59 | raise ValueError("Version is empty") |
| 60 | |
| 61 | # Briefcase requires version numbers to be in the canonical Python format, and some |
| 62 | # installer types use the version to distinguish between upgrades, downgrades and |
| 63 | # reinstalls. So try to produce a consistent ordering by extracting the last valid |
| 64 | # version from the Constructor version string. |
| 65 | # |
| 66 | # Hyphens aren't allowed in this format, but for compatibility with Miniconda's |
| 67 | # version format, we treat them as dots. |
| 68 | matches = list( |
| 69 | re.finditer( |
| 70 | r"(\d+!)?\d+(\.\d+)*((a|b|rc)\d+)?(\.post\d+)?(\.dev\d+)?", |
| 71 | version.lower().replace("-", "."), |
| 72 | ) |
| 73 | ) |
| 74 | if not matches: |
| 75 | logger.warning( |
| 76 | f"Version {version!r} contains no valid version numbers; " |
| 77 | f"defaulting to {DEFAULT_VERSION}" |
| 78 | ) |
| 79 | return f"{name} {version}", DEFAULT_VERSION |
| 80 | |
| 81 | match = matches[-1] |
| 82 | version = match.group() |
| 83 | |
| 84 | # Treat anything else in the version string as part of the name. |
| 85 | start, end = match.span() |
| 86 | strip_chars = " .-_" |
| 87 | before = info["version"][:start].strip(strip_chars) |
| 88 | after = info["version"][end:].strip(strip_chars) |
| 89 | name = " ".join(s for s in [name, before, after] if s) |
| 90 | |
| 91 | return name, version |
| 92 | |
| 93 | |
| 94 | def make_app_name(name, source): |