Menulis library berbeda dari menulis skrip sekali jalan. Skrip hidup untuk dirinya sendiri, sedangkan library hidup untuk dipakai orang lain: dipasang dengan pip, diimpor dari kode yang tidak kita kendalikan, dan diuji secara terpisah dari kode pemakainya. Perbedaan itu harus tercermin sejak struktur folder pertama, sebelum satu baris function pun ditulis. Pada bagian ini kita menyiapkan kerangka package example-package tersebut.

Rancangan Package #

Kita membangun satu package dengan satu module yang memuat tiga function utilitas teks:

FunctionTugasContoh hasil
slugify()Mengubah teks menjadi slug URL"Belajar Python Dasar!" → "belajar-python-dasar"
hitung_kata()Menghitung jumlah kata"Belajar Python setiap hari" → 4
ringkas()Memotong teks panjangteks 42 karakter dipotong jadi 20 karakter

Package ini tidak membutuhkan dependency eksternal — seluruh logikanya cukup memakai standard library re dan unicodedata. Desain tanpa dependency membuat siklus build dan testing nanti berjalan cepat dan bersih.

src Layout versus Layout Datar #

Chapter Membangun Python Package memperkenalkan dua gaya penempatan kode. Pada layout datar, folder package langsung berada di akar proyek; pada src layout, kode berada di dalam folder src. Kita memakai src layout dengan alasan yang berkaitan langsung dengan sys.path.

Saat kita menjalankan Python dari dalam folder proyek, interpreter Python memasukkan folder kerja ke sys.path — mekanisme yang dibahas tuntas pada Chapter Module Search Path. Akibatnya, pada layout datar perintah import example_package bisa lolos secara tidak sengaja meskipun package belum pernah dipasang. Bug yang hanya muncul di komputer pembuat library seperti itu paling sulit dilacak. Dengan src layout, import hanya berhasil jika package benar-benar dipasang atau folder src sengaja dimasukkan ke sys.path — sama seperti pengalaman pengguna package kita.

Membuat Struktur Folder #

Buat folder proyek beserta kerangkanya dari terminal:

bash
mkdir -p paket-python-sederhana/src/example_package paket-python-sederhana/tests
cd paket-python-sederhana

Hasil akhir proyek — termasuk tiga berkas yang baru dibuat pada bagian-bagian berikutnya — berbentuk seperti ini:

text
paket-python-sederhana/
├── pyproject.toml
├── README.md
├── src/
│   └── example_package/
│       └── __init__.py
└── tests/
    └── test_package.py

Berkas __init__.py baru diisi pada Bagian 2 dan test_package.py pada Bagian 3; README.md baru dibutuhkan ketika melengkapi metadata pada Bagian 4.

pyproject.toml Awal #

Tabel [project] dan [build-system] sudah dibahas pada Chapter *pyproject.toml; berikut versi awal untuk proyek kita — simpan sebagai pyproject.toml` di akar proyek:

toml
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "example-package"
version = "0.1.0"
description = "Utilitas teks sederhana: slug URL, penghitung kata, dan peringkas teks"
requires-python = ">=3.10"
dependencies = []

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]

Mari kita bedah baris-baris kuncinya:

  1. Baris 1-3 ([build-system] … build-backend): menunjuk setuptools sebagai backend build — alat yang kelak mengubah folder src/ menjadi wheel dan sdist pada Bagian 4. Syarat setuptools>=61 dipilih karena versi 61 adalah versi pertama yang membaca tabel [project] PEP 621.
  2. Baris 6 (name = "example-package"): distribution name — nama yang dipakai pengguna pada pip install. Nama ini yang harus unik di PyPI kelak.
  3. Baris 7 (version = "0.1.0"): versi rilis pertama, mengikuti semantic versioning: MAJOR.MINOR.PATCH.
  4. Baris 9 (requires-python = ">=3.10"): pip menolak instalasi bila interpreter pengguna lebih tua dari Python 3.10.
  5. Baris 10 (dependencies = []): array kosong yang sah — package ini memang tidak butuh dependency, sehingga pengguna tidak men-download apa pun selain package kita.
  6. Baris 12-14 ([tool.pytest.ini_options]): konfigurasi pytest — testpaths membatasi pencarian test ke folder tests, dan pythonpath memasukkan folder src ke sys.path saat test berjalan. Mekanisme lengkapnya dibahas pada Bagian 3.

Normalisasi Nama Package #

Perhatikan dua ejaan yang hidup berdampingan pada proyek ini:

Jenis namaNilaiDi mana dipakai
Distribution nameexample-packagepip install example-package, URL halaman PyPI
Import nameexample_packageimport example_package di kode Python

Chapter Memahami Python Packages menjelaskan aturannya: distribution name bebas memakai tanda hubung, sedangkan import name harus merupakan identifier Python yang sah — dan tanda hubung bukan identifier sah. setuptools menjembatani keduanya secara otomatis: folder src/example_package/ dipetakan ke distribution name example-package, dan sebaliknya setiap tanda hubung pada distribution name dinormalisasi menjadi garis bawah pada nama berkas artefak — wheel hasil build nanti bernama example_package-0.1.0-py3-none-any.whl, bukan example-package-.... Aturan normalisasi ini akan terus muncul pada Bagian 4.

Kerangka proyek kini berdiri: folder siap, metadata tertulis, dan konvensi penamaan jelas. Berkas pyproject.toml lengkapnya dapat dilihat pada bagian Berkas Sumber di bawah halaman ini. Pada bagian berikutnya kita mengisi __init__.py dengan tiga function utilitas yang sesungguhnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

pyproject.toml

Unduh berkas
toml
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "example-package"
version = "0.1.0"
description = "Utilitas teks sederhana: slug URL, penghitung kata, dan peringkas teks"
requires-python = ">=3.10"
dependencies = []

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]