From c90abe11f86f7a15be1886a0d30c6df785c6afe2 Mon Sep 17 00:00:00 2001 From: "Nick V. Anokhin" Date: Sat, 11 Jul 2026 22:55:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D1=8B=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=B4=D0=B0=D1=80=D1=82=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- net/xeaf/rack/core/core_exception.py | 10 ++++ .../rack/tests/core/core_exception_tests.py | 8 +++ .../rack/tests/utils/exceptions/__init__.py | 16 ++++++ .../exceptions/bad_request_exception_tests.py | 40 ++++++++++++++ .../exceptions/critical_exception_tests.py | 40 ++++++++++++++ .../file_not_found_exception_tests.py | 40 ++++++++++++++ .../exceptions/forbidden_exception_tests.py | 40 ++++++++++++++ .../exceptions/not_found_exception_tests.py | 40 ++++++++++++++ .../unauthorized_exception_tests.py | 40 ++++++++++++++ net/xeaf/rack/utils/exceptions/__init__.py | 16 ++++++ .../utils/exceptions/bad_request_exception.py | 45 ++++++++++++++++ .../utils/exceptions/critical_exception.py | 54 +++++++++++++++++++ .../exceptions/file_not_found_exception.py | 26 +++++++++ .../utils/exceptions/forbidden_exception.py | 45 ++++++++++++++++ .../utils/exceptions/not_found_exception.py | 45 ++++++++++++++++ .../exceptions/unauthorized_exception.py | 45 ++++++++++++++++ 16 files changed, 550 insertions(+) create mode 100644 net/xeaf/rack/tests/utils/exceptions/__init__.py create mode 100644 net/xeaf/rack/tests/utils/exceptions/bad_request_exception_tests.py create mode 100644 net/xeaf/rack/tests/utils/exceptions/critical_exception_tests.py create mode 100644 net/xeaf/rack/tests/utils/exceptions/file_not_found_exception_tests.py create mode 100644 net/xeaf/rack/tests/utils/exceptions/forbidden_exception_tests.py create mode 100644 net/xeaf/rack/tests/utils/exceptions/not_found_exception_tests.py create mode 100644 net/xeaf/rack/tests/utils/exceptions/unauthorized_exception_tests.py create mode 100644 net/xeaf/rack/utils/exceptions/__init__.py create mode 100644 net/xeaf/rack/utils/exceptions/bad_request_exception.py create mode 100644 net/xeaf/rack/utils/exceptions/critical_exception.py create mode 100644 net/xeaf/rack/utils/exceptions/file_not_found_exception.py create mode 100644 net/xeaf/rack/utils/exceptions/forbidden_exception.py create mode 100644 net/xeaf/rack/utils/exceptions/not_found_exception.py create mode 100644 net/xeaf/rack/utils/exceptions/unauthorized_exception.py diff --git a/net/xeaf/rack/core/core_exception.py b/net/xeaf/rack/core/core_exception.py index c24e714..f45c3e3 100644 --- a/net/xeaf/rack/core/core_exception.py +++ b/net/xeaf/rack/core/core_exception.py @@ -40,6 +40,16 @@ class CoreException(Exception): self._process_meta(meta, **kwargs) super().__init__() + # noinspection PyMethodMayBeStatic + def is_pure_status(self) -> bool: + """ + Возвращает признак необходимости возвращать в ответе реальный статус HTTP + + :return: Признак необходимости + """ + + return False + def _process_meta(self, meta: dict | None, **kwargs: Any) -> None: """ Разбирает переданные параметры diff --git a/net/xeaf/rack/tests/core/core_exception_tests.py b/net/xeaf/rack/tests/core/core_exception_tests.py index 466c275..fa408c7 100644 --- a/net/xeaf/rack/tests/core/core_exception_tests.py +++ b/net/xeaf/rack/tests/core/core_exception_tests.py @@ -131,3 +131,11 @@ class CoreExceptionTests(SimpleTestCase): self.assertIsNotNone(self.exc_instance.meta) self.assertIsInstance(self.exc_instance.meta, dict) self.assertEqual(self.exc_instance.meta["trace_id"], "xyz") + + def test_is_pure_status_defaults_to_false(self): + """ + Проверяет, что по умолчанию исключение возвращает False для чистого статуса + """ + + exc = CoreException(status_code=400) + self.assertFalse(exc.is_pure_status()) diff --git a/net/xeaf/rack/tests/utils/exceptions/__init__.py b/net/xeaf/rack/tests/utils/exceptions/__init__.py new file mode 100644 index 0000000..b3fe3ca --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/__init__.py @@ -0,0 +1,16 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Пакет описания тестов для классов исключений +""" + +from .bad_request_exception_tests import BadRequestExceptionTests +from .critical_exception_tests import CriticalExceptionTests +from .file_not_found_exception_tests import FileNotFoundExceptionTests +from .forbidden_exception_tests import ForbiddenExceptionTests +from .not_found_exception_tests import NotFoundExceptionTests +from .unauthorized_exception_tests import UnauthorizedExceptionTests diff --git a/net/xeaf/rack/tests/utils/exceptions/bad_request_exception_tests.py b/net/xeaf/rack/tests/utils/exceptions/bad_request_exception_tests.py new file mode 100644 index 0000000..72756c1 --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/bad_request_exception_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса BadRequestExceptionTests +""" + +from django.test import SimpleTestCase +from rest_framework import status + +from net.xeaf.rack.utils.exceptions import BadRequestException + + +class BadRequestExceptionTests(SimpleTestCase): + """ + Тесты для BadRequestException + """ + + def test_init_with_defaults(self): + """ + Проверяет инициализацию по умолчанию (без параметров) + """ + + exc = BadRequestException() + + self.assertEqual(exc.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(exc.detail, "rack.errors.http.bad_request") + self.assertIsNone(exc.meta) + + def test_is_pure_status_returns_false(self): + """ + Гарантирует, что ошибка возвращает False для чистого HTTP статуса + """ + + exc = BadRequestException() + + self.assertFalse(exc.is_pure_status()) diff --git a/net/xeaf/rack/tests/utils/exceptions/critical_exception_tests.py b/net/xeaf/rack/tests/utils/exceptions/critical_exception_tests.py new file mode 100644 index 0000000..bc741be --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/critical_exception_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CriticalExceptionTests +""" + +from django.test import SimpleTestCase +from rest_framework import status + +from net.xeaf.rack.utils.exceptions import CriticalException + + +class CriticalExceptionTests(SimpleTestCase): + """ + Тесты для CriticalException + """ + + def test_init_with_defaults(self): + """ + Проверяет дефолтные параметры: статус и сообщение об ошибке + """ + + exc = CriticalException() + + self.assertEqual(exc.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertEqual(exc.detail, "rack.errors.http.internal_server_error") + self.assertIsNone(exc.meta) + + def test_is_pure_status_returns_true(self): + """ + Гарантирует, что ошибка возвращает True для чистого HTTP статуса + """ + + exc = CriticalException() + + self.assertTrue(exc.is_pure_status()) diff --git a/net/xeaf/rack/tests/utils/exceptions/file_not_found_exception_tests.py b/net/xeaf/rack/tests/utils/exceptions/file_not_found_exception_tests.py new file mode 100644 index 0000000..7df91c1 --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/file_not_found_exception_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса FileNotFoundExceptionTests +""" + +from django.test import SimpleTestCase +from rest_framework import status + +from net.xeaf.rack.utils.exceptions import FileNotFoundException + + +class FileNotFoundExceptionTests(SimpleTestCase): + """ + Тесты для FileNotFoundException + """ + + def test_init_with_defaults(self): + """ + Проверяет дефолтные параметры: статус и сообщение об ошибке + """ + + exc = FileNotFoundException() + + self.assertEqual(exc.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(exc.detail, "rack.errors.http.not_found") + self.assertIsNone(exc.meta) + + def test_is_pure_status_returns_true(self): + """ + Гарантирует, что ошибка возвращает True для чистого HTTP статуса + """ + + exc = FileNotFoundException() + + self.assertTrue(exc.is_pure_status()) diff --git a/net/xeaf/rack/tests/utils/exceptions/forbidden_exception_tests.py b/net/xeaf/rack/tests/utils/exceptions/forbidden_exception_tests.py new file mode 100644 index 0000000..2de1197 --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/forbidden_exception_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса ForbiddenExceptionTests +""" + +from django.test import SimpleTestCase +from rest_framework import status + +from net.xeaf.rack.utils.exceptions import ForbiddenException + + +class ForbiddenExceptionTests(SimpleTestCase): + """ + Тесты для BadRequestException + """ + + def test_init_with_defaults(self): + """ + Проверяет инициализацию по умолчанию (без параметров) + """ + + exc = ForbiddenException() + + self.assertEqual(exc.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(exc.detail, "rack.errors.http.forbidden") + self.assertIsNone(exc.meta) + + def test_is_pure_status_returns_false(self): + """ + Гарантирует, что ошибка возвращает False для чистого HTTP статуса + """ + + exc = ForbiddenException() + + self.assertFalse(exc.is_pure_status()) diff --git a/net/xeaf/rack/tests/utils/exceptions/not_found_exception_tests.py b/net/xeaf/rack/tests/utils/exceptions/not_found_exception_tests.py new file mode 100644 index 0000000..e90475d --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/not_found_exception_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса NotFoundExceptionTests +""" + +from django.test import SimpleTestCase +from rest_framework import status + +from net.xeaf.rack.utils.exceptions import NotFoundException + + +class NotFoundExceptionTests(SimpleTestCase): + """ + Тесты для NotFoundException + """ + + def test_init_with_defaults(self): + """ + Проверяет дефолтные параметры: статус и сообщение об ошибке + """ + + exc = NotFoundException() + + self.assertEqual(exc.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(exc.detail, "rack.errors.http.not_found") + self.assertIsNone(exc.meta) + + def test_is_pure_status_returns_false(self): + """ + Гарантирует, что ошибка возвращает False для чистого HTTP статуса + """ + + exc = NotFoundException() + + self.assertFalse(exc.is_pure_status()) diff --git a/net/xeaf/rack/tests/utils/exceptions/unauthorized_exception_tests.py b/net/xeaf/rack/tests/utils/exceptions/unauthorized_exception_tests.py new file mode 100644 index 0000000..11d35e3 --- /dev/null +++ b/net/xeaf/rack/tests/utils/exceptions/unauthorized_exception_tests.py @@ -0,0 +1,40 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса UnauthorizedExceptionTests +""" + +from django.test import SimpleTestCase +from rest_framework import status + +from net.xeaf.rack.utils.exceptions import UnauthorizedException + + +class UnauthorizedExceptionTests(SimpleTestCase): + """ + Тесты для UnauthorizedException + """ + + def test_init_with_defaults(self): + """ + Проверяет дефолтные параметры: статус и сообщение об ошибке + """ + + exc = UnauthorizedException() + + self.assertEqual(exc.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(exc.detail, "rack.errors.http.unauthorized") + self.assertIsNone(exc.meta) + + def test_is_pure_status_returns_false(self): + """ + Гарантирует, что ошибка возвращает False для чистого HTTP статуса + """ + + exc = UnauthorizedException() + + self.assertFalse(exc.is_pure_status()) diff --git a/net/xeaf/rack/utils/exceptions/__init__.py b/net/xeaf/rack/utils/exceptions/__init__.py new file mode 100644 index 0000000..ada229c --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/__init__.py @@ -0,0 +1,16 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Пакет классов исключений +""" + +from .bad_request_exception import BadRequestException +from .critical_exception import CriticalException +from .file_not_found_exception import FileNotFoundException +from .forbidden_exception import ForbiddenException +from .not_found_exception import NotFoundException +from .unauthorized_exception import UnauthorizedException diff --git a/net/xeaf/rack/utils/exceptions/bad_request_exception.py b/net/xeaf/rack/utils/exceptions/bad_request_exception.py new file mode 100644 index 0000000..af92ef6 --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/bad_request_exception.py @@ -0,0 +1,45 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса BadRequestException +""" + +from typing import Any + +from django.utils.translation import gettext as _ +from rest_framework import status + +from net.xeaf.rack.core import CoreException + + +class BadRequestException(CoreException): + """ + Исключение, возникающее при ошибке в запросе + """ + + def __init__(self, detail: list | dict | str | None = None, meta: dict | None = None, **kwargs: Any): + """ + Инициализация + + :param detail: Информация об ошибке + :param meta: Информация для обработки ошибки + :param kwargs: Дополнительные параметры + """ + + # Сообщение об ошибке по умолчанию + if detail is None: + detail = _(self.Msg.DEFAULT_MESSAGE) + + super().__init__(status.HTTP_400_BAD_REQUEST, detail, meta, **kwargs) + + class Msg: + """ + Константы сообщений + """ + + DEFAULT_MESSAGE = _("rack.errors.http.bad_request") + """ Некорректный запрос """ diff --git a/net/xeaf/rack/utils/exceptions/critical_exception.py b/net/xeaf/rack/utils/exceptions/critical_exception.py new file mode 100644 index 0000000..af8076c --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/critical_exception.py @@ -0,0 +1,54 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса CriticalException +""" + +from typing import Any + +from django.utils.translation import gettext as _ +from rest_framework import status + +from net.xeaf.rack.core import CoreException + + +class CriticalException(CoreException): + """ + Исключение, возникающее при внутренней ошибке сервера + """ + + def __init__(self, detail: list | str | None = None, meta: dict | None = None, **kwargs: Any): + """ + Инициализация + + :param detail: Информация об ошибке + :param meta: Информация для обработки ошибки + :param kwargs: Дополнительные параметры + """ + + # Сообщение об ошибке по умолчанию + if detail is None: + detail = _(self.Msg.DEFAULT_MESSAGE) + + super().__init__(status.HTTP_500_INTERNAL_SERVER_ERROR, detail, meta, **kwargs) + + def is_pure_status(self) -> bool: + """ + Возвращает признак необходимости возвращать в ответе реальный статус HTTP + + :return: Признак необходимости + """ + + return True + + class Msg: + """ + Константы сообщений + """ + + DEFAULT_MESSAGE = _("rack.errors.http.internal_server_error") + """ Внутренняя ошибка сервера """ diff --git a/net/xeaf/rack/utils/exceptions/file_not_found_exception.py b/net/xeaf/rack/utils/exceptions/file_not_found_exception.py new file mode 100644 index 0000000..e33aba9 --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/file_not_found_exception.py @@ -0,0 +1,26 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса FileNotFoundException +""" + +from .not_found_exception import NotFoundException + + +class FileNotFoundException(NotFoundException): + """ + Исключение, возникающее при невозможности найти запрашиваемый файл + """ + + def is_pure_status(self) -> bool: + """ + Возвращает признак необходимости возвращать в ответе реальный статус HTTP + + :return: Признак необходимости + """ + + return True diff --git a/net/xeaf/rack/utils/exceptions/forbidden_exception.py b/net/xeaf/rack/utils/exceptions/forbidden_exception.py new file mode 100644 index 0000000..b5bb3d6 --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/forbidden_exception.py @@ -0,0 +1,45 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса ForbiddenException +""" + +from typing import Any + +from django.utils.translation import gettext as _ +from rest_framework import status + +from net.xeaf.rack.core import CoreException + + +class ForbiddenException(CoreException): + """ + Исключение, возникающее при нарушении прав доступа + """ + + def __init__(self, detail: list | str | None = None, meta: dict | None = None, **kwargs: Any): + """ + Инициализация + + :param detail: Информация об ошибке + :param meta: Информация для обработки ошибки + :param kwargs: Дополнительные параметры + """ + + # Сообщение об ошибке по умолчанию + if detail is None: + detail = _(self.Msg.DEFAULT_MESSAGE) + + super().__init__(status.HTTP_403_FORBIDDEN, detail, meta, **kwargs) + + class Msg: + """ + Константы сообщений + """ + + DEFAULT_MESSAGE = _("rack.errors.http.forbidden") + """ Доступ запрещен """ diff --git a/net/xeaf/rack/utils/exceptions/not_found_exception.py b/net/xeaf/rack/utils/exceptions/not_found_exception.py new file mode 100644 index 0000000..f55bf8d --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/not_found_exception.py @@ -0,0 +1,45 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса NotFoundException +""" + +from typing import Any + +from django.utils.translation import gettext as _ +from rest_framework import status + +from net.xeaf.rack.core import CoreException + + +class NotFoundException(CoreException): + """ + Исключение, возникающее при невозможности найти запрашиваемый объект + """ + + def __init__(self, detail: list | str | None = None, meta: dict | None = None, **kwargs: Any): + """ + Инициализация + + :param detail: Информация об ошибке + :param meta: Информация для обработки ошибки + :param kwargs: Дополнительные параметры + """ + + # Сообщение об ошибке по умолчанию + if detail is None: + detail = _(self.Msg.DEFAULT_MESSAGE) + + super().__init__(status.HTTP_404_NOT_FOUND, detail, meta, **kwargs) + + class Msg: + """ + Константы сообщений + """ + + DEFAULT_MESSAGE = _("rack.errors.http.not_found") + """ Объект не найден """ diff --git a/net/xeaf/rack/utils/exceptions/unauthorized_exception.py b/net/xeaf/rack/utils/exceptions/unauthorized_exception.py new file mode 100644 index 0000000..8bbc772 --- /dev/null +++ b/net/xeaf/rack/utils/exceptions/unauthorized_exception.py @@ -0,0 +1,45 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса UnauthorizedException +""" + +from typing import Any + +from django.utils.translation import gettext as _ +from rest_framework import status + +from net.xeaf.rack.core import CoreException + + +class UnauthorizedException(CoreException): + """ + Исключение, возникающее при ошибке авторизации + """ + + def __init__(self, detail: list | str | None = None, meta: dict | None = None, **kwargs: Any): + """ + Инициализация + + :param detail: Информация об ошибке + :param meta: Информация для обработки ошибки + :param kwargs: Дополнительные параметры + """ + + # Сообщение об ошибке по умолчанию + if detail is None: + detail = _(self.Msg.DEFAULT_MESSAGE) + + super().__init__(status.HTTP_401_UNAUTHORIZED, detail, meta, **kwargs) + + class Msg: + """ + Константы сообщений + """ + + DEFAULT_MESSAGE = _("rack.errors.http.unauthorized") + """ Учетные данные не были предоставлены """