Skip to content

Python: Add the missing py.typed markers, and check for them going forward - #8823

Open
fei (feiiiiii5) wants to merge 3 commits into
microsoft:mainfrom
feiiiiii5:fix/add-missing-py-typed
Open

fei (feiiiiii5) wants to merge 3 commits into
microsoft:mainfrom
feiiiiii5:fix/add-missing-py-typed

Conversation

@feiiiiii5

Copy link
Copy Markdown

Motivation & Context

18 packages declare Typing :: Typed but ship no py.typed, so the promise never reaches anyone who installs them. Reported in #8820; eavanvalkenburg confirmed it was an oversight and asked for a PR, plus a check so agents catch it themselves.

PEP 561 keeps that classifier through the marker file inside the package directory. Without it a type checker must treat the whole package as untyped. I checked the published artifact rather than the tree: agent_framework_foundry 1.13.1 on PyPI carries Classifier: Typing :: Typed in its METADATA and no py.typed in the wheel, so from agent_framework.foundry import FoundryChatClient resolved to Any while from agent_framework import Agent — from core, which does ship the marker — resolved to a full signature. Same import style, silently different results. foundry is what the root README.md quickstart imports.

The 18: a2a, anthropic, azure-contentunderstanding, azure-cosmos-memory, bedrock, claude, copilotstudio, declarative, devui, foundry, foundry_hosting, github_copilot, hosting, hosting-responses, hosting-telegram, mem0, purview, redis. The other 23 of the 38 declaring packages already have the marker.

Description & Review Guide

  • What are the major changes? 18 empty py.typed files, one per affected package, plus one checklist item in the Python package management skill. No code, no annotations, no packaging configuration.
  • What is the impact? The affected packages become visible to type checkers for the annotations they already carry, and the skill now makes the two drift apart visible: a new package that declares the classifier without the file fails a check the skill tells you to run.
  • What do you want reviewers to focus on? Whether the marker should land for all 18 at once or in batches, and whether the skill is the right home for the check. I had offered a split in Python: 18 packages declare Typing :: Typed but ship no py.typed #8820 and the answer was to go ahead, so this is all 18 in one commit — happy to split if a batch is easier.

How this was verified. uv build --wheel --package agent-framework-foundry on this branch puts py.typed in the wheel; installing that wheel into a clean venv and running mypy over

from agent_framework.foundry import FoundryChatClient
reveal_type(FoundryChatClient)

now reveals the full __init__ signature — project_endpoint: str | None = ..., model: str | None = ..., credential: ... , allow_preview: bool | None = ..., — where the published 1.13.1 wheel gives Any.

What I have not done. I have not type-checked these 18 packages, so I cannot say whether any has annotations that do not hold up. py.typed only makes annotations that are already there visible; it adds none, and some of these may surface further errors downstream once the marker lands. That is a maintainer's call, and why I asked about batching in #8820.

The skill's check reports nothing on this branch and all 18 on main:

cd python/packages
for d in */; do
  d=${d%/}
  [ -f "$d/pyproject.toml" ] || continue
  if grep -q "Typing :: Typed" "$d/pyproject.toml" && ! ls "$d"/agent_framework*/py.typed >/dev/null 2>&1; then
    echo "missing py.typed: $d"
  fi
done

Related Issue

Fixes #8820

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI balanced review requested due to automatic review settings September 29, 2026 10:18
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Sep 29, 2026
@github-actions github-actions Bot changed the title Add the missing py.typed markers, and check for them going forward Python: Add the missing py.typed markers, and check for them going forward Sep 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The package markers are complete and correctly located; the remaining documentation precision issue is non-blocking.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Adds PEP 561 markers to 18 typed Python packages and documents a consistency check for future packages.

Changes:

  • Adds missing py.typed markers.
  • Adds package-management guidance for keeping markers and classifiers aligned.
File Description
python/​.github/​skills/​python-package-management/​SKILL.md Documents marker validation.
python/​packages/​a2a/​agent_framework_a2a/​py.typed Marks A2A as typed.
python/​packages/​anthropic/​agent_framework_anthropic/​py.typed Marks Anthropic as typed.
python/​packages/​azure-contentunderstanding/​agent_framework_azure_contentunderstanding/​py.typed Marks Content Understanding as typed.
python/​packages/​azure-cosmos-memory/​agent_framework_azure_cosmos_memory/​py.typed Marks Cosmos memory as typed.
python/​packages/​bedrock/​agent_framework_bedrock/​py.typed Marks Bedrock as typed.
python/​packages/​claude/​agent_framework_claude/​py.typed Marks Claude as typed.
python/​packages/​copilotstudio/​agent_framework_copilotstudio/​py.typed Marks Copilot Studio as typed.
python/​packages/​declarative/​agent_framework_declarative/​py.typed Marks Declarative as typed.
python/​packages/​devui/​agent_framework_devui/​py.typed Marks DevUI as typed.
python/​packages/​foundry/​agent_framework_foundry/​py.typed Marks Foundry as typed.
python/​packages/​foundry_hosting/​agent_framework_foundry_hosting/​py.typed Marks Foundry hosting as typed.
python/​packages/​github_copilot/​agent_framework_github_copilot/​py.typed Marks GitHub Copilot as typed.
python/​packages/​hosting/​agent_framework_hosting/​py.typed Marks Hosting as typed.
python/​packages/​hosting-responses/​agent_framework_hosting_responses/​py.typed Marks Responses hosting as typed.
python/​packages/​hosting-telegram/​agent_framework_hosting_telegram/​py.typed Marks Telegram hosting as typed.
python/​packages/​mem0/​agent_framework_mem0/​py.typed Marks Mem0 as typed.
python/​packages/​purview/​agent_framework_purview/​py.typed Marks Purview as typed.
python/​packages/​redis/​agent_framework_redis/​py.typed Marks Redis as typed.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread python/.github/skills/python-package-management/SKILL.md Outdated
@feiiiiii5

Copy link
Copy Markdown
Author

Good catch — reworded in bdb73ee. PEP 561 only makes the marker the signal; what a checker does with an untyped import afterwards is its own business, so the skill now states the portable guarantee (without the marker the package is not treated as typed, so the API falls back to the checker's untyped-import behaviour) instead of promising Any. The marker files themselves are unchanged.

@eavanvalkenburg

Copy link
Copy Markdown
Member

fei (@feiiiiii5) thanks for taking a look at this, we do need to fix some typing issues now that did not get flagged before, see the failing check

@feiiiiii5

Copy link
Copy Markdown
Author

Thanks for the pointer — I agree they need fixing, and they are all in test files that nothing type-checked before the markers landed.

Reading the job log, all 18 are under packages/*/tests/: 5 in anthropic/tests/test_anthropic_client.py, 4 in declarative/tests/test_graph_executors.py, 5 across foundry_hosting/tests/, 3 in bedrock/tests/, 1 in redis/tests/. That is what adding the markers does — mypy starts checking the tests against the packages instead of skipping them.

Three of them look different from a plain missing fix:

packages/bedrock/tests/test_bedrock_settings.py:17: error: Module
"agent_framework_bedrock._chat_client" has no attribute "BedrockChatClient"

Both BedrockChatClient and BedrockSettings are defined in agent_framework_bedrock/_chat_client.py on this branch (lines 228 and 218), so mypy is resolving a different copy of the package than the workspace source — the tests reach into a private module, and once the package is typed mypy follows the import instead of treating it as Any. That may want a resolution fix rather than an edit to the test.

I could not reproduce the job locally: uv sync --all-packages --all-extras --all-groups does not finish in the time I have, so I have no mypy 2.3.1 run to work against, and I would rather not push 18 edits I cannot verify.

Two questions so I take the right next step:

  1. Scope — fix them in this PR, or a follow-up that only carries the typing fixes so this one stays reviewable on its own?
  2. If here, change the bedrock _chat_client imports to the public surface, or leave them and work around?

I will pick this back up as soon as a sync completes and run the same ci-test-typing before pushing.

auto-merge was automatically disabled September 29, 2026 23:28

Head branch was pushed to by a user without write access

@feiiiiii5

Copy link
Copy Markdown
Author

Fixed all 18 in d0e88ec. All are in test files, and each is now handled the way the surrounding code already handles it:

Monkeypatching → monkeypatch.setattr (10 errors). declarative and foundry_hosting assigned to a module attribute (base_mod.Engine) or to a method (client.aclose, storage.list_checkpoints), which mypy rejects. monkeypatch.setattr is already the idiom in these suites, so I moved those six patches over and dropped the now-pointless # zuban: ignore comments. Where a test asserted on the mock through the patched object, it now asserts on the mock directly, which also satisfies ty and pyright.

bedrock → import from the package (3 errors). test_bedrock_settings.py and test_bedrock_client.py pulled BedrockChatClient/BedrockSettings out of the private agent_framework_bedrock._chat_client. Both are in that package's __all__, and test_bedrock_client.py was already importing BedrockChatClient from the public path on the line above, so this just makes it consistent. The patch("agent_framework_bedrock._chat_client....") targets in those files are unaffected.

_redis_result → overload pair (1 error). Awaitable[_T] | _T left mypy unable to solve the TypeVar — it inferred Never. Rather than casting at the call site I gave the helper an @overload pair, so inference works and test_providers.py needed no change. The one place I touched library code rather than a test; behaviour-preserving, and the cause rather than a symptom.

Suppression codes (4 errors). The default_options argument in anthropic and foundry_hosting, and four async for loops over _inner_get_response, already carried per-checker suppressions; mypy's code was missing, so I added it alongside. warn_unused_ignores is off and the tests pyright config is basic, so the extra codes are inert elsewhere.

How I verified, since the CI job fans out over many checkers:

  • uv run --python 3.11 python scripts/workspace_poe_tasks.py ci-test-typing — the workflow's exact command — reports the same three core failures before and after my change: Import "agent_hooks" could not be resolved at test_agent_hooks.py:1333. agent_hooks is an external top-level package, not in this repository; CI has it installed and my local env does not, and this PR touches no core file. Environment gap on my side, not a regression, and the only thing still red locally.
  • Each of the five gating checkers, run per package the way the job does, is clean on all five packages: mypy, zuban, pyrefly, ty, pyright.
  • pytest over the five affected packages: 1927 passed, 454 skipped, no failures. uv run poe syntax passes all 82 tasks. ruff format --check and ruff check are clean on all eight files I touched.

One unrelated thing worth flagging: poe syntax reformats packages/typesafe/agent_framework_typesafe/_tool_calls.py, which does not conform to ruff format at main either. I reverted that and kept it out of this PR.

18 packages declare `Typing :: Typed` but ship no py.typed, so the
promise never reaches anyone who installs them: PEP 561 keeps it
through the marker file inside the package directory, and without it a
type checker must treat the whole package as untyped. Verified on the
published wheel, not just the tree - agent_framework_foundry 1.13.1
carried the classifier and not the file, so
`from agent_framework.foundry import FoundryChatClient` resolved to
`Any` while `from agent_framework import Agent`, from a package that
does ship the marker, resolved to a full signature.

flit already ships whatever is in the package directory, so the marker
is not being excluded by configuration - it was simply absent. Adding
the file is the whole change.

The package management skill now checks the two against each other,
since a new package can repeat the same mismatch.
The skill said a package that declares `Typing :: Typed` without
shipping `py.typed` "resolves to Any for every type checker", but
PEP 561 only makes the marker the signal; what a checker then does
with an untyped import is its own business. State the portable
guarantee instead: without the marker the package is not treated as
typed, so its API falls back to the checker's untyped-import
behaviour rather than the inline annotations.
Adding the markers makes mypy check these packages' tests instead of
skipping them, which surfaced 18 errors across five packages. All of them
are in test files, and each is fixed the way the surrounding code already
does it:

- module and method monkeypatching moves to monkeypatch.setattr, the idiom
  used elsewhere in these tests, so mypy no longer sees an assignment to a
  type or a method. The assertions now target the mock directly instead of
  reading it back off the patched object.
- bedrock imports BedrockChatClient and BedrockSettings from the package
  instead of the private _chat_client module; both are in __all__ and the
  same test file already imported BedrockChatClient publicly.
- _redis_result gains an overload pair. Awaitable[_T] | _T left mypy unable
  to solve the TypeVar from a coroutine argument; the overloads let it
  infer, so the test needs no cast.
- the two default_options arguments and four async-for loops get mypy's
  error code added to the per-checker suppressions already on those lines.

Verified with the repo's own five gating checkers (mypy, zuban, pyrefly,
ty, pyright) on Python 3.11 for all five packages, and the affected test
suites pass: 1927 passed, 454 skipped.
@feiiiiii5

Copy link
Copy Markdown
Author

Rebased onto the current main (it had moved 23 commits while this was open). One conflict, in test_bedrock_client.py: upstream added BedrockChatOptions to the public agent_framework_bedrock import, and my change moved BedrockSettings onto that same line. Resolved by taking all three names in one multi-line public import, which is what both sides wanted.

Re-verified after the rebase rather than assuming the resolution was right:

  • all five gating checkers on all five affected packages — mypy, zuban, pyrefly, ty, pyright — 25 runs, all clean
  • pytest over the five packages: 1946 passed, 454 skipped, 8 xpassed, no failures
  • ruff format --check and ruff check clean on the eight files

Now 27 files (the 18 py.typed markers, the skill checklist item, and the eight typing fixes) and mergeable again.

This branch was successfully deployed

1 active deployment
github-app-auth — 65a74a1a Deployed Sep 29, 2026 by feiiiiii5 via add_label #24055
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: 18 packages declare Typing :: Typed but ship no py.typed

3 participants