Published: Sun 09 March 2025
By Sergey Panfilov
In Blog .
tags: Python
This article is written as an addition to Learn Python programming language in Y minutes .
It describes how to quickly install and start Python on Mac with uv Python package and project manager. Installations on Linux and Windows are not covered. They differ mostly in how uv
is installed, which is described at docs.astral.sh/uv/getting-started/installation/ .
Mac OS might have Python preinstalled already:
% which python
python not found
% which python3
/usr/bin/python3
% python3 -V
Python 3 .9.6
Python 3.9 is an old version with security support ending in October 2025.
That particular version came with Xcode Command Line Tools:
% python3
Python 3 .9.6 ( default, Nov 11 2024 , 03 :15:38)
[ Clang 16 .0.0 ( clang-1600.0.26.6)] on darwin
Type "help" , "copyright" , "credits" or "license" for more information.
>>> import sys, pprint
>>> pprint.pprint( sys.path)
[ '' ,
'/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python39.zip' ,
'/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9' ,
'/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/lib-dynload' ,
'/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/site-packages' ]
It's better not to use OS Python installations, because system scripts may depend on particular versions of Python packages and can be easily broken by installation of the different package versions.
uv
provides the simplest and the fastest way to have different Python installations at the moment.
It can be installed with homebrew :
There's also uv Python package available, which can be used in already existing Python installations.
Docker files can either use pre-installed uv Docker images or distroless Docker image (see docs.astral.sh/uv/guides/integration/docker/ ):
COPY --from= ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
The latest Python version can be installed with:
% uv python install
Installed Python 3 .13.2 in 1 .47s
+ cpython-3.13.2-macos-aarch64-none
It is also possible to provide specific Python versions for installation:
uv python install 3 .14
Installed Python 3 .14.0a5 in 1 .64s
+ cpython-3.14.0a5-macos-aarch64-none
Available and installed Python versions can be listed using:
% uv python list
cpython-3.14.0a5+freethreaded-macos-aarch64-none <download available>
cpython-3.14.0a5-macos-aarch64-none .local/share/uv/python/cpython-3.14.0a5-macos-aarch64-none/bin/python3.14
cpython-3.13.2+freethreaded-macos-aarch64-none <download available>
cpython-3.13.2-macos-aarch64-none .local/share/uv/python/cpython-3.13.2-macos-aarch64-none/bin/python3.13
cpython-3.12.9-macos-aarch64-none <download available>
cpython-3.11.11-macos-aarch64-none <download available>
cpython-3.10.16-macos-aarch64-none <download available>
cpython-3.9.21-macos-aarch64-none <download available>
cpython-3.9.6-macos-aarch64-none /Library/Developer/CommandLineTools/usr/bin/python3 -> ../../Library/Frameworks/Python3.framework/Versions/3.9/bin/python3
cpython-3.8.20-macos-aarch64-none <download available>
pypy-3.11.11-macos-aarch64-none <download available>
pypy-3.10.16-macos-aarch64-none <download available>
pypy-3.9.19-macos-aarch64-none <download available>
pypy-3.8.16-macos-aarch64-none <download available>
uv
installs Python by default in .local/share/uv/python
directory. It can be overridden with --install-dir
option or UV_PYTHON_INSTALL_DIR
environment variable.
Once Python interpreter is installed, it can be started with:
% uv run python
Python 3 .13.2 ( main, Feb 12 2025 , 14 :59:08) [ Clang 19 .1.6 ] on darwin
Python packages can be found at https://pypi.org/ and Python is bundled with Python package installer pip :
% uv run pip -V
pip 24 .3.1 from /Users/user/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13/site-packages/pip ( python 3 .13)
It installs packages in site-packages
directory of the active Python installation.
Different project may need different versions of the packages. Isolation of project dependencies in Python is achieved through virtual environments.
They can be created using Python built-in venv package:
user@vm ~ % time uv run python -m venv pyvenv
uv run python -m venv pyvenv 1 .13s user 0 .18s system 97 % cpu 1 .336 total
# virtual environment can be activated with
user@vm ~ % source pyvenv/bin/activate
# notice the change of the shell prompt to venv name and python path
( pyvenv) user@vm ~ % which python
/Users/user/pyvenv/bin/python
# virtual environment site-packages are added to import search paths
( pyvenv) user@vm ~ % python -c 'import sys; from pprint import pprint; pprint(sys.path)'
[ '' ,
'/Users/user/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python313.zip' ,
'/Users/user/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13' ,
'/Users/user/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13/lib-dynload' ,
'/Users/user/pyvenv/lib/python3.13/site-packages' ]
# deactivate virtual environment with
( pyvenv) user@vm ~ % deactivate
Built-in venv
package is a subset of virtualenv package.
uv
package manager has a built-in venv
command for manual creation of virtual environments. It also provides a subset of virtualenv
functionality, but is much faster than built-in venv
or standalone virtualenv
:
user@vm ~ % time uv venv uvvenv
Using CPython 3 .13.2
Creating virtual environment at: uvvenv
Activate with: source uvvenv/bin/activate
uv venv uvvenv 0 .01s user 0 .01s system 60 % cpu 0 .030 total
It took uv venv
0.030 seconds to create virtual environment (44 times faster) compared to 1.336 seconds by uv run python -m venv
above!
There's also pip
command in uv
, providing subset of pip package functionality. However, there's a better way of setting up a Python project with virtual environment and isolated dependencies:
user@vm ~ % uv init myproj
Initialized project ` myproj` at ` /Users/user/myproj`
user@vm ~ % cd myproj
# uv automatically creates pyproject.toml, readme and main module, intilises git repository including .gitignore file
user@vm myproj % ls -1a
.git
.gitignore
.python-version
README.md
main.py
pyproject.toml
Let's add all unstaged files and check the contents of them:
% git add .
% git diff --cached
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..505a3b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+# Python-generated files
+__pycache__/
+*.py[oc]
+build/
+dist/
+wheels/
+*.egg-info
+
+# Virtual environments
+.venv
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000..24ee5b1
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.13
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..6618285
--- /dev/null
+++ b/main.py
@@ -0,0 +1,6 @@
+def main():
+ print("Hello from myproj!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..7b5ee05
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,7 @@
+[project]
+name = "myproj"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.13"
+dependencies = []
Project dependencies can be added with uv add
:
% uv add ipython ipdb pandas jupyter
Added dependencies are recorded in pyproject.toml
, their versions are locked in uv.lock
and installed in automatically created .venv
virtual environment.
diff --git a/pyproject.toml b/pyproject.toml
index 7b5ee05..8f47326 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,4 +4,9 @@ version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
-dependencies = []
+dependencies = [
+ "ipdb>=0.13.13",
+ "ipython>=9.0.2",
+ "jupyter>=1.1.1",
+ "pandas>=2.2.3",
+]
uv.lock
is missing in git diff
output above, because it's not to git yet.
% git add uv.lock pyproject.toml
% git commit -m 'myproj init'
ipython is a better interactive shell, which has a simpler introspection of objects, integration with shell and much more.
It can be started with:
% uv run ipython
In [ 1 ] : ? # follow the prompt and type in to see ipython help
# supports simple capturing of shell commands
In [ 2 ] : project_files = !ls -a
In [ 3 ] : project_files
Out[ 3 ] :
[ '.' ,
'..' ,
'.git' ,
'.gitignore' ,
'.python-version' ,
'.venv' ,
'README.md' ,
'main.py' ,
'pyproject.toml' ,
'uv.lock' ]
In [ 4 ] : import this
The Zen of Python, by Tim Peters
...
# has handy introspection of any Python object and other handy features
In [ 5 ] : this?
Type: module
String form: <module 'this' from '/Users/user/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13/this.py' >
File: ~/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13/this.py
Docstring: <no docstring>
Jupyter notebook is another possibility to have interactive Python shell sessions, which can be persisted as documents with support of Markdown . It's popular in Data Science and Machine Learning.
Let's start jupyter lab server and create the first notebook:
Once server is started, the browser is launched with Jupyter Lab UI, which allows to create Python notebook or console (interactive shell), terminal or files (text, markdown or Python).
Python Notebooks are just JSON files. They can be rendered in Github (e.g. https://github.com/jupyter-naas/awesome-notebooks/blob/master/generate_readme.ipynb) or edited with Visual Studio Code .
It is also possible to quickly launch commands provided by Python packages without creating the project by using uvx alias for uv tool run
command:
Command above would run ruff formatter in the current directory.
See uv tools guide discussing using tools in more details.
It is worth to mention, that uv
is still new. Existing Python projects may use different tooling for management of Python versions and packages, for example pyenv and poetry . New projects is easier and faster to start with uv
.