123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- """Path operations."""
- from __future__ import annotations
- import json
- import os
- import re
- import shutil
- import stat
- from pathlib import Path
- from reflex import constants
- from reflex.config import environment, get_config
- # Shorthand for join.
- join = os.linesep.join
- def chmod_rm(path: Path):
- """Remove a file or directory with chmod.
- Args:
- path: The path to the file or directory.
- """
- path.chmod(stat.S_IWRITE)
- if path.is_dir():
- shutil.rmtree(path)
- elif path.is_file():
- path.unlink()
- def rm(path: str | Path):
- """Remove a file or directory.
- Args:
- path: The path to the file or directory.
- """
- path = Path(path)
- if path.is_dir():
- # In Python 3.12, onerror is deprecated in favor of onexc
- shutil.rmtree(path, onerror=lambda _func, _path, _info: chmod_rm(path))
- elif path.is_file():
- path.unlink()
- def cp(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
- """Copy a file or directory.
- Args:
- src: The path to the file or directory.
- dest: The path to the destination.
- overwrite: Whether to overwrite the destination.
- Returns:
- Whether the copy was successful.
- """
- src, dest = Path(src), Path(dest)
- if src == dest:
- return False
- if not overwrite and dest.exists():
- return False
- if src.is_dir():
- rm(dest)
- shutil.copytree(src, dest)
- else:
- shutil.copyfile(src, dest)
- return True
- def mv(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
- """Move a file or directory.
- Args:
- src: The path to the file or directory.
- dest: The path to the destination.
- overwrite: Whether to overwrite the destination.
- Returns:
- Whether the move was successful.
- """
- src, dest = Path(src), Path(dest)
- if src == dest:
- return False
- if not overwrite and dest.exists():
- return False
- rm(dest)
- shutil.move(src, dest)
- return True
- def mkdir(path: str | Path):
- """Create a directory.
- Args:
- path: The path to the directory.
- """
- Path(path).mkdir(parents=True, exist_ok=True)
- def ls(path: str | Path) -> list[Path]:
- """List the contents of a directory.
- Args:
- path: The path to the directory.
- Returns:
- A list of paths to the contents of the directory.
- """
- return list(Path(path).iterdir())
- def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
- """Create a symbolic link.
- Args:
- src: The path to the file or directory.
- dest: The path to the destination.
- overwrite: Whether to overwrite the destination.
- Returns:
- Whether the link was successful.
- """
- src, dest = Path(src), Path(dest)
- if src == dest:
- return False
- if not overwrite and (dest.exists() or dest.is_symlink()):
- return False
- if src.is_dir():
- rm(dest)
- src.symlink_to(dest, target_is_directory=True)
- else:
- src.symlink_to(dest)
- return True
- def which(program: str | Path) -> Path | None:
- """Find the path to an executable.
- Args:
- program: The name of the executable.
- Returns:
- The path to the executable.
- """
- which_result = shutil.which(program)
- return Path(which_result) if which_result else None
- def use_system_node() -> bool:
- """Check if the system node should be used.
- Returns:
- Whether the system node should be used.
- """
- return environment.REFLEX_USE_SYSTEM_NODE.get()
- def use_system_bun() -> bool:
- """Check if the system bun should be used.
- Returns:
- Whether the system bun should be used.
- """
- return environment.REFLEX_USE_SYSTEM_BUN.get()
- def get_node_bin_path() -> Path | None:
- """Get the node binary dir path.
- Returns:
- The path to the node bin folder.
- """
- bin_path = Path(constants.Node.BIN_PATH)
- if not bin_path.exists():
- path = which("node")
- return path.parent.absolute() if path else None
- return bin_path.absolute()
- def get_node_path() -> Path | None:
- """Get the node binary path.
- Returns:
- The path to the node binary file.
- """
- node_path = Path(constants.Node.PATH)
- if use_system_node() or not node_path.exists():
- node_path = which("node")
- return node_path
- def get_npm_path() -> Path | None:
- """Get npm binary path.
- Returns:
- The path to the npm binary file.
- """
- npm_path = Path(constants.Node.NPM_PATH)
- if use_system_node() or not npm_path.exists():
- npm_path = which("npm")
- return npm_path.absolute() if npm_path else None
- def get_bun_path() -> Path | None:
- """Get bun binary path.
- Returns:
- The path to the bun binary file.
- """
- bun_path = get_config().bun_path
- if use_system_bun() or not bun_path.exists():
- bun_path = which("bun")
- return bun_path.absolute() if bun_path else None
- def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
- """Update the contents of a json file.
- Args:
- file_path: the path to the JSON file.
- update_dict: object to update json.
- """
- fp = Path(file_path)
- # Create the parent directory if it doesn't exist.
- fp.parent.mkdir(parents=True, exist_ok=True)
- # Create the file if it doesn't exist.
- fp.touch(exist_ok=True)
- # Create an empty json object if file is empty
- fp.write_text("{}") if fp.stat().st_size == 0 else None
- # Read the existing json object from the file.
- json_object = {}
- if fp.stat().st_size:
- with fp.open() as f:
- json_object = json.load(f)
- # Update the json object with the new data.
- json_object.update(update_dict)
- # Write the updated json object to the file
- with fp.open("w") as f:
- json.dump(json_object, f, ensure_ascii=False)
- def find_replace(directory: str | Path, find: str, replace: str):
- """Recursively find and replace text in files in a directory.
- Args:
- directory: The directory to search.
- find: The text to find.
- replace: The text to replace.
- """
- directory = Path(directory)
- for root, _dirs, files in os.walk(directory):
- for file in files:
- filepath = Path(root, file)
- text = filepath.read_text(encoding="utf-8")
- text = re.sub(find, replace, text)
- filepath.write_text(text, encoding="utf-8")
- def samefile(file1: Path, file2: Path) -> bool:
- """Check if two files are the same.
- Args:
- file1: The first file.
- file2: The second file.
- Returns:
- Whether the files are the same. If either file does not exist, returns False.
- """
- if file1.exists() and file2.exists():
- return file1.samefile(file2)
- return False
- def update_directory_tree(src: Path, dest: Path):
- """Recursively copies a directory tree from src to dest.
- Only copies files if the destination file is missing or modified earlier than the source file.
- Args:
- src: Source directory
- dest: Destination directory
- Raises:
- ValueError: If the source is not a directory
- """
- if not src.is_dir():
- raise ValueError(f"Source {src} is not a directory")
- # Ensure the destination directory exists
- dest.mkdir(parents=True, exist_ok=True)
- for item in src.iterdir():
- dest_item = dest / item.name
- if item.is_dir():
- # Recursively copy subdirectories
- update_directory_tree(item, dest_item)
- elif item.is_file() and (
- not dest_item.exists() or item.stat().st_mtime > dest_item.stat().st_mtime
- ):
- # Copy file if it doesn't exist in the destination or is older than the source
- shutil.copy2(item, dest_item)
|