Форум сайта python.su
Привет всем.
Я делаю свою графическую капчу, и для ее реализации требуется пакет для работы с растровой графикой. Самый популярный пакет, это 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
Отредактировано paradox81ru (Янв. 28, 2018 15:42:03)
Офлайн
paradox81ru
Питоний вебсервер под виндой - это странное, очень странное решение, объяснений которому крайне трудно придумать.
Можеть быть вам не искать замену Pillow, а поискать замену Windows?
Офлайн
Это тестовый сервер на Windows, а рабочий на Ubuntu 14.04, еще умные мысли есть?
Офлайн
paradox81ruНу и? Есть на нем ошибка? Если нет, то проблему считаю решенной.
а рабочий на Ubuntu 14.04
Офлайн
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
Офлайн
> “/var/pyprojects/aaa-portal/pythonProject/venv3.6/lib/python3.6/site-packages/PIL/Image.py”, line 587
Ты смотрел что там написано? Какая версия PIL? Где исходник? virtual event использовать не нужно.
Офлайн
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
Отредактировано paradox81ru (Янв. 29, 2018 14:59:08)
Офлайн
> https://github.com/python-pillow/Pillow/blob/master/src/PIL/Image.py
Вообще-то я имел в виду исходник твоей капчи.
Офлайн
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()
Офлайн
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
Офлайн