Skip to content

Commit ca3ac5a

Browse files
committed
Merge remote-tracking branch 'origin/main' into masenf/event-context-rb
2 parents e2c1ed1 + 8edcb7b commit ca3ac5a

11 files changed

Lines changed: 209 additions & 74 deletions

File tree

README.md

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,42 +56,47 @@ mkdir my_app_name
5656
cd my_app_name
5757
```
5858

59-
### 2. Set up a virtual environment
59+
### 2. Install uv
6060

61-
Create and activate virtual environment
61+
Reflex recommends [uv](https://docs.astral.sh/uv/) for managing your project environment and dependencies.
62+
See the [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/) for your platform.
6263

6364
```bash
64-
# On Windows:
65-
python -m venv .venv
66-
.venv\Scripts\activate
65+
# macOS/Linux
66+
curl -LsSf https://astral.sh/uv/install.sh | sh
6767

68-
# On macOS/Linux:
69-
python3 -m venv .venv
70-
source .venv/bin/activate
68+
# Windows (PowerShell)
69+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
7170
```
7271

73-
### 3. Install Reflex
72+
### 3. Initialize the Python project
7473

75-
Reflex is available as a pip package (Requires Python 3.10+):
74+
```bash
75+
uv init
76+
```
77+
78+
### 4. Add Reflex
79+
80+
Reflex requires Python 3.10+:
7681

7782
```bash
78-
pip install reflex
83+
uv add reflex
7984
```
8085

81-
### 4. Initialize the project
86+
### 5. Initialize the project
8287

8388
This command initializes a template app in your new directory:
8489

8590
```bash
86-
reflex init
91+
uv run reflex init
8792
```
8893

89-
### 5. Run the app
94+
### 6. Run the app
9095

9196
You can run this app in development mode:
9297

9398
```bash
94-
reflex run
99+
uv run reflex run
95100
```
96101

97102
You should see your app running at http://localhost:3000.
@@ -100,7 +105,7 @@ Now you can modify the source code in `my_app_name/my_app_name.py`. Reflex has f
100105

101106
### Troubleshooting
102107

103-
If you installed Reflex without a virtual environment and the `reflex` command is not found, you can run commands using: `python3 -m reflex init` and `python3 -m reflex run`
108+
If the `reflex` command is not on your PATH, run it through uv instead: `uv run reflex init` and `uv run reflex run`
104109

105110
## 🫧 Example App
106111

reflex/docs/advanced_onboarding/code_structure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ example-big-app/
357357
│ ├─ state.py
358358
│ ├─ template.py
359359
├─ uploaded_files/
360-
├─ requirements.txt
360+
├─ pyproject.toml
361361
├─ rxconfig.py
362362
```
363363

reflex/docs/advanced_onboarding/configuration.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Reflex apps can be configured using a configuration file, environment variables,
55

66
## Configuration File
77

8-
Running `reflex init` will create an `rxconfig.py` file in your root directory.
8+
Running `uv run reflex init` will create an `rxconfig.py` file in your root directory.
99
You can pass keyword arguments to the `Config` class to configure your app.
1010

1111
For example:
@@ -31,15 +31,15 @@ You can override the configuration file by setting environment variables.
3131
For example, to override the `frontend_port` setting, you can set the `FRONTEND_PORT` environment variable.
3232

3333
```bash
34-
FRONTEND_PORT=3001 reflex run
34+
FRONTEND_PORT=3001 uv run reflex run
3535
```
3636

3737
## Command Line Arguments
3838

39-
Finally, you can override the configuration file and environment variables by passing command line arguments to `reflex run`.
39+
Finally, you can override the configuration file and environment variables by passing command line arguments to `uv run reflex run`.
4040

4141
```bash
42-
reflex run --frontend-port 3001
42+
uv run reflex run --frontend-port 3001
4343
```
4444

4545
See the [CLI reference](/docs/api-reference/cli) for all the arguments available.

reflex/docs/advanced_onboarding/how-reflex-works.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ rx.box(height="1em")
6969

7070
We wanted Reflex apps to look and feel like a traditional web app to the end user, while still being easy to build and maintain for the developer. To do this, we built on top of mature and popular web technologies.
7171

72-
When you `reflex run` your app, Reflex compiles the frontend down to a single-page [Next.js](https://nextjs.org) app and serves it on a port (by default `3000`) that you can access in your browser.
72+
When you run `uv run reflex run`, Reflex compiles the frontend down to a single-page [Next.js](https://nextjs.org) app and serves it on a port (by default `3000`) that you can access in your browser.
7373

7474
The frontend's job is to reflect the app's state, and send events to the backend when the user interacts with the UI. No actual logic is run on the frontend.
7575

@@ -128,7 +128,7 @@ Beyond this, Reflex components can be styled using the full power of CSS. We lev
128128

129129
Now let's look at how we added interactivity to our apps.
130130

131-
In Reflex only the frontend compiles to Javascript and runs on the user's browser, while all the state and logic stays in Python and is run on the server. When you `reflex run`, we start a FastAPI server (by default on port `8000`) that the frontend connects to through a websocket.
131+
In Reflex only the frontend compiles to Javascript and runs on the user's browser, while all the state and logic stays in Python and is run on the server. When you run `uv run reflex run`, we start a FastAPI server (by default on port `8000`) that the frontend connects to through a websocket.
132132

133133
All the state and logic are defined within a `State` class.
134134

reflex/docs/getting_started/basics.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ This page gives an introduction to the most common concepts that you will use to
1818
- Create pages and navigate between them
1919
```
2020

21-
[Install](/docs/getting_started/installation) `reflex` using pip.
21+
[Install](/docs/getting_started/installation) `reflex` with uv before continuing.
2222

2323
```bash
24-
pip install reflex
24+
uv add reflex
2525
```
2626

2727
Import the `reflex` library to get started.

reflex/docs/getting_started/dashboard_tutorial.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ Don't worry if you don't understand the code above, in this tutorial we are goin
326326

327327
## Setup for the tutorial
328328

329-
Check out the [installation docs](/docs/getting_started/installation) to get Reflex set up on your machine. Follow these to create a folder called `dashboard_tutorial`, which you will `cd` into and `pip install reflex`.
329+
Check out the [installation docs](/docs/getting_started/installation) to get Reflex set up on your machine. Follow these to create a folder called `dashboard_tutorial`, which you will `cd` into, then run `uv init` and `uv add reflex`.
330330

331-
We will choose template `0` when we run `reflex init` to get the blank template. Finally run `reflex run` to start the app and confirm everything is set up correctly.
331+
We will choose template `0` when we run `uv run reflex init` to get the blank template. Finally run `uv run reflex run` to start the app and confirm everything is set up correctly.
332332

333333
## Overview
334334

reflex/docs/getting_started/project-structure.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,36 @@ Let's create a new app called `{app_name}`
1212
```bash
1313
mkdir {app_name}
1414
cd {app_name}
15-
reflex init
15+
uv init
16+
uv add reflex
17+
uv run reflex init
1618
```
1719

1820
This will create a directory structure like this:
1921

2022
```bash
2123
{app_name}
24+
├── .venv
2225
├── .web
2326
├── assets
2427
├── {app_name}
2528
│ ├── __init__.py
2629
│ └── {app_name}.py
27-
└── rxconfig.py
30+
├── .gitignore
31+
├── .python-version
32+
├── pyproject.toml
33+
├── rxconfig.py
34+
└── uv.lock
2835
```
2936

37+
`uv init` may also create helper files such as `README.md`, `main.py`, and Git metadata. The tree above focuses on the main files you will interact with while building a Reflex app.
38+
3039
Let's go over each of these directories and files.
3140

41+
## .venv
42+
43+
`uv add reflex` creates a local virtual environment in `.venv` by default. This keeps your app dependencies isolated from the rest of your system Python.
44+
3245
## .web
3346

3447
This is where the compiled Javascript files will be stored. You will never need to touch this directory, but it can be useful for debugging.
@@ -45,14 +58,18 @@ For example, if you save an image to `assets/image.png` you can display it from
4558
rx.image(src="https://web.reflex-assets.dev/other/image.png")
4659
```
4760

48-
j
49-
5061
## Main Project
5162

5263
Initializing your project creates a directory with the same name as your app. This is where you will write your app's logic.
5364

5465
Reflex generates a default app within the `{app_name}/{app_name}.py` file. You can modify this file to customize your app.
5566

67+
## Python Project Files
68+
69+
`pyproject.toml` defines your Python project metadata and dependencies. `uv add reflex` records the Reflex dependency there before you initialize the app.
70+
71+
`uv.lock` stores the fully resolved dependency set for reproducible installs. Commit it to version control so everyone working on the app gets the same Python package versions.
72+
5673
## Configuration
5774

5875
The `rxconfig.py` file can be used to configure your app. By default it looks something like this:

reflex/reflex.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,22 @@ def _init(
8989
# Initialize the .gitignore.
9090
frontend_skeleton.initialize_gitignore()
9191

92-
# Initialize the requirements.txt.
93-
needs_user_manual_update = frontend_skeleton.initialize_requirements_txt()
94-
9592
template_msg = f" using the {template} template" if template else ""
96-
# Finish initializing the app.
97-
console.success(
98-
f"Initialized {app_name}{template_msg}."
99-
+ (
100-
f" Make sure to add {constants.RequirementsTxt.DEFAULTS_STUB + constants.Reflex.VERSION} to your requirements.txt or pyproject.toml file."
101-
if needs_user_manual_update
102-
else ""
103-
)
93+
if Path(constants.PyprojectToml.FILE).exists():
94+
needs_user_manual_update = False
95+
next_steps = " Run `uv run reflex run` to start the app."
96+
else:
97+
needs_user_manual_update = frontend_skeleton.initialize_requirements_txt()
98+
next_steps = " Install dependencies from `requirements.txt` with `uv pip install -r requirements.txt` (or your preferred installer) before running `uv run reflex run`."
99+
manual_update = (
100+
f" Make sure to add `{constants.RequirementsTxt.DEFAULTS_STUB + constants.Reflex.VERSION}` to your requirements.txt file."
101+
if needs_user_manual_update
102+
else ""
104103
)
105104

105+
# Finish initializing the app.
106+
console.success(f"Initialized {app_name}{template_msg}.{manual_update}{next_steps}")
107+
106108

107109
@cli.command()
108110
@loglevel_option

reflex/utils/frontend_skeleton.py

Lines changed: 85 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import json
44
import random
5-
import re
65
from pathlib import Path
76

87
from reflex_base import constants
@@ -42,44 +41,101 @@ def initialize_gitignore(
4241
gitignore_file.write_text("\n".join(files_to_ignore) + "\n")
4342

4443

45-
def initialize_requirements_txt() -> bool:
44+
def _read_dependency_file(file_path: Path) -> tuple[str | None, str | None]:
45+
"""Read a dependency file with a forgiving encoding strategy.
46+
47+
Args:
48+
file_path: The file to read.
49+
50+
Returns:
51+
A tuple of file content and the encoding used to read it.
52+
"""
53+
try:
54+
return file_path.read_text(), None
55+
except UnicodeDecodeError:
56+
pass
57+
except Exception as e:
58+
console.error(f"Failed to read {file_path} due to {e}.")
59+
raise SystemExit(1) from None
60+
61+
try:
62+
return file_path.read_text(encoding="utf-8"), "utf-8"
63+
except UnicodeDecodeError:
64+
return None, None
65+
except Exception as e:
66+
console.error(f"Failed to read {file_path} due to {e}.")
67+
raise SystemExit(1) from None
68+
69+
70+
def _has_reflex_requirement_line(requirements_text: str) -> bool:
71+
"""Check whether requirements.txt already contains reflex.
72+
73+
Returns:
74+
Whether reflex is already present in the requirements text.
75+
"""
76+
return any(
77+
_is_reflex_dependency_spec(line) for line in requirements_text.splitlines()
78+
)
79+
80+
81+
def _is_reflex_dependency_spec(requirement: str) -> bool:
82+
"""Check whether a dependency specification refers to the reflex package.
83+
84+
Args:
85+
requirement: The dependency specification to check.
86+
87+
Returns:
88+
Whether the specification refers to the reflex package.
89+
"""
90+
requirement = requirement.strip()
91+
if not requirement.lower().startswith("reflex"):
92+
return False
93+
94+
suffix = requirement[len("reflex") :]
95+
if suffix.startswith("["):
96+
extras_end = suffix.find("]")
97+
if extras_end == -1:
98+
return False
99+
suffix = suffix[extras_end + 1 :]
100+
101+
return not suffix or suffix.lstrip().startswith((
102+
"==",
103+
"!=",
104+
">=",
105+
"<=",
106+
"~=",
107+
">",
108+
"<",
109+
";",
110+
"@",
111+
))
112+
113+
114+
def initialize_requirements_txt(
115+
requirements_file_path: Path = Path(constants.RequirementsTxt.FILE),
116+
pyproject_file_path: Path = Path(constants.PyprojectToml.FILE),
117+
) -> bool:
46118
"""Initialize the requirements.txt file.
47-
If absent and no pyproject.toml file exists, generate one for the user.
48-
If the requirements.txt does not have reflex as dependency,
49-
generate a requirement pinning current version and append to
50-
the requirements.txt file.
119+
120+
If a project already uses pyproject.toml, leave dependency management to the
121+
package manager. Otherwise ensure requirements.txt pins the current Reflex
122+
version for legacy workflows.
51123
52124
Returns:
53125
True if the user has to update the requirements.txt file.
54-
55-
Raises:
56-
SystemExit: If the requirements.txt file cannot be read or written to.
57126
"""
58-
requirements_file_path = Path(constants.RequirementsTxt.FILE)
59-
if (
60-
not requirements_file_path.exists()
61-
and Path(constants.PyprojectToml.FILE).exists()
62-
):
63-
return True
127+
if not requirements_file_path.exists() and pyproject_file_path.exists():
128+
return False
64129

65130
requirements_file_path.touch(exist_ok=True)
66131

67-
for encoding in [None, "utf-8"]:
68-
try:
69-
content = requirements_file_path.read_text(encoding)
70-
break
71-
except UnicodeDecodeError:
72-
continue
73-
except Exception as e:
74-
console.error(f"Failed to read {requirements_file_path} due to {e}.")
75-
raise SystemExit(1) from None
76-
else:
132+
content, encoding = _read_dependency_file(requirements_file_path)
133+
if content is None:
77134
return True
78135

79-
for line in content.splitlines():
80-
if re.match(r"^reflex[^a-zA-Z0-9]", line):
81-
console.debug(f"{requirements_file_path} already has reflex as dependency.")
82-
return False
136+
if _has_reflex_requirement_line(content):
137+
console.debug(f"{requirements_file_path} already has reflex as dependency.")
138+
return False
83139

84140
console.debug(
85141
f"Appending {constants.RequirementsTxt.DEFAULTS_STUB} to {requirements_file_path}"

0 commit comments

Comments
 (0)