def test_proportion_given_for_ingredient(self) -> None:
        with pytest.raises(ProportionGivenForIngredientError) as exc_info:
            compile(["1/2 * spam"])
        assert (str(exc_info.value) == dedent("""
                At line 1 column 1:
                    1/2 * spam
                    ^
                A proportion was given (implying a sub recipe is being referenced) but no sub recipe named spam exists.
            """

                                              # noqa: E501
                                              ).strip())
 def test_ingredient_with_explicit_output_name_always_shown(
         self, output_name: str) -> None:
     # NB: output name always shown even if it matches what would be the inferred name
     assert compile([f"{output_name} = spam"]) == [
         Recipe((SubRecipe(Ingredient(SVS("spam")), (SVS(output_name), ),
                           True), ))
     ]
 def test_reference_compilation(self) -> None:
     sub_recipe = SubRecipe(
         Step(SVS("open"), (Ingredient(SVS("spam")), )),
         (SVS("spam"), SVS("tin")),
         True,
     )
     assert compile([
         "spam, tin = open(spam)\nspam\n1/3*spam\n25% of the spam\nleft over spam\n2 'tin'\n50g spam"  # noqa: E501
     ]) == [
         Recipe((
             sub_recipe,
             # spam
             Reference(sub_recipe, 0, Proportion(1.0)),
             # 1/3*spam
             Reference(sub_recipe, 0,
                       Proportion(Fraction(1, 3), preposition="*")),
             # 25% of the spam
             Reference(
                 sub_recipe,
                 0,
                 Proportion(0.25, percentage=True, preposition="% of the"),
             ),
             # remaining
             Reference(sub_recipe, 0,
                       Proportion(None, remainder_wording="left over")),
             # 2 tin
             Reference(sub_recipe, 1, Quantity(2.0)),
             # 50g spam
             Reference(sub_recipe, 0, Quantity(50.0, "g")),
         )),
     ]
 def test_reference_has_no_inferred_name(self) -> None:
     sub_recipe = SubRecipe(Ingredient(SVS("spam")), (SVS("spam"), ), False)
     assert compile(["spam\nspam\nspam"]) == [
         Recipe((
             sub_recipe,
             Reference(sub_recipe, 0),
             Reference(sub_recipe, 0),
         )),
     ]
 def test_string_compilation(self) -> None:
     # Just a sanity check
     assert compile(["spam {3 eggs}"]) == [
         Recipe((SubRecipe(
             Ingredient(SVS(("spam ", 3, " eggs"))),
             (SVS(["spam ", 3, " eggs"]), ),
             False,
         ), ))
     ]
 def test_processed_ingredient_with_implied_output_name(
         self, syntax: str) -> None:
     assert compile([syntax]) == [
         Recipe((SubRecipe(
             Step(SVS("fry"), (Ingredient(SVS("spam")), )),
             (SVS("spam"), ),
             False,
         ), )),
     ]
 def test_compilation_of_steps(self) -> None:
     assert compile(["fry(slice(spam), eggs)"]) == [
         Recipe((Step(
             SVS("fry"),
             (
                 Step(SVS("slice"), (Ingredient(SVS("spam")), )),
                 Ingredient(SVS("eggs")),
             ),
         ), )),
     ]
 def test_inlining_single_references_name_explicitly_required(self) -> None:
     assert compile(["meat := spam, sliced\nfry(meat, eggs)"]) == [
         Recipe((Step(
             SVS("fry"),
             (
                 SubRecipe(
                     Step(SVS("sliced"), (Ingredient(SVS("spam")), )),
                     (SVS("meat"), ),
                 ),
                 Ingredient(SVS("eggs")),
             ),
         ), ))
     ]
 def test_inlining_single_references(self, quantity_spec: str) -> None:
     assert compile(
         [f"meat = 10g spam, sliced\nfry({quantity_spec} meat, eggs)"]) == [
             Recipe((Step(
                 SVS("fry"),
                 (
                     Step(
                         SVS("sliced"),
                         (Ingredient(SVS("spam"), Quantity(10, "g")), ),
                     ),
                     Ingredient(SVS("eggs")),
                 ),
             ), ))
         ]
Beispiel #10
0
 def test_inlines_within_inlines(self) -> None:
     recipe = Recipe((Step(
         SVS("boil"),
         (
             SubRecipe(
                 Step(
                     SVS("fry"),
                     (Ingredient(SVS("spam"), Quantity(100, "g")), ),
                 ),
                 (SVS("fried spam"), ),
             ),
             Ingredient(SVS("water")),
         ),
     ), ))
     assert compile([
         "100g spam\nfried spam := fry(spam)\nboil(fried spam, water)"
     ]) == [recipe]
Beispiel #11
0
    def render_document(self, element: Document) -> MarkdownRecipe:
        """Render the document and compile all recipe code blocks."""
        # Render the markdown document
        self.output.html = super().render_children(element)  # type: ignore

        # Compile captured recipe blocks
        markdown_source = element.text
        for recipe_source_blocks in self.independent_recipe_source_blocks:
            placeholders = list(recipe_source_blocks.keys())
            sources = [
                recipe_source_block.get_line_number_corrected_source(markdown_source)
                for recipe_source_block in recipe_source_blocks.values()
            ]
            recipes = compile(sources)
            for placeholder, recipe in zip(placeholders, recipes):
                self.output.recipe_placeholders[placeholder] = recipe

        return self.output
