“Python 3.12+ Gerekir” Doğru mu? 14 Projeyi Sekiz Python Sürümünde Çalıştırdık

Yapay zekânın yazdığı 14 Python projesinin "Python 3.12+ gerekir" iddiasını Docker'da 3.8'den 3.15'e sekiz sürümde sınadık. Gerekçe veren üç iddia doğru çıktı, yalnızca "3.12+" yazan on iddianın hiçbiri doğru değildi. Derlenip çalışmayan kod, sürümü sessizce seçen pip, statik araç vermin ve kendi aracımızın iki yanılgısı.

Yapay zekânın yazdığı Python projelerimizin README dosyalarında hep aynı satır vardı: “Runtime: Python 3.12+”. Bu satırı 14 projede tek tek sınadık. Her projeyi Docker’da Python 3.8’den 3.15’e kadar sekiz sürümde derleyip çalıştıran küçük bir araç yazdık. Sonuç: iddianın hangi özelliğe dayandığını söyleyen üç projede iddia doğru çıktı. Yalnızca “3.12+” yazan on projenin hiçbirinde doğru değildi. İş sırasında kendi düzelttiğimiz bir README’nin de yanlış olduğunu, kendi aracımızın da iki kez yanıldığını gördük.

Neden Bu Konu?

22 Ekim’deki README bakımı yazısında altı projenin sürüm iddiasına bakmıştık. Orada her projeyi elle, birkaç sürümde denemiştik. Bu kez soruyu genelleştirdik: “Python X+ gerekir” iddiasını herhangi bir projede birkaç dakikada sınayan bir yöntem var mı? Varsa, bu iddialar ne kadar güvenilir?

Bu sorunun önemi şu: sürüm satırı, projeyi kullanacak kişinin verdiği ilk karardır. “3.12+” diyen bir README, Python 3.10 kullanan birini ya projeyi denemekten vazgeçirir ya da gereksiz bir kurulumla uğraştırır. Tersi daha kötü: “3.9+” diyen ama 3.9’da çalışmayan bir proje, ilk satırda hata verir. Yapay zekâ bu satırı yazarken hiçbir şey denemiyor. Kendi bulunduğu ortamın sürümünü yazıyor.

Proje bilgisi Yazarlar: Ahmet Aksoy & Claude · Tarih: 29.10.2026 Ortam: Ubuntu 24.04, Docker resmi imajları python:3.8-slim … python:3.15-slim (3.15.2) İncelenen projeler: 23–32, 35–38, 41 · Araç: proje 50

Yöntem: Sürüm Matrisi

Araç basit bir fikre dayanıyor. Proje klasörünü her Python sürümünün resmi Docker imajına bağlıyor ve iki şey soruyor:

  1. Derleniyor mu? Klasördeki her .py dosyasını Python’ın kendi compile() fonksiyonuna veriyor. Sözdizimi o sürümde yoksa burada SyntaxError çıkıyor.
  2. Çalışıyor mu? main.py dosyasını çalıştırıp çıkış koduna bakıyor. Çıkış kodu 0 ise “ok”, değilse hata satırının ilk kelimesini yazıyor.

Konteynerde ağ kapalı (--network none). Proje klasörü salt okunur bağlanıyor ve konteynerin içinde geçici bir kopyası çalıştırılıyor. Böylece dosya yazan bir betik de çalışabiliyor, ama sizin klasörünüze hiçbir şey yazılamıyor. Çağrı şöyle:

python3 main.py ../32_data_structures_bin_tree ../24_advanced_type_hinting
== 32_data_structures_bin_tree ==
  3.8    ImportError
  3.9    ImportError
  3.10   ImportError
  3.11   ok
  3.12   ok
  3.13   ok
  3.14   ok
  3.15   ok
  lowest version that runs: 3.11

== 24_advanced_type_hinting ==
  3.8    SyntaxError
  3.9    SyntaxError
  3.10   SyntaxError
  3.11   SyntaxError
  3.12   ok
  3.13   ok
  3.14   ok
  3.15   ok
  lowest version that runs: 3.12

En düşük çalışan sürüm, projenin gerçek alt sınırı. README’deki iddiayla bu satırı karşılaştırmak yetiyor.

Sonuçlar

Proje Özgün README Matris: en düşük çalışan Altındaki sürümde ne oluyor?
23 Desen eşleme 3.10+ (match) 3.10 ✔ SyntaxError
24 Tip ipuçları 3.12+ (PEP 695) 3.12 ✔ SyntaxError
25 asyncio 3.11+ (TaskGroup) 3.11 ✔ AttributeError
26 Multiprocessing 3.12+ 3.8 denenen en eski sürüm
27 Metin normalleştirme 3.12+ 3.8 denenen en eski sürüm
28 Regex 3.12+ 3.9 TypeError
29 sqlite3 3.12+ 3.9 TypeError
30 FastAPI 3.12+ 3.8* denenen en eski sürüm
31 Sıralama 3.12+ 3.9 TypeError
32 İkili ağaç 3.12+ (Node | None) 3.11 ImportError
35 Multiprocessing 3.12+ 3.8 denenen en eski sürüm
36 asyncio 3.12+ 3.8 denenen en eski sürüm
37 argparse 3.12+ 3.8 denenen en eski sürüm
38 Regex doğrulama 3.12+ 3.9 TypeError
41 Python 3.15 3.15 (“eski sürümde mesaj verir”) 3.15 ✔ SyntaxError, mesaj yok

* 30 numaralı proje FastAPI’ye bağlı. Ayrıntısı aşağıda.

Bu tablodan çıkan ilk ders, sürüm satırının nasıl yazıldığıyla ilgili.

1. Gerekçe Yazan İddia Doğru, Yalnızca “3.12+” Yazan Değil

Doğru çıkan üç iddianın ortak noktası, hepsinin bir gerekçe vermesi: 23 “match 3.10’da geldi”, 24 “PEP 695 tip parametresi sözdizimi”, 25 “asyncio.TaskGroup 3.11’de geldi” diyor. Gerekçe somut olduğu için doğru da çıkıyor. Bir özelliğin hangi sürümde geldiği belgelerde yazıyor ve model bunu biliyor.

Yanlış çıkan on bir iddianın on tanesi gerekçe vermiyor, yalnızca “Runtime: Python 3.12+” diyor. Bu projelerin main.py dosyalarının ilk satırı da hep aynı: # Python3.12 Ubuntu 24.04. Kodun yazıldığı ve denendiği ortam bu. Model, bu bilgiyi “gereksinim” satırına taşımış. “Benim makinemde 3.12 var” bilgisi, “3.12 gerekir” iddiasına dönüşmüş.

Gerekçe veren tek yanlış iddia 32: README, 3.12 gerektiğini Node | None yazımına bağlıyor. Bu yazım 3.10’da geldi. Yani gerekçe vermek iddiayı otomatik olarak doğru yapmıyor, ama sınanabilir hâle getiriyor. “Hangi özellik?” sorusunun cevabı yoksa iddia büyük olasılıkla tahmindir.

2. Derleniyor Ama Çalışmıyor

Tablodaki TypeError satırları ilginç. 28, 29, 31 ve 38 numaralı projeler Python 3.8’de sorunsuz derleniyor, ama çalışınca hemen duruyor:

3.8    TypeError  | RUN exit=1 TypeError: 'type' object is not subscriptable

Neden, 38’deki şu fonksiyon tanımı:

def assess_password_strength(password: str) -> list[str]:

list[str] yazımı 3.9’da geldi. Ama bu bir sözdizimi değişikliği değil: 3.8’in ayrıştırıcısı köşeli parantezi tanıyor, dosyayı derliyor. Hata, def satırı çalıştırıldığında, Python tip ipucunu değerlendirmeye çalışınca çıkıyor. Bu yüzden “dosya derleniyor” demek “dosya çalışıyor” demek değil. Matrisin iki ayrı soru sormasının sebebi bu.

32 numaralı projede iki katman üst üste binmiş. Dosyanın başında hiç kullanılmayan bir from typing import Self satırı var. Self 3.11’de geldiği için 3.10’da ImportError çıkıyor. Bu satırı silip matrisi yeniden çalıştırdık:

== 32_selfsiz ==
  3.9    TypeError  | RUN exit=1 TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
  3.10   ok

Satır silinince alt sınır 3.10’a iniyor. Bu kez sınırı README’nin bahsettiği Node | None belirliyor. Yani kullanılmayan tek bir içe aktarma satırı, projenin alt sınırını bir sürüm yukarı taşıyor. 22 Ekim yazısında bunu anlatmıştık. Matris, aynı sonucu iki komutla tekrar üretiyor.

3. Sürümü Kod Değil, pip Seçiyor

22 Ekim’deki bakımda 30 numaralı FastAPI projesinin README’sini “Python 3.10 veya üstü” diye düzeltmiştik. Gerekçemiz şuydu: FastAPI, Pydantic ve Uvicorn’un güncel sürümleri 3.10 istiyor. Bu kez projeyi 3.8 ve 3.9’da gerçekten kurup sunucuyu başlattık:

3.8.20 fastapi 0.124.4 uvicorn 0.33.0 pydantic 2.10.6 b'[{"id":1,"sku":"SKU-MECH-01",...
INFO:     127.0.0.1:57848 - "GET /items HTTP/1.1" 200 OK
3.9.25 fastapi 0.128.8 uvicorn 0.39.0 pydantic 2.13.5 b'[{"id":1,"sku":"SKU-MECH-01",...
INFO:     127.0.0.1:38704 - "GET /items HTTP/1.1" 200 OK

pip install fastapi uvicorn komutu 3.8’de hata vermedi. Sessizce o sürümü destekleyen en yeni FastAPI’yi (0.124.4) seçti ve proje çalıştı. Yani “güncel sürümler 3.10 istiyor” doğru, ama “proje 3.10 gerektiriyor” yanlış. Bizim düzeltmemiz de eksikmiş.

Bunun pratik bir sonucu var: 3.8 kullanan biri bu projeyi çalıştırabilir, ama 3.10’daki biriyle aynı FastAPI’yi kullanmaz. Hata düzeltmeleri ve güvenlik yamaları bu eski sürümlere gelmiyor. Üçüncü taraf paket kullanan bir projede sürüm satırı tek başına yetmiyor, denenen paket sürümleri de yazılmalı. 30 numaralı projenin README dosyasını bu bilgiyle yeniden düzelttik.

4. “Çalışıyor” ile “Doğru Çalışıyor” Aynı Şey Değil

Matris yalnızca çıkış koduna bakıyor. 0 dönen bir program, doğru sonuç vermiş demek değil. İki projede bunun örneğini zaten biliyorduk.

29 (sqlite3): Matris projeyi 3.9’dan 3.15’e kadar “ok” gösteriyor. Ama 19 Ekim yazımızda anlattığımız gibi, bilgisayarın yerel ayarı Türkçe olduğunda Python 3.11 ve 3.12’de with conn: bloğu hata durumunda işlemi geri almıyor. Docker imajlarının yerel ayarı C.UTF-8 olduğu için matris bu hatayı göremez. Proje 3.11’de de 0 ile çıkıyor, ama Türkçe bir bilgisayarda yanlış veri bırakıyor.

26 (multiprocessing): Konteynerde ağ kapalı olduğu hâlde proje “ok” dönüyor. Sebep, 26 Ekim yazımızda anlattığımız gibi, ağ isteği yapan fonksiyonun her hatayı sessizce “404” sonucuna çevirmesi. Ağsız konteynerde “I/O-Bound” aşaması 0,04 saniyede bitiyor ve rapor bir hız karşılaştırması gibi görünüyor.

Yani matris alt sınırı bulur, doğruluğu bulamaz. “3.9’da çalışıyor” bilgisi “3.9’da doğru sonuç veriyor” anlamına gelmesin istiyorsanız, projenin kendi testlerini her sürümde çalıştırmanız gerekir.

5. Statik Araç Ne Diyor?

Kodu çalıştırmadan, yalnızca okuyarak en düşük sürümü tahmin eden araçlar da var. Bunlardan biri vermin. Aynı 14 projeyi vermin 1.9.2 ile de taradık:

23_structural_pattern_matching: Minimum required versions: 3.10
24_advanced_type_hinting: Minimum required versions: 3.12
25_asyncio_basics: Minimum required versions: 3.11
28_regex_mastery: Minimum required versions: 3.9
32_data_structures_bin_tree: Minimum required versions: 3.11
38_regex_validation: Minimum required versions: 3.9
41_python315_features: Minimum required versions: ~2, ~3

Sonuçlar matrisle uyuşuyor. 32 için vermin nedeni de satır satır gösteriyor: 'typing.Self' member requires !2, 3.11. Matrisin denemediği eski sürümler için 3.6 ya da 3.7 diyor. Yani vermin çok hızlı ve çoğu zaman doğru bir ilk kontrol.

İki sınırı var. Birincisi, 41 numaralı Python 3.15 projesinde vermin pes ediyor (“~2, ~3”, yani “karar veremedim”). vermin, kodu üzerinde çalıştığı Python’ın ayrıştırıcısıyla okuyor. Biz onu 3.12’de çalıştırdık, 3.12 de 3.15’in yeni sözdizimini tanımıyor. İkincisi, vermin kütüphaneleri görmüyor: 30 için “3.6” diyor, ama hangi FastAPI sürümünün kurulacağını bilemiyor. Kesin cevap için kodu o sürümde çalıştırmak gerekiyor.

Aracımız da Yanıldı

Matrisin ilk sürümü iki projede yanlış sonuç verdi.

29 hiçbir sürümde çalışmıyor göründü. Hata sqlite3.OperationalError: unable to open database file idi. Proje klasörünü salt okunur bağlamıştık, proje de bir veritabanı dosyası oluşturmaya çalışıyordu. Sorun projede değil, bizim aracımızdaydı. Klasörü konteynerin içinde geçici bir kopyaya alarak düzelttik.

26, 3.8 ve 3.9’da başarısız göründü: “timeout”. İlk akla gelen açıklama, bu sürümlerde bir şeyin takıldığıydı. Projeyi 3.9’da süre sınırı olmadan çalıştırdık:

== 3.9
Threading (CPU-Bound) Duration       : 58.0303 seconds
Multiprocessing (CPU-Bound) Duration : 14.5162 seconds
79.15 s
== 3.10
Threading (CPU-Bound) Duration       : 37.8764 seconds
Multiprocessing (CPU-Bound) Duration : 10.2159 seconds
52.50 s

Proje 3.9’da da çalışıyor, yalnızca daha yavaş. Bizim 60 saniyelik sınırımız buna yetmemiş. Süre sınırını 300 saniyeye çıkardık, zaman aşımını da “uyumsuz” değil “cevap yok” olarak raporlamaya başladık.

Bu iki hatanın dersi, makalenin geri kalanıyla aynı: bir aracın “başarısız” demesi de bir iddiadır ve o da sınanmalıdır.

Bir hata da araçta değil, aracın kendi başlığındaydı. Claude, main.py dosyasının ilk satırına “requires Python 3.9+” yazmıştı. Hiçbir şey denemeden, tıpkı bu yazıda eleştirdiğimiz projeler gibi. Aracı kendi klasörü üzerinde çalıştırdık (--args=--help ile, çünkü konteynerin içinde Docker yok): 3.8’de de çalışıyor. vermin ise 3.7 diyor. Başlığı “runs on Python 3.8+” olarak düzelttik.

README’ye Hangi Sürümü Yazmalı?

Matris en düşük çalışan sürümü buluyor. Bu sürümü README’ye yazmak doğru mu? Python’ın sürüm takvimine göre 29 Ekim 2026 itibarıyla desteklenen sürümler 3.11–3.15. 3.10’un desteği 1 Ekim 2026’da, 3.9’unki Ekim 2025’te bitti. 3.15.0 ise 9 Ekim’de çıktı.

Bizim önerimiz, iki bilgiyi birbirinden ayırmak:

  • Alt sınır: Matrisin bulduğu ve gerekçesi bilinen sürüm. Örneğin 38 için: “Python 3.9 veya üstü (list[str] tip ipucu).”
  • Denendiği sürümler: “3.9–3.15 arasında Docker’da denendi.” Desteği biten bir sürümde çalışmak, o sürümü önermek anlamına gelmiyor.

Bu ikisi karışınca “3.12+” gibi, ne alt sınır ne de deneme bilgisi olan bir satır çıkıyor.

Yapay Zekânın Yazdığı Sürüm İddiasını Değerlendirirken Ne Arıyoruz?

  • İddia bir gerekçe veriyor mu? “3.11+ (asyncio.TaskGroup)” sınanabilir bir iddia. Yalnızca “3.12+” yazıyorsa, büyük olasılıkla modelin bulunduğu ortamın sürümü.
  • Gerekçe doğru mu? 32’deki gibi, gerekçe gösterilen özellik daha eski bir sürümde gelmiş olabilir. Belgelerdeki “New in version” notu tek satırla cevap veriyor.
  • Kullanılmayan içe aktarmalar var mı? Kullanılmayan bir from typing import Self, alt sınırı tek başına yukarı çekebilir.
  • Derlemek yetmez, çalıştırın. list[str] gibi tip ipuçları eski sürümde derlenir, ama çalışırken hata verir.
  • Paketler varsa hangi sürümler kuruluyor? pip, eski Python’da sessizce eski paket seçer. README’ye denenen paket sürümlerini de yazın.
  • “ok”, doğru sonuç demek değil. Yerel ayar, ağ ya da veri gibi ortama bağlı davranışlar matriste görünmez.

Sürüm satırı yazmak modele saniyeler, sınamak bize birkaç dakika sürüyor. Yapay zekâdan bir proje istediğinizde, sürüm satırını yazmasını değil, bu satırı nasıl sınadığını göstermesini isteyin. Ajan kullanıyorsanız işi tümüyle ona bırakabilirsiniz: bu yazıdaki matris aracını da Claude yazdı ve çalıştırdı.

Kodu Kendiniz Deneyin

Matris aracı proje 50’de tek bir main.py dosyasında. Python 3.8 veya üstü, standart kütüphane ve Docker yetiyor. İmajlar ilk çalıştırmada indiriliyor (her biri diskte yaklaşık 120 MB):

python3 main.py ../38_regex_validation
python3 main.py ../37_argparse_cli --args=--help
python3 main.py ../26_multiprocessing_vs_threading --versions 3.9,3.10 --timeout 600 -v

--args her main.py‘ye aynı argümanları geçiriyor, -v hata satırını da gösteriyor.

Hızlı bir statik kontrol için vermin:

pip install vermin
vermin --no-tips --eval-annotations main.py

Kaynaklar

Ahmet Aksoy & Claude

Not: Bu yazıda incelediğimiz kodu ve benzer projelerin kaynak kodlarını https://github.com/ahmetax/practical-python-examples adresinde bulabilirsiniz.

Bir Yanıt Bırak

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir