reflex 0.4.6a4__py3-none-any.whl → 0.4.7a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (68) hide show
  1. reflex/.templates/apps/blank/code/blank.py +1 -0
  2. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
  3. reflex/.templates/jinja/custom_components/src.py.jinja2 +8 -8
  4. reflex/.templates/jinja/web/pages/index.js.jinja2 +0 -4
  5. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +2 -6
  6. reflex/.templates/web/utils/state.js +6 -1
  7. reflex/__init__.py +2 -0
  8. reflex/__init__.pyi +2 -0
  9. reflex/app.py +12 -16
  10. reflex/app.pyi +2 -0
  11. reflex/compiler/compiler.py +10 -11
  12. reflex/compiler/utils.py +3 -3
  13. reflex/components/chakra/forms/pininput.py +2 -1
  14. reflex/components/component.py +71 -104
  15. reflex/components/core/banner.py +1 -1
  16. reflex/components/core/upload.py +2 -1
  17. reflex/components/datadisplay/__init__.py +1 -0
  18. reflex/components/datadisplay/logo.py +49 -0
  19. reflex/components/el/elements/forms.py +7 -4
  20. reflex/components/el/elements/forms.pyi +0 -1
  21. reflex/components/lucide/icon.py +3 -2
  22. reflex/components/lucide/icon.pyi +2 -2
  23. reflex/components/markdown/markdown.py +10 -6
  24. reflex/components/markdown/markdown.pyi +0 -3
  25. reflex/components/radix/themes/components/select.py +10 -3
  26. reflex/config.py +1 -1
  27. reflex/config.pyi +1 -1
  28. reflex/constants/base.py +4 -5
  29. reflex/constants/base.pyi +94 -0
  30. reflex/constants/compiler.py +8 -0
  31. reflex/custom_components/custom_components.py +1 -1
  32. reflex/experimental/__init__.py +14 -0
  33. reflex/experimental/hooks.py +75 -0
  34. reflex/page.py +1 -1
  35. reflex/reflex.py +18 -32
  36. reflex/style.py +4 -4
  37. reflex/testing.py +1 -1
  38. reflex/utils/console.py +6 -4
  39. reflex/utils/exec.py +17 -1
  40. reflex/utils/export.py +0 -3
  41. reflex/utils/prerequisites.py +239 -43
  42. reflex/utils/processes.py +6 -1
  43. reflex/utils/telemetry.py +14 -2
  44. reflex/vars.py +6 -6
  45. reflex/vars.pyi +2 -1
  46. {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/METADATA +15 -10
  47. {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/RECORD +50 -64
  48. {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/WHEEL +1 -1
  49. reflex/.templates/apps/sidebar/README.md +0 -69
  50. reflex/.templates/apps/sidebar/assets/favicon.ico +0 -0
  51. reflex/.templates/apps/sidebar/assets/github.svg +0 -10
  52. reflex/.templates/apps/sidebar/assets/logo.svg +0 -68
  53. reflex/.templates/apps/sidebar/assets/paneleft.svg +0 -13
  54. reflex/.templates/apps/sidebar/assets/reflex_black.svg +0 -37
  55. reflex/.templates/apps/sidebar/assets/reflex_white.svg +0 -8
  56. reflex/.templates/apps/sidebar/code/__init__.py +0 -1
  57. reflex/.templates/apps/sidebar/code/components/__init__.py +0 -0
  58. reflex/.templates/apps/sidebar/code/components/sidebar.py +0 -152
  59. reflex/.templates/apps/sidebar/code/pages/__init__.py +0 -3
  60. reflex/.templates/apps/sidebar/code/pages/dashboard.py +0 -22
  61. reflex/.templates/apps/sidebar/code/pages/index.py +0 -18
  62. reflex/.templates/apps/sidebar/code/pages/settings.py +0 -61
  63. reflex/.templates/apps/sidebar/code/sidebar.py +0 -16
  64. reflex/.templates/apps/sidebar/code/styles.py +0 -60
  65. reflex/.templates/apps/sidebar/code/templates/__init__.py +0 -1
  66. reflex/.templates/apps/sidebar/code/templates/template.py +0 -145
  67. {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/LICENSE +0 -0
  68. {reflex-0.4.6a4.dist-info → reflex-0.4.7a1.dist-info}/entry_points.txt +0 -0
@@ -10,6 +10,7 @@ import os
10
10
  import platform
11
11
  import random
12
12
  import re
13
+ import shutil
13
14
  import stat
14
15
  import sys
15
16
  import tempfile
@@ -30,6 +31,7 @@ from redis.asyncio import Redis
30
31
 
31
32
  import reflex
32
33
  from reflex import constants, model
34
+ from reflex.base import Base
33
35
  from reflex.compiler import templates
34
36
  from reflex.config import Config, get_config
35
37
  from reflex.utils import console, path_ops, processes
@@ -37,6 +39,15 @@ from reflex.utils import console, path_ops, processes
37
39
  CURRENTLY_INSTALLING_NODE = False
38
40
 
39
41
 
42
+ class Template(Base):
43
+ """A template for a Reflex app."""
44
+
45
+ name: str
46
+ description: str
47
+ code_url: str
48
+ demo_url: str
49
+
50
+
40
51
  def check_latest_package_version(package_name: str):
41
52
  """Check if the latest version of the package is installed.
42
53
 
@@ -407,17 +418,42 @@ def initialize_requirements_txt():
407
418
  console.info(f"Unable to check {fp} for reflex dependency.")
408
419
 
409
420
 
410
- def initialize_app_directory(app_name: str, template: constants.Templates.Kind):
421
+ def initialize_app_directory(
422
+ app_name: str,
423
+ template_name: str = constants.Templates.DEFAULT,
424
+ template_code_dir_name: str | None = None,
425
+ template_dir: Path | None = None,
426
+ ):
411
427
  """Initialize the app directory on reflex init.
412
428
 
413
429
  Args:
414
430
  app_name: The name of the app.
415
- template: The template to use.
431
+ template_name: The name of the template to use.
432
+ template_code_dir_name: The name of the code directory in the template.
433
+ template_dir: The directory of the template source files.
434
+
435
+ Raises:
436
+ Exit: If template_name, template_code_dir_name, template_dir combination is not supported.
416
437
  """
417
438
  console.log("Initializing the app directory.")
418
439
 
419
- # Copy the template to the current directory.
420
- template_dir = Path(constants.Templates.Dirs.BASE, "apps", template.value)
440
+ # By default, use the blank template from local assets.
441
+ if template_name == constants.Templates.DEFAULT:
442
+ if template_code_dir_name is not None or template_dir is not None:
443
+ console.error(
444
+ f"Only {template_name=} should be provided, got {template_code_dir_name=}, {template_dir=}."
445
+ )
446
+ raise typer.Exit(1)
447
+ template_code_dir_name = constants.Templates.Dirs.CODE
448
+ template_dir = Path(constants.Templates.Dirs.BASE, "apps", template_name)
449
+ else:
450
+ if template_code_dir_name is None or template_dir is None:
451
+ console.error(
452
+ f"For `{template_name}` template, `template_code_dir_name` and `template_dir` should both be provided."
453
+ )
454
+ raise typer.Exit(1)
455
+
456
+ console.debug(f"Using {template_name=} {template_dir=} {template_code_dir_name=}.")
421
457
 
422
458
  # Remove all pyc and __pycache__ dirs in template directory.
423
459
  for pyc_file in template_dir.glob("**/*.pyc"):
@@ -430,16 +466,16 @@ def initialize_app_directory(app_name: str, template: constants.Templates.Kind):
430
466
  path_ops.cp(str(file), file.name)
431
467
 
432
468
  # Rename the template app to the app name.
433
- path_ops.mv(constants.Templates.Dirs.CODE, app_name)
469
+ path_ops.mv(template_code_dir_name, app_name)
434
470
  path_ops.mv(
435
- os.path.join(app_name, template_dir.name + constants.Ext.PY),
471
+ os.path.join(app_name, template_name + constants.Ext.PY),
436
472
  os.path.join(app_name, app_name + constants.Ext.PY),
437
473
  )
438
474
 
439
475
  # Fix up the imports.
440
476
  path_ops.find_replace(
441
477
  app_name,
442
- f"from {constants.Templates.Dirs.CODE}",
478
+ f"from {template_name}",
443
479
  f"from {app_name}",
444
480
  )
445
481
 
@@ -802,43 +838,38 @@ def install_frontend_packages(packages: set[str], config: Config):
802
838
  )
803
839
 
804
840
 
805
- def check_initialized(frontend: bool = True):
806
- """Check that the app is initialized.
841
+ def needs_reinit(frontend: bool = True) -> bool:
842
+ """Check if an app needs to be reinitialized.
807
843
 
808
844
  Args:
809
845
  frontend: Whether to check if the frontend is initialized.
810
846
 
847
+ Returns:
848
+ Whether the app needs to be reinitialized.
849
+
811
850
  Raises:
812
851
  Exit: If the app is not initialized.
813
852
  """
814
- has_config = os.path.exists(constants.Config.FILE)
815
- has_reflex_dir = not frontend or os.path.exists(constants.Reflex.DIR)
816
- has_web_dir = not frontend or os.path.exists(constants.Dirs.WEB)
817
-
818
- # Check if the app is initialized.
819
- if not (has_config and has_reflex_dir and has_web_dir):
853
+ if not os.path.exists(constants.Config.FILE):
820
854
  console.error(
821
- f"The app is not initialized. Run [bold]{constants.Reflex.MODULE_NAME} init[/bold] first."
855
+ f"{constants.Config.FILE} not found. Run [bold]{constants.Reflex.MODULE_NAME} init[/bold] first."
822
856
  )
823
857
  raise typer.Exit(1)
824
858
 
825
- # Check that the template is up to date.
826
- if frontend and not is_latest_template():
827
- console.error(
828
- "The base app template has updated. Run [bold]reflex init[/bold] again."
829
- )
830
- raise typer.Exit(1)
859
+ # Make sure the .reflex directory exists.
860
+ if not os.path.exists(constants.Reflex.DIR):
861
+ return True
862
+
863
+ # Make sure the .web directory exists in frontend mode.
864
+ if frontend and not os.path.exists(constants.Dirs.WEB):
865
+ return True
831
866
 
832
- # Print a warning for Windows users.
833
867
  if constants.IS_WINDOWS:
834
868
  console.warn(
835
869
  """Windows Subsystem for Linux (WSL) is recommended for improving initial install times."""
836
870
  )
837
- if sys.version_info >= (3, 12):
838
- console.warn(
839
- "Python 3.12 on Windows has known issues with hot reload (reflex-dev/reflex#2335). "
840
- "Python 3.11 is recommended with this release of Reflex."
841
- )
871
+ # No need to reinitialize if the app is already initialized.
872
+ return False
842
873
 
843
874
 
844
875
  def is_latest_template() -> bool:
@@ -1004,33 +1035,35 @@ def check_schema_up_to_date():
1004
1035
  )
1005
1036
 
1006
1037
 
1007
- def prompt_for_template() -> constants.Templates.Kind:
1038
+ def prompt_for_template(templates: list[Template]) -> str:
1008
1039
  """Prompt the user to specify a template.
1009
1040
 
1041
+ Args:
1042
+ templates: The templates to choose from.
1043
+
1010
1044
  Returns:
1011
- The template the user selected.
1045
+ The template name the user selects.
1012
1046
  """
1013
- # Show the user the URLs of each temlate to preview.
1047
+ # Show the user the URLs of each template to preview.
1014
1048
  console.print("\nGet started with a template:")
1015
- console.print("blank (https://blank-template.reflex.run) - A minimal template.")
1016
- console.print(
1017
- "sidebar (https://sidebar-template.reflex.run) - A template with a sidebar to navigate pages."
1018
- )
1019
- console.print("")
1020
1049
 
1021
1050
  # Prompt the user to select a template.
1051
+ id_to_name = {
1052
+ str(idx): f"{template.name} ({template.demo_url}) - {template.description}"
1053
+ for idx, template in enumerate(templates)
1054
+ }
1055
+ for id in range(len(id_to_name)):
1056
+ console.print(f"({id}) {id_to_name[str(id)]}")
1057
+
1022
1058
  template = console.ask(
1023
1059
  "Which template would you like to use?",
1024
- choices=[
1025
- template.value
1026
- for template in constants.Templates.Kind
1027
- if template.value != "demo"
1028
- ],
1029
- default=constants.Templates.Kind.BLANK.value,
1060
+ choices=[str(i) for i in range(len(id_to_name))],
1061
+ show_choices=False,
1062
+ default="0",
1030
1063
  )
1031
1064
 
1032
1065
  # Return the template.
1033
- return constants.Templates.Kind(template)
1066
+ return templates[int(template)].name
1034
1067
 
1035
1068
 
1036
1069
  def should_show_rx_chakra_migration_instructions() -> bool:
@@ -1183,3 +1216,166 @@ def migrate_to_reflex():
1183
1216
  for old, new in updates.items():
1184
1217
  line = line.replace(old, new)
1185
1218
  print(line, end="")
1219
+
1220
+
1221
+ def fetch_app_templates() -> dict[str, Template]:
1222
+ """Fetch the list of app templates from the Reflex backend server.
1223
+
1224
+ Returns:
1225
+ The name and download URL as a dictionary.
1226
+ """
1227
+ config = get_config()
1228
+ if not config.cp_backend_url:
1229
+ console.info(
1230
+ "Skip fetching App templates. No backend URL is specified in the config."
1231
+ )
1232
+ return {}
1233
+ try:
1234
+ response = httpx.get(
1235
+ f"{config.cp_backend_url}{constants.Templates.APP_TEMPLATES_ROUTE}"
1236
+ )
1237
+ response.raise_for_status()
1238
+ return {
1239
+ template["name"]: Template.parse_obj(template)
1240
+ for template in response.json()
1241
+ }
1242
+ except httpx.HTTPError as ex:
1243
+ console.info(f"Failed to fetch app templates: {ex}")
1244
+ return {}
1245
+ except (TypeError, KeyError, json.JSONDecodeError) as tkje:
1246
+ console.info(f"Unable to process server response for app templates: {tkje}")
1247
+ return {}
1248
+
1249
+
1250
+ def create_config_init_app_from_remote_template(
1251
+ app_name: str,
1252
+ template_url: str,
1253
+ ):
1254
+ """Create new rxconfig and initialize app using a remote template.
1255
+
1256
+ Args:
1257
+ app_name: The name of the app.
1258
+ template_url: The path to the template source code as a zip file.
1259
+
1260
+ Raises:
1261
+ Exit: If any download, file operations fail or unexpected zip file format.
1262
+
1263
+ """
1264
+ # Create a temp directory for the zip download.
1265
+ try:
1266
+ temp_dir = tempfile.mkdtemp()
1267
+ except OSError as ose:
1268
+ console.error(f"Failed to create temp directory for download: {ose}")
1269
+ raise typer.Exit(1) from ose
1270
+
1271
+ # Use httpx GET with redirects to download the zip file.
1272
+ zip_file_path = Path(temp_dir) / "template.zip"
1273
+ try:
1274
+ # Note: following redirects can be risky. We only allow this for reflex built templates at the moment.
1275
+ response = httpx.get(template_url, follow_redirects=True)
1276
+ console.debug(f"Server responded download request: {response}")
1277
+ response.raise_for_status()
1278
+ except httpx.HTTPError as he:
1279
+ console.error(f"Failed to download the template: {he}")
1280
+ raise typer.Exit(1) from he
1281
+ try:
1282
+ with open(zip_file_path, "wb") as f:
1283
+ f.write(response.content)
1284
+ console.debug(f"Downloaded the zip to {zip_file_path}")
1285
+ except OSError as ose:
1286
+ console.error(f"Unable to write the downloaded zip to disk {ose}")
1287
+ raise typer.Exit(1) from ose
1288
+
1289
+ # Create a temp directory for the zip extraction.
1290
+ try:
1291
+ unzip_dir = Path(tempfile.mkdtemp())
1292
+ except OSError as ose:
1293
+ console.error(f"Failed to create temp directory for extracting zip: {ose}")
1294
+ raise typer.Exit(1) from ose
1295
+ try:
1296
+ zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir)
1297
+ # The zip file downloaded from github looks like:
1298
+ # repo-name-branch/**/*, so we need to remove the top level directory.
1299
+ if len(subdirs := os.listdir(unzip_dir)) != 1:
1300
+ console.error(f"Expected one directory in the zip, found {subdirs}")
1301
+ raise typer.Exit(1)
1302
+ template_dir = unzip_dir / subdirs[0]
1303
+ console.debug(f"Template folder is located at {template_dir}")
1304
+ except Exception as uze:
1305
+ console.error(f"Failed to unzip the template: {uze}")
1306
+ raise typer.Exit(1) from uze
1307
+
1308
+ # Move the rxconfig file here first.
1309
+ path_ops.mv(str(template_dir / constants.Config.FILE), constants.Config.FILE)
1310
+ new_config = get_config(reload=True)
1311
+
1312
+ # Get the template app's name from rxconfig in case it is different than
1313
+ # the source code repo name on github.
1314
+ template_name = new_config.app_name
1315
+
1316
+ create_config(app_name)
1317
+ initialize_app_directory(
1318
+ app_name,
1319
+ template_name=template_name,
1320
+ template_code_dir_name=template_name,
1321
+ template_dir=template_dir,
1322
+ )
1323
+
1324
+ # Clean up the temp directories.
1325
+ shutil.rmtree(temp_dir)
1326
+ shutil.rmtree(unzip_dir)
1327
+
1328
+
1329
+ def initialize_app(app_name: str, template: str | None = None):
1330
+ """Initialize the app either from a remote template or a blank app. If the config file exists, it is considered as reinit.
1331
+
1332
+ Args:
1333
+ app_name: The name of the app.
1334
+ template: The name of the template to use.
1335
+
1336
+ Raises:
1337
+ Exit: If template is directly provided in the command flag and is invalid.
1338
+ """
1339
+ # Local imports to avoid circular imports.
1340
+ from reflex.utils import telemetry
1341
+
1342
+ # Check if the app is already initialized.
1343
+ if os.path.exists(constants.Config.FILE):
1344
+ telemetry.send("reinit")
1345
+ return
1346
+
1347
+ # Get the available templates
1348
+ templates: dict[str, Template] = fetch_app_templates()
1349
+
1350
+ # Prompt for a template if not provided.
1351
+ if template is None and len(templates) > 0:
1352
+ template = prompt_for_template(list(templates.values()))
1353
+ elif template is None:
1354
+ template = constants.Templates.DEFAULT
1355
+ assert template is not None
1356
+
1357
+ # If the blank template is selected, create a blank app.
1358
+ if template == constants.Templates.DEFAULT:
1359
+ # Default app creation behavior: a blank app.
1360
+ create_config(app_name)
1361
+ initialize_app_directory(app_name)
1362
+ else:
1363
+ # Fetch App templates from the backend server.
1364
+ console.debug(f"Available templates: {templates}")
1365
+
1366
+ # If user selects a template, it needs to exist.
1367
+ if template in templates:
1368
+ template_url = templates[template].code_url
1369
+ else:
1370
+ # Check if the template is a github repo.
1371
+ if template.startswith("https://github.com"):
1372
+ template_url = f"{template.strip('/')}/archive/main.zip"
1373
+ else:
1374
+ console.error(f"Template `{template}` not found.")
1375
+ raise typer.Exit(1)
1376
+ create_config_init_app_from_remote_template(
1377
+ app_name=app_name,
1378
+ template_url=template_url,
1379
+ )
1380
+
1381
+ telemetry.send("init")
reflex/utils/processes.py CHANGED
@@ -14,6 +14,7 @@ import psutil
14
14
  import typer
15
15
  from redis.exceptions import RedisError
16
16
 
17
+ from reflex import constants
17
18
  from reflex.utils import console, path_ops, prerequisites
18
19
 
19
20
 
@@ -227,7 +228,11 @@ def stream_logs(message: str, process: subprocess.Popen, progress=None):
227
228
  yield line
228
229
 
229
230
  # Check if the process failed (not printing the logs for SIGINT).
230
- if process.returncode not in [0, -2]:
231
+
232
+ # Windows uvicorn bug
233
+ # https://github.com/reflex-dev/reflex/issues/2335
234
+ accepted_return_codes = [0, -2, 15] if constants.IS_WINDOWS else [0, -2]
235
+ if process.returncode not in accepted_return_codes:
231
236
  console.error(f"{message} failed with exit code {process.returncode}")
232
237
  for line in logs:
233
238
  console.error(line, end="")
reflex/utils/telemetry.py CHANGED
@@ -4,7 +4,13 @@ from __future__ import annotations
4
4
 
5
5
  import multiprocessing
6
6
  import platform
7
- from datetime import datetime
7
+
8
+ try:
9
+ from datetime import UTC, datetime
10
+ except ImportError:
11
+ from datetime import datetime
12
+
13
+ UTC = None
8
14
 
9
15
  import httpx
10
16
  import psutil
@@ -99,6 +105,12 @@ def _prepare_event(event: str) -> dict:
99
105
  )
100
106
  return {}
101
107
 
108
+ if UTC is None:
109
+ # for python 3.8, 3.9 & 3.10
110
+ stamp = datetime.utcnow().isoformat()
111
+ else:
112
+ # for python 3.11 & 3.12
113
+ stamp = datetime.now(UTC).isoformat()
102
114
  return {
103
115
  "api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb",
104
116
  "event": event,
@@ -111,7 +123,7 @@ def _prepare_event(event: str) -> dict:
111
123
  "cpu_count": get_cpu_count(),
112
124
  "memory": get_memory(),
113
125
  },
114
- "timestamp": datetime.utcnow().isoformat(),
126
+ "timestamp": stamp,
115
127
  }
116
128
 
117
129
 
reflex/vars.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Define a state var."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import contextlib
@@ -21,7 +22,6 @@ from typing import (
21
22
  List,
22
23
  Literal,
23
24
  Optional,
24
- Set,
25
25
  Tuple,
26
26
  Type,
27
27
  Union,
@@ -119,7 +119,7 @@ class VarData(Base):
119
119
  imports: ImportDict = {}
120
120
 
121
121
  # Hooks that need to be present in the component to render this var
122
- hooks: Set[str] = set()
122
+ hooks: Dict[str, None] = {}
123
123
 
124
124
  # Positions of interpolated strings. This is used by the decoder to figure
125
125
  # out where the interpolations are and only escape the non-interpolated
@@ -138,7 +138,7 @@ class VarData(Base):
138
138
  """
139
139
  state = ""
140
140
  _imports = {}
141
- hooks = set()
141
+ hooks = {}
142
142
  interpolations = []
143
143
  for var_data in others:
144
144
  if var_data is None:
@@ -182,7 +182,7 @@ class VarData(Base):
182
182
  # not part of the vardata itself.
183
183
  return (
184
184
  self.state == other.state
185
- and self.hooks == other.hooks
185
+ and self.hooks.keys() == other.hooks.keys()
186
186
  and imports.collapse_imports(self.imports)
187
187
  == imports.collapse_imports(other.imports)
188
188
  )
@@ -200,7 +200,7 @@ class VarData(Base):
200
200
  lib: [import_var.dict() for import_var in import_vars]
201
201
  for lib, import_vars in self.imports.items()
202
202
  },
203
- "hooks": list(self.hooks),
203
+ "hooks": self.hooks,
204
204
  }
205
205
 
206
206
 
@@ -1659,7 +1659,7 @@ class Var:
1659
1659
  hooks={
1660
1660
  "const {0} = useContext(StateContexts.{0})".format(
1661
1661
  format.format_state_name(state_name)
1662
- )
1662
+ ): None
1663
1663
  },
1664
1664
  imports={
1665
1665
  f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
reflex/vars.pyi CHANGED
@@ -1,4 +1,5 @@
1
1
  """ Generated with stubgen from mypy, then manually edited, do not regen."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from dataclasses import dataclass
@@ -35,7 +36,7 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]: ...
35
36
  class VarData(Base):
36
37
  state: str
37
38
  imports: dict[str, set[ImportVar]]
38
- hooks: set[str]
39
+ hooks: Dict[str, None]
39
40
  interpolations: List[Tuple[int, int]]
40
41
  @classmethod
41
42
  def merge(cls, *others: VarData | None) -> VarData | None: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: reflex
3
- Version: 0.4.6a4
3
+ Version: 0.4.7a1
4
4
  Summary: Web apps in pure Python.
5
5
  Home-page: https://reflex.dev
6
6
  License: Apache-2.0
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.8
15
15
  Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
18
19
  Requires-Dist: alembic (>=1.11.1,<2.0)
19
20
  Requires-Dist: build (>=1.0.3,<2.0)
20
21
  Requires-Dist: charset-normalizer (>=3.3.2,<4.0)
@@ -74,6 +75,18 @@ Description-Content-Type: text/markdown
74
75
  [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md)
75
76
 
76
77
  ---
78
+
79
+ # Reflex
80
+
81
+ Reflex is a library to build full-stack web apps in pure Python.
82
+
83
+ Key features:
84
+ * **Pure Python** - Write your app's frontend and backend all in Python, no need to learn Javascript.
85
+ * **Full Flexibility** - Reflex is easy to get started with, but can also scale to complex apps.
86
+ * **Deploy Instantly** - After building, deploy your app with a [single command](https://reflex.dev/docs/hosting/deploy-quick-start/) or host it on your own server.
87
+
88
+ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) to learn how Reflex works under the hood.
89
+
77
90
  ## ⚙️ Installation
78
91
 
79
92
  Open a terminal and run (Requires Python 3.8+):
@@ -263,19 +276,11 @@ You can create a multi-page app by adding more pages.
263
276
  </div>
264
277
 
265
278
 
266
-
267
-
268
-
269
279
  ## ✅ Status
270
280
 
271
281
  Reflex launched in December 2022 with the name Pynecone.
272
282
 
273
- As of July 2023, we are in the **Public Beta** stage.
274
-
275
- - :white_check_mark: **Public Alpha**: Anyone can install and use Reflex. There may be issues, but we are working to resolve them actively.
276
- - :large_orange_diamond: **Public Beta**: Stable enough for non-enterprise use-cases.
277
- - **Public Hosting Beta**: _Optionally_, deploy and host your apps on Reflex!
278
- - **Public**: Reflex is production ready.
283
+ As of February 2024, our hosting service is in alpha! During this time anyone can deploy their apps for free. See our [roadmap](https://github.com/reflex-dev/reflex/issues/2727) to see what's planned.
279
284
 
280
285
  Reflex has new releases and features coming every week! Make sure to :star: star and :eyes: watch this repository to stay up to date.
281
286