reflex/scripts/make_pyi.py
Thomas Brandého 0af4770180
generate pyi files when building/publishing 3rd party component (#2945)
* build pyi files when building/publishing 3rd party

* fix typo in workflow

* add future annotation

* add tests to pass coverage check

* add more unit tests

* omit pyi_generator from test coverage

* change black from dev deps to direct deps

* remake all pyi

* format pyi if black is present, return as if otherwise

* fix requested changes

---------

Co-authored-by: Masen Furer <m_github@0x26.net>
2024-03-29 09:54:21 -07:00

113 lines
3.4 KiB
Python

"""The pyi generator module."""
import logging
import subprocess
import sys
from pathlib import Path
from reflex.utils.pyi_generator import PyiGenerator, _relative_to_pwd, generate_init
logger = logging.getLogger("pyi_generator")
LAST_RUN_COMMIT_SHA_FILE = Path(".pyi_generator_last_run").resolve()
GENERATOR_FILE = Path(__file__).resolve()
GENERATOR_DIFF_FILE = Path(".pyi_generator_diff").resolve()
def _git_diff(args: list[str]) -> str:
"""Run a git diff command.
Args:
args: The args to pass to git diff.
Returns:
The output of the git diff command.
"""
cmd = ["git", "diff", "--no-color", *args]
return subprocess.run(cmd, capture_output=True, encoding="utf-8").stdout
def _git_changed_files(args: list[str] | None = None) -> list[Path]:
"""Get the list of changed files for a git diff command.
Args:
args: The args to pass to git diff.
Returns:
The list of changed files.
"""
if not args:
args = []
if "--name-only" not in args:
args.insert(0, "--name-only")
diff = _git_diff(args).splitlines()
return [Path(file.strip()) for file in diff]
def _get_changed_files() -> list[Path] | None:
"""Get the list of changed files since the last run of the generator.
Returns:
The list of changed files, or None if all files should be regenerated.
"""
try:
last_run_commit_sha = LAST_RUN_COMMIT_SHA_FILE.read_text().strip()
except FileNotFoundError:
logger.info(
"make_pyi.py last run could not be determined, regenerating all .pyi files"
)
return None
changed_files = _git_changed_files([f"{last_run_commit_sha}..HEAD"])
# get all unstaged changes
changed_files.extend(_git_changed_files())
if _relative_to_pwd(GENERATOR_FILE) not in changed_files:
return changed_files
logger.info("make_pyi.py has changed, checking diff now")
diff = "".join(_git_diff([GENERATOR_FILE.as_posix()]).splitlines()[2:])
try:
last_diff = GENERATOR_DIFF_FILE.read_text()
if diff != last_diff:
logger.info("make_pyi.py has changed, regenerating all .pyi files")
changed_files = None
else:
logger.info("make_pyi.py has not changed, only regenerating changed files")
except FileNotFoundError:
logger.info(
"make_pyi.py diff could not be determined, regenerating all .pyi files"
)
changed_files = None
GENERATOR_DIFF_FILE.write_text(diff)
return changed_files
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("blib2to3.pgen2.driver").setLevel(logging.INFO)
targets = (
[arg for arg in sys.argv[1:] if not arg.startswith("tests")]
if len(sys.argv) > 1
else ["reflex/components"]
)
logger.info(f"Running .pyi generator for {targets}")
changed_files = _get_changed_files()
if changed_files is None:
logger.info("Changed files could not be detected, regenerating all .pyi files")
else:
logger.info(f"Detected changed files: {changed_files}")
gen = PyiGenerator()
gen.scan_all(targets, changed_files)
generate_init()
current_commit_sha = subprocess.run(
["git", "rev-parse", "HEAD"], capture_output=True, encoding="utf-8"
).stdout.strip()
LAST_RUN_COMMIT_SHA_FILE.write_text(current_commit_sha)