Asosiy qismga o'tish

Kod sifati: ruff, formatlash va CI

16-bob 22 kod bloki 2991 so'z

Muhokamani avtomatlashtirish

Kod ko'rigida ikki xil izoh bo'ladi:

Tur Misol Kim aytishi kerak
Mexanik "bu yerda bo'sh joy ortiqcha", "import ishlatilmayapti" Asbob
Mazmuniy "bu funksiya ikki ish qilyapti", "bu chegara xato" Odam

Agar odam mexanik narsalarni aytsa, u mazmunga vaqt ajratmaydi. Shuning uchun birinchisini butunlay avtomatlashtirish kerak.

ruff: linter va formatlovchi

ruff — Rust da yozilgan, shuning uchun juda tez. U flake8, isort, pyupgrade va boshqa o'nlab asbobning o'rnini bosadi.

shu faylni yaratingruff.toml

python20 qator
line-length = 100
target-version = "py310"

[lint]
select = [
    "E", "W",    # pycodestyle: bo'sh joy, uzunlik
    "F",         # pyflakes: ishlatilmagan import/o'zgaruvchi, noto'g'ri nom
    "I",         # isort: import tartibi
    "B",         # bugbear: keng tarqalgan xatolar
    "UP",        # pyupgrade: eskirgan yozuvlar
    "SIM",       # soddalashtirish mumkin bo'lgan joylar
    "RUF",       # ruff ning o'z qoidalari
]
ignore = ["E501"]     # uzunlikni formatlovchi o'zi hal qiladi

[lint.per-file-ignores]
"tests/*" = ["S101"]  # testda assert — normal

[format]
quote-style = "double"

Ataylab muammoli fayl yozamiz:

shu faylni yaratingyomon_uslub.py

python22 qator
import os
import sys
import json
from typing import List, Optional


def hisobla( sonlar:List[int], koef:Optional[int]=None ) ->int:
    natija=0
    for i in range( len(sonlar) ):
        natija = natija + sonlar[i]
    if koef != None:
        natija = natija*koef
    unused = "hech qayerda ishlatilmaydi"
    if natija > 0:
        return natija
    else:
        return 0


def toplam(elementlar=[]):
    elementlar.append(1)
    return elementlar
matn1 qator
ruff check yomon_uslub.py
Chiqish (qaytish kodi 1, 36 ms)
I001 [*] Import block is un-sorted or un-formatted
 --> yomon_uslub.py:1:1
  |
1 | / import os
2 | | import sys
3 | | import json
4 | | from typing import List, Optional
  | |_________________________________^
  |
help: Organize imports

F401 [*] `os` imported but unused
 --> yomon_uslub.py:1:8
  |
1 | import os
  |        ^^
2 | import sys
3 | import json
  |
help: Remove unused import: `os`

F401 [*] `sys` imported but unused
 --> yomon_uslub.py:2:8
  |
1 | import os
2 | import sys
  |        ^^^
3 | import json
4 | from typing import List, Optional
  |
help: Remove unused import: `sys`

F401 [*] `json` imported but unused
 --> yomon_uslub.py:3:8
  |
1 | import os
2 | import sys
3 | import json
  |        ^^^^
4 | from typing import List, Optional
  |
help: Remove unused import: `json`

UP035 `typing.List` is deprecated, use `list` instead
 --> yomon_uslub.py:4:1
  |
2 | import sys
3 | import json
4 | from typing import List, Optional
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |

UP006 [*] Use `list` instead of `List` for type annotation
 --> yomon_uslub.py:7:21
  |
7 | def hisobla( sonlar:List[int], koef:Optional[int]=None ) ->int:
  |                     ^^^^
8 |     natija=0
9 |     for i in range( len(sonlar) ):
  |
help: Replace with `list`

UP045 [*] Use `X | None` for type annotations
 --> yomon_uslub.py:7:37
  |
7 | def hisobla( sonlar:List[int], koef:Optional[int]=None ) ->int:
  |                                     ^^^^^^^^^^^^^
8 |     natija=0
9 |     for i in range( len(sonlar) ):
  |
help: Convert to `X | None`

E711 Comparison to `None` should be `cond is not None`
  --> yomon_uslub.py:11:16
   |
 9 |     for i in range( len(sonlar) ):
10 |         natija = natija + sonlar[i]
11 |     if koef != None:
   |                ^^^^
12 |         natija = natija*koef
13 |     unused = "hech qayerda ishlatilmaydi"
   |
help: Replace with `cond is not None`

F841 Local variable `unused` is assigned to but never used
  --> yomon_uslub.py:13:5
   |
11 |     if koef != None:
12 |         natija = natija*koef
13 |     unused = "hech qayerda ishlatilmaydi"
   |     ^^^^^^
14 |     if natija > 0:
15 |         return natija
   |
help: Remove assignment to unused variable `unused`

B006 Do not use mutable data structures for argument defaults
  --> yomon_uslub.py:20:23
   |
20 | def toplam(elementlar=[]):
   |                       ^^
21 |     elementlar.append(1)
22 |     return elementlar
   |
help: Replace with `None`; initialize within function

Found 10 errors.
[*] 6 fixable with the `--fix` option (3 hidden fixes can be enabled with the `--unsafe-fixes` option).

Har xato kodi bilan ko'rsatildi (F401, E225, ...), shuning uchun kerak bo'lsa qoidani o'chirish oson.

Ba'zilarini ruff o'zi tuzatadi:

matn3 qator
ruff check --fix yomon_uslub.py
echo "--- tuzatishdan keyin ---"
cat yomon_uslub.py
Chiqish (qaytish kodi 0, 35 ms)
E711 Comparison to `None` should be `cond is not None`
 --> yomon_uslub.py:7:16
  |
5 |     for i in range( len(sonlar) ):
6 |         natija = natija + sonlar[i]
7 |     if koef != None:
  |                ^^^^
8 |         natija = natija*koef
9 |     unused = "hech qayerda ishlatilmaydi"
  |
help: Replace with `cond is not None`

F841 Local variable `unused` is assigned to but never used
  --> yomon_uslub.py:9:5
   |
 7 |     if koef != None:
 8 |         natija = natija*koef
 9 |     unused = "hech qayerda ishlatilmaydi"
   |     ^^^^^^
10 |     if natija > 0:
11 |         return natija
   |
help: Remove assignment to unused variable `unused`

B006 Do not use mutable data structures for argument defaults
  --> yomon_uslub.py:16:23
   |
16 | def toplam(elementlar=[]):
   |                       ^^
17 |     elementlar.append(1)
18 |     return elementlar
   |
help: Replace with `None`; initialize within function

Found 10 errors (7 fixed, 3 remaining).
No fixes available (3 hidden fixes can be enabled with the `--unsafe-fixes` option).
--- tuzatishdan keyin ---


def hisobla( sonlar:list[int], koef:int | None=None ) ->int:
    natija=0
    for i in range( len(sonlar) ):
        natija = natija + sonlar[i]
    if koef != None:
        natija = natija*koef
    unused = "hech qayerda ishlatilmaydi"
    if natija > 0:
        return natija
    else:
        return 0


def toplam(elementlar=[]):
    elementlar.append(1)
    return elementlar
matn2 qator
echo "--- qolgan muammolar ---"
ruff check yomon_uslub.py
Chiqish (qaytish kodi 1, 22 ms)
--- qolgan muammolar ---
E711 Comparison to `None` should be `cond is not None`
 --> yomon_uslub.py:7:16
  |
5 |     for i in range( len(sonlar) ):
6 |         natija = natija + sonlar[i]
7 |     if koef != None:
  |                ^^^^
8 |         natija = natija*koef
9 |     unused = "hech qayerda ishlatilmaydi"
  |
help: Replace with `cond is not None`

F841 Local variable `unused` is assigned to but never used
  --> yomon_uslub.py:9:5
   |
 7 |     if koef != None:
 8 |         natija = natija*koef
 9 |     unused = "hech qayerda ishlatilmaydi"
   |     ^^^^^^
10 |     if natija > 0:
11 |         return natija
   |
help: Remove assignment to unused variable `unused`

B006 Do not use mutable data structures for argument defaults
  --> yomon_uslub.py:16:23
   |
