diff --git a/CHANGELOG.md b/CHANGELOG.md index bb10bb162..b490ba407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. - Added `PhUmidRecognizer` for Philippine Unified Multi-Purpose ID (UMID/CRN) numbers in dashed and plain 12-digit formats; disabled by default (#2045) (Thanks @Surya-5555) #### Fixed +- Prevented `SlimSpacyNlpEngine` from registering an inert spaCy recognizer and advertising unsupported spaCy NER entities. The new `NlpEngine.has_ner` property defaults to `True`; engines without native NER output, including `SlimSpacyNlpEngine` and `NoOpNlpEngine`, explicitly return `False` so Presidio skips NLP recognizer registration. - `PhoneRecognizer.DEFAULT_SUPPORTED_REGIONS` used `"UK"`, which is not a valid `phonenumbers` (libphonenumber) region code — region codes are ISO 3166-1 alpha-2, where the United Kingdom is `"GB"`. The `"UK"` entry was a no-op, so UK numbers in national/local format (e.g. `020 7946 0958`) were never detected by default; only international-format `+44 …` numbers matched, because they carry the country code and match under any region. Replaced `"UK"` with `"GB"`. - Language model recognizers (`BasicLangExtractRecognizer`, `AzureOpenAILangExtractRecognizer`) configured in a recognizer registry YAML now honour `config_path` (and other recognizer-specific kwargs). Previously these entries were validated by the strict `PredefinedRecognizerConfig` schema, which has no `config_path` field and does not allow extra keys, so `config_path` was silently dropped and the recognizer fell back to its bundled default model configuration. Added a `LangExtractRecognizerConfig` model (`extra="allow"`) and registered both recognizer class names in `CONFIG_MODEL_MAP`. - `BasicLangExtractRecognizer` now honours values under `langextract.model.provider.language_model_params` (including `timeout` and `num_ctx`). Previously these were silently dropped because `langextract.extract()` ignores its `language_model_params` argument when a pre-built `ModelConfig` is passed via `config=`, causing Ollama-backed recognizers to fall back to langextract's 120s default regardless of the configured timeout. The recognizer now merges `language_model_params` into `ModelConfig.provider_kwargs`, which is the path that reaches the provider constructor. Explicit entries under `provider.kwargs:` still take precedence. Also fixed a `TypeError` when `kwargs:` or `language_model_params:` is `null` in the YAML. (#1943, Thanks @lsternlicht) diff --git a/docs/analyzer/analyzer_engine_provider.md b/docs/analyzer/analyzer_engine_provider.md index 5dd9c40d2..585387b27 100644 --- a/docs/analyzer/analyzer_engine_provider.md +++ b/docs/analyzer/analyzer_engine_provider.md @@ -153,9 +153,9 @@ The default configuration of `AnalyzerEngine` is defined in the following files: In general, recognizers that are not added to the configuration would not be created, with one exception. ### Enabling/Disabling the NLP recognizer -One exception to this is the recognizer which extracts the `NlpEngine` entities (e.g. `SpacyRecognizer` when the `NlpEngine` is `SpacyNlpEngine`; `TransformersRecognizer` when the engine is `TransformersNlpEngine` and `StanzaRecognizer` when the engine is `StanzaNlpEngine`). +One exception to this is the recognizer which extracts the `NlpEngine` entities when the engine provides native NER output (e.g. `SpacyRecognizer` when the `NlpEngine` is `SpacyNlpEngine`; `TransformersRecognizer` when the engine is `TransformersNlpEngine` and `StanzaRecognizer` when the engine is `StanzaNlpEngine`). -Recognizers (including the NLP recognizer) could be disabled by defining `enabled=false` in the YAML configuration. For example: +Recognizers (including the NLP recognizer) could be disabled by defining `enabled: false` in the YAML configuration. For example: ```yaml recognizer_registry: global_regex_flags: 26 diff --git a/docs/analyzer/customizing_nlp_models.md b/docs/analyzer/customizing_nlp_models.md index 8713f9864..e13f517ed 100644 --- a/docs/analyzer/customizing_nlp_models.md +++ b/docs/analyzer/customizing_nlp_models.md @@ -15,11 +15,26 @@ In addition, other types of NLP frameworks [can be integrated into Presidio](dev - [spaCy or stanza](nlp_engines/spacy_stanza.md) - [transformers](nlp_engines/transformers.md) +## Declaring NER support for a custom NLP engine + +The `NlpEngine.has_ner` property indicates whether an engine produces native +NER output. The base implementation returns `True`. Engines without native NER +output should override the property and return `False`; otherwise, they are +treated as NER-capable: + +```python +@property +def has_ner(self) -> bool: + return False +``` + +Presidio uses this property to determine whether to register an NLP recognizer. + ## Configure Presidio to use the new model Configuration can be done in two ways: -- **Via code**: Create an `NlpEngine` using the `NlpEnginerProvider` class, and pass it to the `AnalyzerEngine` as input: +- **Via code**: Create an `NlpEngine` using the `NlpEngineProvider` class, and pass it to the `AnalyzerEngine` as input: ```python from presidio_analyzer import AnalyzerEngine, RecognizerRegistry diff --git a/docs/analyzer/index.md b/docs/analyzer/index.md index 1ebd34443..af9c3191d 100644 --- a/docs/analyzer/index.md +++ b/docs/analyzer/index.md @@ -92,6 +92,7 @@ classDiagram } class NlpEngine { + +bool has_ner +process_text(text, language) NlpArtifacts +process_batch(texts, language) Iterator[NlpArtifacts] } diff --git a/docs/analyzer/languages.md b/docs/analyzer/languages.md index 4316c112c..ec27d1237 100644 --- a/docs/analyzer/languages.md +++ b/docs/analyzer/languages.md @@ -6,7 +6,7 @@ In its default configuration, it contains recognizers and models for English. To extend Presidio to detect PII in an additional language, these modules require modification: 1. The `NlpEngine` containing the NLP model which performs tokenization, -lemmatization, Named Entity Recognition and other NLP tasks. +lemmatization, Named Entity Recognition (if supported), and other NLP tasks. 2. PII recognizers (different `EntityRecognizer` objects) should be adapted or created. !!! note "Note" diff --git a/docs/samples/python/customizing_presidio_analyzer.ipynb b/docs/samples/python/customizing_presidio_analyzer.ipynb index 06767a9d5..9dffac4dc 100644 --- a/docs/samples/python/customizing_presidio_analyzer.ipynb +++ b/docs/samples/python/customizing_presidio_analyzer.ipynb @@ -470,7 +470,7 @@ "### Example 5: Supporting new languages\n", "\n", "Two main parts in Presidio handle the text, and should be adapted if a new language is required:\n", - "1. The `NlpEngine` containing the NLP model which performs tokenization, lemmatization, Named Entity Recognition and other NLP tasks.\n", + "1. The `NlpEngine` containing the NLP model which performs tokenization, lemmatization, Named Entity Recognition (if supported), and other NLP tasks.\n", "2. The different PII recognizers (`EntityRecognizer` objects) should be adapted or created." ] }, diff --git a/docs/tutorial/05_languages.md b/docs/tutorial/05_languages.md index 6fee83a4e..188af273d 100644 --- a/docs/tutorial/05_languages.md +++ b/docs/tutorial/05_languages.md @@ -2,7 +2,7 @@ Two main parts in Presidio handle the text, and should be adapted if a new language is required: -1. The `NlpEngine` containing the NLP model which performs tokenization, lemmatization, Named Entity Recognition and other NLP tasks. +1. The `NlpEngine` containing the NLP model which performs tokenization, lemmatization, Named Entity Recognition (if supported), and other NLP tasks. 2. The different PII recognizers (`EntityRecognizer` objects) should be adapted or created. ## Adapting the NLP engine diff --git a/presidio-analyzer/presidio_analyzer/nlp_engine/nlp_engine.py b/presidio-analyzer/presidio_analyzer/nlp_engine/nlp_engine.py index 0e9c6d9f8..dc966be14 100644 --- a/presidio-analyzer/presidio_analyzer/nlp_engine/nlp_engine.py +++ b/presidio-analyzer/presidio_analyzer/nlp_engine/nlp_engine.py @@ -12,6 +12,16 @@ class NlpEngine(ABC): on tokens. """ + @property + def has_ner(self) -> bool: + """Return whether this engine performs Named Entity Recognition natively. + + The default is ``True``. Engines without native NER output should + override this property and return ``False``; otherwise, they are + treated as NER-capable. + """ + return True + @abstractmethod def load(self) -> None: """Load the NLP model.""" diff --git a/presidio-analyzer/presidio_analyzer/nlp_engine/no_op_nlp_engine.py b/presidio-analyzer/presidio_analyzer/nlp_engine/no_op_nlp_engine.py index cfb016143..4cac0a772 100644 --- a/presidio-analyzer/presidio_analyzer/nlp_engine/no_op_nlp_engine.py +++ b/presidio-analyzer/presidio_analyzer/nlp_engine/no_op_nlp_engine.py @@ -23,6 +23,11 @@ class NoOpNlpEngine(NlpEngine): engine_name = "no_op" is_available = True + @property + def has_ner(self) -> bool: + """Return False because this engine produces no NER output.""" + return False + def __init__( self, models: List[Dict[str, str]], diff --git a/presidio-analyzer/presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py b/presidio-analyzer/presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py index 075bb4902..49a5eccad 100644 --- a/presidio-analyzer/presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py +++ b/presidio-analyzer/presidio_analyzer/nlp_engine/slim_spacy_nlp_engine.py @@ -64,6 +64,11 @@ class SlimSpacyNlpEngine(NlpEngine): engine_name = "slim" is_available = bool(spacy) + @property + def has_ner(self) -> bool: + """Return False because the slim pipeline does not include NER.""" + return False + def __init__( self, models: Optional[List[Dict[str, str]]] = None, diff --git a/presidio-analyzer/presidio_analyzer/nlp_engine/spacy_nlp_engine.py b/presidio-analyzer/presidio_analyzer/nlp_engine/spacy_nlp_engine.py index 8ec293be7..530d2464a 100644 --- a/presidio-analyzer/presidio_analyzer/nlp_engine/spacy_nlp_engine.py +++ b/presidio-analyzer/presidio_analyzer/nlp_engine/spacy_nlp_engine.py @@ -28,6 +28,11 @@ class SpacyNlpEngine(NlpEngine): engine_name = "spacy" is_available = bool(spacy) + @property + def has_ner(self) -> bool: + """Returns True if this engine performs Named Entity Recognition natively.""" + return True + def __init__( self, models: Optional[List[Dict[str, str]]] = None, diff --git a/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry.py b/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry.py index 130b5a44f..1f5ce21bb 100644 --- a/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry.py +++ b/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry.py @@ -9,8 +9,6 @@ from presidio_analyzer import EntityRecognizer, PatternRecognizer from presidio_analyzer.nlp_engine import ( NlpEngine, - NoOpNlpEngine, - SpacyNlpEngine, StanzaNlpEngine, TransformersNlpEngine, ) @@ -59,7 +57,7 @@ def validate_nlp_engine_compatibility( self, nlp_engine: Optional[NlpEngine] ) -> None: """Validate that registered recognizers can use the selected NLP engine.""" - if not isinstance(nlp_engine, NoOpNlpEngine): + if nlp_engine is None or nlp_engine.has_ner: return nlp_recognizers = [ @@ -68,8 +66,9 @@ def validate_nlp_engine_compatibility( if nlp_recognizers: names = sorted({rec.__class__.__name__ for rec in nlp_recognizers}) raise ValueError( - "NoOpNlpEngine cannot be used with NLP engine recognizers. " - f"Remove or disable these recognizers: {names}." + f"{nlp_engine.__class__.__name__} does not provide NER output " + f"required by these recognizers: {names}. " + "Remove or disable them." ) def _create_nlp_recognizer( @@ -79,7 +78,7 @@ def _create_nlp_recognizer( ) -> SpacyRecognizer: nlp_recognizer = self.get_nlp_recognizer(nlp_engine) - if nlp_engine: + if nlp_engine is not None: return nlp_recognizer( supported_language=supported_language, supported_entities=nlp_engine.get_supported_entities(), @@ -87,20 +86,27 @@ def _create_nlp_recognizer( return nlp_recognizer(supported_language=supported_language) - def add_nlp_recognizer(self, nlp_engine: NlpEngine) -> None: + def add_nlp_recognizer(self, nlp_engine: Optional[NlpEngine]) -> None: """ - Adding NLP recognizer in accordance with the nlp engine. + Add an NLP recognizer when the engine provides native NER output. + + For engines without NER output, validate compatibility and skip + registration. :param nlp_engine: The NLP engine. :return: None """ - if isinstance(nlp_engine, NoOpNlpEngine): + if nlp_engine is not None and not nlp_engine.has_ner: self.validate_nlp_engine_compatibility(nlp_engine) - logger.info("Skipping NLP recognizer registration for no-op NLP engine.") + logger.debug( + "Skipping NLP recognizer registration for %s because it does not " + "provide NER output.", + nlp_engine.__class__.__name__, + ) return - if not nlp_engine: + if nlp_engine is None: supported_languages = self.supported_languages else: supported_languages = nlp_engine.get_supported_languages() @@ -117,7 +123,7 @@ def add_nlp_recognizer(self, nlp_engine: NlpEngine) -> None: def load_predefined_recognizers( self, languages: Optional[List[str]] = None, - nlp_engine: NlpEngine = None, + nlp_engine: Optional[NlpEngine] = None, countries: Optional[List[str]] = None, ) -> None: """ @@ -164,25 +170,22 @@ def load_predefined_recognizers( @staticmethod def get_nlp_recognizer( - nlp_engine: NlpEngine, + nlp_engine: Optional[NlpEngine], ) -> Type[SpacyRecognizer]: - """Return the recognizer leveraging the selected NLP Engine.""" + """Return the recognizer leveraging the selected NLP Engine. + + :raises ValueError: If the engine does not provide NER output. + """ + if nlp_engine is not None and not nlp_engine.has_ner: + raise ValueError( + f"{nlp_engine.__class__.__name__} does not have an NLP recognizer" + ) if isinstance(nlp_engine, StanzaNlpEngine): return StanzaRecognizer if isinstance(nlp_engine, TransformersNlpEngine): return TransformersRecognizer - if isinstance(nlp_engine, NoOpNlpEngine): - raise ValueError("NoOpNlpEngine does not have an NLP recognizer") - if not nlp_engine or isinstance(nlp_engine, SpacyNlpEngine): - return SpacyRecognizer - else: - logger.warning( - "nlp engine should be either SpacyNlpEngine," - "StanzaNlpEngine or TransformersNlpEngine" - ) - # Returning default - return SpacyRecognizer + return SpacyRecognizer def get_recognizers( self, diff --git a/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry_provider.py b/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry_provider.py index cfebe00de..2ef7d8b97 100644 --- a/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry_provider.py +++ b/presidio-analyzer/presidio_analyzer/recognizer_registry/recognizer_registry_provider.py @@ -7,7 +7,7 @@ from presidio_analyzer import EntityRecognizer from presidio_analyzer.input_validation import ConfigurationValidator -from presidio_analyzer.nlp_engine import NlpEngine, NoOpNlpEngine +from presidio_analyzer.nlp_engine import NlpEngine from presidio_analyzer.predefined_recognizers import SpacyRecognizer from presidio_analyzer.recognizer_registry import RecognizerRegistry from presidio_analyzer.recognizer_registry.recognizers_loader_utils import ( @@ -101,8 +101,10 @@ def __update_based_on_nlp_recognizer_conf( The method adds the NLP recognizer to the list of recognizers if it is not already present, or removes it if it is not enabled in the configuration. - Furthermore, it checks if there are - any inconsistencies in configuration. For example: + For engines without native NER output, this method leaves the + recognizer list unchanged. + For engines with native NER output, it also checks for configuration + inconsistencies. For example: - Multiple enabled NLP recognizers in the configuration for one language. - The NLP recognizer in the configuration does not match the Nlp Engine. @@ -117,12 +119,14 @@ def __update_based_on_nlp_recognizer_conf( """ nlp_engine = self.nlp_engine - if not nlp_engine: + if nlp_engine is None: return - if isinstance(nlp_engine, NoOpNlpEngine): - logger.info( - "Skipping NLP recognizer configuration updates for no-op NLP engine." + if not nlp_engine.has_ner: + logger.debug( + "Skipping NLP recognizer configuration updates for %s because it " + "does not provide NER output.", + nlp_engine.__class__.__name__, ) return @@ -160,9 +164,9 @@ def __update_based_on_nlp_recognizer_conf_and_lang( f"NLP recognizer (e.g. SpacyRecognizer, StanzaRecognizer) " f"is not in the list of recognizers " f"for language {language}. " - f"Adding the default recognizer to the list." + f"Adding the default recognizer to the list. " f"If you wish to remove the NLP recognizer, " - f"define it as `enabled=false`." + f"define it as `enabled: false`." ) logger.warning(warning_text) warnings.warn(warning_text) diff --git a/presidio-analyzer/tests/test_no_op_nlp_engine.py b/presidio-analyzer/tests/test_no_op_nlp_engine.py index c6a979bbd..7e979f18b 100644 --- a/presidio-analyzer/tests/test_no_op_nlp_engine.py +++ b/presidio-analyzer/tests/test_no_op_nlp_engine.py @@ -25,6 +25,9 @@ def no_op_nlp_engine(): class TestNoOpNlpEngine: + def test_has_ner_is_false(self, no_op_nlp_engine): + assert no_op_nlp_engine.has_ner is False + def test_when_init_without_models_then_raises(self): with pytest.raises(TypeError): NoOpNlpEngine() @@ -285,7 +288,10 @@ def test_when_analyzer_uses_no_op_with_registered_nlp_recognizer_then_raises( recognizers=[SpacyRecognizer()], supported_languages=["en"] ) - with pytest.raises(ValueError, match="NoOpNlpEngine cannot be used"): + with pytest.raises( + ValueError, + match="NoOpNlpEngine does not provide NER output", + ): AnalyzerEngine(registry=registry, nlp_engine=no_op_nlp_engine) def test_when_get_nlp_recognizer_with_no_op_then_raises(self, no_op_nlp_engine): @@ -428,5 +434,8 @@ def test_when_provider_uses_no_op_with_nlp_recognizer_then_raises(self, tmp_path provider = AnalyzerEngineProvider(analyzer_engine_conf_file=analyzer_conf_file) - with pytest.raises(ValueError, match="NoOpNlpEngine cannot be used"): + with pytest.raises( + ValueError, + match="NoOpNlpEngine does not provide NER output", + ): provider.create_engine() diff --git a/presidio-analyzer/tests/test_recognizer_registry.py b/presidio-analyzer/tests/test_recognizer_registry.py index a7b2d596b..a90157381 100644 --- a/presidio-analyzer/tests/test_recognizer_registry.py +++ b/presidio-analyzer/tests/test_recognizer_registry.py @@ -11,7 +11,9 @@ PatternRecognizer, RecognizerRegistry, ) +from presidio_analyzer.nlp_engine import TransformersNlpEngine from presidio_analyzer.predefined_recognizers import SpacyRecognizer, UsSsnRecognizer +from tests.mocks import NlpEngineMock def create_mock_pattern_recognizer(lang, entity, name): @@ -49,6 +51,25 @@ def mock_recognizer_registry(): ) +def test_when_custom_engine_has_ner_then_spacy_recognizer_is_returned_without_warning( + caplog, +): + recognizer = RecognizerRegistry.get_nlp_recognizer(NlpEngineMock()) + + assert recognizer is SpacyRecognizer + assert not caplog.records + + +def test_when_known_engine_type_has_ner_false_then_get_nlp_recognizer_raises(): + class TransformersNlpEngineWithoutNer(TransformersNlpEngine): + @property + def has_ner(self) -> bool: + return False + + with pytest.raises(ValueError, match="does not have an NLP recognizer"): + RecognizerRegistry.get_nlp_recognizer(TransformersNlpEngineWithoutNer()) + + def test_when_get_recognizers_then_all_recognizers_returned(mock_recognizer_registry): registry = mock_recognizer_registry registry.load_predefined_recognizers() diff --git a/presidio-analyzer/tests/test_slim_spacy_nlp_engine.py b/presidio-analyzer/tests/test_slim_spacy_nlp_engine.py index 3ce2e08e2..d150fd935 100644 --- a/presidio-analyzer/tests/test_slim_spacy_nlp_engine.py +++ b/presidio-analyzer/tests/test_slim_spacy_nlp_engine.py @@ -2,11 +2,17 @@ import pytest +from presidio_analyzer import AnalyzerEngine, Pattern, PatternRecognizer from presidio_analyzer.nlp_engine import ( NlpEngineProvider, SlimSpacyNlpEngine, ) from presidio_analyzer.nlp_engine.slim_spacy_nlp_engine import DEFAULT_SLIM_MODELS +from presidio_analyzer.predefined_recognizers import SpacyRecognizer +from presidio_analyzer.recognizer_registry import ( + RecognizerRegistry, + RecognizerRegistryProvider, +) @pytest.fixture(scope="module") @@ -271,6 +277,9 @@ def test_when_not_punct_then_returns_false(self, slim_nlp_engine): class TestSlimSpacyNlpEngineSupportedEntitiesAndLanguages: """Tests for supported entities and languages.""" + def test_has_ner_is_false(self, slim_nlp_engine): + assert slim_nlp_engine.has_ner is False + def test_when_get_supported_entities_then_empty(self, slim_nlp_engine): assert slim_nlp_engine.get_supported_entities() == [] @@ -283,6 +292,81 @@ def test_when_get_supported_languages_not_loaded_then_raises(self): engine.get_supported_languages() +class TestSlimSpacyNlpEngineRegistryIntegration: + """Tests for registering a slim engine with analyzer registries.""" + + def test_when_registry_adds_slim_engine_then_spacy_recognizer_is_not_added( + self, slim_nlp_engine + ): + registry = RecognizerRegistry(supported_languages=["en"]) + + registry.add_nlp_recognizer(slim_nlp_engine) + + assert registry.recognizers == [] + + def test_when_provider_uses_slim_engine_then_no_spacy_entities_are_advertised( + self, slim_nlp_engine + ): + provider = RecognizerRegistryProvider( + registry_configuration={ + "supported_languages": ["en"], + "recognizers": [{"name": "CreditCardRecognizer", "type": "predefined"}], + }, + nlp_engine=slim_nlp_engine, + ) + + registry = provider.create_recognizer_registry() + + assert not any( + isinstance(recognizer, SpacyRecognizer) + for recognizer in registry.recognizers + ) + assert registry.get_supported_entities(languages=["en"]) == ["CREDIT_CARD"] + + def test_when_get_nlp_recognizer_with_slim_engine_then_raises( + self, slim_nlp_engine + ): + with pytest.raises(ValueError, match="does not have an NLP recognizer"): + RecognizerRegistry.get_nlp_recognizer(slim_nlp_engine) + + def test_when_analyzer_uses_slim_with_spacy_recognizer_then_raises( + self, slim_nlp_engine + ): + registry = RecognizerRegistry( + recognizers=[SpacyRecognizer()], supported_languages=["en"] + ) + + with pytest.raises( + ValueError, + match="SlimSpacyNlpEngine does not provide NER output", + ): + AnalyzerEngine(registry=registry, nlp_engine=slim_nlp_engine) + + def test_when_analyzer_uses_slim_then_in_text_context_enhances_score( + self, slim_nlp_engine + ): + zip_recognizer = PatternRecognizer( + supported_entity="ZIP", + patterns=[Pattern(name="zip", regex=r"\b\d{5}\b", score=0.3)], + context=["zip"], + ) + registry = RecognizerRegistry( + recognizers=[zip_recognizer], supported_languages=["en"] + ) + analyzer = AnalyzerEngine(registry=registry, nlp_engine=slim_nlp_engine) + + results = analyzer.analyze( + text="my zip code is 10023", + language="en", + entities=["ZIP"], + return_decision_process=True, + ) + + assert len(results) == 1 + assert results[0].score > 0.3 + assert results[0].analysis_explanation.supportive_context_word == "zip" + + class TestSlimSpacyNlpEngineProvider: """Tests for creating slim engine via NlpEngineProvider.""" diff --git a/presidio-analyzer/tests/test_spacy_nlp_engine.py b/presidio-analyzer/tests/test_spacy_nlp_engine.py index 152032c1c..6f6cc2f62 100644 --- a/presidio-analyzer/tests/test_spacy_nlp_engine.py +++ b/presidio-analyzer/tests/test_spacy_nlp_engine.py @@ -14,6 +14,10 @@ def default(self, obj): return json.JSONEncoder.default(self, obj) +def test_has_ner_is_true(): + assert SpacyNlpEngine().has_ner is True + + def test_simple_process_text(spacy_nlp_engine): nlp_artifacts = spacy_nlp_engine.process_text("simple text", language="en") assert len(nlp_artifacts.tokens) == 2