Skip to content

Commit 4ea66d8

Browse files
authored
Merge pull request CloudBotIRC#175 from linuxdaemon/gonzobot+fix-deprecations
Fix various DeprecationWarnings in the core and plugins
2 parents f008689 + de9d266 commit 4ea66d8

11 files changed

Lines changed: 124 additions & 52 deletions

File tree

cloudbot/__main__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import signal
77

88
# store the original working directory, for use when restarting
9+
from functools import partial
10+
11+
from cloudbot.util import async_util
12+
913
original_wd = os.path.realpath(".")
1014

1115
# set up environment - we need to make sure we are in the install directory
@@ -47,9 +51,10 @@ def exit_gracefully(signum, frame):
4751
stopped_while_restarting = True
4852
else:
4953
_bot.loop.call_soon_threadsafe(
50-
lambda: asyncio.async(_bot.stop("Killed (Received SIGINT {})".format(signum)), loop=_bot.loop))
54+
partial(async_util.wrap_future, _bot.stop("Killed (Received SIGINT {})".format(signum)), loop=_bot.loop)
55+
)
5156

52-
logger.warn("Bot received Signal Interrupt ({})".format(signum))
57+
logger.warning("Bot received Signal Interrupt ({})".format(signum))
5358

5459
# restore the original handler so if they do it again it triggers
5560
signal.signal(signal.SIGINT, original_sigint)

cloudbot/clients/irc.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from cloudbot.client import Client
99
from cloudbot.event import Event, EventType
10+
from cloudbot.util import async_util
1011

1112
logger = logging.getLogger("cloudbot")
1213

@@ -218,8 +219,7 @@ def _send(self, line):
218219
:type line: str
219220
"""
220221
logger.info("[{}] >> {}".format(self.name, line))
221-
asyncio.async(self._protocol.send(line), loop=self.loop)
222-
222+
async_util.wrap_future(self._protocol.send(line), loop=self.loop)
223223

224224
@property
225225
def connected(self):
@@ -272,14 +272,14 @@ def connection_lost(self, exc):
272272
# we've been closed intentionally, so don't reconnect
273273
return
274274
logger.error("[{}] Connection lost: {}".format(self.conn.name, exc))
275-
asyncio.async(self.conn.connect(), loop=self.loop)
275+
async_util.wrap_future(self.conn.connect(), loop=self.loop)
276276

277277
def eof_received(self):
278278
self._connected = False
279279
# create a new connected_future for when we are connected.
280280
self._connected_future = asyncio.Future(loop=self.loop)
281281
logger.info("[{}] EOF received.".format(self.conn.name))
282-
asyncio.async(self.conn.connect(), loop=self.loop)
282+
async_util.wrap_future(self.conn.connect(), loop=self.loop)
283283
return True
284284

285285
@asyncio.coroutine
@@ -339,7 +339,7 @@ def data_received(self, data):
339339
# Reply to pings immediately
340340

341341
if command == "PING":
342-
asyncio.async(self.send("PONG " + command_params[-1]), loop=self.loop)
342+
async_util.wrap_future(self.send("PONG " + command_params[-1]), loop=self.loop)
343343

344344
# Parse the command and params
345345

@@ -406,7 +406,7 @@ def data_received(self, data):
406406
irc_prefix=prefix, irc_command=command, irc_paramlist=command_params, irc_ctcp_text=ctcp_text)
407407

408408
# handle the message, async
409-
asyncio.async(self.bot.process(event), loop=self.loop)
409+
async_util.wrap_future(self.bot.process(event), loop=self.loop)
410410

411411
# Channel Commands
412412
# NOTICE #chan :Text

cloudbot/event.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import concurrent.futures
33
import enum
44
import logging
5+
import warnings
6+
from functools import partial
7+
8+
import sys
59

610
logger = logging.getLogger("cloudbot")
711

@@ -153,7 +157,7 @@ def prepare(self):
153157
# we're running a coroutine hook with a db, so initialise an executor pool
154158
self.db_executor = concurrent.futures.ThreadPoolExecutor(1)
155159
# be sure to initialize the db in the database executor, so it will be accessible in that thread.
156-
self.db = yield from self.async(self.bot.db_session)
160+
self.db = yield from self.async_call(self.bot.db_session)
157161

158162
def prepare_threaded(self):
159163
"""
@@ -189,7 +193,7 @@ def close(self):
189193
if self.db is not None:
190194
#logger.debug("Closing database session for {}:threaded=False".format(self.hook.description))
191195
# be sure the close the database in the database executor, as it is only accessable in that one thread
192-
yield from self.async(self.db.close)
196+
yield from self.async_call(self.db.close)
193197
self.db = None
194198

195199
def close_threaded(self):
@@ -310,17 +314,34 @@ def has_permission(self, permission, notice=True):
310314
return self.conn.permissions.has_perm_mask(self.mask, permission, notice=notice)
311315

312316
@asyncio.coroutine
313-
def async(self, function, *args, **kwargs):
317+
def async_call(self, func, *args, **kwargs):
314318
if self.db_executor is not None:
315319
executor = self.db_executor
316320
else:
317321
executor = None
318-
if kwargs:
319-
result = yield from self.loop.run_in_executor(executor, function, *args)
320-
else:
321-
result = yield from self.loop.run_in_executor(executor, lambda: function(*args, **kwargs))
322+
323+
part = partial(func, *args, **kwargs)
324+
result = yield from self.loop.run_in_executor(executor, part)
322325
return result
323326

327+
if sys.version_info < (3, 7, 0):
328+
# noinspection PyCompatibility
329+
@asyncio.coroutine
330+
def async_(self, function, *args, **kwargs):
331+
warnings.warn(
332+
"event.async() is deprecated, use event.async_call() instead.",
333+
DeprecationWarning, stacklevel=2
334+
)
335+
result = yield from self.async_call(function, *args, **kwargs)
336+
return result
337+
338+
339+
# Silence deprecation warnings about use of the 'async' name as a function
340+
try:
341+
setattr(Event, 'async', getattr(Event, 'async_'))
342+
except AttributeError:
343+
pass
344+
324345

325346
class CommandEvent(Event):
326347
"""

cloudbot/hook.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def sieve(param=None, **kwargs):
309309
"""
310310

311311
def _sieve_hook(func):
312-
assert len(inspect.getargspec(func).args) == 3, \
312+
assert len(inspect.getfullargspec(func).args) == 3, \
313313
"Sieve plugin has incorrect argument count. Needs params: bot, input, plugin"
314314

315315
hook = _get_hook(func, "sieve")

cloudbot/plugin.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@
55
import logging
66
import os
77
import re
8+
import warnings
89
from collections import defaultdict
910
from operator import attrgetter
1011
from itertools import chain
1112

1213
import sqlalchemy
14+
import sys
15+
16+
import time
1317

1418
from cloudbot.event import Event
1519
from cloudbot.hook import Priority, Action
16-
from cloudbot.util import database
20+
from cloudbot.util import database, async_util
1721

1822
logger = logging.getLogger("cloudbot")
1923

@@ -200,7 +204,7 @@ def load_plugin(self, path):
200204
self._log_hook(on_cap_ack_hook)
201205

202206
for periodic_hook in plugin.periodic:
203-
task = asyncio.async(self._start_periodic(periodic_hook))
207+
task = async_util.wrap_future(self._start_periodic(periodic_hook))
204208
plugin.tasks.append(task)
205209
self._log_hook(periodic_hook)
206210

@@ -640,12 +644,18 @@ def __init__(self, _type, plugin, func_hook):
640644
self.function = func_hook.function
641645
self.function_name = self.function.__name__
642646

643-
self.required_args = inspect.getargspec(self.function)[0]
644-
if self.required_args is None:
645-
self.required_args = []
647+
sig = inspect.signature(self.function)
646648

647649
# don't process args starting with "_"
648-
self.required_args = [arg for arg in self.required_args if not arg.startswith("_")]
650+
self.required_args = [arg for arg in sig.parameters.keys() if not arg.startswith('_')]
651+
if sys.version_info < (3, 7, 0):
652+
if "async" in self.required_args:
653+
logger.warning("Use of deprecated function 'async' in %s", self.description)
654+
time.sleep(1)
655+
warnings.warn(
656+
"event.async() is deprecated, use event.async_call() instead.",
657+
DeprecationWarning, stacklevel=2
658+
)
649659

650660
if asyncio.iscoroutine(self.function) or asyncio.iscoroutinefunction(self.function):
651661
self.threaded = False

cloudbot/reloader.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import asyncio
22
import os.path
3+
from functools import partial
34

45
from watchdog.observers import Observer
56
from watchdog.events import PatternMatchingEventHandler
67

8+
from cloudbot.util import async_util
9+
710

811
class PluginReloader(object):
912
def __init__(self, bot):
@@ -38,7 +41,8 @@ def reload(self, path):
3841

3942
if isinstance(path, bytes):
4043
path = path.decode()
41-
self.bot.loop.call_soon_threadsafe(lambda: asyncio.async(self._reload(path), loop=self.bot.loop))
44+
45+
self.bot.loop.call_soon_threadsafe(partial(async_util.wrap_future, self._reload(path), loop=self.bot.loop))
4246

4347
def unload(self, path):
4448
"""
@@ -48,7 +52,8 @@ def unload(self, path):
4852
"""
4953
if isinstance(path, bytes):
5054
path = path.decode()
51-
self.bot.loop.call_soon_threadsafe(lambda: asyncio.async(self._unload(path), loop=self.bot.loop))
55+
56+
self.bot.loop.call_soon_threadsafe(partial(async_util.wrap_future, self._unload(path), loop=self.bot.loop))
5257

5358
@asyncio.coroutine
5459
def _reload(self, path):

cloudbot/util/async_util.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
Wraps various asyncio functions
3+
"""
4+
5+
import asyncio
6+
7+
import sys
8+
9+
10+
def wrap_future(fut, *, loop=None):
11+
"""
12+
Wraps asyncio.async()/asyncio.ensure_future() depending on the python version
13+
:param fut: The awaitable, future, or coroutine to wrap
14+
:param loop: The loop to run in
15+
:return: The wrapped future
16+
"""
17+
if sys.version_info < (3, 4, 4):
18+
return asyncio.async(fut, loop=loop)
19+
20+
return asyncio.ensure_future(fut, loop=loop)

plugins/autojoin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def get_channels(db, conn):
2121

2222
@asyncio.coroutine
2323
@hook.irc_raw('004')
24-
def do_joins(db, conn, async):
25-
chans = yield from async(get_channels, db, conn)
24+
def do_joins(db, conn, async_call):
25+
chans = yield from async_call(get_channels, db, conn)
2626
join_throttle = conn.config.get("join_throttle", 0.4)
2727
for chan in chans:
2828
conn.join(chan[1])

plugins/geoip.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import geoip2.errors
1111

1212
from cloudbot import hook
13+
from cloudbot.util import async_util
1314

1415
logger = logging.getLogger("cloudbot")
1516

@@ -52,6 +53,7 @@ def update_db():
5253
return geoip2.database.Reader(PATH)
5354

5455

56+
@asyncio.coroutine
5557
def check_db(loop):
5658
"""
5759
runs update_db in an executor thread and sets geoip_reader to the result
@@ -68,7 +70,7 @@ def check_db(loop):
6870
@asyncio.coroutine
6971
@hook.on_start
7072
def load_geoip(loop):
71-
asyncio.async(check_db(loop), loop=loop)
73+
async_util.wrap_future(check_db(loop), loop=loop)
7274

7375

7476
@asyncio.coroutine

plugins/log.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import asyncio
2-
import os
32
import codecs
3+
import os
44
import time
55

66
import cloudbot
77
from cloudbot import hook
88
from cloudbot.event import EventType
9-
10-
119
# +---------+
1210
# | Formats |
1311
# +---------+
12+
from cloudbot.hook import Priority
1413
from cloudbot.util.formatting import strip_colors
1514

1615
base_formats = {
@@ -246,10 +245,20 @@ def console_log(bot, event):
246245
bot.logger.info(text)
247246

248247

249-
@hook.on_stop
250248
@hook.command("flushlog", permissions=["botcontrol"])
251249
def flush_log():
252250
for name, stream in stream_cache.values():
253251
stream.flush()
254252
for name, stream in raw_cache.values():
255253
stream.flush()
254+
255+
256+
@hook.on_stop
257+
def close_logs():
258+
for name, stream in stream_cache.values():
259+
stream.flush()
260+
stream.close()
261+
262+
for name, stream in raw_cache.values():
263+
stream.flush()
264+
stream.close()

0 commit comments

Comments
 (0)