constants.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. """Constants used throughout the package."""
  2. from __future__ import annotations
  3. import os
  4. import platform
  5. import re
  6. from enum import Enum
  7. from types import SimpleNamespace
  8. from platformdirs import PlatformDirs
  9. # importlib is only available for Python 3.8+ so we need the backport for Python 3.7
  10. try:
  11. from importlib import metadata
  12. except ImportError:
  13. import importlib_metadata as metadata # pyright: ignore[reportMissingImports]
  14. IS_WINDOWS = platform.system() == "Windows"
  15. def get_fnm_name() -> str | None:
  16. """Get the appropriate fnm executable name based on the current platform.
  17. Returns:
  18. The fnm executable name for the current platform.
  19. """
  20. platform_os = platform.system()
  21. if platform_os == "Windows":
  22. return "fnm-windows"
  23. elif platform_os == "Darwin":
  24. return "fnm-macos"
  25. elif platform_os == "Linux":
  26. machine = platform.machine()
  27. if machine == "arm" or machine.startswith("armv7"):
  28. return "fnm-arm32"
  29. elif machine.startswith("aarch") or machine.startswith("armv8"):
  30. return "fnm-arm64"
  31. return "fnm-linux"
  32. return None
  33. # App names and versions.
  34. # The name of the Reflex package.
  35. MODULE_NAME = "reflex"
  36. # The current version of Reflex.
  37. VERSION = metadata.version(MODULE_NAME)
  38. # Files and directories used to init a new project.
  39. # The directory to store reflex dependencies.
  40. REFLEX_DIR = (
  41. # on windows, we use C:/Users/<username>/AppData/Local/reflex.
  42. # on macOS, we use ~/Library/Application Support/reflex.
  43. # on linux, we use ~/.local/share/reflex.
  44. PlatformDirs(MODULE_NAME, False).user_data_dir
  45. )
  46. # The root directory of the reflex library.
  47. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  48. # The name of the assets directory.
  49. APP_ASSETS_DIR = "assets"
  50. # The template directory used during reflex init.
  51. TEMPLATE_DIR = os.path.join(ROOT_DIR, MODULE_NAME, ".templates")
  52. # The web subdirectory of the template directory.
  53. WEB_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "web")
  54. # The assets subdirectory of the template directory.
  55. ASSETS_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, APP_ASSETS_DIR)
  56. # The jinja template directory.
  57. JINJA_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "jinja")
  58. # Bun config.
  59. # The Bun version.
  60. BUN_VERSION = "0.7.0"
  61. # Min Bun Version
  62. MIN_BUN_VERSION = "0.7.0"
  63. # The directory to store the bun.
  64. BUN_ROOT_PATH = os.path.join(REFLEX_DIR, "bun")
  65. # Default bun path.
  66. DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
  67. # URL to bun install script.
  68. BUN_INSTALL_URL = "https://bun.sh/install"
  69. # FNM / Node config.
  70. # The FNM version.
  71. FNM_VERSION = "1.35.1"
  72. # The Node version.
  73. NODE_VERSION = "18.17.0"
  74. # The minimum required node version.
  75. NODE_VERSION_MIN = "16.8.0"
  76. # The directory to store fnm.
  77. FNM_DIR = os.path.join(REFLEX_DIR, "fnm")
  78. FNM_FILENAME = get_fnm_name()
  79. # The fnm executable binary.
  80. FNM_EXE = os.path.join(FNM_DIR, "fnm.exe" if IS_WINDOWS else "fnm")
  81. # The node bin path.
  82. NODE_BIN_PATH = os.path.join(
  83. FNM_DIR,
  84. "node-versions",
  85. f"v{NODE_VERSION}",
  86. "installation",
  87. "bin" if not IS_WINDOWS else "",
  88. )
  89. # The default path where node is installed.
  90. NODE_PATH = os.path.join(NODE_BIN_PATH, "node.exe" if IS_WINDOWS else "node")
  91. # The default path where npm is installed.
  92. NPM_PATH = os.path.join(NODE_BIN_PATH, "npm")
  93. # The URL to the fnm release binary
  94. FNM_INSTALL_URL = (
  95. f"https://github.com/Schniz/fnm/releases/download/v{FNM_VERSION}/{FNM_FILENAME}.zip"
  96. )
  97. # The frontend directories in a project.
  98. # The web folder where the NextJS app is compiled to.
  99. WEB_DIR = ".web"
  100. # The name of the utils file.
  101. UTILS_DIR = "utils"
  102. # The name of the output static directory.
  103. STATIC_DIR = "_static"
  104. # The name of the state file.
  105. STATE_PATH = "/".join([UTILS_DIR, "state"])
  106. # The name of the components file.
  107. COMPONENTS_PATH = "/".join([UTILS_DIR, "components"])
  108. # The directory where the app pages are compiled to.
  109. WEB_PAGES_DIR = os.path.join(WEB_DIR, "pages")
  110. # The directory where the static build is located.
  111. WEB_STATIC_DIR = os.path.join(WEB_DIR, STATIC_DIR)
  112. # The directory where the utils file is located.
  113. WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR)
  114. # The directory where the assets are located.
  115. WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public")
  116. # The Tailwind config.
  117. TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js")
  118. # Default Tailwind content paths
  119. TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
  120. # The NextJS config file
  121. NEXT_CONFIG_FILE = "next.config.js"
  122. # The sitemap config file.
  123. SITEMAP_CONFIG_FILE = os.path.join(WEB_DIR, "next-sitemap.config.js")
  124. # The node modules directory.
  125. NODE_MODULES = "node_modules"
  126. # The package lock file.
  127. PACKAGE_LOCK = "package-lock.json"
  128. # The reflex json file.
  129. REFLEX_JSON = os.path.join(WEB_DIR, "reflex.json")
  130. # The env json file.
  131. ENV_JSON = os.path.join(WEB_DIR, "env.json")
  132. # Compiler variables.
  133. # The extension for compiled Javascript files.
  134. JS_EXT = ".js"
  135. # The extension for python files.
  136. PY_EXT = ".py"
  137. # The expected variable name where the rx.App is stored.
  138. APP_VAR = "app"
  139. # The expected variable name where the API object is stored for deployment.
  140. API_VAR = "api"
  141. # The name of the router variable.
  142. ROUTER = "router"
  143. # The name of the socket variable.
  144. SOCKET = "socket"
  145. # The name of the variable to hold API results.
  146. RESULT = "result"
  147. # The name of the final variable.
  148. FINAL = "final"
  149. # The name of the process variable.
  150. PROCESSING = "processing"
  151. # The name of the state variable.
  152. STATE = "state"
  153. # The name of the events variable.
  154. EVENTS = "events"
  155. # The name of the initial hydrate event.
  156. HYDRATE = "hydrate"
  157. # The name of the is_hydrated variable.
  158. IS_HYDRATED = "is_hydrated"
  159. # The name of the index page.
  160. INDEX_ROUTE = "index"
  161. # The name of the document root page.
  162. DOCUMENT_ROOT = "_document"
  163. # The name of the theme page.
  164. THEME = "theme"
  165. # The prefix used to create setters for state vars.
  166. SETTER_PREFIX = "set_"
  167. # The name of the frontend zip during deployment.
  168. FRONTEND_ZIP = "frontend.zip"
  169. # The name of the backend zip during deployment.
  170. BACKEND_ZIP = "backend.zip"
  171. # The default title to show for Reflex apps.
  172. DEFAULT_TITLE = "Reflex App"
  173. # The default description to show for Reflex apps.
  174. DEFAULT_DESCRIPTION = "A Reflex app."
  175. # The default image to show for Reflex apps.
  176. DEFAULT_IMAGE = "favicon.ico"
  177. # The default meta list to show for Reflex apps.
  178. DEFAULT_META_LIST = []
  179. # The gitignore file.
  180. GITIGNORE_FILE = ".gitignore"
  181. # Files to gitignore.
  182. DEFAULT_GITIGNORE = {WEB_DIR, "*.db", "__pycache__/", "*.py[cod]"}
  183. # The name of the reflex config module.
  184. CONFIG_MODULE = "rxconfig"
  185. # The python config file.
  186. CONFIG_FILE = f"{CONFIG_MODULE}{PY_EXT}"
  187. # The previous config file.
  188. OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
  189. # The deployment URL.
  190. PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
  191. # Token expiration time in seconds.
  192. TOKEN_EXPIRATION = 60 * 60
  193. # Testing variables.
  194. # Testing os env set by pytest when running a test case.
  195. PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
  196. # Env modes
  197. class Env(str, Enum):
  198. """The environment modes."""
  199. DEV = "dev"
  200. PROD = "prod"
  201. # Log levels
  202. class LogLevel(str, Enum):
  203. """The log levels."""
  204. DEBUG = "debug"
  205. INFO = "info"
  206. WARNING = "warning"
  207. ERROR = "error"
  208. CRITICAL = "critical"
  209. def __le__(self, other: LogLevel) -> bool:
  210. """Compare log levels.
  211. Args:
  212. other: The other log level.
  213. Returns:
  214. True if the log level is less than or equal to the other log level.
  215. """
  216. levels = list(LogLevel)
  217. return levels.index(self) <= levels.index(other)
  218. # Templates
  219. class Template(str, Enum):
  220. """The templates to use for the app."""
  221. DEFAULT = "default"
  222. COUNTER = "counter"
  223. class Endpoint(Enum):
  224. """Endpoints for the reflex backend API."""
  225. PING = "ping"
  226. EVENT = "_event"
  227. UPLOAD = "_upload"
  228. def __str__(self) -> str:
  229. """Get the string representation of the endpoint.
  230. Returns:
  231. The path for the endpoint.
  232. """
  233. return f"/{self.value}"
  234. def get_url(self) -> str:
  235. """Get the URL for the endpoint.
  236. Returns:
  237. The full URL for the endpoint.
  238. """
  239. # Import here to avoid circular imports.
  240. from reflex.config import get_config
  241. # Get the API URL from the config.
  242. config = get_config()
  243. url = "".join([config.api_url, str(self)])
  244. # The event endpoint is a websocket.
  245. if self == Endpoint.EVENT:
  246. # Replace the protocol with ws.
  247. url = url.replace("https://", "wss://").replace("http://", "ws://")
  248. # Return the url.
  249. return url
  250. class SocketEvent(Enum):
  251. """Socket events sent by the reflex backend API."""
  252. PING = "ping"
  253. EVENT = "event"
  254. def __str__(self) -> str:
  255. """Get the string representation of the event name.
  256. Returns:
  257. The event name string.
  258. """
  259. return str(self.value)
  260. class RouteArgType(SimpleNamespace):
  261. """Type of dynamic route arg extracted from URI route."""
  262. # Typecast to str is needed for Enum to work.
  263. SINGLE = str("arg_single")
  264. LIST = str("arg_list")
  265. # the name of the backend var containing path and client information
  266. ROUTER_DATA = "router_data"
  267. class RouteVar(SimpleNamespace):
  268. """Names of variables used in the router_data dict stored in State."""
  269. CLIENT_IP = "ip"
  270. CLIENT_TOKEN = "token"
  271. HEADERS = "headers"
  272. PATH = "pathname"
  273. ORIGIN = "asPath"
  274. SESSION_ID = "sid"
  275. QUERY = "query"
  276. COOKIE = "cookie"
  277. class RouteRegex(SimpleNamespace):
  278. """Regex used for extracting route args in route."""
  279. ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
  280. # group return the catchall pattern (i.e. "[[..slug]]")
  281. CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
  282. # group return the arg name (i.e. "slug")
  283. STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
  284. # group return the arg name (i.e. "slug")
  285. OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
  286. # 404 variables
  287. SLUG_404 = "404"
  288. TITLE_404 = "404 - Not Found"
  289. FAVICON_404 = "favicon.ico"
  290. DESCRIPTION_404 = "The page was not found"
  291. # Color mode variables
  292. USE_COLOR_MODE = "useColorMode"
  293. COLOR_MODE = "colorMode"
  294. TOGGLE_COLOR_MODE = "toggleColorMode"
  295. # Server socket configuration variables
  296. POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
  297. PING_INTERVAL = 25
  298. PING_TIMEOUT = 120
  299. # Alembic migrations
  300. ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
  301. # Keys in the client_side_storage dict
  302. COOKIES = "cookies"
  303. LOCAL_STORAGE = "local_storage"
  304. # Names of event handlers on all components mapped to useEffect
  305. ON_MOUNT = "on_mount"
  306. ON_UNMOUNT = "on_unmount"
  307. # If this env var is set to "yes", App.compile will be a no-op
  308. SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"