watch.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. """General utility functions."""
  2. from __future__ import annotations
  3. import contextlib
  4. import os
  5. import shutil
  6. import time
  7. from watchdog.events import FileSystemEvent, FileSystemEventHandler
  8. from watchdog.observers import Observer
  9. from reflex.constants import Dirs
  10. from reflex.utils.prerequisites import get_web_dir
  11. class AssetFolderWatch:
  12. """Asset folder watch class."""
  13. def __init__(self, root):
  14. """Initialize the Watch Class.
  15. Args:
  16. root: root path of the public.
  17. """
  18. self.path = str(root / Dirs.APP_ASSETS)
  19. self.event_handler = AssetFolderHandler(root)
  20. def start(self):
  21. """Start watching asset folder."""
  22. self.observer = Observer()
  23. self.observer.schedule(self.event_handler, self.path, recursive=True)
  24. self.observer.start()
  25. def _decode_path(path: str | bytes | bytearray | memoryview) -> str:
  26. """Decode path to string.
  27. Args:
  28. path: The path to decode.
  29. Returns:
  30. The decoded path as a str.
  31. """
  32. if isinstance(path, (bytes, bytearray)):
  33. return path.decode()
  34. elif isinstance(path, memoryview):
  35. return path.tobytes().decode()
  36. return path
  37. class AssetFolderHandler(FileSystemEventHandler):
  38. """Asset folder event handler."""
  39. def __init__(self, root):
  40. """Initialize the AssetFolderHandler Class.
  41. Args:
  42. root: root path of the public.
  43. """
  44. super().__init__()
  45. self.root = root
  46. def on_modified(self, event: FileSystemEvent):
  47. """Event handler when a file or folder was modified.
  48. This is called every time after a file is created, modified and deleted.
  49. Args:
  50. event: Event information.
  51. """
  52. src_path = _decode_path(event.src_path)
  53. dest_path = self.get_dest_path(src_path)
  54. # wait 1 sec for fully saved
  55. time.sleep(1)
  56. if os.path.isfile(src_path):
  57. with contextlib.suppress(PermissionError):
  58. shutil.copyfile(src_path, dest_path)
  59. if os.path.isdir(src_path):
  60. if os.path.exists(dest_path):
  61. shutil.rmtree(dest_path)
  62. with contextlib.suppress(PermissionError):
  63. shutil.copytree(src_path, dest_path)
  64. def on_deleted(self, event: FileSystemEvent):
  65. """Event hander when a file or folder was deleted.
  66. Args:
  67. event: Event infomation.
  68. """
  69. dest_path = self.get_dest_path(_decode_path(event.src_path))
  70. if os.path.isfile(dest_path):
  71. # when event is about a file, pass
  72. # this will be deleted at on_modified function
  73. return
  74. if os.path.exists(dest_path):
  75. shutil.rmtree(dest_path)
  76. def get_dest_path(self, src_path: str) -> str:
  77. """Get public file path.
  78. Args:
  79. src_path: The asset file path.
  80. Returns:
  81. The public file path.
  82. """
  83. return src_path.replace(
  84. str(self.root / Dirs.APP_ASSETS),
  85. str(self.root / get_web_dir() / Dirs.PUBLIC),
  86. )