Repository: hhursev/recipe-scrapers (python)

Issue:
Plugins: surprising behaviour from schema.org fill plugin when implementation and metadata are both absent
The `lekkerensimpel` scraper includes test coverage where some test webpages contain `schema.org` recipe metadata, and some do not.

If we temporarily remove the `category` implementation -- as in the case during development of #1228 -- then we uncover a surprising change-in-behaviour: pages that do contain recipe (and category) metadata continue to return the expected result, but pages that do not now begin raising `NotImplementedError`.

That's confusing: the implementation has not changed based on the HTML input, and yet the exception seems to indicate that the method is sometimes implemented, and sometimes not.

The approximate cause appears to be this line of code: https://github.com/hhursev/recipe-scrapers/blob/141470384b9e8a486f3a14255d37e3b608d3383a/recipe_scrapers/plugins/schemaorg_fill.py#L60

Basically: if no relevant `schema.org` recipe metadata was found on the page, then regardless of whether an implementation of the method exists, we re-raise the existing exception (a `NotImplementedError`, as [raised by the abstract scraper](https://github.com/hhursev/recipe-scrapers/blob/141470384b9e8a486f3a14255d37e3b608d3383a/recipe_scrapers/_abstract.py#L117-L119)).

I think what we should probably do instead is to raise a different `SchemaOrgException` if we detect that no relevant `schema.org` recipe metadata exists -- there's no point in calling the function under those circumstances.

Pull request description:
schema.org-fill: raise a `RecipeSchemaNotFound` when no recipe metadata is found
Supports / unblocks #1228.
Resolves #1229.

Patch:
diff --git a/recipe_scrapers/__init__.py b/recipe_scrapers/__init__.py
index 361c378a..9b67ce5d 100644
--- a/recipe_scrapers/__init__.py
+++ b/recipe_scrapers/__init__.py
@@ -5,6 +5,7 @@ __all__ = (
     "ElementNotFoundInHtml",
     "FieldNotProvidedByWebsiteException",
     "NoSchemaFoundInWildMode",
+    "RecipeSchemaNotFound",
     "StaticValueException",
     "WebsiteNotImplementedError",
     "scrape_html",
@@ -26,6 +27,7 @@ from ._exceptions import (
     ElementNotFoundInHtml,
     FieldNotProvidedByWebsiteException,
     NoSchemaFoundInWildMode,
+    RecipeSchemaNotFound,
     StaticValueException,
     WebsiteNotImplementedError,
 )
diff --git a/recipe_scrapers/_exceptions.py b/recipe_scrapers/_exceptions.py
index 458e791e..680d0331 100644
--- a/recipe_scrapers/_exceptions.py
+++ b/recipe_scrapers/_exceptions.py
@@ -55,6 +55,15 @@ class SchemaOrgException(FillPluginException):
     ...
 
 
+class RecipeSchemaNotFound(SchemaOrgException):
+    """No recipe schema metadata found on the page"""
+
+    def __init__(self, url):
+        self.url = url
+        message = f"No Recipe Schema found at {self.url}."
+        super().__init__(message)
+
+
 class StaticValueException(RecipeScrapersExceptions):
     """Error to communicate that the scraper is returning a fixed/static value."""
 
diff --git a/recipe_scrapers/plugins/schemaorg_fill.py b/recipe_scrapers/plugins/schemaorg_fill.py
index d7e05e69..c2bf7ea1 100644
--- a/recipe_scrapers/plugins/schemaorg_fill.py
+++ b/recipe_scrapers/plugins/schemaorg_fill.py
@@ -1,7 +1,7 @@
 import functools
 import logging
 
-from recipe_scrapers._exceptions import FillPluginException
+from recipe_scrapers._exceptions import FillPluginException, RecipeSchemaNotFound
 from recipe_scrapers.settings import settings
 
 from ._interface import PluginInterface
@@ -57,7 +57,9 @@ class SchemaOrgFillPlugin(PluginInterface):
                 return decorated(self, *args, **kwargs)
             except (FillPluginException, NotImplementedError) as e:
                 function = getattr(self.schema, decorated.__name__)
-                if self.schema.data and function:
+                if not self.schema.data:
+                    raise RecipeSchemaNotFound(url=self.url)
+                if function:
                     logger.info(
                         f"{class_name}.{method_name}() seems to not be implemented but .schema is available! Attempting to return result from SchemaOrg."
                     )


Test patch:
diff --git a/tests/library/test_exceptions.py b/tests/library/test_exceptions.py
index 37372ae5..dd7f1077 100644
--- a/tests/library/test_exceptions.py
+++ b/tests/library/test_exceptions.py
@@ -1,7 +1,9 @@
 import unittest
 
 from recipe_scrapers import (
+    AbstractScraper,
     NoSchemaFoundInWildMode,
+    RecipeSchemaNotFound,
     WebsiteNotImplementedError,
     scrape_html,
 )
@@ -17,3 +19,13 @@ class TestExceptions(unittest.TestCase):
 
         self.assertEqual(exception.url, "example.com")
         self.assertEqual(exception.message, "No Recipe Schema found at example.com.")
+
+    def test_no_schema_found_for_fill_plugin(self):
+        class TestScraper(AbstractScraper):
+            @classmethod
+            def host(cls):
+                return "example.com"
+
+        scraper = TestScraper(html="<html></html>", url="http://example.com/recipe")
+        with self.assertRaises(RecipeSchemaNotFound):
+            scraper.category()