Python ile komut satırı aracı yazmanın standart yolu argparse modülüdür: programın hangi argümanları beklediğini ArgumentParser nesnesine tanımlarsınız, modül sys.argv listesini sizin yerinize ayrıştırır, değerleri doğru tipe çevirir, -h yardım metnini kendiliğinden üretir ve kullanıcı yanlış bir şey yazdığında anlaşılır bir hata mesajıyla çıkar. Ek paket kurmanız gerekmez; modül Python 3.2 sürümünden beri standart kütüphanededir.

Bu yazıda konumsal ve isteğe bağlı argümanları, action, nargs, type ve choices parametrelerini, alt komutları, karşılıklı dışlayan grupları ve hata yönetimini çalışan örneklerle anlatıyoruz. Python 3.15 ile gelen değişiklikler (yanlış yazılan seçenekler için varsayılan öneri, yardım metninde ters tırnak vurgusu, kısa ve tek tireli uzun seçenekte dest kuralı) ayrı bir bölümde. Tüm örnekler Python 3.15.2 ile çalıştırıldı.

sys.argv yerine neden argparse?

Komut satırına yazılan her şey Python'a sys.argv adında bir metin listesi olarak gelir. İlk eleman betiğin adıdır, geri kalanlar kullanıcının yazdıklarıdır. Küçük bir betikte sys.argv[1] okumak yeterli görünebilir; ancak seçeneklerin sırası değiştiğinde, bir değer eksik kaldığında, sayı beklenen yere metin yazıldığında ya da kullanıcı yardım istediğinde bu kontrollerin hepsini elle yazmanız gerekir. argparse bu işlerin tamamını tanımladığınız kurallardan türetir: neyin zorunlu olduğunu, hangi tipe çevrileceğini ve yardım ekranında nasıl görüneceğini tek yerde belirtirsiniz.

Modülün üç temel parçası vardır. ArgumentParser program düzeyindeki ayarları (program adı, açıklama, alt metin) tutar. add_argument() her bir argümanın kuralını ekler. parse_args() ise ayrıştırmayı çalıştırır ve sonucu Namespace adlı basit bir nesnede, öznitelik olarak döndürür. Python belgeleri daha karmaşık gereksinimler için (örneğin seçeneklerle konumsal argümanların karışmasını tamamen kapatmak) başka kütüphanelere bakılabileceğini de not eder; sıradan araçların büyük çoğunluğu için argparse yeterlidir.

İlk komut satırı aracınız

Aşağıdaki selam.py betiği bir konumsal argüman (isim), değer alan bir seçenek (-t/--tekrar) ve açma kapama bayrağı (-b/--buyuk) tanımlar. Yardım metnindeki %(default)s belirteci, varsayılan değeri yardım ekranına otomatik yazar.

python
import argparse

parser = argparse.ArgumentParser(
    prog='selam',
    description='Verilen isme selam verir.')
parser.add_argument('isim')                      # konumsal argüman
parser.add_argument('-t', '--tekrar', type=int, default=1,
                    help='kaç kez yazılacağı (varsayılan: %(default)s)')
parser.add_argument('-b', '--buyuk', action='store_true',
                    help='çıktıyı büyük harfle yaz')

args = parser.parse_args()
print(args)
metin = f'Merhaba {args.isim}!'
if args.buyuk:
    metin = metin.upper()
for _ in range(args.tekrar):
    print(metin)

Betiği farklı argümanlarla çalıştırdığımızda gördüğümüz gerçek çıktılar şunlar:

bash
$ python selam.py Ayşe -t 2 --buyuk
Namespace(isim='Ayşe', tekrar=2, buyuk=True)
MERHABA AYŞE!
MERHABA AYŞE!

$ python selam.py --help
usage: selam [-h] [-t TEKRAR] [-b] isim

Verilen isme selam verir.

positional arguments:
  isim

