Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-extra-platforms for
openSUSE:Factory checked in at 2026-08-05 17:48:17
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-extra-platforms (Old)
and /work/SRC/openSUSE:Factory/.python-extra-platforms.new.16738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-extra-platforms"
Wed Aug 5 17:48:17 2026 rev:31 rq:1369498 version:13.6.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-extra-platforms/python-extra-platforms.changes
2026-08-03 18:02:09.353068596 +0200
+++
/work/SRC/openSUSE:Factory/.python-extra-platforms.new.16738/python-extra-platforms.changes
2026-08-05 17:49:05.047273680 +0200
@@ -1,0 +2,13 @@
+Tue Aug 4 07:44:06 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- update to 13.6.0:
+ * Add AlmaLinux platform detection: ALMALINUX / is_almalinux()
+ (via ID=almalinux in os-release).
+ * Add EndeavourOS platform detection: ENDEAVOUROS /
+ is_endeavouros() (via ID=endeavouros in os-release). Closes
+ #658.
+ * Unrecognized shell, terminal, CI and agent warnings now report
+ the environment variables (SHELL, TERM, CI, LLM, ...) each is
+ detected from, not just the architecture/platform primitives.
+
+-------------------------------------------------------------------
Old:
----
extra_platforms-13.5.3.tar.gz
New:
----
extra_platforms-13.6.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-extra-platforms.spec ++++++
--- /var/tmp/diff_new_pack.nPIlbf/_old 2026-08-05 17:49:05.887303109 +0200
+++ /var/tmp/diff_new_pack.nPIlbf/_new 2026-08-05 17:49:05.891303249 +0200
@@ -23,7 +23,7 @@
%bcond_with libalternatives
%endif
Name: python-extra-platforms
-Version: 13.5.3
+Version: 13.6.0
Release: 0
Summary: Detect platforms and group them by family
License: Apache-2.0
++++++ extra_platforms-13.5.3.tar.gz -> extra_platforms-13.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/changelog.md
new/extra-platforms-13.6.0/changelog.md
--- old/extra-platforms-13.5.3/changelog.md 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/changelog.md 2026-08-03 09:24:41.000000000
+0200
@@ -1,7 +1,16 @@
# Changelog
+## [`13.6.0`
(2026-08-03)](https://github.com/kdeldycke/extra-platforms/compare/v13.5.3...v13.6.0)
+
+- Add AlmaLinux platform detection: `ALMALINUX` / `is_almalinux()` (via
`ID=almalinux` in `os-release`).
+- Add EndeavourOS platform detection: `ENDEAVOUROS` / `is_endeavouros()` (via
`ID=endeavouros` in `os-release`). Closes
[#658](https://github.com/kdeldycke/extra-platforms/issues/658).
+- Unrecognized shell, terminal, CI and agent warnings now report the
environment variables (`SHELL`, `TERM`, `CI`, `LLM`, ...) each is detected
from, not just the architecture/platform primitives.
+
## [`13.5.3`
(2026-08-02)](https://github.com/kdeldycke/extra-platforms/compare/v13.5.2...v13.5.3)
+> [!NOTE]
+> `13.5.3` is available on [π
PyPI](https://pypi.org/project/extra-platforms/13.5.3/) and [π
GitHub](https://github.com/kdeldycke/extra-platforms/releases/tag/v13.5.3).
+
- Sync CI tooling, workflow pins and dependency floors with the latest
repomatic release; no functional changes to the package.
## [`13.5.2`
(2026-08-01)](https://github.com/kdeldycke/extra-platforms/compare/v13.5.1...v13.5.2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/citation.cff
new/extra-platforms-13.6.0/citation.cff
--- old/extra-platforms-13.5.3/citation.cff 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/citation.cff 2026-08-03 09:24:41.000000000
+0200
@@ -8,6 +8,6 @@
email: [email protected]
orcid: "https://orcid.org/0000-0001-9748-9014"
doi: 10.5281/zenodo.13341712
-version: 13.5.3
-date-released: 2026-08-02
+version: 13.6.0
+date-released: 2026-08-03
url: "https://github.com/kdeldycke/extra-platforms"
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/cli.md
new/extra-platforms-13.6.0/docs/cli.md
--- old/extra-platforms-13.5.3/docs/cli.md 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/docs/cli.md 2026-08-03 09:24:41.000000000
+0200
@@ -57,7 +57,7 @@
```{code-block} shell-session
$ extra-platforms
-extra-platforms 13.5.2
+extra-platforms 13.5.3
ββ Architecture ββ π± ARM64 (AArch64) ββ[AARCH64]ββββββββββββ
id: aarch64
@@ -87,7 +87,7 @@
```{code-block} json
{
- "version": "13.5.2",
+ "version": "13.5.3",
"architecture": {
"id": "aarch64",
"name": "ARM64 (AArch64)",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/detection.md
new/extra-platforms-13.6.0/docs/detection.md
--- old/extra-platforms-13.5.3/docs/detection.md 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/docs/detection.md 2026-08-03
09:24:41.000000000 +0200
@@ -27,6 +27,7 @@
| {func}`~is_aarch64` | π± | {data}`~AARCH64` |
| {func}`~is_aix` | βΏ | {data}`~AIX` |
| {func}`~is_alacritty` | π³ | {data}`~ALACRITTY` |
+| {func}`~is_almalinux` | ο | {data}`~ALMALINUX` |
| {func}`~is_alpine` | ποΈ | {data}`~ALPINE` |
| {func}`~is_altlinux` | Ξ | {data}`~ALTLINUX` |
| {func}`~is_amzn` | β€» | {data}`~AMZN` |
@@ -80,6 +81,7 @@
| {func}`~is_dash` | π¨ | {data}`~DASH` |
| {func}`~is_debian` | π | {data}`~DEBIAN` |
| {func}`~is_dragonfly_bsd` | πͺ° | {data}`~DRAGONFLY_BSD` |
+| {func}`~is_endeavouros` | π | {data}`~ENDEAVOUROS` |
| {func}`~is_exherbo` | π½ | {data}`~EXHERBO` |
| {func}`~is_fedora` | π© | {data}`~FEDORA` |
| {func}`~is_fish` | π | {data}`~FISH` |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/install.md
new/extra-platforms-13.6.0/docs/install.md
--- old/extra-platforms-13.5.3/docs/install.md 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/docs/install.md 2026-08-03 09:24:41.000000000
+0200
@@ -31,7 +31,7 @@
````{tab-item} Specific version
```shell-session
-$ uvx [email protected]
+$ uvx [email protected]
```
````
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/platforms.md
new/extra-platforms-13.6.0/docs/platforms.md
--- old/extra-platforms-13.5.3/docs/platforms.md 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/docs/platforms.md 2026-08-03
09:24:41.000000000 +0200
@@ -70,6 +70,7 @@
| Icon | Symbol | Name | Detection
function |
| :--: | :--------------------- | :----------------------------- |
:------------------------ |
| βΏ | {data}`~AIX` | IBM AIX |
{func}`~is_aix` |
+| ο | {data}`~ALMALINUX` | AlmaLinux |
{func}`~is_almalinux` |
| ποΈ | {data}`~ALPINE` | Alpine Linux |
{func}`~is_alpine` |
| Ξ | {data}`~ALTLINUX` | ALT Linux |
{func}`~is_altlinux` |
| β€» | {data}`~AMZN` | Amazon Linux |
{func}`~is_amzn` |
@@ -84,6 +85,7 @@
| ΟΎ | {data}`~CYGWIN` | Cygwin |
{func}`~is_cygwin` |
| π | {data}`~DEBIAN` | Debian |
{func}`~is_debian` |
| πͺ° | {data}`~DRAGONFLY_BSD` | DragonFly BSD |
{func}`~is_dragonfly_bsd` |
+| π | {data}`~ENDEAVOUROS` | EndeavourOS |
{func}`~is_endeavouros` |
| π½ | {data}`~EXHERBO` | Exherbo Linux |
{func}`~is_exherbo` |
| π© | {data}`~FEDORA` | Fedora |
{func}`~is_fedora` |
| π | {data}`~FREEBSD` | FreeBSD |
{func}`~is_freebsd` |
@@ -202,13 +204,14 @@
---
sankey-beta
-ALL_PLATFORMS,LINUX,45
+ALL_PLATFORMS,LINUX,47
ALL_PLATFORMS,BSD,7
ALL_PLATFORMS,SYSTEM_V,3
ALL_PLATFORMS,UNIX_LAYERS,2
ALL_PLATFORMS,OTHER_POSIX,2
ALL_PLATFORMS,LINUX_LAYERS,2
ALL_PLATFORMS,ALL_WINDOWS,1
+LINUX,ALMALINUX,1
LINUX,ALPINE,1
LINUX,ALTLINUX,1
LINUX,AMZN,1
@@ -221,6 +224,7 @@
LINUX,CLEARLINUX,1
LINUX,CLOUDLINUX,1
LINUX,DEBIAN,1
+LINUX,ENDEAVOUROS,1
LINUX,EXHERBO,1
LINUX,FEDORA,1
LINUX,GENERIC_LINUX,1
@@ -307,6 +311,7 @@
(β WSL1)
(β WSL2)
)π§ LINUX(
+ (ο ALMALINUX)
(ποΈ ALPINE)
(Ξ ALTLINUX)
(β€» AMZN)
@@ -319,6 +324,7 @@
(β³οΈ CLEARLINUX)
(κ© CLOUDLINUX)
(π DEBIAN)
+ (π ENDEAVOUROS)
(π½ EXHERBO)
(π© FEDORA)
(π₯ GENERIC_LINUX)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/pytest.md
new/extra-platforms-13.6.0/docs/pytest.md
--- old/extra-platforms-13.5.3/docs/pytest.md 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/docs/pytest.md 2026-08-03 09:24:41.000000000
+0200
@@ -115,6 +115,7 @@
| {deco}`~pytest.skip_all_terminals` |
{deco}`~pytest.unless_any_terminal` | π» | {data}`~ALL_TERMINALS`
|
| {deco}`~pytest.skip_all_traits` | {deco}`~pytest.unless_any_trait`
| β | {data}`~ALL_TRAITS` |
| {deco}`~pytest.skip_all_windows` |
{deco}`~pytest.unless_any_windows` | πͺ | {data}`~ALL_WINDOWS`
|
+| {deco}`~pytest.skip_almalinux` | {deco}`~pytest.unless_almalinux`
| ο | {data}`~ALMALINUX` |
| {deco}`~pytest.skip_alpine` | {deco}`~pytest.unless_alpine`
| ποΈ | {data}`~ALPINE` |
| {deco}`~pytest.skip_altlinux` | {deco}`~pytest.unless_altlinux`
| Ξ | {data}`~ALTLINUX` |
| {deco}`~pytest.skip_amzn` | {deco}`~pytest.unless_amzn`
| β€» | {data}`~AMZN` |
@@ -157,6 +158,7 @@
| {deco}`~pytest.skip_dash` | {deco}`~pytest.unless_dash`
| π¨ | {data}`~DASH` |
| {deco}`~pytest.skip_debian` | {deco}`~pytest.unless_debian`
| π | {data}`~DEBIAN` |
| {deco}`~pytest.skip_dragonfly_bsd` |
{deco}`~pytest.unless_dragonfly_bsd` | πͺ° | {data}`~DRAGONFLY_BSD`
|
+| {deco}`~pytest.skip_endeavouros` |
{deco}`~pytest.unless_endeavouros` | π | {data}`~ENDEAVOUROS`
|
| {deco}`~pytest.skip_exherbo` | {deco}`~pytest.unless_exherbo`
| π½ | {data}`~EXHERBO` |
| {deco}`~pytest.skip_fedora` | {deco}`~pytest.unless_fedora`
| π© | {data}`~FEDORA` |
| {deco}`~pytest.skip_fish` | {deco}`~pytest.unless_fish`
| π | {data}`~FISH` |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/trait.md
new/extra-platforms-13.6.0/docs/trait.md
--- old/extra-platforms-13.5.3/docs/trait.md 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/docs/trait.md 2026-08-03 09:24:41.000000000
+0200
@@ -145,6 +145,7 @@
| π± | {data}`~AARCH64` | ARM64 (AArch64) |
{func}`~is_aarch64` | Architecture |
| βΏ | {data}`~AIX` | IBM AIX |
{func}`~is_aix` | Platform |
| π³ | {data}`~ALACRITTY` | Alacritty |
{func}`~is_alacritty` | Terminal |
+| ο | {data}`~ALMALINUX` | AlmaLinux |
{func}`~is_almalinux` | Platform |
| ποΈ | {data}`~ALPINE` | Alpine Linux |
{func}`~is_alpine` | Platform |
| Ξ | {data}`~ALTLINUX` | ALT Linux |
{func}`~is_altlinux` | Platform |
| β€» | {data}`~AMZN` | Amazon Linux |
{func}`~is_amzn` | Platform |
@@ -180,6 +181,7 @@
| π¨ | {data}`~DASH` | Dash |
{func}`~is_dash` | Shell |
| π | {data}`~DEBIAN` | Debian |
{func}`~is_debian` | Platform |
| πͺ° | {data}`~DRAGONFLY_BSD` | DragonFly BSD |
{func}`~is_dragonfly_bsd` | Platform |
+| π | {data}`~ENDEAVOUROS` | EndeavourOS |
{func}`~is_endeavouros` | Platform |
| π½ | {data}`~EXHERBO` | Exherbo Linux |
{func}`~is_exherbo` | Platform |
| π© | {data}`~FEDORA` | Fedora |
{func}`~is_fedora` | Platform |
| π | {data}`~FISH` | Fish |
{func}`~is_fish` | Shell |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/__init__.py
new/extra-platforms-13.6.0/extra_platforms/__init__.py
--- old/extra-platforms-13.5.3/extra_platforms/__init__.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/__init__.py 2026-08-03
09:24:41.000000000 +0200
@@ -82,6 +82,7 @@
is_aarch64,
is_aix,
is_alacritty,
+ is_almalinux,
is_alpine,
is_altlinux,
is_amzn,
@@ -117,6 +118,7 @@
is_dash,
is_debian,
is_dragonfly_bsd,
+ is_endeavouros,
is_exherbo,
is_fedora,
is_fish,
@@ -284,6 +286,7 @@
)
from .platform_data import (
AIX,
+ ALMALINUX,
ALPINE,
ALTLINUX,
AMZN,
@@ -298,6 +301,7 @@
CYGWIN,
DEBIAN,
DRAGONFLY_BSD,
+ ENDEAVOUROS,
EXHERBO,
FEDORA,
FREEBSD,
@@ -410,7 +414,7 @@
"""
-__version__ = "13.5.3"
+__version__ = "13.6.0"
def _initialize_group_detection_functions() -> None:
@@ -561,6 +565,7 @@
"ALL_TRAITS",
"ALL_TRAIT_IDS",
"ALL_WINDOWS",
+ "ALMALINUX",
"ALPINE",
"ALTLINUX",
"AMZN",
@@ -605,6 +610,7 @@
"DASH",
"DEBIAN",
"DRAGONFLY_BSD",
+ "ENDEAVOUROS",
"EXHERBO",
"FEDORA",
"FISH",
@@ -752,6 +758,7 @@
"is_aarch64",
"is_aix",
"is_alacritty",
+ "is_almalinux",
"is_alpine",
"is_altlinux",
"is_amzn",
@@ -805,6 +812,7 @@
"is_dash",
"is_debian",
"is_dragonfly_bsd",
+ "is_endeavouros",
"is_exherbo",
"is_fedora",
"is_fish",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/detection.py
new/extra-platforms-13.6.0/extra_platforms/detection.py
--- old/extra-platforms-13.5.3/extra_platforms/detection.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/detection.py 2026-08-03
09:24:41.000000000 +0200
@@ -105,17 +105,82 @@
"""
-def _unrecognized_message(report: bool = True) -> str:
+_CATEGORY_ENV_SIGNALS: dict[str, tuple[str, ...]] = {
+ "shell": (
+ "SHELL",
+ "BASH_VERSION",
+ "FISH_VERSION",
+ "KSH_VERSION",
+ "NU_VERSION",
+ "XONSH_VERSION",
+ "ZSH_VERSION",
+ "PSModulePath",
+ ),
+ "terminal": (
+ "TERM",
+ "TERM_PROGRAM",
+ "TERM_PROGRAM_VERSION",
+ "COLORTERM",
+ "TERMINAL_NAME",
+ ),
+ "CI": ("CI",),
+ "agent": ("LLM",),
+}
+"""Environment variables to surface in an unrecognized-trait report, per
category.
+
+Architectures and platforms are diagnosed from the ``sys.platform`` /
+``platform.*`` / ``os_release_id()`` primitives the report already prints.
Shells,
+terminals, CI systems and agents are instead identified by environment
variables,
+which those primitives never expose. This maps each such category (keyed by the
+label {func}`_single_match` passes) to the variables its ``is_*()`` heuristics
read.
+
+The first entry is the category's primary signal, always shown (even when
unset, so
+a reader sees why an unrecognized trait logged at ``INFO`` rather than
``WARNING``).
+The rest are shown only when set: an unrecognized trait leaves every
presence-only
+marker unset, so the value-bearing variables (a ``SHELL`` path, a
``TERM_PROGRAM``
+name) are what carry the smoking gun.
+
+.. caution::
+ Only gating and value-bearing variables belong here, never a blanket dump
of
+ :data:`os.environ`: the environment routinely holds credentials and tokens.
+
+.. note::
+ The ``shell`` entry must list every
+ {attr}`~extra_platforms.Shell.version_env_var` in the registry;
+ ``test_shell_env_signals_cover_version_vars`` enforces it.
+"""
+
+
+def _env_signal_lines(var_names: tuple[str, ...]) -> str:
+ """Render environment variables as aligned ``name: value`` report lines.
+
+ The first variable is always shown (rendering ``<not set>`` when absent);
the
+ rest only when present. See {data}`_CATEGORY_ENV_SIGNALS`.
+ """
+ primary, *rest = var_names
+ shown = [primary, *(name for name in rest if name in environ)]
+ width = max(len(name) for name in shown) + 1 # +1 for the trailing colon.
+ rows: list[str] = []
+ for name in shown:
+ value = repr(environ[name]) if name in environ else "<not set>"
+ rows.append(f" {name + ':':<{width}} {value}")
+ return "\n".join(rows)
+
+
+def _unrecognized_message(report: bool = True, category: str | None = None) ->
str:
"""Generate a message for unrecognized environments.
- ```{important}
- This message must contain all the primitives used in the `detection`
module so
- maintainers can debug heuristics from user reports.
- ```
+ The report opens with the ``sys.platform`` / ``platform.*`` /
``os_release_id()``
+ primitives that identify architectures and platforms. When ``category``
names an
+ environment-variable-driven trait type, the variables that category's
heuristics
+ read are appended, since the primitives above never expose them (see
+ {data}`_CATEGORY_ENV_SIGNALS`).
:param report: If `True`, append a request to report the issue on GitHub.
Set to `False` for environments where the trait is legitimately absent
- (e.g., no terminal in CI, no CI locally).
+ (like no terminal in CI, or no CI locally).
+ :param category: Trait category label (as passed to {func}`_single_match`)
whose
+ environment-variable signals to append, or `None` for the primitives
alone.
"""
msg = (
"Environment:\n"
@@ -128,6 +193,9 @@
f" platform.architecture: {platform.architecture()!r}\n"
f" os_release_id: {os_release_id()!r}"
)
+ env_signals = _CATEGORY_ENV_SIGNALS.get(category) if category else None
+ if env_signals:
+ msg += f"\n{_env_signal_lines(env_signals)}"
if report:
msg += (
"\n\nPlease report this at "
@@ -142,17 +210,23 @@
*,
strict: bool,
expected: bool = True,
+ category: str | None = None,
) -> None:
"""Log or raise on unrecognized trait detection.
- :param trait_name: Human-readable name of the trait type (e.g.,
`"architecture"`).
+ :param trait_name: Human-readable name of the trait type (like
`"architecture"`).
:param strict: If `True`, raise {exc}`SystemError` instead of logging.
:param expected: If `True`, the trait is always expected to be detected
(architecture, platform, shell), so an unrecognized result logs a
`WARNING`
and asks users to report the issue. If `False` (terminal, CI), the
trait may
legitimately be absent, so only `INFO` is logged without a report
request.
+ :param category: Trait category label whose environment-variable signals to
+ include in the message (see {data}`_CATEGORY_ENV_SIGNALS`).
"""
- msg = f"Unrecognized {trait_name}:
{_unrecognized_message(report=expected)}"
+ msg = (
+ f"Unrecognized {trait_name}: "
+ f"{_unrecognized_message(report=expected, category=category)}"
+ )
if strict:
raise SystemError(msg)
# Defer logging import to keep cold module load fast: stdlib logging pulls
@@ -413,6 +487,12 @@
@cache
+def is_almalinux() -> bool:
+ """Return {data}`True` if current platform is
{data}`~extra_platforms.ALMALINUX`."""
+ return os_release_id() == "almalinux"
+
+
+@cache
def is_alpine() -> bool:
"""Return {data}`True` if current platform is
{data}`~extra_platforms.ALPINE`."""
return os_release_id() == "alpine"
@@ -542,6 +622,14 @@
@cache
+def is_endeavouros() -> bool:
+ """Return {data}`True` if current platform is
+ {data}`~extra_platforms.ENDEAVOUROS`.
+ """
+ return os_release_id() == "endeavouros"
+
+
+@cache
def is_exherbo() -> bool:
"""Return {data}`True` if current platform is
{data}`~extra_platforms.EXHERBO`."""
return os_release_id() == "exherbo"
@@ -2125,10 +2213,10 @@
if matching:
raise RuntimeError(
f"Multiple {plural or label + 's'} match: {matching!r}. "
- f"{_unrecognized_message()}"
+ f"{_unrecognized_message(category=label)}"
)
- _report_unrecognized(label, strict=strict, expected=expected)
+ _report_unrecognized(label, strict=strict, expected=expected,
category=label)
return unknown
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/group_data.py
new/extra-platforms-13.6.0/extra_platforms/group_data.py
--- old/extra-platforms-13.5.3/extra_platforms/group_data.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/group_data.py 2026-08-03
09:24:41.000000000 +0200
@@ -71,6 +71,7 @@
from .group import Group
from .platform_data import (
AIX,
+ ALMALINUX,
ALPINE,
ALTLINUX,
AMZN,
@@ -85,6 +86,7 @@
CYGWIN,
DEBIAN,
DRAGONFLY_BSD,
+ ENDEAVOUROS,
EXHERBO,
FEDORA,
FREEBSD,
@@ -399,6 +401,7 @@
"βοΈ",
(
AIX,
+ ALMALINUX,
ALPINE,
ALTLINUX,
AMZN,
@@ -413,6 +416,7 @@
CYGWIN,
DEBIAN,
DRAGONFLY_BSD,
+ ENDEAVOUROS,
EXHERBO,
FEDORA,
FREEBSD,
@@ -537,6 +541,7 @@
"Linux distributions",
"π§",
(
+ ALMALINUX,
ALPINE,
ALTLINUX,
AMZN,
@@ -549,6 +554,7 @@
CLEARLINUX,
CLOUDLINUX,
DEBIAN,
+ ENDEAVOUROS,
EXHERBO,
FEDORA,
GENERIC_LINUX,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/extra-platforms-13.5.3/extra_platforms/platform_data.py
new/extra-platforms-13.6.0/extra_platforms/platform_data.py
--- old/extra-platforms-13.5.3/extra_platforms/platform_data.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/platform_data.py 2026-08-03
09:24:41.000000000 +0200
@@ -44,6 +44,21 @@
AIX = Platform("aix", "IBM AIX", "βΏ", "https://ibm.com/products/aix/")
+ALMALINUX = Platform("almalinux", "AlmaLinux", "ο", "https://almalinux.org")
+"""
+```{note}
+AlmaLinux has no representative Unicode emoji, so like
+{data}`~extra_platforms.NOBARA` it uses a [NerdFont](https://www.nerdfonts.com)
+icon instead:
+[`ο` (i.e. `nf-linux-almalinux` /
+`f31d`)](https://www.nerdfonts.com/cheat-sheet?glyphSearch=nf-linux-almalinux).
+
+The side-effect of using a NerdFont character is it will only display correctly
+when using a supported font. Otherwise, it will appear as an unknown or
invisible
+character depending on the fonts.
+```
+"""
+
ALPINE = Platform("alpine", "Alpine Linux", "ποΈ", "https://alpinelinux.org")
ALTLINUX = Platform("altlinux", "ALT Linux", "Ξ", "https://altlinux.org")
@@ -74,6 +89,8 @@
"dragonfly_bsd", "DragonFly BSD", "πͺ°", "https://www.dragonflybsd.org"
)
+ENDEAVOUROS = Platform("endeavouros", "EndeavourOS", "π",
"https://endeavouros.com")
+
EXHERBO = Platform("exherbo", "Exherbo Linux", "π½", "https://exherbolinux.org")
FEDORA = Platform("fedora", "Fedora", "π©", "https://fedoraproject.org")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/pytest.py
new/extra-platforms-13.6.0/extra_platforms/pytest.py
--- old/extra-platforms-13.5.3/extra_platforms/pytest.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/pytest.py 2026-08-03
09:24:41.000000000 +0200
@@ -223,6 +223,7 @@
skip_all_terminals: MarkDecorator
skip_all_traits: MarkDecorator
skip_all_windows: MarkDecorator
+ skip_almalinux: MarkDecorator
skip_alpine: MarkDecorator
skip_altlinux: MarkDecorator
skip_amzn: MarkDecorator
@@ -265,6 +266,7 @@
skip_dash: MarkDecorator
skip_debian: MarkDecorator
skip_dragonfly_bsd: MarkDecorator
+ skip_endeavouros: MarkDecorator
skip_exherbo: MarkDecorator
skip_fedora: MarkDecorator
skip_fish: MarkDecorator
@@ -393,6 +395,7 @@
unless_aarch64: MarkDecorator
unless_aix: MarkDecorator
unless_alacritty: MarkDecorator
+ unless_almalinux: MarkDecorator
unless_alpine: MarkDecorator
unless_altlinux: MarkDecorator
unless_amzn: MarkDecorator
@@ -446,6 +449,7 @@
unless_dash: MarkDecorator
unless_debian: MarkDecorator
unless_dragonfly_bsd: MarkDecorator
+ unless_endeavouros: MarkDecorator
unless_exherbo: MarkDecorator
unless_fedora: MarkDecorator
unless_fish: MarkDecorator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/pyproject.toml
new/extra-platforms-13.6.0/pyproject.toml
--- old/extra-platforms-13.5.3/pyproject.toml 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/pyproject.toml 2026-08-03 09:24:41.000000000
+0200
@@ -5,12 +5,13 @@
[project]
# Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
name = "extra-platforms"
-version = "13.5.3"
+version = "13.6.0"
description = "π Detect architectures, platforms, shells, terminals, CI
systems and agents, grouped by family"
readme = "readme.md"
keywords = [
"Agent detection",
"Alacritty",
+ "AlmaLinux",
"Almquist Shell",
"Alpine Linux",
"ALT Linux",
@@ -53,6 +54,7 @@
"Debian",
"Distro detection",
"DragonFly BSD",
+ "EndeavourOS",
"Exherbo Linux",
"Fedora",
"Fish",
@@ -255,7 +257,7 @@
]
urls.Changelog =
"https://github.com/kdeldycke/extra-platforms/blob/main/changelog.md"
urls.Documentation = "https://kdeldycke.github.io/extra-platforms"
-urls.Download =
"https://github.com/kdeldycke/extra-platforms/releases/tag/v13.5.3"
+urls.Download =
"https://github.com/kdeldycke/extra-platforms/releases/tag/v13.6.0"
urls.Funding = "https://github.com/sponsors/kdeldycke"
urls.Homepage = "https://github.com/kdeldycke/extra-platforms"
urls.Issues = "https://github.com/kdeldycke/extra-platforms/issues"
@@ -452,7 +454,7 @@
report.precision = 2
[tool.bumpversion]
-current_version = "13.5.3"
+current_version = "13.6.0"
# Parse versions with an optional .devN suffix (PEP 440).
parse =
"(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\.dev(?P<dev>\\d+))?"
serialize = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/readme.md
new/extra-platforms-13.6.0/readme.md
--- old/extra-platforms-13.5.3/readme.md 2026-08-02 09:29:03.000000000
+0200
+++ new/extra-platforms-13.6.0/readme.md 2026-08-03 09:24:41.000000000
+0200
@@ -29,7 +29,7 @@
```shell-session
$ uvx extra-platforms
-extra-platforms 13.5.2
+extra-platforms 13.5.3
ββ Architecture ββ π± ARM64 (AArch64) ββ[AARCH64]ββββββββββββ
id: aarch64
@@ -72,7 +72,7 @@
```pycon
>>> import extra_platforms
>>> extra_platforms.__version__
-'13.5.2'
+'13.5.3'
```
## Examples
@@ -145,6 +145,7 @@
>>> from extra_platforms import LINUX
>>> LINUX.members
mappingproxy({
+ 'almalinux': Platform(id='almalinux', name='AlmaLinux'),
'alpine': Platform(id='alpine', name='Alpine Linux'),
'altlinux': Platform(id='altlinux', name='ALT Linux'),
'amzn': Platform(id='amzn', name='Amazon Linux'),
@@ -157,6 +158,7 @@
'clearlinux': Platform(id='clearlinux', name='Clear Linux OS'),
'cloudlinux': Platform(id='cloudlinux', name='CloudLinux OS'),
'debian': Platform(id='debian', name='Debian'),
+ 'endeavouros': Platform(id='endeavouros', name='EndeavourOS'),
'exherbo': Platform(id='exherbo', name='Exherbo Linux'),
'fedora': Platform(id='fedora', name='Fedora'),
'generic_linux': Platform(id='generic_linux', name='Generic Linux'),
@@ -192,8 +194,9 @@
'xenserver': Platform(id='xenserver', name='XenServer'),
})
>>> LINUX.member_ids
-frozenset({'rocky', 'kvmibm', 'pidora', 'pikaos', 'sourcemage', 'sles',
'tuxedo', 'rhel', 'opensuse', 'android', 'ultramarine', 'fedora', 'ubuntu',
'nixos', 'generic_linux', 'oracle', 'buildroot', 'gentoo', 'void', 'chromeos',
'alpine', 'slackware', 'arch', 'centos', 'amzn', 'xenserver', 'nobara',
'cachyos', 'cloudlinux', 'ibm_powerkvm', 'kali', 'exherbo', 'mandriva',
'openwrt', 'raspbian', 'debian', 'altlinux', 'guix', 'slitaz', 'linuxmint',
'mageia', 'scientific', 'manjaro', 'parallels', 'clearlinux'})
+frozenset({'rocky', 'kvmibm', 'pidora', 'pikaos', 'sourcemage', 'sles',
'tuxedo', 'rhel', 'opensuse', 'android', 'ultramarine', 'fedora', 'ubuntu',
'nixos', 'generic_linux', 'oracle', 'buildroot', 'gentoo', 'void', 'chromeos',
'almalinux', 'alpine', 'slackware', 'arch', 'centos', 'amzn', 'xenserver',
'nobara', 'cachyos', 'cloudlinux', 'ibm_powerkvm', 'kali', 'exherbo',
'mandriva', 'openwrt', 'raspbian', 'debian', 'endeavouros', 'altlinux', 'guix',
'slitaz', 'linuxmint', 'mageia', 'scientific', 'manjaro', 'parallels',
'clearlinux'})
>>> print("\n".join([p.name for p in LINUX]))
+AlmaLinux
Alpine Linux
ALT Linux
Amazon Linux
@@ -206,6 +209,7 @@
Clear Linux OS
CloudLinux OS
Debian
+EndeavourOS
Exherbo Linux
Fedora
Generic Linux
@@ -341,6 +345,7 @@
(β WSL1)
(β WSL2)
)π§ LINUX(
+ (ο ALMALINUX)
(ποΈ ALPINE)
(Ξ ALTLINUX)
(β€» AMZN)
@@ -353,6 +358,7 @@
(β³οΈ CLEARLINUX)
(κ© CLOUDLINUX)
(π DEBIAN)
+ (π ENDEAVOUROS)
(π½ EXHERBO)
(π© FEDORA)
(π₯ GENERIC_LINUX)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/tests/test_platform_data.py
new/extra-platforms-13.6.0/tests/test_platform_data.py
--- old/extra-platforms-13.5.3/tests/test_platform_data.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/tests/test_platform_data.py 2026-08-03
09:24:41.000000000 +0200
@@ -15,6 +15,9 @@
from __future__ import annotations
+import ast
+from pathlib import Path
+
from extra_platforms import (
ALL_PLATFORM_GROUPS,
ALL_PLATFORMS,
@@ -108,3 +111,76 @@
assert BSD_WITHOUT_MACOS.issubset(BSD)
assert UNIX.issuperset(BSD_WITHOUT_MACOS)
assert BSD.issuperset(BSD_WITHOUT_MACOS)
+
+
+def _in_private_use_area(char: str) -> bool:
+ """Return ``True`` for a Unicode Private Use Area codepoint.
+
+ NerdFont glyphs live in the PUA. Ordinary emoji sit above ``U+E000`` as
well, so
+ a bare ``>= U+E000`` test misclassifies them; match the three PUA ranges
instead.
+ """
+ codepoint = ord(char)
+ return (
+ 0xE000 <= codepoint <= 0xF8FF
+ or 0xF0000 <= codepoint <= 0xFFFFD
+ or 0x100000 <= codepoint <= 0x10FFFD
+ )
+
+
+def test_nerdfont_icons_documented_in_source():
+ """Every platform with a NerdFont icon embeds that glyph in its docstring.
+
+ NerdFont glyphs live in the Unicode Private Use Area and render as an
+ invisible or placeholder character without a patched font, so
+ each such platform carries an attribute docstring warning about the font
+ requirement, whose icon link repeats the glyph. Because the glyph is
invisible
+ in an editor, it is easy to drop from that link by accident, which then
renders
+ as broken plaintext instead of a hyperlink. Lock the invariant by parsing
the
+ source: every NerdFont-icon platform must have an attribute docstring, and
that
+ docstring must contain the glyph itself.
+ """
+ source = (
+ Path(__file__).parent.parent / "extra_platforms" / "platform_data.py"
+ ).read_text(encoding="utf-8")
+ body = ast.parse(source).body
+
+ checked = []
+ for definition, following in zip(body, body[1:]):
+ # Match a module-level "NAME = Platform(id, name, icon, url)"
assignment.
+ if not (
+ isinstance(definition, ast.Assign)
+ and len(definition.targets) == 1
+ and isinstance(definition.targets[0], ast.Name)
+ and isinstance(definition.value, ast.Call)
+ and isinstance(definition.value.func, ast.Name)
+ and definition.value.func.id == "Platform"
+ and len(definition.value.args) >= 3
+ and isinstance(definition.value.args[2], ast.Constant)
+ and isinstance(definition.value.args[2].value, str)
+ ):
+ continue
+
+ icon = definition.value.args[2].value
+ # Only NerdFont icons (Private Use Area codepoints) are concerned.
+ if not any(_in_private_use_area(char) for char in icon):
+ continue
+
+ name = definition.targets[0].id
+ checked.append(name)
+
+ # The assignment must be immediately followed by an attribute
docstring.
+ assert (
+ isinstance(following, ast.Expr)
+ and isinstance(following.value, ast.Constant)
+ and isinstance(following.value.value, str)
+ ), (
+ f"Platform {name} uses NerdFont icon {icon!r} but has no attribute
"
+ f"docstring documenting the font requirement."
+ )
+ # The docstring must embed the glyph, or the icon link renders broken.
+ assert icon in following.value.value, (
+ f"Platform {name}'s NerdFont icon {icon!r} is missing from its "
+ f"attribute docstring; the icon link renders as broken plaintext."
+ )
+
+ assert checked, "expected NerdFont-icon platforms such as ALMALINUX and
NOBARA"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/tests/test_root.py
new/extra-platforms-13.6.0/tests/test_root.py
--- old/extra-platforms-13.5.3/tests/test_root.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/tests/test_root.py 2026-08-03
09:24:41.000000000 +0200
@@ -85,7 +85,7 @@
trait as trait_module,
)
from extra_platforms._deprecated import DEPRECATED_ALIASES, REMOVAL_VERSION
-from extra_platforms.detection import _unrecognized_message
+from extra_platforms.detection import _CATEGORY_ENV_SIGNALS,
_unrecognized_message
from extra_platforms.pytest import skip_hermetic_build
from .conftest import github_runner_os
@@ -623,6 +623,54 @@
assert "os_release_id:" in message
[email protected](
+ ("category", "gating_var"),
+ (
+ ("terminal", "TERM"),
+ ("CI", "CI"),
+ ("agent", "LLM"),
+ ),
+)
+def test_category_env_signals_gating_var(category, gating_var):
+ """The primary signal of each env-gated category is its ``expected=`` gate
var."""
+ assert _CATEGORY_ENV_SIGNALS[category][0] == gating_var
+
+
+def test_shell_env_signals_cover_version_vars():
+ """Every shell startup variable in the registry is reported on a shell
miss."""
+ version_vars = {
+ env_var
+ for shell in ALL_SHELLS
+ if (env_var := getattr(shell, "version_env_var", None))
+ }
+ assert version_vars <= set(_CATEGORY_ENV_SIGNALS["shell"])
+
+
[email protected]("category", ("shell", "terminal", "CI", "agent"))
+def test_unrecognized_message_reports_set_category_vars(monkeypatch, category):
+ """A set category variable, and its value, appear in that category's
report."""
+ primary_var = _CATEGORY_ENV_SIGNALS[category][0]
+ monkeypatch.setenv(primary_var, "sentinel-value")
+ message = _unrecognized_message(category=category)
+ assert f"{primary_var}:" in message
+ assert "sentinel-value" in message
+
+
+def test_unrecognized_message_hides_unset_secondary_vars(monkeypatch):
+ """Unset non-primary category variables are omitted, keeping the report
concise."""
+ monkeypatch.delenv("TERM_PROGRAM", raising=False)
+ message = _unrecognized_message(category="terminal")
+ assert "TERM_PROGRAM:" not in message
+
+
+def test_unrecognized_message_without_category_omits_env_block(monkeypatch):
+ """Architecture and platform reports carry only the system primitives."""
+ monkeypatch.setenv("TERM", "xterm-256color")
+ message = _unrecognized_message(category="platform")
+ assert "TERM:" not in message
+ assert "os_release_id:" in message
+
+
@pytest.mark.skipif(
sys.version_info < (3, 14), reason="platform.invalidate_caches added in
Python 3.14"
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/extra-platforms-13.5.3/tests/test_sphinx_crossrefs.py
new/extra-platforms-13.6.0/tests/test_sphinx_crossrefs.py
--- old/extra-platforms-13.5.3/tests/test_sphinx_crossrefs.py 2026-08-02
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/tests/test_sphinx_crossrefs.py 2026-08-03
09:24:41.000000000 +0200
@@ -865,7 +865,8 @@
def _get_all_public_symbols() -> Iterator[str]:
"""Collect all public symbols from extra_platforms and pytest modules.
- Returns a list of (symbol_name, is_pytest_decorator) tuples for
parametrization.
+ Yields each symbol name as a string; ``pytest`` submodule symbols carry a
+ ``pytest.`` prefix.
"""
# Add all symbols exposed in extra_platforms root.
@@ -878,7 +879,7 @@
yield f"pytest.{symbol_name}"
[email protected]("symbol_name", _get_all_public_symbols())
[email protected]("symbol_name", list(_get_all_public_symbols()))
def test_get_expected_page_for_symbol_handles_public_api(symbol_name):
"""Test that get_expected_page_for_symbol correctly handles each public
symbol.
@@ -1025,12 +1026,18 @@
"extend",
"insert",
}
+ # Standard-library members (like ``os.environ``) resolve via intersphinx
to the
+ # Python docs, so they have no anchor on a local extra_platforms page to
check.
+ stdlib_symbols = {
+ "os.environ",
+ }
if (
symbol in python_builtins
or symbol.split(".")[-1] in python_builtins
or (role == "meth" and symbol in python_builtin_methods)
+ or symbol in stdlib_symbols
):
- pytest.skip("Skipping builtin symbol")
+ pytest.skip("Skipping builtin or standard-library symbol")
if (
symbol.startswith((
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/extra-platforms-13.5.3/uv.lock
new/extra-platforms-13.6.0/uv.lock
--- old/extra-platforms-13.5.3/uv.lock 2026-08-02 09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/uv.lock 2026-08-03 09:24:41.000000000 +0200
@@ -373,7 +373,7 @@
[[package]]
name = "extra-platforms"
-version = "13.5.3.dev0"
+version = "13.6.0.dev0"
source = { editable = "." }
[package.optional-dependencies]