-
Notifications
You must be signed in to change notification settings - Fork 0
/
recipes.py
217 lines (176 loc) · 7.55 KB
/
recipes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
"""ActiveState Code Recipes magics
This magic supplies this functions:
%lookup line magic
It uses current line as arguments for querying ActiveState recipes,
then returns indexed list with up to 10 recipe names
%listing line magic
Listing of recipe by index from lookup results or by id
%place line magic
Places recipe by index from lookup results or by id, on input line
%imply line magic
Imports recipe by index from lookup results or by id, in user namespace
%desc line magic
Prints recipe's description by index from lookup results or by id
As not so obvious feature, there is _recipesmagics object in user_ns
that holds the data from last query, with json kind of structure:
_recipesmagics = {idx: {'title': title, 'id': id}}
"""
#-----------------------------------------------------------------------------
# Copyright (C) 2012-2014, debjan
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with IPython package
#-----------------------------------------------------------------------------
try:
from urllib import quote_plus
from urllib2 import build_opener
except ImportError: # Python 3
from urllib.parse import quote_plus
from urllib.request import build_opener
from IPython.core.magic import Magics, magics_class, line_magic
ns = "{http://www.w3.org/1999/xhtml}"
opener = build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
_loaded = False
@magics_class
class RecipesMagics(Magics):
"""Provides %lookup, %fetch, %imply and %desc magics"""
@line_magic
def lookup(self, line):
"""Searches for ActiveState recipes from supplied terms.
Usage: %lookup <search terms>
Returns: indexed list of recipes, that can be fetched by %fetch magic,
or imported with %imply magic, from returned index in lookup results
"""
if len(line):
_recipesmagics = {}
provider = 'http://www.bing.com/search?q=%s+' % quote_plus(line)
provider += 'python+site:code.activestate.com/recipes'
try:
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, recover=True)
tree = etree.parse(opener.open(provider), parser)
except ImportError: # fallback to SPL parser
import xml.etree.ElementTree as ET
parser = ET.XMLParser()
parser.parser.UseForeignDTD(True)
parser.entity['nbsp'] = u'\u00A0'
tree = ET.parse(opener.open(provider), parser)
except Exception as e:
self.shell.write(header('Exception:') + '%s\n' % e)
base_url = 'http://code.activestate.com/recipes'
path = ".//{0}li//{0}h2/{0}a".format(ns)
for el in [et.get('href') for et in tree.findall(path)]:
sn = el.split('/')[4].split('-')
if sn[0].isdigit():
_recipesmagics[len(_recipesmagics)] = {
'title': ' '.join(sn[1:]),
'id': sn[0]
}
self.shell.push('_recipesmagics')
if self.shell._ofind('_recipesmagics')['found']:
_recipesmagics = self.shell.user_ns['_recipesmagics']
if len(_recipesmagics):
for k, v in _recipesmagics.items():
self.shell.write(header(' %s:' % k, 4) + '%s %s\n' %
(v['title'],
'...' * (int(len(v['title']) / 49))))
else:
self.shell.write('Sorry, no results.')
@line_magic
def listing(self, line):
"""Listing of ActiveState recipe.
Usage: %listing <idx|id>
where "idx" is recipe's index number returned from lookup results
or "id" is recipe ID
Returns: pretty-printed recipe
"""
recipe_code = self.get_recipe(line, 'code')
if recipe_code:
self.shell.write(self.shell.pycolorize(recipe_code.decode('utf-8')))
@line_magic
def imply(self, line):
"""Imports ActiveState recipe.
Usage: %imply <idx|id>
where "idx" is recipe's index number returned from lookup results
or "id" is recipe id
Returns: imported recipe code as "recipe" in user namespace
"""
if self.shell._ofind('recipe')['found']:
msg = header('Warning: ', 9) +\
'"recipe" variable is already initialized. ' +\
'Please delete it before importing recipe.\n'
self.shell.write(msg + header('Suggest: ', 9) + '"del recipe"')
else:
# ActiveState recipe 82234
recipe_code = self.get_recipe(line, 'code')
if recipe_code:
import imp
recipe = imp.new_module('recipe')
try:
exec(recipe_code, recipe.__dict__)
self.shell.push('recipe')
self.shell.write('%s Recipe %s imported as "recipe".' %
(header('Info:', 6), line))
except Exception as e:
msg = header('Error:') +\
'Recipe %s could not be imported\n' % line
self.shell.write(msg + header('Exception:') + '%s\n' % e)
@line_magic
def place(self, line):
"""Places ActiveState recipe on next input line.
Usage: %place <idx|id>
where "idx" is recipe's index number returned from lookup results
or "id" is recipe ID
Returns: Recipe code on input line
"""
recipe_code = self.get_recipe(line, 'code')
if recipe_code:
self.shell.set_next_input(recipe_code)
@line_magic
def desc(self, line):
"""Get ActiveState recipe description
Usage: %desc <idx|id>
where "idx" is recipe's index number returned from lookup results
or "id" is recipe id
Returns: description string
"""
description = self.get_recipe(line, 'description')
if description:
self.shell.write(description)
def get_recipe(self, i, k):
if len(i):
try:
import json
from ast import literal_eval
n = int(literal_eval(i))
if n < 10 and self.shell._ofind('_recipesmagics')['found']:
i = self.shell.user_ns['_recipesmagics'][n]['id']
r = 'http://code.activestate.com/recipes/api/2/recipes/%s/'
req = opener.open(r % i)
try:
enc = req.headers.getparam('charset')
except AttributeError: # Python 3
enc = req.headers.get_content_charset()
return json.loads(req.read().decode(enc))[k].encode('utf-8')
except Exception as e:
msg = header('Error:') +\
'Recipe with index or id as %s, not found.\n' % i
self.shell.write(msg + header('Exception:') + '%s\n' % e)
else:
self.shell.write(usage())
def header(t, x=12):
return('\x1b[1;31m%s\x1b[0m' % t + ' ' * (x - len(t)))
def usage():
return header('%lookup', 10) + '<search term>\n' +\
header('%desc', 10) + '<idx|id>\n' +\
header('%listing', 10) + '<idx|id>\n' +\
header('%place', 10) + '<idx|id>\n' +\
header('%imply', 10) + '<idx|id>\n'
def load_ipython_extension(ip):
global _loaded
if not _loaded:
ip.register_magics(RecipesMagics)
ip.write(usage())
_loaded = True