Repository: pika/pika

Issue:
1.0.0 API changes could raise helpful exceptions
If an API change resulted in a parameter order change where the user could pass a callable where it is not intended, Pika could raise `TypeError` with a helpful message.

See the discussion at the end of issues #963 and #945

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index e6ec95e..238fa6f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,13 +1,34 @@
 Version History
 ===============
 
-1.0.0b1 2018-12-31
+1.0.0b2 2019-01-22
+------------------
 
 `GitHub milestone <https://github.com/pika/pika/milestone/8>`_
 
-- `AsyncioConnection`, `TornadoConnection` and `TwistedProtocolConnection` are no longer auto-imported (`PR <https://github.com/pika/pika/pull/1129>`_)
-- `BlockingConnection.consume` now returns `(None, None, None)` when inactivity timeout is reached (`PR <https://github.com/pika/pika/pull/899>`_)
-- Python `3.7` support (`Issue <https://github.com/pika/pika/issues/1107>`_)
+- ``AsyncioConnection``, ``TornadoConnection`` and ``TwistedProtocolConnection`` are no longer auto-imported (`PR <https://github.com/pika/pika/pull/1129>`_)
+- ``BlockingConnection.consume`` now returns ``(None, None, None)`` when inactivity timeout is reached (`PR <https://github.com/pika/pika/pull/899>`_)
+- Python 3.7 support (`Issue <https://github.com/pika/pika/issues/1107>`_)
+
+
+**IMPORTANT**: The signature of the following methods has changed from Pika 0.13.0. In general, the callback parameter that indicates completion of the method has been moved to the end of the parameter list to be consistent with other parts of Pika's API and with other libraries in general.
+
+- ``basic_cancel``
+- ``basic_consume``
+- ``basic_get``
+- ``basic_qos``
+- ``basic_recover``
+- ``confirm_delivery``
+- ``exchange_bind``
+- ``exchange_declare``
+- ``exchange_delete``
+- ``exchange_unbind``
+- ``flow``
+- ``queue_bind``
+- ``queue_declare``
+- ``queue_delete``
+- ``queue_purge``
+- ``queue_unbind``
 
 0.12.0 2018-06-19
 -----------------
diff --git a/docs/examples/tls_mutual_authentication.rst b/docs/examples/tls_mutual_authentication.rst
index 614b5bc..8a68e19 100644
--- a/docs/examples/tls_mutual_authentication.rst
+++ b/docs/examples/tls_mutual_authentication.rst
@@ -1,7 +1,7 @@
 TLS parameters example
 ======================
 
-This example demonstrates a TLS session with RabbitMQ using mutual authentication (server and client authentication). It was tested against RabbitMQ 3.7.4, using Python 3.6.5 and Pika 1.0.0b1.
+This example demonstrates a TLS session with RabbitMQ using mutual authentication (server and client authentication). It was tested against RabbitMQ 3.7.4, using Python 3.6.5 and Pika 1.0.0.
 
 See `the RabbitMQ TLS/SSL documentation <https://www.rabbitmq.com/ssl.html>`_ for certificate generation and RabbitMQ TLS configuration. Please note that the `RabbitMQ TLS (x509 certificate) authentication mechanism <https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl>`_ must be enabled for these examples to work.
 
diff --git a/docs/version_history.rst b/docs/version_history.rst
index 44a2063..2fe388f 100644
--- a/docs/version_history.rst
+++ b/docs/version_history.rst
@@ -1,14 +1,34 @@
 Version History
 ===============
 
-1.0.0b1 2018-12-31
+1.0.0b2 2019-01-22
 ------------------
 
 `GitHub milestone <https://github.com/pika/pika/milestone/8>`_
 
-- `AsyncioConnection`, `TornadoConnection` and `TwistedProtocolConnection` are no longer auto-imported (`PR <https://github.com/pika/pika/pull/1129>`_)
-- `BlockingConnection.consume` now returns `(None, None, None)` when inactivity timeout is reached (`PR <https://github.com/pika/pika/pull/899>`_)
-- Python `3.7` support (`Issue <https://github.com/pika/pika/issues/1107>`_)
+- ``AsyncioConnection`, ``TornadoConnection`` and ``TwistedProtocolConnection`` are no longer auto-imported (`PR <https://github.com/pika/pika/pull/1129>`_)
+- ``BlockingConnection.consume`` now returns ``(None, None, None)`` when inactivity timeout is reached (`PR <https://github.com/pika/pika/pull/899>`_)
+- Python 3.7 support (`Issue <https://github.com/pika/pika/issues/1107>`_)
+
+
+**IMPORTANT**: The signature of the following methods has changed from Pika 0.13.0. In general, the callback parameter that indicates completion of the method has been moved to the end of the parameter list to be consistent with other parts of Pika's API and with other libraries in general.
+
+- ``basic_cancel``
+- ``basic_consume``
+- ``basic_get``
+- ``basic_qos``
+- ``basic_recover``
+- ``confirm_delivery``
+- ``exchange_bind``
+- ``exchange_declare``
+- ``exchange_delete``
+- ``exchange_unbind``
+- ``flow``
+- ``queue_bind``
+- ``queue_declare``
+- ``queue_delete``
+- ``queue_purge``
+- ``queue_unbind``
 
 0.12.0 2018-06-19
 -----------------
diff --git a/pika/__init__.py b/pika/__init__.py
index f06e71a..d244dff 100644
--- a/pika/__init__.py
+++ b/pika/__init__.py
@@ -1,4 +1,4 @@
-__version__ = '1.0.0b1'
+__version__ = '1.0.0b2'
 
 import logging
 
diff --git a/pika/adapters/blocking_connection.py b/pika/adapters/blocking_connection.py
index cfac55f..fd48e94 100644
--- a/pika/adapters/blocking_connection.py
+++ b/pika/adapters/blocking_connection.py
@@ -24,14 +24,11 @@ import logging
 import threading
 import time
 
-import pika.channel
-
-from pika.adapters.utils import connection_workflow
-from pika import compat
-import pika.connection
-from pika import exceptions
-
+import pika.compat as compat
+import pika.exceptions as exceptions
 import pika.spec
+import pika.validators as validators
+from pika.adapters.utils import connection_workflow
 
 # NOTE: import SelectConnection after others to avoid circular depenency
 from pika.adapters import select_connection
@@ -706,9 +703,7 @@ class BlockingConnection(object):
         :returns: opaque timer id
 
         """
-        if not callable(callback):
-            raise TypeError(
-                'callback must be a callable, but got %r' % (callback,))
+        validators.require_callback(callback)
 
         evt = _TimerEvt(callback=callback)
         timer_id = self._impl._adapter_add_timeout(
@@ -1642,10 +1637,8 @@ class BlockingChannel(object):
             consumer_tag is already present.
 
         """
-        if not callable(on_message_callback):
-            raise ValueError('callback on_message_callback must be callable; got %r'
-                             % on_message_callback)
-
+        validators.require_string(queue, 'queue')
+        validators.require_callback(on_message_callback, 'on_message_callback')
         return self._basic_consume_impl(
             queue=queue,
             on_message_callback=on_message_callback,
@@ -2169,6 +2162,8 @@ class BlockingChannel(object):
         """
         assert not self._basic_getempty_result
 
+        validators.require_string(queue, 'queue')
+
         # NOTE: nested with for python 2.6 compatibility
         with _CallbackResult(self._RxMessageArgs) as get_ok_result:
             with self._basic_getempty_result:
@@ -2427,6 +2422,7 @@ class BlockingChannel(object):
           `spec.Exchange.DeclareOk`
 
         """
+        validators.require_string(exchange, 'exchange')
         with _CallbackResult(
             self._MethodFrameCallbackResultArgs) as declare_ok_result:
             self._impl.exchange_declare(
@@ -2549,6 +2545,7 @@ class BlockingChannel(object):
           `spec.Queue.DeclareOk`
 
         """
+        validators.require_string(queue, 'queue')
         with _CallbackResult(self._MethodFrameCallbackResultArgs) as \
                 declare_ok_result:
             self._impl.queue_declare(
@@ -2621,6 +2618,8 @@ class BlockingChannel(object):
           `spec.Queue.BindOk`
 
         """
+        validators.require_string(queue, 'queue')
+        validators.require_string(exchange, 'exchange')
         with _CallbackResult(
             self._MethodFrameCallbackResultArgs) as bind_ok_result:
             self._impl.queue_bind(queue=queue,
diff --git a/pika/channel.py b/pika/channel.py
index 49b40dc..2b7c63d 100644
--- a/pika/channel.py
+++ b/pika/channel.py
@@ -12,7 +12,8 @@ import uuid
 import pika.frame as frame
 import pika.exceptions as exceptions
 import pika.spec as spec
-from pika.compat import unicode_type, dictkeys, is_integer
+import pika.validators as validators
+from pika.compat import basestring, unicode_type, dictkeys, is_integer
 
 
 LOGGER = logging.getLogger(__name__)
@@ -59,7 +60,7 @@ class Channel(object):
         if not isinstance(channel_number, int):
             raise exceptions.InvalidChannelNumber
 
-        self._validate_rpc_completion_callback(on_open_callback)
+        validators.rpc_completion_callback(on_open_callback)
 
         self.channel_number = channel_number
         self.callbacks = connection.callbacks
@@ -222,8 +223,9 @@ class Channel(object):
         :raises ValueError:
 
         """
+        validators.require_string(consumer_tag, 'consumer_tag')
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
 
         if consumer_tag in self._cancelled:
             # We check for cancelled first, because basic_cancel removes
@@ -301,9 +303,10 @@ class Channel(object):
         :raises ValueError:
 
         """
-        self._require_callback(on_message_callback)
+        validators.require_string(queue, 'queue')
+        validators.require_callback(on_message_callback)
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.rpc_completion_callback(callback)
 
         # If a consumer tag was not passed, create one
         if not consumer_tag:
@@ -365,7 +368,8 @@ class Channel(object):
         :raises ValueError:
 
         """
-        self._require_callback(callback)
+        validators.require_string(queue, 'queue')
+        validators.require_callback(callback)
         if self._on_getok_callback is not None:
             raise exceptions.DuplicateGetOkCallback()
         self._on_getok_callback = callback
@@ -466,9 +470,9 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
-        self._validate_zero_or_greater('prefetch_size', prefetch_size)
-        self._validate_zero_or_greater('prefetch_count', prefetch_count)
+        validators.rpc_completion_callback(callback)
+        validators.zero_or_greater('prefetch_size', prefetch_size)
+        validators.zero_or_greater('prefetch_count', prefetch_count)
         return self._rpc(spec.Basic.Qos(prefetch_size, prefetch_count,
                                         all_channels),
                          callback, [spec.Basic.QosOk])
@@ -506,7 +510,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.rpc_completion_callback(callback)
         return self._rpc(spec.Basic.Recover(requeue), callback,
                          [spec.Basic.RecoverOk])
 
@@ -574,7 +578,7 @@ class Channel(object):
                              'to receieve Basic.Ack/Basic.Nack notifications')
 
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
 
         if not (self.connection.publisher_confirms and
                 self.connection.basic_nack):
@@ -619,7 +623,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
         return self._rpc(spec.Exchange.Bind(0, destination, source, routing_key,
                                             nowait, arguments or dict()),
                          callback, [spec.Exchange.BindOk] if not nowait
@@ -658,8 +662,9 @@ class Channel(object):
         :raises ValueError:
 
         """
+        validators.require_string(exchange, 'exchange')
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
         return self._rpc(spec.Exchange.Declare(0, exchange, exchange_type,
                                                passive, durable, auto_delete,
                                                internal, nowait,
@@ -681,7 +686,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
         return self._rpc(spec.Exchange.Delete(0, exchange, if_unused, nowait),
                          callback, [spec.Exchange.DeleteOk] if not nowait
                          else [])
@@ -706,7 +711,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
         return self._rpc(spec.Exchange.Unbind(0, destination, source,
                                               routing_key, nowait, arguments),
                          callback,
@@ -727,7 +732,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.rpc_completion_callback(callback)
         self._on_flowok_callback = callback
         self._rpc(spec.Channel.Flow(active), self._on_flowok,
                   [spec.Channel.FlowOk])
@@ -785,8 +790,10 @@ class Channel(object):
         :raises ValueError:
 
         """
+        validators.require_string(queue, 'queue')
+        validators.require_string(exchange, 'exchange')
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
         if routing_key is None:
             routing_key = queue
         return self._rpc(spec.Queue.Bind(0, queue, exchange, routing_key,
@@ -823,8 +830,9 @@ class Channel(object):
         :raises ValueError:
 
         """
+        validators.require_string(queue, 'queue')
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        nowait = validators.rpc_completion_callback(callback)
 
         if queue:
             condition = (spec.Queue.DeclareOk, {'queue': queue})
@@ -853,7 +861,8 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        validators.require_string(queue, 'queue')
+        nowait = validators.rpc_completion_callback(callback)
         replies = [spec.Queue.DeleteOk] if not nowait else []
         return self._rpc(spec.Queue.Delete(0, queue, if_unused, if_empty,
                                            nowait),
@@ -869,7 +878,8 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        nowait = self._validate_rpc_completion_callback(callback)
+        validators.require_string(queue, 'queue')
+        nowait = validators.rpc_completion_callback(callback)
         replies = [spec.Queue.PurgeOk] if not nowait else []
         return self._rpc(spec.Queue.Purge(0, queue, nowait), callback, replies)
 
@@ -893,7 +903,8 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.require_string(queue, 'queue')
+        validators.rpc_completion_callback(callback)
         if routing_key is None:
             routing_key = queue
         return self._rpc(spec.Queue.Unbind(0, queue, exchange, routing_key,
@@ -908,7 +919,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.rpc_completion_callback(callback)
         return self._rpc(spec.Tx.Commit(), callback, [spec.Tx.CommitOk])
 
     def tx_rollback(self, callback=None):
@@ -919,7 +930,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.rpc_completion_callback(callback)
         return self._rpc(spec.Tx.Rollback(), callback, [spec.Tx.RollbackOk])
 
     def tx_select(self, callback=None):
@@ -932,7 +943,7 @@ class Channel(object):
 
         """
         self._raise_if_not_open()
-        self._validate_rpc_completion_callback(callback)
+        validators.rpc_completion_callback(callback)
         return self._rpc(spec.Tx.Select(), callback, [spec.Tx.SelectOk])
 
     # Internal methods
@@ -1443,48 +1454,6 @@ class Channel(object):
         """
         LOGGER.error('Unexpected frame: %r', frame_value)
 
-    @staticmethod
-    def _require_callback(callback):
-        """Require that callback is callable and is not None
-
-        :raises: TypeError
-
-        """
-        if not callable(callback):
-            raise TypeError(
-                'Callback must be callable, but got %r' % (callback,))
-
-    @staticmethod
-    def _validate_rpc_completion_callback(callback):
-        """Verify callback is callable if not None
-
-        :returns: boolean indicating nowait
-        :raises: TypeError
-
-        """
-        if callback is None:
-            # No callback means we will not expect a response
-            # i.e. nowait=True
-            return True
-
-        if callable(callback):
-            # nowait=False
-            return False
-        else:
-            raise TypeError(
-                'Completion callback must be callable if not None')
-
-    def _validate_zero_or_greater(self, name, value):
-        """Verify that value is zero or greater. If not, 'name'
-        will be used in error message
-
-        :raises: ValueError
-
-        """
-        if int(value) < 0:
-            errmsg = '{} must be >= 0, but got {}'.format(name, value)
-            raise ValueError(errmsg)
-
 
 class ContentFrameAssembler(object):
     """Handle content related frames, building a message and return the message
diff --git a/pika/compat.py b/pika/compat.py
index d99341e..041a73f 100644
--- a/pika/compat.py
+++ b/pika/compat.py
@@ -5,7 +5,7 @@ import re
 import socket
 import sys as _sys
 
-PY2 = _sys.version_info < (3,)
+PY2 = _sys.version_info.major == 2
 PY3 = not PY2
 RE_NUM = re.compile(r'(\d+).+')
 
@@ -29,7 +29,7 @@ try:
 except AttributeError:
     SOL_TCP = socket.IPPROTO_TCP
 
-if not PY2:
+if PY3:
     # these were moved around for Python 3
     from urllib.parse import (quote as url_quote, unquote as url_unquote,
                               urlencode, parse_qs as url_parse_qs,
diff --git a/pika/connection.py b/pika/connection.py
index 15ba02d..2330635 100644
--- a/pika/connection.py
+++ b/pika/connection.py
@@ -14,17 +14,15 @@ import platform
 import warnings
 import ssl
 
-from pika import __version__
-from pika import callback as pika_callback
+import pika.callback as callback
 import pika.channel
-from pika import credentials as pika_credentials
-from pika import exceptions
-from pika import frame
-from pika import heartbeat as pika_heartbeat
-
-from pika import spec
-
 import pika.compat
+import pika.credentials as credentials
+import pika.exceptions as exceptions
+import pika.frame as frame
+import pika.heartbeat as heartbeat
+import pika.spec as spec
+import pika.validators as validators
 from pika.compat import (xrange, basestring, # pylint: disable=W0622
                          url_unquote, dictkeys, dict_itervalues,
                          dict_iteritems)
@@ -71,7 +69,7 @@ class Parameters(object):  # pylint: disable=R0902
     DEFAULT_BLOCKED_CONNECTION_TIMEOUT = None
     DEFAULT_CHANNEL_MAX = pika.channel.MAX_CHANNELS
     DEFAULT_CLIENT_PROPERTIES = None
-    DEFAULT_CREDENTIALS = pika_credentials.PlainCredentials(DEFAULT_USERNAME,
+    DEFAULT_CREDENTIALS = credentials.PlainCredentials(DEFAULT_USERNAME,
                                                             DEFAULT_PASSWORD)
     DEFAULT_CONNECTION_ATTEMPTS = 1
     DEFAULT_FRAME_MAX = spec.FRAME_MAX_SIZE
@@ -303,9 +301,9 @@ class Parameters(object):  # pylint: disable=R0902
             from  `pika.credentials.VALID_TYPES`
 
         """
-        if not isinstance(value, tuple(pika_credentials.VALID_TYPES)):
-            raise TypeError('Credentials must be an object of type: %r, but '
-                            'got %r' % (pika_credentials.VALID_TYPES, value))
+        if not isinstance(value, tuple(credentials.VALID_TYPES)):
+            raise TypeError('credentials must be an object of type: %r, but '
+                            'got %r' % (credentials.VALID_TYPES, value))
         # Copy the mutable object to avoid accidental side-effects
         self._credentials = copy.deepcopy(value)
 
@@ -381,9 +379,7 @@ class Parameters(object):  # pylint: disable=R0902
         :param str value: hostname or ip address of broker
 
         """
-        if not isinstance(value, basestring):
-            raise TypeError('host must be a str or unicode str, but got %r' %
-                            (value,))
+        validators.require_string(value, 'host')
         self._host = value
 
     @property
@@ -402,8 +398,7 @@ class Parameters(object):  # pylint: disable=R0902
         :param str value: locale value to pass to broker; e.g., "en_US"
 
         """
-        if not isinstance(value, basestring):
-            raise TypeError('locale must be a str, but got %r' % (value,))
+        validators.require_string(value, 'locale')
         self._locale = value
 
     @property
@@ -546,8 +541,7 @@ class Parameters(object):  # pylint: disable=R0902
         :param str value: rabbitmq virtual host name
 
         """
-        if not isinstance(value, basestring):
-            raise TypeError('virtual_host must be a str, but got %r' % (value,))
+        validators.require_string(value, 'virtual_host')
         self._virtual_host = value
 
     @property
@@ -718,7 +712,7 @@ class ConnectionParameters(Parameters):
             self.tcp_options = tcp_options
 
         if kwargs:
-            raise TypeError('Unexpected kwargs: %r' % (kwargs,))
+            raise TypeError('unexpected kwargs: %r' % (kwargs,))
 
 
 class URLParameters(Parameters):
@@ -829,8 +823,8 @@ class URLParameters(Parameters):
                          else self.DEFAULT_PORT)
 
         if parts.username is not None:
-            self.credentials = pika_credentials.PlainCredentials(url_unquote(parts.username),
-                                                                 url_unquote(parts.password))
+            self.credentials = credentials.PlainCredentials(url_unquote(parts.username),
+                                                            url_unquote(parts.password))
 
         # Get the Virtual Host
         if len(parts.path) > 1:
@@ -1148,7 +1142,7 @@ class Connection(pika.compat.AbstractBase):
         self._internal_connection_workflow = internal_connection_workflow
 
         # Define our callback dictionary
-        self.callbacks = pika_callback.CallbackManager()
+        self.callbacks = callback.CallbackManager()
 
         # Attributes that will be properly initialized by _init_connection_state
         # and/or during connection handshake.
@@ -1254,8 +1248,7 @@ class Connection(pika.compat.AbstractBase):
         :param method callback: The method to call
 
         """
-        if not callable(callback):
-            raise TypeError('callback should be a function or method.')
+        validators.require_callback(callback)
         self.callbacks.add(0, self.ON_CONNECTION_BACKPRESSURE, callback,
                            False)
 
@@ -1270,8 +1263,7 @@ class Connection(pika.compat.AbstractBase):
             callback(pika.connection.Connection, exception)
 
         """
-        if not callable(callback):
-            raise TypeError('callback should be a function or method.')
+        validators.require_callback(callback)
         self.callbacks.add(0, self.ON_CONNECTION_CLOSED, callback, False)
 
     def add_on_connection_blocked_callback(self, callback):
@@ -1293,9 +1285,7 @@ class Connection(pika.compat.AbstractBase):
             `pika.spec.Connection.Blocked`
 
         """
-        if not callable(callback):
-            raise TypeError('callback should be a function or method.')
-
+        validators.require_callback(callback)
         self.callbacks.add(0,
                            spec.Connection.Blocked,
                            functools.partial(callback, self),
@@ -1312,9 +1302,7 @@ class Connection(pika.compat.AbstractBase):
             `method` member is of type `pika.spec.Connection.Unblocked`
 
         """
-        if not callable(callback):
-            raise TypeError('callback should be a function or method.')
-
+        validators.require_callback(callback)
         self.callbacks.add(0,
                            spec.Connection.Unblocked,
                            functools.partial(callback, self),
@@ -1327,8 +1315,7 @@ class Connection(pika.compat.AbstractBase):
         :param method callback: Callback to call when open
 
         """
-        if not callable(callback):
-            raise TypeError('callback should be a function or method.')
+        validators.require_callback(callback)
         self.callbacks.add(0, self.ON_CONNECTION_OPEN_OK, callback, False)
 
     def add_on_open_error_callback(self, callback, remove_default=True):
@@ -1342,8 +1329,7 @@ class Connection(pika.compat.AbstractBase):
         :param bool remove_default: Remove default exception raising callback
 
         """
-        if not callable(callback):
-            raise TypeError('callback should be a function or method.')
+        validators.require_callback(callback)
         if remove_default:
             self.callbacks.remove(0, self.ON_CONNECTION_ERROR,
                                   self._default_on_connection_error)
@@ -1659,7 +1645,7 @@ class Connection(pika.compat.AbstractBase):
                 'publisher_confirms': True
             },
             'information': 'See http://pika.rtfd.org',
-            'version': __version__
+            'version': pika.__version__
         }
 
         if self.params.client_properties:
@@ -1714,7 +1700,7 @@ class Connection(pika.compat.AbstractBase):
         if self.params.heartbeat is not None and self.params.heartbeat > 0:
             LOGGER.debug('Creating a HeartbeatChecker: %r',
                          self.params.heartbeat)
-            return pika_heartbeat.HeartbeatChecker(self, self.params.heartbeat)
+            return heartbeat.HeartbeatChecker(self, self.params.heartbeat)
 
         return None
 
@@ -2312,9 +2298,7 @@ class Connection(pika.compat.AbstractBase):
 
         # Validate the callback is callable
         if callback is not None:
-            if not callable(callback):
-                raise TypeError('callback should be None, function or method.')
-
+            validators.require_callback(callback)
             for reply in acceptable_replies:
                 self.callbacks.add(channel_number, reply, callback)
 
diff --git a/pika/validators.py b/pika/validators.py
new file mode 100644
index 0000000..5aca86c
--- /dev/null
+++ b/pika/validators.py
@@ -0,0 +1,58 @@
+from pika.compat import basestring
+
+
+def require_string(value, value_name):
+    """Require that value is a string
+
+    :raises: TypeError
+
+    """
+    if not isinstance(value, basestring):
+        raise TypeError('%s must be a str or unicode str, but got %r' % (
+            value_name,
+            value,
+        ))
+
+
+def require_callback(callback, callback_name='callback'):
+    """Require that callback is callable and is not None
+
+    :raises: TypeError
+
+    """
+    if not callable(callback):
+        raise TypeError('callback %s must be callable, but got %r' % (
+            callback_name,
+            callback,
+        ))
+
+
+def rpc_completion_callback(callback):
+    """Verify callback is callable if not None
+
+    :returns: boolean indicating nowait
+    :raises: TypeError
+
+    """
+    if callback is None:
+        # No callback means we will not expect a response
+        # i.e. nowait=True
+        return True
+
+    if callable(callback):
+        # nowait=False
+        return False
+    else:
+        raise TypeError('completion callback must be callable if not None')
+
+
+def zero_or_greater(name, value):
+    """Verify that value is zero or greater. If not, 'name'
+    will be used in error message
+
+    :raises: ValueError
+
+    """
+    if int(value) < 0:
+        errmsg = '{} must be >= 0, but got {}'.format(name, value)
+        raise ValueError(errmsg)
diff --git a/setup.py b/setup.py
index 2c85324..64b79ca 100644
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ long_description = ('Pika is a pure-Python implementation of the AMQP 0-9-1 '
 
 setuptools.setup(
     name='pika',
-    version='1.0.0b1',
+    version='1.0.0b2',
     description='Pika Python AMQP Client Library',
     long_description=open('README.rst').read(),
     maintainer='Gavin M. Roy',


Test patch:
diff --git a/tests/acceptance/blocking_adapter_test.py b/tests/acceptance/blocking_adapter_test.py
index beb5037..9b4555e 100644
--- a/tests/acceptance/blocking_adapter_test.py
+++ b/tests/acceptance/blocking_adapter_test.py
@@ -361,7 +361,7 @@ class TestUsingInvalidQueueArgument(BlockingTestCaseBase):
         """
         connection = self._connect()
         ch = connection.channel()
-        with self.assertRaises(AssertionError):
+        with self.assertRaises(TypeError):
             ch.queue_declare(queue=[1, 2, 3])
 
 
diff --git a/tests/unit/blocking_channel_tests.py b/tests/unit/blocking_channel_tests.py
index bd85135..c685f47 100644
--- a/tests/unit/blocking_channel_tests.py
+++ b/tests/unit/blocking_channel_tests.py
@@ -47,6 +47,39 @@ class BlockingChannelTests(unittest.TestCase):
     def test_init_initial_value_buback_return(self):
         self.assertIsNone(self.obj._puback_return)
 
+    def test_basic_consume_legacy_parameter_queue(self):
+        # This is for the unlikely scenario where only
+        # the first parameter is updated
+        with self.assertRaises(TypeError):
+            self.obj.basic_consume('queue',
+                                   'whoops this should be a callback')
+
+    def test_basic_consume_legacy_parameter_callback(self):
+        with self.assertRaises(TypeError):
+            self.obj.basic_consume(mock.Mock(), 'queue')
+
+    def test_queue_declare_legacy_parameter_callback(self):
+        with self.assertRaises(TypeError):
+            self.obj.queue_declare(mock.Mock(), 'queue')
+
+    def test_exchange_declare_legacy_parameter_callback(self):
+        with self.assertRaises(TypeError):
+            self.obj.exchange_declare(mock.Mock(), 'exchange')
+
+    def test_queue_bind_legacy_parameter_callback(self):
+        with self.assertRaises(TypeError):
+            self.obj.queue_bind(mock.Mock(),
+                                'queue',
+                                'exchange')
+
+    def test_basic_cancel_legacy_parameter(self):
+        with self.assertRaises(TypeError):
+            self.obj.basic_cancel(mock.Mock(), 'tag')
+
+    def test_basic_get_legacy_parameter(self):
+        with self.assertRaises(TypeError):
+            self.obj.basic_get(mock.Mock())
+
     def test_basic_consume(self):
         with mock.patch.object(self.obj._impl, '_generate_consumer_tag'):
             self.obj._impl._generate_consumer_tag.return_value = 'ctag0'
diff --git a/tests/unit/channel_tests.py b/tests/unit/channel_tests.py
index 62f28fe..f0eefa0 100644
--- a/tests/unit/channel_tests.py
+++ b/tests/unit/channel_tests.py
@@ -250,6 +250,135 @@ class ChannelTests(unittest.TestCase):
         self.obj.basic_cancel(consumer_tag, callback=callback_mock)
         self.assertFalse(rpc.called)
 
+    def test_basic_consume_legacy_parameter_queue(self):
+        # This is for the unlikely scenario where only
+        # the first parameter is updated
+        self.obj._set_state(self.obj.OPEN)
+        with self.assertRaises(TypeError):
+            self.obj.basic_consume('queue',
+                                   'whoops this should be a callback')
+
+    def test_basic_consume_legacy_parameter_callback(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.basic_consume(callback_mock, 'queue')
+
+    def test_queue_declare_legacy_parameter_callback(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.queue_declare(callback_mock, 'queue')
+
+    def test_exchange_declare_legacy_parameter_callback(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.exchange_declare(callback_mock, 'exchange')
+
+    def test_queue_bind_legacy_parameter_callback(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.queue_bind(callback_mock,
+                                'queue',
+                                'exchange')
+
+    def test_basic_cancel_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.basic_cancel(callback_mock, 'tag')
+
+    def test_basic_get_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.basic_get(callback_mock)
+
+    def test_basic_qos_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.basic_get(callback_mock, 0, 0, False)
+
+    def test_basic_recover_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.basic_recover(callback_mock, True)
+
+    def test_confirm_delivery_legacy_no_parameters(self):
+        self.obj._set_state(self.obj.OPEN)
+        with self.assertRaises(TypeError):
+            self.obj.confirm_delivery()
+
+    def test_confirm_delivery_legacy_nowait_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.confirm_delivery(callback_mock, True)
+
+    def test_exchange_bind_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.exchange_bind(callback_mock,
+                                   'destination',
+                                   'source',
+                                   'routing_key',
+                                   True)
+
+    def test_exchange_delete_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.exchange_delete(callback_mock,
+                                     'exchange',
+                                     True)
+
+    def test_exchange_unbind_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.exchange_unbind(callback_mock,
+                                     'destination',
+                                     'source',
+                                     'routing_key',
+                                     True)
+
+    def test_flow_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.flow(callback_mock, True)
+
+    def test_queue_delete_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.queue_delete(callback_mock,
+                                  'queue',
+                                  True,
+                                  True)
+
+    def test_queue_unbind_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.queue_unbind(callback_mock,
+                                  'queue',
+                                  'exchange',
+                                  'routing_key')
+
+    def test_queue_purge_legacy_parameter(self):
+        self.obj._set_state(self.obj.OPEN)
+        callback_mock = mock.Mock()
+        with self.assertRaises(TypeError):
+            self.obj.queue_purge(callback_mock,
+                                  'queue',
+                                  True)
+
     def test_basic_consume_channel_closed(self):
         mock_callback = mock.Mock()
         mock_on_msg_callback = mock.Mock()
@@ -259,16 +388,11 @@ class ChannelTests(unittest.TestCase):
                           callback=mock_callback)
 
     @mock.patch('pika.channel.Channel._raise_if_not_open')
-    @mock.patch('pika.channel.Channel._require_callback')
-    def test_basic_consume_calls_raise_if_not_open(self,
-                                                   require,
-                                                   raise_if_not_open):
+    def test_basic_consume_calls_raise_if_not_open(self, raise_if_not_open):
         self.obj._set_state(self.obj.OPEN)
         mock_callback = mock.Mock()
         mock_on_msg_callback = mock.Mock()
-        self.obj.basic_consume('test-queue', mock_on_msg_callback,
-                               callback=mock_callback)
-        require.assert_called_once_with(mock_on_msg_callback)
+        self.obj.basic_consume('test-queue', mock_on_msg_callback, callback=mock_callback)
         raise_if_not_open.assert_called_once_with()
 
     def test_basic_consume_consumer_tag_no_completion_callback(self):
@@ -367,12 +491,10 @@ class ChannelTests(unittest.TestCase):
                                         'consumer_tag': consumer_tag
                                     })])
 
-    @mock.patch('pika.channel.Channel._require_callback')
-    def test_basic_get_calls_require_callback(self, require):
+    def test_basic_get_requires_callback(self):
         self.obj._set_state(self.obj.OPEN)
-        mock_callback = mock.Mock()
-        self.obj.basic_get('test-queue', mock_callback)
-        require.assert_called_once_with(mock_callback)
+        with self.assertRaises(TypeError):
+            self.obj.basic_get('test-queue', None)
 
     @mock.patch('pika.channel.Channel._send_method')
     def test_basic_get_callback(self, _unused):
@@ -890,7 +1012,7 @@ class ChannelTests(unittest.TestCase):
 
     def test_queue_bind_raises_channel_wrong_state(self):
         self.assertRaises(exceptions.ChannelWrongStateError,
-                          self.obj.queue_bind, None,
+                          self.obj.queue_bind, '',
                           'foo', 'bar', 'baz')
 
     def test_queue_bind_raises_value_error_on_invalid_callback(self):
@@ -1570,13 +1692,6 @@ class ChannelTests(unittest.TestCase):
         self.assertRaises(exceptions.ChannelWrongStateError,
                           self.obj._raise_if_not_open)
 
-    def test_validate_callback_raises_value_error_not_callable(
-            self):
-        self.obj._set_state(self.obj.OPEN)
-        self.assertRaises(TypeError,
-                          self.obj._validate_rpc_completion_callback,
-                          'foo')
-
     def test_no_side_effects_from_send_method_error(self):
         self.obj._set_state(self.obj.OPEN)