पाइथन पैकेज बनाएं: विकास से PyPI गाइड

कोड से PyPI तैनाती तक Python पैकेजिंग का अध्ययन करें

Page content

Python पैकेजिंग ने काफी विकास किया है, जिसमें आधुनिक उपकरण और मानक इसे अपने कोड को वितरित करने के लिए आसान बनाते हैं।

यह गाइड आपको पेशेवर Python पैकेज बनाने और उन्हें PyPI पर प्रकाशित करने के लिए मार्गदर्शन करता है। अगर आप Python के नए हैं या एक तेज़ संदर्भ की आवश्यकता है, तो हमारे Python चिट्ठा की जांच करें ताकि आप Python के मूलभूत तत्वों के साथ तुरंत शुरू कर सकें।

Python पैकेजेज

अपने Python कोड को पैकेज करने का कारण?

अपने Python प्रोजेक्ट को पैकेज करने से कई फायदे मिलते हैं:

  • पुन: उपयोग: कई प्रोजेक्ट्स के बीच कोड को कॉपी-पेस्ट किए बिना साझा करें
  • वितरण: दूसरों को अपने कोड को एक सरल pip install के साथ स्थापित करने की अनुमति दें
  • निर्भरता प्रबंधन: निर्भरताओं को स्पष्ट रूप से निर्दिष्ट और प्रबंधित करें
  • संस्करण: रिलीज़ को ट्रैक करें और पिछले संस्करणों के साथ पिछले संगतता बनाए रखें
  • पेशेवर मानक: Python समुदाय के सर्वोत्तम प्रथाओं का पालन करें
  • दस्तावेज़ीकरण: संरचना उचित दस्तावेज़ीकरण और परीक्षण को प्रोत्साहित करती है

आधुनिक Python पैकेज संरचना

एक अच्छी तरह से संगठित पैकेज इस संरचना का पालन करता है:

my-package/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_core.py
├── docs/
│   └── index.md
├── .github/
│   └── workflows/
│       └── publish.yml
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore

src-layout अब फ्लैट लेआउट के बजाय पसंद किया जाता है क्योंकि यह:

  • विकास के दौरान स्थापित नहीं हुए कोड के दुर्घटना से आयात को रोकता है
  • परीक्षण को अधिक विश्वसनीय बनाता है स्थापित करने के लिए मजबूर करके
  • स्रोत कोड को अन्य प्रोजेक्ट फाइलों से स्पष्ट रूप से अलग करता है

अपने पैकेज के आंतरिक संरचना को संगठित करते समय, अपने कोड को अधिक बनाए रखने योग्य और परीक्षण योग्य बनाने के लिए क्लीन आर्किटेक्चर सिद्धांतों को लागू करने का विचार करें। हमारा गाइड Python डिजाइन पैटर्न्स फॉर क्लीन आर्किटेक्चर SOLID सिद्धांतों, निर्भरता इंजेक्शन, और लेयर्ड आर्किटेक्चर पैटर्न्स को कवर करता है जो Python पैकेजेज के साथ उत्कृष्ट रूप से काम करते हैं।

pyproject.toml फ़ाइल: आधुनिक कॉन्फ़िगरेशन

pyproject.toml (PEP 518, 621) Python प्रोजेक्ट कॉन्फ़िगरेशन के लिए आधुनिक मानक है, जो पुराने setup.py को बदलता है:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Your Name", email = "you@example.com"}
]
keywords = ["example", "tutorial", "package"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "mypy>=1.5.0",
]
docs = [
    "sphinx>=7.0.0",
    "sphinx-rtd-theme>=1.3.0",
]

