=============
Features
--------
* Compatibility with Python 3.5, 3.6, and 3.7.
Afkak is now more strict about string types, even on Python 2.7.
Topic and consumer group names are text — `str` on Python 3; `str` or `unicode` on Python 2.
Message keys, content and offset commit metadata are bytes — `bytes` on Python 3; `str` on Python 2.
The `FastMurmur2` extra now pulls in [pyhash](https://pypi.org/project/pyhash/) rather than [Murmur](https://pypi.org/project/Murmur/), as the former provides Python 3 support.
* The new ``snappy`` setuptools extra pulls in python-snappy, which is required for Snappy compression support.
* The `afkak.consumer.Consumer` class now supports an `auto_offset_reset` parameter.
This controls how an `OffsetOutOfRange` error from the broker is handled.
The default of `None` causes the error to propagate.
* Many internal changes have been made to enable the use of Twisted [endpoint APIs](https://twistedmatrix.com/documents/current/core/howto/endpoints.html) rather than `ReconnectingClientFactory`.
The endpoint used to connect to the Kafka broker can be configured by passing the *endpoint_factory* argument to `KafkaClient`.
The expotential backoff between connection attempts can now be configured by passing the *retry_policy* argument to `KafkaClient`.
* Afkak’s API documentation [is now available at afkak.readthedocs.io](https://afkak.readthedocs.io/en/latest/).
Bugfixes
--------
* Afkak now functions better when the network identity of Kafka brokers doesn’t match the network identity in cluster metadata.
This could result in `KafkaUnavailableError` when using the client, rapid connection and disconnection, and frequent DNS resolution attempts.
Fixes [47](https://github.com/ciena/afkak/issues/47) and possibly [#15](https://github.com/ciena/afkak/issues/15).
* In a rare case when `afkak.consumer.Consumer` was stopped after all received messages have been processed and before invocation of an internal callback it would produce an `IndexError` with the message “list index out of range”.
The consumer will now stop cleanly (fixes BPSO-94789).
* Afkak now raises a generic `afkak.common.BrokerResponseError` rather than ignoring unknown error codes.
Backwards-Incompatible Changes
------------------------------
This release includes many changes that are technically backwards-incompatible in that they change public API surface, but unlikely to impact real-world clients.
* The *timeout* argument to `KafkaClient` can no longer be set to `None` (meaning “no timeout”).
You can still configure an outrageously large value.
The value of *timeout* is now coerced with the `float()` function.
It may raise `ValueError` in some circumstances where it used to raise `TypeError`.
* Keys passed to`afkak.partitioner.HashedPartitioner` must now be byte or text strings (`bytes` or `str` on Python 3; `str` or `unicode` on Python 2).
Arbitrary objects are no longer stringified when passed as a partition key.
Previously, unknown objects would be coerced by calling `str(key)`.
Now a `TypeError` will be raised as this likely represents a programming error.
`None` is no longer accepted as a partition key as not passing a key when using a hashed partitioner likely represents a programming error.
Now a `TypeError` will be raised.
Use `b''` as the partition key instead to get the same behavior `None` used to give.
* The way the reactor is passed around has been unified.
`KafkaClient` now has a public `reactor` attribute which is used by `Producer` and `Consumer`.
This change simplifies testing with mock I/O.
The `clock` argument to `afkak.producer.Producer` has been removed.
The producer now uses the reactor associated with the `KafkaClient` passed as its `client` argument.
Fixes [3](https://github.com/ciena/afkak/issues/3).
* The constants ``CODEC_NONE``, ``CODEC_GZIP``, and ``CODEC_SNAPPY`` have been relocated to the ``afkak.common`` module from the ``afkak.kafkacodec`` module.
They remain importable directly from the ``afkak`` module.
The ``ALL_CODECS`` constant is no longer available.
* Exception types for additional broker error codes have been added.
These exceptions derive from `afkak.common.BrokerResponseError`.
A subclass, `afkak.common.RetriableBrokerResponseError`, marks those error codes which are retriable.
This information is also available as a bool on the `retriable` attribute.
* The `message` attribute of these types has changed to match the upstream table:
* `UnknownError`: `'UNKNOWN'` → `'UNKNOWN_SERVER_ERROR'`
* `InvalidResponseError`: `'INVALID_MESSAGE'` → `'CORRUPT_MESSAGE'`
* `StaleLeaderEpochCodeError`: `'STALE_LEADER_EPOCH_CODE'` → `'NETWORK_EXCEPTION'`
* `OffsetsLoadInProgress`: `'OFFSETS_LOAD_IN_PROGRESS'` → `'COORDINATOR_LOAD_IN_PROGRESS'`
* `ConsumerCoordinatorNotAvailableError`: `'CONSUMER_COORDINATOR_NOT_AVAILABLE'` → `'COORDINATOR_NOT_AVAILABLE'`
* The following types have been renamed to match the name in the Kafka documentation, though the old names remain as deprecated aliases:
<!-- TODO: actually deprecate them -->
* `InvalidResponseError` → `CorruptMessage`
* `StaleLeaderEpochCodeError` → `NetworkException`
* `OffsetsLoadInProgress` → `CoordinatorLoadInProgress`
* `ConsumerCoordinatorNotAvailableError` → `CoordinatorNotAvailable`
* `NotCoordinatorForConsumerError` → `NotCoordinator`
* `afkak.common.kafka_errors` has been renamed to `afkak.common.BrokerResponseError.errnos`.
* A number of symbols have been made private or removed:
* The meaning of `KafkaClient.clients` has changed and it is now documented as private.
It will be renamed in a future release without further notice.
* `KakaBrokerClient` has been renamed `_KafkaBrokerClient`, meaning it is no longer a public API.
* The `afkak.protocol` module has been renamed `afkak._protocol`, meaning is no longer a public API.
* The symbol `afkak.partitioner.murmur2_hash_c` no longer exists.
* The `afkak.brokerclient.CLIENT_ID` constant has been removed.
* `afkak.util` has been renamed `afkak._util`, meaning its contents are no longer part of the public API.
* `afkak.common.check_error` has been renamed `_check_error`, making it private.