Skip to content

[Bug]: CLI shows vNone / skillware None when editable and PyPI installs overlap on the same Python #333

Description

@rosspeili

Affected Component

Packaging / PyPI install

Skill ID (if applicable)

No response

Bug Description

When a maintainer mixes editable (pip install -e . from a clone) and PyPI wheel (pip install skillware / pip install -U skillware) on the same Python interpreter, pip can leave orphaned or duplicate skillware distribution metadata. The CLI then fails to resolve a version string and prints:

  • Interactive splash: Skillware vNone — Skill Management Framework
  • skillware --versionskillware None

Pip also breaks against the corrupt registration (pip show may crash; pip uninstall may fail with uninstall-no-record-file).

Expected: Skillware v0.5.4 on splash and skillware 0.5.4 for --version; pip show skillware succeeds.

Observed: Maintainer machine, Windows 11, Python 3.13, after upgrading from an editable 0.5.1-era install to PyPI 0.5.4 without a clean uninstall (Sep 2026).

Not a PyPI wheel defect — published 0.5.4 has valid METADATA and RECORD on a clean install. This is a local install-state footgun for contributors who switch clone dev ↔ global PyPI testing on one Python.

Version display code unchanged between v0.5.1 and v0.5.4 (_package_version_str() in skillware/cli.py, since #222). Security policy / 0.5.4 release chore unrelated. CLI themes (#248 / #305) unrelated.

Impact

Audience Risk
End users — clean pip install skillware or pip install -U skillware Low
Maintainers — editable + PyPI on same Python High (scenario we hit post-0.5.4)
CI / fresh venvs None

Root cause

  1. _package_version_str()get_installed_version()importlib.metadata.version("skillware").
  2. get_installed_version() returns None when metadata is missing, empty, "dev", or unparseable.
  3. Multiple distributions named skillware can register; a corrupt one (orphan editable dist-info with no METADATA / RECORD) wins → no parseable Version → f-string prints None.
  4. Fallback does not coerce invalid values to "dev" when metadata exists but is broken.

Example orphan (skillware-0.5.1.dist-info/direct_url.json only):

{"dir_info": {"editable": true}, "url": "file:///D:/ARPA/OpenSource/Skillware"}

Suggested product fix

1. Harden _package_version_str() — treat None, "", literal "None" as invalid; final fallback "dev". Add test with mocked corrupt metadata.

2. detect_install_conflicts() in version_policy.py — flag duplicate distributions, missing Version / Metadata-Version, editable + wheel on same prefix, orphan dist-info (no RECORD).

3. Surface warnings

  • CLI startup: dim stderr one-liner (like emit_upgrade_advisory) when conflicts detected.
  • skillware doctor --install: full report + copy-paste fix commands (Windows + Unix). Goal: one command to diagnose.
  • Optional: install block in skillware config show.

4. CONTRIBUTING.md — subsection Editable vs PyPI on the same Python: one mode per interpreter; py -3.13 -m pip on Windows; uninstall before switching; manual cleanup if uninstall-no-record-file.

5. Optional: scripts/dev_install.ps1 / scripts/dev_install.sh — uninstall, remove orphans if needed, pip install -e ".[dev,all]".

Acceptance criteria

  • _package_version_str() never returns None or empty
  • detect_install_conflicts() identifies duplicate / orphan installs
  • skillware doctor --install reports health + fix commands (exit 0 = ok, 1 = conflicts)
  • CLI startup warning when conflicts exist
  • CONTRIBUTING.md documents editable ↔ PyPI switching
  • CHANGELOG [Unreleased] entry
  • No PyPI wheel layout change required

Out of scope

  • Fixing pip uninstall upstream
  • Auto-deleting site-packages without user consent
  • Changing end-user PyPI install flow

Steps to Reproduce

  1. Clone repo; install editable on Python 3.13:
    cd D:\path\to\skillware
    py -3.13 -m pip install -e ".[dev,all]"
  2. Without uninstalling, install or upgrade from PyPI on the same interpreter:
    py -3.13 -m pip install -U skillware
    or py -3.13 -m pip install skillware==0.5.4.
  3. Optionally repeat across releases so old skillware-0.5.1.dist-info (editable marker) survives alongside a newer wheel.
  4. Run:
    skillware
    skillware --version
    py -3.13 -m pip show skillware
    py -3.13 -m pip uninstall skillware -y

Actual (corrupt state): vNone; pip show may crash; pip uninstall may fail.

Orphan artifacts (example)

Under %LOCALAPPDATA%\Programs\Python\Python313\Lib\site-packages\:

  • skillware-0.5.1.dist-info/ — orphan editable; often only direct_url.json, no METADATA, no RECORD
  • skillware-0.5.4.dist-info/ — valid wheel metadata
  • skillware/ — leftover package dir from wheel
  • Repo skillware.egg-info/ — second editable marker when working from clone

Manual recovery (workaround today)

If pip uninstall works:

py -3.13 -m pip uninstall skillware -y
py -3.13 -m pip install skillware==0.5.4

If pip uninstall fails (uninstall-no-record-file):

rmdir /s /q "%LOCALAPPDATA%\Programs\Python\Python313\Lib\site-packages\skillware"
rmdir /s /q "%LOCALAPPDATA%\Programs\Python\Python313\Lib\site-packages\skillware-0.5.1.dist-info"
rmdir /s /q "%LOCALAPPDATA%\Programs\Python\Python313\Lib\site-packages\skillware-0.5.4.dist-info"
del "%LOCALAPPDATA%\Programs\Python\Python313\Scripts\skillware.exe"

cd %USERPROFILE%
py -3.13 -m pip install --force-reinstall skillware==0.5.4

Editable dev only (one mode per Python):

cd /d D:\ARPA\OpenSource\Skillware
py -3.13 -m pip uninstall skillware -y
py -3.13 -m pip install -e ".[dev,all]"

Rule: do not keep editable and PyPI wheel on the same interpreter; uninstall before switching.

Error Logs

C:\Users\vpeil>skillware --version
skillware None

C:\Users\vpeil>skillware
  Skillware vNone — Skill Management Framework

C:\Users\vpeil>py -3.13 -m pip show skillware
ERROR: Exception:
...
  File "...pip\_internal\commands\show.py", line 192, in print_results
    metadata_version_tuple = tuple(map(int, dist.metadata_version.split(".")))
ValueError: invalid literal for int() with base 10: ''

C:\Users\vpeil>py -3.13 -m pip uninstall skillware -y
Found existing installation: skillware None
error: uninstall-no-record-file

× Cannot uninstall skillware None
╰─> The package's contents are unknown: no RECORD file was found for skillware.

Environment

Windows 11 (10.0.26200)
Python 3.13 (py launcher: py -3.13)
Install: pip install -e . from clone, then pip install skillware==0.5.4 on same interpreter
PyPI wheel 0.5.4 valid on clean install; corruption from overlapping registrations
Accidentally also installed on Python 3.9 from same repo (separate site-packages — avoid)
Config: ~/.config/skillware/config.yaml and/or .skillware.yaml (unrelated)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working.cliskillware CLI — doctor, paths, config show, mail submenu, interactive menu, or docs/usage/cli.md.documentationImprovements or additions to documentation.enhancementNew feature or request.good first issueGood for newcomers.packagingPyPI wheel, pyproject.toml, MANIFEST.in, or install extras.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions