ctypes
(который включен в стандартную библиотеку Python начиная с версии 2.5, а до этого доступен в качестве стороннего модуля) позволяет нам вызывать практически что угодно откуда угодно.Например, нам нужно узнать тип файла. Для этого в UNIX-системах есть механизм, который определяет тип файла по некой последовательности байтов в начале файла, называемой magic number. В системе есть база данных, сопоставляющая эти последовательности с типами файлов. Например, файлы в формате GIF всегда содержат строку "GIF8" в начале файла, плюс несколько байтов далее, содержащих информацию о версии формата, количестве цветов и т.д. Эта база данных содержится в текстовом файле, который называется
magic
, и может быть расположен в разных местах, в зависимости от операционной системы или дистрибутива. Например, в системе FreeBSD этот файл обычно лежит в /usr/share/misc/
, в версии Linux, которую я использую - в /usr/share/misc/file/
. Можно, конечно, написать свой механизм работы с этим файлом, и самостоятельно искать соответствия, однако мы — ленивые программисты, и пойдем другим путем :)В системе есть программа
file
, являющаяся интерфейсом пользователя к базе данных magic
. Тип файла определить можно запуском этой команды с параметром - именем файла, либо передав ей содержимое файла на стандартный ввод.$ file --mime image.gif
image.gif: image/gif
$ cat image.gif|file - --mime
/dev/stdin: image/gif
Программа
file
использует библиотеку libmagic.so
, которая как раз и является встроенным системным средством работы с файлом magic
, её-то мы и задействуем. Для начала посмотрим руководство по библиотеке — man libmagic
. Руководство содержит описание функций библиотеки. Не буду тут подробно описывать интерфейс библиотеки, думаю вы с этим справитесь сами. Опишу лишь механизм использования этой библиотеки из python через ctypes
.Простейшая функция интерфейса к
libmagic
выглядит примерно так:import ctypes
def get_magic_type(string):
magic = ctypes.cdll.LoadLibrary('libmagic.so.1')
cookie = magic.magic_open()
magic.magic_load(cookie, None)
result = magic.magic_buffer(cookie, string, len(string))
mimetype = ctypes.c_char_p(result)
magic.magic_close(cookie)
return mimetype.value
Проверим как работает:
>>> data = open("image.gif").read()
>>> get_magic_type(data)
'image/gif'</code
Эта функция принимает данные, тип которых мы хотим определить, в виде строки. Вызов
ctypes.cdll.LoadLibrary
загружает нужную битблиотеку — в данном случае она известна системе как libmagic.so.1
, и связывает ее пространство имен с переменной magic
. При обращении к атрибутам этой переменной фактически происходит обращение к переменным и функциям из этой библиотеки, и работать с ними нужно так, как подразумевает интерфейс этой библиотеки.При этом есть одна тонкость, о которой стоит упомянуть. Язык C - это язык со строгой статической типизацией. Функции библиотеки часто принимают и возвращают указатели определенного типа, и это следует учитывать, приводя тип данных в программе на python в соответствующий тип данных, который принимает функция в соответствие с документацией.
Если это не сделано, то модуль
ctypes
старается привести типы данных. В вызове magic.magic_buffer(cookie, string, len(string))
так и происходит. Однако, возможно это не всегда будет то что вам нужно, и типы нужно привести самостоятельно. Для облегчения задачи можно присвоить нужной функции атрибут argtypes
, содержащий список типов данных для каждого позиционного аргумента.Упомянутый выше вызов возвращает указатель на строку, так что в переменной result будет содержаться целое число — значение указателя. Чтобы получить саму строку, мы приводим это значение к соответствующему типу —
mimetype = ctypes.c_char_p(result)
— после чего можно получить значение, на которое ссылается указатель — mimetype.value
. Как и с аргументами, можно заранее определить тип возвращаемого значения, присвоив атрибуту restype
нужной функции значение нужного типа, например так: magic.magic_buffer.restype = ctypes.c_char_p
. В этом случае переменная result сразу получит сам указатель, а не число.Скорее всего, для production-ready решения придется добавить в функцию несколько проверок и обработку ошибок (библиотечные функции
magic_error
или magic_errno
), добавить более тонкую настройку (magic_setflags
), возможно использовать еще какие-то возможности библиотеки, но в данном примере я этого не делаю для простоты изложения.И еще. Помните, что когда вы практически напрямую вызываете функции из библиотеки на C, вы можете столкнуться с разными фатальными ошибками, присущими программам и библиотекам, написанным на этом языке, например segmentation fault, что редко случается при программировании на "чистом" Python c использованием проверенных и стабильных модулей. Не то чтобы C плохой, просто придя в этот монастырь вам придется учесть, что у него есть устав. Рассматривайте программирование с использованием ctypes как сильно облегченный вариант программирования на голом C. Будьте аккуратны. Я предупредил.
В качестве бонуса рассмотрим вариант валидации в формах Dajngo загруженного по HTTP файла изображения. Многие считают достаточным проверить значение content-type для содержимого соответствующего поля формы. Однако, это значение приходит в данных POST, и, следовательно, его легко подделать. В манипуляторах Django валидация происходит через попытку загрузить содержимое в объект
Image
пакета PIL — если нам приехал мусор, то данная операция вызовет исключение. Я же сделаю валидацию с использованием написанной выше функции.from django import newforms as forms
class ImageForm(forms.Form):
valid_image_types = ('image/jpeg', 'image/gif', 'image/png')
image = forms.Field(widget=forms.FileInput)
def clean_image(self):
data = self.data.get('image', None)
if data:
mimetype = get_magic_type(data['content'])
if mimetype not in self.valid_image_types:
raise forms.ValidationError(_('This file is not valid image.'))
return data
Хм. Лично для меня только одна новость- то что это входит теперь в стандартную постаку py2.5 ( каюсь - новщества в 2.5. поленился вдумчиво изучить). А для тех кто не вкурсе - все расписано очень доходчиво. Единственное я бы дополнил статью данными о том ,какой выигрыш ты получил в данном конкретном случае. Такой вариант работает быстрее чем с использованием PIL ? Насколько процентов? Это было бы понагладней IMHO/
ОтветитьУдалитьHow To Register & Claim 1xbet Korean Casino - Legalbet.co.kr
ОтветитьУдалитьHow To Register & Claim 1xbet korean 1xbet Korean Casino at Legalbet.co.kr. 바카라 사이트 We use cookies. By using this website, you consent to our use deccasino of this website.