Commit 341fcee1 authored by fimap.dev@gmail.com's avatar fimap.dev@gmail.com

Added missing pybing files. Thanks to cmendozabenitez for reporting it. Bug #63 fixed.

parent cc6e986e
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
from bing import Bing
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the Bing class which is used to create and execute queries
against Bing.
"""
import urllib, httplib2
# Issue #1 (http://code.google.com/p/pybing/issues/detail?id=1)
# Python 2.6 has json built in, 2.5 needs simplejson
try: import json
except ImportError: import simplejson as json
from pybing import constants
class Bing(object):
def __init__(self, app_id):
self.app_id = app_id
def search(self, query, source_type=None, api_version=None, extra_params=None, **kwargs):
kwargs.update({
'AppId': self.app_id,
'Version': api_version or constants.API_VERSION,
'Query': query,
'Sources': source_type or constants.DEFAULT_SOURCE_TYPE,
})
if extra_params:
kwargs.update(extra_params)
query_string = urllib.urlencode(kwargs)
response, contents = httplib2.Http().request(constants.JSON_ENDPOINT + '?' + query_string)
return json.loads(contents)
def search_web(self, query, params):
return self.search(query, source_type=constants.WEB_SOURCE_TYPE, extra_params=params)
def search_image(self, query):
return self.search(query, source_type=constants.IMAGE_SOURCE_TYPE)
def search_news(self, query):
return self.search(query, source_type=constants.NEWS_SOURCE_TYPE)
def search_spell(self, query):
return self.search(query, source_type=constants.SPELL_SOURCE_TYPE)
def search_related(self, query):
return self.search(query, source_type=constants.RELATED_SOURCE_TYPE)
def search_phonebook(self, query):
return self.search(query, source_type=constants.PHONEBOOK_SOURCE_TYPE)
def search_answers(self, query):
return self.search(query, source_type=constants.ANSWERS_SOURCE_TYPE)
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the any constants used when querying Bing.
"""
API_VERSION = '2.0'
JSON_ENDPOINT = 'http://api.search.live.net/json.aspx'
MAX_PAGE_SIZE = 50
MAX_RESULTS = 1000
WEB_SOURCE_TYPE = 'Web'
IMAGE_SOURCE_TYPE = 'Image'
NEWS_SOURCE_TYPE = 'News'
SPELL_SOURCE_TYPE = 'Spell'
RELATED_SOURCE_TYPE = 'RelatedSearch'
PHONEBOOK_SOURCE_TYPE = 'Phonebook'
ANSWERS_SOURCE_TYPE = 'InstanceAnswer'
SOURCE_TYPES = (
WEB_SOURCE_TYPE,
IMAGE_SOURCE_TYPE,
NEWS_SOURCE_TYPE,
SPELL_SOURCE_TYPE,
RELATED_SOURCE_TYPE,
PHONEBOOK_SOURCE_TYPE,
ANSWERS_SOURCE_TYPE,
)
DEFAULT_SOURCE_TYPE = WEB_SOURCE_TYPE
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
# Mixins
from mixin import QueryMixin
from pagable import Pagable
# Base Query
from query import BingQuery
# Concrete Queries
from web import WebQuery
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the QueryMixin base class used for all queries.
"""
class QueryMixin(object):
"""
Any methods that might be mixed into queries should extend this
base class.
"""
def get_request_parameters(self):
params = {}
# Since we're mixing in, super() may or may not have the attribute
sup = super(QueryMixin, self)
if hasattr(sup, 'get_request_parameters'):
params = sup.get_request_parameters()
return params
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds a mixin to specify a query class you can page through
using the count and offset parameter.
"""
from mixin import QueryMixin
class Pagable(QueryMixin):
"""
This class is a mixin used with BingQuery classes to specify that
queries can be paged through using the offset and count parameters.
Some examples of Pagable requests are WebRequests and VideoRequests.
Some non-Pagable requests are TranslationRequests and SearchRequests with
the Spell source type.
From the Bing API:
- Count specifies the number of results to return per Request.
- Offset specifies the offset requested, from zero, for the starting
point of the result set to be returned for this Request.
Note: This mixin currently supports only a single Source Type query.
"""
def __init__(self, *args, **kwargs):
self._count = None
self._offset = 0
super(Pagable, self).__init__(*args, **kwargs)
def execute(self, *args, **kwargs):
if self.count and self.offset and self.count + self.offset > 1000:
raise ValueError, "Count + Offset must be less than 1000"
super(Pagable, self).execute(*args, **kwargs)
def get_request_parameters(self):
params = super(Pagable, self).get_request_parameters()
if self.count:
params['%s.Count' % self.SOURCE_TYPE] = self.count
if self.offset:
params['%s.Offset' % self.SOURCE_TYPE] = self.offset
return params
@property
def count(self):
return self._count
def set_count(self, value):
if value is not None:
if value < 1:
raise ValueError, 'Count must be positive'
elif value > 50:
raise ValueError, 'Count must be less than 50'
obj = self._clone()
obj._count = value
return obj
@property
def offset(self):
return self._offset
def set_offset(self, value):
if value < 0:
raise ValueError, 'Offset must be positive'
elif value > 1000:
raise ValueError, 'Offset must be less than 1000'
obj = self._clone()
obj._offset = value
return obj
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the base Query class used by the various types of Bing queries.
"""
import copy, urllib, httplib2
# Issue #1 (http://code.google.com/p/pybing/issues/detail?id=1)
# Python 2.6 has json built in, 2.5 needs simplejson
try: import json
except ImportError: import simplejson as json
from pybing import constants
from pybing.query.mixin import QueryMixin
class BingQuery(QueryMixin):
SOURCE_TYPE = None
def __init__(self, app_id, query=None, version=None, *args, **kwargs):
self.app_id = app_id
self.version = version or constants.API_VERSION
self._query = query
# Needed for mixin's __init__'s to be called.
super(BingQuery, self).__init__(*args, **kwargs)
def set_query(self, query):
if not query:
raise ValueError, 'Query cannot be empty or None'
obj = self._clone()
obj._query = query
return obj
@property
def query(self):
return self._query
def execute(self):
if not self.query:
raise ValueError, 'Query cannot be empty or None'
elif not self.SOURCE_TYPE:
raise ValueError, 'Source Type cannot be empty or None'
from pybing.resultset import BingResultSet
return BingResultSet(self)
def get_request_parameters(self):
params = super(BingQuery, self).get_request_parameters()
params.update({
'AppId': self.app_id,
'Version': self.version,
'Query': self.query,
'Sources': self.SOURCE_TYPE,
})
return params
def get_request_url(self):
query_string = urllib.urlencode(self.get_request_parameters())
return constants.JSON_ENDPOINT + '?' + query_string
def get_search_response(self):
contents = self._get_url_contents(self.get_request_url())
return json.loads(contents)['SearchResponse'][self.SOURCE_TYPE]
def get_search_results(self):
from pybing.result import BingResult
response = self.get_search_response()
return [BingResult(result) for result in response['Results']]
def _get_url_contents(self, url):
response, contents = httplib2.Http().request(url)
return contents
def _clone(self):
"""
Do a deep copy of this object returning a clone that can be
modified without affecting the old copy.
"""
return copy.deepcopy(self)
def __unicode__(self):
return 'BingQuery: %s' % self.get_request_url()
__str__ = __unicode__
def __repr__(self):
return '<%s>' % unicode(self)
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the Bing WebQuery class used to do web searches against Bing.
"""
from pybing import constants
from pybing.query import BingQuery, Pagable
class WebQuery(BingQuery, Pagable):
SOURCE_TYPE = constants.WEB_SOURCE_TYPE
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the base BingResult class.
"""
class BingResult(object):
"""
The base BingResult class corresponds to a single result from a Bing
Query response.
"""
def __init__(self, result):
if isinstance(result, dict):
self.load_from_dict(result)
else:
raise TypeError, 'Invalid result type'
def load_from_dict(self, data):
for key, value in data.iteritems():
setattr(self, key.lower(), value)
def __repr__(self):
return '<BingResult>'
# This file is part of PyBing (http://pybing.googlecode.com).
#
# Copyright (C) 2009 JJ Geewax http://geewax.org/
# All rights reserved.
#
# This software is licensed as described in the file COPYING.txt,
# which you should have received as part of this distribution.
"""
This module holds the logic for dealing with a set of results from a query.
"""
from pybing import constants
from pybing.query import BingQuery, Pagable
class BingResultSet(object):
"""
This class corresponds to a set of results from a BingQuery.
"""
def __init__(self, query, offset=0, count=None):
if not isinstance(query, BingQuery):
raise TypeError, 'query must be a BingQuery instance'
self.query = query
self.results = {}
# These offset + count are used internally to signify whether or
# not the query should be cut down (whether they've been sliced).
self.offset, self.count = offset, count
def get_offset(self, index=0):
return self.query.offset + self.offset + index
def __getitem__(self, key):
"""
Allows you to grab an index or slice a query with array notation like
resultset[4] or resultset[0:4]
"""
if not isinstance(self.query, Pagable):
raise TypeError, 'Array access only supported on Pagable Queries'
if isinstance(key, int):
absolute_index = self.get_offset()
if absolute_index < 0 or absolute_index >= constants.MAX_RESULTS:
raise IndexError
if absolute_index not in self.results:
# Make a copy of the query for only this one result:
query = self.query.set_offset(absolute_index).set_count(1)
results = query.get_search_results()
if results:
self.results[absolute_index] = results[0]
return self.results.get(absolute_index)
elif isinstance(key, slice):
# Return a new result set that is sliced internally (not the query)
offset = key.start or 0
if key.stop: count = key.stop - offset
else: count = None
return BingResultSet(self.query, self.offset + offset, count)
else:
raise TypeError
def __len__(self):
"""
Returns the number of results if you were to iterate over this result set.
This is at least 0 and at most 1000.
"""
count = constants.MAX_RESULTS
if self.count:
count = self.count
elif self.query.count:
count = self.query.count
if count > constants.MAX_RESULTS:
count = constants.MAX_RESULTS
if count == constants.MAX_RESULTS:
count = count - self.get_offset()
return count
def __iter__(self):
"""
Allows you to iterate over the search results in the standard Python
format such as
for result in my_query.execute():
print result.title, result.url
"""
query = self.query.set_offset(self.get_offset())
end_index = constants.MAX_RESULTS
# If we've internally sliced out items
if self.count:
query = query.set_count(self.count if self.count < constants.MAX_PAGE_SIZE else constants.MAX_PAGE_SIZE)
end_index = self.get_offset() + self.count
if end_index > constants.MAX_RESULTS:
end_index = constants.MAX_RESULTS
# If we want to just go until the end, grab them the most per page
if not query.count:
query.set_count(constants.MAX_PAGE_SIZE)
while query.offset < end_index:
# If we don't have a full page left, only grab up to the end
count = end_index - query.offset
if count and count < constants.MAX_PAGE_SIZE:
query = query.set_count(count)
# Yield back each result
for result in query.get_search_results():
yield result
# Update the offset to move onto the next page
query = query.set_offset(query.offset + query.count)
def __repr__(self):
return '<BingResultSet (%s)>' % self.query
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment