Buyruq qatori va paketlash
Dastur — bu interfeys
Kutubxonangiz qanchalik yaxshi bo'lmasin, foydalanuvchi uni buyruq qatori orqali ko'radi. Yaxshi CLI ning uch belgisi bor: tushunarli yordam, mantiqiy qaytish kodlari, va bashorat qilinadigan chiqish.
9-bobda argparse ni qisqacha ishlatgandik. Endi uni to'liq qilamiz.
argparse: to'liq interfeys
shu faylni yaratingsrc/hisobot/cli.py
"""Buyruq qatori interfeysi.""" import argparse import logging import sys from pathlib import Path from . import __version__ from .chiqarish import csv_yoz, json_yoz, xulosa from .hisob import daraja_taqsimoti, eng_yaxshi, guruh_ortachasi from .ko_p import hammasi from .oqish import oqi, tozala from .sozlama import Sozlama from .xatolar import HisobotXato, SozlamaXato log = logging.getLogger(__name__) def jurnalni_sozla(batafsil: int, jim: bool) -> None: if jim: daraja = logging.ERROR else: daraja = {0: logging.WARNING, 1: logging.INFO}.get(batafsil, logging.DEBUG) logging.basicConfig(level=daraja, stream=sys.stderr, format="%(levelname)-8s %(name)s: %(message)s") def tahlilchi() -> argparse.ArgumentParser: p = argparse.ArgumentParser( prog="hisobot", description="CSV fayldan talabalar hisobotini tayyorlaydi.", epilog="Misol: hisobot talabalar.csv --format json --chiqish natija.json", ) p.add_argument("fayllar", nargs="+", type=Path, metavar="FAYL", help="bir yoki bir nechta CSV fayl") p.add_argument("--format", choices=["matn", "json", "csv"], default="matn", help="chiqish formati (sukut: matn)") p.add_argument("--chiqish", type=Path, metavar="YO'L", help="natijani faylga yozish (sukut: ekranga)") p.add_argument("--max-baho", type=int, metavar="N", help="eng katta ruxsat etilgan baho (sukut: 100)") p.add_argument("--qatiy", action="store_true", help="bitta xato qator ham butun ishni to'xtatsin") p.add_argument("-j", "--ishchilar", type=int, default=4, metavar="N", help="parallel ishchilar soni (sukut: 4)") p.add_argument("-v", "--batafsil", action="count", default=0, help="-v: INFO, -vv: DEBUG") p.add_argument("-q", "--jim", action="store_true", help="faqat xatolarni ko'rsat") p.add_argument("--version", action="version", version=f"hisobot {__version__}") return p def main(argv: list[str] | None = None) -> int: args = tahlilchi().parse_args(argv) jurnalni_sozla(args.batafsil, args.jim) try: sozlama = Sozlama.muhitdan() if args.max_baho is not None: sozlama = Sozlama(max_baho=args.max_baho, min_baho=sozlama.min_baho, chiqish_papka=sozlama.chiqish_papka, qatiy=args.qatiy or sozlama.qatiy) sozlama.tekshir() except SozlamaXato as e: log.error("sozlama xatosi: %s", e) return 2 natijalar = hammasi(list(args.fayllar), ishchilar=args.ishchilar) xatolar_bor = False birlashgan = None for nom in sorted(natijalar): n = natijalar[nom] if isinstance(n, str): log.error("%s: %s", nom, n) xatolar_bor = True continue if birlashgan is None: birlashgan = n else: birlashgan.talabalar.extend(n.talabalar) birlashgan.xatolar.extend(n.xatolar) if birlashgan is None: log.error("hech qaysi fayl o'qilmadi") return 1 if birlashgan.xatolar: xatolar_bor = True if sozlama.qatiy: log.error("qat'iy rejim: %d xato qator topildi", len(birlashgan.xatolar)) return 1 if args.format == "json": if args.chiqish: json_yoz(birlashgan, args.chiqish) else: import json print(json.dumps(xulosa(birlashgan), ensure_ascii=False, indent=2)) elif args.format == "csv": if not args.chiqish: log.error("--format csv uchun --chiqish kerak") return 2 csv_yoz(birlashgan, args.chiqish) else: chop_et(birlashgan) return 1 if xatolar_bor else 0 def chop_et(natija) -> None: print(f"jami: {natija.jami} to'g'ri: {len(natija.talabalar)} xato: {len(natija.xatolar)}") print("\nguruh bo'yicha o'rtacha:") for g, o in guruh_ortachasi(natija.talabalar).items(): print(f" {g}: {o}") print("\ndarajalar:") for d, n in daraja_taqsimoti(natija.talabalar).items(): print(f" {d}: {n}") eng = eng_yaxshi(natija.talabalar) if eng is not None: print(f"\neng yuqori: {eng.ism} ({eng.baho}, {eng.guruh})") for qator, ism, sabab in natija.xatolar: print(f" {qator}-qator ({ism}): {sabab}", file=sys.stderr) if __name__ == "__main__": sys.exit(main())
shu faylni yaratingsrc/hisobot/__main__.py
"""python3 -m hisobot bilan ishga tushadi.""" import sys from .cli import main if __name__ == "__main__": sys.exit(main())
.venv/bin/python3 -m hisobot --help
usage: hisobot [-h] [--format {matn,json,csv}] [--chiqish YO'L] [--max-baho N]
[--qatiy] [-j N] [-v] [-q] [--version]
FAYL [FAYL ...]
CSV fayldan talabalar hisobotini tayyorlaydi.
positional arguments:
FAYL bir yoki bir nechta CSV fayl
options:
-h, --help show this help message and exit
--format {matn,json,csv}
chiqish formati (sukut: matn)
--chiqish YO'L natijani faylga yozish (sukut: ekranga)
--max-baho N eng katta ruxsat etilgan baho (sukut: 100)
--qatiy bitta xato qator ham butun ishni to'xtatsin
-j N, --ishchilar N parallel ishchilar soni (sukut: 4)
-v, --batafsil -v: INFO, -vv: DEBUG
-q, --jim faqat xatolarni ko'rsat
--version show program's version number and exit
Misol: hisobot talabalar.csv --format json --chiqish natija.json
Yordam matni avtomatik yasaldi. metavar, help va epilog — hammasi shu yerda ko'rinadi.
Sinab ko'ramiz
echo "=== 1) oddiy ishlatish ===" .venv/bin/python3 -m hisobot sinov_malumot.csv echo "qaytish kodi: $?"
=== 1) oddiy ishlatish === jami: 8 to'g'ri: 6 xato: 2 guruh bo'yicha o'rtacha: A: 90.67 B: 83.0 C: 61.0 darajalar: yaxshi: 3 a'lo: 2 qoniqarli: 1 eng yuqori: Feruza (95, A) qaytish kodi: 1 WARNING hisobot.oqish: 8 qatordan 2 tasi rad etildi 5-qator (Elyor): baho son emas: '' 8-qator (Hasan): baho son emas: 'abc'
echo "=== 2) JSON format ===" .venv/bin/python3 -m hisobot sinov_malumot.csv --format json --jim
=== 2) JSON format ===
{
"jami": 8,
"togri": 6,
"xato": 2,
"guruh_ortachasi": {
"A": 90.67,
"B": 83.0,
"C": 61.0
},
"darajalar": {
"yaxshi": 3,
"a'lo": 2,
"qoniqarli": 1
},
"xatolar": [
{
"qator": 5,
"ism": "Elyor",
"sabab": "baho son emas: ''"
},
{
"qator": 8,
"ism": "Hasan",
"sabab": "baho son emas: 'abc'"
}
]
}
echo "=== 3) bir nechta fayl ===" .venv/bin/python3 -m hisobot ko_p_sinov/f1.csv ko_p_sinov/f2.csv ko_p_sinov/f3.csv --jim echo "qaytish kodi: $?" echo "" echo "=== 4) --max-baho bilan (5 balli tizim) ===" .venv/bin/python3 -m hisobot sinov_malumot.csv --max-baho 5 --jim 2>&1 | head -12 echo "qaytish kodi: ${PIPESTATUS[0]}"
=== 3) bir nechta fayl === jami: 6 to'g'ri: 6 xato: 0 guruh bo'yicha o'rtacha: A: 82.0 B: 92.0 darajalar: yaxshi: 3 a'lo: 3 eng yuqori: X3 (93, B) qaytish kodi: 0 === 4) --max-baho bilan (5 balli tizim) === 5-qator (Elyor): baho son emas: '' 8-qator (Hasan): baho son emas: 'abc' jami: 8 to'g'ri: 6 xato: 2 guruh bo'yicha o'rtacha: A: 90.67 B: 83.0 C: 61.0 darajalar: yaxshi: 3 a'lo: 2 qaytish kodi: 1
echo "=== 5) noto'g'ri argument ===" .venv/bin/python3 -m hisobot sinov_malumot.csv --format xml echo "qaytish kodi: $?" echo "" echo "=== 6) versiya ===" .venv/bin/python3 -m hisobot --version
=== 5) noto'g'ri argument ===
qaytish kodi: 2
=== 6) versiya ===
hisobot 0.4.0
usage: hisobot [-h] [--format {matn,json,csv}] [--chiqish YO'L] [--max-baho N]
[--qatiy] [-j N] [-v] [-q] [--version]
FAYL [FAYL ...]
hisobot: error: argument --format: invalid choice: 'xml' (choose from 'matn', 'json', 'csv')
argparse noto'g'ri --format ni o'zi rad etdi va qaytish kodi 2 berdi — bu CLI ning
standart konvensiyasi.
Qaytish kodlari
import pandas as pd show(pd.DataFrame([ {"kod": 0, "ma'nosi": "Muvaffaqiyat", "bizda": "hamma qator to'g'ri"}, {"kod": 1, "ma'nosi": "Umumiy xato", "bizda": "xato qatorlar bor yoki fayl o'qilmadi"}, {"kod": 2, "ma'nosi": "Noto'g'ri ishlatish", "bizda": "yomon argument yoki sozlama"}, {"kod": 130, "ma'nosi": "Ctrl+C bilan to'xtatilgan", "bizda": "Python o'zi qaytaradi"}, ]))
| kod | ma'nosi | bizda |
|---|---|---|
| 0 | Muvaffaqiyat | hamma qator to'g'ri |
| 1 | Umumiy xato | xato qatorlar bor yoki fayl o'qilmadi |
| 2 | Noto'g'ri ishlatish | yomon argument yoki sozlama |
| 130 | Ctrl+C bilan to'xtatilgan | Python o'zi qaytaradi |
Nega bu muhim: skriptlar qaytish kodiga qarab qaror qiladi.
if .venv/bin/python3 -m hisobot ko_p_sinov/f1.csv --jim > /dev/null 2>&1; then echo "f1.csv: toza o'tdi -> keyingi qadamga o'tish mumkin" else echo "f1.csv: muammo bor -> to'xtaymiz" fi if .venv/bin/python3 -m hisobot sinov_malumot.csv --jim > /dev/null 2>&1; then echo "sinov_malumot.csv: toza o'tdi" else echo "sinov_malumot.csv: muammo bor (xato qatorlar) -> tekshirish kerak" fi
f1.csv: toza o'tdi -> keyingi qadamga o'tish mumkin sinov_malumot.csv: muammo bor (xato qatorlar) -> tekshirish kerak
stdout va stderr ni ajratish
echo "=== natija faylga, xatolar ekranga ===" .venv/bin/python3 -m hisobot sinov_malumot.csv --format json > natija2.json echo "--- fayl ichida faqat JSON bor: ---" head -5 natija2.json .venv/bin/python3 -c "import json; json.load(open('natija2.json')); print('JSON to\'g\'ri tahlil qilindi')" rm natija2.json
=== natija faylga, xatolar ekranga ===
--- fayl ichida faqat JSON bor: ---
{
"jami": 8,
"togri": 6,
"xato": 2,
"guruh_ortachasi": {
JSON to'g'ri tahlil qilindi
WARNING hisobot.oqish: 8 qatordan 2 tasi rad etildi
Natija faylga toza JSON bo'lib tushdi, ogohlantirishlar esa ekranga chiqdi. Agar jurnal stdout ga
yozilganda, JSON buzilardi.
Paketni o'rnatiladigan qilamiz
Hozir python3 -m hisobot deb chaqiramiz. Foydalanuvchi shunchaki hisobot deb yozishi kerak.
shu faylni yaratingpyproject.toml
[project]
name = "hisobot"
version = "1.0.0"
description = "CSV ma'lumotini tekshirib hisobot chiqaradigan asbob"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [{name = "Kurs talabasi"}]
keywords = ["csv", "hisobot", "tahlil"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = []
[project.optional-dependencies]
dev = [
"pytest>=8",
"pytest-cov>=5",
"mypy>=1.8",
"ruff>=0.5",
]
[project.scripts]
hisobot = "hisobot.cli:main"
[project.urls]
Homepage = "https://example.uz/hisobot"
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q --strict-markers"
[tool.coverage.run]
source = ["hisobot"]
[tool.coverage.report]
show_missing = true
exclude_lines = [
"pragma: no cover",
"if __name__ == .__main__.:",
"raise NotImplementedError",
]
shu faylni yaratingREADME.md
# hisobot CSV fayldan talabalar hisobotini tayyorlaydigan buyruq qatori asbobi. ## O'rnatish
pip install -e ".[dev]"
## Ishlatish
hisobot talabalar.csv hisobot talabalar.csv --format json --chiqish natija.json hisobot *.csv -j 8 -v
## Sozlash Muhit o'zgaruvchilari: `HISOBOT_MAX_BAHO`, `HISOBOT_MIN_BAHO`, `HISOBOT_QATIY`.
.venv/bin/python3 -m pip install --quiet -e . ls .venv/bin/ | grep hisobot .venv/bin/hisobot --version .venv/bin/hisobot sinov_malumot.csv --jim
hisobot hisobot 0.4.0 jami: 8 to'g'ri: 6 xato: 2 guruh bo'yicha o'rtacha: A: 90.67 B: 83.0 C: 61.0 darajalar: yaxshi: 3 a'lo: 2 qoniqarli: 1 eng yuqori: Feruza (95, A) 5-qator (Elyor): baho son emas: '' 8-qator (Hasan): baho son emas: 'abc'
[project.scripts] bo'limi hisobot nomli bajariladigan fayl yasadi. Endi python3 -m shart emas.
main() ni sinash
CLI ni sinashning to'g'ri usuli — main() ni to'g'ridan-to'g'ri chaqirish, jarayon
ishga tushirmasdan.
shu faylni yaratingtests/test_cli.py
import json import pytest from hisobot.cli import main TOGRI = "ism,guruh,baho,sana\nAziz,A,85,2026-09-01\nBobur,B,95,2026-09-02\n" XATOLI = "ism,guruh,baho,sana\nAziz,A,abc,2026-09-01\n" def _fayl(papka, nom, matn): y = papka / nom y.write_text(matn, encoding="utf-8") return str(y) def test_toza_fayl_nol_qaytaradi(tmp_path, capsys): kod = main([_fayl(tmp_path, "a.csv", TOGRI), "--jim"]) assert kod == 0 assert "to'g'ri: 2" in capsys.readouterr().out def test_xatoli_fayl_bir_qaytaradi(tmp_path): assert main([_fayl(tmp_path, "b.csv", XATOLI), "--jim"]) == 1 def test_fayl_yoq_bir_qaytaradi(tmp_path): assert main([str(tmp_path / "yoq.csv"), "--jim"]) == 1 def test_json_format(tmp_path, capsys): main([_fayl(tmp_path, "c.csv", TOGRI), "--format", "json", "--jim"]) malumot = json.loads(capsys.readouterr().out) assert malumot["togri"] == 2 assert malumot["guruh_ortachasi"] == {"A": 85.0, "B": 95.0} def test_json_faylga(tmp_path): chiqish = tmp_path / "n.json" main([_fayl(tmp_path, "d.csv", TOGRI), "--format", "json", "--chiqish", str(chiqish), "--jim"]) assert json.loads(chiqish.read_text(encoding="utf-8"))["togri"] == 2 def test_csv_chiqishsiz_ikki_qaytaradi(tmp_path): assert main([_fayl(tmp_path, "e.csv", TOGRI), "--format", "csv", "--jim"]) == 2 def test_max_baho_cheklovi(tmp_path): """85 va 95 — 5 balli tizimda ikkalasi ham xato.""" assert main([_fayl(tmp_path, "f.csv", TOGRI), "--max-baho", "5", "--jim"]) == 1 def test_notogri_max_baho(tmp_path): assert main([_fayl(tmp_path, "g.csv", TOGRI), "--max-baho", "-5", "--jim"]) == 2 def test_bir_nechta_fayl(tmp_path, capsys): kod = main([_fayl(tmp_path, "h1.csv", TOGRI), _fayl(tmp_path, "h2.csv", TOGRI), "--jim"]) assert kod == 0 assert "jami: 4" in capsys.readouterr().out def test_notogri_argument_sistemexit(tmp_path): with pytest.raises(SystemExit) as e: main([_fayl(tmp_path, "i.csv", TOGRI), "--format", "xml"]) assert e.value.code == 2
.venv/bin/python3 -m pytest tests/test_cli.py -q 2>&1 | tail -12
guruh bo'yicha o'rtacha: A: 85.0 B: 95.0 darajalar: yaxshi: 1 a'lo: 1 eng yuqori: Bobur (95, B) =========================== short test summary info ============================ FAILED tests/test_cli.py::test_max_baho_cheklovi - AssertionError: assert 0 == 1
Test o'lik bayroqni topdi
test_max_baho_cheklovi yiqildi: --max-baho 5 berilgan, lekin qaytish kodi 1 emas, 0 chiqdi.
Yuqoridagi 4-misolga qayting — u yerda ham --max-baho 5 bilan natija o'zgarmagan edi, 85 baho
baribir qabul qilindi.
Sabab kodda ko'rinib turibdi: main() sozlama obyektini yasaydi, keyin uni hech kimga
bermaydi. hammasi() -> tozala() -> bahoni_tekshir() zanjiri modul darajasidagi ODATIY
sozlamani ishlatadi.
Ya'ni bayroq bor, yordam matnida ko'rinadi, lekin hech narsa qilmaydi. Bu eng yomon turdagi xato: foydalanuvchi uni ishlatadi va natijaga ishonadi.
11-bobdagi qoida aynan shu holat uchun edi: sozlamani argument sifatida uzating. Endi zanjirni to'liq ulaymiz — uch faylga tegamiz.
shu faylni yaratingsrc/hisobot/oqish.py
"""CSV o'qish va qatorlarni tekshirib modelga aylantirish.""" import csv import logging from pathlib import Path from .modellar import Natija, Talaba from .sozlama import Sozlama from .tekshiruv import bahoni_tekshir, sanani_tekshir from .xatolar import FaylXato log = logging.getLogger(__name__) KERAKLI_USTUNLAR = {"ism", "guruh", "baho", "sana"} def oqi(yol: str | Path) -> list[dict[str, str]]: """CSV ni o'qiydi. Har qanday muammoda FaylXato qaytaradi.""" p = Path(yol) log.debug("fayl o'qilmoqda: %s", p) try: matn = p.read_text(encoding="utf-8") except FileNotFoundError: raise FaylXato(str(p), "fayl topilmadi") from None except PermissionError: raise FaylXato(str(p), "o'qishga ruxsat yo'q") from None except UnicodeDecodeError as e: raise FaylXato(str(p), f"matn UTF-8 emas ({e.reason})") from None qatorlar = list(csv.DictReader(matn.splitlines())) if not qatorlar: raise FaylXato(str(p), "fayl bo'sh yoki faqat sarlavha bor") yetishmayotgan = KERAKLI_USTUNLAR - set(qatorlar[0]) if yetishmayotgan: raise FaylXato(str(p), f"ustunlar yetishmaydi: {sorted(yetishmayotgan)}") log.info("o'qildi: %d qator, fayl %s", len(qatorlar), p.name) return qatorlar def tozala(qatorlar: list[dict[str, str]], sozlama: Sozlama | None = None) -> Natija: """Xom lug'atlarni Talaba obyektlariga aylantiradi. Xatolar to'planadi, otilmaydi.""" natija = Natija() for i, q in enumerate(qatorlar, start=2): try: baho = bahoni_tekshir(q.get("baho"), sozlama) sana = sanani_tekshir(q.get("sana")) except ValueError as e: log.debug("%d-qator rad etildi: %s", i, e) natija.xatolar.append((i, q.get("ism", "?"), str(e))) continue natija.talabalar.append( Talaba( ism=q.get("ism", "").strip(), guruh=q.get("guruh", "").strip(), baho=baho, sana=sana, ) ) if natija.xatolar: log.warning("%d qatordan %d tasi rad etildi", natija.jami, len(natija.xatolar)) return natija
shu faylni yaratingsrc/hisobot/ko_p.py
"""Bir nechta CSV faylni parallel qayta ishlash.""" import logging from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from .modellar import Natija from .oqish import oqi, tozala from .sozlama import Sozlama from .xatolar import HisobotXato log = logging.getLogger(__name__) def bitta(yol: Path, sozlama: Sozlama | None = None) -> tuple[Path, Natija | None, str | None]: """Bitta faylni qayta ishlaydi. Xato otmaydi — uni qaytaradi.""" try: return yol, tozala(oqi(yol), sozlama), None except HisobotXato as e: return yol, None, str(e) def hammasi( yollar: list[Path], ishchilar: int = 4, sozlama: Sozlama | None = None ) -> dict[str, Natija | str]: """Fayllarni parallel qayta ishlaydi. Bitta faylning xatosi boshqalarni to'xtatmaydi.""" natijalar: dict[str, Natija | str] = {} with ThreadPoolExecutor(max_workers=ishchilar) as p: kelajaklar = {p.submit(bitta, y, sozlama): y for y in yollar} for k in as_completed(kelajaklar): yol, natija, xato = k.result() natijalar[yol.name] = natija if natija is not None else f"XATO: {xato}" log.debug("tugadi: %s", yol.name) return natijalar
cli.py da esa bitta chaqiruvga sozlama=sozlama qo'shiladi:
natijalar = hammasi(list(args.fayllar), ishchilar=args.ishchilar, sozlama=sozlama)
sed -i 's/hammasi(list(args.fayllar), ishchilar=args.ishchilar)/hammasi(list(args.fayllar), ishchilar=args.ishchilar, sozlama=sozlama)/' src/hisobot/cli.py grep -n "hammasi(list" src/hisobot/cli.py
66: natijalar = hammasi(list(args.fayllar), ishchilar=args.ishchilar, sozlama=sozlama)
.venv/bin/python3 -m pytest tests/test_cli.py -q 2>&1 | tail -4 echo "" echo "=== endi --max-baho haqiqatan ishlaydi ===" .venv/bin/python3 -m hisobot sinov_malumot.csv --max-baho 5 --jim 2>&1 | head -8
.......... [100%] === endi --max-baho haqiqatan ishlaydi === 2-qator (Aziz): baho 0..5 oralig'ida bo'lishi kerak, berilgan: 85 3-qator (Bobur): baho 0..5 oralig'ida bo'lishi kerak, berilgan: 92 4-qator (Dilnoza): baho 0..5 oralig'ida bo'lishi kerak, berilgan: 78 5-qator (Elyor): baho son emas: '' 6-qator (Feruza): baho 0..5 oralig'ida bo'lishi kerak, berilgan: 95 7-qator (Gulnora): baho 0..5 oralig'ida bo'lishi kerak, berilgan: 61 8-qator (Hasan): baho son emas: 'abc' 9-qator (Iroda): baho 0..5 oralig'ida bo'lishi kerak, berilgan: 88
Endi --max-baho 5 haqiqiy ta'sir qiladi: 85 va 95 baholar ham rad etiladi.
:::tip Bayroq qo'shganda uning ta'sirini sinang "Bayroq bor, lekin ishlamaydi" — bu xato turini faqat test topadi. Qo'lda sinasangiz, chiqishga yuzaki qarab "ishlayotganga o'xshaydi" deb o'tib ketish juda oson. Har yangi parametr uchun uning natijaga ta'sirini tekshiradigan test yozing, shunchaki mavjudligini emas. :::
capsys — pytest fixture i, u stdout/stderr ni ushlaydi. main() ni chaqirish jarayon
ishga tushirishdan yuz barobar tezroq va xato izini to'g'ridan-to'g'ri ko'rsatadi.
:::tip main(argv) naqshi
main() ni argv argumenti bilan yozing va sys.argv ni faqat None bo'lganda o'qing. Shunda
test uni istalgan argumentlar bilan chaqira oladi. sys.exit() ni esa faqat if __name__ ==
"__main__" blokida chaqiring — main() ning o'zi son qaytarsin.
:::
Paketni yig'ish
.venv/bin/python3 -m pip install --quiet build .venv/bin/python3 -m build --wheel --outdir dist_sinov 2>&1 | tail -5 ls -la dist_sinov/
See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
********************************************************************************
!!
Successfully built hisobot-1.0.0-py3-none-any.whl
total 12
drwxrwxrwx 1 abulqosim abulqosim 512 Sep 29 14:01 .
drwxrwxrwx 1 abulqosim abulqosim 512 Sep 29 14:01 ..
-rwxrwxrwx 1 abulqosim abulqosim 11360 Sep 29 14:01 hisobot-1.0.0-py3-none-any.whl
.venv/bin/python3 -c " import zipfile, glob w = glob.glob('dist_sinov/*.whl')[0] with zipfile.ZipFile(w) as z: nomlar = sorted(z.namelist()) print('wheel ichidagi fayllar:') for n in nomlar: print(' ', n) " rm -rf dist_sinov
wheel ichidagi fayllar: hisobot-1.0.0.dist-info/METADATA hisobot-1.0.0.dist-info/RECORD hisobot-1.0.0.dist-info/WHEEL hisobot-1.0.0.dist-info/entry_points.txt hisobot-1.0.0.dist-info/top_level.txt hisobot/__init__.py hisobot/__main__.py hisobot/chiqarish.py hisobot/cli.py hisobot/hisob.py hisobot/ko_p.py hisobot/modellar.py hisobot/muhit.py hisobot/oqish.py hisobot/sozlama.py hisobot/tarmoq.py hisobot/tekshiruv.py hisobot/xatolar.py
E'tibor bering: wheel ichida testlar yo'q — src/ tartibi tufayli faqat paket kirdi. Bu
to'g'ri: foydalanuvchiga testlar kerak emas.
Xulosa
| Tushuncha | Asosiy fikr |
|---|---|
argparse |
Yordam matni avtomatik; choices noto'g'ri qiymatni o'zi rad etadi |
| Qaytish kodi | 0 muvaffaqiyat, 1 xato, 2 noto'g'ri ishlatish |
stdout / stderr |
Natija stdout ga, jurnal stderr ga — quvurda ishlashi uchun |
main(argv) |
sys.argv ni faqat None da o'qing; sys.exit ni tashqarida chaqiring |
[project.scripts] |
hisobot buyrug'ini yasaydi |
capsys |
Testda chiqishni ushlaydi |
| Wheel | src/ tartibi tufayli testlar paketga tushmaydi |
Talaba ishi
--chiqishberilgan, lekin papka mavjud emas bo'lsa nima bo'ladi? Tushunarli xato bilan qaytish kodi 2 bering.--formatgamarkdownvariantini qo'shing. Nechta joyga tegish kerak bo'ldi?hisobotni--qatiybilan ishga tushiring. Qaytish kodi va chiqish qanday farq qiladi?argparseo'rnigatyperbilan qayta yozing. Kod qisqardimi? Yordam matni qanday ko'rinadi?
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.