path_ops.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. """Path operations."""
  2. from __future__ import annotations
  3. import json
  4. import os
  5. import re
  6. import shutil
  7. from pathlib import Path
  8. from reflex import constants
  9. from reflex.config import environment
  10. # Shorthand for join.
  11. join = os.linesep.join
  12. def rm(path: str | Path):
  13. """Remove a file or directory.
  14. Args:
  15. path: The path to the file or directory.
  16. """
  17. path = Path(path)
  18. if path.is_dir():
  19. shutil.rmtree(path)
  20. elif path.is_file():
  21. path.unlink()
  22. def cp(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
  23. """Copy a file or directory.
  24. Args:
  25. src: The path to the file or directory.
  26. dest: The path to the destination.
  27. overwrite: Whether to overwrite the destination.
  28. Returns:
  29. Whether the copy was successful.
  30. """
  31. src, dest = Path(src), Path(dest)
  32. if src == dest:
  33. return False
  34. if not overwrite and dest.exists():
  35. return False
  36. if src.is_dir():
  37. rm(dest)
  38. shutil.copytree(src, dest)
  39. else:
  40. shutil.copyfile(src, dest)
  41. return True
  42. def mv(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
  43. """Move a file or directory.
  44. Args:
  45. src: The path to the file or directory.
  46. dest: The path to the destination.
  47. overwrite: Whether to overwrite the destination.
  48. Returns:
  49. Whether the move was successful.
  50. """
  51. src, dest = Path(src), Path(dest)
  52. if src == dest:
  53. return False
  54. if not overwrite and dest.exists():
  55. return False
  56. rm(dest)
  57. shutil.move(src, dest)
  58. return True
  59. def mkdir(path: str | Path):
  60. """Create a directory.
  61. Args:
  62. path: The path to the directory.
  63. """
  64. Path(path).mkdir(parents=True, exist_ok=True)
  65. def ls(path: str | Path) -> list[Path]:
  66. """List the contents of a directory.
  67. Args:
  68. path: The path to the directory.
  69. Returns:
  70. A list of paths to the contents of the directory.
  71. """
  72. return list(Path(path).iterdir())
  73. def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
  74. """Create a symbolic link.
  75. Args:
  76. src: The path to the file or directory.
  77. dest: The path to the destination.
  78. overwrite: Whether to overwrite the destination.
  79. Returns:
  80. Whether the link was successful.
  81. """
  82. src, dest = Path(src), Path(dest)
  83. if src == dest:
  84. return False
  85. if not overwrite and (dest.exists() or dest.is_symlink()):
  86. return False
  87. if src.is_dir():
  88. rm(dest)
  89. src.symlink_to(dest, target_is_directory=True)
  90. else:
  91. src.symlink_to(dest)
  92. return True
  93. def which(program: str | Path) -> str | Path | None:
  94. """Find the path to an executable.
  95. Args:
  96. program: The name of the executable.
  97. Returns:
  98. The path to the executable.
  99. """
  100. return shutil.which(str(program))
  101. def use_system_node() -> bool:
  102. """Check if the system node should be used.
  103. Returns:
  104. Whether the system node should be used.
  105. """
  106. return environment.REFLEX_USE_SYSTEM_NODE.get()
  107. def use_system_bun() -> bool:
  108. """Check if the system bun should be used.
  109. Returns:
  110. Whether the system bun should be used.
  111. """
  112. return environment.REFLEX_USE_SYSTEM_BUN.get()
  113. def get_node_bin_path() -> Path | None:
  114. """Get the node binary dir path.
  115. Returns:
  116. The path to the node bin folder.
  117. """
  118. bin_path = Path(constants.Node.BIN_PATH)
  119. if not bin_path.exists():
  120. str_path = which("node")
  121. return Path(str_path).parent.resolve() if str_path else None
  122. return bin_path.resolve()
  123. def get_node_path() -> str | None:
  124. """Get the node binary path.
  125. Returns:
  126. The path to the node binary file.
  127. """
  128. node_path = Path(constants.Node.PATH)
  129. if use_system_node() or not node_path.exists():
  130. system_node_path = which("node")
  131. return str(system_node_path) if system_node_path else None
  132. return str(node_path)
  133. def get_npm_path() -> Path | None:
  134. """Get npm binary path.
  135. Returns:
  136. The path to the npm binary file.
  137. """
  138. npm_path = Path(constants.Node.NPM_PATH)
  139. if use_system_node() or not npm_path.exists():
  140. system_npm_path = which("npm")
  141. npm_path = Path(system_npm_path) if system_npm_path else None
  142. return npm_path.absolute() if npm_path else None
  143. def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
  144. """Update the contents of a json file.
  145. Args:
  146. file_path: the path to the JSON file.
  147. update_dict: object to update json.
  148. """
  149. fp = Path(file_path)
  150. # Create the file if it doesn't exist.
  151. fp.touch(exist_ok=True)
  152. # Create an empty json object if file is empty
  153. fp.write_text("{}") if fp.stat().st_size == 0 else None
  154. # Read the existing json object from the file.
  155. json_object = {}
  156. if fp.stat().st_size:
  157. with fp.open() as f:
  158. json_object = json.load(f)
  159. # Update the json object with the new data.
  160. json_object.update(update_dict)
  161. # Write the updated json object to the file
  162. with fp.open("w") as f:
  163. json.dump(json_object, f, ensure_ascii=False)
  164. def find_replace(directory: str | Path, find: str, replace: str):
  165. """Recursively find and replace text in files in a directory.
  166. Args:
  167. directory: The directory to search.
  168. find: The text to find.
  169. replace: The text to replace.
  170. """
  171. directory = Path(directory)
  172. for root, _dirs, files in os.walk(directory):
  173. for file in files:
  174. filepath = Path(root, file)
  175. text = filepath.read_text(encoding="utf-8")
  176. text = re.sub(find, replace, text)
  177. filepath.write_text(text, encoding="utf-8")