Beispiel #12
0
 def test_dont_inline_multi_output_subrecipes(self) -> None:
     sub_recipe = SubRecipe(
         Ingredient(SVS("spam")),
         (SVS("meat"), SVS("tin")),
         True,
     )
     assert compile(["meat, tin = spam\nfry(meat, eggs)"]) == [
         Recipe((
             sub_recipe,
             Step(
                 SVS("fry"),
                 (
                     Reference(sub_recipe, 0),
                     Ingredient(SVS("eggs")),
                 ),
             ),
         ))
     ]
Beispiel #13
0
 def test_inlining_within_a_block(self) -> None:
     recipe0 = Recipe((SubRecipe(Ingredient(SVS("egg")), (SVS("egg"), ),
                                 show_output_names=False), ))
     recipe1 = Recipe(
         (Step(SVS("fry"),
               (Ingredient(SVS("spam"), Quantity(50, "g")), )), ),
         follows=recipe0,
     )
     recipe2 = Recipe(
         (SubRecipe(Ingredient(SVS("potato")), (SVS("potato"), ),
                    show_output_names=False), ),
         follows=recipe1,
     )
     assert compile(["egg", "50g spam\nfry(spam)", "potato"]) == [
         recipe0,
         recipe1,
         recipe2,
     ]
Beispiel #14
0
 def test_dont_inline_partial_uses_of_a_subrecipe(self) -> None:
     sub_recipe = SubRecipe(
         Ingredient(SVS("spam"), Quantity(100, "g")),
         (SVS("spam"), ),
         False,
     )
     assert compile(["100g spam\nfry(50g spam, eggs)"]) == [
         Recipe((
             sub_recipe,
             Step(
                 SVS("fry"),
                 (
                     Reference(sub_recipe, 0, Quantity(50, "g")),
                     Ingredient(SVS("eggs")),
                 ),
             ),
         ))
     ]
Beispiel #15
0
 def test_dont_inline_definitions_from_earlier_blocks(self) -> None:
     sub_recipe = SubRecipe(
         Ingredient(SVS("spam"), Quantity(100, "g")),
         (SVS("spam"), ),
         False,
     )
     recipe0 = Recipe((sub_recipe, ))
     recipe1 = Recipe(
         (Step(
             SVS("fry"),
             (
                 Reference(sub_recipe, 0, Quantity(50, "g")),
                 Ingredient(SVS("eggs")),
             ),
         ), ),
         follows=recipe0,
     )
     assert compile(["100g spam",
                     "fry(50g spam, eggs)"]) == [recipe0, recipe1]
Beispiel #16
0
 def test_ingredient_compilation(self) -> None:
     assert compile(
         ["500g spam\n2 eggs\n1 kg foo\n1 can of dog food\nheat"]) == [
             Recipe((
                 # With unit
                 SubRecipe(
                     Ingredient(SVS("spam"), Quantity(500.0, "g")),
                     (SVS("spam"), ),
                     False,
                 ),
                 # No unit
                 SubRecipe(
                     Ingredient(SVS("eggs"), Quantity(2.0)),
                     (SVS("eggs"), ),
                     False,
                 ),
                 # With spacing between number and unit
                 SubRecipe(
                     Ingredient(SVS("foo"),
                                Quantity(1.0, "kg",
                                         value_unit_spacing=" ")),
                     (SVS("foo"), ),
                     False,
                 ),
                 # With spacing between number and unit
                 SubRecipe(
                     Ingredient(
                         SVS("dog food"),
                         Quantity(
                             1,
                             "can",
                             value_unit_spacing=" ",
                             preposition=" of",
                         ),
                     ),
                     (SVS("dog food"), ),
                     False,
                 ),
                 # No quantity
                 SubRecipe(Ingredient(SVS("heat")), (SVS("heat"), ), False),
             ))
         ]
Beispiel #17
0
def compile_recipes(app: Sphinx, doctree: nodes.Node) -> None:
    """Parse and compile the recipes appearing on a page."""
    env = app.env

    # Skip if no recipe sources found anywhere
    if not hasattr(env, "recipe_sources"):
        return

    docname = env.docname

    page_recipes = env.recipe_sources[docname]  # type: ignore

    for i, page_recipe_blocks in enumerate(page_recipes):
        sources = [source for source, _node in page_recipe_blocks]
        try:
            recipes = compile(sources)
        except (ParseError, RecipeCompileError) as e:
            filename = env.doc2path(docname)
            raise SphinxRecipeCompileError(
                f"Recipe compile error in {filename}: {e}")

        for (_source, node), recipe in zip(page_recipe_blocks, recipes):
            node.id_prefix = f"sub-recipe-{i}-"
            node.recipe = recipe
Beispiel #18
0
 def test_ingredient_with_implied_output_name(self) -> None:
     assert compile(["spam"]) == [
         Recipe((SubRecipe(Ingredient(SVS("spam")), (SVS("spam"), ),
                           False), ))
     ]
Beispiel #19
0
 def test_multiple_outputs(self) -> None:
     assert compile(["foo, bar = spam"]) == [
         Recipe((SubRecipe(Ingredient(SVS("spam")),
                           (SVS("foo"), SVS("bar")), True), ))
     ]
Beispiel #20
0
 def test_step_with_multiple_inputs_has_no_inferred_name(self) -> None:
     assert compile(["fry(spam, eggs)"]) == [
         Recipe(
             (Step(SVS("fry"),
                   (Ingredient(SVS("spam")), Ingredient(SVS("eggs")))), )),
     ]