diff --git a/README.rst b/README.rst index b318956..cda24a4 100644 --- a/README.rst +++ b/README.rst @@ -21,6 +21,8 @@ Example print(error) else: print(reply) + finally: + cb.close() Installing ---------- @@ -49,7 +51,7 @@ Python 3.4.2+. pip install cleverbot.py[async] -This is not required if you already have aiohttp. +This is not required if you already have aiohttp 1.0.0 or later. **Requirements:** @@ -58,13 +60,13 @@ This is not required if you already have aiohttp. **Dependencies:** -- Requests 1.0.0+ -- aiohttp 1.0.0+ (Optional, for asynchronous support.) +- requests 1.0.0+ +- aiohttp 1.0.0+ (Optional, for asynchronous support) Usage ----- -First import the module: +First import the package: .. code:: py @@ -115,7 +117,7 @@ can take significantly longer. -------------- -If something goes wrong with the request, such as an invalid API key an +If something goes wrong with the request, such as an invalid API key, an ``APIError`` will be raised containing the error message or, if you've defined a timeout and you don't get a reply within the defined amount of seconds you'll get a ``Timeout``. @@ -148,22 +150,21 @@ Print out all of the data Cleverbot gained from the previous conversation: print(cb.data) -To access them you can either use them like an attribute or directly get them -from ``cb.data``. +Take note of the ``cs`` key as we'll use it to save the conversation in the +next section. -For example: +To access the data you can either get them from an attribute or directly get +them from ``cb.data``: .. code:: py - cb.output + cb.output == cb.data['output'] - cb.data['output'] - -Take note of the ``cs`` key as we'll use it to save the conversation in the -next section. +However modifying the data with an attribute is only applicable to the +cleverbot state. To get a list of all of the keys' descriptions either take a look at the -``_query`` method's docstring in cleverbot.py or go to the JSON Reply section +``_query`` method's docstring in cleverbot.py or go to the 'JSON Reply' section in `the official Cleverbot API docs `_. -------------- @@ -200,13 +201,11 @@ Or by setting it when creating a new Cleverbot instance: -------------- -If you wish to use ``cleverbot`` as a variable name you can do one of the -following: +When you're all done, close Cleverbot's connection to the API: .. code:: py - import cleverbot as some_other_name - -.. code:: py + cb.close() - from cleverbot import * +This should only be done when you're not going to use the current instance of +Cleverbot anymore. diff --git a/cleverbot/__init__.py b/cleverbot/__init__.py index b3205df..756207f 100644 --- a/cleverbot/__init__.py +++ b/cleverbot/__init__.py @@ -1,4 +1,5 @@ -__version__ = '1.2.2' +__all__ = ['Cleverbot', 'CleverbotError', 'APIError', 'DecodeError', 'Timeout'] +__version__ = '1.3.0' from .cleverbot import Cleverbot from .errors import CleverbotError, APIError, DecodeError, Timeout diff --git a/cleverbot/_async.py b/cleverbot/_async.py index 92c77c3..37e0977 100644 --- a/cleverbot/_async.py +++ b/cleverbot/_async.py @@ -1,8 +1,37 @@ import asyncio + import aiohttp +import requests + +from . import __version__ from .errors import APIError, DecodeError, Timeout +def __init__(self, key, **kwargs): + """Initialize Cleverbot with the given arguments. + + Arguments: + key: The key argument is always required. It is your API key. + cs: The cs argument stands for "cleverbot state". It is the encoded + state of the conversation so far and includes the whole + conversation history up to that point. + timeout: How many seconds to wait for the API to send data before + giving up and raising an error. + loop: The event loop used for asay. + **kwargs: Keyword arguments to pass into requests.Session.get and + aiohttp.ClientSession.get + """ + self.session = requests.Session() + loop = kwargs.pop('loop', asyncio.get_event_loop()) + self.asession = aiohttp.ClientSession(loop=loop) + self.key = key + self.data = {} + if 'cs' in kwargs: + self.data['cs'] = kwargs.pop('cs') + self.timeout = kwargs.pop('timeout', None) + self.kwargs = kwargs + + @asyncio.coroutine def asay(self, text, **vtext): """Talk to Cleverbot asynchronously. @@ -41,9 +70,15 @@ def asay(self, text, **vtext): return (yield from self._aquery(params)) +def close(self): + """Close the connections to the API.""" + self.session.close() + self.asession.close() + + @asyncio.coroutine def _aquery(self, params): - """Get Cleverbot's reply and store it in a dictionary. + """Get Cleverbot's reply and store the data in a dictionary. Keys: cs: State of the conversation so far, which contains an encoded copy of @@ -67,10 +102,14 @@ def _aquery(self, params): that if an interaction didn't occur, the interaction_other will not be defined. """ - session = aiohttp.ClientSession(loop=self.loop) + headers = { + 'User-Agent': 'cleverbot.py/' + __version__ + ' ' + '(+https://github.com/orlnub123/cleverbot.py)' + } try: - reply = yield from session.get( - self.url, params=params, timeout=self.timeout, **self.kwargs) + reply = yield from self.asession.get( + self.url, params=params, headers=headers, timeout=self.timeout, + **self.kwargs) except asyncio.TimeoutError: raise Timeout(self.timeout) else: @@ -84,5 +123,3 @@ def _aquery(self, params): return data['output'] else: raise APIError(data['error'], data['status']) - finally: - session.close() diff --git a/cleverbot/cleverbot.py b/cleverbot/cleverbot.py index 1c6f5a4..3b72076 100644 --- a/cleverbot/cleverbot.py +++ b/cleverbot/cleverbot.py @@ -1,6 +1,8 @@ __all__ = ['Cleverbot'] import requests + +from . import __version__ from .errors import CleverbotError, APIError, DecodeError, Timeout @@ -19,24 +21,32 @@ def __init__(self, key, **kwargs): conversation history up to that point. timeout: How many seconds to wait for the API to send data before giving up and raising an error. - loop: The event loop used for asay. - **kwargs: Keyword arguments to pass into requests.get + **kwargs: Keyword arguments to pass into requests.Session.get """ + self.session = requests.Session() self.key = key self.data = {} if 'cs' in kwargs: self.data['cs'] = kwargs.pop('cs') self.timeout = kwargs.pop('timeout', None) - self.loop = kwargs.pop('loop', None) self.kwargs = kwargs - def __getattr__(self, attr): + def __getattribute__(self, attr): """Allow access to the stored data through attributes.""" try: - return self.data[attr] - except KeyError as error: - pass - object.__getattribute__(self, attr) + return super(Cleverbot, self).__getattribute__(attr) + except AttributeError as error: + try: + return super(Cleverbot, self).__getattribute__('data')[attr] + except KeyError: + raise error + + def __setattr__(self, attr, value): + """Allow modifying the cleverbot state with an attribute.""" + if attr == 'cs': + self.data['cs'] = value + else: + super(Cleverbot, self).__setattr__(attr, value) def say(self, text, **vtext): """Talk to Cleverbot. @@ -77,15 +87,19 @@ def say(self, text, **vtext): return self._query(params) def asay(self, *args, **kwargs): - """Look in _async.py for the actual function.""" + """Look in _async.py for the actual method.""" raise CleverbotError("asay requires aiohttp and Python 3.4.2+") def reset(self): """Reset all of Cleverbot's stored data.""" self.data = {} + def close(self): + """Close the connection to the API.""" + self.session.close() + def _query(self, params): - """Get Cleverbot's reply and store it in a dictionary. + """Get Cleverbot's reply and store the data in a dictionary. Keys: cs: State of the conversation so far, which contains an encoded @@ -113,9 +127,13 @@ def _query(self, params): Note that if an interaction didn't occur, the interaction_other will not be defined. """ + headers = { + 'User-Agent': 'cleverbot.py/' + __version__ + ' ' + '(+https://github.com/orlnub123/cleverbot.py)' + } try: - reply = requests.get( - self.url, params=params, timeout=self.timeout, **self.kwargs) + reply = self.session.get(self.url, params=params, headers=headers, + timeout=self.timeout, **self.kwargs) except requests.Timeout: raise Timeout(self.timeout) else: @@ -131,6 +149,6 @@ def _query(self, params): raise APIError(data['error'], data['status']) try: - from ._async import asay, _aquery + from ._async import __init__, asay, close, _aquery except (ImportError, SyntaxError): pass