5 poin oleh GN⁺ 2025-03-31 | 1 komentar | Bagikan ke WhatsApp
  • Bahkan skrip kecil untuk menguji API pun perlu menyesuaikan lingkungan eksekusi karena dependensi, tetapi dengan shebang eksekusi uv skrip bisa dibuat agar langsung dapat dijalankan tanpa prosedur instalasi
  • Contoh jam_users.py menguji endpoint /users pada API Go dengan httpx, IPython, dan loguru, lalu menghapus dan membuat pengguna sebelum masuk ke REPL
  • Cara lama mengharuskan paket dipasang secara global pada Python sistem atau menyiapkan virtual environment secara manual, sehingga berbagi dan menjalankan ulang skrip menjadi merepotkan
  • Jika mendeklarasikan dependencies = ["ipython", "httpx", "loguru"] pada header # /// script lalu menjalankan uv run jam_users.py, uv akan menangani lingkungan terisolasi dan instalasi dependensi
  • Dengan menambahkan shebang #!/usr/bin/env -S uv run --script dan izin eksekusi, pada sistem Unix skrip bisa dijalankan seperti ./jam_users.py dengan syarat hanya uv yang sudah terpasang

Mendeklarasikan dependensi di dalam skrip

  • Contoh jam_users.py menyiapkan data pengguna uji untuk API lokal http://localhost:4000/v1/users
    • Mengirim permintaan API dengan httpx
    • Masuk ke REPL IPython untuk memeriksa respons dan melanjutkan pengujian
    • Mencatat log penghapusan dan pembuatan dengan loguru
  • Alur skrip dasarnya adalah menginisialisasi ulang daftar pengguna lalu mengisi kembali data uji
    • Mengambil daftar pengguna yang ada dengan GET /v1/users
    • Memanggil DELETE /v1/users/{id} untuk setiap pengguna
    • Mengirim daftar pengguna yang sudah disiapkan sebagai JSON ke POST /v1/users
    • Setelah itu menjalankan IPython.embed() untuk membuka REPL
  • Untuk menjalankannya dengan python jam_users.py, httpx, IPython, dan loguru harus sudah terpasang di lingkungan eksekusi
  • Memasang secara global pada Python sistem atau membuat virtual environment terpisah memang bisa, tetapi keduanya tetap memerlukan persiapan sebelum dijalankan
    • Instalasi global bisa membuat Python sistem penuh dengan paket
    • Pendekatan virtual environment mengharuskan proses pembuatan, aktivasi, instalasi, dan eksekusi dikelola secara manual
    • Keduanya juga memerlukan Python sistem yang kompatibel dengan paket yang dibutuhkan

Membuat skrip yang bisa langsung dijalankan dengan uv

  • Dengan menambahkan tag # /// script milik uv di bagian atas skrip, dependensi dapat dideklarasikan langsung di dalam file
# /// script

# dependencies = ["ipython", "httpx", "loguru"]

# ///
  • Jika header ini ada, skrip bisa dijalankan dengan uv run jam_users.py
    • uv membuat virtual environment terisolasi untuk skrip tersebut
    • Mengunduh dan memasang dependensi yang dibutuhkan
    • Menjalankan skrip dalam konteks virtual environment itu
  • Shebang Python biasa #!/usr/bin/env python tidak bisa memanfaatkan header skrip uv karena Python mengabaikan komentar # /// script
  • Jika pemanggilan uv dimasukkan langsung ke shebang, skrip dapat diperlakukan seperti berkas eksekusi
#!/usr/bin/env -S uv run --script

# /// script

# dependencies = ["ipython", "httpx", "loguru"]

# ///
  • Flag -S pada env membuat string setelahnya dipecah menjadi argumen terpisah lalu diteruskan ke env
  • Setelah memberi izin eksekusi dengan chmod +x jam_users.py, skrip bisa dijalankan langsung seperti berikut
./jam_users.py
  • Pendekatan ini memungkinkan skrip dijalankan tanpa instalasi dependensi terpisah atau pengelolaan virtual environment, selama uv terpasang pada sistem Unix
  • Saat membagikan skrip Python yang lebih kompleks kepada pengguna lain, beban untuk menjelaskan langkah persiapan sistem sebelum eksekusi bisa jauh berkurang

1 komentar

 
GN⁺ 2025-03-31
Pendapat di Hacker News
  • Bukan soal UV itu sendiri, tetapi secara umum, cara mengendalikan eksekusi kode dengan komentar terasa mengganggu
    Memakai komentar untuk instruksi linter atau catatan developer tidak masalah, tetapi kalau itu data terkait konfigurasi atau eksekusi, menurut saya bentuk seperti UV_ENV = { "dependencies": { "requests": "2.32.3", "pandas": "2.2.3" } } jauh lebih baik
    Cara ini adalah sintaks Python yang valid, lebih mudah dibuat dan divalidasi karena memakai struktur data standar alih-alih parsing komentar secara arbitrer, dan yang terpenting, tetap memegang prinsip bahwa meski semua komentar dihapus, kode harus berjalan dengan cara yang sama

    • Saya setuju, tetapi ingin melangkah lebih jauh
      Cara yang diusulkan itu pun pada runtime tidak melakukan apa-apa, yaitu konstanta ajaib, sehingga hanya diparsing lewat analisis statis; alat lain bisa saja menganggapnya kode tak terpakai dan menghapusnya, bukannya dibaca oleh uv
      Lebih baik setelah import uv, panggil langsung apa yang harus dilakukan uv, misalnya uv.exec(dependencies=["clown"], python=">=3.10")
      Eksekusi pertama dilakukan di runtime Python mana pun yang bisa menemukan paket uv hipotetis ini, lalu setelah paket uv menyiapkan virtual environment dan runtime Python, ia melakukan re-exec(3) dengan flag seperti variabel lingkungan
      Pada runtime kedua, uv.exec yang mendeteksi flag itu cukup tidak melakukan apa-apa
    • Ini bukan sesuatu yang diciptakan uv, melainkan penggunaan standar PEP 723 seperti alat-alat lain
      https://peps.python.org/pep-0723/
    • Masuk akal, tetapi menurut saya mem-parsing dan mengevaluasi kode imperatif jauh lebih sulit dan kurang fleksibel dibanding membiarkannya sebagai data deklaratif sesuai prinsip hak istimewa minimum
      Selain itu, baris shebang sendiri sebenarnya juga berfungsi sebagai komentar
      Hanya saja selama 45 tahun ia sudah begitu mengakar sehingga orang jarang menyadari bahwa itu adalah komentar shell
    • Syarat “meski komentar dihapus, kode harus berjalan dengan cara yang sama” tetap benar
      Jika dependensi yang sama sudah diinstal, kode akan berjalan dengan cara yang sama
      Ini bukan mengubah makna kode itu sendiri, melainkan mengubah lingkungan tempat kode dijalankan, dan dalam hal itu tidak berbeda dari komentar #!/bin/bash di awal skrip shell
    • Sepenuhnya setuju, dan berharap bentuk seperti ini pada akhirnya distandardisasi
      Namun uv kemungkinan besar tidak ingin menjalankan kode hanya untuk mengetahui dependensi, jadi ini harus berupa subset sintaks Python yang sangat terbatas
      Fakta bahwa hal seperti ini dibutuhkan sejak awal menunjukkan kelemahan bahasa tersebut
      Pernyataan import sendiri seharusnya bisa menyampaikan semua informasi tentang dependensi
  • Dalam beberapa bulan terakhir topik ini sangat sering muncul di HN, dan contoh terbarunya adalah sebagai berikut
    https://news.ycombinator.com/item?id=43500124
    https://news.ycombinator.com/item?id=42463975
    Saya suka uv, tetapi sulit menyetujui istilah self-contained karena dua hal
    Pertama, untuk menjalankan skrip, uv harus sudah terinstal
    Memang bisa membuat skrip shell yang memeriksa apakah uv sudah terinstal dan jika belum menginstalnya lewat curlpipe, tetapi boilerplate-nya akan cukup bertambah dan pendekatan curlpipe itu sendiri juga kurang baik
    Kedua, membuat virtual environment secara otomatis di suatu tempat di direktori home bukanlah self-contained yang sebenarnya
    Meski dijalankan sekali lalu skripnya dihapus, virtual environment itu tetap tersisa dan memakan ruang; saya tidak menemukan jaminan di dokumentasi uv bahwa virtual environment sementara seperti ini akan dibersihkan otomatis

    • Keluhan bahwa uv diperlukan, dan jika tidak ada harus diinstal manual atau memakai curl | sh, memang valid
      Namun, begitu package manager mulai memasukkan uv ke repositori mereka, ini akan makin kurang menjadi masalah
      Misalnya uv sudah tersedia di Alpine Linux dan Homebrew: https://repology.org/project/uv/versions
      Selain itu, metadata skrip inline adalah standar Python
      Jika sistem tidak memiliki uv dan uv juga belum dipaketkan, tetapi ada versi Python yang cocok untuk skrip, skrip bisa dijalankan dengan pipx: https://pipx.pypa.io/stable/examples/#pipx-run-examples
      pipx jauh lebih luas dipaketkan: https://repology.org/project/pipx/versions
    • Untuk memecahkan masalah seperti ini, saya sempat memaksa menggabungkan docker+uv shebang, dan cukup berhasil
      Di mesin developer acak, Docker mungkin lebih umum daripada uv, dan karena artikel aslinya juga menyebut proyek perusahaan, ini terasa masuk akal
      Namun saat ini sama sekali belum ada caching, jadi setiap kali dijalankan akan mengunduh lagi, yang terasa canggung; sepertinya bisa diperbaiki dengan volume
      Kira-kira seperti ini: https://hugojosefson.github.io/docker-shebang/#python
    • Biasanya, untuk menjalankan program di komputer, kita memang harus menginstal sesuatu terlebih dahulu, jadi instalasi uv sendiri tampaknya tidak terlalu buruk
      Meski begitu, jika saat eksekusi ia mengunduh sesuatu yang tidak jelas dari internet, sulit menyebutnya self-contained; self-contained yang benar-benar lengkap lebih dekat ke AppImage
    • Setuju 100%
      Dengan memakai sesuatu seperti py2exe, kita bisa membuat “skrip Python” yang self-contained
      Bagi developer, itu menimbulkan banyak masalah, tetapi bagi pengguna masalahnya diminimalkan
    • Sebagai tambahan kecil, berkat deduplikasi paket uv, virtualenv tidak memakan ruang selama tidak ada dependensi yang unik
  • Nix juga menggunakan cara yang sama, dan baris shebang-nya terlihat seperti ini
    #! nix-shell -i python3 -p "python312.withPackages (pkgs: [ pkgs.boto3 pkgs.click ])"
    Dengan begitu, satu-satunya yang diperlukan di sistem hanyalah Nix; Python bahkan tidak perlu terpasang

    • Memang benar, tetapi karena masih banyak paket PyPI yang belum dipaketkan di nixpkgs, pendekatan ini tidak seumum uv
    • Seperti yang juga dijelaskan tepat di artikel aslinya, uv akan memasang Python secara instan saat diperlukan
    • Teknik yang sama bisa diterapkan ke bahasa apa pun
      Contoh yang paling obvious adalah bash dengan semua dependensi ditentukan, dan saya juga pernah membuat skrip Rust satu file yang cepat dengan Nix shebang
      https://nixos.wiki/wiki/Nix-shell_shebang
    • Syarat bahwa Nix harus terpasang adalah tuntutan yang jauh lebih berat daripada syarat bahwa uv harus ada
    • Saya penasaran bagaimana melakukan hal yang sama dengan perintah berbasis flake, nix shell, alih-alih nix-shell
  • Seperti komentar-komentar lain, klaim “self-contained” bergantung pada fakta bahwa uv sudah terpasang
    Jika benar-benar menginginkan skrip Python yang self-contained, kompiler Nuitka layak dilihat
    Saya memakainya di produksi untuk layanan gRPC tanpa masalah, dan cukup menjalankan nuitka --onefile run.py
    Karena ini kompiler, binary yang dihasilkan juga bisa lebih cepat daripada program Python asli yang dibundel dengan PyInstaller
    Di halaman GitHub pembuatnya tertulis, “misi hidup saya adalah terus mencoba membuat Python Compiler terbaik yang mungkin sampai saya tua dan mati”
    https://nuitka.net/
    https://github.com/kayhayen

  • Saya sangat menyukai pola ini, tetapi sayangnya belum berhasil membuatnya bekerja dengan benar bersama LSP
    Saya memakai pyright di Helix, dan tetap tidak berhasil meski menjalankan editor dengan uv run hx script.py
    Bisa saja melakukan sesuatu seperti uv run --with whatever-it-is-i-need hx script.py, tetapi duplikasinya makin banyak

    • Saya memakai skrip uve buatan sendiri yang jelek
      $ cat ~/.local/bin/uve
      #!/bin/bash
      temp=$(mktemp)
      uv export --script $1 --no-hashes > $temp
      uv run --with-requirements $temp vim $1
      unlink $temp
      Saya berharap editor segera mendukung uv python find --script
  • Kelihatannya cukup berguna
    Saya penasaran apakah uv adalah pilihan yang lebih aman untuk distribusi jangka panjang proyek berbasis Python
    Saya teringat rug pull ketika sekitar 5 tahun lalu memakai Anaconda untuk manajemen dependensi, lalu belakangan aturannya berubah sehingga pelanggan organisasi dengan lebih dari 200 karyawan tidak lagi bisa memakai Anaconda secara gratis dan harus membayar lisensi komersial

    • uv berlisensi MIT atau Apache-2.0
      Mereka bisa menghentikan pengembangan atau memindahkan pekerjaan berikutnya ke fork dengan lisensi lain, tetapi tidak bisa mengubah lisensi masa lalu secara retroaktif, jadi yang sudah ada saat ini dijamin open source
      Jika benar-benar khawatir, buat saja fork dan terus sinkronkan
      Pada dasarnya ini juga berlaku hampir sama untuk proyek OSS lain, jadi saya tidak akan terlalu khawatir
      Sejauh yang saya tahu, conda tidak pernah menjadi open source dan selama ini mendistribusikan binary
      https://github.com/astral-sh/uv?tab=readme-ov-file#license
    • Menurut saya rug pull Anaconda terjadi di sisi repositori
      Paket-paket conda-forge masih bisa dipakai gratis
      uv hanya menggunakan PyPI, jadi jika ada masalah, tinggal beralih dari uv ke pip atau Poetry dan paketnya tetap diambil dari tempat yang sama
    • Sepemahaman saya, jika sebuah proyek memiliki Contributor License Agreement (CLA) dan membuat kontributor menyerahkan hak cipta kontribusinya kepada pemilik proyek, maka relicensing dimungkinkan
      Pemilik seperti itu pada akhirnya akan diakuisisi oleh orang kaya terburuk yang bisa dibayangkan
      Saya sempat menelusuri panduan kontribusi dan issue uv tetapi tidak melihat CLA, sedangkan PyTorch memiliki CLA di bagian paling atas panduan kontribusinya
      Meski begitu, seharusnya ada fork komunitas dari versi FOSS terakhir Anaconda
      Itu terjadi pada Redis, dan Redis juga menggunakan CLA: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md#redis-software-grant-and-contributor-license-agreement
      Jangan pernah menandatangani CLA
      Saya lebih memilih hanya berkontribusi pada proyek copyleft
      Kita dibayar terlalu mahal untuk bekerja secara gratis
  • Untuk utilitas kecil, ini terlihat seperti alternatif packaging yang bagus dibanding containerization
    Sekarang saya harus meyakinkan semua rekan kerja untuk memasang uv

    • uv luar biasa cepat, jadi itu akan membantu
    • Bagi kami, kendalanya adalah pemindai kerentanan SCA belum bekerja dengan uv
  • Ini terlihat mirip dengan bundler/inline di Ruby
    Senang melihat hal serupa hadir di Python, dan dalam praktiknya memang sangat nyaman
    https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html

  • Penasaran apakah ada yang berhasil membuat ini berjalan di Windows
    Saya ingin memakai trik ini untuk tool bagi mod game yang sedang saya kerjakan, tetapi tidak berhasil membuat trik shebang-nya berjalan

  • Use case ini membuat saya menyukai uv, tetapi rasanya bertentangan dengan Zen of Python bahwa PEP yang resmi dan sangat berguna tidak didukung oleh tool Python resmi
    Dari sudut pandang saya, ini adalah momen pertama ketika Python tidak lagi “batteries included”
    Sekarang saya juga punya dua dependency manager Python di sistem
    Saya tahu ada banyak hal yang bisa dikatakan soal manajemen dependency Python, tetapi selama beberapa tahun terakhir saya bertahan dengan pip+venv bawaan selama proyek hanya punya requirements.txt

    • Dalam spesifikasi build Python, alur seperti itu memang pernah terjadi
      Seingat saya, pyproject.toml muncul lebih dulu daripada library tomllib
      Jadi selama beberapa versi, kita harus mendefinisikan modul dalam bahasa yang tidak bisa dibaca Python secara bawaan
      Ini situasi yang lebih buruk daripada memiliki cara bawaan untuk menyertakan metadata yang tidak digunakan
      Toh itu memang metadata, dan kalau tidak, seharusnya itu sekadar sintaks Python