Skip to content

Commit

Permalink
Merge pull request #110 from Shizmob/develop
Browse files Browse the repository at this point in the history
Release 0.9.1
  • Loading branch information
theunkn0wn1 authored Feb 3, 2019
2 parents 26d9bd5 + d9fbd60 commit 7ec7d65
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 70 deletions.
19 changes: 0 additions & 19 deletions docs/api/async.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ API reference
:maxdepth: 2

client
async
features
6 changes: 3 additions & 3 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Introduction to pydle

What is pydle?
--------------
pydle is an IRC library for Python 3.4 and up.
pydle is an IRC library for Python 3.5 and up.

Although old and dated on some fronts, IRC is still used by a variety of communities as the real-time communication method of choice,
and the most popular IRC networks can still count on tens of thousands of users at any point during the day.
Expand Down Expand Up @@ -35,8 +35,8 @@ All dependencies can be installed using the standard package manager for Python,

Compatibility
-------------
pydle works in any interpreter that implements Python 3.2 or higher. Although mainly tested in CPython_, the standard Python implementation,
there is no reason why pydle itself should not work in alternative implementations like PyPy_, as long as they support the Python 3.2 language requirements.
pydle works in any interpreter that implements Python 3.5 or higher. Although mainly tested in CPython_, the standard Python implementation,
there is no reason why pydle itself should not work in alternative implementations like PyPy_, as long as they support the Python 3.5 language requirements.

.. _CPython: https://python.org
.. _PyPy: http://pypy.org
62 changes: 30 additions & 32 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ pydle will automatically take care of ensuring that the connection persists, and
import pydle
client = pydle.Client('MyBot')
# Client.connect() is a blocking function.
client.connect('irc.freenode.net', tls=True)
# Client.connect() is a coroutine.
await client.connect('irc.freenode.net', tls=True)
client.handle_forever()
Adding functionality
Expand All @@ -33,17 +33,17 @@ To truly start adding functionality to the client, subclass :class:`pydle.Client
class MyClient(pydle.Client):
""" This is a simple bot that will greet people as they join the channel. """
def on_connect(self):
super().on_connect()
async def on_connect(self):
await super().on_connect()
# Can't greet many people without joining a channel.
self.join('#kochira')
await self.join('#kochira')
def on_join(self, channel, user):
super().on_join(channel, user)
self.message(channel, 'Hey there, {user}!', user=user)
async def on_join(self, channel, user):
await super().on_join(channel, user)
await self.message(channel, 'Hey there, {user}!', user=user)
client = MyClient('MyBot')
client.connect('irc.freenode.net', tls=True)
await client.connect('irc.freenode.net', tls=True)
client.handle_forever()
This trivial example shows a few things:
Expand Down Expand Up @@ -75,14 +75,14 @@ and handle them within a single loop.
class MyClient(pydle.Client):
""" This is a simple bot that will greet people as they join the channel. """
def on_connect(self):
super().on_connect()
async def on_connect(self):
await super().on_connect()
# Can't greet many people without joining a channel.
self.join('#kochira')
await self.join('#kochira')
def on_join(self, channel, user):
super().on_join(channel, user)
self.message(channel, 'Hey there, {user}!', user=user)
async def on_join(self, channel, user):
await super().on_join(channel, user)
await self.message(channel, 'Hey there, {user}!', user=user)
# Setup pool and connect clients.
pool = pydle.ClientPool()
Expand Down Expand Up @@ -138,12 +138,12 @@ Asynchronous functionality
Some actions inevitably require blocking and waiting for a result. Since pydle is an asynchronous library where a client runs in a single thread,
doing this blindly could lead to issues like the operation blocking the handling of messages entirely.

Fortunately, pydle implements coroutines_ which allow you to handle a blocking operation almost as if it were a regular operation,
Fortunately, pydle utilizes asyncio coroutines_ which allow you to handle a blocking operation almost as if it were a regular operation,
while still retaining the benefits of asynchronous program flow. Coroutines allow pydle to be notified when a blocking operation is done,
and then resume execution of the calling function appropriately. That way, blocking operations do not block the entire program flow,
and then resume execution of the calling function appropriately. That way, blocking operations do not block the entire program flow.

In order for a function to be declared as a coroutine, it has to be decorated using the :func:`pydle.coroutine` decorator.
It can then call functions that would normally block using Python's ``yield from`` operator.
In order for a function to be declared as a coroutine, it has to be declared as an ``async def`` function or decorated with the :meth:`asyncio.coroutine` decorator.
It can then call functions that would normally block using Python's ``await`` operator.
Since a function that calls a blocking function is itself blocking too, it has to be declared a coroutine as well.

.. hint::
Expand All @@ -168,12 +168,12 @@ the act of WHOISing will not block the entire program flow of the client.
that would be invalidated upon parting, quitting or changing nicknames.
"""
def on_connect(self):
super().on_connect()
async def on_connect(self):
await super().on_connect()
self.join('#kochira')
@pydle.coroutine
def is_admin(self, nickname):
await def is_admin(self, nickname):
"""
Check whether or not a user has administrative rights for this bot.
This is a blocking function: use a coroutine to call it.
Expand All @@ -184,29 +184,27 @@ the act of WHOISing will not block the entire program flow of the client.
# Check the WHOIS info to see if the source has identified with NickServ.
# This is a blocking operation, so use yield.
if source in ADMIN_NICKNAMES:
info = yield from self.whois(source)
info = await self.whois(source)
admin = info['identified']
return admin
@pydle.coroutine
def on_message(self, target, source, message):
super().on_message(target, source, message)
async def on_message(self, target, source, message):
await super().on_message(target, source, message)
# Tell a user if they are an administrator for this bot.
if message.startswith('!adminstatus'):
admin = yield from self.is_admin(source)
admin = await self.is_admin(source)
if admin:
self.message(target, '{source}: You are an administrator.', source=source)
else:
self.message(target, '{source}: You are not an administrator.', source=source)
Writing your own blocking operation that can work with coroutines is trivial:
just make your blocking method return a :class:`pydle.Future` instance (without the act of creating and returning the Future itself being blocking),
and any coroutine yielding it will halt execution until the returned future is resolved, using either
:meth:`pydle.Future.set_result` or :meth:`pydle.Future.set_exception`, while pydle can still handle everything else.
Simply use the existing asyncio apis: https://docs.python.org/3.7/library/asyncio-task.html#coroutines-and-tasks


You can create a :class:`pydle.Future` instance that belongs to the client by calling :meth:`pydle.async.EventLoop.create_future`.

.. _coroutines: https://en.wikipedia.org/wiki/Coroutine
4 changes: 2 additions & 2 deletions pydle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from asyncio import coroutine, Future

__name__ = 'pydle'
__version__ = '0.9.0'
__version_info__ = (0, 9, 0)
__version__ = '0.9.1'
__version_info__ = (0, 9, 1)
__license__ = 'BSD'


Expand Down
14 changes: 3 additions & 11 deletions pydle/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ async def connect(self, hostname=None, port=None, reconnect=False, **kwargs):
if self.server_tag:
self.logger = logging.getLogger(self.__class__.__name__ + ':' + self.server_tag)

# store the handle forever task, so we can cancel it during disconnection
self._handle_forever_task = self.eventloop.create_task(self.handle_forever())
# ensure_future(self.handle_forever(), loop=self.eventloop)
self.eventloop.create_task(self.handle_forever())

async def disconnect(self, expected=True):
""" Disconnect from server. """
Expand All @@ -134,13 +132,6 @@ async def _disconnect(self, expected):
# Callback.
await self.on_disconnect(expected)

# cancel the handle forever task
try:
if self._handle_forever_task:
self._handle_forever_task.cancel()
except asyncio.CancelledError:
# a canceled error is expected here, and is not an error.
self.logger.debug("swallowing expected CancelError")
# Shut down event loop.
if expected and self.own_eventloop:
self.connection.stop()
Expand Down Expand Up @@ -397,7 +388,8 @@ async def on_data(self, data):

while self._has_message():
message = self._parse_message()
await self.on_raw(message)
self.eventloop.create_task(self.on_raw(message))


async def on_data_error(self, exception):
""" Handle error. """
Expand Down
2 changes: 1 addition & 1 deletion pydle/features/rfc1459/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ async def connect(self, hostname=None, port=None, password=None, **kwargs):
await super().connect(hostname, port, **kwargs)

# Check if a password was provided and we don't already have one
if password is not None and self.password:
if password is not None and not self.password:
# if so, set the password.
self.password = password
# And initiate the IRC connection.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='pydle',
version='0.9.0',
version='0.9.1',
python_requires=">=3.5",
packages=[
'pydle',
Expand Down

0 comments on commit 7ec7d65

Please sign in to comment.