Найти - Пользователи
Полная версия: Проблема с Pillow
Начало » Python для экспертов » Проблема с Pillow
1 2
paradox81ru
Код изменил, но результат пока тот же. Вот измененный код:
 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 _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 _draw_code_img(self, img_drw, code):
        """ Создает и возвращает изображение с кодом капчи"""
        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 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)]
        img = Image.new("RGBA", self._base_size, 'white')
        img_drw = ImageDraw.Draw(img)
        # Создаем первое изображение с мусором — случайными символами
        self._draw_trash_lines(img_drw)
        self._draw_code_img(img_drw, code)
        # Сохраняем изображение
        img.save(self._destination, 'PNG')
        # И возвращаем строку
        return ''.join(code)
    def __call__(self, *args, **kwargs):
        return self.captcha()
Хотя реакция как раз на создание Image, если раньше ошибка появлялась парно, то после того как создается один Image, ошибка стала выходить по одному разу.
Rodegast
Странно у меня этой ошибки нет. Попробуй избавится от virtual event. Но перед этим зайди на сервер, запусти python и попробуй ввести что-то вроде hasattr(“”, “upper”).
scidam
У вас вероятно этот случай..

И еще, после img.save() целесообразно вызвать img.close().

paradox81ru
scidam
У вас вероятно этот случай..

И еще, после img.save() целесообразно вызвать img.close().
Да, я видел этот пост, действительно ситуация точно как моя, но решение проблемы там не указано. Там ссылаются на то, что код питона работающий в отдельном потоке, продолжает работать после того, как уничтожается интерпретатор питона при завершении основного. Но, во первых я никакие потоки не создаю, если только они не создаются в пакете Pillow, да и мало чего-то мне вериться, что интерпретатор питона будет уничтожаться, если все еще работает хотя бы один поток, который управляется этим интерпретатором, это уже тогда скорее будет довольно серьезная ошибка интерпретатора. Хотя, как я читал, многопоточность действительно не самая сильная сторона питона. Но, по любому, я сам никакие потоки не создавал.
scidam
Вот пример
как в подобной ситуации переписали __del__ функцию, чтобы она не содержала hasattr.

Я не призываю, однако, изменять Image.py, но попробовать стоит, и если это решит проблему, вполне можно и предложить pull request для __del__ в Image.py, там __del__ небольшой.

Более глубже, почему так, т.е. почему builtins функции недоступны (уже очищены), когда выполняется __del__ , не разбирался.





paradox81ru
scidam
ДА-ДА-ДА!!! Scidam, я Вам премного благодарен, благодаря Вам я все таки решил проблему. Но, все по порядку.
Я попробовал изменить код, как Вы посоветовали, чтобы убрать hasattr. Капча так и не работает, а ошибка в логе стала немного другая:
 Exception ignored in: <object repr() failed>
Traceback (most recent call last):
  File "/var/pyprojects/paradox-portal/pythonProject/venv3.6/lib/python3.6/site-packages/PIL/Image.py", line 593, in __del__
NameError: name 'AttributeError' is not defined
Но принципиально ничего не изменилось. Раньше терял встроенную функцию, теперь теряет встроенный класс исключения.
Но я на этом не остановился, и решил вообще убрать все из __del__, закрыл его просто pass, после чего ошибка в логе пропала, но капча так появляться и не стала. Тогда я решил все таки еще раз запустить рабочий сайт в режиме debug (да, я знаю что этого ни в коем случае делать нельзя, но, во первых сайт у меня в разработке, во вторых он закрыт режимом MAINTENANCE_MODE, т.е. выдает только одну страницу о том что сайт в разработке, а в третьих, зачем вообще этот сайт нужен, если он адекватно не работает ), и наконец то увидел реальную ошибку, оказалось что я просто неправильно указал путь к шрифтам для отображения символьных кодов в капче, слэши поставил не в ту сторону, в сырой строке они экранировались что ли, двойными становились, и если винды это как-то переваривали, то для ubuntu оказалось смертельно. А ведь раньше сайт в debug режиме тоже мне показывал эту лабуду про отсутствующий hasattr. Блин, с института ведь еще помню, что если 2+2=5, то виноват не компьютер, а сам дурак, ищи ошибку. Но с другой стороны, как вот ее искать, если ошибка одна, а показывает совсем другое.
Спасибо большое Scidam, проблема решена.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB