---
title: "0.2.1 Install Python 3.14 with uv and its support cycle"
description: "How to install Python 3.13 or 3.14 with uv on Linux, macOS and Windows, which versions are supported in 2026 and how to check the interpreter from Python."
url: "https://pythonparatrading.com/en/python-trading-course/level-0/install-python-with-uv/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-0/install-python-with-uv.png"
---

# 0.2.1 · Installing Python with uv: versions and support cycle

> Python is not just one thing: there is a new version every October and each one lives for five years. This lesson installs the course version with uv, explains what it means for a version to be "supported" and shows how to check, from the code itself, which interpreter it is running on.

- Course: [Level 0: Python from scratch and a data mindset](https://pythonparatrading.com/en/python-trading-course/level-0/)
- Module: 0.2 · Modern working environment
- Duration: 35 min
- Prerequisites: [0.1.3 · Course structure, entry tests, personal repository and learning with AI](https://pythonparatrading.com/en/python-trading-course/level-0/course-structure-and-learning-with-ai/)

## By the end of this lesson

- Install uv and, with it, Python 3.14 (or 3.13) on Linux, macOS or Windows without touching the system Python.
- Explain the phases of a Python version's life cycle and say which versions are supported in October 2026.
- Check the interpreter's version, implementation and platform from Python with `sys` and `platform`.
- Compare versions as tuples, not as text, and store the environment's version alongside a backtest's results.

## Why the version matters

A Python program does not run on its own: it is run by an **interpreter**, the `python` program that reads your code
and turns it into operations. Each version of the interpreter adds features to the language, changes details of the
standard library and, over time, stops receiving fixes. The course libraries (NumPy, pandas, Polars…) publish packages
for specific versions and drop the old ones.

For systematic trading, this has three practical consequences:

- **Reproducibility.** A backtest must give the same result today and a year from now. If the interpreter or a library
  changes, a calculation can change with them: a different default value, a rounding, a warning that did not appear
  before.
- **Security.** A live system connects to a broker with API keys. If its interpreter no longer receives security
  patches, any flaw that is discovered will stay open forever.
- **Compatibility.** What is new in libraries reaches recent versions first. Staying on an old version ends up leaving
  you out of the improvements in pandas 3.0 or NumPy 2 (module 0.5).

The course uses **Python 3.14** (specifically 3.14.8, the version all the code you will see has been run with). 3.13
also works. This lesson explains why and how to install it.

## The life cycle of a version

Since 2019, Python has released a new version every year, in October (PEP 602). Each version goes through five phases,
which the Python developer's guide describes as follows[^1]:

```mermaid
stateDiagram-v2
  [*] --> feature
  feature --> prerelease: first beta
  prerelease --> bugfix: final release
  bugfix --> security: after two years
  security --> eol: after five years
  eol --> [*]
```

- **feature** and **prerelease**: the version is under development. Betas and release candidates are for testing
  libraries, not for working.
- **bugfix**: the version is now stable. It receives bug and security fixes, with new installers roughly every two
  months (3.14.1, 3.14.2…). It lasts about two years starting with 3.13; in earlier versions, 18 months.
- **security**: security patches only, released as source code and without installers.
- **end-of-life** (*eol*): five years after the release, support ends. The interpreter keeps working, but nobody fixes
  it.

The figure at the top draws that cycle for versions 3.10 to 3.15, with a line at October 2026. The thick bar is the
bugfix phase; the thin one, the security-only phase. The dates come from the developer's guide:

| Version | Released | Phase in October 2026 | End of support |
|---|---|---|---|
| 3.10 | Oct 4, 2021 | End of life | Oct 1, 2026 |
| 3.11 | Oct 24, 2022 | Security only | Oct 2027 (planned) |
| 3.12 | Oct 2, 2023 | Security only | Oct 2028 (planned) |
| 3.13 | Oct 7, 2024 | Security only | Oct 2029 (planned) |
| 3.14 | Oct 7, 2025 | Bugfix | Oct 2030 (planned) |
| 3.15 | Oct 9, 2026 | Bugfix | Oct 2031 (planned) |

Two dates this month change the map. On October 1, 2026, **Python 3.10 reached the end of its support**: if a server or
an old tutorial uses it, it is time to migrate. On October 9, **Python 3.15.0** was released[^2], which among other
changes uses UTF-8 as the default encoding and introduces explicit lazy imports (lesson 0.3.6).

<aside class="callout" data-kind="nota">

Why not 3.15, if it is the latest? A newly released version takes weeks or months to have packages for every library,
and its first bugfix releases fix errors nobody had found in the betas. For learning and for trading, a version with a
year of track record (3.14) is the sensible choice; 3.15 is fine for trying out. When the course is updated to 3.15,
one line of the project will change (lesson 0.2.2).

</aside>

## Installing uv and Python

**uv** is a Python project and package manager written in Rust by Astral. It replaces several classic tools (pip,
venv, pyenv, pip-tools) with a single, much faster one. In this course it does two things: install Python versions
(this lesson) and manage each project's environments and dependencies (lesson 0.2.2).

It is installed with an official script. On Linux and macOS, from the terminal (the terminal is covered in more detail in
lesson 0.2.4; for now, copying and pasting is enough):

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

On Windows, from PowerShell:

```bash
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```

If you prefer a system package manager, `brew install uv` (macOS) or `winget install --id=astral-sh.uv -e` (Windows)
also work. Close and reopen the terminal and check that it responds:

```bash
uv --version
```

<aside class="callout" data-kind="riesgo">

Running a script downloaded from the internet with `| sh` or `| iex` gives that script your user's permissions. Do it
only with official addresses you have checked letter by letter (`astral.sh`, not a link from a forum or a video). The
uv documentation explains how to read the script before running it.

</aside>

With uv installed, installing Python is a single command:

```bash
uv python install 3.14         # the latest 3.14.x
uv python list                 # installed and available versions
python3.14 --version           # uv leaves an executable named after the version
uv python upgrade 3.14         # move to the latest 3.14.x later on
```

uv downloads a precompiled Python distribution and stores it in its own folder. It does not overwrite anything:

- **Linux**: the distribution ships its own Python, used by system tools. Do not upgrade it or install packages into it
  with `sudo pip`; a change there can break the system package manager.
- **macOS**: there may be a `python3` from the Xcode tools, usually old. Leave it alone.
- **Windows**: you do not need the python.org installer or the Microsoft Store if you use uv.

Nor do you need to install Python by hand before starting a project: if a project asks for 3.14 and you do not have
it, uv downloads it when you run the first command. In practice you will almost never call `python` directly, but
`uv run python` inside a project, which picks the right version for you (lesson 0.2.2).

To pin the version of a folder, `uv python pin 3.14` creates a `.python-version` file in it with the number. uv reads
it every time you work in that folder, and other tools understand it too. The course project will have it from the
start (lesson 0.2.2).

<aside class="callout" data-kind="nota">

When listing versions you will see some ending in `t`, such as `3.14t`. They are the *free-threaded* build of CPython:
without the global interpreter lock (GIL), several threads can run Python in parallel. It was added in 3.13 and is much
more mature in 3.14, although single-threaded code runs somewhat slower and not every library supports it. It is not
the course build; it is installed with `uv python install 3.14t` and is covered in lesson 0.4.6.

</aside>

## Checking the interpreter from Python

Installing is half the job. The other half is knowing, from the code itself, which interpreter it is running on: on
your laptop, on a server or on someone else's computer. The standard library's `sys` module tells you:

```python
import sys

sys.version_info
```
```output
sys.version_info(major=3, minor=14, micro=8, releaselevel='final', serial=0)
```

`sys.version_info` is a named tuple: major, minor and patch (*micro*) version, and the release level (`final` in a
stable release; `alpha`, `beta` or `candidate` in a pre-release). Since it is a tuple, it can be compared with another
tuple:

```python
print(sys.version_info[:2])
print(sys.version_info >= (3, 13))
print(sys.version_info >= (3, 15))
```
```output
(3, 14)
True
False
```

The `platform` module adds the rest: the version as text, the implementation (CPython is the reference one, the one uv
installs) and the machine's operating system and architecture. On Linux, `platform.system()` returns `Linux`; on macOS,
`Darwin`; on Windows, `Windows`. The output below is from the machine where the course was run:

```python
import platform

print("Python:", platform.python_version())
print("Implementation:", platform.python_implementation())
print("System:", platform.system(), platform.machine())
```
```output
Python: 3.14.8
Implementation: CPython
System: Linux x86_64
```

## Versions as tuples, not as text

A classic mistake when working with versions is comparing them as text strings. Python compares strings character by
character, and the character "1" comes before "9":

```python
versiones = ["3.9", "3.10", "3.14", "3.13"]
print(sorted(versiones))
print("3.9" > "3.10")
```
```output
['3.10', '3.13', '3.14', '3.9']
True
```

Going by the text, 3.9 is newer than 3.10. It is the same problem as sorting dates written as "9/3/2025" or amounts
stored as text: the order looks reasonable until it stops being so. The solution is to convert each version into a
tuple of integers before comparing it:

```python
def como_tupla(version: str) -> tuple[int, ...]:
    """'3.10' -> (3, 10)"""
    return tuple(int(parte) for parte in version.split("."))

print(sorted(versiones, key=como_tupla))
print(como_tupla("3.9") > como_tupla("3.10"))
print(max(versiones, key=como_tupla))
```
```output
['3.9', '3.10', '3.13', '3.14']
False
3.14
```

Tuples are compared element by element: first the 3 with the 3 and, since they tie, the 9 with the 10, this time as
numbers. That is why `sys.version_info` is a tuple and not text. The `packaging` library, which pip and uv use under
the hood, does the same with more complex versions ("2.5.4", "3.0.0rc1"), but to compare Python versions the tuple is
enough.

## Which versions are supported today

With the table above you can compute how many months of support each version has left on a given date. A fixed date
(October 21, 2026) is used so that the result does not change depending on the day you run the cell; the developer's
guide gives the end-of-support dates as year and month:

```python
fin_soporte = {
    "3.10": (2026, 10), "3.11": (2027, 10), "3.12": (2028, 10),
    "3.13": (2029, 10), "3.14": (2030, 10), "3.15": (2031, 10),
}
hoy = (2026, 10)

for version, (anio, mes) in fin_soporte.items():
    meses = (anio - hoy[0]) * 12 + (mes - hoy[1])
    estado = "out of support" if meses <= 0 else f"{meses} months"
    print(f"Python {version:>5}: {estado}")
```
```output
Python  3.10: out of support
Python  3.11: 12 months
Python  3.12: 24 months
Python  3.13: 36 months
Python  3.14: 48 months
Python  3.15: 60 months
```

For a new project, the rule of thumb: the most recent version with at least a few months of track record and every
library available. For a live system, also a version with years of support left, because migrating a system that
trades costs more than migrating a notebook.

## The version, alongside the results

A backtest produces numbers: return, volatility, drawdown. Those numbers depend on the data, the code and the
environment. Git will store the code (lesson 0.2.5) and your `datos/` folder the data; the environment has to be
recorded. A dictionary with the versions, stored alongside each result, is enough to start with:

```python
import json
import numpy as np
import pandas as pd

entorno = {
    "python": platform.python_version(),
    "implementacion": platform.python_implementation(),
    "sistema": platform.system(),
    "numpy": np.__version__,
    "pandas": pd.__version__,
}
print(json.dumps(entorno, indent=2))
```
```output
{
  "python": "3.14.8",
  "implementacion": "CPython",
  "sistema": "Linux",
  "numpy": "2.5.4",
  "pandas": "3.0.6"
}
```

If six months from now the same backtest gives a different Sharpe, the first step is to compare this block with the
current one. Lesson 0.2.2 goes one step further: it pins the exact versions of every library in a file (`uv.lock`) so
that the environment is identical on any machine.

## Exercises

1. Install uv and Python 3.14 on your computer. Run `uv python list` and note which Python versions you already had
   installed (the system ones) and where they are.

**Solution.**

The output depends on your machine. On Linux the system Python usually appears at `/usr/bin/python3` (whatever version
your distribution ships) and uv's 3.14 in a folder under your user. On macOS the `python3` from the Xcode tools may
appear and, if you use Homebrew, its own. On Windows, only the ones you installed yourself. What matters: the system
Python is still there and you have not touched it.

2. Write a function `exigir_python(minima)` that takes a tuple such as `(3, 13)` and raises a `RuntimeError` with a
   clear message if the interpreter is older. Where would you call it in a system that trades live?

**Solution.**

```python
def exigir_python(minima: tuple[int, int]) -> None:
    actual = sys.version_info[:2]
    if actual < minima:
        raise RuntimeError(
            f"Python {minima[0]}.{minima[1]} or later is required; "
            f"this one is {actual[0]}.{actual[1]}"
        )

exigir_python((3, 13))
print("Version OK")
```
```output
Version OK
```

At the start of the program, before connecting to the broker or loading data: it is better for the system not to
start than to start on an interpreter nobody has tested it with. Exceptions are covered in lesson 0.3.6.

3. Extend the environment metadata cell with the execution date and time in UTC and save it to an `entorno.json` file.
   Why UTC and not local time?

**Solution.**

```python
from datetime import datetime, timezone
from pathlib import Path

entorno["ejecutado_utc"] = datetime(2026, 10, 21, 16, 30,
                                    tzinfo=timezone.utc).isoformat()
Path("entorno.json").write_text(json.dumps(entorno, indent=2))
print(json.loads(Path("entorno.json").read_text())["ejecutado_utc"])
```
```output
2026-10-21T16:30:00+00:00
```

In your own code you would use `datetime.now(timezone.utc)`; here the date is fixed so that the output is always the
same. UTC because it is the same on every machine and has no daylight saving changes: a server in Frankfurt, another
in Virginia and your laptop write the same time for the same instant. Market time zones are covered in lesson 0.4.4.

[^1]: Python Developer's Guide, "Status of Python versions" (devguide.python.org/versions), accessed on October 21,
    2026. The end-of-support dates from 3.11 on are planned and are adjusted by each version's release managers.

[^2]: Python 3.15.0 release notes, python.org, October 9, 2026.

## Self-assessment

1. A server running your live system has Python 3.10. What has changed since October 1, 2026?

   - A. Nothing: the code keeps working and will keep receiving patches
   - B. Python 3.10 has stopped receiving fixes, including security fixes
   - C. Python 3.10 stops working on Linux
   - D. pip can no longer install packages on 3.10

   *Answer B: Python 3.10 has stopped receiving fixes, including security fixes.* End of support does not switch anything off: the interpreter keeps working. What ends are the patches, security ones included, and little by little libraries stop publishing releases for it. A live system should not depend on an unsupported version.

2. What does `sorted(["3.9", "3.10", "3.14"])` return?

   - A. `['3.9', '3.10', '3.14']`
   - B. `['3.14', '3.10', '3.9']`
   - C. An error, because they are not numbers
   - D. `['3.10', '3.14', '3.9']`

   *Answer D: `['3.10', '3.14', '3.9']`.* Strings are compared character by character: "1" comes before "9", so "3.10" and "3.14" come before "3.9". Versions are compared as tuples of integers, like `sys.version_info`.

3. What phase is Python 3.14 in, in October 2026?

   - A. Bugfix: it receives bug and security fixes, with installers roughly every two months
   - B. Development (feature)
   - C. Security only
   - D. End of life

   *Answer A: Bugfix: it receives bug and security fixes, with installers roughly every two months.* It was released on October 7, 2025, and versions since 3.13 get about two years of bug fixes. It will move to security-only around October 2027 and its support ends in October 2030.

4. Why is it worth storing the versions of Python, NumPy and pandas alongside a backtest's results?

   - A. Because regulations require it
   - B. To make the backtest run faster
   - C. To be able to reproduce the result: a version change can change a calculation, a default value or performance
   - D. It is useless if the code is in Git

   *Answer C: To be able to reproduce the result: a version change can change a calculation, a default value or performance.* Git stores the code, not the interpreter or the libraries. If a result changes when you repeat it months later, the first question is whether the environment changed. Lesson 0.2.2 solves the problem at the root with a lockfile.

## Key points

- A new Python version comes out every October. It gets about two years of bug fixes, then security patches only, and its support ends after five years.
- In October 2026: 3.10 out of support since the 1st, 3.15 released on the 9th; the course uses 3.14 and 3.13 also works.
- uv installs and manages Python versions without touching the system Python: `uv python install 3.14`.
- `sys.version_info` is a tuple: compare it with `(3, 13)`, never compare versions as text.
- A backtest result without the environment's version is not reproducible: store it alongside the numbers.

## Resources

### Research

- [The working environment - 1](https://pythonparatrading.com/en/docker-for-trading/) · Research-0013 · 4 min

### Documentation

- [Python Developer's Guide: Status of Python versions](https://devguide.python.org/versions/) · devguide.python.org
- [Python 3.15.0 (release notes, October 9, 2026)](https://www.python.org/downloads/release/python-3150/) · python.org
- [uv: installation (official documentation)](https://docs.astral.sh/uv/getting-started/installation/) · docs.astral.sh
- [uv: installing and managing Python versions](https://docs.astral.sh/uv/guides/install-python/) · docs.astral.sh
- [PEP 602: annual release cycle for Python](https://peps.python.org/pep-0602/) · peps.python.org

Previous lesson: [0.1.3 · Course structure, entry tests, personal repository and learning with AI](https://pythonparatrading.com/en/python-trading-course/level-0/course-structure-and-learning-with-ai/)

Next lesson: [0.2.2 · Virtual environments and dependencies: uv init, uv add, pyproject.toml and lockfiles](https://pythonparatrading.com/en/python-trading-course/level-0/virtual-environments-dependencies-with-uv/)
