コード例 #1
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_scale(self) -> None:
        sr_2 = SubRecipe(Ingredient(SVS("spam"), Quantity(2)),
                         (SVS([2, "spams"]), ))
        sr_6 = SubRecipe(Ingredient(SVS("spam"), Quantity(6)),
                         (SVS([6, "spams"]), ))

        assert sr_2.scale(3) == sr_6
コード例 #2
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_scale(self) -> None:
        sr_2 = SubRecipe(Ingredient(SVS("spam"), Quantity(2)),
                         (SVS([2, " blocks of spam"]), ))
        sr_6 = SubRecipe(Ingredient(SVS("spam"), Quantity(6)),
                         (SVS([6, " blocks of spam"]), ))

        assert Reference(sr_2, 0).scale(3) == Reference(sr_6, 0)
        assert Reference(sr_2, 0, Quantity(100, "g")).scale(3) == Reference(
            sr_6,
            0,
            Quantity(300, "g"),
        )
コード例 #3
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_substitute(self) -> None:
        a = Ingredient(SVS("a"))
        b = SubRecipe(a, (SVS("b"), ))
        c = Ingredient(SVS("c"))
        d = SubRecipe(c, (SVS("d"), ))

        orig = Reference(b, 0)

        assert orig.substitute(a, c) == Reference(SubRecipe(c, (SVS("b"), )),
                                                  0)
        assert orig.substitute(b, d) == Reference(SubRecipe(c, (SVS("d"), )),
                                                  0)
        assert orig.substitute(orig, c) == c
コード例 #4
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_child_assertion_checked(self) -> None:
        ingredient = Ingredient(SVS("spam"))
        singleton_sub_recipe = SubRecipe(Ingredient(SVS("eggs")),
                                         (SVS("foo"), ))
        multiple_sub_recipe = SubRecipe(Ingredient(SVS("foo")),
                                        (SVS("bar"), SVS("baz")))

        # Should work
        SubRecipe(ingredient, (SVS("foo"), ))
        SubRecipe(singleton_sub_recipe, (SVS("foo"), ))

        # Cannot have child with multiple outputs
        with pytest.raises(MultiOutputSubRecipeUsedAsNonRootNodeError):
            SubRecipe(multiple_sub_recipe, (SVS("foo"), ))
コード例 #5
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_scale(self) -> None:
        sr_2 = SubRecipe(Ingredient(SVS("spam"), Quantity(2)), (SVS("spam"), ))
        ref_sr_2 = Reference(sr_2)

        sr_6 = SubRecipe(Ingredient(SVS("spam"), Quantity(6)), (SVS("spam"), ))
        ref_sr_6 = Reference(sr_6)

        first_rec_2 = Recipe((sr_2, ))
        second_rec_2 = Recipe((ref_sr_2, ), follows=first_rec_2)

        first_rec_6 = Recipe((sr_6, ))
        second_rec_6 = Recipe((ref_sr_6, ), follows=first_rec_6)

        assert first_rec_2.scale(3) == first_rec_6
        assert second_rec_2.scale(3) == second_rec_6
コード例 #6
0
    def test_separate_recipes(self) -> None:
        compiled = compile_markdown(
            dedent("""
                Fried egg:

                ```recipe
                1 egg
                ```

                ```recipe
                fry(egg)
                ```

                Boiled egg:

                ```new-recipe
                2 egg
                ```

                ```recipe
                boil(egg)
                ```
                """).strip())

        e1 = SubRecipe(
            Ingredient(SVS("egg"), Quantity(1)),
            (SVS("egg"), ),
            show_output_names=False,
        )
        r1 = Recipe((e1, ))
        r2 = Recipe((Step(SVS("fry"), (Reference(e1), )), ), follows=r1)

        e2 = SubRecipe(
            Ingredient(SVS("egg"), Quantity(2)),
            (SVS("egg"), ),
            show_output_names=False,
        )
        r3 = Recipe((e2, ))
        r4 = Recipe((Step(SVS("boil"), (Reference(e2), )), ), follows=r3)

        assert compiled.recipes == [[r1, r2], [r3, r4]]

        # Check anchor IDs are unique too
        html = compiled.render()
        assert 'id="recipe-egg"' in html
        assert 'href="#recipe-egg"' in html
        assert 'id="recipe2-egg"' in html
        assert 'href="#recipe2-egg"' in html
コード例 #7
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_nested_reference_to_sub_recipe_not_in_recipe(self) -> None:
        external_sr = SubRecipe(Ingredient(SVS("eggs")), (SVS("foo"), ))
        ref = Reference(external_sr)
        step = Step(SVS("bar"), (ref, ))

        with pytest.raises(ReferenceToInvalidSubRecipeError):
            Recipe((step, ))
コード例 #8
0
ファイル: test_html.py プロジェクト: mossblaser/recipe_grid
 def test_multiple_output_hidden_subrecipe(self) -> None:
     assert render_recipe_tree(
         SubRecipe(
             Ingredient(SVS("spam")),
             (SVS("spam"), SVS("tin")),
             show_output_names=False,
         ),
         "qux-",
     ) == (""
           '<table class="rg-table">\n'
           "  <tr>\n"
           '    <td class="rg-ingredient '
           "rg-border-left-sub-recipe "
           "rg-border-right-sub-recipe "
           "rg-border-top-sub-recipe "
           'rg-border-bottom-sub-recipe">spam</td>\n'
           '    <td class="rg-sub-recipe-outputs '
           "rg-border-right-none "
           "rg-border-top-none "
           'rg-border-bottom-none">\n'
           '      <ul class="rg-sub-recipe-output-list">\n'
           '        <li id="qux-spam">spam</li>\n'
           '        <li id="qux-tin">tin</li>\n'
           "      </ul>\n"
           "    </td>\n"
           "  </tr>\n"
           "</table>")
コード例 #9
0
 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")),
         )),
     ]
コード例 #10
0
    def test_recipes_split_across_blocks(self) -> None:
        compiled = compile_markdown(
            dedent("""
                A recipe in two parts. Part one:

                    sauce = boil down(tomatoes, water)

                Part two:

                    pour over(pasta, sauce)
                """).strip())

        sr = SubRecipe(
            Step(
                SVS("boil down"),
                (
                    Ingredient(SVS("tomatoes")),
                    Ingredient(SVS("water")),
                ),
            ),
            (SVS("sauce"), ),
        )
        r1 = Recipe((sr, ))
        r2 = Recipe(
            (Step(
                SVS("pour over"),
                (
                    Ingredient(SVS("pasta")),
                    Reference(sr),
                ),
            ), ),
            follows=r1,
        )

        assert compiled.recipes == [[r1, r2]]
コード例 #11
0
 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), ))
     ]
コード例 #12
0
ファイル: test_html.py プロジェクト: mossblaser/recipe_grid
def test_generate_subrecipe_output_id() -> None:
    sub_recipe = SubRecipe(
        Ingredient(SVS("spam")),
        (SVS("foo"), SVS(["foo bar ", 123, " baz?"])),
    )
    assert generate_subrecipe_output_id(sub_recipe, 0, "qux-") == "qux-foo"
    assert generate_subrecipe_output_id(sub_recipe, 1,
                                        "qux-") == "qux-foo-bar-123-baz"
コード例 #13
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_reference_to_nested_sub_recipe(self) -> None:
        nested_sr = SubRecipe(Ingredient(SVS("eggs")), (SVS("foo"), ))
        step = Step(SVS("scramble"), (nested_sr, ))

        ref = Reference(nested_sr)

        with pytest.raises(ReferenceToInvalidSubRecipeError):
            Recipe((step, ref))
コード例 #14
0
 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),
         )),
     ]
コード例 #15
0
 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,
         ), )),
     ]
コード例 #16
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,
     ]
コード例 #17
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,
         ), ))
     ]
コード例 #18
0
ファイル: test_html.py プロジェクト: mossblaser/recipe_grid
def test_render_sub_recipe_outputs() -> None:
    assert render_sub_recipe_outputs(
        SubRecipe(Ingredient(SVS("spam")),
                  (SVS("foo"), SVS("bar"), SVS("baz"))),
        "sub-recipe-",
    ) == ('<ul class="rg-sub-recipe-output-list">\n'
          '  <li id="sub-recipe-foo">foo</li>\n'
          '  <li id="sub-recipe-bar">bar</li>\n'
          '  <li id="sub-recipe-baz">baz</li>\n'
          "</ul>")
コード例 #19
0
ファイル: test_html.py プロジェクト: mossblaser/recipe_grid
 def test_subrecipe_outputs(self) -> None:
     assert render_cell(
         Cell(SubRecipe(Ingredient(SVS("spam")),
                        (SVS("foo"), SVS("bar"))))) == (
                            '<td class="rg-sub-recipe-outputs">\n'
                            '  <ul class="rg-sub-recipe-output-list">\n'
                            '    <li id="sub-recipe-foo">foo</li>\n'
                            '    <li id="sub-recipe-bar">bar</li>\n'
                            "  </ul>\n"
                            "</td>")
コード例 #20
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),
             ))
         ]
コード例 #21
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_name_validation(self) -> None:
        sr = SubRecipe(Ingredient(SVS("spam")), (SVS("foo"), SVS("bar")))

        # Should work
        Reference(sr)
        Reference(sr, 0)
        Reference(sr, 1)

        # Unknown name
        with pytest.raises(OutputIndexError):
            Reference(sr, 2)
コード例 #22
0
 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")),
             ),
         ), ))
     ]
コード例 #23
0
ファイル: test_html.py プロジェクト: mossblaser/recipe_grid
 def test_single_output_hidden_subrecipe(self) -> None:
     assert render_recipe_tree(
         SubRecipe(Ingredient(SVS("spam")), (SVS("spam"), ),
                   show_output_names=False),
         "qux-",
     ) == ('<table class="rg-table" id="qux-spam">'
           "<tr>"
           '<td class="rg-ingredient '
           "rg-border-left-sub-recipe "
           "rg-border-right-sub-recipe "
           "rg-border-top-sub-recipe "
           'rg-border-bottom-sub-recipe">spam</td>'
           "</tr>"
           "</table>")
コード例 #24
0
ファイル: test_recipe.py プロジェクト: mossblaser/recipe_grid
    def test_substitute(self) -> None:
        a = Ingredient(SVS("a"))
        b = Ingredient(SVS("b"))
        c = Ingredient(SVS("c"))

        orig = SubRecipe(a, (SVS("foo"), ))

        assert orig.substitute(a, b) == SubRecipe(b, (SVS("foo"), ))
        assert orig.substitute(orig, b) == b
        assert orig.substitute(c, b) == orig
コード例 #25
0
def test_single_output_sub_recipe_hidden() -> None:
    ingredient = Ingredient(SVS("spam"))
    step = Step(SVS("fry"), (ingredient, ))
    sub_recipe = SubRecipe(step, (SVS("out"), ), show_output_names=False)

    assert recipe_tree_to_table(sub_recipe) == set_border_around_table(
        Table.from_dict(
            cast(
                Mapping[Tuple[int, int], Cell[RecipeTreeNode]],
                {
                    (0, 0): Cell(ingredient),
                    (0, 1): Cell(step)
                },
            )),
        BorderType.sub_recipe,
    )
コード例 #26
0
    def _compile_stmt(self, ast_stmt: ast.Stmt) -> RecipeTreeNode:
        # Compile the recipe tree (the RHS of the statement)
        recipe_tree: RecipeTreeNode = self._compile_expr(ast_stmt.expr)

        # Determine the output names, if present (e.g. from the LHS of the statement)
        output_names: Tuple[ScaledValueString, ...] = ()
        output_name_inferred = False
        if ast_stmt.outputs:
            output_names = tuple(
                compile_string(output_name)
                for output_name in ast_stmt.outputs)
        else:
            inferred_output_name = infer_output_name(recipe_tree)
            if inferred_output_name is not None:
                output_name_inferred = True
                output_names = (inferred_output_name, )

        # Wrap tree in SubRecipe when named outputs have been used
        if output_names:
            recipe_tree = SubRecipe(
                sub_tree=recipe_tree,
                output_names=output_names,
                show_output_names=not output_name_inferred,
            )

            for output_index, output_name in enumerate(output_names):
                normalised_output_name = normalise_output_name(output_name)

                # Check for name conflicts
                if normalised_output_name in self._named_outputs:
                    assert ast_stmt.outputs  # For benefit of MyPy
                    raise NameRedefinedError.from_output_name(
                        self._sources[self._current_recipe_index],
                        ast_stmt.outputs[output_index],
                    )

                # Add to LUT of outputs to recipe trees
                self._named_outputs[normalised_output_name] = NamedOutput(
                    name=output_name,
                    definition_recipe_index=self._current_recipe_index,
                    sub_recipe=recipe_tree,
                    output_index=output_index,
                    unwrap_subrecipe_when_inlined=not ast_stmt.named,
                )

        return recipe_tree
コード例 #27
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]
コード例 #28
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")),
                 ),
             ),
         ))
     ]
コード例 #29
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")),
                 ),
             ),
         ))
     ]
コード例 #30
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]