From 959afbd350db1c329f101edd18c9bbbc46aa8d30 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 22 Feb 2012 12:06:00 +0530 Subject: [PATCH] News download system: Allow use of __future__ in recipes, and do not change line numbers of code in the recipe when compiling it --- src/calibre/web/feeds/recipes/__init__.py | 60 +++++++++-------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index bfb46fa799..5d5b81004c 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -4,20 +4,14 @@ __copyright__ = '2008, Kovid Goyal ' ''' Builtin recipes. ''' -import re, imp, inspect, time, os -from calibre.web.feeds.news import BasicNewsRecipe, CustomIndexRecipe, \ - AutomaticNewsRecipe, CalibrePeriodical +import re, time, io +from calibre.web.feeds.news import (BasicNewsRecipe, CustomIndexRecipe, + AutomaticNewsRecipe, CalibrePeriodical) from calibre.ebooks.BeautifulSoup import BeautifulSoup -from calibre.ptempfile import PersistentTemporaryDirectory -from calibre import __appname__, english_sort from calibre.utils.config import JSONConfig -BeautifulSoup, time, english_sort - basic_recipes = (BasicNewsRecipe, AutomaticNewsRecipe, CustomIndexRecipe, CalibrePeriodical) -_tdir = None -_crep = 0 custom_recipes = JSONConfig('custom_recipes/index.json') @@ -28,39 +22,33 @@ def custom_recipe_filename(id_, title): def compile_recipe(src): ''' - Compile the code in src and return the first object that is a recipe or profile. - @param src: Python source code - @type src: string - @return: Recipe class or None, if no such class was found in C{src} + Compile the code in src and return a recipe object, if found. + + :param src: Python source code as bytestring or unicode object + + :return: Recipe class or None, if no such class was found in src ''' - global _tdir, _crep - if _tdir is None or not os.path.exists(_tdir): - _tdir = PersistentTemporaryDirectory('_recipes') - temp = os.path.join(_tdir, 'recipe%d.py'%_crep) - _crep += 1 if not isinstance(src, unicode): match = re.search(r'coding[:=]\s*([-\w.]+)', src[:200]) enc = match.group(1) if match else 'utf-8' src = src.decode(enc) - src = re.sub(r'from __future__.*', '', src) - f = open(temp, 'wb') - src = 'from %s.web.feeds.news import BasicNewsRecipe, AutomaticNewsRecipe\n'%__appname__ + src - src = '# coding: utf-8\n' + src - src = 'from __future__ import with_statement\n' + src + # Python complains if there is a coding declaration in a unicode string + src = re.sub(r'^#.*coding\s*[:=]\s*([-\w.]+)', '#', src, flags=re.MULTILINE) + # Translate newlines to \n + src = io.StringIO(src, newline=None).getvalue() - src = src.replace('from libprs500', 'from calibre').encode('utf-8') - f.write(src) - f.close() - module = imp.find_module(os.path.splitext(os.path.basename(temp))[0], - [os.path.dirname(temp)]) - module = imp.load_module(os.path.splitext(os.path.basename(temp))[0], *module) - classes = inspect.getmembers(module, - lambda x : inspect.isclass(x) and \ - issubclass(x, (BasicNewsRecipe,)) and \ - x not in basic_recipes) - if not classes: - return None + namespace = { + 'BasicNewsRecipe':BasicNewsRecipe, + 'AutomaticNewsRecipe':AutomaticNewsRecipe, + 'time':time, 're':re, + 'BeautifulSoup':BeautifulSoup + } + exec src in namespace - return classes[0][1] + for x in namespace.itervalues(): + if (isinstance(x, type) and issubclass(x, BasicNewsRecipe) and x not + in basic_recipes): + return x + return None