Уведомления

Группа в Telegram: @pythonsu

#1 Янв. 28, 2018 15:12:44

paradox81ru
Зарегистрирован: 2017-03-26
Сообщения: 27
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с Pillow

Привет всем.
Я делаю свою графическую капчу, и для ее реализации требуется пакет для работы с растровой графикой. Самый популярный пакет, это Pillow, и с помощью него я собственно капчу и сделал. И на моем компе в тестовом режиме эта капча вполне себе нормально работала. Но потом пришло время проверить свою капчу на боевом сервере… И вот тут то и возникла проблема, капча не проявилась, а при запросе этой капчи возвращается ошибка 500. Я стал изучать логи вэбсервера, и нашел вот такую интересную ошибку:

 Exception ignored in: <object repr() failed>
Traceback (most recent call last):
  File "D:\pyprojects\aaa-portal\pythonProject\venv\Lib/site-packages\PIL\Image.py", line 587, in __del__
NameError: name 'hasattr' is not defined

Как видно, ошибка весьма нетипичная, так как ссылается на отсутствие встроенной функции ‘hasattr’. Далее я нашел эту же ошибку и на своем тестовом сервере, которая, вправду, в отличии от боевого сервера, все таки не мешает отобразить капчу. Но на боевом сервере картинка не отображается. Попытки найти информацию по данной проблеме на просторах интернета ни к чему не привели, надо сказать что подобный вопрос я вообще нашел всего в двух местах, причем один из них без ответа, а второй ответ не помог. Мои личные поиски решения проблемы так же ни к чему не привели, и в какую сторону копать я уже больше и не знаю.
В связи с вышесказанным, я не очень то уже надеюсь, что кто-то подскажет как решить данную проблему, но мог бы тогда кто-нибудь подсказать какую-нибудь реальную альтернативу пакету Pillow? Только что-бы она работала под питоном 3.6.

Отредактировано paradox81ru (Янв. 28, 2018 15:42:03)

Офлайн

#2 Янв. 28, 2018 15:17:46

FishHook
От:
Зарегистрирован: 2011-01-08
Сообщения: 8312
Репутация: +  568  -
Профиль   Отправить e-mail  

Проблема с Pillow

paradox81ru
Питоний вебсервер под виндой - это странное, очень странное решение, объяснений которому крайне трудно придумать.
Можеть быть вам не искать замену Pillow, а поискать замену Windows?



Офлайн

#3 Янв. 28, 2018 15:21:57

paradox81ru
Зарегистрирован: 2017-03-26
Сообщения: 27
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с Pillow

Это тестовый сервер на Windows, а рабочий на Ubuntu 14.04, еще умные мысли есть?

Офлайн

#4 Янв. 28, 2018 15:26:48

FishHook
От:
Зарегистрирован: 2011-01-08
Сообщения: 8312
Репутация: +  568  -
Профиль   Отправить e-mail  

Проблема с Pillow

paradox81ru
а рабочий на Ubuntu 14.04
Ну и? Есть на нем ошибка? Если нет, то проблему считаю решенной.



Офлайн

#5 Янв. 28, 2018 15:34:24

paradox81ru
Зарегистрирован: 2017-03-26
Сообщения: 27
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с Pillow

FishHook

Ну и? Есть на нем ошибка? Если нет, то проблему считаю решенной.
Я же написал, что ошибка эта есть на обоих серверах, только под виндами капча все таки отображается, а на рабочем возвращает ошибку 500.
Вот лог с убунты, чтобы винды не смущали:
 Exception ignored in: <object repr() failed>
Traceback (most recent call last):
  File "/var/pyprojects/aaa-portal/pythonProject/venv3.6/lib/python3.6/site-packages/PIL/Image.py", line 587, in __del__
NameError: name 'hasattr' is not defined

Офлайн

#6 Янв. 29, 2018 13:44:22

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2750
Репутация: +  184  -
Профиль   Отправить e-mail  

Проблема с Pillow

> “/var/pyprojects/aaa-portal/pythonProject/venv3.6/lib/python3.6/site-packages/PIL/Image.py”, line 587

Ты смотрел что там написано? Какая версия PIL? Где исходник? virtual event использовать не нужно.



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#7 Янв. 29, 2018 14:40:39

paradox81ru
Зарегистрирован: 2017-03-26
Сообщения: 27
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с Pillow

Rodegast

Ты смотрел что там написано? Какая версия PIL? Где исходник? virtual event использовать не нужно.
Да смотрел, проверяется некий атрибут ‘fp’ в своем классе Image, и вроде как не находит, и создает его со значением None. Смотрел это в дебагере в PyCharm.
 class Image(object):
   ...
    if sys.version_info >= (3, 4, 0):
        def __del__(self):
            if (hasattr(self, 'fp') and hasattr(self, '_exclusive_fp')
               and self.fp and self._exclusive_fp):
                self.fp.close()
            self.fp = None
Версия Pillow 5, до этого была 4.x какая-то там, было то же самое, почему и решил попробовать обновить ее до 5 версии, но проблему не решило. А что, виртуальное окружение действительно проблемы может создавать?
https://github.com/python-pillow/Pillow/blob/master/src/PIL/Image.py
Да, так, на всякий случай, Pillow ставил через pip, а не c гитхаба, чтобы чего не подумали.

Отредактировано paradox81ru (Янв. 29, 2018 14:59:08)

Офлайн

#8 Янв. 29, 2018 15:26:00

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2750
Репутация: +  184  -
Профиль   Отправить e-mail  

Проблема с Pillow

> https://github.com/python-pillow/Pillow/blob/master/src/PIL/Image.py

Вообще-то я имел в виду исходник твоей капчи.



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#9 Янв. 29, 2018 15:38:21

paradox81ru
Зарегистрирован: 2017-03-26
Сообщения: 27
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с Pillow

Rodegast

Вообще-то я имел в виду исходник твоей капчи.

 import os
import random
from PIL import Image, ImageDraw, ImageFont, ImageChops, ImageColor, ImageFilter
from django.conf import settings
class Captcha:
    def __init__(self, destination,
                 trash_range=15,
                 code_range=5,
                 offset=50,
                 font_size=40,
                 base_size=(250, 50)):
        self._destination = destination
        self._trash_range = trash_range
        self._code_range = code_range
        self._offset = offset
        self._font_size = font_size
        self._base_size = base_size
        self._letters = self._letters()
        self._font_path = self._font_path()
    @classmethod
    def _random_fill(cls, red=255, green=255, blue=255):
        """ Случайный цвет RGB """
        fill = (random.randrange(red),
                random.randrange(green),
                random.randrange(blue))
        return fill
    @classmethod
    def _random_font(cls, path='fonts', s1=12, s2=35, main=None):
        """ Случайный шрифт """
        if main is None:
            size = random.randrange(s1, s2)
        else:
            size = main
        font_files = os.listdir(path)
        r_font = os.path.join(path, random.choice(font_files))
        font = ImageFont.truetype(r_font, size)
        return font
    @classmethod
    def _random_coords(cls, coords=(250, 50)):
        """ Случайные координаты """
        coords = (random.randrange(0, coords[0]),
                  random.randrange(0, coords[1]))
        return coords
    @classmethod
    def _letters(cls):
        """ Создает и возвращает кортеж символов """
        letters = (
            'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'j', 'k', 'm', 'n',
            'p', 'q', 'r', 's', 't', 'u',
            'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9'
        )
        # или, лучше воспользуемся готовыми наборами:
        # letters = tuple(string.ascii_lowercase + string.digits)
        return letters
    @classmethod
    def _font_path(cls):
        """ Возвращает папку со шрифтами"""
        return os.path.join(settings.BASE_DIR, r'common\helpers\fonts')
    def _background_img(self):
        """ Создает и возвращает фон с мусором """
        img = Image.new("RGBA", self._base_size, 'white')
        img_drw = ImageDraw.Draw(img)
#        self._draw_trash_letters(img_drw)
        self._draw_trash_lines(img_drw)
        del img_drw
        return img
    # Не используется
    def _draw_trash_letters(self, img_drw):
        """ Рисует мусор из светных символов """
        for i in range(self._trash_range):
            img_drw.font = self._random_font(self._font_path)
            img_drw.text(self._random_coords(self._base_size), random.choice(self._letters), self._random_fill())
    def _draw_trash_lines(self, img_drw):
        """
        Рисуте мусор из прямых линий
        :type img_drw: ImageDraw.Draw
        :return:
        """
        img_drw.line(self._rand_line('h'), ImageColor.getrgb("Black"), 2)
        img_drw.line(self._rand_line('h'), ImageColor.getrgb("Black"), 2)
        img_drw.line(self._rand_line('v'), ImageColor.getrgb("Black"), 2)
        img_drw.line(self._rand_line('v'), ImageColor.getrgb("Black"), 2)
    def _rand_line(self, direction):
        """
        Возвращает случайные линии горизонтальные или вертикальные
        :param direction: направление линии 'h' или 'v'
        :return:
        """
        direction = direction.lower()
        if direction not in ('h', 'v'):
            raise ValueError("Координаты могут быть только 'h' или 'v'")
        if direction == 'h':
            max_size = self._base_size[1]
            cord1 = random.randint(1, max_size-2)
            cord2 = random.randint(1, max_size-2)
            line_coord = [0, cord1, 250, cord2]
        else:
            max_size = self._base_size[0]
            cord1 = random.randint(1, max_size-2)
            cord2 = random.randint(1, max_size-2)
            line_coord = [cord1, 0, cord2, 50]
        return line_coord
    def _code_img(self, code):
        """ Создает и возвращает изображение с кодом капчи"""
        img = Image.new("RGBA", self._base_size, 'white')
        d2 = ImageDraw.Draw(img)
        x = 0
        offset = random.randrange(30, self._offset)
        for c in code:
            d2.text((x, 0), c, 'black', font=self._random_font(self._font_path, main=self._font_size))
            x += offset
        # img = im2.filter(ImageFilter.BLUR)
        del d2
        return img
    def captcha(self):
        # Если установлен режим отладки и отключена капча,
        if settings.DEBUG and settings.NO_CAPTCHA:
            # то сделаем капчу равной "12345".
            code = list('12345')
        else:
            # Иначе генерируем случайный набор символов из списка символов в пределах.
            code = [random.choice(self._letters) for i in range(self._code_range)]
        # Создаем первое изображение с мусором — случайными символами
        im1 = self._background_img()
        # Создаем вторую часть для отображения кода
        im2 = self._code_img(code)
        # Накладываем второе изображение на первое.
        img = ImageChops.multiply(im1, im2)
        # Сохраняем изображение
        img.save(self._destination, 'PNG')
        # И возвращаем строку
        return ''.join(code)
    def __call__(self, *args, **kwargs):
        return self.captcha()

Офлайн

#10 Янв. 29, 2018 18:08:05

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2750
Репутация: +  184  -
Профиль   Отправить e-mail  

Проблема с Pillow

1) Не надо пытаться что-то удалить через del. Мусорщик удалит сам то что нужно.
2) Не надо создавать несколько изображений. Т.е. должно быть что-то вроде этого:

 class Captcha:
   .....
    def _draw_trash_lines(self, img_drw):
        """
        Рисуте мусор из прямых линий
        :type img_drw: ImageDraw.Draw
        :return:
        """
        img_drw.line(self._rand_line('h'), ImageColor.getrgb("Black"), 2)
        img_drw.line(self._rand_line('h'), ImageColor.getrgb("Black"), 2)
        img_drw.line(self._rand_line('v'), ImageColor.getrgb("Black"), 2)
        img_drw.line(self._rand_line('v'), ImageColor.getrgb("Black"), 2)
    
    def _code_img(self, code, img_drw):
        """ Создает и возвращает изображение с кодом капчи"""
        x = 0
        offset = random.randrange(30, self._offset)
        for c in code:
            img_drw.text((x, 0), c, 'black', font=self._random_font(self._font_path, main=self._font_size))
            x += offset
        
    def __call__(self):
        code = "".join( random.choice(self._letters) for i in range(self._code_range) )
        # Создаем первое изображение с мусором — случайными символами
        img = Image.new("RGBA", self._base_size, 'white')
        img_drw = ImageDraw.Draw(img)
        self._draw_trash_lines(img_drw)
        # Создаем вторую часть для отображения кода
        self._code_img(code, img_drw)
        img.save(self._destination, 'PNG')
        # И возвращаем строку
        return code



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version