Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Asosiy tuzilma
- 2.2. type, default, choices
- 2.3. Amallar (action) va nargs
- 2.4. Guruhlar
- 2.5. Ichki buyruqlar
- 2.6. Xatolar
- 2.7. Testlanadigan tuzilma
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Argumentlar va amallar
- Misol 2 — Xatolar va tuzoqlar
- Misol 3 — Ichki buyruqlar
- Misol 4 — Amaliy: vazifalar menejeri CLI
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
15.15-dars: argparse — CLI
15-QISM — STANDART KUTUBXONA · 15-dars
1. Kirish va motivatsiya
15.11-darsda sys.argv ni ko'rdik: argumentlar oddiy satrlar ro'yxati. Kichik skript uchun sys.argv[1] yetadi. Lekin haqiqiy buyruq qatori dasturida savollar ko'payadi:
- Qaysi argument majburiy, qaysi ixtiyoriy?
-n 5,--soni=5,-n5— hammasi bir xil ishlashi kerak."besh"kiritilsa — tushunarli xato va 2 chiqish kodi.--helpavtomatik, har argumentning tavsifi va sukut qiymati bilan.git commit,git pushkabi ichki buyruqlar.
Bularni qo'lda yozish — yuzlab qator va har dasturda boshqacha xulq. argparse standart kutubxonada va bularning hammasini beradi.
Real vaziyat. Ma'lumotlarni import qilish skripti --tozala bayrog'ini shunday e'lon qilgan:
parser.add_argument("--tozala", type=bool, default=False)Operator ehtiyotkorlik uchun --tozala False deb yozdi. Skript jadvalni tozaladi: bool("False") — True. Boshqa jamoaning CLI'sida esa sys.argv qo'lda tahlil qilinardi va noma'lum argumentlar jim e'tiborsiz qolardi — --quruq-rejim o'rniga --quruq_rejim yozilgani uchun "quruq" ishga tushirish haqiqiy o'chirishga aylandi.
Bu darsda argparse ni to'liq o'rganamiz va testlanadigan CLI dastur quramiz.
Bu darsda:
- Pozitsion va ixtiyoriy argumentlar,
type,default,choices,required - Amallar:
store_true,count,append,nargs,BooleanOptionalAction -
type=booltuzog'i - O'z tur funksiyasi va
ArgumentTypeError - O'zaro istisno qiluvchi guruhlar
- Ichki buyruqlar:
add_subparsers,set_defaults, umumiy (parent) parserlar - Xatolar: chiqish kodi 2,
exit_on_error=False, 3.14suggest_on_error - Testlanadigan tuzilma:
main(argv) -> int - Amaliy: vazifalar menejeri CLI
2. Nazariya — chuqur tushuntirish
2.1. Asosiy tuzilma
import argparse
parser = argparse.ArgumentParser(prog="import", description="CSV ni bazaga import qilish")
parser.add_argument("fayl") # pozitsion — majburiy
parser.add_argument("-n", "--soni", type=int, default=100) # ixtiyoriy
parser.add_argument("--format", choices=["csv", "tsv"], default="csv")
args = parser.parse_args() # sys.argv[1:] dan
args = parser.parse_args(["a.csv", "-n", "5"]) # ⭐ testlarda — aniq ro'yxat
args.fayl, args.soni| Tushuncha | Qanday |
|---|---|
| Pozitsion | "fayl" — nomi - siz, majburiy |
| Ixtiyoriy | "-n", "--soni" — atribut nomi uzun shakldan: soni (- → _) |
dest |
Atribut nomini aniq berish |
required=True |
Ixtiyoriy argumentni majburiy qilish (kamdan-kam) |
metavar |
Yordam matnidagi nom |
help |
Tavsif; %(default)s sukut qiymatini qo'yadi |
Yozish shakllari bir xil ishlaydi: -n 5, -n5, --soni 5, --soni=5. -- dan keyingi hamma narsa pozitsion (masalan, - bilan boshlanadigan fayl nomi).
2.2. type, default, choices
| Parametr | Xulq |
|---|---|
type=int |
Satrni o'giradi; xato bo'lsa invalid int value va kod 2 |
type=Path, type=float |
Istalgan chaqiriladigan narsa |
default="8000" |
Satr sukut ham type orqali o'tadi → int |
choices=[...] |
Ro'yxatdan tashqari qiymat — xato |
O'z tur funksiyasi:
def musbat_son(qiymat: str) -> int:
son = int(qiymat)
if son <= 0:
raise argparse.ArgumentTypeError(f"musbat son kerak: {qiymat}")
return son2.3. Amallar (action) va nargs
| Amal | Misol | Natija |
|---|---|---|
store (sukut) |
--soni 5 |
5 |
store_true / store_false |
--quruq |
True |
count |
-vvv |
3 |
append |
--teg a --teg b |
["a", "b"] |
BooleanOptionalAction |
--rang / --no-rang |
True / False |
version |
--version |
Versiyani chiqarib, chiqadi |
nargs |
Ma'nosi |
|---|---|
N (son) |
Aynan N ta |
"?" |
0 yoki 1 (const — argumentsiz berilganda) |
"*" |
0 yoki ko'p |
"+" |
1 yoki ko'p |
type=bool tuzog'i: bool("False") — True. Mantiqiy bayroqlar uchun store_true yoki BooleanOptionalAction.
append bilan default=[] — ro'yxat qiymatlari sukutga qo'shiladi; sukut bo'sh bo'lmasa kutilmagan natija beradi.
2.4. Guruhlar
| Guruh | Maqsad |
|---|---|
add_argument_group("Chiqish") |
Yordam matnida bo'limlar |
add_mutually_exclusive_group() |
Bir vaqtda faqat bittasi: --json yoki --csv |
add_mutually_exclusive_group(required=True) |
Aynan bittasi majburiy |
2.5. Ichki buyruqlar
parser = argparse.ArgumentParser(prog="vazifa")
buyruqlar = parser.add_subparsers(dest="buyruq", required=True)
qosh = buyruqlar.add_parser("qosh", help="yangi vazifa")
qosh.add_argument("matn")
qosh.set_defaults(ishlovchi=vazifa_qosh) # ⭐ har buyruqqa funksiya
args = parser.parse_args()
return args.ishlovchi(args)Umumiy argumentlar — parents:
umumiy = argparse.ArgumentParser(add_help=False)
umumiy.add_argument("--fayl", type=Path)
buyruqlar.add_parser("qosh", parents=[umumiy])| Tafsilot | Izoh |
|---|---|
required=True |
Buyruqsiz ishga tushirish — xato (aks holda args.buyruq = None) |
set_defaults(ishlovchi=...) |
if/elif zanjiri o'rniga |
| Umumiy bayroqlar | Asosiy parserda — buyruqdan oldin; parents bilan — buyruqdan keyin |
2.6. Xatolar
| Holat | Xulq |
|---|---|
| Noto'g'ri argument | stderr ga usage va error: ..., kod 2 (SystemExit) |
--help |
stdout ga yordam, kod 0 |
parse_known_args() |
Noma'lum argumentlarni xato qilmay, alohida ro'yxatda qaytaradi |
exit_on_error=False |
Chiqish o'rniga ArgumentError — o'z xato ishlovingiz uchun |
parser.error("xabar") |
O'z tekshiruvingiz xatosini standart shaklda chiqarish (kod 2) |
suggest_on_error=True (3.14) |
choices va ichki buyruqlarda o'xshash variantni taklif qiladi: maybe you meant 'json'? |
color (3.14) |
Yordam va xatolarni rangli chiqarish (terminalda) |
Argumentlararo shartlar (masalan, "--oxiri --boshi dan keyin bo'lsin") argparse da yo'q — parse_args dan keyin tekshirib, parser.error(...) chaqiring.
2.7. Testlanadigan tuzilma
def parser_yarat() -> argparse.ArgumentParser: ...
def main(argv: list[str] | None = None) -> int:
args = parser_yarat().parse_args(argv) # None → sys.argv[1:]
try:
return args.ishlovchi(args)
except FoydalanuvchiXatosi as x:
print(f"xato: {x}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())| Afzallik | Nega |
|---|---|
main(["qosh", "x"]) |
Test jarayon ishga tushirmasdan |
int qaytaradi |
Chiqish kodi aniq (15.11) |
| Parser alohida funksiyada | Yordam matnini testlash, hujjat yaratish |
| Sozlamalar muhitdan sukut | default=os.environ.get("VAZIFA_FAYL", ...) |
Katta CLI'lar uchun tashqi kutubxonalar: click, typer (tur annotatsiyalari asosida) — 19.6-dars.
3. Tez ma'lumotnoma
import argparse
p = argparse.ArgumentParser(prog="dastur", description="...")
p.add_argument("kirish", type=Path)
p.add_argument("-n", "--soni", type=int, default=10, help="soni (sukut: %(default)s)")
p.add_argument("-v", "--verbose", action="count", default=0)
p.add_argument("--quruq", action="store_true")
p.add_argument("--rang", action=argparse.BooleanOptionalAction, default=True)
p.add_argument("--format", choices=["json", "csv"], default="json")
g = p.add_mutually_exclusive_group(); g.add_argument("--tez"); g.add_argument("--aniq")
sub = p.add_subparsers(dest="buyruq", required=True)
b = sub.add_parser("qosh"); b.set_defaults(ishlovchi=qosh)
args = p.parse_args(argv)Qoidalar
mantiqiy bayroq — store_true / BooleanOptionalAction, type=bool emas
main(argv) -> int — testlanadigan
buyruq — set_defaults(ishlovchi=...)
argumentlararo shart — parse_args dan keyin parser.error
noto'g'ri foydalanish — kod 2 (argparse o'zi)
prog ni aniq bering — yordam matni barqaror4. Batafsil misollar
Misol 1 — Argumentlar va amallar
"""Pozitsion va ixtiyoriy; yozish shakllari; type, default, choices; store_true, count, append, nargs, BooleanOptionalAction; vars(); yordam matni."""
import argparse
from pathlib import Path
def parser_yarat() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(prog="import", description="CSV faylni bazaga import qilish", color=False)
p.add_argument("fayl", type=Path, help="kirish fayli")
p.add_argument("-n", "--soni", type=int, default="100", help="bir paketdagi qatorlar (sukut: %(default)s)")
p.add_argument("-v", "--verbose", action="count", default=0, help="batafsil chiqish (-vv, -vvv)")
p.add_argument("--quruq", action="store_true", help="bazaga yozmasdan tekshirish")
p.add_argument("--rang", action=argparse.BooleanOptionalAction, default=True, help="rangli chiqish")
p.add_argument("--format", choices=["csv", "tsv"], default="csv")
p.add_argument("--ustun", action="append", default=[], metavar="NOM", help="faqat shu ustunlar (takrorlanadi)")
p.add_argument("--sana", nargs=2, metavar=("BOSHI", "OXIRI"), help="sana oralig'i")
p.add_argument("--kodlash", nargs="?", const="utf-8", default=None)
return p
def main() -> None:
p = parser_yarat()
print("=== 1. Oddiy ===")
args = p.parse_args(["sotuv.csv"])
print(f" {vars(args) | {'fayl': f'Path({args.fayl.as_posix()!r})'}}")
print(f" soni turi: {type(args.soni).__name__} (sukut '100' satr edi — type orqali o'tdi)")
print("\n=== 2. Yozish shakllari bir xil ===")
for argv in (["a.csv", "-n", "5"], ["a.csv", "-n5"], ["a.csv", "--soni", "5"], ["a.csv", "--soni=5"], ["-n", "5", "a.csv"]):
print(f" {' '.join(argv):20} → soni={p.parse_args(argv).soni}")
print("\n=== 3. Amallar ===")
args = p.parse_args(["a.csv", "-vvv", "--quruq", "--no-rang", "--ustun", "sana", "--ustun", "summa",
"--sana", "2026-09-01", "2026-09-30", "--kodlash"])
print(f" verbose={args.verbose}, quruq={args.quruq}, rang={args.rang}")
print(f" ustun={args.ustun}, sana={args.sana}, kodlash={args.kodlash!r}")
print(f" --kodlash berilmasa: {p.parse_args(['a.csv']).kodlash!r}, qiymat bilan: {p.parse_args(['a.csv', '--kodlash', 'cp1251']).kodlash!r}")
print("\n=== 4. '--' dan keyin ===")
print(f" fayl nomi '-yashirin.csv': {p.parse_args(['--', '-yashirin.csv']).fayl}")
print("\n=== 5. Yordam matni ===")
for qator in p.format_help().splitlines():
print(f" {qator}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Oddiy ===
{'fayl': "Path('sotuv.csv')", 'soni': 100, 'verbose': 0, 'quruq': False, 'rang': True, 'format': 'csv', 'ustun': [], 'sana': None, 'kodlash': None}
soni turi: int (sukut '100' satr edi — type orqali o'tdi)
=== 2. Yozish shakllari bir xil ===
a.csv -n 5 → soni=5
a.csv -n5 → soni=5
a.csv --soni 5 → soni=5
a.csv --soni=5 → soni=5
-n 5 a.csv → soni=5
=== 3. Amallar ===
verbose=3, quruq=True, rang=False
ustun=['sana', 'summa'], sana=['2026-09-01', '2026-09-30'], kodlash='utf-8'
--kodlash berilmasa: None, qiymat bilan: 'cp1251'
=== 4. '--' dan keyin ===
fayl nomi '-yashirin.csv': -yashirin.csv
=== 5. Yordam matni ===
usage: import [-h] [-n SONI] [-v] [--quruq] [--rang | --no-rang]
[--format {csv,tsv}] [--ustun NOM] [--sana BOSHI OXIRI]
[--kodlash [KODLASH]]
fayl
CSV faylni bazaga import qilish
positional arguments:
fayl kirish fayli
options:
-h, --help show this help message and exit
-n, --soni SONI bir paketdagi qatorlar (sukut: 100)
-v, --verbose batafsil chiqish (-vv, -vvv)
--quruq bazaga yozmasdan tekshirish
--rang, --no-rang rangli chiqish
--format {csv,tsv}
--ustun NOM faqat shu ustunlar (takrorlanadi)
--sana BOSHI OXIRI sana oralig'i
--kodlash [KODLASH]Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.
Misol 2 — Xatolar va tuzoqlar
"""Xato xabarlari va kod 2; type=bool tuzog'i; ArgumentTypeError; o'zaro istisno guruhi; argumentlararo tekshiruv parser.error bilan; exit_on_error=False; parse_known_args; 3.14 suggest_on_error."""
import argparse
import contextlib
import io
from datetime import date
def xato_ushla(parser: argparse.ArgumentParser, argv: list[str]) -> str:
oqim = io.StringIO()
with contextlib.redirect_stderr(oqim):
try:
parser.parse_args(argv)
except SystemExit as chiqish:
return f"kod {chiqish.code}: {oqim.getvalue().strip().splitlines()[-1]}"
return "xato yo'q"
def musbat_son(qiymat: str) -> int:
try:
son = int(qiymat)
except ValueError:
raise argparse.ArgumentTypeError(f"son kerak: {qiymat!r}") from None
if son <= 0:
raise argparse.ArgumentTypeError(f"musbat son kerak: {qiymat}")
return son
def main() -> None:
p = argparse.ArgumentParser(prog="hisobot", color=False)
p.add_argument("fayl")
p.add_argument("--soni", type=musbat_son, default=10)
p.add_argument("--format", choices=["json", "csv"], default="json")
p.add_argument("--boshi", type=date.fromisoformat)
p.add_argument("--oxiri", type=date.fromisoformat)
guruh = p.add_mutually_exclusive_group()
guruh.add_argument("--jim", action="store_true")
guruh.add_argument("--batafsil", action="store_true")
print("=== 1. Standart xatolar (kod 2) ===")
for argv in ([], ["a", "--soni", "besh"], ["a", "--soni", "-3"], ["a", "--format", "xml"],
["a", "--nomalum"], ["a", "--jim", "--batafsil"], ["a", "--boshi", "17.09.2026"]):
korinish = " ".join(argv) or "(bo'sh)"
print(f" {korinish:26} → {xato_ushla(p, argv)}")
print("\n=== 2. ⚠️ type=bool ===")
yomon = argparse.ArgumentParser(prog="import", color=False)
yomon.add_argument("--tozala", type=bool, default=False)
print(f" --tozala False → {yomon.parse_args(['--tozala', 'False']).tozala}")
print(f" --tozala 0 → {yomon.parse_args(['--tozala', '0']).tozala}")
yaxshi = argparse.ArgumentParser(prog="import", color=False)
yaxshi.add_argument("--tozala", action=argparse.BooleanOptionalAction, default=False)
print(f" BooleanOptionalAction: --tozala → {yaxshi.parse_args(['--tozala']).tozala}, "
f"--no-tozala → {yaxshi.parse_args(['--no-tozala']).tozala}, berilmasa → {yaxshi.parse_args([]).tozala}")
print("\n=== 3. Argumentlararo tekshiruv ===")
def tahlil(argv: list[str]) -> argparse.Namespace:
args = p.parse_args(argv)
if args.boshi and args.oxiri and args.oxiri < args.boshi:
p.error(f"--oxiri ({args.oxiri}) --boshi ({args.boshi}) dan oldin bo'lishi mumkin emas")
return args
oqim = io.StringIO()
with contextlib.redirect_stderr(oqim):
try:
tahlil(["a", "--boshi", "2026-09-30", "--oxiri", "2026-09-01"])
except SystemExit as chiqish:
print(f" kod {chiqish.code}: {oqim.getvalue().strip().splitlines()[-1]}")
print("\n=== 4. exit_on_error=False ===")
boshqariladigan = argparse.ArgumentParser(prog="api", exit_on_error=False, color=False)
boshqariladigan.add_argument("--port", type=int)
try:
boshqariladigan.parse_args(["--port", "sakkiz"])
except argparse.ArgumentError as xato:
print(f" ArgumentError: {xato} — dastur o'zi hal qiladi (masalan, veb-forma)")
print("\n=== 5. parse_known_args ===")
args, qolgan = p.parse_known_args(["a", "--soni", "5", "--pytest-bayroq", "x"])
print(f" ma'lum: soni={args.soni}, noma'lum: {qolgan}")
print("\n=== 6. 3.14: suggest_on_error ===")
taklifli = argparse.ArgumentParser(prog="eksport", suggest_on_error=True, color=False)
taklifli.add_argument("--format", choices=["json", "csv", "xlsx"])
print(f" {xato_ushla(taklifli, ['--format', 'jsn'])}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Standart xatolar (kod 2) ===
(bo'sh) → kod 2: hisobot: error: the following arguments are required: fayl
a --soni besh → kod 2: hisobot: error: argument --soni: son kerak: 'besh'
a --soni -3 → kod 2: hisobot: error: argument --soni: musbat son kerak: -3
a --format xml → kod 2: hisobot: error: argument --format: invalid choice: 'xml' (choose from 'json', 'csv')
a --nomalum → kod 2: hisobot: error: unrecognized arguments: --nomalum
a --jim --batafsil → kod 2: hisobot: error: argument --batafsil: not allowed with argument --jim
a --boshi 17.09.2026 → kod 2: hisobot: error: argument --boshi: invalid fromisoformat value: '17.09.2026'
=== 2. ⚠️ type=bool ===
--tozala False → True
--tozala 0 → True
BooleanOptionalAction: --tozala → True, --no-tozala → False, berilmasa → False
=== 3. Argumentlararo tekshiruv ===
kod 2: hisobot: error: --oxiri (2026-09-01) --boshi (2026-09-30) dan oldin bo'lishi mumkin emas
=== 4. exit_on_error=False ===
ArgumentError: argument --port: invalid int value: 'sakkiz' — dastur o'zi hal qiladi (masalan, veb-forma)
=== 5. parse_known_args ===
ma'lum: soni=5, noma'lum: ['--pytest-bayroq', 'x']
=== 6. 3.14: suggest_on_error ===
kod 2: eksport: error: argument --format: invalid choice: 'jsn', maybe you meant 'json'? (choose from 'json', 'csv', 'xlsx')Nima ko'rsatdi: 2.2, 2.3, 2.4, 2.6-bo'limlar.
Misol 3 — Ichki buyruqlar
"""add_subparsers required; set_defaults bilan ishlovchi; parents bilan umumiy argumentlar; buyruqlarning o'z yordami; main(argv) -> int."""
import argparse
import contextlib
import io
import sys
def deploy(args: argparse.Namespace) -> int:
print(f" deploy: muhit={args.muhit}, versiya={args.versiya}, quruq={args.quruq}, verbose={args.verbose}")
return 0
def qaytar(args: argparse.Namespace) -> int:
if args.qadam > 3:
print(f" xato: {args.qadam} qadam orqaga — ko'pi bilan 3", file=sys.stderr)
return 1
print(f" rollback: muhit={args.muhit}, qadam={args.qadam}")
return 0
def holat(args: argparse.Namespace) -> int:
print(f" status: muhitlar={args.muhit or ['hammasi']}, format={args.format}")
return 0
def parser_yarat() -> argparse.ArgumentParser:
umumiy = argparse.ArgumentParser(add_help=False)
umumiy.add_argument("-v", "--verbose", action="count", default=0)
umumiy.add_argument("--quruq", action="store_true")
asosiy = argparse.ArgumentParser(prog="relizchi", description="Relizlarni boshqarish", color=False)
asosiy.add_argument("--version", action="version", version="%(prog)s 2.4.0")
buyruqlar = asosiy.add_subparsers(dest="buyruq", required=True, metavar="BUYRUQ")
d = buyruqlar.add_parser("deploy", parents=[umumiy], help="yangi versiyani joylashtirish", color=False)
d.add_argument("muhit", choices=["staging", "prod"])
d.add_argument("versiya")
d.set_defaults(ishlovchi=deploy)
r = buyruqlar.add_parser("rollback", parents=[umumiy], help="oldingi versiyaga qaytish", color=False)
r.add_argument("muhit", choices=["staging", "prod"])
r.add_argument("--qadam", type=int, default=1)
r.set_defaults(ishlovchi=qaytar)
s = buyruqlar.add_parser("status", parents=[umumiy], help="holatni ko'rsatish", color=False)
s.add_argument("muhit", nargs="*")
s.add_argument("--format", choices=["jadval", "json"], default="jadval")
s.set_defaults(ishlovchi=holat)
return asosiy
def main(argv: list[str] | None = None) -> int:
args = parser_yarat().parse_args(argv)
return args.ishlovchi(args)
def ishga_tushir(argv: list[str]) -> None:
xato, chiqish = io.StringIO(), io.StringIO()
with contextlib.redirect_stderr(xato), contextlib.redirect_stdout(chiqish):
try:
kod = main(argv)
except SystemExit as s:
kod = s.code
print(f"$ relizchi {' '.join(argv)}")
for qator in chiqish.getvalue().splitlines()[:12]:
print(qator if qator.startswith(" ") else f" {qator}")
if xato.getvalue():
print(f" [stderr] {xato.getvalue().strip().splitlines()[-1]}")
print(f" → chiqish kodi {kod}\n")
def main_misol() -> None:
for argv in (["deploy", "prod", "2.4.0", "-vv"], ["rollback", "prod", "--qadam", "5"], ["status", "staging", "prod", "--format", "json"],
[], ["deploy", "test", "2.4.0"], ["--version"], ["-h"]):
ishga_tushir(argv)
if __name__ == "__main__":
main_misol()Natijaning muhim qismi:
$ relizchi deploy prod 2.4.0 -vv
deploy: muhit=prod, versiya=2.4.0, quruq=False, verbose=2
→ chiqish kodi 0
$ relizchi rollback prod --qadam 5
[stderr] xato: 5 qadam orqaga — ko'pi bilan 3
→ chiqish kodi 1
$ relizchi status staging prod --format json
status: muhitlar=['staging', 'prod'], format=json
→ chiqish kodi 0
$ relizchi
[stderr] relizchi: error: the following arguments are required: BUYRUQ
→ chiqish kodi 2
$ relizchi deploy test 2.4.0
[stderr] relizchi deploy: error: argument muhit: invalid choice: 'test' (choose from 'staging', 'prod')
→ chiqish kodi 2
$ relizchi --version
relizchi 2.4.0
→ chiqish kodi 0
$ relizchi -h
usage: relizchi [-h] [--version] BUYRUQ ...
Relizlarni boshqarish
positional arguments:
BUYRUQ
deploy yangi versiyani joylashtirish
rollback oldingi versiyaga qaytish
status holatni ko'rsatish
options:
-h, --help show this help message and exit
→ chiqish kodi 0Nima ko'rsatdi: 2.5, 2.6, 2.7-bo'limlar.
Misol 4 — Amaliy: vazifalar menejeri CLI
To'liq CLI dastur: vazifa qosh, vazifa royxat, vazifa bajar, vazifa ochir. Ma'lumotlar JSON faylda (joylashuvi --fayl yoki VAZIFA_FAYL muhit o'zgaruvchisidan), --json bayrog'i mashina uchun chiqish beradi, xatolar stderr ga va aniq chiqish kodlari bilan. Butun ish main(argv, muhit) orqali — shuning uchun misol butun "sessiyani" jarayon ishga tushirmasdan sinab ko'radi.
"""Ichki buyruqlar; muhitdan sukut; JSON saqlash va atomar yozish; ustuvorlik va muddat tekshiruvi; --json chiqish; FoydalanuvchiXatosi va chiqish kodlari; main(argv) sessiyasi."""
import argparse
import contextlib
import io
import json
import os
import sys
import tempfile
from datetime import date
from pathlib import Path
class FoydalanuvchiXatosi(Exception):
pass
def ustuvorlik(qiymat: str) -> int:
nomlar = {"past": 1, "orta": 2, "yuqori": 3}
if qiymat not in nomlar:
raise argparse.ArgumentTypeError(f"ustuvorlik {sorted(nomlar)} dan biri bo'lishi kerak")
return nomlar[qiymat]
def yukla(fayl: Path) -> list[dict]:
return json.loads(fayl.read_text(encoding="utf-8")) if fayl.exists() else []
def saqla(fayl: Path, vazifalar: list[dict]) -> None:
vaqtinchalik = fayl.with_suffix(".tmp")
vaqtinchalik.write_text(json.dumps(vazifalar, ensure_ascii=False, indent=1), encoding="utf-8")
os.replace(vaqtinchalik, fayl)
def topish(vazifalar: list[dict], id_: int) -> dict:
for v in vazifalar:
if v["id"] == id_:
return v
raise FoydalanuvchiXatosi(f"{id_}-vazifa topilmadi")
def qosh(args: argparse.Namespace) -> int:
vazifalar = yukla(args.fayl)
if args.muddat and args.muddat < args.bugun:
raise FoydalanuvchiXatosi(f"muddat o'tmishda: {args.muddat}")
yangi = {"id": max((v["id"] for v in vazifalar), default=0) + 1, "matn": args.matn,
"ustuvorlik": args.ustuvorlik, "muddat": args.muddat.isoformat() if args.muddat else None, "bajarildi": False}
vazifalar.append(yangi)
saqla(args.fayl, vazifalar)
print(json.dumps(yangi, ensure_ascii=False) if args.json else f"qo'shildi: #{yangi['id']} {yangi['matn']}")
return 0
def royxat(args: argparse.Namespace) -> int:
vazifalar = [v for v in yukla(args.fayl) if args.hammasi or not v["bajarildi"]]
vazifalar.sort(key=lambda v: (-v["ustuvorlik"], v["muddat"] or "9999", v["id"]))
if args.json:
print(json.dumps(vazifalar, ensure_ascii=False))
return 0
if not vazifalar:
print("vazifalar yo'q")
belgi = {1: "·", 2: "!", 3: "‼"}
for v in vazifalar:
holat = "✔" if v["bajarildi"] else " "
muddat = f" (muddat: {v['muddat']})" if v["muddat"] else ""
print(f"[{holat}] #{v['id']} {belgi[v['ustuvorlik']]} {v['matn']}{muddat}")
return 0
def bajar(args: argparse.Namespace) -> int:
vazifalar = yukla(args.fayl)
for id_ in args.idlar:
topish(vazifalar, id_)["bajarildi"] = True
saqla(args.fayl, vazifalar)
print(f"bajarildi: {', '.join(f'#{i}' for i in args.idlar)}")
return 0
def ochir(args: argparse.Namespace) -> int:
vazifalar = yukla(args.fayl)
topish(vazifalar, args.id)
if not args.tasdiq:
raise FoydalanuvchiXatosi("o'chirish uchun --tasdiq bayrog'i kerak")
saqla(args.fayl, [v for v in vazifalar if v["id"] != args.id])
print(f"o'chirildi: #{args.id}")
return 0
def parser_yarat(muhit: dict[str, str]) -> argparse.ArgumentParser:
umumiy = argparse.ArgumentParser(add_help=False)
umumiy.add_argument("--fayl", type=Path, default=Path(muhit.get("VAZIFA_FAYL", "vazifalar.json")))
umumiy.add_argument("--json", action="store_true", help="mashina o'qiydigan chiqish")
p = argparse.ArgumentParser(prog="vazifa", color=False)
b = p.add_subparsers(dest="buyruq", required=True)
q = b.add_parser("qosh", parents=[umumiy], color=False)
q.add_argument("matn")
q.add_argument("-u", "--ustuvorlik", type=ustuvorlik, default="orta")
q.add_argument("--muddat", type=date.fromisoformat)
q.set_defaults(ishlovchi=qosh)
r = b.add_parser("royxat", parents=[umumiy], color=False)
r.add_argument("-a", "--hammasi", action="store_true")
r.set_defaults(ishlovchi=royxat)
bj = b.add_parser("bajar", parents=[umumiy], color=False)
bj.add_argument("idlar", type=int, nargs="+")
bj.set_defaults(ishlovchi=bajar)
o = b.add_parser("ochir", parents=[umumiy], color=False)
o.add_argument("id", type=int)
o.add_argument("--tasdiq", action="store_true")
o.set_defaults(ishlovchi=ochir)
return p
def main(argv: list[str] | None = None, muhit: dict[str, str] | None = None, bugun: date | None = None) -> int:
muhit = dict(os.environ) if muhit is None else muhit
args = parser_yarat(muhit).parse_args(argv)
args.bugun = bugun or date.today()
try:
return args.ishlovchi(args)
except FoydalanuvchiXatosi as xato:
print(f"xato: {xato}", file=sys.stderr)
return 1
def main_misol() -> None:
with tempfile.TemporaryDirectory() as katalog:
muhit = {"VAZIFA_FAYL": str(Path(katalog) / "vazifalar.json")}
sessiya = [
["qosh", "15.15-darsni yozish", "-u", "yuqori", "--muddat", "2026-09-18"],
["qosh", "Zaxirani tekshirish", "-u", "past"],
["qosh", "Serverni yangilash", "--muddat", "2026-09-20"],
["qosh", "Eski vazifa", "--muddat", "2026-09-01"],
["qosh", "Noto'g'ri", "-u", "shoshilinch"],
["royxat"],
["bajar", "1", "3"],
["bajar", "9"],
["royxat", "--hammasi"],
["ochir", "2"],
["ochir", "2", "--tasdiq"],
["royxat", "--hammasi", "--json"],
]
for argv in sessiya:
chiqish, xato = io.StringIO(), io.StringIO()
with contextlib.redirect_stdout(chiqish), contextlib.redirect_stderr(xato):
try:
kod = main(argv, muhit, bugun=date(2026, 9, 17))
except SystemExit as s:
kod = s.code
print(f"$ vazifa {' '.join(argv)}")
for qator in chiqish.getvalue().splitlines():
print(f" {qator}")
if xato.getvalue():
print(f" [stderr] {xato.getvalue().strip().splitlines()[-1]}")
print(f" (kod {kod})")
saqlangan = json.loads((Path(katalog) / "vazifalar.json").read_text(encoding="utf-8"))
print(f"\nFaylda {len(saqlangan)} ta vazifa, .tmp qolmadi: {not (Path(katalog) / 'vazifalar.tmp').exists()}")
if __name__ == "__main__":
main_misol()Natijaning muhim qismi:
$ vazifa qosh 15.15-darsni yozish -u yuqori --muddat 2026-09-18
qo'shildi: #1 15.15-darsni yozish
(kod 0)
$ vazifa qosh Zaxirani tekshirish -u past
qo'shildi: #2 Zaxirani tekshirish
(kod 0)
$ vazifa qosh Serverni yangilash --muddat 2026-09-20
qo'shildi: #3 Serverni yangilash
(kod 0)
$ vazifa qosh Eski vazifa --muddat 2026-09-01
[stderr] xato: muddat o'tmishda: 2026-09-01
(kod 1)
$ vazifa qosh Noto'g'ri -u shoshilinch
[stderr] vazifa qosh: error: argument -u/--ustuvorlik: ustuvorlik ['orta', 'past', 'yuqori'] dan biri bo'lishi kerak
(kod 2)
$ vazifa royxat
[ ] #1 ‼ 15.15-darsni yozish (muddat: 2026-09-18)
[ ] #3 ! Serverni yangilash (muddat: 2026-09-20)
[ ] #2 · Zaxirani tekshirish
(kod 0)
$ vazifa bajar 1 3
bajarildi: #1, #3
(kod 0)
$ vazifa bajar 9
[stderr] xato: 9-vazifa topilmadi
(kod 1)
$ vazifa royxat --hammasi
[✔] #1 ‼ 15.15-darsni yozish (muddat: 2026-09-18)
[✔] #3 ! Serverni yangilash (muddat: 2026-09-20)
[ ] #2 · Zaxirani tekshirish
(kod 0)
$ vazifa ochir 2
[stderr] xato: o'chirish uchun --tasdiq bayrog'i kerak
(kod 1)
$ vazifa ochir 2 --tasdiq
o'chirildi: #2
(kod 0)
$ vazifa royxat --hammasi --json
[{"id": 1, "matn": "15.15-darsni yozish", "ustuvorlik": 3, "muddat": "2026-09-18", "bajarildi": true}, {"id": 3, "matn": "Serverni yangilash", "ustuvorlik": 2, "muddat": "2026-09-20", "bajarildi": true}]
(kod 0)
Faylda 2 ta vazifa, .tmp qolmadi: TrueNima ko'rsatdi: 2.1–2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"type=bool mantiqiy bayroq" |
bool("False") — True; store_true yoki BooleanOptionalAction |
"Sukut qiymat type dan o'tmaydi" |
Satr sukut o'tadi |
"argparse xatoda istisno beradi" |
SystemExit(2); exit_on_error=False bilan ArgumentError |
| "Noma'lum argument jim e'tiborsiz qoladi" | Xato — parse_known_args bundan mustasno |
| "Ichki buyruqsiz ishga tushirish xato beradi" | Faqat required=True bilan |
"argparse argumentlararo shartlarni tekshiradi" |
Yo'q — parser.error bilan o'zingiz |
"suggest_on_error bayroq nomidagi xatoni tuzatadi" |
3.14 da choices va buyruqlar uchun taklif beradi |
"parse_args() ni testlash uchun jarayon kerak" |
parse_args(ro'yxat) va main(argv) |
6. Keng tarqalgan xatolar va yechimlari
1. type=bool
p.add_argument("--tozala", type=bool) # ❌
p.add_argument("--tozala", action="store_true") # ✅2. Qo'lda sys.argv
if "--quruq" in sys.argv: ... # ❌ noma'lum argumentlar jim
args = p.parse_args() # ✅3. Testlanmaydigan tuzilma
args = p.parse_args(); ... sys.exit(...) # ❌ modul darajasida
def main(argv=None) -> int: ... # ✅4. if/elif bilan buyruqlar
if args.buyruq == "qosh": ... elif ... # ⚠️
buyruq.set_defaults(ishlovchi=qosh); args.ishlovchi(args) # ✅5. Argumentlararo shart tekshirilmagan
args = p.parse_args() # ⚠️ --oxiri < --boshi
if args.oxiri < args.boshi: p.error("...") # ✅6. prog berilmagan
argparse.ArgumentParser() # ⚠️ usage da skript fayl nomi
argparse.ArgumentParser(prog="vazifa") # ✅7. Foydalanuvchi xatosi va traceback
return args.ishlovchi(args) # ⚠️ KeyError traceback
except FoydalanuvchiXatosi as x: print(x, file=sys.stderr); return 1 # ✅8. append va bo'sh bo'lmagan sukut
p.add_argument("--teg", action="append", default=["umumiy"]) # ⚠️ ["umumiy", "yangi"]7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 15.11-dars (o'tilgan):
sys.argv, chiqish kodlari,stdout/stderr - 15.14-dars (o'tilgan): CLI dasturlarni
subprocessbilan chaqirish - 15.16-dars:
logging—-vdarajasini jurnal darajasiga bog'lash - 17-qism: CLI ni
main(argv)orqali testlash,capsys - 19.6-dars: CLI dasturni paket sifatida chiqarish —
project.scripts,click,typer - 26-qism: avtomatlashtirish vositalari
8. Eng yaxshi amaliyotlar
main(argv: list[str] | None = None) -> intvasys.exit(main()).Parserni alohida funksiyada yarating.
Mantiqiy bayroqlar —
store_trueyokiBooleanOptionalAction.Qiymat tekshiruvi —
type,choices, o'z tur funksiyasi.Argumentlararo shartlar —
parser.error.Buyruqlar —
add_subparsers(required=True)vaset_defaults(ishlovchi=...).Har argumentga
help, sukut uchun%(default)s.Mashina uchun
--jsonchiqish, odam uchun jadval.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
import argparse
p = argparse.ArgumentParser(prog="x")
p.add_argument("fayl")
p.add_argument("-n", type=int, default="3")
p.add_argument("-v", action="count", default=0)
p.add_argument("--teg", action="append", default=[])
p.add_argument("--rang", action=argparse.BooleanOptionalAction, default=True)
1. print(p.parse_args(["a"]).n)
2. print(type(p.parse_args(["a"]).n).__name__)
3. print(p.parse_args(["a", "-vvv"]).v)
4. print(p.parse_args(["a", "--teg", "x", "--teg", "y"]).teg)
5. print(p.parse_args(["a", "--no-rang"]).rang)
6. print(p.parse_args(["-n7", "a"]).n)
7. print(p.parse_args(["--", "-a"]).fayl)
8. print(vars(p.parse_args(["a"]))["fayl"])
9. q = argparse.ArgumentParser(); q.add_argument("--d", type=bool)
print(q.parse_args(["--d", ""]).d)
10. print(p.parse_known_args(["a", "--z"])[1])
11. try:
p.parse_args([])
except SystemExit as e:
print(e.code)
12. print(bool("False"))Javoblar
3int3['x', 'y']False7-aaFalse— faqat bo'sh satrFalse['--z']2(vastderrga xato xabari)True
Vazifa 2: Xatolarni tuzating
1. p.add_argument("--quruq", type=bool, default=False)
2. args = p.parse_args()
if args.buyruq == "qosh": qosh(args)
elif args.buyruq == "ochir": ochir(args)
# buyruq berilmasa hech narsa bo'lmaydi
3. import sys
fayl = sys.argv[1]
soni = int(sys.argv[2]) if len(sys.argv) > 2 else 10
4. p.add_argument("--boshi"); p.add_argument("--oxiri")
args = p.parse_args()
hisobot(args.boshi, args.oxiri) # "2026-09-30" > "2026-10-01" solishtirilmaydi
5. args = argparse.ArgumentParser().parse_args() # modul darajasida
sys.exit(ishla(args))Javoblar
1. p.add_argument("--quruq", action="store_true")
2. b = p.add_subparsers(dest="buyruq", required=True)
b.add_parser("qosh").set_defaults(ishlovchi=qosh)
b.add_parser("ochir").set_defaults(ishlovchi=ochir)
args = p.parse_args()
sys.exit(args.ishlovchi(args))
3. p = argparse.ArgumentParser(prog="dastur")
p.add_argument("fayl", type=Path)
p.add_argument("soni", type=int, nargs="?", default=10)
args = p.parse_args()
4. p.add_argument("--boshi", type=date.fromisoformat, required=True)
p.add_argument("--oxiri", type=date.fromisoformat, required=True)
args = p.parse_args()
if args.oxiri < args.boshi:
p.error("--oxiri --boshi dan oldin bo'lishi mumkin emas")
5. def main(argv=None) -> int:
args = parser_yarat().parse_args(argv)
return ishla(args)
if __name__ == "__main__":
sys.exit(main())Vazifa 3: mini-grep
mini_grep NAQSH [FAYL ...] — argparse va re (15.1–15.3) bilan:
-i(harfga farqsiz),-n(qator raqami),-c(faqat son),-v(teskari)- Fayllar berilmasa —
stdindan o'qish -rbilan kataloglarni rekursiv (pathlib)- Topilsa kod 0, topilmasa 1, xato bo'lsa 2 (asl
grepkabi) main(argv)va 10 ta test
Vazifa 4: Konfiguratsiya ustuvorligi
CLI yozing, sozlama qiymati quyidagi tartibda aniqlansin: CLI argument > muhit o'zgaruvchisi > TOML fayl > sukut:
--port,--rejim,--baza-urlDASTUR_PORTva boshqalar--sozlama config.toml(16.6-darsgacha oddiyKALIT=qiymatfayl bilan)--korsat— har qiymat va uning manbasini chiqarsin- Barcha kombinatsiyalar uchun testlar
Vazifa 5: Ichki buyruqli git-ga o'xshash vosita
eslatma vositasi: init, yoz, royxat, qidir, teg qosh, teg ochir:
- Ichki-ichki buyruqlar (
teg qosh) - Umumiy
--katalogva-vbayroqlari (parents) - Har buyruqda
--helpmisollar bilan (epilog,RawDescriptionHelpFormatter) --jsonchiqish
Vazifa 6: Yordam matnidan hujjat
- Misol 4 dagi parserdan barcha buyruqlar va argumentlarni aylanib chiqing (
_subparsers,_actions— ichki API ekanini hujjatlashtiring) - Markdown hujjat yarating: har buyruq uchun jadval (argument, turi, sukut, tavsif)
- Hujjat va kod mos ekanini testda tekshiring
Vazifa 7: O'ylash
Buyruq qatori interfeyslari (CLI) 50 yildan ortiq mavjud va grafik interfeyslar, veb va mobil ilovalarga qaramay hali ham dasturchilar va tizim administratorlarining asosiy vositasi. Nega? Yaxshi CLI dizaynining qanday tamoyillari bor va ular API dizayniga qanday o'xshaydi?
Javob
Qisqa javob: CLI birlashuvchan (composable), avtomatlashtiriladigan va matnli — bir dasturning chiqishi boshqasining kirishi bo'ladi, har qanday buyruq skriptga, CI ga va cron ga aylanadi. Yaxshi CLI — bu inson va mashina uchun API: barqaror shartnoma (argumentlar, chiqish formati, chiqish kodlari), bashorat qilinadigan xulq va aniq xatolar.
1. Nega CLI yashab qolgan
| Xususiyat | Nima beradi |
|---|---|
| Birlashuvchanlik | `git log |
| Avtomatlashtirish | Har buyruq — skript, CI, cron |
| Takrorlanuvchanlik | Buyruq tarixini saqlash va qayta bajarish |
| Masofadan ishlash | SSH orqali, grafik interfeyssiz |
| Resurs | Sekin tarmoq va zaif serverda ham |
| Aniqlik | "Bu tugmani bosing" emas, aniq buyruq |
2. Unix falsafasi
- Bitta ishni yaxshi qiling
- Chiqish — boshqa dasturning kirishi (ortiqcha bezaksiz)
- Matn — universal interfeys
3. Yaxshi CLI tamoyillari
| Tamoyil | Amaliyot |
|---|---|
| Bashorat qilinadigan nomlar | --help, --version, -v, -q, --dry-run |
| Xavfsiz sukut | Xavfli amal — --force/--tasdiq bilan |
| Chiqish kodlari | 0 / 1 / 2 (15.11) |
stdout — ma'lumot, stderr — diagnostika |
Quvurlar buzilmaydi |
| Mashina formati | --json, --porcelain (git) |
| Aniq xatolar | Nima noto'g'ri va qanday tuzatish — maybe you meant ... |
| Orqaga moslik | Bayroqlarni o'zgartirish — buzuvchi o'zgarish |
| Tezlik | Ishga tushish vaqti sezilmasin |
| Interaktivlik ixtiyoriy | TTY bo'lmasa savol bermaslik |
4. CLI va API o'xshashligi
| API | CLI |
|---|---|
| Endpoint | Buyruq / ichki buyruq |
| Parametrlar va turlar | Argumentlar, type, choices |
| HTTP status | Chiqish kodi |
| JSON javob | --json chiqish |
| Hujjat (OpenAPI) | --help, man sahifa |
| Versiyalash | --version, eskirish ogohlantirishlari |
| Validatsiya xatosi 400/422 | Kod 2, usage |
git --porcelain — mashhur misol: odam o'qiydigan chiqish o'zgarishi mumkin, --porcelain formati esa barqaror shartnoma.
5. Xulosa
- CLI — birlashuvchan va avtomatlashtiriladigan interfeys, shuning uchun eskirmaydi
- Yaxshi CLI — inson va mashina uchun API
- Argumentlar, chiqish formati va kodlari — ommaviy shartnoma, ularni o'zgartirish ehtiyotkorlik talab qiladi
argparsebu tamoyillarning ko'pini standart holda beradi: yordam, xatolar, kod 2
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda buyruq qatori dasturlarini argparse bilan yaratishni o'rgandik.
Eng muhim uch fikr:
Argumentlar va amallar — deklarativ. Pozitsion va ixtiyoriy argumentlar
type,default,choicesbilan e'lon qilinadi;-n 5,-n5,--soni=5bir xil ishlaydi, satr sukut hamtypedan o'tadi. Amallar:store_true,count(-vvv),append,nargs,BooleanOptionalAction(--rang/--no-rang).type=bool— tuzoq:--tozala Falsejadvalni tozalaydi.Xatolar standart va aniq. Noto'g'ri argument —
stderrgausageva xabar, chiqish kodi 2; o'z tur funksiyasiArgumentTypeErrorbilan, argumentlararo shartlar esaparse_argsdan keyinparser.errorbilan tekshiriladi.exit_on_error=Falsechiqish o'rnigaArgumentErrorberadi, 3.14 ningsuggest_on_erroresachoicesdamaybe you meant 'json'?deb taklif qiladi.Tuzilma: ichki buyruqlar va
main(argv) -> int.add_subparsers(required=True), har buyruqqaset_defaults(ishlovchi=...), umumiy bayroqlarparentsbilan.main(argv)butun CLI ni jarayon ishga tushirmasdan testlash imkonini beradi — vazifalar menejeri misolida butun sessiya (qo'shish, xato, bajarish, o'chirish, JSON chiqish) chiqish kodlari bilan tekshirildi.
Keyingi darsda 15-qismni logging bilan yakunlaymiz: jurnal darajalari, handlerlar, formatlash, konfiguratsiya va nega print emas.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!