Skrip Python yang portable?

July 5, 2025

metadata dalam script

Beberapa tahun yang lalu (2018) ketika mengisi pelatihan tentang Python, salah satu permasalahan utama adalah setup environment Python yang menantang / rumit. Ditambah lagi laptop peserta, ada yang menggunakan sistem operasi Linux, Windows, dan Mac OS.

Linux dan Mac OS dulu ada bawaan versi Python yang biasanya sudah agak lama (Python 2 atau Python 3 yang tidak terbaru), dan menginstall Python versi terbaru kala itu idealnya mudah. Menggunakan package manager yang ada di sistem (homebrew, apt, yum, ..), namun kadang pemahaman peserta tentang path Python mana yang benar sering menjadi titik nyeri. Bahkan peserta yang mempunyai latar belakang sebagai programmer,

"Saya sudah install package django dengan pip, namun ketika mengeksekusi django-admin hasilnya command not found: django-admin".

Apalagi peserta yang menggunakan Windows, opsinya bisa menggunakan installer resmi Python, wsl, anaconda, dan lain sebagainya. Perlu mengecek apakah Python executable ada di path terminal dan biasanya jarang yang familiar menggunakan command line.

Hal ini ditambah dengan package yang memerlukan beberapa library yang perlu terpasang di sistem. Seperti psycopg, Pillow, dan lain sebagainya.

Revolusi UV

Berawal dari ruff, alat yang digunakan untuk linter dan format source code skrip Python pada akhir tahun 2022. Ruff merupakan terobosan karena kinerjanya yang lebih cepat dari linter dan formatter Python yang ada, karena dibuat menggunakan Rust. Dan membuktikan bahwa tooling Python dapat jauh lebih cepat. Ruff dibuat oleh Charlie Marsh.

Kemudian rye, yang dibuat oleh Armin Ronacher (pembuat Flask framework), terinspirasi dari cargo sebagai pintu masuk utama untuk development di Rust. Dengan filosofi:

Untuk detailnya bisa kunjungi https://rye.astral.sh/philosophy/

Dan di tahun 2024, https://astral.sh/blog/uv, uv yang awalnya digunakan untuk menggantikan pip, sekarang sudah menjadi "cargo" untuk Python, https://astral.sh/blog/uv-unified-python-packaging.

PEP 723

Dengan PEP 723 yang dilanjutkan dengan spesifikasi yang didefinisikan di PyPA https://packaging.python.org/en/latest/specifications/inline-script-metadata/ Kita bisa menyertakan informasi metadata dalam skrip Python. Metadata ini dapat berupa versi Python yang diperlukan, packages python yang diperlukan, dan konfigurasi [tool].

Yang diperlukan cukup uv. Berikut instalasi nya:

$ curl -LsSf https://astral.sh/uv/install.sh | sh
downloading uv 0.7.19 aarch64-unknown-linux-gnu
no checksums to verify
installing to /home/sakti/.local/bin
  uv
  uvx
everything's installed!

To add $HOME/.local/bin to your PATH, either restart your shell or run:

    source $HOME/.local/bin/env (sh, bash, zsh)
    source $HOME/.local/bin/env.fish (fish)

kemudian kita buat skrip Pythonnya:

$ uv init --script progress --python 3.13
Initialized script at `progress`
$ # uv secara otomatis menginstall versi python yang dispesifikasikan
$ # tambah dependency
$ uv add --script progress rich
$ # edit file
$ cat progress
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "rich",
# ]
# ///

import time
from rich.progress import track

for i in track(range(20), description="Progress:"):
    time.sleep(0.05)
$ chmod +x progress
$ ./progress
Installed 4 packages in 9ms
Progress: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:01

Satu file Django

Untuk experiment kita coba buat Django app dengan satu file. Berikut file web.py:

# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "django",
# ]
# ///

import os
import django
from django.conf import settings
from django.http import HttpResponse
from django.urls import path
from django.core.management import execute_from_command_line

# Minimal Django settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '__main__')

if not settings.configured:
    settings.configure(
        SECRET_KEY='simple-key',
        DEBUG=True,
        ALLOWED_HOSTS=['*'],
        ROOT_URLCONF='__main__',
        INSTALLED_APPS=[],
        MIDDLEWARE=[],
    )
    django.setup()

# Simple view
def hello(request):
    return HttpResponse("Hello from Django!")

# URL configuration
urlpatterns = [
    path('', hello),
]

# Run server
if __name__ == '__main__':
    execute_from_command_line(['manage.py', 'runserver'])

Kemudian jalankan:

uv run web.py
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
July 05, 2025 - 20:32:26
Django version 5.2.4, using settings None
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI or ASGI server instead.
For more information on production servers see: https://docs.djangoproject.com/en/5.2/howto/deployment/

Dan coba cek menggunakan curl:

$ curl localhost:8000
Hello from Django!%

Sekarang kita bisa membuat "simple" portable / hermetic skrip Python, asalkan uv sudah terinstall.

Return to blog

footer