constants.py 9.9 KB

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