constants.py 11 KB

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