← Назад к вопросам

Можно ли менять код фреймворка?

1.0 Junior🔥 161 комментариев
#Soft Skills#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Можно ли менять код фреймворка

Теоретически можно, но практически это плохая идея. Это один из важнейших принципов профессиональной разработки: никогда не модифицируй код зависимостей напрямую.

Почему это плохо

1. Обновления фреймворка перепишут твои изменения

# После pip install --upgrade fastapi
# Все твои изменения в site-packages/fastapi/ исчезнут
pip install --upgrade fastapi
# Твой код потерялся

2. Непредсказуемое поведение на разных машинах

# На машине разработчика: свой модифицированный код
# На production: стандартный код (обновился, переустановился)
# Результат: работает у меня, не работает на сервере

# Ад для отладки

3. Конфликты с CI/CD и контейнеризацией

# Dockerfile
FROM python:3.11
RUN pip install fastapi  # Устанавливает стандартную версию
# Твои локальные изменения не будут в контейнере

4. Невозможно поделиться кодом с командой

# Ты изменил файл фреймворка
# Коллега клонирует репо — и ничего не работает
# Потому что у него стандартная версия фреймворка

Что делать вместо этого

1. Monkey Patching (если действительно нужна срочная фиксация)

# В своем коде, НЕ в коде фреймворка

from fastapi import FastAPI
from fastapi.routing import APIRoute

# Сохраняем оригинальный метод
original_operation_uses_models = APIRoute.operation_uses_models

# Переопределяем его
def patched_operation_uses_models(self):
    # Твоя кастомная логика
    print("Applying patch...")
    return original_operation_uses_models(self)

# Применяем патч
APIRoute.operation_uses_models = patched_operation_uses_models

# Теперь все новые экземпляры APIRoute используют твой патч
app = FastAPI()

2. Наследование и расширение

# Вместо изменения fastapi.FastAPI

from fastapi import FastAPI as BaseFastAPI
from fastapi.routing import APIRoute
from typing import Optional

class MyFastApp(BaseFastAPI):
    """Расширенная версия FastAPI с кастомной логикой"""
    
    def add_api_route(
        self,
        path: str,
        endpoint,
        **kwargs,
    ):
        # Твоя логика перед добавлением роута
        print(f"Adding route: {path}")
        
        # Вызываем оригинальный метод
        return super().add_api_route(path, endpoint, **kwargs)

# Используешь вместо FastAPI
app = MyFastApp()

@app.get("/")
def read_root():
    return {"message": "Hello"}

3. Middleware и хуки

# Большинство фреймворков имеют точки расширения

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

app = FastAPI()

class CustomMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Твоя логика ДО запроса
        print(f"Request: {request.url}")
        
        response = await call_next(request)
        
        # Твоя логика ПОСЛЕ запроса
        response.headers["X-Custom-Header"] = "value"
        
        return response

app.add_middleware(CustomMiddleware)

4. Hooks и callbacks

# Используй встроенные механизмы расширения

from fastapi import FastAPI
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Код, выполняемый при запуске приложения
    print("Application startup")
    
    yield
    
    # Код, выполняемый при завершении приложения
    print("Application shutdown")

app = FastAPI(lifespan=lifespan)

5. Dependency Injection

# Большинство фреймворков поддерживают DI

from fastapi import FastAPI, Depends
from typing import Annotated

class CustomLogger:
    def log(self, msg: str):
        print(f"[LOG] {msg}")

logger = CustomLogger()

async def get_logger() -> CustomLogger:
    return logger

app = FastAPI()

@app.get("/")
async def read_root(logger: Annotated[CustomLogger, Depends(get_logger)]):
    logger.log("Root endpoint called")
    return {"message": "Hello"}

6. Конфигурация и переменные окружения

# Вместо изменения фреймворка — используй конфиг

from fastapi import FastAPI
from pydantic import BaseSettings

class Settings(BaseSettings):
    app_name: str = "MyApp"
    custom_header: str = "default"
    debug: bool = False
    
    class Config:
        env_file = ".env"

settings = Settings()
app = FastAPI(title=settings.app_name, debug=settings.debug)

@app.get("/")
def read_root():
    return {"header": settings.custom_header}

7. Создание своего микрофреймворка

Если фреймворк сильно не подходит, лучше написать свой обертка:

# myframework.py — твой обертка
from fastapi import FastAPI as BaseFastAPI
from fastapi.routing import APIRoute

class MyFramework(BaseFastAPI):
    """Обертка над FastAPI с кастомной логикой"""
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._init_custom_stuff()
    
    def _init_custom_stuff(self):
        # Инициализация твоих кастомных компонентов
        pass
    
    def custom_route(self, *args, **kwargs):
        # Твой кастомный декоратор
        def decorator(func):
            # Твоя логика
            return self.get(*args, **kwargs)(func)
        return decorator

# Используется как обычно
app = MyFramework()

@app.custom_route("/")
def read_root():
    return {"message": "Hello"}

Когда нужно изменять фреймворк (редко)

Сценарий 1: Фиксим критический баг

# 1. Сообщаем разработчикам фреймворка (issue)
# 2. Используем патч из их репо
# 3. Если срочно нужно:

# requirements.txt
fastapi @ git+https://github.com/myusername/fastapi@bugfix-branch
# или локальный патч
fastapi @ file:///path/to/local/fastapi

Сценарий 2: Форк фреймворка

# Только если:
# 1. Фреймворк не поддерживается
# 2. Нужны серьезные изменения
# 3. Готов поддерживать форк

git clone https://github.com/fastapi/fastapi.git
git checkout -b my-fork
# Твои изменения
git push origin my-fork

# requirements.txt
fastapi @ git+https://github.com/myusername/fastapi@my-fork

Лучшие практики

  1. Не трогай site-packages — никогда
  2. Используй механизмы расширения фреймворка — middleware, hooks, callbacks
  3. Наследуй и расширяй — создавай свои классы на основе фреймворка
  4. Monkey patch только в своем коде — если критично
  5. Форк только в крайнем случае — потом будет сложно мержить
  6. Пропоси фичу в проект — если нужно что-то большое
  7. Документируй расширения — чтобы команда поняла кастомизацию

Аналогия

# Плохо: менять Django в site-packages
vi /path/to/venv/lib/python3.11/site-packages/django/db/models/base.py

# Хорошо: расширить модели Django
from django.db import models

class MyModel(models.Model):
    # Твоя логика
    pass

Вывод

Менять код фреймворка можно технически, но это антипаттерн. Всегда есть правильный способ расширить фреймворк через его API: наследование, middleware, hooks, конфигурация, зависимости. Если фреймворк не позволяет расширяться нужным тебе способом — подумай, правильный ли выбран фреймворк.

Можно ли менять код фреймворка? | PrepBro