Skip to content

Instantly share code, notes, and snippets.

@sbp
Created August 14, 2012 15:46
Show Gist options
  • Save sbp/3350409 to your computer and use it in GitHub Desktop.
Save sbp/3350409 to your computer and use it in GitHub Desktop.
Services sketch, intended for a phenny-like meta-service
# remote python execution
# wolfram alpha service
# remote services?
import re, urllib.request, urllib.parse, traceback
from html.entities import name2codepoint
def error(who, message):
output = {"who": who, "message": message}
stack = traceback.extract_stack()
caller = stack[-2]
output["caller"] = caller[0] + ":" + str(caller[1])
return output
def web_get(url, data=None, headers=None):
output = {}
if data is not None:
data = urllib.parse.urlencode(data)
url = "?".join((url, data))
if headers is None:
headers = {}
if not "User-Agent" in headers:
headers["User-Agent"] = "Mozilla/5.0 (Services)"
output["url"] = url
request = urllib.request.Request(url, headers=headers)
try: response = urllib.request.urlopen(request)
except HTTPError as e:
output["error"] = error("Server (%s)" % url, "HTTP Error %s" % e.code)
except URLError as e:
output["error"] = error("Network", "Reason given: %s" % e.reason)
else:
octets = response.read()
response.close()
output["octets"] = octets
return output
web = type("Web", (object,), {"get": web_get})
del web_get
services = {}
def decode_entities(text):
def entity(match):
name = match.group(1).lower()
if name.startswith("#x"):
return chr(int(name[2:], 16))
elif name.startswith("#"):
return chr(int(name[1:]))
elif name in name2codepoint:
return name2codepoint[name]
return '[' + value + ']'
return re.compile(r'&([^;\s]+);').sub(entity, text)
def service(name):
def wrapper(service_function):
def wrapped_service(input):
# try:
output = service_function(input, {})
# except Exception as e:
# output = {"error": str(e)}
return output
services[name] = wrapped_service
return wrapper
@service("Google calculator")
def _(input, output):
"""
"expression": "1 + 2"
=>
'url': 'http://www.google.com/ig/calculator?q=1+%2B+2',
'expression': '1 + 2',
'expression_substituted': '1 + 2',
'response': "'3'"
"""
substitutions = {
"ϕ": "phi",
"π": "pi"
}
expression = input["expression"]
output["expression"] = expression
for a, b in substitutions.items():
expression = expression.replace(a, b)
output["expression_substituted"] = expression
calculator = "http://www.google.com/ig/calculator"
response = web.get(calculator, {"q": expression})
output["url"] = response["url"]
if "error" in response:
output["error"] = response["error"]
return output
text = response["octets"].decode("utf-8")
output["text"] = text
def parse(text):
text = text.strip("{}")
r_entry = re.compile(r"(\w+):\s*\"([^\"]*)\",?\s*")
while text:
m = r_entry.match(text)
if not m: break
yield m.groups()
text = text[m.end():]
fields = dict(parse(text))
output["google_left"] = fields.get("lhs")
output["google_right"] = fields.get("rhs")
if fields.get("error"):
output["error"] = error("Google", "The web service probably considers the input malformed")
return output
right = fields.get("rhs", "")
if right:
right = right.encode("iso-8859-1")
right = right.decode("unicode-escape")
substitutions = {
"<sup>": "^(",
"</sup>": ")",
"\xA0": "," # nbsp
}
for a, b in substitutions.items():
right = right.replace(a, b)
right = decode_entities(right)
output["response"] = right
else: output["error"] = error("Google", "The response had no 'rhs' field. The input may be malformed")
return output
del _
print(services["Google calculator"]({"expression": "(5 * 2 km/sec**2) in miles/hr**2\""}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment