Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add source examples for Python 3.10 and 3.9 with updated syntax #842

Merged
merged 3 commits into from
Mar 21, 2024

Conversation

tiangolo
Copy link
Owner

@tiangolo tiangolo commented Mar 14, 2024

✨ Add source examples for Python 3.10 and 3.9 with updated syntax.

I automated the generation of source examples and includes in the docs.

The biggest part of the work here was adding the highlights to each version of Python, this was done by @estebanx64 😎 🙇


To update the files and to update the includes I used this script:

import filecmp
import functools
import os
import re
import shutil
import subprocess
from pathlib import Path

from typer import Typer

base_dir = Path(__file__).parent.parent
target_versions = ["py39", "py310"]
all_versions = ["py37", *target_versions]
ends = {"py37": "", **{v: f"_{v}" for v in target_versions}}

app = Typer()


def rm_old_versions() -> None:
    os.chdir(base_dir)
    docs_src_files = list(Path("docs_src").glob("**/*.py"))
    for file in docs_src_files:
        if any(version in str(file) for version in target_versions):
            file.unlink()


def get_docs_src_files() -> list[Path]:
    os.chdir(base_dir)
    docs_src_files = list(Path("docs_src").glob("**/*.py"))
    docs_src_files = [
        f
        for f in docs_src_files
        if not any(version in str(f) for version in target_versions)
    ]
    return docs_src_files


@app.command()
def generate_docs_src() -> None:
    """
    Read the source files for Python 3.7 and generate the source files for
    Python 3.9 and 3.10 to be included in the docs.
    """
    os.chdir(base_dir)
    docs_src_files = get_docs_src_files()
    updated_version_files: dict[str, list[Path]] = {}
    # target_version = target_versions[1]
    for target_version in target_versions:
        result = subprocess.run(
            [
                "ruff",
                "check",
                "--target-version",
                target_version,
                "--fix",
                "--unsafe-fixes",
                *docs_src_files,
            ],
            capture_output=True,
        )
        # print(result.stderr.decode())
        assert result.returncode == 0, result.stderr.decode()
        result2 = subprocess.run(
            ["ruff", "format", *docs_src_files],
            capture_output=True,
        )
        assert result2.returncode == 0, result2.stderr.decode()
        result3 = subprocess.run(
            ["git", "diff", "--name-only", *docs_src_files],
            capture_output=True,
        )
        assert result3.returncode == 0, result3.stderr.decode()
        files = result3.stdout.decode().splitlines()
        # file = files[0]
        processed_target_paths = set[Path]()
        src_paths = set[Path]()
        for file in files:
            file_path = Path(file)
            src_version_path = file_path
            if "tutorial" not in file_path.name:
                # list(file_path.parents)
                # parent = file_path.parents[0]
                for parent in file_path.parents:
                    if "tutorial" in parent.name:
                        src_version_path = parent
                        break
            src_paths.add(src_version_path)
            target_version_path = src_version_path.with_name(
                f"{src_version_path.stem}_{target_version}{src_version_path.suffix}"
            )
            if target_version_path in processed_target_paths:
                continue
            shutil.rmtree(target_version_path, ignore_errors=True)
            src_version_path.rename(target_version_path)
            updated_version_files.setdefault(target_version, []).append(
                target_version_path
            )
            processed_target_paths.add(target_version_path)
        result4 = subprocess.run(
            ["git", "checkout", *src_paths],
            capture_output=True,
        )
        assert result4.returncode == 0, result4.stderr.decode()
    # target_version = target_versions[0]
    reversed_versions = list(reversed(all_versions))
    # i = 0
    # version = reversed_versions[i]
    for i, version in enumerate(reversed_versions):
        if version not in updated_version_files:
            continue
        # file = updated_version_files[version][0]
        for file in updated_version_files[version]:
            # sub_v = reversed_versions[i + 1]
            for sub_v in reversed_versions[i + 1 :]:
                end = ends[version]
                sub_end = ends[sub_v]
                sub_file = file.with_stem(file.stem.replace(end, sub_end))
                if sub_file.exists():
                    if file.is_dir():
                        if not filecmp.dircmp(file, sub_file).diff_files:
                            shutil.rmtree(file, ignore_errors=True)
                            break
                    else:
                        if filecmp.cmp(file, sub_file):
                            file.unlink()
                            break


@app.command()
def update_md_files() -> None:
    """
    WARNING: Run only once, running again would double nest tabs, which would not work.

    Update Markdown files that have source files included without any tabs and convert
    them to include tabs for different versions.
    """
    os.chdir(base_dir)
    docs_src_files = get_docs_src_files()
    # docs_src_files = [Path("docs_src/advanced/decimal/tutorial001.py")]
    md_files = list(Path("docs").glob("**/*.md"))
    # md_files = [Path("docs/advanced/decimal.md")]
    # md_file = md_files[25]
    version_titles = {
        "py37": "Python 3.7+",
        "py39": "Python 3.9+",
        "py310": "Python 3.10+",
    }
    # md_file = md_files[25]
    for md_file in md_files:
        for src_file in docs_src_files:
            content = md_file.read_text()
            new_content = content
            file_str_re = str(src_file).replace(".", "\\.")
            re_str = rf"(```([^`])*)(\./{file_str_re})(([^`])*```" + "([^`^]*(md!}))?)"
            # version = target_versions[1]
            version_files = [
                src_file.with_stem(f"{src_file.stem}_{version}")
                for version in target_versions
            ]
            version_files = [f for f in version_files if f.exists()]

            def replace(
                version_files: list[Path], src_file: Path, match: re.Match
            ) -> str:
                if not version_files:
                    return match.group(0)
                result_parts = []
                for version in reversed(all_versions):
                    end = ends[version]
                    version_file = src_file.with_stem(f"{src_file.stem}{end}")
                    if version_file.exists():
                        include_block_plain = f"{match.group(0)}".replace(
                            str(src_file), str(version_file)
                        )
                        result_parts.append(
                            f"//// tab | {version_titles[version]}\n\n{include_block_plain}\n\n////"
                        )
                return "\n\n".join(result_parts)

            new_content = re.sub(
                re_str, functools.partial(replace, version_files, src_file), content
            )
            md_file.write_text(new_content)


if __name__ == "__main__":
    app()

@tiangolo tiangolo added the docs Improvements or additions to documentation label Mar 14, 2024
Copy link

📝 Docs preview for commit 01eac81 at: https://b52b0865.sqlmodel.pages.dev

Copy link

📝 Docs preview for commit ade64eb at: https://45960ceb.sqlmodel.pages.dev

Copy link
Collaborator

@alejsdev alejsdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@tiangolo tiangolo marked this pull request as ready for review March 21, 2024 22:44
@tiangolo tiangolo merged commit 9141c8a into main Mar 21, 2024
19 checks passed
@tiangolo tiangolo deleted the py-versions branch March 21, 2024 22:49
@tiangolo
Copy link
Owner Author

Thanks @estebanx64! Awesome! And thanks @alejsdev for the review. 🤓

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants