{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-23-10-00",
   "metadata": {},
   "source": [
    "# 23-10 · Проверка исключённых каталогов\n",
    "\n",
    "Практика к разделу [«Какие каталоги не нужно сканировать»](../../site/chapters/glava-23/23-09-isklyucheniya.html). Настоящий файл — `projects/python/safesort/src/safesort/config.py`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-01",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Воспроизвести `Config.excluded_names()` и проверить, что каталог результата и служебный `.safesort` исключены всегда — даже если их явно нет в пользовательских настройках."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-02",
   "metadata": {},
   "source": [
    "## Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-10-03",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass, field\n",
    "\n",
    "DEFAULT_DESTINATION = \"Sorted\"\n",
    "DEFAULT_EXCLUDE = (\".git\", \".venv\")\n",
    "STATE_DIRNAME = \".safesort\"\n",
    "\n",
    "\n",
    "@dataclass(frozen=True)\n",
    "class Config:\n",
    "    destination: str = DEFAULT_DESTINATION\n",
    "    exclude: tuple = field(default_factory=lambda: DEFAULT_EXCLUDE)\n",
    "\n",
    "    def excluded_names(self):\n",
    "        return frozenset({*self.exclude, self.destination, STATE_DIRNAME})\n",
    "\n",
    "\n",
    "nastrojki = Config()\n",
    "print(sorted(nastrojki.excluded_names()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-04",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-10-05",
   "metadata": {},
   "outputs": [],
   "source": [
    "imena_dlya_proverki = [\".git\", \".venv\", \"Sorted\", \".safesort\", \"Downloads\", \"photo.jpg\"]\n",
    "propuskat = {imya: imya in nastrojki.excluded_names() for imya in imena_dlya_proverki}\n",
    "print(propuskat)\n",
    "\n",
    "assert propuskat[\".git\"] is True\n",
    "assert propuskat[\"Sorted\"] is True\n",
    "assert propuskat[\".safesort\"] is True\n",
    "assert propuskat[\"Downloads\"] is False\n",
    "assert propuskat[\"photo.jpg\"] is False\n",
    "print(\"Верно: обязательные исключения пропускаются, обычные имена — нет.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-06",
   "metadata": {},
   "source": [
    "## Starter\n",
    "\n",
    "Заполните отмеченное место. Неизменённый starter не проходит tests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "task-23-10",
   "metadata": {
    "tags": [
     "exercise",
     "starter"
    ]
   },
   "outputs": [],
   "source": [
    "def config_s_isklyucheniem(name: str) -> Config:\n",
    "    # TODO: return Config with DEFAULT_EXCLUDE plus name.\n",
    "    raise NotImplementedError\n",
    "\n",
    "\n",
    "svoi_nastrojki = config_s_isklyucheniem(\"node_modules\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-08",
   "metadata": {},
   "source": [
    "## Task\n",
    "\n",
    "Напишите функцию, добавляющую одно пользовательское имя к exclusions и сохраняющую обязательные исключения."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-09",
   "metadata": {},
   "source": [
    "## Tests\n",
    "\n",
    "Запустите после task cell: есть основной пример и хотя бы один крайний случай."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tests-23-10",
   "metadata": {
    "tags": [
     "exercise-tests"
    ]
   },
   "outputs": [],
   "source": [
    "assert \"node_modules\" in svoi_nastrojki.excluded_names()\n",
    "assert \"Sorted\" in svoi_nastrojki.excluded_names()\n",
    "assert \".safesort\" in svoi_nastrojki.excluded_names()\n",
    "assert \"build\" in config_s_isklyucheniem(\"build\").excluded_names()\n",
    "print(\"Tests passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-11",
   "metadata": {},
   "source": [
    "## Hint\n",
    "\n",
    "Создайте tuple через `(*DEFAULT_EXCLUDE, name)`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-10-12",
   "metadata": {},
   "source": [
    "## Solution\n",
    "\n",
    "<details><summary>Показать решение после собственной попытки</summary>\n",
    "\n",
    "```python\n",
    "def config_s_isklyucheniem(name: str) -> Config:\n",
    "    return Config(exclude=(*DEFAULT_EXCLUDE, name))\n",
    "\n",
    "\n",
    "svoi_nastrojki = config_s_isklyucheniem(\"node_modules\")\n",
    "```\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
