constants.py 10 KB

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