def add_doc_requirements(struct: Structure,
                         opts: ScaffoldOpts) -> ActionParams:
    """In order to build the docs new requirements are necessary now.

    The default ``tox.ini`` generated by PyScaffold should already include
    ``-e {toxinidir}/docs/requirements.txt`` in its dependencies. Therefore,
    this action will make sure ``tox -e docs`` run without problems.

    It is important to sort the requirements otherwise pre-commit will raise an error
    for a newly generated file and that would correspond to a bad user experience.
    """
    leaf = struct.get("docs", {}).get("requirements.txt")
    original, file_op = reify_leaf(leaf, opts)
    contents = original or ""

    missing = [req for req in DOC_REQUIREMENTS if req not in contents]
    requirements = [*contents.splitlines(), *missing]

    # It is not trivial to sort the requirements because they include a comment header
    j = (i for (i, line) in enumerate(requirements)
         if line and not is_commented(line))
    comments_end = next(
        j, 0)  # first element of the iterator is a non commented line
    comments = requirements[:comments_end]
    sorted_requirements = sorted(requirements[comments_end:])

    new_contents = "\n".join([*comments, *sorted_requirements]) + "\n"
    # ^  pre-commit requires a new line at the end of the file

    files: Structure = {"docs": {"requirements.txt": (new_contents, file_op)}}

    return merge(struct, files), opts
def add_dsproject(struct: Structure, opts: ScaffoldOpts) -> ActionParams:
    """Adds basic module for custom extension
    See :obj:`pyscaffold.actions.Action`
    """
    gitignore_all = (template("gitignore_all"), NO_OVERWRITE)

    files: Structure = {
        "configs": {".gitignore": ("", NO_OVERWRITE)},
        "data": {
            ".gitignore": (template("gitignore_data"), NO_OVERWRITE),
            **{
                folder: {".gitignore": gitignore_all}
                for folder in ("external", "interim", "preprocessed", "raw")
            },
        },
        "environment.yml": (template("environment_yml"), NO_OVERWRITE),
        "models": {".gitignore": gitignore_all},
        "notebooks": {"template.ipynb": (template("template_ipynb"), NO_OVERWRITE)},
        "references": {".gitignore": ("", NO_OVERWRITE)},
        "reports": {"figures": {".gitignore": ("", NO_OVERWRITE)}},
        "scripts": {
            "train_model.py": (
                template("train_model_py"),
                add_permissions(stat.S_IXUSR, NO_OVERWRITE),
            )
        },
    }

    return merge(struct, files), opts
def add_files(struct: Structure, opts: ScaffoldOpts) -> ActionParams:
    """Add custom extension files. See :obj:`pyscaffold.actions.Action`"""

    files: Structure = {
        ".github": {
            "workflows": {
                "publish-package.yml":
                (template("publish_package"), NO_OVERWRITE)
            }
        },
        "README.rst": (template("readme"), NO_OVERWRITE),
        "CONTRIBUTING.rst": (template("contributing"), NO_OVERWRITE),
        "setup.cfg": modify_setupcfg(struct["setup.cfg"], opts),
        "src": {
            opts["package"]: {
                f"{EXTENSION_FILE_NAME}.py":
                (template("extension"), NO_OVERWRITE)
            }
        },
        "tests": {
            "__init__.py": ("", NO_OVERWRITE),
            "conftest.py": (template("conftest"), NO_OVERWRITE),
            "helpers.py": (template("helpers"), NO_OVERWRITE),
            "test_custom_extension.py": (
                template("test_custom_extension"),
                NO_OVERWRITE,
            ),
        },
    }

    return merge(struct, files), opts
Ejemplo n.º 4
0
def test_empty_string_leads_to_empty_file_during_merge():
    # When the original struct contains a leaf,
    struct = {"a": {"b": "0"}}
    # and the merged struct overrides it with an empty content
    extra_files = {"a": {"b": ""}}
    struct = structure.merge(struct, extra_files)
    # then the resulting content should exist and be empty
    assert struct["a"]["b"] == ""
Ejemplo n.º 5
0
def test_merge_rules_just_in_original():
    # When an update rule exists in the original struct,
    struct = {"a": {"b": ("0", SKIP_ON_UPDATE)}}
    # but not in the merged,
    extra_files = {"a": {"b": "3"}}
    struct = structure.merge(struct, extra_files)
    # then just the content should be updated
    # and the rule should be kept identical
    assert struct["a"]["b"] == ("3", SKIP_ON_UPDATE)
Ejemplo n.º 6
0
def replace_files(struct: Structure, opts: ScaffoldOpts) -> ActionParams:
    """Replace all rst files to proper md and activate Sphinx md.
    See :obj:`pyscaffold.actions.Action`
    """
    # Define new files
    NO_OVERWRITE = no_overwrite()
    files: Structure = {
        "README.md": (template("readme"), NO_OVERWRITE),
        "AUTHORS.md": (template("authors"), NO_OVERWRITE),
        "CHANGELOG.md": (template("changelog"), NO_OVERWRITE),
        "CONTRIBUTING.md": (template("contributing"), NO_OVERWRITE),
        "docs": {
            "index.md": (template("index"), NO_OVERWRITE),
            "readme.md": (default_myst_include("README.md"), NO_OVERWRITE),
            "license.md": (template("license"), NO_OVERWRITE),
            "authors.md": (default_myst_include("AUTHORS.md"), NO_OVERWRITE),
            "changelog.md":
            (default_myst_include("CHANGELOG.md"), NO_OVERWRITE),
            "contributing.md":
            (default_myst_include("CONTRIBUTING.md"), NO_OVERWRITE),
        },
    }

    # TODO: Automatically convert RST to MD
    #
    # >>> content, file_op = reify_leaf(struct.get("CONTRIBUTING.rst"), opts)
    # >>> md_content = rst_to_myst(content or "", **RST2MYST_OPTS).text
    # >>> files["CONTRIBUTING.md"] = (md_content, file_op)
    #
    # Currently there is a problem in rst-to-myst, preventing automatic conversion:
    # https://github.com/executablebooks/rst-to-myst/issues/33#issuecomment-922264030

    # Modify pre-existing files
    content, file_op = reify_leaf(struct["setup.cfg"], opts)
    files["setup.cfg"] = (add_long_desc(content), file_op)

    content, file_op = reify_leaf(struct["docs"]["conf.py"], opts)
    files["docs"]["conf.py"] = (add_myst(content), file_op)

    # Remove all unnecessary .rst files from struct
    unnecessary = [
        "README.rst",
        "AUTHORS.rst",
        "CHANGELOG.rst",
        "CONTRIBUTING.rst",
        "docs/index.rst",
        "docs/readme.rst",
        "docs/license.rst",
        "docs/authors.rst",
        "docs/changelog.rst",
        "docs/contributing.rst",
    ]
    struct = reduce(reject, unnecessary, struct)

    return merge(struct, files), opts
Ejemplo n.º 7
0
def test_merge_basics():
    # Given an existing struct,
    struct = {"a": {"b": {"c": "1", "d": "2"}}}
    # when it is merged to another struct with some common folder
    extra_files = {"a": {"b": {"c": "0"}, "e": "2"}, "f": {"g": {"h": "0"}}}
    struct = structure.merge(struct, extra_files)
    # then the result, should contain both files from the original and the
    # merged struct,
    assert struct["a"]["b"]["d"] == "2"
    assert struct["f"]["g"]["h"] == "0"
    assert struct["a"]["e"] == "2"
    # the common leaves should be overridden and a tuple (content, rule)
    assert struct["a"]["b"]["c"] == "0"
Ejemplo n.º 8
0
def add_files(struct: Structure, opts: ScaffoldOpts) -> ActionParams:
    """Add some Travis files to structure

    Args:
        struct: project representation as (possibly) nested :obj:`dict`.
        opts: given options, see :obj:`create_project` for an extensive list.

    Returns:
        struct, opts: updated project representation and options
    """
    files: Structure = {
        ".travis.yml": (template("travis"), no_overwrite()),
        "tests": {
            "travis_install.sh": (template("travis_install"), no_overwrite())
        },
    }

    return structure.merge(struct, files), opts
Ejemplo n.º 9
0
    def add_files(struct, opts):
        nov, sou = operations.no_overwrite(), operations.skip_on_update()
        struct = structure.ensure(struct, "tests/file0", "new")
        struct = structure.ensure(struct, "tests/file1", "new", nov)
        struct = structure.ensure(struct, "tests/file2", "new", sou)
        struct = structure.merge(
            struct,
            {
                "tests": {
                    "file3": ("new", nov),
                    "file4": ("new", sou),
                    "file5": ("new", operations.create),
                    "file6": "new",
                }
            },
        )

        return struct, opts
Ejemplo n.º 10
0
    def add_files(struct, opts):
        struct = structure.ensure(struct, "tests/extra.file", "content")
        struct = structure.merge(struct, {"tests": {"another.file": "content"}})

        return struct, opts