К сожалению модуль hotpot не может определить тип исходного файла
Я с завидной регулярностью сталкиваюсь со всевозможными ошибками, так или иначе связанными с модулями Python. Существует огромное количество разнообразных модулей Python, которые разработчики активно используют, но далеко не всегда заботятся об установке зависимостей. Некоторые даже не удосуживаются их документировать. Параллельно существует две мажорные версии Python: 2 и 3. В разных дистрибутивах отдано предпочтение одной или другой версии, по этой причине самостоятельно установленную программу в зависимости от дистрибутива нужно при запуске предварять python или python2/python3. Например:
Причём обычно не происходит никаких проверок и угадали ли вы с выбором версии или нет вы узнаете только при появлении первых ошибок, вызванных неправильным синтаксисом программного кода для данной версии.
Также прибавляет путаницу то, что модули можно установить как из стандартного репозитория дистрибутивов, так и с помощью pip (инструмент для установки пакетов Python).
Цель этой заметки — рассмотреть некоторые характерные проблемы модулей Python. Все возможные ошибки вряд ли удастся охватить, но описанное здесь должно помочь понять, в каком примерно направлении двигаться.
Отсутствие модуля Python
Большинство ошибок модулей Python начинаются со строк:
В них трудно разобраться, поэтому поищите фразы вида:
- ModuleNotFoundError: No module named
- No module named
- ImportError: No module named
За ними следует название модуля.
Поищите по указанному имени в системном репозитории, или попробуйте установить командой вида:
Пакет Python установлен, но программа его не видит
Причина может быть в том, что вы установили модуль для другой версии. Например, программа написана на Python3, а вы установили модуль с этим же названием, но написанный на Python2. В этом случае он не будет существовать для программы. Поэтому нужно правильно указывать номер версии.
Команда pip также имеет свои две версии: pip2 и pip3. Если версия не указана, то это означает, что используется какая-то из двух указанных (2 или 3) версий, которая является основной в системе. Например, сейчас в Debian и производных по умолчанию основной версией Python является вторая. Поэтому в репозитории есть два пакета: python-pip (вторая версия) и python3-pip (третья).
В Arch Linux и производных по умолчанию основной версией является третья, поэтому в репозиториях присутствует пакет python-pip (третья версия) и python2-pip (вторая).
Это же самое относится к пакетам Python и самому Python: если версия не указана, значит имеется ввиду основная для вашего дистрибутива версия. По этой причине многие пакеты в репозитории присутствуют с двумя очень похожими названиями.
Установлена новая версия модуля, но программа видит старую версию
Я несколько раз сталкивался с подобными необъяснимыми ошибками.
Иногда помогает удаление модуля командой вида:
Также попробуйте удалить его используя системный менеджер пакетов.
Если модуль вам нужен, попробуйте вновь установить его и проверьте, решило ли это проблему.
Если проблема не решена, то удалите все файлы модуля, обычно они расположены в папках вида:
- /usr/lib/python2.7/site-packages/модуль
- /usr/lib/python3.7/site-packages/модуль
Ошибки, в которых присутствует слово AttributeError, NoneType, object has no attribute обычно вызваны не отсутствием модуля, а тем, что модуль не получил ожидаемого аргумента, либо получил неправильное число аргументов. Было бы правильнее сказать, что ошибка вызвана недостаточной проверкой данных и отсутствием перехвата исключений (то есть программа плохо написана).
В этих случаях обычно ничего не требуется дополнительно устанавливать. В моей практике частыми случаями таких ошибок является обращение программы к определённому сайту, но сайт может быть недоступен, либо API ключ больше недействителен, либо программа не получила ожидаемые данные по другим причинам. Также программа может обращаться к другой программе, но из-за ошибки в ней получит не тот результат, который ожидала, и уже это вызывает приведённые выше ошибки, которые мы видим.
Опять же, хорошо написанная программа в этом случае должна вернуть что-то вроде «информация не загружена», «работа программы N завершилась ошибкой» и так далее. Как правило, нужно разбираться с причиной самой первой проблемы или обращаться к разработчику.
Модуль установлен, но при обновлении или обращении к нему появляется ошибки
Это самая экзотическая ошибка, которая вызвана, видимо, повреждением файлов пакета. К примеру, при попытке обновления я получал ошибку:
При этом сам модуль установлен как следует из самой первой строки.
Проблема может решиться удалением всех файлов пакета (с помощью rm) и затем повторной установки.
К примеру в рассматриваемом случае, удаление:
После этого проблема с модулем исчезла.
Заключение
Пожалуй, это далеко не полный «справочник ошибок Python», но если вы можете сориентироваться, какого рода ошибка у вас возникла:
- отсутствует модуль
- модуль неправильной версии
- модуль повреждён
- внешняя причина — программа не получила ожидаемые данные
Так вот, если вы хотя бы примерно поняли главную причину, то вам будет проще понять, в каком направлении двигаться для её решения.
Не позволяйте типам появляться во внешнем интерфейсе модуля, если только вы не уверены в том, что все пользователи смогут корректно их понять и работать с ними. Используйте наивысший уровень абстракции, который в состоянии понять клиентский код.
Чем более широко распространяется ваша библиотека, тем меньше ваш контроль над средами программирования, используемыми вашими клиентами, и тем меньше множество типов, которые ваша библиотека может надежно использовать в своем внешнем интерфейсе. Взаимодействие между модулями включает обмен бинарными данными. Увы, С++ не определяет стандартные бинарные интерфейсы; широко распространенные библиотеки для взаимодействия со внешним миром могут полагаться на такие встроенные типы, как int и char . Даже один и тот же тип на одном и том же компиляторе может оказаться бинарно несовместимым при компиляции с разными опциями.
Обычно либо вы полностью контролируете компилятор и опции компиляции, используемые для сборки модуля и его клиентов (и тогда вы можете использовать любой тип), либо вы не имеете такой возможности и должны использовать только типы, предоставляемые вычислительной платформой, или встроенные типы С++ (но даже в этом случае следует документировать размер и представление последних). В частности, использовать в интерфейсе модуля типы стандартной библиотеки можно только в том случае, если все другие модули, использующие данный, будут компилироваться в то же время и с теми же исходными файлами стандартной библиотеки.
Требуется найти определенный компромисс между проблемами используемых типов, которые могут не быть корректно восприняты всеми клиентами, и проблемами использования низкого уровня абстракции. Абстракция важна; если некоторые клиенты понимают только низкоуровневые типы и вы ограничены в использовании этими типами, то, возможно, следует подумать о дополнительных операциях, работающих с высокоуровневыми типами. Рассмотрим функцию SummarizeFile , которая получает в качестве аргумента файл. Имеется три варианта действий — передать указатель char* на строку в стиле С с именем файла; передать string с именем файла и передать объект istream или пользовательский объект Filе . Каждый из этих вариантов представляет свой уровень компромисса.
• Вариант 1. char* . Очевидно, что тип char* доступен наиболее широкому кругу клиентов. К сожалению, это также наиболее низкоуровневый вариант; в частности, он более проблематичен (например, вызывающий и вызываемый код должны явно решить, кто именно выделяет память для строки и кто ее освобождает), более подвержен ошибкам (например, файл может не существовать), и менее безопасен (например, может оказаться подвержен классической атаке, основанной на переполнении буфера).
• Вариант 2. string . Тип string доступен меньшему кругу клиентов, ограниченному использованием С++ и компиляцией с использованием той же реализации стандартной библиотеки, того же компилятора и совместимых настроек компилятора. Взамен мы получаем менее проблематичный (надо меньше беспокоиться об управлении памятью; однако см. рекомендацию 60) и более безопасный код (например, тип string увеличивает при необходимости свой буфер и не так подвержен атакам на основе переполнения буфера). Но и этот вариант относительно низкоуровневый, а потому так же открытый для ошибок, как и предыдущий (например, указанный файл может и не существовать).
• Вариант 3. istream или File . Если уж вы переходите к типам, являющимся классами, т.е. в любом случае требуется, чтобы клиент использовал язык программирования С++, причем тот же компилятор с теми же опциями компиляции, то воспользуйтесь преимуществами абстракции: класс istream (или пользовательский класс File , представляющий собой оболочку вокруг istream , позволяющую устранить зависимость от реализации стандартной библиотеки) повышает уровень абстракции и делает API существенно менее проблематичным. Функция получает объект типа File или соответствующий входной поток, она не должна заботиться об управлении памятью для строк, содержащих имя файла, и защищена от множества ошибок, которые вполне возможны при использовании первых двух вариантов. Остается только выполнить несколько проверок: файл должен быть открыт, а его содержимое иметь верный формат, но, в принципе, этим и ограничивается список неприятностей, которые могут произойти в данном варианте.
Даже если вы предпочтете воспользоваться во внешнем интерфейсе модуля низкоуровневой абстракцией, всегда используйте во внутренней реализации абстракции максимально высокого уровня и преобразуйте их в низкоуровневые абстракции на границах модуля. Например, если у вас имеются клиенты, не использующие С++, вы можете воспользоваться непрозрачным указателем void* или дескриптором типа int для работы с клиентом, но во внутренней реализации используйте высокоуровневые объекты. Преобразование между этими объектами и выбранными низкоуровневыми типами выполняйте только в интерфейсе модуля.
Пример. Использование std::string в интерфейсе модуля. Пусть мы хотим, чтобы модуль предоставлял следующую функцию API:
std::string Translate(const std::string&);
Для библиотек, используемых внутри одной команды компании, это обычно неплохое решение. Но если вы планируете динамически компоновать данный модуль с вызывающим кодом, который использует иную реализацию std::string (например, иное размещение в памяти), то из-за такого несоответствия могут случиться разные странные и неприятные вещи.
Мы встречались с разработчиками, которые пытались использовать собственный класс-оболочку CustomString для объектов std::string , но в результате они сталкивались с той же проблемой, поскольку не имели полного контроля над процессом сборки всех клиентских приложений.
Одно из решений состоит в переходе к переносимым (вероятно, встроенным) типам, как вместо функции с аргументом string , так и в дополнение к ней. Такой новой функцией может быть функция
void Translate(const char* src, char* dest, size_t destSize);
Использование низкоуровневой абстракции более переносимо, но всегда добавляет сложности; здесь, например, как вызывающий, так и вызываемый код должны явно использовать обрезку строки, если размера буфера оказывается недостаточно. (Заметим, что данная версия использует буфер, выделяемый вызывающим кодом, для того чтобы избежать ловушки, связанной с выделением и освобождением памяти в разных модулях — см. рекомендацию 60.)
Ядро — это та часть операционной системы, работа которой полностью скрыта от пользователя, т. к. пользователь с ним не работает напрямую: пользователь работает с программами. Но, тем не менее, без ядра невозможна работа ни одной программы, т.е. они без ядра бесполезны. Этот механизм чем-то напоминает отношения официанта и клиента: работа хорошего официанта должна быть практически незаметна для клиента, но без официанта клиент не сможет передать заказ повару, и этот заказ не будет доставлен.
В Linux ядро монолитное, т.е. все его драйвера и подсистемы работают в своем адресном пространстве, отделенном от пользовательского. Сам термин «монолит» говорит о том, что в ядре сконцентрировано всё, и, по логике, ничего не может в него добавляться или удаляться. В случае с ядром Linux — это правда лишь отчасти: ядро Linux может работать в таком режиме, однако, в подавляющем большинстве сборок возможна модификация части кода ядра без его перекомпиляции, и даже без его выгрузки. Это достигается путем загрузки и выгрузки некоторых частей ядра, которые называются модулями. Чаще всего в процессе работы необходимо подключать модули драйверов устройств, поддержки криптографических алгоритмов, сетевых средств, и, чтобы уметь это правильно делать, нужно разбираться в строении ядра и уметь правильно работать с его модулями. Об этом и пойдет речь в этой статье.
В современных ядрах при подключении оборудования модули подключаются автоматически, а это событие обрабатывается демоном udev, который создает соответствующий файл устройства в каталоге "/dev". Все это выполняется в том случае, если соответствующий модуль корректно установлен в дерево модулей. В случае с файловыми системами ситуация та же: при попытке монтирования файловой системы ядро подгружает необходимый модуль автоматически, и выполняет монтирование.
Если необходимость в модуле не на столько очевидна, ядро его не загружает самостоятельно. Например, для поддержки функции шифрования на loop устройстве нужно вручную подгрузить модуль «cryptoloop», а для непосредственного шифрования — модуль алгоритма шифрования, например «blowfish».
Поиск необходимого модуля
Модули хранятся в каталоге "/lib/modules/<версия ядра>" в виде файлов с расширением «ko». Для получения списка всех модулей из дерева можно выполнить команду поиска всех файлов с расширением «ko» в каталоге с модулями текущего ядра:
find /lib/modules/`uname -r` -name ‘*.ko’
Полученный список даст некоторое представление о доступных модулях, их назначении и именах. Например, путь «kernel/drivers/net/wireless/rt2x00/rt73usb.ko» явно указывает на то, что этот модуль — драйвер устройства беспроводной связи на базе чипа rt73. Более детальную информацию о модуле можно получить при помощи команды modinfo:
Загрузка и выгрузка модулей
Загрузить модуль в ядро можно при помощи двух команд: «insmod» и «modprobe», отличающихся друг от друга возможностью просчета и удовлетворения зависимостей. Команда «insmod» загружает конкретный файл с расширением «ko», при этом, если модуль зависит от других модулей, еще не загруженных в ядро, команда выдаст ошибку, и не загрузит модуль. Команда «modprobe» работает только с деревом модулей, и возможна загрузка только оттуда по имени модуля, а не по имени файла. Отсюда следует область применения этих команд: при помощи «insmod» подгружается файл модуля из произвольного места файловой системы (например, пользователь скомпилировал модули и перед переносом в дерево ядра решил проверить его работоспособность), а «modprobe» — для подгрузки уже готовых модулей, включенных в дерево модулей текущей версии ядра. Например, для загрузки модуля ядра «rt73usb» из дерева ядра, включая все зависимости, и отключив аппаратное шифрование, нужно выполнить команду:
После загрузки модуля можно проверить его наличие в списке загруженных в ядро модулей при помощи команды «lsmod»:
Module | Size | Used by | |
rt73usb | 17305 | 0 | |
crc_itu_t | 999 | 1 | rt73usb |
rt2x00usb | 5749 | 1 | rt73usb |
rt2x00lib | 19484 | 2 | rt73usb,rt2x00usb |
Из вывода команды ясно, что модуль подгружен, а так же в своей работе использует другие модули.
Чтобы его выгрузить, можно воспользоваться командой «rmmod» или той же командой «modprobe» с ключем "-r". В качестве параметра обоим командам нужно передать только имя модуля. Если модуль не используется, то он будет выгружен, а если используется — будет выдана ошибка, и придется выгружать все модули, которые от него зависят:
Для автоматической загрузки модулей в разных дистрибутивах предусмотрены разные механизмы. Я не буду вдаваться здесь в подробности, они для каждого дистрибутива свои, но один метод загрузки всегда действенен и удобен: при помощи стартовых скриптов. В тех же RedHat системах можно записать команды загрузки модуля прямо в "/etc/rc.d/rc.local" со всеми опциями.
Файлы конфигурация модулей находится в каталоге "/etc/modprobe.d/" и имеют расширение «conf». В этих файлах преимущественно перечисляются альтернативные имена модулей, их параметры, применяемые при их загрузке, а так же черные списки, запрещенные для загрузки. Например, чтобы вышеупомянутый модуль сразу загружался с опцией «nohwcrypt=1» нужно создать файл, в котором записать строку:
options rt73usb nohwcrypt=1
Черный список модулей хранится преимущественно в файле "/etc/modules.d/blacklist.conf" в формате «blacklist <имя модуля>». Используется эта функция для запрета загрузки глючных или конфликтных модулей.
Сборка модуля и добавление его в дерево
Иногда нужного драйвера в ядре нет, поэтому приходится его компилировать вручную. Это так же тот случай, если дополнительное ПО требует добавление своего модуля в ядро, типа vmware, virtualbox или пакет поддержки карт Nvidia. Сам процесс компиляции не отличается от процесса сборки программы, но определенные требования все же есть.
Во первых, нужен компилятор. Обычно установка «gcc» устанавливает все, что нужно для сборки модуля. Если чего-то не хватает — программа сборки об этом скажет, и нужно будет доустановить недостающие пакеты.
Во вторых, нужны заголовочные файлы ядра. Дело в том, что модули ядра всегда собираются вместе с ядром, используя его заголовочные файлы, т.к. любое отклонение и несоответствие версий модуля и загруженного ядра ведет к невозможности загрузить этот модуль в ядро.
Если система работает на базе ядра дистрибутива, то нужно установить пакеты с заголовочными файлами ядра. В большинстве дистрибутивов это пакеты «kernel-headers» и/или «kernel-devel». Для сборки модулей этого будет достаточно. Если ядро собиралось вручную, то эти пакеты не нужны: достаточно сделать символическую ссылку "/usr/src/linux", ссылающуюся на дерево сконфигурированных исходных кодов текущего ядра.
После компиляции модуля на выходе будет получен один или несколько файлов с расширением «ko». Можно попробовать их загрузить при помощи команды «insmod» и протестировать их работу.
Если модули загрузились и работают (или лень вручную подгружать зависимости), нужно их скопировать в дерево модулей текущего ядра, после чего обязательно обновить зависимости модулей командой «depmod». Она пройдется рекурсивно по дереву модулей и запишет все зависимости в файл «modules.dep», который, в последствие, будет анализироваться командой «modprobe». Теперь модули готовы к загрузке командой modprobe и могут загружаться по имени со всеми зависимостями.
Стоит отметить, что при обновлении ядра этот модуль работать не будет. Нужны будут новые заголовочные файлы и потребуется заново пересобрать модуль.
Ошибка «Invalid argument» может говорить о чем угодно, саму ошибку ядро на консоль написать не может, только при помощи функции «printk» записать в системный лог. Посмотрев логи можно уже узнать в чем ошибка:
В этом примере выведена только последняя строка с ошибкой, чтобы не загромаждать статью. Модуль может написать и несколько строк, поэтому лучше выводить полный лог, или хотя бы последние строк десять.
Ошибку уже легко найти: значение «2» неприемлемо для параметра «nohwcrypt». После исправления, модуль корректно загрузится в ядро.
Из всего сказанного можно сделать один вывод: ядро Linux играет по своим правилам и занимается серьезными вещами. Тем не менее — это всего лишь программа, оно, по сути, не сильно отличается от других обычных программ. Понимание того, что ядро не так уж страшно, как кажется, может стать первым шагом к пониманию внутреннего устройства системы и, как результат, поможет быстро и эффективно решать задачи, с которыми сталкивается любой администратор Linux в повседневной работе.
Давайте узнаем, как использовать Recuva в следующем разделе
Recuva не может определить тип файловой системы
Независимо от того, является ли ваша карта неузнаваемой или поврежденной, есть вероятность, что вы вернете свои данные. В случае, если вы хотите получить удаленные файлы. Это также может быть получено.
Прежде чем получить решение, вы должны знать, почему оно не может определить тип файловой системы.
Некоторые из причин включают в себя
- USB-накопитель был извлечен без размонтирования
- Карта MicroSD была удалена без выключения устройства
Для выполнения задачи восстановления ваших данных вам необходимо скачать и установить вспомогательный инструмент. Это поможет вам вернуть случайно удаленные файлы с карты.
Перед этим перейдите с опцией «Быстрое форматирование» и вытрите свою карту. Рекомендуется не выбирать «Полный формат» или просто «Формат». При таком подходе он позволяет открывать карту, как и раньше.
Вы также можете использовать USB Image Tool для резервного копирования поврежденной карты, прежде чем выбрать быстрое форматирование. Он будет хранить данные и поможет вам восстановить то же самое.
После форматирования вы не увидите ни одного файла и папки. Но на диске есть файлы и папки. И, Recuva может сделать изображения файлов, чтобы восстановить их все.
Пошаговый процесс
Он запустится и найдет обычные файлы восстановления с компьютера в двух процессах сканирования. Чтобы выполнить эту операцию, вы можете выбрать нормальное или глубокое сканирование.
Шансы на восстановление будут высоки, если поврежденная карта не использовалась на других устройствах. Если файлы фрагментированы на карте microSD или USB-накопителе, их невозможно восстановить.
Нижняя линия
Надеюсь, что эта информация решила ваши проблемы и ‘Recuva не может определить тип файловой системыПрограммное обеспечение поможет вам вернуть ваши данные!
Читайте также: