constants.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. """Constants used throughout the package."""
  2. import os
  3. import platform
  4. import re
  5. from enum import Enum
  6. from types import SimpleNamespace
  7. from typing import Any, Type
  8. # importlib is only available for Python 3.8+ so we need the backport for Python 3.7
  9. try:
  10. from importlib import metadata
  11. except ImportError:
  12. import importlib_metadata as metadata # pyright: ignore[reportMissingImports]
  13. def get_value(key: str, default: Any = None, type_: Type = str) -> Type:
  14. """Get the value for the constant.
  15. Obtain os env value and cast non-string types into
  16. their original types.
  17. Args:
  18. key: constant name.
  19. default: default value if key doesn't exist.
  20. type_: the type of the constant.
  21. Returns:
  22. the value of the constant in its designated type
  23. """
  24. value = os.getenv(key, default)
  25. try:
  26. if value and type_ != str:
  27. value = eval(value)
  28. except Exception:
  29. pass
  30. finally:
  31. # Special case for db_url expects None to be a valid input when
  32. # user explicitly overrides db_url as None
  33. return value if value != "None" else None # noqa B012
  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 = os.path.expandvars(os.path.join("$HOME", f".{MODULE_NAME}"))
  42. # The root directory of the reflex library.
  43. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  44. # The name of the assets directory.
  45. APP_ASSETS_DIR = "assets"
  46. # The template directory used during reflex init.
  47. TEMPLATE_DIR = os.path.join(ROOT_DIR, MODULE_NAME, ".templates")
  48. # The web subdirectory of the template directory.
  49. WEB_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "web")
  50. # The assets subdirectory of the template directory.
  51. ASSETS_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, APP_ASSETS_DIR)
  52. # The jinja template directory.
  53. JINJA_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "jinja")
  54. # Bun config.
  55. # The Bun version.
  56. BUN_VERSION = "0.7.0"
  57. # The directory to store the bun.
  58. BUN_ROOT_PATH = os.path.join(REFLEX_DIR, ".bun")
  59. # The bun path.
  60. BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
  61. # URL to bun install script.
  62. BUN_INSTALL_URL = "https://bun.sh/install"
  63. # NVM / Node config.
  64. # The NVM version.
  65. NVM_VERSION = "0.39.1"
  66. # The Node version.
  67. NODE_VERSION = "18.17.0"
  68. # The minimum required node version.
  69. NODE_VERSION_MIN = "16.8.0"
  70. # The directory to store nvm.
  71. NVM_DIR = os.path.join(REFLEX_DIR, ".nvm")
  72. # The nvm path.
  73. NVM_PATH = os.path.join(NVM_DIR, "nvm.sh")
  74. # The node bin path.
  75. NODE_BIN_PATH = os.path.join(NVM_DIR, "versions", "node", f"v{NODE_VERSION}", "bin")
  76. # The default path where node is installed.
  77. NODE_PATH = (
  78. "node" if platform.system() == "Windows" else os.path.join(NODE_BIN_PATH, "node")
  79. )
  80. # The default path where npm is installed.
  81. NPM_PATH = (
  82. "npm" if platform.system() == "Windows" else os.path.join(NODE_BIN_PATH, "npm")
  83. )
  84. # The URL to the nvm install script.
  85. NVM_INSTALL_URL = (
  86. f"https://raw.githubusercontent.com/nvm-sh/nvm/v{NVM_VERSION}/install.sh"
  87. )
  88. # The frontend directories in a project.
  89. # The web folder where the NextJS app is compiled to.
  90. WEB_DIR = ".web"
  91. # The name of the utils file.
  92. UTILS_DIR = "utils"
  93. # The name of the output static directory.
  94. STATIC_DIR = "_static"
  95. # The name of the state file.
  96. STATE_PATH = "/".join([UTILS_DIR, "state"])
  97. # The name of the components file.
  98. COMPONENTS_PATH = "/".join([UTILS_DIR, "components"])
  99. # The directory where the app pages are compiled to.
  100. WEB_PAGES_DIR = os.path.join(WEB_DIR, "pages")
  101. # The directory where the static build is located.
  102. WEB_STATIC_DIR = os.path.join(WEB_DIR, STATIC_DIR)
  103. # The directory where the utils file is located.
  104. WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR)
  105. # The directory where the assets are located.
  106. WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public")
  107. # The Tailwind config.
  108. TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js")
  109. # Default Tailwind content paths
  110. TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
  111. # The NextJS config file
  112. NEXT_CONFIG_FILE = "next.config.js"
  113. # The sitemap config file.
  114. SITEMAP_CONFIG_FILE = os.path.join(WEB_DIR, "next-sitemap.config.js")
  115. # The node modules directory.
  116. NODE_MODULES = "node_modules"
  117. # The package lock file.
  118. PACKAGE_LOCK = "package-lock.json"
  119. # The reflex json file.
  120. REFLEX_JSON = os.path.join(WEB_DIR, "reflex.json")
  121. # The env json file.
  122. ENV_JSON = os.path.join(WEB_DIR, "env.json")
  123. # Commands to run the app.
  124. DOT_ENV_FILE = ".env"
  125. # The frontend default port.
  126. FRONTEND_PORT = get_value("FRONTEND_PORT", "3000")
  127. # The backend default port.
  128. BACKEND_PORT = get_value("BACKEND_PORT", "8000")
  129. # The backend api url.
  130. API_URL = get_value("API_URL", "http://localhost:8000")
  131. # The deploy url
  132. DEPLOY_URL = get_value("DEPLOY_URL")
  133. # Default host in dev mode.
  134. BACKEND_HOST = get_value("BACKEND_HOST", "0.0.0.0")
  135. # The default timeout when launching the gunicorn server.
  136. TIMEOUT = get_value("TIMEOUT", 120, type_=int)
  137. # The command to run the backend in production mode.
  138. RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {TIMEOUT} --log-level critical".split()
  139. RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {TIMEOUT}".split()
  140. # Socket.IO web server
  141. PING_INTERVAL = 25
  142. PING_TIMEOUT = 120
  143. # flag to make the engine print all the SQL statements it executes
  144. SQLALCHEMY_ECHO = get_value("SQLALCHEMY_ECHO", False, type_=bool)
  145. # Compiler variables.
  146. # The extension for compiled Javascript files.
  147. JS_EXT = ".js"
  148. # The extension for python files.
  149. PY_EXT = ".py"
  150. # The expected variable name where the rx.App is stored.
  151. APP_VAR = "app"
  152. # The expected variable name where the API object is stored for deployment.
  153. API_VAR = "api"
  154. # The name of the router variable.
  155. ROUTER = "router"
  156. # The name of the socket variable.
  157. SOCKET = "socket"
  158. # The name of the variable to hold API results.
  159. RESULT = "result"
  160. # The name of the final variable.
  161. FINAL = "final"
  162. # The name of the process variable.
  163. PROCESSING = "processing"
  164. # The name of the state variable.
  165. STATE = "state"
  166. # The name of the events variable.
  167. EVENTS = "events"
  168. # The name of the initial hydrate event.
  169. HYDRATE = "hydrate"
  170. # The name of the is_hydrated variable.
  171. IS_HYDRATED = "is_hydrated"
  172. # The name of the index page.
  173. INDEX_ROUTE = "index"
  174. # The name of the document root page.
  175. DOCUMENT_ROOT = "_document"
  176. # The name of the theme page.
  177. THEME = "theme"
  178. # The prefix used to create setters for state vars.
  179. SETTER_PREFIX = "set_"
  180. # The name of the frontend zip during deployment.
  181. FRONTEND_ZIP = "frontend.zip"
  182. # The name of the backend zip during deployment.
  183. BACKEND_ZIP = "backend.zip"
  184. # The name of the sqlite database.
  185. DB_NAME = os.getenv("DB_NAME", "reflex.db")
  186. # The sqlite url.
  187. DB_URL = get_value("DB_URL", f"sqlite:///{DB_NAME}")
  188. # The redis url
  189. REDIS_URL = get_value("REDIS_URL")
  190. # The default title to show for Reflex apps.
  191. DEFAULT_TITLE = "Reflex App"
  192. # The default description to show for Reflex apps.
  193. DEFAULT_DESCRIPTION = "A Reflex app."
  194. # The default image to show for Reflex apps.
  195. DEFAULT_IMAGE = "favicon.ico"
  196. # The default meta list to show for Reflex apps.
  197. DEFAULT_META_LIST = []
  198. # The gitignore file.
  199. GITIGNORE_FILE = ".gitignore"
  200. # Files to gitignore.
  201. DEFAULT_GITIGNORE = {WEB_DIR, DB_NAME, "__pycache__/", "*.py[cod]"}
  202. # The name of the reflex config module.
  203. CONFIG_MODULE = "rxconfig"
  204. # The python config file.
  205. CONFIG_FILE = f"{CONFIG_MODULE}{PY_EXT}"
  206. # The previous config file.
  207. OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
  208. # The deployment URL.
  209. PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
  210. # Token expiration time in seconds.
  211. TOKEN_EXPIRATION = 60 * 60
  212. # The event namespace for websocket
  213. EVENT_NAMESPACE = get_value("EVENT_NAMESPACE")
  214. # Testing variables.
  215. # Testing os env set by pytest when running a test case.
  216. PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
  217. # Env modes
  218. class Env(str, Enum):
  219. """The environment modes."""
  220. DEV = "dev"
  221. PROD = "prod"
  222. # Log levels
  223. class LogLevel(str, Enum):
  224. """The log levels."""
  225. DEBUG = "debug"
  226. INFO = "info"
  227. WARNING = "warning"
  228. ERROR = "error"
  229. CRITICAL = "critical"
  230. # Templates
  231. class Template(str, Enum):
  232. """The templates to use for the app."""
  233. DEFAULT = "default"
  234. COUNTER = "counter"
  235. class Endpoint(Enum):
  236. """Endpoints for the reflex backend API."""
  237. PING = "ping"
  238. EVENT = "event"
  239. UPLOAD = "upload"
  240. def __str__(self) -> str:
  241. """Get the string representation of the endpoint.
  242. Returns:
  243. The path for the endpoint.
  244. """
  245. return f"/{self.value}"
  246. def get_url(self) -> str:
  247. """Get the URL for the endpoint.
  248. Returns:
  249. The full URL for the endpoint.
  250. """
  251. # Import here to avoid circular imports.
  252. from reflex.config import get_config
  253. # Get the API URL from the config.
  254. config = get_config()
  255. url = "".join([config.api_url, str(self)])
  256. # The event endpoint is a websocket.
  257. if self == Endpoint.EVENT:
  258. # Replace the protocol with ws.
  259. url = url.replace("https://", "wss://").replace("http://", "ws://")
  260. # Return the url.
  261. return url
  262. class SocketEvent(Enum):
  263. """Socket events sent by the reflex backend API."""
  264. PING = "ping"
  265. EVENT = "event"
  266. def __str__(self) -> str:
  267. """Get the string representation of the event name.
  268. Returns:
  269. The event name string.
  270. """
  271. return str(self.value)
  272. class Transports(Enum):
  273. """Socket transports used by the reflex backend API."""
  274. POLLING_WEBSOCKET = "['polling', 'websocket']"
  275. WEBSOCKET_POLLING = "['websocket', 'polling']"
  276. WEBSOCKET_ONLY = "['websocket']"
  277. POLLING_ONLY = "['polling']"
  278. def __str__(self) -> str:
  279. """Get the string representation of the transports.
  280. Returns:
  281. The transports string.
  282. """
  283. return str(self.value)
  284. def get_transports(self) -> str:
  285. """Get the transports config for the backend.
  286. Returns:
  287. The transports config for the backend.
  288. """
  289. # Import here to avoid circular imports.
  290. from reflex.config import get_config
  291. # Get the API URL from the config.
  292. config = get_config()
  293. return str(config.backend_transports)
  294. class RouteArgType(SimpleNamespace):
  295. """Type of dynamic route arg extracted from URI route."""
  296. # Typecast to str is needed for Enum to work.
  297. SINGLE = str("arg_single")
  298. LIST = str("arg_list")
  299. # the name of the backend var containing path and client information
  300. ROUTER_DATA = "router_data"
  301. class RouteVar(SimpleNamespace):
  302. """Names of variables used in the router_data dict stored in State."""
  303. CLIENT_IP = "ip"
  304. CLIENT_TOKEN = "token"
  305. HEADERS = "headers"
  306. PATH = "pathname"
  307. ORIGIN = "asPath"
  308. SESSION_ID = "sid"
  309. QUERY = "query"
  310. COOKIE = "cookie"
  311. class RouteRegex(SimpleNamespace):
  312. """Regex used for extracting route args in route."""
  313. ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
  314. # group return the catchall pattern (i.e. "[[..slug]]")
  315. CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
  316. # group return the arg name (i.e. "slug")
  317. STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
  318. # group return the arg name (i.e. "slug")
  319. OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
  320. # 404 variables
  321. SLUG_404 = "404"
  322. TITLE_404 = "404 - Not Found"
  323. FAVICON_404 = "favicon.ico"
  324. DESCRIPTION_404 = "The page was not found"
  325. # Color mode variables
  326. USE_COLOR_MODE = "useColorMode"
  327. COLOR_MODE = "colorMode"
  328. TOGGLE_COLOR_MODE = "toggleColorMode"
  329. # Server socket configuration variables
  330. CORS_ALLOWED_ORIGINS = get_value("CORS_ALLOWED_ORIGINS", ["*"], list)
  331. POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
  332. # Alembic migrations
  333. ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")