---
title: Paketleme
source: 'https://academia.sh/tr/kurslar/python-projeleri/paketleme'
course: 'Python Projeleri: Paketleme ve Test'
language: tr
updated: '2026-08-17T18:10:32+00:00'
license: 'CC BY-SA 4.0'
---

# Paketleme

Aynı kaynaktan üç dizin durumu ve üç dahil etme kuralıyla yapılan dokuz üretim 5 ayrı içerik verir; açık liste bu sayıyı 1'e indirir, arşiv özetini yalnız sıralı girdi ile kaynaktan türetilmiş damganın birlikte kullanılması tekleştirir ve derleme adımının ne zaman çalıştığı kurulumu 3 ayrı sonuçtan 1'e düşürür.

Önceki ders aynı takımı üç eksenli bir matriste koşturdu ve kalan farkın gerçek ortam farkı
olduğunu gösterdi. O ölçümün sessiz bir varsayımı vardı: her ortamda koşan şey **aynı
kaynaktı**. Testler doğrudan çalışma dizininden okundu.

Bir proje başkasının kullanımına verildiğinde bu varsayım kendiliğinden sağlanmaz. Kaynak
dizini olduğu gibi gitmez; bir **dağıtım biçimine (distribution format)** paketlenir, o
paket taşınır ve karşı tarafta açılır. Bu dersin sorusu paketlemenin nasıl yapıldığı değil:
**aynı kaynaktan üretilen dağıtım kaç ayrı içerik verir, ve neyi sabitlemek bu sayıyı bire
indirir?**

## Dağıtım Biçimi ve Derleme Sistemi

İki dağıtım biçimi ayrılır ve aradaki fark bu dersin son ölçümünün konusudur.

**Kaynak dağıtımı** kaynak dosyaları ve proje bildirimini taşır. Kurulan tarafta bir
**derleme adımı** çalışır ve kurulacak ağacı üretir. **Kurulmuş dağıtım** ise bu adımın
sonucunu taşır; kurulan tarafta yalnız açma işi kalır.

Bu iki biçimi üreten araç sınıfı **derleme sistemidir (build system)**. Görevi üç sorunun
yanıtını üretmektir: hangi dosyalar dağıtıma girecek, dağıtım hangi düzende yazılacak, ve
derleme adımı ne zaman çalışacak. Bu kurs araç adı yazmaz; derleme sistemi burada üç
işlevle **modellenir** — bir dahil etme kuralı, bir arşiv yazıcısı ve bir derleme adımı.
Model gerçek bir arşiv üretmez, hiçbir dosya diske yazmaz ve hiçbir paket indirmez; ürettiği
tek şey içerik özetleridir.

## Üç Dizin Durumu

Dahil etme kuralının ne yapacağı, kaynağın yanında **ne durduğuna** bağlıdır. Bir çalışma
dizini kaynaktan ibaret değildir: derleme artıkları, önbellek dosyaları ve yerel ayar
dosyaları da orada birikir. Ölçüm üç durum tanımlar. **Temiz** dizin yalnız kaynağı taşır.
**Önbellekli** dizinde bir üretilmiş dosya vardır. **Artıklı** dizinde ayrıca bir kayıt
dosyası ve bir yerel ayar dosyası bulunur.

Üç dahil etme kuralı denenir. **Her şeyi topla** dizindeki her dosyayı alır. **Desenle ele**
belirli uzantıları dışarıda bırakır. **Açık liste** yalnız bildirimde adı geçen dosyaları
alır.

Ölçümün varsayımları:

- **KY15** — Kaynak ağacı sabittir ve üç durumda da aynıdır; durumlar yalnız kaynağın
  yanında duran dosyalarla ayrılır.
- **KY16** — Dağıtımın içeriği, dosya adları ve gövdelerinin ada göre sıralanmış özetidir;
  dizin listeleme sırası bu özete girmez.
- **KY17** — Arşiv özeti ise sıraya ve damgaya duyarlıdır; iki koşumun listeleme sırası ve
  koşum damgası **kurgu** değerlerle modellenir, gerçek bir saat okunmaz.
- **KY18** — Derleme adımı kaynaktan tek bir dosya üretir ve içine çalıştığı ortamın adını
  yazar. Gerçek bir derleme adımı bundan çok daha fazlasını yapar; modelin tuttuğu tek
  özellik, **çıktısının çalıştığı ortama bağlı olmasıdır**.
- **KY19** — Özetler kısaltılmış sekiz onaltılık basamaktır; iki özetin eşitliği içeriğin
  eşitliği olarak okunur.
- **KY20** — Hiçbir dosya diske yazılmaz, hiçbir arşiv üretilmez, hiçbir paket indirilmez;
  bütün üretim bellekte modellenir.
- **KY21** — Süre ölçülmez. Sayılan şey **ayrı içerik**, **ayrı özet** ve **ayrı kurulum**
  sayısıdır.

## Ölçüm

```python
"""Paketleme: ayni kaynaktan uretilen dagitim kac ayri icerik veriyor."""

import hashlib

KAYNAK = {
    "olcum/__init__.py": "from .indirim import indirim\n",
    "olcum/indirim.py": "def indirim(tutar, uye, kupon):\n    return tutar\n",
    "olcum/oran.json": '{"tavan": 20}\n',
    "testler/test_indirim.py": "def test_tavan():\n    pass\n",
    "BENIOKU.md": "# olcum\n",
    "proje.toml": '[proje]\nad = "olcum"\n',
}
ARTIK = {
    "temiz": {},
    "önbellekli": {"olcum/__onbellek__/indirim.onb": "derlenmis\n"},
    "artıklı": {"olcum/__onbellek__/indirim.onb": "derlenmis\n",
                "olcum/gecici.log": "kosum kaydi\n",
                "yerel_ayar.txt": "yol = /ev/kullanici\n"},
}
LISTE = ("olcum/__init__.py", "olcum/indirim.py", "olcum/oran.json",
         "proje.toml")
KURALLAR = {
    "her şeyi topla": lambda ad: True,
    "desenle ele": lambda ad: not ad.endswith((".onb", ".log")),
    "açık liste": lambda ad: ad in LISTE,
}


def dizin(durum):
    return {**KAYNAK, **ARTIK[durum]}


def ozet(metin):
    return hashlib.sha256(metin.encode()).hexdigest()[:8]


def icerik_ozeti(dosyalar):
    """Dagitimin icerigi: ad ve govde, adina gore sirali."""
    return ozet("".join(f"{a}\n{dosyalar[a]}" for a in sorted(dosyalar)))


def sec(durum, kural):
    d = dizin(durum)
    return {a: g for a, g in d.items() if KURALLAR[kural](a)}


print(f"{'dahil etme':<15s} " + " ".join(f"{d:>14s}" for d in ARTIK)
      + "  ayrı içerik")
tum = []
for kural in KURALLAR:
    satir, ozetler = [], []
    for durum in ARTIK:
        secim = sec(durum, kural)
        o = icerik_ozeti(secim)
        ozetler.append(o)
        tum.append(o)
        satir.append(f"{len(secim)}·{o}")
    print(f"{kural:<15s} " + " ".join(f"{s:>14s}" for s in satir)
          + f"{len(set(ozetler)):11d}")
print(f"dokuz üretim, {len(set(tum))} ayrı içerik")

print()
print(f"{'sıra kuralı':<18s} {'damga kuralı':<20s} {'koşum A':>9s} "
      f"{'koşum B':>9s}  ayrı")


def listele(dosyalar, kosum):
    return sorted(dosyalar, reverse=(kosum == "koşum B"))


def arsiv(dosyalar, sirali, kaynaktan_damga, kosum, damga):
    adlar = sorted(dosyalar) if sirali else listele(dosyalar, kosum)
    d = icerik_ozeti(dosyalar) if kaynaktan_damga else damga
    return ozet("".join(f"{a}\n{dosyalar[a]}" for a in adlar) + f"damga={d}\n")


SECIM = sec("artıklı", "açık liste")
DAMGA = {"koşum A": "d-101", "koşum B": "d-207"}
for s_ad, sirali in (("listeleme sırası", False), ("adına göre sıralı", True)):
    for d_ad, kaynaktan in (("koşum damgası", False),
                            ("kaynaktan türetilmiş", True)):
        a = [arsiv(SECIM, sirali, kaynaktan, k, DAMGA[k]) for k in DAMGA]
        print(f"{s_ad:<18s} {d_ad:<20s} {a[0]:>9s} {a[1]:>9s}"
              f"{len(set(a)):6d}")

print()


def derle(dosyalar, ortam):
    """Derleme adimi: kaynaktan bir dosya uretir."""
    return {**dosyalar, "olcum/_yapi.py": f'YAPILDI = "{ortam}"\n'}


kaynak_dagitimi = SECIM
kurulmus_dagitim = derle(SECIM, "üretim ortamı")
print(f"{'dağıtım biçimi':<20s} " + " ".join(f"{d:>10s}" for d in ARTIK)
      + "  ayrı kurulum")
for ad, kurulum in (("kaynak dağıtımı",
                     lambda o: icerik_ozeti(derle(kaynak_dagitimi, o))),
                    ("kurulmuş dağıtım",
                     lambda o: icerik_ozeti(kurulmus_dagitim))):
    o = [kurulum(d) for d in ARTIK]
    print(f"{ad:<20s} " + " ".join(f"{x:>10s}" for x in o)
          + f"{len(set(o)):13d}")
```

```
dahil etme               temiz     önbellekli        artıklı  ayrı içerik
her şeyi topla      6·3f5fd540     7·c6e0e7fa     9·5bd4ca54          3
desenle ele         6·3f5fd540     6·3f5fd540     7·f43474cf          2
açık liste          4·3f6d7475     4·3f6d7475     4·3f6d7475          1
dokuz üretim, 5 ayrı içerik

sıra kuralı        damga kuralı           koşum A   koşum B  ayrı
listeleme sırası   koşum damgası         0ef2d766  ed542e63     2
listeleme sırası   kaynaktan türetilmiş  f2347fb6  153d3f8d     2
adına göre sıralı  koşum damgası         0ef2d766  94813185     2
adına göre sıralı  kaynaktan türetilmiş  f2347fb6  f2347fb6     1

dağıtım biçimi            temiz önbellekli    artıklı  ayrı kurulum
kaynak dağıtımı        ca94ea4c   c836cc41   8cd3c4ae            3
kurulmuş dağıtım       47abe500   47abe500   47abe500            1
```

## Dokuz Üretim, Beş İçerik

Üst tablo dersin ilk sayısını veriyor: aynı kaynaktan yapılan **9** üretim **5 ayrı
içerik** üretiyor.

Satırlar tek tek okunduğunda sayının nereden geldiği görülür. **Her şeyi topla** kuralı üç
dizin durumunda **6**, **7** ve **9** dosya alıyor ve **3 ayrı içerik** veriyor. Bu kuralın
sonucu kaynağa değil, **kaynağın yanında ne durduğuna** bağlıdır; aynı depodan çekilmiş iki
çalışma dizini ayrı paket üretir.

**Desenle ele** kuralı iki durumda aynı sonucu veriyor — `temiz` ile `önbellekli` aynı
özeti, `3f5fd540`, taşıyor — çünkü elediği uzantı önbellek dosyasını yakalıyor. Üçüncü
durumda ise `yerel_ayar.txt` dosyası desene takılmıyor ve içeriğe giriyor: **7** dosya,
ayrı özet. Kural, saydığı şeyi **bildiği** kadar eleyebilir.

**Açık liste** üç durumda da **4** dosya ve tek bir özet, `3f6d7475`, veriyor: **1 ayrı
içerik**. Aradaki fark yöntemseldir. İlk iki kural "neyin dışarıda kalacağını" söyler ve
listelenmemiş her yeni dosya sessizce içeri girer. Üçüncü kural "neyin içeri gireceğini"
söyler; listelenmemiş her dosya sessizce dışarıda kalır. **Sessizliğin yönü değişir, ve
tekleşme bu yön değişiminden gelir.**

İki özet dokuz üretimin ikisinde birden geçiyor: `3f5fd540` hem `her şeyi topla`/`temiz`
hem `desenle ele`/`temiz` hücrelerinde. Dokuz hücre, beş ayrı içerik; kuralların çakıştığı
yerde sayı düşüyor.

Dosya sayıları bir ayrımı daha görünür kılıyor. Açık liste **4** dosya alıyor, oysa temiz
dizinde **6** kaynak dosyası var. Dışarıda kalan ikisi `testler/test_indirim.py` ve
`BENIOKU.md`. Bu bir kusur değil, bir karardır: dağıtım **kurulan** ağacı taşır, depodaki
her şeyi değil. Test dosyası depoda kalır ve orada koşar; dağıtıma girdiğinde kurulan
tarafın disk alanını ve içe aktarma ad uzayını kaplar. Aynı karar belge dosyası için de
verilebilir ve tersi de savunulabilir — savunulamaz olan, kararın **verilmemiş** olmasıdır.
İlk iki kuralda bu karar hiç verilmiyor; dosyanın dağıtıma girip girmeyeceğini adının
uzantısı belirliyor.

## Bildirimin İki İşi

Üç dahil etme kuralı da bir yerde yazılıdır ve o yer proje bildirimidir. Bildirim iki ayrı
iş yapar ve ikisi sık sık aynı dosyada durduğu için karıştırılır.

**Birinci iş: paketin kimliği.** Ad, sürüm, bağımlılık bildirimi ve dağıtıma girecek
dosyalar. Bunlar veriyi tanımlar; hangi araçla okundukları sonucu değiştirmez.

**İkinci iş: derleme sisteminin seçimi.** Dağıtımı hangi derleme sisteminin üreteceği ve o
sistemin hangi arayüzle çağrılacağı. Bu bir yönergedir; sonucu doğrudan belirler.

Ayrımın pratik karşılığı şudur: birinci iş **bildirimseldir** ve iki ayrı araç aynı bildirimi
okuyup aynı dosya kümesini seçer. İkinci iş bir **seçimdir** ve seçilen sisteme göre üretilen
arşivin düzeni, damgası ve derleme adımının davranışı değişir. Ölçümün üst tablosu birinci
işi, orta tablosu ikinci işi sınıyor.

Bu yüzden bir bildirimin "eksiksiz" olması iki ayrı anlama gelir. Dosya listesi eksiksizse
dağıtımın içeriği tekleşir. Derleme sistemi ve çağrı arayüzü de yazılıysa, dağıtımı üreten
her koşum aynı sistemi aynı biçimde çağırır. İkincisi yazılmadığında üretim aracın
öntanımlısına düşer ve ortak tanımın çözümleme ölçümündeki durum tekrarlanır: **karar
verilmemiştir, o yüzden ortam verir.**

## Arşivi Sabitleyen İki Karar

İçeriğin tekleşmesi arşivin tekleşmesi anlamına gelmez. Orta tablo aynı dosya kümesini —
`açık liste` kuralının seçtiği dört dosyayı — iki koşumda arşivliyor ve dört kural
bileşimini yan yana koyuyor.

Üç bileşimde iki koşum **2 ayrı özet** veriyor. Bir bileşimde **1**.

**Listeleme sırası** kullanıldığında koşum A ve koşum B dosyaları ayrı düzende yazıyor;
gövde aynı, bayt dizisi ayrı. Damga kaynaktan türetilse bile bu fark kalıyor: ikinci satır
`f2347fb6` ile `153d3f8d` veriyor. **Koşum damgası** kullanıldığında ise sıra sabitlense
bile fark kalıyor; üçüncü satır `0ef2d766` ile `94813185` veriyor.

Yalnız dördüncü satır **1** veriyor: sıra adına göre sabitlenmiş **ve** damga kaynaktan
türetilmiş. İkisi birden gerekiyor, biri yetmiyor. Bu, **yeniden üretilebilirliğin**
tanımıdır: aynı girdiden aynı çıktı, koşumdan koşuma.

Arşiv özetinin neden ayrıca ölçüldüğü de buradan anlaşılır. Kurulan taraf içeriği kendi
hesaplayamaz; eline geçen şey bir arşivdir ve onu ancak **baytları üzerinden** doğrulayabilir.
İçerik tek olsa bile arşiv baytları koşumdan koşuma değişiyorsa, iki arşivin aynı kaynaktan
geldiği doğrulanamaz. Yeniden üretilebilirlik iddiası bu yüzden içerik düzeyinde değil,
**bayt düzeyinde** kurulur.

Üçüncü satırdaki `0ef2d766` ile birinci satırdaki `0ef2d766` aynı olduğu için bir ayrıntı
görülebiliyor: koşum A'nın listeleme sırası zaten adına göre sıralıdır, bu yüzden o koşumda
iki sıra kuralı aynı sonucu verir. Fark yalnız koşum B'de ortaya çıkar. **Bir yapılandırma
kusuru her koşumda görünmez; görünmediği koşumda yok olduğu sanılır.**

## Derlemenin Ne Zaman Çalıştığı

Alt tablo son ölçümü veriyor ve iki dağıtım biçimini ayırıyor.

**Kaynak dağıtımı** üç dizin durumunda **3 ayrı kurulum** veriyor. Nedeni tabloda görünmüyor
ama modelde açıktır: derleme adımı kurulan tarafta çalışıyor ve ürettiği dosyaya kurulduğu
ortamın adını yazıyor. Kurulan ağaç bu yüzden ortamdan ortama değişiyor. Dağıtımın kendisi
tekti — dört dosya, tek özet — ama kurulum sonucu tek değil.

**Kurulmuş dağıtım** üç durumda da **1 ayrı kurulum** veriyor: `47abe500`. Derleme adımı bir
kez, üretim tarafında çalıştı; kurulan taraf yalnız açıyor.

İkisi arasındaki fark **derleme adımının nerede çalıştığıdır**, ve bu bir dağıtım biçimi
kararıdır. Kaynak dağıtımı derlemeyi kurulan tarafa taşır; karşılığında kurulan taraf
kendi ortamına uygun bir çıktı üretebilir. Kurulmuş dağıtım derlemeyi bir kez yapar;
karşılığında ortam farkını dağıtıma yansıtamaz.

Bu, kurulmuş dağıtımın bedelsiz olduğu anlamına gelmez. Derleme adımı ortamdan bir şey
okuyorsa, kurulmuş dağıtım okuduğu değeri **içine gömer** ve artık yalnız o ortama uyar. O
zaman dağıtımın hangi ortama uyduğunun paketin kendisinde yazılı olması gerekir; yazılı
değilse kurulan taraf uymayan bir paketi açar ve hata kurulumdan çok sonra, çalışma
sırasında görünür. Kaynak dağıtımının ürettiği **3** ayrı kurulum bir kusur değil, bu
uyarlamanın kendisidir; kusur, üç ayrı sonucun **beklenmiyor** olmasıdır.

Bir kez üretilip her yere dağıtılan yapı çıktısı ilkesi **DevOps Kültürü ve Temelleri**
kursunda işletim tarafında kurulmuştu. Buradaki ölçüm o ilkenin dil tarafındaki karşılığını
sayıyla veriyor: ilke uygulandığında kurulum sonucu **1**, uygulanmadığında ortam sayısı
kadar.

Üç ölçümü tek cümlede birleştirmek gerekirse: **aynı kaynaktan çıkan dağıtımın tek içerik
vermesi için üç şeyin birden sabitlenmesi gerekir** — dahil edilecek dosyaların açık
listesi, arşivin yazım sırası ve damgası, ve derleme adımının hangi tarafta çalışacağı.
Üçünden biri açık bırakıldığında sayı birden büyüktür ve büyüklüğü, açık bırakılan yerin
ortamdan ne kadar etkilendiğine bağlıdır.

## Özet

- Dağıtım biçimi ikiye ayrılır: kaynak dağıtımı derleme adımını kurulan tarafa taşır,
  kurulmuş dağıtım o adımın sonucunu taşır.
- Aynı kaynaktan yapılan **9** üretim **5 ayrı içerik** verir; `her şeyi topla` kuralı
  **3**, `desenle ele` **2**, `açık liste` **1** ayrı içerik üretir.
- Eleyen kurallar listelenmemiş dosyayı sessizce içeri alır; açık liste sessizce dışarıda
  bırakır. Tekleşme bu yön değişiminden gelir.
- Arşiv özetinin tekleşmesi için sıranın **ve** damganın birlikte sabitlenmesi gerekir; dört
  bileşimden yalnız biri **1** ayrı özet verir, üçü **2**.
- Kaynak dağıtımı üç ortamda **3 ayrı kurulum**, kurulmuş dağıtım **1** verir; dağıtımın
  kendisi tek olsa bile kurulum sonucu tek olmayabilir.

## Sonraki Adım

Bu ders dağıtımı üretti ve içeriğini bir özetle adlandırdı. Özet, üretim tarafında hesaplanan
bir sayıdır ve orada kalır. Paket bir **paket kaydına** gönderildiğinde ise ona bir ad ve bir
sürüm verilir; kurulan taraf artık içeriği değil, **adı** ister. Sonraki dersin sorusu
buradan doğar: yayımlanmış bir sürümün içeriği sonradan değişebilir mi, aynı ad iki ayrı
içeriği taşıyabilir mi, ve taşıyabiliyorsa kilit dosyasının yazdığı sürüm numarası neyi
garanti eder?