[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"

[project.scripts]
mypackage-cli = "mypackage.cli:main"

[tool.setuptools.packages.find]
where = ["src"]

मुख्य कॉन्फ़िगरेशन सेक्शन्स

  1. build-system: बिल्ड बैकएंड निर्दिष्ट करता है (setuptools, hatchling, poetry-core, flit-core)
  2. project: कोर मेटाडेटा और निर्भरताएं
  3. project.optional-dependencies: अतिरिक्त फीचर निर्भरताएं (dev, docs, testing)
  4. project.scripts: कमांड-लाइन एंट्री पॉइंट्स
  5. tool.*: टूल-स्पेसिफिक कॉन्फ़िगरेशन (black, pytest, mypy, आदि)

एक बिल्ड बैकएंड का चयन

Setuptools (स्टैंडर्ड चॉइस)

सबसे व्यापक रूप से उपयोग किया जाने वाला और संगत विकल्प:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

फायदे: सार्वभौमिक संगतता, विस्तृत विशेषताएं, बड़ा पारिस्थितिकी तंत्र नुकसान: अधिक विस्तृत कॉन्फ़िगरेशन, नए विकल्पों की तुलना में धीमी

Hatchling (आधुनिक और तेज़)

हल्का और प्रदर्शन-उन्मुख आधुनिक बैकएंड:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

फायदे: तेज़ बिल्ड, सरल कॉन्फ़िग, अच्छे डिफ़ॉल्ट नुकसान: setuptools की तुलना में कम प्लगइन्स

Poetry (ऑल-इन-वन)

पूर्ण निर्भरता और वातावरण प्रबंधन:

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"

फायदे: निर्भरता समाधान, लॉक फाइलों, एकीकृत वर्कफ्लो नुकसान: अलग वर्कफ्लो, बड़ा टूल सतह क्षेत्र

Flit (मिनिमलिस्ट)

सरल पैकेजेज के लिए सरल उपकरण:

[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"

फायदे: अत्यंत सरल, न्यूनतम कॉन्फ़िगरेशन नुकसान: जटिल पैकेजेज के लिए सीमित विशेषताएं

अपने पैकेज को बिल्ड करना

बिल्ड उपकरण स्थापित करें

# आधुनिक बिल्ड टूल स्थापित करें
pip install build

# या Poetry के लिए
pip install poetry

# या Hatchling के लिए
pip install hatch

वितरण फाइलों को बिल्ड करें

build पैकेज का उपयोग करके (किसी भी बैकएंड के साथ काम करता है):

# पिछले बिल्ड्स को साफ़ करें
rm -rf dist/ build/ *.egg-info

# स्रोत वितरण और व्हील बिल्ड करें
python -m build

# यह बनाता है:
# dist/mypackage-0.1.0.tar.gz    (स्रोत वितरण)
# dist/mypackage-0.1.0-py3-none-any.whl  (व्हील)

Poetry का उपयोग करके:

poetry build

Hatch का उपयोग करके:

hatch build

वितरण प्रारूपों को समझें

स्रोत वितरण (sdist) - .tar.gz

  • स्रोत कोड और बिल्ड निर्देशों को शामिल करता है
  • उपयोगकर्ताओं का pip इसे स्थापित करने के दौरान बिल्ड करता है
  • परीक्षण, दस्तावेज़, और अन्य विकास फाइलों को शामिल करता है

व्हील - .whl

  • प्री-बिल्ड बाइनरी वितरण
  • तेज़ स्थापित (बिल्ड चरण नहीं)
  • प्लेटफॉर्म-स्पेसिफिक या शुद्ध Python
  • वितरण के लिए सिफ़ारिश किया जाने वाला प्रारूप

PyPI पर प्रकाशित करना

विधि 1: Twine के साथ मैनुअल अपलोड (परंपरागत)

# twine स्थापित करें
pip install twine

# अपलोड से पहले पैकेज की जांच करें
twine check dist/*

# पहले TestPyPI पर अपलोड करें (सिफ़ारिश की जाती है)
twine upload --repository testpypi dist/*

# TestPyPI से परीक्षण स्थापित करें
pip install --index-url https://test.pypi.org/simple/ mypackage

# उत्पादन PyPI पर अपलोड करें
twine upload dist/*

क्रेडेंशियल्स को ~/.pypirc में कॉन्फ़िगर करें:

[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBpLm9yZw...

विधि 2: ट्रस्टेड प्रकाशन (CI/CD के लिए सिफ़ारिश की जाती है)

ट्रस्टेड प्रकाशन OpenID Connect (OIDC) का उपयोग करता है ताकि CI/CD प्लेटफॉर्म से प्रमाणिकता प्राप्त की जा सके बिना टोकन को सुरक्षित रखने की आवश्यकता हो।

PyPI में सेटअप:

  1. अपने PyPI प्रोजेक्ट सेटिंग्स पर जाएं
  2. “Publishing” सेक्शन पर जाएं
  3. एक नया “pending publisher” जोड़ें
  4. कॉन्फ़िगर करें:
    • Owner: आपका GitHub यूज़रनेम/संगठन
    • Repository: रिपॉजिटरी नाम
    • Workflow: publish.yml
    • Environment: release (वैकल्पिक लेकिन सिफ़ारिश की जाती है)

GitHub Actions वर्कफ्लो (.github/workflows/publish.yml):

name: Publish to PyPI

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build          

      - name: Build package
        run: python -m build

      - name: Store distribution packages
        uses: actions/upload-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

  publish:
    needs: build
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write

    steps:
      - name: Download distributions
        uses: actions/download-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1

फायदे:

  • प्रबंधित या सुरक्षित करने के लिए कोई API टोकन नहीं
  • OIDC के माध्यम से स्वचालित प्रमाणिकता
  • पर्यावरण सुरक्षा नियमों के माध्यम से बढ़ी हुई सुरक्षा
  • सभी प्रकाशनों का ऑडिट ट्रेल

## सर्वोत्तम प्रथाएँ

### 1. संस्करण प्रबंधन

सांख्यिकीय संस्करण प्रबंधन (SemVer) का उपयोग करें: `MAJOR.MINOR.PATCH`

```bash
# संस्करण बढ़ाने के लिए उपकरण स्थापित करें
pip install bump2version

# संस्करण बढ़ाएं
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0

.bumpversion.cfg में कॉन्फ़िगर करें:

[bumpversion]
current_version = 0.1.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:src/mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

2. आवश्यक फाइलों को शामिल करें

README.md: स्पष्ट परियोजना विवरण, स्थापना, उपयोग उदाहरण LICENSE: उपयुक्त लाइसेंस चुनें (MIT, Apache 2.0, GPL, आदि) CHANGELOG.md: संस्करणों के बीच परिवर्तनों का दस्तावेज़ीकरण .gitignore: बिल्ड आर्टिफैक्ट्स, कैश, वर्चुअल एन्वायर्नमेंट्स को बाहर करें

3. व्यापक परीक्षण

# परीक्षण निर्भरताओं को स्थापित करें
pip install pytest pytest-cov

# कवरेज के साथ परीक्षण चलाएं
pytest --cov=mypackage tests/

# कवरेज रिपोर्ट बनाएं
pytest --cov=mypackage --cov-report=html tests/

pyproject.toml में कॉन्फ़िगर करें:

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "--strict-markers --cov=mypackage"

4. कोड गुणवत्ता उपकरण

# फॉर्मेटिंग
black src/ tests/

# लिंटिंग
flake8 src/ tests/

# टाइप चेकिंग
mypy src/

# इम्पोर्ट सॉर्टिंग
isort src/ tests/

प्री-कमिट हुक्स में एकीकृत करें (.pre-commit-config.yaml):

repos:
  - repo: https://github.com/psf/black
    rev: 23.9.1
    hooks:
      - id: black

  - repo: https://github.com/pycqa/flake8
    rev: 6.1.0
    hooks:
      - id: flake8

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.5.1
    hooks:
      - id: mypy

5. दस्तावेज़ीकरण

दस्तावेज़ीकरण के लिए Sphinx का उपयोग करें:

# Sphinx स्थापित करें
pip install sphinx sphinx-rtd-theme

# दस्तावेज़ीकरण शुरू करें
cd docs
sphinx-quickstart

# दस्तावेज़ीकरण बनाएं
make html

या MkDocs के लिए सरल मार्कडाउन-आधारित दस्तावेज़ीकरण का उपयोग करें:

pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve

6. निरंतर एकीकरण

भिन्न प्लेटफॉर्म और वातावरणों पर अपने Python पैकेजों का परीक्षण करना विश्वसनीयता के लिए महत्वपूर्ण है। AWS Lambda पर JavaScript, Python और Golang के प्रदर्शन के बारे में अंतर्दृष्टि के लिए, हमारा पढ़ें AWS Lambda प्रदर्शन: JavaScript vs Python vs Golang, जो Python के प्रदर्शन का पता लगाता है Serverless वातावरणों में।

पूर्ण CI/CD वर्कफ्लो (.github/workflows/ci.yml):

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

    steps:
      - uses: actions/checkout@v4

      - name: Python सेट अप करें
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}

      - name: निर्भरताओं को स्थापित करें
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"          

      - name: परीक्षण चलाएं
        run: pytest --cov=mypackage --cov-report=xml

      - name: कवरेज अपलोड करें
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Python सेट अप करें
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: निर्भरताओं को स्थापित करें
        run: |
          pip install black flake8 mypy          

      - name: Black फॉर्मेटिंग चेक
        run: black --check src/ tests/

      - name: Flake8
        run: flake8 src/ tests/

      - name: MyPy
        run: mypy src/

सामान्य समस्याएँ और समाधान

समस्या 1: स्थापना के बाद इम्पोर्ट त्रुटियाँ

समस्या: पैकेज स्थापित हुआ लेकिन इम्पोर्ट विफल हो रहे हैं

समाधान: सही पैकेज संरचना के साथ __init__.py फाइलें और सही pyproject.toml कॉन्फ़िगरेशन सुनिश्चित करें:

[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]

समस्या 2: गायब निर्भरताएँ

समस्या: पैकेज स्थापित हो गया लेकिन रनटाइम पर गायब निर्भरताओं के कारण विफल हो रहा है

समाधान: pyproject.toml में सभी रनटाइम निर्भरताओं का वर्णन करें:

[project]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

समस्या 3: संस्करण संघर्ष

समस्या: पैकेज विकास में काम करता है लेकिन उत्पादन में विफल हो जाता है

समाधान: वर्चुअल एन्वायर्नमेंट्स का उपयोग करें और न्यूनतम संस्करणों का निर्देश दें:

# अलग वातावरण बनाएं
python -m venv .venv
source .venv/bin/activate  # Windows पर: .venv\Scripts\activate

# विकास के लिए एडिटेबल मोड में स्थापित करें
pip install -e ".[dev]"

समस्या 4: बड़ा पैकेज आकार

समस्या: पैकेज डाउनलोड/स्थापना करने में बहुत समय ले रहा है

समाधान: MANIFEST.in का उपयोग करके अनावश्यक फाइलों को बाहर करें:

include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-exclude tests *
recursive-exclude docs *

समस्या 5: प्लेटफॉर्म-विशिष्ट समस्याएँ

समस्या: पैकेज एक OS पर काम करता है लेकिन दूसरे पर विफल हो जाता है

समाधान: CI/CD मैट्रिक्स बिल्ड्स का उपयोग करके कई प्लेटफॉर्मों पर परीक्षण करें (उपरोक्त CI उदाहरण देखें)

प्रकाशन चेकलिस्ट

अपने पैकेज को PyPI पर प्रकाशित करने से पहले, यह सुनिश्चित करें:

  • सभी परीक्षण Python संस्करणों पर पास हो गए
  • कोड फॉर्मेटेड और लिंटेड है
  • README.md उदाहरणों के साथ पूर्ण है
  • LICENSE फाइल शामिल है
  • संस्करण संख्या अपडेट की गई है
  • CHANGELOG.md अपडेट की गई है
  • निर्भरताएँ सही ढंग से निर्दिष्ट की गई हैं
  • पैकेज बिना त्रुटियों के बनता है (python -m build)
  • पैकेज TestPyPI पर परीक्षण किया गया है
  • दस्तावेज़ीकरण अप-टू-डेट है
  • Git रिपॉजिटरी संस्करण के साथ टैग की गई है
  • GitHub रिलीज़ बनाया गया है (विश्वसनीय प्रकाशन के लिए)

उन्नत विषय

एंट्री पॉइंट्स और CLI उपकरण

कमांड-लाइन इंटरफेस बनाएं:

[project.scripts]
mypackage = "mypackage.cli:main"

src/mypackage/cli.py में कार्यान्वयन:

import click

@click.command()
@click.option('--name', default='World', help='Name to greet')
def main(name):
    """Simple CLI tool example"""
    click.echo(f'Hello, {name}!')

if __name__ == '__main__':
    main()

बाहरी API के साथ इंटरैक्ट करने वाले Python पैकेजों को बनाने के लिए एक वास्तविक उदाहरण के लिए, हमारा पढ़ें Ollama को Python के साथ एकीकृत करना: REST API और Python क्लाइंट उदाहरण

प्लगिन प्रणाली

प्लगिन खोजने की अनुमति दें:

[project.entry-points."mypackage.plugins"]
plugin_a = "mypackage_plugin_a:PluginA"

C एक्सटेंशन्स

प्रदर्शन-संवेदनशील कोड के लिए:

[tool.setuptools.ext-modules]
name = "mypackage._speedups"
sources = ["src/mypackage/_speedups.c"]

नेमस्पेस पैकेज

एक सामान्य नेमस्पेस के तहत वितरित पैकेजों के लिए:

[tool.setuptools.packages.find]
where = ["src"]
include = ["company.*"]

[tool.setuptools.package-data]
"company.mypackage" = ["py.typed"]

उपयोगी उपकरण और संसाधन

पैकेज विकास उपकरण

  • cookiecutter: Python पैकेजों के लिए प्रोजेक्ट टेम्प्लेट्स
  • tox: Python संस्करणों के पार टेस्ट ऑटोमेशन
  • nox: टॉक्स वैकल्पिक, लचीला टेस्ट ऑटोमेशन
  • pre-commit: कोड गुणवत्ता के लिए Git हुक फ्रेमवर्क
  • commitizen: कमिट संदेशों और संस्करण प्रबंधन के लिए मानकीकरण

दस्तावेज़ीकरण प्लेटफॉर्म

  • Read the Docs: मुफ्त दस्तावेज़ीकरण होस्टिंग
  • GitHub Pages: MkDocs या Sphinx दस्तावेज़ीकरण होस्ट करें
  • pdoc: सरल API दस्तावेज़ीकरण जनरेटर

पैकेज रजिस्ट्री

  • PyPI: आधिकारिक Python पैकेज इंडेक्स (pypi.org)
  • TestPyPI: परीक्षण वातावरण (test.pypi.org)
  • Anaconda.org: Conda पैकेज वितरण
  • GitHub Packages: निजी पैकेज होस्टिंग

मॉनिटरिंग और एनालिटिक्स

  • PyPI Stats: पैकेजों के डाउनलोड सांख्यिकी
  • Libraries.io: निर्भरता मॉनिटरिंग और अलर्ट्स
  • Snyk: सुरक्षा कमजोरियों का स्कैनिंग
  • Dependabot: स्वचालित निर्भरता अपडेट्स

निष्कर्ष

आधुनिक Python पैकेजिंग pyproject.toml, शक्तिशाली बिल्ड बैकएंड्स, और सुरक्षित प्रकाशन के लिए Trusted Publishing के साथ डेवलपर-फ्रेंडली बन गया है। इस गाइड का पालन करके, आप अपने उपयोगकर्ताओं को प्रभावी ढंग से सेवा देने वाले पेशेवर, बनाए रखने योग्य Python पैकेज बना सकते हैं।

मुख्य निष्कर्ष:

  1. सभी नए प्रोजेक्ट्स के लिए pyproject.toml का उपयोग करें
  2. अपने आवश्यकताओं के लिए सही बिल्ड बैकएंड चुनें
  3. स्वचालित परीक्षण और CI/CD लागू करें
  4. सुरक्षित, टोकन-मुक्त डिप्लॉयमेंट के लिए Trusted Publishing का उपयोग करें
  5. सांख्यिकीय संस्करण प्रबंधन का पालन करें और चेंजलॉग बनाए रखें
  6. व्यापक दस्तावेज़ीकरण और उदाहरण प्रदान करें

Python पैकेजिंग पारिस्थितिकी तत्काल उपकरणों, मानकों और सुरक्षा विशेषताओं के साथ लगातार सुधर रही है। Python Enhancement Proposals (PEPs) और समुदाय के सर्वोत्तम प्रथाओं से अपडेट रहें ताकि आपके पैकेज आधुनिक और बनाए रखने योग्य रहें।

उपयोगी लिंक्स

इस साइट पर अन्य उपयोगी लेख