fix(analyzer): let AzureAILanguageRecognizer load from a registry config, and un-mute the test that guards it - #2224
Merged
omri374 merged 1 commit intoAug 5, 2026
Conversation
RecognizerListLoader passes `name` for every entry it builds, and passed `context` before that. `AzureAILanguageRecognizer.__init__` accepted neither, so any YAML entry naming the class raised TypeError before the registry finished loading. That is data-privacy-stack#1457, fixed in data-privacy-stack#1458 with `**kwargs` and reintroduced by data-privacy-stack#1800 when the `**kwargs` came out without explicit parameters replacing it. Adds both parameters, appended last so existing positional callers are unaffected. `name` falls back to "Azure AI Language PII" when omitted, matching how AzureHealthDeidRecognizer handles the same situation. The regression test data-privacy-stack#1458 added could not catch this. Its marker, `skipif(pytest.importorskip("azure"), ...)`, skips whichever way the dependency goes: absent, importorskip raises while the decorator is evaluated and takes the whole module out of collection; present, it returns a truthy module and skipif fires. Measured on main, `pytest tests --collect-only` yields 3248 without the extra against 3292 with it -- 44 tests, the entire file, silently uncollected. Both markers now gate on importlib.util.find_spec for the module each test actually imports. The AHDS test additionally needs AHDS_ENDPOINT, since its recognizer builds a client from that variable when the config supplies none. The YAML fixture drops `ta_client: "test"`, which no longer reaches the constructor -- PredefinedRecognizerConfig ignores extra keys -- and gains a second entry so the fixture covers both the plain form and the class_name rename form. Closes data-privacy-stack#2223 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Change Description
AzureAILanguageRecognizercannot be built from a registry configuration onmain.RecognizerListLoaderpassesnamefor every entry it constructs, and passedcontextbefore that; the constructor accepts neither, so any YAML entry naming the class raises before the registry finishes loading:This is #1457, reported in 2024 against the
contextkwarg and fixed in #1458 by adding**kwargs. #1800 removed the**kwargs— correctly, sinceRemoteRecognizer.__init__does not accept them either, so it never absorbed anything and only moved the sameTypeErrorone frame deeper — but without adding the explicit parameters that were holding the loader path up. The other two recognizers that PR touched have since been repaired;ahds_recognizer.pyandlm_recognizer.pyboth takename: Optional[str] = Nonetoday.The regression test #1458 added was already unable to run by then, which is the second half of this PR.
Why the regression test could not fire
@pytest.mark.skipif(pytest.importorskip("azure"), reason="Optional dependency not installed")pytest.importorskipskips when a module is missing and otherwise returns the module, which is truthy. Both branches lose:azurepresent?Skippedis raised while the decorator is evaluated, i.e. at module import — the whole file drops out of collectionskipif(True)→ skipped, reportingOptional dependency not installedwhile it is installedMeasured on
main:So 44 tests — the entire module — are silently uncollected in any environment without the extra, and the two tests the marker was written for skip on every CI run, where
--all-extrasis used (ci.yml:74).azureis also the wrong module to name.azure.*are namespace packages and theahdsextra populates them too, soimport azurecan succeed withazure.ai.textanalyticsabsent.Changes
azure_ai_language.py— addednameandcontext, appended last so existing positional callers are unaffected.namefalls back to"Azure AI Language PII"when omitted, matching howAzureHealthDeidRecognizerresolves the same conflict between a configured name and a hardcoded display name.test_analyzer_engine_provider.py— both markers now gate onimportlib.util.find_specfor the module each test actually imports. A local helper swallows theModuleNotFoundErrorthatfind_specraises when an intermediate parent package is missing, which is whatazure.health.deidentificationdoes without theahdsextra.AHDS_ENDPOINT.AzureHealthDeidRecognizer.__init__builds a client when the config supplies none, and that path raisesValueErrorwithout the variable — so the endpoint is as much a precondition as the package.tests/test_ahds_recognizer.py:26already gates on it this way.tests/conf/test_azure_ai_language_reco.yaml— droppedta_client: "test", which no longer reaches the constructor (see follow-up 1 below), and added a second entry so the fixture covers both entry shapes: the plain form wherenamedoubles as the class selector, and theclass_namerename form. The test supplies credentials throughmonkeypatch.setenvinstead; nothing reaches the network, as the SDK client is constructed locally andanalyzeis overridden.tests/test_azure_ai_language_recognizer.py— three direct-construction tests for the name default, the name override, andcontext.Verification
Reverting only
azure_ai_language.pyfails exactly three tests —test_name_can_be_overridden,test_context_is_accepted,test_analyzer_engine_provider_with_azure_ai_language— and nothing else.Full
presidio-analyzersuite, this branch against anorigin/mainbaseline in the same environment: identical failure sets, 42 pre-existing failures/errors from optional dependencies absent locally (transformers,langextract, some spaCy models). Compared line by line, not by count.The delta is exactly this PR: three new tests, plus the Azure AI Language test moving from skipped to passing.
With the extra uninstalled and
site-packages/azureremoved,pytest tests --collect-onlyreports 3295 on this branch against 3248 onmain. The module is collected, its 41 other tests run, and only the two optional-dependency tests skip, with accurate reasons.ruff checkfrom the repo root as CI runs it,ruff format --checkon the modified module, andgit diff --checkall pass.Reproducing the "extra absent" rows takes more than
pip uninstall azure-ai-textanalytics azure-core:azure-commonalso claims theazurenamespace, and pip leaves an emptysite-packages/azure/ai/behind. Either keepsimport azuresucceeding.Follow-ups, not in this PR
PredefinedRecognizerConfigsilently drops recognizer-specific kwargs. It inherits pydantic's defaultextra="ignore", sota_client— andazure_ai_key/azure_ai_endpoint, which users would reasonably put in YAML — never reach the constructor.HuggingFaceRecognizerConfig,GLiNERRecognizerConfigandLangExtractRecognizerConfigeach setextra="allow"to work around this one class at a time. Worth a decision on whether that should be the default for predefined entries.AzureHealthDeidRecognizerdoes not acceptcontexteither. Same shape as thenamegap fixed here, and the same YAML path reaches it. Left alone to keep this diff scoped.AzureAILanguageRecognizeris still absent fromdefault_recognizers.yaml, so fix(analyzer): make every recognizer listed in default_recognizers.yaml loadable #2170's contract tests do not reach it — the load test only walks shipped entries, and the signature test only coversPatternRecognizersubclasses. Adding a shipped entry is a separate question, since the recognizer needs credentials to construct.Per the changelog policy in #2200,
CHANGELOG.mdis not modified.Issue reference
Closes #2223. Follows up on #1457 / #1458 (the original report and fix), #1521 (which introduced the marker), #1800 (which removed the
**kwargs) and #1819 (which made the loader passname).Checklist
🤖 Generated with Claude Code