16 | def toplam(elementlar=[]):
   |                       ^^
17 |     elementlar.append(1)
18 |     return elementlar
   |
help: Replace with `None`; initialize within function

Found 3 errors.
No fixes available (3 hidden fixes can be enabled with the `--unsafe-fixes` option).

Avtomatik tuzatilmagan ikkitasi eng muhimi. Ularni ko'rib chiqamiz.

B006: o'zgaruvchan sukut argumenti

Bu Python dagi eng mashhur tuzoq:

python9 qator
def toplam(elementlar=[]):
    elementlar.append(1)
    return elementlar

print("1-chaqiruv:", toplam())
print("2-chaqiruv:", toplam())
print("3-chaqiruv:", toplam())
print("\nsabab: sukut qiymati FUNKSIYA E'LON QILINGANDA bir marta yaratiladi")
print("id har safar bir xil:", id(toplam.__defaults__[0]))
Chiqish
1-chaqiruv: [1]
2-chaqiruv: [1, 1]
3-chaqiruv: [1, 1, 1]

sabab: sukut qiymati FUNKSIYA E'LON QILINGANDA bir marta yaratiladi
id har safar bir xil: 139659140556352

Ro'yxat chaqiruvlar orasida saqlanib qoldi. To'g'ri yozuv:

python9 qator
def toplam_togri(elementlar=None):
    if elementlar is None:
        elementlar = []
    elementlar.append(1)
    return elementlar

print("1-chaqiruv:", toplam_togri())
print("2-chaqiruv:", toplam_togri())
print("3-chaqiruv:", toplam_togri())
Chiqish
1-chaqiruv: [1]
2-chaqiruv: [1]
3-chaqiruv: [1]

ruff buni B006 bilan ushladi — bu haqiqiy xato, uslub masalasi emas.

E711: != None

python8 qator
class Gaalati:
    def __eq__(self, boshqa):
        return True

g = Gaalati()
print("g == None  ->", g == None)          # __eq__ chaqiriladi
print("g is None  ->", g is None)          # obyekt o'ziga solishtiriladi
print("\nshuning uchun har doim `is None` / `is not None`")
Chiqish
g == None  -> True
g is None  -> False

shuning uchun har doim `is None` / `is not None`

== None obyektning __eq__ metodini chaqiradi va u yolg'on gapirishi mumkin. is None esa obyekt aynan None ekanini tekshiradi.

Formatlash

shu faylni yaratingformatsiz.py

python6 qator
def hisobla(a,b,   c):
    natija={'birinchi':a,   'ikkinchi':b,
        'uchinchi':c}
    if a>b and b>c:
        return    natija
    return {  }
matn5 qator
echo "--- formatlashdan oldin ---"
cat formatsiz.py
ruff format formatsiz.py
echo "--- formatlashdan keyin ---"
cat formatsiz.py
Chiqish (qaytish kodi 0, 22 ms)
--- formatlashdan oldin ---
def hisobla(a,b,   c):
    natija={'birinchi':a,   'ikkinchi':b,
        'uchinchi':c}
    if a>b and b>c:
        return    natija
    return {  }
1 file reformatted
--- formatlashdan keyin ---
def hisobla(a, b, c):
    natija = {"birinchi": a, "ikkinchi": b, "uchinchi": c}
    if a > b and b > c:
        return natija
    return {}

Formatlovchining asosiy foydasi — muhokamani tugatish. Bo'sh joy, qavs, qator uzunligi haqida bahslashish shart emas: asbob qaror qiladi, hamma bo'ysunadi.

matn1 qator
rm -f yomon_uslub.py formatsiz.py
Chiqish (qaytish kodi 0, 5 ms)
(chiqish yo'q)

Loyihani tekshiramiz

python5 qator
echo "=== linter nima deydi ==="
ruff check src tests
echo ""
echo "=== formatlash kerakmi ==="
ruff format --check src tests 2>&1 | tail -3
Chiqish (qaytish kodi 0, 96 ms)
=== linter nima deydi ===
RUF022 [*] `__all__` is not sorted
  --> src/hisobot/__init__.py:9:11
   |
 8 |   __version__ = "0.4.0"
 9 |   __all__ = ["daraja_taqsimoti", "guruh_ortachasi", "Daraja", "Natija", "Talaba",
   |  ___________^
10 | |            "oqi", "tozala", "bahoni_tekshir", "__version__"]
   | |____________________________________________________________^
   |
help: Apply an isort-style sorting to `__all__`

F401 [*] `.oqish.oqi` imported but unused
  --> src/hisobot/cli.py:11:20
   |
 9 | from .hisob import daraja_taqsimoti, eng_yaxshi, guruh_ortachasi
10 | from .ko_p import hammasi
11 | from .oqish import oqi, tozala
   |                    ^^^
12 | from .sozlama import Sozlama
13 | from .xatolar import HisobotXato, SozlamaXato
   |
help: Remove unused import

F401 [*] `.oqish.tozala` imported but unused
  --> src/hisobot/cli.py:11:25
   |
 9 | from .hisob import daraja_taqsimoti, eng_yaxshi, guruh_ortachasi
10 | from .ko_p import hammasi
11 | from .oqish import oqi, tozala
   |                         ^^^^^^
12 | from .sozlama import Sozlama
13 | from .xatolar import HisobotXato, SozlamaXato
   |
help: Remove unused import

F401 [*] `.xatolar.HisobotXato` imported but unused
  --> src/hisobot/cli.py:13:22
   |
11 | from .oqish import oqi, tozala
12 | from .sozlama import Sozlama
13 | from .xatolar import HisobotXato, SozlamaXato
   |                      ^^^^^^^^^^^
14 |
15 | log = logging.getLogger(__name__)
   |
help: Remove unused import: `.xatolar.HisobotXato`

Found 4 errors.
[*] 4 fixable with the `--fix` option.

=== formatlash kerakmi ===
Would reformat: tests/test_tarmoq.py
Would reformat: tests/test_tekshiruv.py
18 files would be reformatted, 9 files already formatted

Loyihamiz toza emas: linter __init__.py dagi tartibsizlikni topdi va deyarli hamma fayl formatlanmagan. Buni bir marta tuzatamiz:

matn2 qator
ruff check --fix src tests
ruff format src tests
Chiqish (qaytish kodi 0, 163 ms)
Found 4 errors (4 fixed, 0 remaining).
18 files reformatted, 9 files left unchanged
python4 qator
echo "=== endi qayta tekshiramiz ==="
ruff check src tests
ruff format --check src tests
echo "qaytish kodi: $?"
Chiqish (qaytish kodi 0, 72 ms)
=== endi qayta tekshiramiz ===
All checks passed!
27 files already formatted
qaytish kodi: 0

Endi ikkalasi ham toza. --check fayllarni o'zgartirmaydi, faqat "formatlash kerakmi?" deb aytadi — CI da aynan shu ishlatiladi.

:::gotcha Formatlashdan keyin testlarni qayta chopting ruff check --fix kodni o'zgartiradi. Odatda bu xavfsiz, lekin har doim emas. Shuning uchun tuzatishdan keyin testlarni albatta qayta ishga tushiring:

matn2 qator
.venv/bin/python3 -m pytest -q
python3 -m mypy
Chiqish (qaytish kodi 1, 5772 ms)
........................................................................ [ 86%]
...........                                                              [100%]
src/hisobot/cli.py:127: error: Function is missing a type annotation for one or more parameters  [no-untyped-def]
Found 1 error in 1 file (checked 13 source files)

:::

Uchta asbob, uchta vazifa

python11 qator
import pandas as pd
show(pd.DataFrame([
    {"asbob": "ruff format", "nima qiladi": "Ko'rinish: bo'sh joy, qator uzunligi, qavslar",
     "xato topadimi": "yo'q"},
    {"asbob": "ruff check", "nima qiladi": "Uslub + keng tarqalgan xatolar (B006, F401)",
     "xato topadimi": "ba'zilarini"},
    {"asbob": "mypy", "nima qiladi": "Turlar mosligini tekshiradi",
     "xato topadimi": "ha, mantiqiy xatolarni"},
    {"asbob": "pytest", "nima qiladi": "Xulq to'g'riligini tekshiradi",
     "xato topadimi": "ha, asosiysini"},
]))
asbobnima qiladixato topadimi
ruff formatKo'rinish: bo'sh joy, qator uzunligi, qavslaryo'q
ruff checkUslub + keng tarqalgan xatolar (B006, F401)ba'zilarini
mypyTurlar mosligini tekshiradiha, mantiqiy xatolarni
pytestXulq to'g'riligini tekshiradiha, asosiysini

To'rttasi bir-birini almashtirmaydi. Har biri boshqa turdagi muammoni topadi.

Bitta buyruqqa yig'amiz

shu faylni yaratingtekshir.sh

bash29 qator
#!/usr/bin/env bash
# Loyihaning barcha tekshiruvlari. CI ham, dasturchi ham shu bitta buyruqni ishlatadi.
set -u
XATO=0

ishga() {
    echo ""
    echo "=== $1 ==="
    shift
    if "$@"; then
        echo "OK"
    else
        echo "YIQILDI (kod $?)"
        XATO=1
    fi
}

ishga "format"  ruff format --check src tests
ishga "linter"  ruff check src tests
ishga "turlar"  python3 -m mypy
ishga "testlar" .venv/bin/python3 -m pytest -q

echo ""
if [ "$XATO" -eq 0 ]; then
    echo "HAMMASI O'TDI"
else
    echo "BA'ZI TEKSHIRUVLAR YIQILDI"
fi
exit "$XATO"
matn4 qator
chmod +x tekshir.sh
./tekshir.sh
echo ""
echo "umumiy qaytish kodi: $?"
Chiqish (qaytish kodi 0, 4065 ms)
=== format ===
27 files already formatted
OK

=== linter ===
All checks passed!
OK

=== turlar ===
src/hisobot/cli.py:127: error: Function is missing a type annotation for one or more parameters  [no-untyped-def]
Found 1 error in 1 file (checked 13 source files)
YIQILDI (kod 1)

=== testlar ===
........................................................................ [ 86%]
...........                                                              [100%]
OK

BA'ZI TEKSHIRUVLAR YIQILDI

umumiy qaytish kodi: 0

Muhim tafsilot: skript birinchi xatoda to'xtamaydi. Sababi — dasturchi hamma muammoni bir marta ko'rishi kerak, birma-bir emas.

Uzluksiz integratsiya (CI)

shu faylni yarating.github/workflows/tekshir.yml

yaml37 qator
name: tekshiruvlar

on:
  push:
    branches: [main]
  pull_request:

jobs:
  sinov:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python: ["3.10", "3.11", "3.12"]

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
          cache: pip

      - name: bog'liqliklarni o'rnatish
        run: pip install -e ".[dev]"

      - name: format
        run: ruff format --check src tests

      - name: linter
        run: ruff check src tests

      - name: turlar
        run: mypy

      - name: testlar
        run: pytest --cov=hisobot --cov-report=term-missing --cov-fail-under=75
python9 qator
ls -la .github/workflows/
.venv/bin/python3 -c "
import re, pathlib
matn = pathlib.Path('.github/workflows/tekshir.yml').read_text(encoding='utf-8')
qadamlar = re.findall(r'- name: (.+)', matn)
versiyalar = re.search(r'python: \[(.+)\]', matn).group(1)
print('Python versiyalari:', versiyalar)
print('qadamlar:', qadamlar)
"
Chiqish (qaytish kodi 0, 120 ms)
total 1
drwxrwxrwx 1 abulqosim abulqosim 512 Sep 29 14:01 .
drwxrwxrwx 1 abulqosim abulqosim 512 Sep 29 14:01 ..
-rwxrwxrwx 1 abulqosim abulqosim 713 Sep 29 14:01 tekshir.yml
Python versiyalari: "3.10", "3.11", "3.12"
qadamlar: ["bog'liqliklarni o'rnatish", 'format', 'linter', 'turlar', 'testlar']

matrix uch versiyada sinaydi. fail-fast: false — bittasi yiqilsa ham qolganlari ishlaydi, shunda muammo bitta versiyagami yoki hammasidami — darhol ma'lum.

pre-commit: commit dan oldin

CI xatoni yuborgandan keyin topadi. pre-commit esa commit paytida:

shu faylni yarating.pre-commit-config.yaml

yaml17 qator
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.5.0
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
        args: [--maxkb=500]
      - id: detect-private-key

detect-private-key alohida qimmatli: u maxfiy kalit tasodifan commit ga tushishini to'xtatadi. 11-bobdagi qoidani eslang — ochilgan sirni o'chirish yetarli emas.

O'rnatish: pre-commit install. Shundan keyin har git commit da avtomatik ishlaydi.

:::tip Formatlovchini keyin qo'shsangiz Mavjud loyihaga formatlovchi qo'shsangiz, birinchi ishga tushirish butun kodni o'zgartiradi va git tarixi buziladi. Yechim: formatlashni alohida commit qiling va uning hash ini .git-blame-ignore-revs fayliga yozing. Shunda git blame uni o'tkazib yuboradi. :::

Sifatni raqamda o'lchash

python8 qator
.venv/bin/python3 -m pytest --cov=hisobot --cov-report=term --quiet 2>&1 | tail -15
echo ""
echo "--- kod hajmi ---"
find src tests -name "*.py" | xargs wc -l | tail -1
echo "--- fayllar soni ---"
find src -name "*.py" | wc -l
echo "--- testlar soni ---"
.venv/bin/python3 -m pytest --collect-only -q 2>/dev/null | tail -2
Chiqish (qaytish kodi 0, 12766 ms)
src/hisobot/__init__.py        6      0   100%
src/hisobot/__main__.py        2      2     0%   3-5
src/hisobot/chiqarish.py      22      0   100%
src/hisobot/cli.py            85      4    95%   22, 106-107, 120
src/hisobot/hisob.py          12      0   100%
src/hisobot/ko_p.py           22      0   100%
src/hisobot/modellar.py       33      1    97%   21
src/hisobot/muhit.py          17     17     0%   3-26
src/hisobot/oqish.py          42      4    90%   25-28
src/hisobot/sozlama.py        39      1    97%   59
src/hisobot/tarmoq.py         30      4    87%   41-44
src/hisobot/tekshiruv.py      22      0   100%
src/hisobot/xatolar.py        13      4    69%   24-27
--------------------------------------------------------
TOTAL                        345     37    89%

--- kod hajmi ---
 1096 total
--- fayllar soni ---
13
--- testlar soni ---
tests/test_xatolar.py: 6

Bu raqamlarni kuzatib borish foydali: qamrov tushib ketsa yoki fayl 800 qatordan oshsa — signal.

Lekin ehtiyot bo'ling: raqam maqsadga aylansa, u o'lchov bo'lishdan to'xtaydi. 100% qamrov talab qilsangiz, odamlar bo'sh test yozadi (7-bobda ko'rdik).

Xulosa

Tushuncha Asosiy fikr
Mexanik izohlar Asbob aytsin, odam emas
ruff check Linter: F401, B006, E711 — ba'zilari haqiqiy xato
ruff format Ko'rinish haqidagi bahsni tugatadi
B006 O'zgaruvchan sukut argumenti chaqiruvlar orasida saqlanadi
is None == None obyektning __eq__ iga tayanadi — ishonchsiz
To'rt asbob format, linter, turlar, testlar — biri boshqasini almashtirmaydi
Bitta skript tekshir.sh — birinchi xatoda to'xtamasin
CI matrix Bir necha Python versiyasida sinang
pre-commit Xatoni yuborishdan oldin ushlaydi; detect-private-key muhim

Talaba ishi

  1. ruff.toml ga "ANN" (annotatsiyalar) qoidasini qo'shing. Nechta yangi ogohlantirish chiqdi? Ularning hammasi foydalimi?
  2. tekshir.sh ga qamrov chegarasini qo'shing (--cov-fail-under=75). Loyiha o'tadimi?
  3. ruff check --statistics src tests ni ishga tushiring. Qaysi qoida eng ko'p buzilgan?
  4. B006 xatosini o'z kodingizda qidiring: grep -rn "=\[\]\|={}" src/. Topildimi?

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar