Skip to content

Commit ad60870

Browse files
Merge branch 'master' into maintenance/flask-connexion-rest-api-20260918
2 parents 97aaa6d + 3ed2087 commit ad60870

152 files changed

Lines changed: 9272 additions & 886 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎celery-async-tasks/README.md‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,28 @@
33
Example project for integrating Celery and Redis into a Django application.
44
This repository holds the code for the Real Python [Asynchronous Tasks With Django and Celery](https://realpython.com/asynchronous-tasks-with-django-and-celery/) tutorial.
55

6-
## Setup (macOS)
6+
## Setup
77

8-
To try the project, set up a virtual environment and install the listed dependencies:
8+
The project targets Linux and macOS. Celery doesn't support Windows.
9+
10+
To try the project, set up a virtual environment with Python 3.12 or newer and install the listed dependencies:
911

1012
```sh
1113
$ python -m venv venv
1214
$ source venv/bin/activate
1315
(venv) $ python -m pip install -r requirements.txt
1416
```
1517

16-
You'll also need to install Redis on your system:
18+
You'll also need to install Redis on your system.
19+
20+
On Linux (Debian or Ubuntu):
21+
22+
```sh
23+
$ sudo apt update
24+
$ sudo apt install redis
25+
```
26+
27+
On macOS with [Homebrew](https://brew.sh/):
1728

1829
```sh
1930
$ brew install redis
@@ -46,4 +57,4 @@ $ redis-server
4657
(venv) $ python -m celery -A django_celery worker -l info
4758
```
4859

49-
When all three processes are running, you can go to `localhost:8000/` and submit a feedback response. Celery will simulate a work-intensive process and send an email at the end of it. You'll see the email message show up in the log stream on the terminal window where the Celery worker is running.
60+
When all three processes are running, you can go to `http://localhost:8000/` and submit a feedback response. Celery will simulate a work-intensive process and send an email at the end of it. You'll see the email message show up in the log stream on the terminal window where the Celery worker is running.
Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
amqp==5.3.1
2-
asgiref==3.8.1
3-
billiard==4.2.1
4-
celery==5.4.0
5-
click==8.1.7
1+
amqp==5.4.0
2+
asgiref==3.12.1
3+
billiard==4.3.0
4+
celery==5.6.3
5+
click==8.5.0
66
click-didyoumean==0.3.1
7-
click-plugins==1.1.1
8-
click-repl==0.3.0
9-
Django==5.1.3
10-
kombu==5.4.2
11-
prompt_toolkit==3.0.48
7+
click-plugins==1.1.1.2
8+
click-repl==0.4.0
9+
Django==6.1.1
10+
kombu==5.6.2
11+
packaging==26.3
12+
prompt_toolkit==3.0.53
1213
python-dateutil==2.9.0.post0
13-
redis==5.2.0
14-
six==1.16.0
15-
sqlparse==0.5.2
16-
tzdata==2024.2
14+
redis==8.1.0
15+
six==1.17.0
16+
sqlparse==0.6.0
17+
typing_extensions==4.16.0
18+
tzdata==2026.4
19+
tzlocal==5.4.4
1720
vine==5.1.0
18-
wcwidth==0.2.13
21+
wcwidth==0.9.1
Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
amqp==5.3.1
2-
asgiref==3.8.1
3-
billiard==4.2.1
4-
celery==5.4.0
5-
click==8.1.7
1+
amqp==5.4.0
2+
asgiref==3.12.1
3+
billiard==4.3.0
4+
celery==5.6.3
5+
click==8.5.0
66
click-didyoumean==0.3.1
7-
click-plugins==1.1.1
8-
click-repl==0.3.0
9-
Django==5.1.3
10-
kombu==5.4.2
11-
prompt_toolkit==3.0.48
7+
click-plugins==1.1.1.2
8+
click-repl==0.4.0
9+
Django==6.1.1
10+
kombu==5.6.2
11+
packaging==26.3
12+
prompt_toolkit==3.0.53
1213
python-dateutil==2.9.0.post0
13-
redis==5.2.0
14-
six==1.16.0
15-
sqlparse==0.5.2
16-
tzdata==2024.2
14+
redis==8.1.0
15+
six==1.17.0
16+
sqlparse==0.6.0
17+
typing_extensions==4.16.0
18+
tzdata==2026.4
19+
tzlocal==5.4.4
1720
vine==5.1.0
18-
wcwidth==0.2.13
21+
wcwidth==0.9.1

‎generate-images-openai/README.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Generate Images in Python With OpenAI's GPT Image API
2+
3+
Learn to use the OpenAI Python library to create images with the GPT Image models. In the associated tutorial on [generating images with the OpenAI API](https://realpython.com/generate-images-openai/), you'll create images from text prompts, tune the size and quality of your results, and edit an image with a follow-up prompt. You'll learn how to interact with the Images API and incorporate this functionality into your Python scripts.
4+
5+
## Setup
6+
7+
Create and activate a virtual environment, then install the `openai` package:
8+
9+
```console
10+
$ python --version
11+
Python 3.14.6
12+
$ python -m venv venv
13+
$ source venv/bin/activate
14+
(venv) $ python -m pip install openai==3.13.0
15+
```
16+
17+
You need to be on Python 3.10 or higher.
18+
19+
The scripts read your API key from the `OPENAI_API_KEY` environment variable:
20+
21+
```console
22+
(venv) $ export OPENAI_API_KEY="<your-key-value-here>"
23+
```
24+
25+
## Create and Edit Images
26+
27+
Follow the instructions in [the tutorial](https://realpython.com/generate-images-openai/) to create images from text prompts, edit an image with a follow-up prompt, and convert a Base64 JSON response to a PNG image file.
28+
29+
You can find the code for each of these steps in dedicated scripts:
30+
31+
- `create_png.py`: Create an image from a text prompt and write it straight to a PNG file.
32+
- `create.py`: Create an image from a text prompt and save the whole JSON response to a file.
33+
- `convert.py`: Convert a Base64-encoded PNG image delivered in a JSON response to a PNG image file.
34+
- `edit.py`: Read Base64-encoded image data and make an API request to edit that image with a prompt.
35+
36+
In the tutorial, you'll walk through each of these scripts and their functionality and output in more detail.
37+
38+
Note that `convert.py` and `edit.py` read a saved response from the `responses/` directory. Run `create.py` first, then update the `JSON_FILE` and `SOURCE_FILE` constants to match the filename that it wrote.
39+
40+
## A Note on Models
41+
42+
The GPT Image models replaced DALL·E, which OpenAI [retired on May 12, 2026](https://developers.openai.com/api/docs/deprecations) along with the image variations endpoint. These scripts call `gpt-image-2.5-flare`.
43+
44+
OpenAI retires image models on a regular schedule, so if a script starts failing with a message like `The model 'gpt-image-2.5-flare' does not exist`, then check the deprecations page and swap the model name.

‎generate-images-openai/convert.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import json
2+
from base64 import b64decode
3+
from pathlib import Path
4+
5+
DATA_DIR = Path.cwd() / "responses"
6+
JSON_FILE = DATA_DIR / "An ec-1789379975.json"
7+
IMAGE_DIR = Path.cwd() / "images" / JSON_FILE.stem
8+
9+
IMAGE_DIR.mkdir(parents=True, exist_ok=True)
10+
11+
with open(JSON_FILE, mode="r", encoding="utf-8") as file:
12+
response = json.load(file)
13+
14+
for index, image_dict in enumerate(response["data"]):
15+
image_data = b64decode(image_dict["b64_json"])
16+
image_file = IMAGE_DIR / f"{JSON_FILE.stem}-{index}.png"
17+
with open(image_file, mode="wb") as png:
18+
png.write(image_data)

‎generate-images-openai/create.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import json
2+
from pathlib import Path
3+
4+
from openai import OpenAI
5+
6+
client = OpenAI()
7+
8+
PROMPT = "An eco-friendly computer from the 90s in the style of vaporwave"
9+
DATA_DIR = Path.cwd() / "responses"
10+
11+
DATA_DIR.mkdir(exist_ok=True)
12+
13+
response = client.images.generate(
14+
model="gpt-image-2.5-flare",
15+
prompt=PROMPT,
16+
n=1,
17+
size="1024x1024",
18+
quality="low",
19+
)
20+
21+
file_name = DATA_DIR / f"{PROMPT[:5]}-{response.created}.json"
22+
23+
with open(file_name, mode="w", encoding="utf-8") as file:
24+
json.dump(response.to_dict(), file)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from base64 import b64decode
2+
3+
from openai import OpenAI
4+
5+
client = OpenAI()
6+
7+
PROMPT = "A vaporwave computer"
8+
9+
response = client.images.generate(
10+
model="gpt-image-2.5-flare",
11+
prompt=PROMPT,
12+
n=1,
13+
size="1024x1024",
14+
quality="low",
15+
)
16+
17+
with open("vaporwave.png", mode="wb") as png:
18+
png.write(b64decode(response.data[0].b64_json))

‎generate-images-openai/edit.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import json
2+
from base64 import b64decode
3+
from pathlib import Path
4+
5+
from openai import OpenAI
6+
7+
client = OpenAI()
8+
9+
DATA_DIR = Path.cwd() / "responses"
10+
SOURCE_FILE = DATA_DIR / "An ec-1789379975.json"
11+
EDIT_PROMPT = "Add a large potted plant growing out of the computer"
12+
13+
with open(SOURCE_FILE, mode="r", encoding="utf-8") as json_file:
14+
saved_response = json.load(json_file)
15+
image_data = b64decode(saved_response["data"][0]["b64_json"])
16+
17+
response = client.images.edit(
18+
model="gpt-image-2.5-flare",
19+
image=("image.png", image_data),
20+
prompt=EDIT_PROMPT,
21+
n=3,
22+
size="1024x1024",
23+
quality="low",
24+
)
25+
26+
new_file_name = f"edit-{SOURCE_FILE.stem[:5]}-{response.created}.json"
27+
28+
with open(DATA_DIR / new_file_name, mode="w", encoding="utf-8") as file:
29+
json.dump(response.to_dict(), file)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
annotated-types==0.8.0
2+
anyio==4.15.1
3+
h11==0.16.0
4+
httpcore2==2.12.0
5+
httpx2==2.12.0
6+
idna==3.19
7+
jiter==0.17.0
8+
openai==3.13.0
9+
pydantic-core==2.46.5
10+
pydantic==2.13.5
11+
sniffio==1.3.1
12+
truststore==0.10.4
13+
typing-extensions==4.16.0
14+
typing-inspection==0.4.4

‎hello-jev/README.md‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Hello Jev: Getting Started With Jev in Python
2+
3+
This folder provides the code examples for *Get Started With Jev in Python*,
4+
a video on the [Real Python YouTube channel](https://www.youtube.com/@realpython).
5+
6+
Both scripts run the same train-station info desk. The desk asks a visitor
7+
whether they lost something and then points them at the lost-and-found counter
8+
or asks what else it can help with. The difference is how the answer is read:
9+
10+
- **`plain_python.py`** handles the input by hand. It only accepts an uppercase
11+
`Y` or `N`, so anything else sends the visitor back around the loop.
12+
- **`jev_noul.py`** replaces that branching with a single `Noul` question. Jev
13+
scores how affirmative the answer is on a scale from `0` to `1`, so the script
14+
compares thresholds instead of strings and understands replies like
15+
`yeah, I've lost something`.
16+
17+
## Setup
18+
19+
Install [uv](https://docs.astral.sh/uv/), then create the virtual environment.
20+
Because `pyproject.toml` pins `requires-python = ">=3.14"`, uv downloads
21+
CPython 3.14 for you if you don't have it yet:
22+
23+
```sh
24+
$ uv sync
25+
```
26+
27+
`jev_noul.py` reaches Jev through [OpenRouter](https://openrouter.ai/), which
28+
lets you try the model without opening an account with TypeSafe AI. Grab an
29+
OpenRouter API key, then create a `.env` file next to the scripts and add it:
30+
31+
```dotenv
32+
OPENROUTER_API_KEY=<YOUR-OPENROUTER-KEY>
33+
```
34+
35+
Keep `.env` out of version control. An API key belongs in your environment, not
36+
in your repository.
37+
38+
## Running the Examples
39+
40+
Start with the plain script to see where hand-written input handling gives up:
41+
42+
```sh
43+
$ uv run plain_python.py
44+
Did you lose something? (Y/N)
45+
```
46+
47+
Only an uppercase `Y` or `N` gets you past the prompt. Try `yes` or `y` and the
48+
script asks again.
49+
50+
Now run the Jev version. The `--env-file` flag tells uv to load your API key
51+
from `.env` before it starts the script:
52+
53+
```sh
54+
$ uv run --env-file .env jev_noul.py
55+
Did you lose something?
56+
```
57+
58+
Answer in your own words. `jev_noul.py` treats a Noul above `0.8` as a yes and
59+
below `0.2` as a no, and asks again for anything in between.
60+
61+
## Where to Go Next
62+
63+
`Noul` is one of three Jev primitives. `Score` rates something on a scale, and
64+
`Choice` picks from a list of options. The
65+
[TypeSafe SDK on PyPI](https://pypi.org/project/typesafe-sdk/) documents all
66+
three, and the TypeSafe AI blog explains the idea behind them in
67+
[Introducing System One Models and Jev](https://typesafe.ai/blog/introducing-system-one-models-and-jev).

0 commit comments

Comments
 (0)