.
├── second_laba_python # Кодовая база моей лабораторной работы
│ ├── src/ # Исходный код
│ ├── tests/ # Unit тесты для проверки функциональности
│ ├── .gitignore # файл, игнорируемый git
│ ├── .pre-commit-config.yaml # Конфигурация автоматической проверки кодстайла перед коммитом
│ ├── README.md # Описание проекта, структуры и функционала
│ ├── uv.lock # Lock-файл зафиксированных версий зависимостей
В папке src лежат файлы с реализацией задачи заданной в лабораторной работе:
.
├── src
│ ├── services/ # Папка с сервисами для работы с консолью
│ ├── __init__.py
│ ├── config.py # Конфигурация логирования (настройка handlers, formatters, loggers)
│ ├── container.py # Контейнер зависимостей (Dependency Injection) для управления сервисами
│ ├── enums.py # Перечисления: режимы чтения файлов (string/bytes) и отображения (simple/detailed)
│ ├── main.py # Точка входа в приложение, CLI-команды (ls, cat, cd, cp, mv, rm, zip, unzip, tar, untar, grep)
│ ├── errorss.py # Пользовательские исключения (в настоящее время не используется)
В папке services лежат файлы с реализацией сервисов для работы с консолью:
.
├── services # Папка с реализацией консольных команд
│ ├── __init__.py
│ ├── base.py # Абстрактный базовый класс OSConsoleServiceBase с интерфейсом консольных команд
│ ├── windows_console.py # Реализация консольного сервиса (команды ls, cat, cd, cp, mv, rm, zip, unzip, tar, untar, grep)
- Typer парсит аргументы командной строки
- Вызывается соответствующая команда из main.py
- Команда получает контейнер зависимостей из контекста и вызывает соответствующий метод сервиса
- Сервис WindowsConsoleService выполняет операцию с файловой системой
- Результаты выводятся в консоль или обрабатываются ошибки
- FileReadMode: string (режим чтения файла как текст) / bytes (режим чтения файла как байтов) для команды cat
- FileDisplayMode simple (простой режим) / detailed (подробный)
3) container.py - контейнер зависимостей для управления сервисами (хранит все зависимости приложения и передается через контекст Typer в команды)
5) windows_console.py - тут у меня реализация всех команд, краткое описание функционала в данном файле:
-
- получает статистику файла через entry.stat()
- извлекает права доступа через oct(stat_module.S_IMODE(stat_info.st_mode))
- формирует строку: {тип}{права} {размер:>10} {дата} {имя}\n
- обрабатывает ошибки: при ошибке возвращается строка и логируется предупреждение
-
- преобразовывает путь в объект Path
- проверяет существования и тип
- получает список элементов через path.iterdir()
- в зависимости от режима: добавляет названия (simple) или форматирует через format_detailed (detailed)
- Ошибки: FileNotFoundError, NotADirectoryError с логированием
-
- обрабатывает специальные пути: '
' и '/' преобразовываются с помощью os.path.expanduser() - преобразовывает относительный путь в абсолютный относительно текущей директории
- разрешает путь через path.resolve() для обработки '..'
- проверяет существование и тип
- совершает смену директории через os.chdir(path)
- Ошибки: FileNotFoundError, NotADirectoryError
- обрабатывает специальные пути: '
-
- проверяет существования файла
- проверяет, что путь не является директорией
- выводит в зависимости от режима: path.read_text(encoding="utf-8") или path.read_bytes() (как текст или байты)
- Ошибки: FileNotFoundError, IsADirectoryError, OSError с полным логированием
-
- если src_path.is_dir() (директория):
- проверяет наличия флага recursive`
- определяет конечный путь: если dst_path - существующая директория, то dst_path/src_path.name, иначе dst_path
- если конечный путь существует:
- это директория: копирует содержимое через shutil.copytree() для каждой поддиректории и shutil.copy2() для файлов
- это файл: ошибка FileExistsError
-
- если не существует: shutil.copytree(src_path, final_dst)
- иначе (файл):
- определяет конечный путь
- создает родительские директории через mkdir(parents=True, exist_ok=True)
- копирует через shutil.copy2()
- Ошибки: FileNotFoundError, IsADirectoryError, PermissionError, OSError
- если src_path.is_dir() (директория):
-
- определяет конечный путь: если dst_path - существующая директория, то dst_path/src_path.name
- создает родительские директории через mkdir(parents=True, exist_ok=True)
- перемещает через shutil.move()
- Ошибки: FileNotFoundError, PermissionError, OSError
-
- проверяет защищенные пути ('..', '/' запрещены)
- разрешает путь через resolve()
- проверяет, что путь не является корнем диска (Path(res.anchor))
- проверяет существование
- если директория: проверяет флага r, удаляет через shutil.rmtree() или ошибка
- если файл: удаляет через res.unlink()
- Ошибки: PermissionError, FileNotFoundError, IsADirectoryErros, OSError
-
- проверяет существование и тип (должен быть директорией)
- создает родительские директории архива
- открывает zip-архив в режиме записи с сжатием ZIP_DEFLATED
- совершает рекурсивный обход через src_dir.rglob("*") всех файлов
- добавляет каждый файл в архив с сохранением относительного пути через arcname = path.relative_to(src_dir)
- пропускает директорию (добавляются только файлы)
- Ошибки: FileNotFoundError, NotADirectoryError, общие исключения с логированием
-
- определет папки назначения (текущая директория или res)
- проверяет существование архива
- создает директории назначения
- открывает zip-архив в режиме чтения
- распаковывает все файлы через zf.extractall(dst_dir)
- Ошибки: FileNotFoundError, общие исключения с логированием
-
- проверяет существование и тип (должен быть директорией)
- создает родительские директории архива
- открывает tar.gz архива в режиме "w:gz" (запись с gzip сжатием)
- добавляет всю директорию через tf.add(src_dir, arcname=src_dir.name)
- Ошибки: FileNotFoundError, NotADirectoryError, общие исключения
-
- определяет папку назначения (текущая директория или res)
- проверяет существование архива
- создает директорию назначения
- открывает tar.gz архива в режиме "r:gz" (чтение с gzip распаковкой)
- распаковывает все файлы через tf.extractall(dst_dir)
- Ошибки: FileNotFoundError, общие исключения
-
- компилирует регулярное выражение с флагами (re.IGNORECASE если нужно)
- определяет список файлов:
- если path - файл: добавляется в список
- если директория: обход через rglob('') (рекурсивно) или glob('') (не рекурсивно)
- для каждого файла:
- открывает в режиме текста с кодировкой UTF-8 и обработкой ошибок errors='ignore'
- построчно читает и ищет совпадения через rgx.search(line)
- добавляет в результат строки формата "{file_path}:{line_number}:{line.strip()}"
- обрабатывает ошибки чтения отдельных файлов (продолжается поиск в остальных)
- Ошибки: re.error при некорректном регулярном выражении, ошибки чтения файлов логируются
-
Логирование:
- Все операции подробно регистрируются на разных уровнях (DEBUG, INFO, ERROR)
- Логи сохраняются в файле shell.log
- Максимальный размер файла 5МБ, при переполнении он переименуется в shell.log.i и создастся новый shell.log
-
Обработка путей:
- Использование pathlib.Path обеспечивает универсальную работу с путями
- Автоматическое разрешение относительных путей и обработка специальных символов ('~', '.', '..')
-
Безопасность:
- В rm() есть защита от удаления корня и '..', что предотвращает случайное удаление важных директорий
- Команда rm запрашивает подтверждение при рекурсивном удалении директорий, что повышает безопасность
