def test_recipe_missing_servings(self, tmp_path: Path) -> None:
        f = tmp_path / "recipe.md"
        f.open("w").write("# A serving-less recipe")
        with pytest.raises(RecipeMissingServingsError):
            compile_recipe_markdown(f)

        # Shouldn't crash
        compile_recipe_markdown(f, require_servings=False)
 def test_recipe_compile_error(self, tmp_path: Path) -> None:
     f = tmp_path / "recipe.md"
     f.open("w").write(
         dedent("""
                 # Fail for 1
                 ```recipe
                 1/2 of undefined sub recipe
                 ```
             """))
     with pytest.raises(RecipeInDirectoryCompileError):
         compile_recipe_markdown(f)
 def test_recipe_syntax_error(self, tmp_path: Path) -> None:
     f = tmp_path / "recipe.md"
     f.open("w").write(
         dedent("""
                 # Fail for 1
                 ```recipe
                 Missing a ( closing bracket...
                 ```
             """))
     with pytest.raises(RecipeInDirectoryCompileError):
         compile_recipe_markdown(f)
    def test_cache_transparency(self, tmp_path: Path) -> None:
        f = tmp_path / "recipe.md"
        f.open("w").write("# A recipe for 2")

        r1 = compile_recipe_markdown(f)
        r2 = compile_recipe_markdown(f)

        # Result from cache
        assert r2 is r1

        # Changing the file on disk should return newly compiled output
        f.open("w").write("# A different recipe for 3")
        r3 = compile_recipe_markdown(f)
        assert r3.title == "A different recipe"
        assert r3.servings == 3
    def test_valid(self, tmp_path: Path) -> None:
        f = tmp_path / "recipe.md"
        f.open("w").write("# A recipe for 2")
        r = compile_recipe_markdown(f)

        assert r.title == "A recipe"
        assert r.servings == 2
Пример #6
0
def generate_standalone_page(
    input_file: Path,
    scale: Optional[Union[int, float, Fraction]] = None,
    servings: Optional[int] = None,
    embed_local_links: bool = True,
) -> str:
    """
    Generate a standalone page with a rendered markdown recipe.

    Parameters
    ==========
    input_file : Path
        The file containing the recipe grid markdown file to compile.
    scale : number or None
        If given, scales the recipe by the provided factor (e.g. scale=2 will
        double all quantities). Must not be given if 'servings' is used.
    servings : int or None
        If given, scales the recipe to the specified number of servings. Will
        fail if the recipe does not specify the number of servings it makes in
        its title. Must not be given if 'scale' is used.
    embed_local_links : bool
        If True, links to local files will be replaced by ``data:`` URLs with
        the referenced file contents embedded.
    """
    recipe = compile_recipe_markdown(
        input_file,
        require_title=False,
        require_servings=servings is not None,
    )

    if scale is not None:
        assert servings is None
    elif servings is not None:
        scale = Fraction(servings, recipe.servings)
    else:
        scale = 1

    recipe_html = recipe.render(scale)

    if embed_local_links:
        recipe_html = postprocess_html(
            recipe_html,
            complete_document=True,
            stages=[
                partial(
                    embed_local_links_as_data_urls,
                    source=input_file,
                    root=input_file.parent,
                ),
            ],
        )

    return standalone_recipe_template.render(
        title=recipe.title if recipe.title is not None else "Recipe",
        body=recipe_html,
    )
Пример #7
0
    def from_recipe_source(
        cls,
        servings: Optional[int],
        recipe_source: Path,
        parent: CategoryPage,
        other_scalings: MutableMapping[Optional[int], "RecipePage"],
    ) -> "RecipePage":
        """
        The ``other_scalings[servings]`` dictionary entry will be populated
        with the value returned by this function.

        For unscaled recipes (i.e. those without a serving count in the title),
        the 'servings' argument will be ignored and 'None' will be implicitly
        used instead.

        If an other_scalings entry already exists, the existing page will be
        returned (and the 'parent' argument will be ignored). For unscaled
        recipes, this means that repeated calls with different scales will all
        return the same (unscaled) recipe page. This page should later have its
        :py:attr:`parent` attribute replaced with the corresponding unscaled
        category page.
        """
        recipe = compile_recipe_markdown(recipe_source, require_servings=False)

        # Actually checked by compile_recipe_markdown; only here for type
        # checking purposes...
        assert recipe.title is not None

        if recipe.servings is None:
            servings = None
        else:
            # Sanity check
            assert servings is not None

        if servings in other_scalings:
            return other_scalings[servings]
        else:
            recipe_page = RecipePage(
                title=recipe.title,
                parent=parent,
                servings=servings,
                native_servings=recipe.servings,
                recipe_html=recipe.render(
                    Fraction(servings, recipe.servings) if servings is not None else 1
                ),
                recipe_source=recipe_source,
                other_scalings=other_scalings,
            )
            other_scalings[servings] = recipe_page
            return recipe_page