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