options:
  -h, --help           show this help message and exit
  -t, --tekrar TEKRAR  kaç kez yazılacağı (varsayılan: 1)
  -b, --buyuk          çıktıyı büyük harfle yaz

Dikkat edilecek birkaç nokta var. -t 2 değeri komut satırından metin olarak gelir, type=int sayesinde Namespace içinde tam sayı olur. --tekrar seçeneğinin değeri args.tekrar adıyla okunur; uzun seçenek adındaki tireler alt çizgiye çevrilir (--dosya-adi için args.dosya_adi). Yardım ekranındaki TEKRAR yazısı metavar olarak adlandırılır ve varsayılan olarak hedef adın büyük harfli hâlidir. -h/--help seçeneğini siz eklemediniz; add_help=True varsayılanı onu her parser'a ekler.

Yanlış kullanımda modül, kullanım satırını ve hatayı standart hata akışına yazar ve programı 2 çıkış koduyla sonlandırır:

bash
$ python selam.py
usage: selam [-h] [-t TEKRAR] [-b] isim
selam: error: the following arguments are required: isim

$ python selam.py Ali -t iki
usage: selam [-h] [-t TEKRAR] [-b] isim
selam: error: argument -t/--tekrar: invalid int value: 'iki'

Konumsal ve isteğe bağlı argümanlar

add_argument() çağrısına verdiğiniz ilk metin, argümanın türünü belirler. Tire ile başlamayan bir ad ('isim') konumsal argümandır: kullanıcı onu yazmak zorundadır ve sırası önemlidir. -t ya da --tekrar gibi tireli adlar isteğe bağlı seçeneklerdir; yazılmazsa değeri default olur, o da verilmemişse None. Bir seçeneğe hem kısa hem uzun ad verebilirsiniz; hedef öznitelik adı uzun adla belirlenir.

Değer alan seçeneklerde birkaç yazım biçimi geçerlidir: --tekrar 3, --tekrar=3, kısa seçenekte bitişik -t3. Değer almayan kısa bayraklar tek tire altında birleştirilebilir; -xyz yazımı, son harf hariç hepsi değer almıyorsa -x -y -z anlamına gelir. Uzun seçenekler, belirsizlik yoksa kısaltılarak da yazılabilir (--tek yazımı --tekrar olarak anlaşılır). Bu davranış istenmiyorsa allow_abbrev=False verin.

Seçenekleri zorunlu yapmak için required=True kullanılabilir; ancak belgeler kullanıcıların "seçenek" kelimesinden isteğe bağlılık beklediğini hatırlatır ve bunun mümkünse kaçınılması gereken bir tasarım olduğunu söyler. Zorunlu bir bilgi için konumsal argüman genellikle daha doğal bir seçimdir.

add_argument parametreleri

add_argument parametreleri: action, nargs, type, default

Bir argümanın davranışı add_argument() anahtar kelimeleriyle belirlenir. Aşağıdaki tablo en sık kullanılanları özetler:

ParametreNe işe yararÖrnek
actionArgüman görüldüğünde ne yapılacağı (varsayılan store)action='store_true'
nargsKaç değer tüketileceği: sayı, ?, *, +nargs='+'
typeMetni dönüştüren çağrılabilir nesnetype=int, type=pathlib.Path
defaultArgüman yoksa kullanılacak değerdefault=1
choicesİzin verilen değerler listesichoices=['gzip', 'yok']
requiredSeçeneği zorunlu yapar (yalnızca seçenekler)required=True
helpYardım ekranındaki açıklamahelp='çıktı dosyası'
metavarYardımda görünen değer adımetavar='DOSYA'
destNamespace içindeki öznitelik adıdest='cikti'
deprecatedKullanılınca uyarı yazdırır (3.13 ile geldi)deprecated=True

nargs değerlerinin anlamı şöyledir: tam sayı N tam N değeri bir liste olarak toplar (nargs=1 bile tek elemanlı liste üretir); '?' bir değeri varsa alır, yoksa default kullanır (seçenek yazılıp değer verilmezse const devreye girer); '*' sıfır ya da daha fazla değeri listeye toplar; '+' en az bir değer ister, hiç yoksa hata verir. nargs verilmezse tek değer okunur ve liste değil, değerin kendisi saklanır.

action türleri ve çoklu değerler

action türleri ve çoklu değerler

Varsayılan store eylemi değeri olduğu gibi saklar. Diğer yerleşik eylemler farklı ihtiyaçlara karşılık gelir: store_true ve store_false bayrak içindir; count seçeneğin kaç kez yazıldığını sayar (-vvv gibi ayrıntı seviyeleri için); append aynı seçeneğin her kullanımını bir listeye ekler; extend çok değerli bir seçeneğin değerlerini tek listeye yayar; version sürüm bilgisini yazıp çıkar.

python
import argparse

p = argparse.ArgumentParser(prog='yedek')
p.add_argument('dosyalar', nargs='+')
p.add_argument('-v', '--verbose', action='count', default=0)
p.add_argument('--etiket', action='append', default=[])
p.add_argument('--sikistir', choices=['gzip', 'bz2', 'yok'], default='yok')

print(p.parse_args(['a.txt', 'b.txt', '-vv', '--etiket', 'gunluk', '--etiket', 'acil']))
# çıktı: Namespace(dosyalar=['a.txt', 'b.txt'], verbose=2, etiket=['gunluk', 'acil'], sikistir='yok')
print(p.parse_args(['c.txt', '--sikistir=gzip']))
# çıktı: Namespace(dosyalar=['c.txt'], verbose=0, etiket=[], sikistir='gzip')
print(vars(p.parse_args(['d.txt'])))
# çıktı: {'dosyalar': ['d.txt'], 'verbose': 0, 'etiket': [], 'sikistir': 'yok'}

Bu örnekte parse_args() fonksiyonuna liste verdik. Liste verilmezse sys.argv[1:] kullanılır; liste vermek, aracı test ederken ve bu yazıdaki gibi denemeler yaparken çok işe yarar. vars() yerleşik fonksiyonu Namespace nesnesini sözlüğe çevirir; sonucu JSON'a yazmak ya da başka bir fonksiyona ** ile açmak istediğinizde kullanışlıdır.

type ile dönüştürme ve doğrulama

type parametresi tek bir metin alan herhangi bir çağrılabilir nesne olabilir: int, float, pathlib.Path ya da kendi fonksiyonunuz. Fonksiyon ArgumentTypeError, TypeError veya ValueError fırlatırsa argparse bunu yakalar ve düzgün biçimlenmiş bir hata mesajına çevirir. Başka bir istisna türü yakalanmaz. Varsayılan değer bir metinse dönüştürme ona da uygulanır; metin değilse değer olduğu gibi kullanılır.

python
import argparse

p = argparse.ArgumentParser(prog='hesap', exit_on_error=False)
p.add_argument('--adet', type=int)
try:
    p.parse_args(['--adet', 'beş'])
except argparse.ArgumentError as e:
    print('Yakalandı:', e)
# çıktı: Yakalandı: argument --adet: invalid int value: 'beş'

def pozitif(metin):
    sayi = int(metin)
    if sayi <= 0:
        raise argparse.ArgumentTypeError(f'{sayi} pozitif değil')
    return sayi

q = argparse.ArgumentParser(prog='hesap')
q.add_argument('--adet', type=pozitif)
print(q.parse_args(['--adet', '4']))
# çıktı: Namespace(adet=4)
q.parse_args(['--adet', '-2'])
# usage: hesap [-h] [--adet ADET]
# hesap: error: argument --adet: -2 pozitif değil

Burada iki ayrıntı öne çıkıyor. Birincisi exit_on_error=False: parser hata durumunda programı kapatmak yerine ArgumentError fırlatır, siz de yakalayıp kendi akışınızı sürdürürsünüz. İkincisi -2 değerinin seçenek sanılmamasıdır. Parser'da negatif sayıya benzeyen bir seçenek tanımlı olmadığı için -2 bir değer olarak kabul edildi. Tireyle başlayan ve sayıya benzemeyen bir değeri konumsal argüman olarak geçmek gerekiyorsa, önüne -- yazarak "bundan sonrası seçenek değil" diyebilirsiniz.

Belgelerin açıkça uyardığı bir tuzak type=bool kullanımıdır. bool() boş metni False, boş olmayan her metni True yapar; yani --hatali False yazmak True üretir. Açma kapama için store_true ya da hem --renk hem --no-renk biçimini tek tanımla üreten BooleanOptionalAction kullanın:

python
import argparse

p = argparse.ArgumentParser(prog='ayar')
p.add_argument('--renk', action=argparse.BooleanOptionalAction, default=True)
p.add_argument('--hatali', type=bool)

print(p.parse_args([]))
# çıktı: Namespace(renk=True, hatali=None)
print(p.parse_args(['--no-renk']))
# çıktı: Namespace(renk=False, hatali=None)
print(p.parse_args(['--hatali', 'False']))
# çıktı: Namespace(renk=True, hatali=True)

JSON ya da YAML çözümleme, dosya açma gibi karmaşık hata durumları olan dönüşümleri type içinde yapmamak gerekir; belgeler bunları ayrıştırmadan sonra, uygulama kodunda ele almayı önerir. Aynı nedenle argparse.FileType Python 3.14 itibarıyla kullanımdan kaldırılmak üzere işaretlendi (deprecated): bir argüman dosyayı açtıktan sonra sonraki argüman hata verirse dosya kapatılmadan kalabiliyordu. Dosya yolunu type=pathlib.Path ile alıp dosyayı with bloğunda açmak daha güvenlidir.

choices ve Python 3.15 ile gelen öneriler

Bir argüman yalnızca belirli değerleri alabiliyorsa choices listesi verin. Değerler, type dönüşümü yapıldıktan sonra kontrol edilir; yani type=int ile birlikte choices sayılardan oluşmalıdır. Python 3.15 ile suggest_on_error parametresinin varsayılanı True oldu: kullanıcı geçerli bir seçeneğe yakın bir değer yazdığında hata mesajı "maybe you meant" önerisi içerir. Bu özellik yalnızca seçenekleri metin olan choices listelerinde ve alt komut adlarında çalışır.

python
import argparse

p = argparse.ArgumentParser(prog='derle')
p.add_argument('--mod', choices=['debug', 'release'])
p.parse_args(['--mod', 'relase'])
# usage: derle [-h] [--mod {debug,release}]
# derle: error: argument --mod: invalid choice: 'relase', maybe you meant 'release'? (choose from 'debug', 'release')

Belgeler choices için enum.Enum kullanılmasını önermez; çünkü yardım, kullanım ve hata metinlerinde nasıl görüneceğini denetlemek zorlaşır. Çok sayıda seçenek yardım ekranını kalabalıklaştırıyorsa açık bir metavar verebilirsiniz.

Alt komutlar: git benzeri araçlar

git commit, git push gibi her biri farklı argüman alan komutlar için add_subparsers() kullanılır. Bu çağrı özel bir eylem nesnesi döndürür; onun add_parser() metodu her alt komut için ayrı bir ArgumentParser üretir. Belgelerin önerdiği etkili desen, her alt parser'a set_defaults(func=...) ile çalıştıracağı fonksiyonu bağlamaktır. Böylece ayrıştırmadan sonra args.func(args) demek yeterli olur.

python
import argparse

def ekle(args):
    print(f'{args.ad} eklendi (öncelik {args.oncelik})')

def listele(args):
    print('Görevler listeleniyor, tamamlananlar:', args.hepsi)

parser = argparse.ArgumentParser(prog='gorev')
alt = parser.add_subparsers(dest='komut', required=True)

p_ekle = alt.add_parser('ekle', help='yeni görev ekler')
p_ekle.add_argument('ad')
p_ekle.add_argument('-o', '--oncelik', type=int, default=3)
p_ekle.set_defaults(func=ekle)

p_liste = alt.add_parser('liste', aliases=['ls'], help='görevleri listeler')
p_liste.add_argument('--hepsi', action='store_true')
p_liste.set_defaults(func=listele)

for satir in (['ekle', 'Fatura öde', '-o', '1'], ['ls', '--hepsi']):
    args = parser.parse_args(satir)
    print(args.komut, end=': ')
    args.func(args)
# çıktı: ekle: Fatura öde eklendi (öncelik 1)
# çıktı: ls: Görevler listeleniyor, tamamlananlar: True

dest='komut' seçilen alt komutun adını saklar; takma ad kullanıldığında (ls) yazılan ad kaydedilir. required=True verilmezse kullanıcı hiç alt komut yazmadan da program çalışır ve args.func bulunmadığı için hata alırsınız. Dönen Namespace yalnızca ana parser'ın ve seçilen alt parser'ın özniteliklerini içerir; diğer alt komutların argümanları görünmez. Bir alt parser, ana parser ile aynı dest adını kullanırsa ikisi tek özniteliği paylaşır ve ana değer korunmaz; farklı adlar verin.

Karşılıklı dışlayan seçenekler

--sessiz ve --ayrintili gibi birlikte kullanılması anlamsız seçenekleri add_mutually_exclusive_group() ile gruplayabilirsiniz. Grup required=True alırsa kullanıcının en az birini seçmesi gerekir.

python
import argparse

p = argparse.ArgumentParser(prog='cikti')
grup = p.add_mutually_exclusive_group()
grup.add_argument('-q', '--sessiz', action='store_true')
grup.add_argument('-v', '--ayrintili', action='store_true')
print(p.parse_args(['-q']))
# çıktı: Namespace(sessiz=True, ayrintili=False)
p.parse_args(['-q', '-v'])
# usage: cikti [-h] [-q | -v]
# cikti: error: argument -v/--ayrintili: not allowed with argument -q/--sessiz

Yardım ekranında seçenekleri başlık altında toplamak için ise add_argument_group() vardır; bu yalnızca görünümü düzenler, kural koymaz. Python 3.14 ile dışlayan bir grubun içinde yeniden grup açmak hata fırlatır hâle geldi; bu iç içe kullanım hiçbir zaman desteklenmiyordu.

Bilinmeyen argümanları başka araca bırakmak

Bazen betiğiniz yalnızca kendi seçeneklerini okuyup kalanını başka bir programa aktarmalıdır. parse_known_args() tanımadığı argümanlar için hata vermez; bir (Namespace, kalan_liste) ikilisi döndürür. Önek eşleştirmesi bu metotta da geçerlidir; kısaltma gibi görünen bir argüman tanınan seçeneğe bağlanabilir.

python
import argparse

p = argparse.ArgumentParser(prog='calistir')
p.add_argument('--debug', action='store_true')
args, kalan = p.parse_known_args(['--debug', '--port', '8080', 'app.py'])
print(args)
# çıktı: Namespace(debug=True)
print(kalan)
# çıktı: ['--port', '8080', 'app.py']

Python 3.15 ile argparse'ta neler değişti?

Python 3.15 ile argparse'ta neler değişti? suggest_on_error varsayılanı True oldu; yanlış yazılan seçim ve alt komut..., BooleanOptionalAction tek tireli uzun seçenekleri (-foo...

3.15 sürüm notlarında argparse için şu maddeler yer alıyor:

  • suggest_on_error varsayılanı True oldu; yanlış yazılan seçim ve alt komut adları için öneri artık kendiliğinden geliyor (parametre 3.14 ile eklenmişti).
  • BooleanOptionalAction tek tireli uzun seçenekleri (-foo için -nofoo) ve farklı önek karakterlerini destekliyor.
  • Açıklama, alt metin ve argüman help metinlerinde tek ya da çift ters tırnak arasına yazılan kod parçaları, renk açıkken farklı renkte gösteriliyor; renk kapalıyken tırnaklar olduğu gibi kalıyor.
  • add_argument('-f', '-foo') gibi kısa seçenekle tek tireli uzun seçenek birlikte verildiğinde dest artık uzun addan çıkarılıyor. Eski davranışı korumak için dest değerini açıkça yazın.
  • Modülün __version__ özniteliği kullanımdan kaldırılmak üzere işaretlendi; 3.20 sürümünde silinmesi planlanıyor.
python
import argparse

p = argparse.ArgumentParser(prog='eski')
p.add_argument('-f', '-foo')
print(p.parse_args(['-f', '1']))
# çıktı: Namespace(foo='1')

q = argparse.ArgumentParser(prog='eski')
q.add_argument('-f', '-foo', dest='f')
print(q.parse_args(['-f', '1']))
# çıktı: Namespace(f='1')

Sürümün diğer yenilikleri için Python 3.15 yenilikleri yazısına bakabilirsiniz.

Sık yapılan hatalar

  • Tip dönüştürmeyi unutmak: type verilmeyen her değer metindir; args.port + 1 gibi işlemler TypeError verir.
  • type=bool kullanmak: her boş olmayan metin True olur. store_true ya da BooleanOptionalAction kullanın.
  • Yardım metninde tek % yazmak: help metni %-biçimlendirmeden geçer; yüzde işareti için %% yazın.
  • Alt komutta required=True vermemek: komutsuz çağrıda args.func bulunamaz.
  • Ayrıştırmayı modül yüklenirken yapmak: parse_args() çağrısını if __name__ == '__main__': bloğuna ya da bir main() fonksiyonuna koyun; aksi hâlde modülü başka yerden içe aktarmak da argüman okumaya çalışır. Konu için modül ve paket yazısına bakın.
  • Çıkış kodunu yok saymak: ayrıştırma hatası 2 koduyla çıkar; kabuk betikleri ve cron görevleri bu kodu kontrol edebilir.

Sık Sorulan Sorular

argparse ile optparse arasındaki fark nedir?

Taşıma rehberine göre argparse, optparse modülünün kendi başına sunmadığı konumsal argüman işleme, alt komutlar, sıfır veya daha fazla değer alan argümanlar ve daha bilgilendirici kullanım mesajları gibi özellikler sağlar. Belgeler temel komut satırı uygulamaları için argparse modülünü önerir; ancak optparse ile çalışan ve bundan memnun olan uygulamaların taşınmak zorunda olmadığını, seçeneklerle konumsal argümanların karışmasını daha ince denetlemek gibi durumlarda optparse modülünün hâlâ bir alternatif olduğunu da belirtir.

Argümanları test ederken komut satırına mı yazmam gerekir?

Hayır. parser.parse_args(['--adet', '4']) gibi bir liste verdiğinizde sys.argv hiç okunmaz. Bu yöntem birim testlerde de kullanılır; unittest ile test yazma yazısında testlerin nasıl kurulacağını anlattık.

Hata olduğunda programın kapanmasını nasıl engellerim?

ArgumentParser(exit_on_error=False) ile oluşturun ve argparse.ArgumentError istisnasını yakalayın. Hata yakalama mantığının ayrıntıları için try/except rehberine bakabilirsiniz.

Ayrıntı seviyesini -v, -vv, -vvv ile nasıl yaparım?

add_argument('-v', action='count', default=0) tanımlayın. default verilmezse seçenek yazılmadığında değer None olur; karşılaştırma yaparken hata almamak için 0 vermek iyi bir alışkanlıktır. Bu sayıyı logging seviyelerine bağlamak yaygın bir yöntemdir.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Python öğrenme rehberi · unittest ile test yazma · Modül ve paket import · logging modülü · Python 3.15 yenilikleri