Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.0 #176

Merged
merged 64 commits into from
Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
96863c9
Fix use of mutable default arguments
ZeroKnight Jun 17, 2020
790b8a1
Merge pull request #141 from ZeroKnight/fix-mda
theunkn0wn1 Jun 17, 2020
f7937b7
Fix detection of `monitor` support
ZeroKnight Jun 19, 2020
483307b
Ensure we add nicknames in `RPL_MON*` handlers
ZeroKnight Jun 19, 2020
024159d
Turn `(un)monitor` into proper coroutines
ZeroKnight Jun 19, 2020
b535879
Change MonitoringSupport parent to ISUPPORTSupport
ZeroKnight Jun 19, 2020
339b3c0
Remove unnecessary else
ZeroKnight Jun 21, 2020
ca18f83
Fixes a documentation async/await typo
bernardosulzbach Jul 9, 2020
514ad98
Merge pull request #146 from bernardosulzbach/doc-async-typo
theunkn0wn1 Jul 9, 2020
60820f1
Fix parsing IRCv3 tags with missing/empty values
ZeroKnight Oct 13, 2020
da92424
[#148] added two regression test cases
theunkn0wn1 Oct 13, 2020
97a1e62
Add test case for empty and missing IRCv3 tags
ZeroKnight Oct 19, 2020
4ec52da
Merge pull request #149 from ZeroKnight/fix-148
theunkn0wn1 Oct 28, 2020
f622633
Correct a typo in features/overview.rst
felixonmars Nov 8, 2020
b4a5641
Merge pull request #144 from ZeroKnight/fix-143
theunkn0wn1 Nov 22, 2020
b7e5c57
Merge pull request #150 from felixonmars/patch-1
theunkn0wn1 Nov 22, 2020
b747ce1
[#151] Fixup irccat.py example
theunkn0wn1 Nov 22, 2020
2dc9065
Enable RplWhoisHostSupport implementation (as part of full featured c…
kenneaal Feb 21, 2021
baa7070
doc: move to libera.chat
shizmob May 21, 2021
517a151
Merge pull request #152 from theunkn0wn1/develop
theunkn0wn1 Aug 11, 2021
9798eb3
Merge pull request #156 from kenneaal/develop
theunkn0wn1 Aug 11, 2021
b96190a
[#161] Remove Deprecated Marker from WHOIS
Rixxan Mar 22, 2022
ea48213
Merge pull request #163 from Rixxan/develop
theunkn0wn1 Mar 25, 2022
dc3c82a
Add missing async tags to functions
Rixxan Mar 26, 2022
2a433ea
Add More Awaits + WHOIS patch
Rixxan Mar 26, 2022
f3b0aed
A few more hanging awaits
Rixxan Mar 26, 2022
cf6ae19
Final Holdout Missing Awaits
Rixxan Mar 26, 2022
0827c1d
Update style for info checks
Rixxan Mar 26, 2022
c0c0d61
Merge pull request #164 from Rixxan/develop
theunkn0wn1 Mar 26, 2022
0166bef
Implement decorator-based event handler registration
theunkn0wn1 Mar 26, 2022
896b0d7
[Tests] Add Async to existing test library
Rixxan Mar 28, 2022
493df7f
Necromance more Tests
Rixxan Mar 28, 2022
74a14e4
Formatting for Tests
Rixxan Mar 29, 2022
d89bdce
Fix a few more tests
Rixxan Mar 29, 2022
6120030
Add Back Needed Lamdas
Rixxan Mar 29, 2022
a63b981
Remove unnecessary-pass
Rixxan Mar 29, 2022
c7b5cfa
Minor Improvements
Rixxan Mar 29, 2022
825710a
Additional Fixes
Rixxan Mar 29, 2022
302359b
Undo Earlier Change
Rixxan Mar 29, 2022
62bcbf4
Additional Formatting Updates
Rixxan Mar 31, 2022
5229e16
Revert staticmethod updates
Rixxan Apr 2, 2022
0ae2167
Add ... to ABCs
Rixxan Apr 2, 2022
1d5ccc0
Additional Requested Updates
Rixxan Apr 2, 2022
8290a0e
Additional Requested Reversion
Rixxan Apr 2, 2022
da58825
Add Missing awaits in coroutine calls
Rixxan Apr 2, 2022
a217560
Update supported versions
hburn7 Apr 7, 2022
b621ef0
Updated versioning
hburn7 Apr 7, 2022
be70b93
Merge pull request #1 from hburn7/develop
hburn7 Apr 7, 2022
1268884
Update intro.rst
hburn7 Apr 7, 2022
5fa9658
Merge pull request #171 from hburn7/patch-1
theunkn0wn1 Apr 9, 2022
30f8143
Merge pull request #170 from Rixxan/missing-awaits
theunkn0wn1 Apr 9, 2022
9664517
#78 Remove Event Loop Tests
Rixxan May 6, 2022
f57b592
#78 Minor Fixes to Test Library
Rixxan May 6, 2022
6ec21f0
#78 Remove Problematic Segment
Rixxan May 7, 2022
575bd93
Merge pull request #165 from theunkn0wn1/user/theunkn0wn1/feature/dis…
theunkn0wn1 Jun 19, 2022
714e05d
Merge pull request #168 from Rixxan/develop
theunkn0wn1 Jun 19, 2022
3bef770
[Minor] Add Imports Used Upstream
Rixxan Jun 19, 2022
290fee6
Merge branch 'develop' into formatting-updates
Rixxan Jun 19, 2022
46001b9
[#169] Add Back Intent-Based Defaults
Rixxan Jun 19, 2022
83a5bc6
Merge pull request #169 from Rixxan/formatting-updates
theunkn0wn1 Jun 19, 2022
1d217a5
#167 Migrate to poetry.
theunkn0wn1 Jun 19, 2022
97387c6
#167 Demote to point release
theunkn0wn1 Jun 19, 2022
f8ab61b
Merge pull request #175 from theunkn0wn1/user/theunkn0wn1/poetry
theunkn0wn1 Jun 19, 2022
e10369d
v1.0.0
theunkn0wn1 Jul 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pydle
Python IRC library.
-------------------

pydle is a compact, flexible and standards-abiding IRC library for Python 3.5+.
pydle is a compact, flexible and standards-abiding IRC library for Python 3.5 through 3.9.

Features
--------
Expand All @@ -26,6 +26,9 @@ Basic Usage

From there, you can `import pydle` and subclass `pydle.Client` for your own functionality.

> To enable SSL support, install the `sasl` extra.
> `pip install pydle[sasl]`

Setting a nickname and starting a connection over TLS:
```python
import pydle
Expand Down Expand Up @@ -103,7 +106,7 @@ class Client(MyBase):

**Q: How do I...?**

Stop! Read the [documentation](http://pydle.readthedocs.org) first. If you're still in need of support, join us on IRC! We hang at `#kochira` on `irc.freenode.net`. If someone is around, they'll most likely gladly help you.
Stop! Read the [documentation](http://pydle.readthedocs.org) first. If you're still in need of support, join us on IRC! We hang at `#pydle` on `irc.libera.chat`. If someone is around, they'll most likely gladly help you.

License
-------
Expand Down
4 changes: 2 additions & 2 deletions docs/features/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ In addition, it registers the `pydle.Client.on_ctcp(from, query, contents)` hook
request, and a per-type hook in the form of `pydle.Client.on_ctcp_<type>(from, contents)`, which allows you to act upon CTCP
requests of type `type`. `type` will always be lowercased. A few examples of `type` can be: `action`, `time`, `version`.

Finally, it registers the `pydle.Client.on_ctcp_reply(from, queyr, contents)` hook, which acts similar to the above hook,
Finally, it registers the `pydle.Client.on_ctcp_reply(from, query, contents)` hook, which acts similar to the above hook,
except it is triggered when the client receives a CTCP response. It also registers `pydle.Client.on_ctcp_<type>_reply`, which
works similar to the per-type hook described above.

Expand Down Expand Up @@ -252,4 +252,4 @@ Support For `RPL_WHOIS_HOST` messages, this allows pydle to expose an IRC users
real IP address and host, if the bot has access to that information.

This information will fill in the `real_ip_address` and `real_hostname` fields
of an :class:`pydle.Client.whois()` response.
of an :class:`pydle.Client.whois()` response.
4 changes: 2 additions & 2 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.5 and up.
pydle is an IRC library for Python 3.5 through 3.9.

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,7 +35,7 @@ All dependencies can be installed using the standard package manager for Python,

Compatibility
-------------
pydle works in any interpreter that implements Python 3.5 or higher. Although mainly tested in CPython_, the standard Python implementation,
pydle works in any interpreter that implements Python 3.5-3.9. 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
Expand Down
4 changes: 2 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ Fortunately, pydle utilizes asyncio coroutines_ which allow you to handle a bloc
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.

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.
In order for a function to be declared as a coroutine, it has to be declared as an ``async def`` function.
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.

Expand Down Expand Up @@ -220,7 +220,7 @@ the act of WHOISing will not block the entire program flow of the client.
self.join('#kochira')


await def is_admin(self, nickname):
async 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 Down
13 changes: 6 additions & 7 deletions pydle/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# noinspection PyUnresolvedReferences
from asyncio import coroutine, Future
from functools import cmp_to_key
from . import connection, protocol, client, features

from .client import Error, NotInChannel, AlreadyInChannel, BasicClient, ClientPool
from .features.ircv3.cap import NEGOTIATING as CAPABILITY_NEGOTIATING, FAILED as CAPABILITY_FAILED, \
NEGOTIATED as CAPABILITY_NEGOTIATED

# noinspection PyUnresolvedReferences
from asyncio import coroutine, Future

__name__ = 'pydle'
__version__ = '0.9.4rc1'
Expand All @@ -15,12 +15,11 @@

def featurize(*features):
""" Put features into proper MRO order. """
from functools import cmp_to_key

def compare_subclass(left, right):
if issubclass(left, right):
return -1
elif issubclass(right, left):
if issubclass(right, left):
return 1
return 0

Expand All @@ -32,9 +31,9 @@ def compare_subclass(left, right):

class Client(featurize(*features.ALL)):
""" A fully featured IRC client. """
pass
...


class MinimalClient(featurize(*features.LITE)):
""" A cut-down, less-featured IRC client. """
pass
...
59 changes: 37 additions & 22 deletions pydle/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
import asyncio
import logging
from asyncio import new_event_loop, gather, get_event_loop, sleep

from . import connection, protocol
import warnings
from . import connection, protocol
import inspect
import functools

__all__ = ['Error', 'AlreadyInChannel', 'NotInChannel', 'BasicClient', 'ClientPool']
DEFAULT_NICKNAME = '<unregistered>'


class Error(Exception):
""" Base class for all pydle errors. """
pass
...


class NotInChannel(Error):
Expand Down Expand Up @@ -56,10 +57,10 @@ def PING_TIMEOUT(self, value):
)
self.READ_TIMEOUT = value

def __init__(self, nickname, fallback_nicknames=[], username=None, realname=None,
def __init__(self, nickname, fallback_nicknames=None, username=None, realname=None,
eventloop=None, **kwargs):
""" Create a client. """
self._nicknames = [nickname] + fallback_nicknames
self._nicknames = [nickname] + (fallback_nicknames or [])
self.username = username or nickname.lower()
self.realname = realname or nickname
if eventloop:
Expand Down Expand Up @@ -149,12 +150,12 @@ async def _disconnect(self, expected):
if expected and self.own_eventloop:
self.connection.stop()

async def _connect(self, hostname, port, reconnect=False, channels=[],
async def _connect(self, hostname, port, reconnect=False, channels=None,
encoding=protocol.DEFAULT_ENCODING, source_address=None):
""" Connect to IRC host. """
# Create connection if we can't reuse it.
if not reconnect or not self.connection:
self._autojoin_channels = channels
self._autojoin_channels = channels or []
self.connection = connection.Connection(hostname, port, source_address=source_address,
eventloop=self.eventloop)
self.encoding = encoding
Expand All @@ -167,10 +168,8 @@ def _reconnect_delay(self):
if self.RECONNECT_ON_ERROR and self.RECONNECT_DELAYED:
if self._reconnect_attempts >= len(self.RECONNECT_DELAYS):
return self.RECONNECT_DELAYS[-1]
else:
return self.RECONNECT_DELAYS[self._reconnect_attempts]
else:
return 0
return self.RECONNECT_DELAYS[self._reconnect_attempts]
return 0

## Internal database management.

Expand All @@ -197,21 +196,21 @@ def _create_user(self, nickname):
'hostname': None
}

def _sync_user(self, nick, metadata):
async def _sync_user(self, nick, metadata):
# Create user in database.
if nick not in self.users:
self._create_user(nick)
await self._create_user(nick)
if nick not in self.users:
return
self.users[nick].update(metadata)

def _rename_user(self, user, new):
async def _rename_user(self, user, new):
if user in self.users:
self.users[new] = self.users[user]
self.users[new]['nickname'] = new
del self.users[user]
else:
self._create_user(new)
await self._create_user(new)
if new not in self.users:
return

Expand Down Expand Up @@ -297,8 +296,7 @@ def server_tag(self):
tag = host

return tag
else:
return None
return None

## IRC API.

Expand Down Expand Up @@ -372,7 +370,7 @@ async def handle_forever(self):
try:
await self.rawmsg("PING", self.server_tag)
data = await self.connection.recv(timeout=self.READ_TIMEOUT)
except (asyncio.TimeoutError, ConnectionResetError) as e:
except (asyncio.TimeoutError, ConnectionResetError):
data = None

if not data:
Expand Down Expand Up @@ -430,7 +428,7 @@ async def on_unknown(self, message):

async def _ignored(self, message):
""" Ignore message. """
pass
...

def __getattr__(self, attr):
""" Return on_unknown or _ignored for unknown handlers, depending on the invocation type. """
Expand All @@ -441,13 +439,30 @@ def __getattr__(self, attr):
# In that case, return the method that logs and possibly acts on unknown messages.
return self.on_unknown
# Are we in an existing handler calling super()?
else:
# Just ignore it, then.
return self._ignored
# Just ignore it, then.
return self._ignored

# This isn't a handler, just raise an error.
raise AttributeError(attr)

# Bonus features
def event(self, func):
"""
Registers the specified `func` to handle events of the same name.

The func will always be called with, at least, the bot's `self` instance.

Returns decorated func, unmodified.
"""
if not func.__name__.startswith("on_"):
raise NameError("Event handlers must start with 'on_'.")

if not inspect.iscoroutinefunction(func):
raise AssertionError("Wrapped function {!r} must be an `async def` function.".format(func))
setattr(self, func.__name__, functools.partial(func, self))

return func


class ClientPool:
""" A pool of clients that are ran and handled in parallel. """
Expand Down
6 changes: 4 additions & 2 deletions pydle/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from .isupport import ISUPPORTSupport
from .whox import WHOXSupport
from .ircv3 import IRCv3Support, IRCv3_1Support, IRCv3_2Support
from .rpl_whoishost import RplWhoisHostSupport

ALL = [ IRCv3Support, WHOXSupport, ISUPPORTSupport, CTCPSupport, AccountSupport, TLSSupport, RFC1459Support ]
LITE = [ WHOXSupport, ISUPPORTSupport, CTCPSupport, TLSSupport, RFC1459Support ]
ALL = [IRCv3Support, WHOXSupport, ISUPPORTSupport, CTCPSupport, AccountSupport, TLSSupport, RFC1459Support,
RplWhoisHostSupport]
LITE = [WHOXSupport, ISUPPORTSupport, CTCPSupport, TLSSupport, RFC1459Support]
21 changes: 11 additions & 10 deletions pydle/features/account.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## account.py
# Account system support.
from pydle.features import rfc1459
import asyncio


class AccountSupport(rfc1459.RFC1459Support):

Expand All @@ -15,16 +15,17 @@ def _create_user(self, nickname):
'identified': False
})

def _rename_user(self, user, new):
super()._rename_user(user, new)
async def _rename_user(self, user, new):
await super()._rename_user(user, new)
# Unset account info to be certain until we get a new response.
self._sync_user(new, {'account': None, 'identified': False})
self.whois(new)
await self._sync_user(new, {'account': None, 'identified': False})
await self.whois(new)

## IRC API.
@asyncio.coroutine
def whois(self, nickname):
info = yield from super().whois(nickname)
async def whois(self, nickname):
info = await super().whois(nickname)
if info is None:
return info
info.setdefault('account', None)
info.setdefault('identified', False)
return info
Expand All @@ -39,7 +40,7 @@ async def on_raw_307(self, message):
}

if nickname in self.users:
self._sync_user(nickname, info)
await self._sync_user(nickname, info)
if nickname in self._pending['whois']:
self._whois_info[nickname].update(info)

Expand All @@ -52,6 +53,6 @@ async def on_raw_330(self, message):
}

if nickname in self.users:
self._sync_user(nickname, info)
await self._sync_user(nickname, info)
if nickname in self._pending['whois']:
self._whois_info[nickname].update(info)
Loading