Skip to content

feat: add deprecation notices to SDK, CLI, Launchpad and docs - #734

Merged
olivermeyer merged 8 commits into
mainfrom
feat/PYSDK-154-deprecation-release
Oct 13, 2026
Merged

olivermeyer merged 8 commits into
mainfrom
feat/PYSDK-154-deprecation-release

Conversation

@olivermeyer

Copy link
Copy Markdown
Collaborator

Why?
The Python SDK is deprecated (PYSDK-154). Support ends on April 30, 2027, and then the repo and the PyPI project get archived. Users need to see this wherever they use the SDK, with a pointer to the Platform REST API or the Console.

How?
aignostics.constants.DEPRECATION_NOTICE feeds a log warning on import, a stderr line in the CLI, a Launchpad banner and a Read the Docs banner. The README gets the notice and a migration table, CONTRIBUTING.md gets the maintenance rules, and library users also get a FutureWarning. The CLI skips its stderr line with --format json, because some commands write JSON errors to stderr that callers parse.

🤖 Generated with Claude Code

@olivermeyer olivermeyer added the skip:test:long_running Skip long-running tests (≥5min) label Oct 12, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@olivermeyer
olivermeyer force-pushed the feat/PYSDK-154-deprecation-release branch from 5e44238 to a693c28 Compare October 12, 2026 09:33
@olivermeyer
olivermeyer marked this pull request as ready for review October 12, 2026 09:56
@olivermeyer
olivermeyer requested a review from a team as a code owner October 12, 2026 09:56
@claude

claude Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Claude finished @olivermeyer's task in 5m 56s —— View job


PR Review Complete ✅

Comprehensive review of deprecation notices implementation for PYSDK-154.


✅ Critical Checks - All Passed

1. Test Markers ✅

All new tests have proper category markers:

  • tests/aignostics/constants_test.py → @pytest.mark.unit
  • tests/aignostics/deprecation_test.py → @pytest.mark.integration (3 tests)
  • tests/aignostics/gui/frame_test.py → @pytest.mark.integration (3 tests)
  • tests/aignostics/cli_test.py → Added 4 integration tests, all properly marked

Verified with: uv run pytest -m "not unit and not integration and not e2e" --collect-only → 0 tests found ✅

2. Code Quality ✅

make lint → All checks passed
  ✅ ruff check
  ✅ ruff format
  ✅ pyright (0 errors, 0 warnings)
  ✅ mypy (Success: no issues found)

Minor: One warning about invalid noqa directive in client_token_provider_test.py:16 (pre-existing, not introduced by this PR)

3. Conventional Commits ✅

All 7 commits follow the required format:

5c3bc87 refactor(utils): add root callback to prepare_cli
3220b6c feat: log deprecation notice on import
acbb2fb feat(cli): show deprecation notice
2cd62e3 feat(gui): show deprecation banner in Launchpad
8466b4a docs: add deprecation notice to README
02e4daf docs: add deprecation banner to docs site
a693c28 docs: state maintenance-only policy

4. Test Coverage ✅

Excellent test coverage for all new functionality:

  • Constants validation (constants_test.py) - verifies notice includes date + alternatives
  • Import behavior (deprecation_test.py) - verifies library mode emits FutureWarning + log warning
  • CLI behavior (cli_test.py) - verifies stderr output + JSON format exception
  • GUI rendering (frame_test.py) - verifies banner presence + links

🎯 Architecture Compliance Review

Excellent Implementation ✅

Single Source of Truth:

# src/aignostics/constants.py
DEPRECATION_NOTICE = (
    "The Aignostics Python SDK is deprecated. Support ends on April 30, 2027. "
    "After that date, the repository and the PyPI project are archived and get no updates. "
    f"Use the Aignostics Platform REST API ({PLATFORM_API_DOCS_URL}) or the Console ({CONSOLE_URL})."
)

All surfaces correctly reference this constant - no text duplication. ✅

Surfaces Covered:

  1. Library Import (src/aignostics/__init__.py):

    • Logs warning via logger.warning()
    • Emits FutureWarning in library mode (__is_library_mode__)
    • Proper stacklevel for warning attribution
  2. CLI (src/aignostics/cli.py):

    • Root help text includes notice
    • Stderr output before each command
    • Smart exception: Skips notice when --format json to keep stderr parseable
    • Clean implementation using custom _RootGroup class
  3. GUI (src/aignostics/gui/_frame.py):

    • Amber warning banner on every page
    • Links to both REST API docs and Console
    • Uses semantic color (bg-amber-100) and proper icon (warning)
  4. Documentation (docs/source/conf.py + custom.css):

    • Furo announcement banner on every page
    • Auto-linkifies URLs in the notice
    • Custom CSS ensures proper wrapping
  5. README.md & CONTRIBUTING.md:

    • Prominent deprecation section
    • Migration table mapping old interfaces to alternatives
    • Maintenance mode rules clearly stated

CLI Implementation Deep Dive 🔍

The CLI implementation is particularly clever:

Problem: Need to show deprecation notice on stderr but NOT break JSON parsers that read stderr for error objects.

Solution: Custom _RootGroup that detects --format json during argument parsing:

class _RootGroup(TyperGroup):
    def parse_args(self, ctx: Any, args: list[str]) -> list[str]:
        ctx.meta[_JSON_OUTPUT_REQUESTED] = _requests_json_output(args)
        return super().parse_args(ctx, args)

Then skip notice if JSON requested:

def _print_deprecation_notice(ctx: typer.Context) -> None:
    if ctx.meta.get(_JSON_OUTPUT_REQUESTED, False):
        return
    typer.secho(DEPRECATION_NOTICE, err=True, fg=typer.colors.YELLOW)

This preserves backward compatibility for JSON consumers. ✅


📝 Suggestions (Non-Blocking)

1. CLI Argument Detection Robustness

File: src/aignostics/cli.py:34

The _requests_json_output() function uses pairwise() which is clean but might miss edge cases:

def _requests_json_output(args: list[str]) -> bool:
    return "--format=json" in args or any(
        arg == "--format" and value == "json" 
        for arg, value in pairwise(args)
    )

Edge cases not handled (likely irrelevant in practice):

  • --format as last argument without value
  • Multiple spaces between --format and json

Recommendation: Current implementation is fine for real-world usage. Click/Typer will error on malformed arguments anyway.

2. Add Inline Documentation

File: src/aignostics/cli.py:22-38

The _RootGroup class is clever but relies on knowing Click's context lifecycle (that it empties ctx.args before callbacks). Consider adding a docstring explaining why parse_args() must record the decision:

class _RootGroup(TyperGroup):
    """Root command group that records if the command arguments request JSON output.
    
    Click empties ``ctx.args`` before it calls the root callback. So the group records the
    decision in ``ctx.meta`` while it parses the arguments.
    """

Update: I see this comment is already in the docstring! ✅ No action needed.

3. Test Coverage Edge Case

Consider adding a test for --format without a value to ensure graceful handling:

@pytest.mark.integration
def test_cli_format_flag_without_value_shows_notice(runner: CliRunner) -> None:
    """Check that an incomplete --format flag still shows the deprecation notice."""
    result = runner.invoke(cli, ["application", "--format"])
    # Should fail on missing value but notice should appear
    assert result.exit_code != 0
    assert DEPRECATION_NOTICE in normalize_output(result.stderr)

This is not blocking - just defensive.


🔒 Security & Compliance

Medical Device Considerations ✅

  • No PHI/PII in deprecation notice
  • No hardcoded secrets or tokens
  • Proper data flow: notice is display-only
  • No breaking changes to API contracts

Deprecation Best Practices ✅

  • Clear end-of-support date (April 30, 2027)
  • Migration paths provided for all interfaces
  • Maintenance mode rules documented
  • FutureWarning is appropriate for library deprecation (PEP 565)

📚 Documentation Quality

Comprehensive Coverage ✅

CLAUDE.md updated with:

  • Maintenance mode section (lines 37-48)
  • Deprecation notice section (lines 503-515)
  • Clear rules for accepting changes

CONTRIBUTING.md updated with:

  • Maintenance mode policy
  • Numbered rules for contributions

README.md updated with:

  • Prominent deprecation section at top
  • Migration table with alternatives
  • Preserved interface comparison table

Docs site (conf.py + custom.css):

  • Banner on every page via Furo announcement theme option
  • Auto-linkified URLs
  • Proper CSS wrapping for mobile

🧪 Test Quality Assessment

New Tests Added: 11 tests across 4 files

Coverage breakdown:

  1. constants_test.py (1 test):

    • Validates notice includes date + both alternative URLs
  2. deprecation_test.py (3 tests, all @pytest.mark.integration, 60s timeout):

    • test_import_in_library_mode_emits_future_warning() - subprocess test
    • test_import_in_library_mode_logs_deprecation_warning() - subprocess test
    • test_cli_mode_logs_notice_without_future_warning() - subprocess test via CLI script
  3. cli_test.py (4 new tests, all @pytest.mark.integration):

    • test_cli_help_starts_with_deprecation_notice() - help text positioning
    • test_cli_command_prints_deprecation_notice_to_stderr() - stderr output
    • test_cli_subcommand_help_prints_deprecation_notice() - subcommand help
    • test_cli_json_format_skips_deprecation_notice() - JSON format exception (parametrized)
    • test_cli_text_format_prints_deprecation_notice() - text format baseline
  4. gui/frame_test.py (3 tests, all @pytest.mark.integration):

    • test_gui_deprecation_banner_on_start_page() - banner presence on /
    • test_gui_deprecation_banner_on_system_page() - banner on /system (proves it's in shared frame)
    • test_gui_deprecation_banner_links_to_alternatives() - validates both URLs are linked

Test Quality: All tests are well-structured, properly marked, have descriptive names, and test one thing. Subprocess tests properly isolate environment. ✅


🚀 CI/CD Status

Current CI pipeline: In Progress

  • ✅ Build Native Only: Success
  • 🔄 CI/CD: Running
  • 🔄 Claude Code PR Review: Running (this review)

The PR has skip:test:long_running label as recommended. ✅


🎨 Code Style & Quality

Strengths:

  • Clean separation of concerns
  • DRY principle followed (single constant)
  • Type hints complete
  • Docstrings added for new functions
  • Imports organized correctly
  • No dead code

Consistency with Codebase:

  • Follows modulith architecture
  • Uses existing patterns (typer.secho, logger.warning)
  • Respects presentation layer boundaries
  • Settings/constants properly located

🔍 Breaking Changes Analysis

No breaking changes ✅

What's preserved:

  • API signatures unchanged
  • CLI commands work identically
  • Output formats (JSON, text) unchanged except for stderr notice
  • GUI pages render with added banner (non-disruptive)
  • Library imports work as before (warning is advisory)

What's added (non-breaking):

  • Stderr deprecation notice (doesn't affect stdout)
  • GUI banner (doesn't block functionality)
  • FutureWarning (standard Python warning, filterable)
  • Log line (informational only)

📊 Impact Assessment

Files Changed: 22 files (+428/-10 lines)

Change Distribution:

  • Core implementation: 4 files (constants, init, cli, gui/_frame)
  • Utility support: 2 files (utils/_cli for root callback)
  • Tests: 4 files (constants_test, deprecation_test, cli_test, gui/frame_test)
  • Documentation: 12 files (README, CONTRIBUTING, CLAUDE.md, docs)

Risk Level: Low

  • Changes are additive (warnings/notices)
  • No logic changes to existing functionality
  • Test coverage is comprehensive
  • JSON output preserved for parsers

🏆 What's Excellent About This PR

  1. Single source of truth - One constant, many surfaces
  2. Thoughtful CLI design - JSON format exception prevents breaking parsers
  3. Comprehensive testing - 11 tests covering all surfaces and edge cases
  4. Complete documentation - CLAUDE.md, README, CONTRIBUTING all updated
  5. Clean architecture - No shortcuts, follows SDK patterns
  6. Proper Python warnings - FutureWarning follows PEP 565
  7. Medical device compliance - Deprecation doesn't expose PHI/PII
  8. Conventional commits - All 7 commits properly formatted

Final Summary

This PR implements SDK deprecation notices across all user-facing surfaces (library, CLI, GUI, docs) with:

  • ✅ All critical checks passing (markers, linting, commits, coverage)
  • ✅ Excellent architecture (single constant, clean separation)
  • ✅ Comprehensive testing (11 tests, all properly marked)
  • ✅ Thoughtful implementation (JSON format exception for parsers)
  • ✅ Complete documentation (CLAUDE.md, README, CONTRIBUTING)
  • ✅ No breaking changes
  • ✅ Follows all repository standards

Suggested improvements are minor and non-blocking. This is production-ready.


Verdict: ✅ claude:review:passed

@codecov

codecov Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

❗ There is a different number of reports uploaded between BASE (9200416) and HEAD (f87508b). Click for more details.

HEAD has 12 uploads less than BASE
Flag BASE (9200416) HEAD (f87508b)
13 1
Files with missing lines Coverage Δ
src/aignostics/__init__.py 90.00% <100.00%> (+4.28%) ⬆️
src/aignostics/cli.py 96.49% <100.00%> (+1.14%) ⬆️
src/aignostics/constants.py 91.66% <100.00%> (+1.19%) ⬆️
src/aignostics/gui/_frame.py 77.22% <100.00%> (-1.44%) ⬇️
src/aignostics/utils/_cli.py 88.00% <100.00%> (+2.63%) ⬆️

... and 25 files with indirect coverage changes

@claude claude Bot added the claude:review:passed Automated Claude PR review found no blocking issues on the current head commit label Oct 12, 2026

@arne-aignx arne-aignx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving my general comments instead of commenting the code 1 by 1.

The CLI is a bit spammy, when I launch launchpad:

~/Projects/python-sdk (feat/PYSDK-154-deprecation-release ✔) uv run aignostics launchpad
2026-10-12 13:31:56 | 4593   | WARNING  | aignostics:<module>:49 | The Aignostics Python SDK is deprecated. Support ends on April 30, 2027. After that date, the repository and the PyPI project are archived and get no updates. Use the Aignostics Platform REST API (https://platform.aignostics.com/api/v1/docs) or the Console (https://platform.aignostics.com). | {'__project__name__': 'aignostics'}
The Aignostics Python SDK is deprecated. Support ends on April 30, 2027. After that date, the repository and the PyPI project are archived and get no updates. Use the Aignostics Platform REST API (https://platform.aignostics.com/api/v1/docs) or the Console (https://platform.aignostics.com).
2026-10-12 13:31:58 | 4688   | WARNING  | aignostics:<module>:49 | The Aignostics Python SDK is deprecated. Support ends on April 30, 2027. After that date, the repository and the PyPI project are archived and get no updates. Use the Aignostics Platform REST API (https://platform.aignostics.com/api/v1/docs) or the Console (https://platform.aignostics.com). | {'__project__name__': 'aignostics'}
Using `create_lazy(coroutine=...)` is deprecated and will be removed in NiceGUI 4.0. Use `create_lazy(awaitable=...)` instead.
2026-10-12 13:32:02 | 4721   | WARNING  | aignostics:<module>:49 | The Aignostics Python SDK is deprecated. Support ends on April 30, 2027. After that date, the repository and the PyPI project are archived and get no updates. Use the Aignostics Platform REST API (https://platform.aignostics.com/api/v1/docs) or the Console (https://platform.aignostics.com). | {'__project__name__': 'aignostics'}

Also do we want to extend After that date, the repository and the PyPI project are archived and get no updates. by Starting <replace-with-next-release-date> updates are limitted to security updates.?

@olivermeyer

Copy link
Copy Markdown
Collaborator Author

@arne-aignx addressed both points:

  1. Silenced the deprecation warnings in child processes
  2. Clarified the notice, trying to keep it reasonably short

olivermeyer and others added 7 commits October 13, 2026 13:32
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI now writes a notice to stderr, and CliRunner's result.output
mixes stdout and stderr. Compare only stdout for the empty config values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@olivermeyer
olivermeyer force-pushed the feat/PYSDK-154-deprecation-release branch from 22af85f to f87508b Compare October 13, 2026 11:32
@sonarqubecloud

Copy link
Copy Markdown

@olivermeyer
olivermeyer merged commit 8284373 into main Oct 13, 2026
25 of 27 checks passed
@olivermeyer
olivermeyer deleted the feat/PYSDK-154-deprecation-release branch October 13, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude:review:passed Automated Claude PR review found no blocking issues on the current head commit skip:test:long_running Skip long-running tests (≥5min)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants