i need a rundown, stat
This commit is contained in:
parent
e6e8d544d2
commit
5b1909cac5
2
bot.py
2
bot.py
|
@ -311,7 +311,7 @@ class Sopel(irc.Bot):
|
|||
# Now that we've sent the first part, we need to send the rest. Doing
|
||||
# this recursively seems easier to me than iteratively
|
||||
if excess:
|
||||
self.say(recipient, excess, max_messages - 1)
|
||||
self.say(excess, recipient, max_messages - 1)
|
||||
|
||||
def notice(self, text, dest):
|
||||
"""Send an IRC NOTICE to a user or a channel.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# coding=utf-8
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
The config object provides a simplified to access Sopel's configuration file.
|
||||
The sections of the file are attributes of the object, and the keys in the
|
||||
|
@ -13,9 +14,6 @@ object is initialized.
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
"""
|
||||
# Copyright 2012-2015, Elsie Powell, embolalia.com
|
||||
# Copyright © 2012, Elad Alfassa <elad@fedoraproject.org>
|
||||
# Licensed under the Eiffel Forum License 2.
|
||||
|
||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
||||
|
||||
|
@ -25,239 +23,235 @@ from tools import get_input
|
|||
import loader
|
||||
import os
|
||||
import sys
|
||||
if sys.version_info.major < 3:
|
||||
import ConfigParser
|
||||
else:
|
||||
basestring = str
|
||||
import configparser as ConfigParser
|
||||
import configparser as ConfigParser
|
||||
import config.core_section
|
||||
from config.types import StaticSection
|
||||
|
||||
|
||||
class ConfigurationError(Exception):
|
||||
""" Exception type for configuration errors """
|
||||
""" Exception type for configuration errors """
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return 'ConfigurationError: %s' % self.value
|
||||
def __str__(self):
|
||||
return 'ConfigurationError: %s' % self.value
|
||||
|
||||
|
||||
class Config(object):
|
||||
def __init__(self, filename, validate=True):
|
||||
"""The bot's configuration.
|
||||
def __init__(self, filename, validate=True):
|
||||
"""The bot's configuration.
|
||||
|
||||
The given filename will be associated with the configuration, and is
|
||||
the file which will be written if write() is called. If load is not
|
||||
given or True, the configuration object will load the attributes from
|
||||
the file at filename.
|
||||
The given filename will be associated with the configuration, and is
|
||||
the file which will be written if write() is called. If load is not
|
||||
given or True, the configuration object will load the attributes from
|
||||
the file at filename.
|
||||
|
||||
A few default values will be set here if they are not defined in the
|
||||
config file, or a config file is not loaded. They are documented below.
|
||||
"""
|
||||
self.filename = filename
|
||||
"""The config object's associated file, as noted above."""
|
||||
self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
|
||||
self.parser.read(self.filename)
|
||||
self.define_section('core', config.core_section.CoreSection,
|
||||
validate=validate)
|
||||
self.get = self.parser.get
|
||||
A few default values will be set here if they are not defined in the
|
||||
config file, or a config file is not loaded. They are documented below.
|
||||
"""
|
||||
self.filename = filename
|
||||
"""The config object's associated file, as noted above."""
|
||||
self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
|
||||
self.parser.read(self.filename)
|
||||
self.define_section('core', config.core_section.CoreSection,
|
||||
validate=validate)
|
||||
self.get = self.parser.get
|
||||
|
||||
@property
|
||||
def homedir(self):
|
||||
"""An alias to config.core.homedir"""
|
||||
# Technically it's the other way around, so we can bootstrap filename
|
||||
# attributes in the core section, but whatever.
|
||||
configured = None
|
||||
if self.parser.has_option('core', 'homedir'):
|
||||
configured = self.parser.get('core', 'homedir')
|
||||
if configured:
|
||||
return configured
|
||||
else:
|
||||
return os.path.dirname(self.filename)
|
||||
@property
|
||||
def homedir(self):
|
||||
"""An alias to config.core.homedir"""
|
||||
# Technically it's the other way around, so we can bootstrap filename
|
||||
# attributes in the core section, but whatever.
|
||||
configured = None
|
||||
if self.parser.has_option('core', 'homedir'):
|
||||
configured = self.parser.get('core', 'homedir')
|
||||
if configured:
|
||||
return configured
|
||||
else:
|
||||
return os.path.dirname(self.filename)
|
||||
|
||||
def save(self):
|
||||
"""Save all changes to the config file."""
|
||||
cfgfile = open(self.filename, 'w')
|
||||
self.parser.write(cfgfile)
|
||||
cfgfile.flush()
|
||||
cfgfile.close()
|
||||
def save(self):
|
||||
"""Save all changes to the config file."""
|
||||
cfgfile = open(self.filename, 'w')
|
||||
self.parser.write(cfgfile)
|
||||
cfgfile.flush()
|
||||
cfgfile.close()
|
||||
|
||||
def add_section(self, name):
|
||||
"""Add a section to the config file.
|
||||
def add_section(self, name):
|
||||
"""Add a section to the config file.
|
||||
|
||||
Returns ``False`` if already exists.
|
||||
"""
|
||||
try:
|
||||
return self.parser.add_section(name)
|
||||
except ConfigParser.DuplicateSectionError:
|
||||
return False
|
||||
Returns ``False`` if already exists.
|
||||
"""
|
||||
try:
|
||||
return self.parser.add_section(name)
|
||||
except ConfigParser.DuplicateSectionError:
|
||||
return False
|
||||
|
||||
def define_section(self, name, cls_, validate=True):
|
||||
"""Define the available settings in a section.
|
||||
def define_section(self, name, cls_, validate=True):
|
||||
"""Define the available settings in a section.
|
||||
|
||||
``cls_`` must be a subclass of ``StaticSection``. If the section has
|
||||
already been defined with a different class, ValueError is raised.
|
||||
``cls_`` must be a subclass of ``StaticSection``. If the section has
|
||||
already been defined with a different class, ValueError is raised.
|
||||
|
||||
If ``validate`` is True, the section's values will be validated, and an
|
||||
exception raised if they are invalid. This is desirable in a module's
|
||||
setup function, for example, but might not be in the configure function.
|
||||
"""
|
||||
if not issubclass(cls_, StaticSection):
|
||||
raise ValueError("Class must be a subclass of StaticSection.")
|
||||
current = getattr(self, name, None)
|
||||
current_name = str(current.__class__)
|
||||
new_name = str(cls_)
|
||||
if (current is not None and not isinstance(current, self.ConfigSection)
|
||||
and not current_name == new_name):
|
||||
raise ValueError(
|
||||
"Can not re-define class for section from {} to {}.".format(
|
||||
current_name, new_name)
|
||||
)
|
||||
setattr(self, name, cls_(self, name, validate=validate))
|
||||
If ``validate`` is True, the section's values will be validated, and an
|
||||
exception raised if they are invalid. This is desirable in a module's
|
||||
setup function, for example, but might not be in the configure function.
|
||||
"""
|
||||
if not issubclass(cls_, StaticSection):
|
||||
raise ValueError("Class must be a subclass of StaticSection.")
|
||||
current = getattr(self, name, None)
|
||||
current_name = str(current.__class__)
|
||||
new_name = str(cls_)
|
||||
if (current is not None and not isinstance(current, self.ConfigSection)
|
||||
and not current_name == new_name):
|
||||
raise ValueError(
|
||||
"Can not re-define class for section from {} to {}.".format(
|
||||
current_name, new_name)
|
||||
)
|
||||
setattr(self, name, cls_(self, name, validate=validate))
|
||||
|
||||
class ConfigSection(object):
|
||||
class ConfigSection(object):
|
||||
|
||||
"""Represents a section of the config file.
|
||||
"""Represents a section of the config file.
|
||||
|
||||
Contains all keys in thesection as attributes.
|
||||
Contains all keys in thesection as attributes.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, name, items, parent):
|
||||
object.__setattr__(self, '_name', name)
|
||||
object.__setattr__(self, '_parent', parent)
|
||||
for item in items:
|
||||
value = item[1].strip()
|
||||
if not value.lower() == 'none':
|
||||
if value.lower() == 'false':
|
||||
value = False
|
||||
object.__setattr__(self, item[0], value)
|
||||
def __init__(self, name, items, parent):
|
||||
object.__setattr__(self, '_name', name)
|
||||
object.__setattr__(self, '_parent', parent)
|
||||
for item in items:
|
||||
value = item[1].strip()
|
||||
if not value.lower() == 'none':
|
||||
if value.lower() == 'false':
|
||||
value = False
|
||||
object.__setattr__(self, item[0], value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return None
|
||||
def __getattr__(self, name):
|
||||
return None
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
object.__setattr__(self, name, value)
|
||||
if type(value) is list:
|
||||
value = ','.join(value)
|
||||
self._parent.parser.set(self._name, name, value)
|
||||
def __setattr__(self, name, value):
|
||||
object.__setattr__(self, name, value)
|
||||
if type(value) is list:
|
||||
value = ','.join(value)
|
||||
self._parent.parser.set(self._name, name, value)
|
||||
|
||||
def get_list(self, name):
|
||||
value = getattr(self, name)
|
||||
if not value:
|
||||
return []
|
||||
if isinstance(value, basestring):
|
||||
value = value.split(',')
|
||||
# Keep the split value, so we don't have to keep doing this
|
||||
setattr(self, name, value)
|
||||
return value
|
||||
def get_list(self, name):
|
||||
value = getattr(self, name)
|
||||
if not value:
|
||||
return []
|
||||
if isinstance(value, str):
|
||||
value = value.split(',')
|
||||
# Keep the split value, so we don't have to keep doing this
|
||||
setattr(self, name, value)
|
||||
return value
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.parser.sections():
|
||||
items = self.parser.items(name)
|
||||
section = self.ConfigSection(name, items, self) # Return a section
|
||||
setattr(self, name, section)
|
||||
return section
|
||||
else:
|
||||
raise AttributeError("%r object has no attribute %r"
|
||||
% (type(self).__name__, name))
|
||||
def __getattr__(self, name):
|
||||
if name in self.parser.sections():
|
||||
items = self.parser.items(name)
|
||||
section = self.ConfigSection(name, items, self) # Return a section
|
||||
setattr(self, name, section)
|
||||
return section
|
||||
else:
|
||||
raise AttributeError("%r object has no attribute %r"
|
||||
% (type(self).__name__, name))
|
||||
|
||||
def option(self, question, default=False):
|
||||
"""Ask "y/n" and return the corresponding boolean answer.
|
||||
def option(self, question, default=False):
|
||||
"""Ask "y/n" and return the corresponding boolean answer.
|
||||
|
||||
Show user in terminal a "y/n" prompt, and return true or false based on
|
||||
the response. If default is passed as true, the default will be shown
|
||||
as ``[y]``, else it will be ``[n]``. ``question`` should be phrased as
|
||||
a question, but without a question mark at the end.
|
||||
Show user in terminal a "y/n" prompt, and return true or false based on
|
||||
the response. If default is passed as true, the default will be shown
|
||||
as ``[y]``, else it will be ``[n]``. ``question`` should be phrased as
|
||||
a question, but without a question mark at the end.
|
||||
|
||||
"""
|
||||
d = 'n'
|
||||
if default:
|
||||
d = 'y'
|
||||
ans = get_input(question + ' (y/n)? [' + d + '] ')
|
||||
if not ans:
|
||||
ans = d
|
||||
return ans.lower() == 'y'
|
||||
"""
|
||||
d = 'n'
|
||||
if default:
|
||||
d = 'y'
|
||||
ans = get_input(question + ' (y/n)? [' + d + '] ')
|
||||
if not ans:
|
||||
ans = d
|
||||
return ans.lower() == 'y'
|
||||
|
||||
def _modules(self):
|
||||
home = os.getcwd()
|
||||
modules_dir = os.path.join(home, 'modules')
|
||||
filenames = sopel.loader.enumerate_modules(self)
|
||||
os.sys.path.insert(0, modules_dir)
|
||||
for name, mod_spec in iteritems(filenames):
|
||||
path, type_ = mod_spec
|
||||
try:
|
||||
module, _ = sopel.loader.load_module(name, path, type_)
|
||||
except Exception as e:
|
||||
filename, lineno = sopel.tools.get_raising_file_and_line()
|
||||
rel_path = os.path.relpath(filename, os.path.dirname(__file__))
|
||||
raising_stmt = "%s:%d" % (rel_path, lineno)
|
||||
stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
|
||||
else:
|
||||
if hasattr(module, 'configure'):
|
||||
prompt = name + ' module'
|
||||
if module.__doc__:
|
||||
doc = module.__doc__.split('\n', 1)[0]
|
||||
if doc:
|
||||
prompt = doc
|
||||
prompt = 'Configure {} (y/n)? [n]'.format(prompt)
|
||||
do_configure = get_input(prompt)
|
||||
do_configure = do_configure and do_configure.lower() == 'y'
|
||||
if do_configure:
|
||||
module.configure(self)
|
||||
self.save()
|
||||
def _modules(self):
|
||||
home = os.getcwd()
|
||||
modules_dir = os.path.join(home, 'modules')
|
||||
filenames = sopel.loader.enumerate_modules(self)
|
||||
os.sys.path.insert(0, modules_dir)
|
||||
for name, mod_spec in iteritems(filenames):
|
||||
path, type_ = mod_spec
|
||||
try:
|
||||
module, _ = sopel.loader.load_module(name, path, type_)
|
||||
except Exception as e:
|
||||
filename, lineno = sopel.tools.get_raising_file_and_line()
|
||||
rel_path = os.path.relpath(filename, os.path.dirname(__file__))
|
||||
raising_stmt = "%s:%d" % (rel_path, lineno)
|
||||
stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
|
||||
else:
|
||||
if hasattr(module, 'configure'):
|
||||
prompt = name + ' module'
|
||||
if module.__doc__:
|
||||
doc = module.__doc__.split('\n', 1)[0]
|
||||
if doc:
|
||||
prompt = doc
|
||||
prompt = 'Configure {} (y/n)? [n]'.format(prompt)
|
||||
do_configure = get_input(prompt)
|
||||
do_configure = do_configure and do_configure.lower() == 'y'
|
||||
if do_configure:
|
||||
module.configure(self)
|
||||
self.save()
|
||||
|
||||
|
||||
def _wizard(section, config=None):
|
||||
dotdir = os.path.expanduser('~/.sopel')
|
||||
configpath = os.path.join(dotdir, (config or 'default') + '.cfg')
|
||||
if section == 'all':
|
||||
_create_config(configpath)
|
||||
elif section == 'mod':
|
||||
_check_dir(False)
|
||||
if not os.path.isfile(configpath):
|
||||
print("No config file found." +
|
||||
" Please make one before configuring these options.")
|
||||
sys.exit(1)
|
||||
config = Config(configpath, validate=False)
|
||||
config._modules()
|
||||
dotdir = os.path.expanduser('~/.sopel')
|
||||
configpath = os.path.join(dotdir, (config or 'default') + '.cfg')
|
||||
if section == 'all':
|
||||
_create_config(configpath)
|
||||
elif section == 'mod':
|
||||
_check_dir(False)
|
||||
if not os.path.isfile(configpath):
|
||||
print("No config file found." +
|
||||
" Please make one before configuring these options.")
|
||||
sys.exit(1)
|
||||
config = Config(configpath, validate=False)
|
||||
config._modules()
|
||||
|
||||
|
||||
def _check_dir(create=True):
|
||||
dotdir = os.path.join(os.path.expanduser('~'), '.sopel')
|
||||
if not os.path.isdir(dotdir):
|
||||
if create:
|
||||
print('Creating a config directory at ~/.sopel...')
|
||||
try:
|
||||
os.makedirs(dotdir)
|
||||
except Exception as e:
|
||||
print('There was a problem creating %s:' % dotdir, file=sys.stderr)
|
||||
print('%s, %s' % (e.__class__, str(e)), file=sys.stderr)
|
||||
print('Please fix this and then run Sopel again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("No config file found. Please make one before configuring these options.")
|
||||
sys.exit(1)
|
||||
dotdir = os.path.join(os.path.expanduser('~'), '.sopel')
|
||||
if not os.path.isdir(dotdir):
|
||||
if create:
|
||||
print('Creating a config directory at ~/.sopel...')
|
||||
try:
|
||||
os.makedirs(dotdir)
|
||||
except Exception as e:
|
||||
print('There was a problem creating %s:' % dotdir, file=sys.stderr)
|
||||
print('%s, %s' % (e.__class__, str(e)), file=sys.stderr)
|
||||
print('Please fix this and then run Sopel again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("No config file found. Please make one before configuring these options.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _create_config(configpath):
|
||||
_check_dir()
|
||||
print("Please answer the following questions" +
|
||||
" to create your configuration file:\n")
|
||||
try:
|
||||
config = Config(configpath, validate=False)
|
||||
sopel.config.core_section.configure(config)
|
||||
if config.option(
|
||||
'Would you like to see if there are any modules'
|
||||
' that need configuring'
|
||||
):
|
||||
config._modules()
|
||||
config.save()
|
||||
except Exception:
|
||||
print("Encountered an error while writing the config file." +
|
||||
" This shouldn't happen. Check permissions.")
|
||||
raise
|
||||
sys.exit(1)
|
||||
print("Config file written sucessfully!")
|
||||
_check_dir()
|
||||
print("Please answer the following questions" +
|
||||
" to create your configuration file:\n")
|
||||
try:
|
||||
config = Config(configpath, validate=False)
|
||||
sopel.config.core_section.configure(config)
|
||||
if config.option(
|
||||
'Would you like to see if there are any modules'
|
||||
' that need configuring'
|
||||
):
|
||||
config._modules()
|
||||
config.save()
|
||||
except Exception:
|
||||
print("Encountered an error while writing the config file." +
|
||||
" This shouldn't happen. Check permissions.")
|
||||
raise
|
||||
sys.exit(1)
|
||||
print("Config file written sucessfully!")
|
||||
|
|
4
irc.py
4
irc.py
|
@ -332,12 +332,12 @@ class Bot(asynchat.async_chat):
|
|||
LOGGER.error("Could not save traceback from %s to file: %s", trigger.sender, str(e))
|
||||
|
||||
if trigger and self.config.core.reply_errors and trigger.sender is not None:
|
||||
self.msg(trigger.sender, signature)
|
||||
self.say(signature, trigger.sender)
|
||||
if trigger:
|
||||
LOGGER.error('Exception from {}: {} ({})'.format(trigger.sender, str(signature), trigger.raw))
|
||||
except Exception as e:
|
||||
if trigger and self.config.core.reply_errors and trigger.sender is not None:
|
||||
self.msg(trigger.sender, "Got an error.")
|
||||
self.say("Got an error.", trigger.sender)
|
||||
if trigger:
|
||||
LOGGER.error('Exception from {}: {} ({})'.format(trigger.sender, str(e), trigger.raw))
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Methods for loading modules.
|
||||
"""
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# coding=utf-8
|
||||
"""This contains decorators and tools for creating callable plugin functions.
|
||||
"""
|
||||
# Copyright 2013, Ari Koivula, <ari@koivu.la>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ASCII
|
||||
"""
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
help.py - Sopel Help Module
|
||||
Copyright 2008, Sean B. Palmer, inamidst.com
|
||||
|
|
|
@ -70,9 +70,9 @@ def setup(bot):
|
|||
for oldtime in oldtimes:
|
||||
for (channel, nick, message) in bot.rdb[oldtime]:
|
||||
if message:
|
||||
bot.say(nick + ': ' + message)
|
||||
bot.say(nick + ': ' + message, channel)
|
||||
else:
|
||||
bot.say(nick + '!')
|
||||
bot.say(nick + '!', channel)
|
||||
del bot.rdb[oldtime]
|
||||
dump_database(bot.rfn, bot.rdb)
|
||||
time.sleep(2.5)
|
||||
|
|
23
modules/rundown.py
Executable file
23
modules/rundown.py
Executable file
|
@ -0,0 +1,23 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Redpill on the Bogdanovs.
|
||||
"""
|
||||
import os
|
||||
import random
|
||||
|
||||
from module import commands, example
|
||||
|
||||
@commands("rundown")
|
||||
@example(".rundown")
|
||||
def grog(bot, trigger):
|
||||
"""
|
||||
Provides rundown on demand.
|
||||
"""
|
||||
if trigger.group(2) in ["-c", "--cabal"]:
|
||||
with open(os.path.join(bot.config.homedir, "cabaldown.txt"), "r") as file:
|
||||
data = file.read()
|
||||
else:
|
||||
with open(os.path.join(bot.config.homedir, "rundown.txt"), "r") as file:
|
||||
data = file.read()
|
||||
bot.say(data)
|
|
@ -1,19 +1,10 @@
|
|||
# coding=utf-8
|
||||
"""Useful miscellaneous tools and shortcuts for Sopel modules
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Useful miscellaneous tools and shortcuts for Sopel modules
|
||||
|
||||
*Availability: 3+*
|
||||
"""
|
||||
|
||||
# tools.py - Sopel misc tools
|
||||
# Copyright 2008, Sean B. Palmer, inamidst.com
|
||||
# Copyright © 2012, Elad Alfassa <elad@fedoraproject.org>
|
||||
# Copyright 2012, Elsie Powell, embolalia.com
|
||||
# Licensed under the Eiffel Forum License 2.
|
||||
|
||||
# https://sopel.chat
|
||||
|
||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
@ -24,82 +15,72 @@ from collections import defaultdict
|
|||
|
||||
from tools._events import events # NOQA
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
raw_input = input
|
||||
unicode = str
|
||||
iteritems = dict.items
|
||||
itervalues = dict.values
|
||||
iterkeys = dict.keys
|
||||
else:
|
||||
iteritems = dict.iteritems
|
||||
itervalues = dict.itervalues
|
||||
iterkeys = dict.iterkeys
|
||||
iteritems = dict.items
|
||||
itervalues = dict.values
|
||||
iterkeys = dict.keys
|
||||
|
||||
_channel_prefixes = ('#', '&', '+', '!')
|
||||
|
||||
|
||||
def get_input(prompt):
|
||||
"""Get decoded input from the terminal (equivalent to python 3's ``input``).
|
||||
"""
|
||||
if sys.version_info.major >= 3:
|
||||
return input(prompt)
|
||||
else:
|
||||
return raw_input(prompt).decode('utf8')
|
||||
"""Get decoded input from the terminal (equivalent to python 3's ``input``).
|
||||
"""
|
||||
return input(prompt)
|
||||
|
||||
|
||||
def get_raising_file_and_line(tb=None):
|
||||
"""Return the file and line number of the statement that raised the tb.
|
||||
"""Return the file and line number of the statement that raised the tb.
|
||||
|
||||
Returns: (filename, lineno) tuple
|
||||
Returns: (filename, lineno) tuple
|
||||
|
||||
"""
|
||||
if not tb:
|
||||
tb = sys.exc_info()[2]
|
||||
"""
|
||||
if not tb:
|
||||
tb = sys.exc_info()[2]
|
||||
|
||||
filename, lineno, _context, _line = traceback.extract_tb(tb)[-1]
|
||||
filename, lineno, _context, _line = traceback.extract_tb(tb)[-1]
|
||||
|
||||
return filename, lineno
|
||||
return filename, lineno
|
||||
|
||||
|
||||
def get_command_regexp(prefix, command):
|
||||
"""Return a compiled regexp object that implements the command."""
|
||||
# Escape all whitespace with a single backslash. This ensures that regexp
|
||||
# in the prefix is treated as it was before the actual regexp was changed
|
||||
# to use the verbose syntax.
|
||||
prefix = re.sub(r"(\s)", r"\\\1", prefix)
|
||||
"""Return a compiled regexp object that implements the command."""
|
||||
# Escape all whitespace with a single backslash. This ensures that regexp
|
||||
# in the prefix is treated as it was before the actual regexp was changed
|
||||
# to use the verbose syntax.
|
||||
prefix = re.sub(r"(\s)", r"\\\1", prefix)
|
||||
|
||||
# This regexp match equivalently and produce the same
|
||||
# groups 1 and 2 as the old regexp: r'^%s(%s)(?: +(.*))?$'
|
||||
# The only differences should be handling all whitespace
|
||||
# like spaces and the addition of groups 3-6.
|
||||
pattern = r"""
|
||||
(?:{prefix})({command}) # Command as group 1.
|
||||
(?:\s+ # Whitespace to end command.
|
||||
( # Rest of the line as group 2.
|
||||
(?:(\S+))? # Parameters 1-4 as groups 3-6.
|
||||
(?:\s+(\S+))?
|
||||
(?:\s+(\S+))?
|
||||
(?:\s+(\S+))?
|
||||
.* # Accept anything after the parameters.
|
||||
# Leave it up to the module to parse
|
||||
# the line.
|
||||
))? # Group 2 must be None, if there are no
|
||||
# parameters.
|
||||
$ # EoL, so there are no partial matches.
|
||||
""".format(prefix=prefix, command=command)
|
||||
return re.compile(pattern, re.IGNORECASE | re.VERBOSE)
|
||||
# This regexp match equivalently and produce the same
|
||||
# groups 1 and 2 as the old regexp: r'^%s(%s)(?: +(.*))?$'
|
||||
# The only differences should be handling all whitespace
|
||||
# like spaces and the addition of groups 3-6.
|
||||
pattern = r"""
|
||||
(?:{prefix})({command}) # Command as group 1.
|
||||
(?:\s+ # Whitespace to end command.
|
||||
( # Rest of the line as group 2.
|
||||
(?:(\S+))? # Parameters 1-4 as groups 3-6.
|
||||
(?:\s+(\S+))?
|
||||
(?:\s+(\S+))?
|
||||
(?:\s+(\S+))?
|
||||
.* # Accept anything after the parameters.
|
||||
# Leave it up to the module to parse
|
||||
# the line.
|
||||
))? # Group 2 must be None, if there are no
|
||||
# parameters.
|
||||
$ # EoL, so there are no partial matches.
|
||||
""".format(prefix=prefix, command=command)
|
||||
return re.compile(pattern, re.IGNORECASE | re.VERBOSE)
|
||||
|
||||
|
||||
def deprecated(old):
|
||||
def new(*args, **kwargs):
|
||||
print('Function %s is deprecated.' % old.__name__, file=sys.stderr)
|
||||
trace = traceback.extract_stack()
|
||||
for line in traceback.format_list(trace[:-1]):
|
||||
stderr(line[:-1])
|
||||
return old(*args, **kwargs)
|
||||
new.__doc__ = old.__doc__
|
||||
new.__name__ = old.__name__
|
||||
return new
|
||||
def new(*args, **kwargs):
|
||||
print('Function %s is deprecated.' % old.__name__, file=sys.stderr)
|
||||
trace = traceback.extract_stack()
|
||||
for line in traceback.format_list(trace[:-1]):
|
||||
stderr(line[:-1])
|
||||
return old(*args, **kwargs)
|
||||
new.__doc__ = old.__doc__
|
||||
new.__name__ = old.__name__
|
||||
return new
|
||||
|
||||
|
||||
# from
|
||||
|
@ -107,144 +88,144 @@ def deprecated(old):
|
|||
# A simple class to make mutli dimensional dict easy to use
|
||||
class Ddict(dict):
|
||||
|
||||
"""Class for multi-dimensional ``dict``.
|
||||
"""Class for multi-dimensional ``dict``.
|
||||
|
||||
A simple helper class to ease the creation of multi-dimensional ``dict``\s.
|
||||
A simple helper class to ease the creation of multi-dimensional ``dict``\s.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, default=None):
|
||||
self.default = default
|
||||
def __init__(self, default=None):
|
||||
self.default = default
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self:
|
||||
self[key] = self.default()
|
||||
return dict.__getitem__(self, key)
|
||||
def __getitem__(self, key):
|
||||
if key not in self:
|
||||
self[key] = self.default()
|
||||
return dict.__getitem__(self, key)
|
||||
|
||||
|
||||
class Identifier(unicode):
|
||||
"""A `unicode` subclass which acts appropriately for IRC identifiers.
|
||||
class Identifier(str):
|
||||
"""A `str` subclass which acts appropriately for IRC identifiers.
|
||||
|
||||
When used as normal `unicode` objects, case will be preserved.
|
||||
However, when comparing two Identifier objects, or comparing a Identifier
|
||||
object with a `unicode` object, the comparison will be case insensitive.
|
||||
This case insensitivity includes the case convention conventions regarding
|
||||
``[]``, ``{}``, ``|``, ``\\``, ``^`` and ``~`` described in RFC 2812.
|
||||
"""
|
||||
When used as normal `str` objects, case will be preserved.
|
||||
However, when comparing two Identifier objects, or comparing a Identifier
|
||||
object with a `str` object, the comparison will be case insensitive.
|
||||
This case insensitivity includes the case convention conventions regarding
|
||||
``[]``, ``{}``, ``|``, ``\\``, ``^`` and ``~`` described in RFC 2812.
|
||||
"""
|
||||
|
||||
def __new__(cls, identifier):
|
||||
# According to RFC2812, identifiers have to be in the ASCII range.
|
||||
# However, I think it's best to let the IRCd determine that, and we'll
|
||||
# just assume unicode. It won't hurt anything, and is more internally
|
||||
# consistent. And who knows, maybe there's another use case for this
|
||||
# weird case convention.
|
||||
s = unicode.__new__(cls, identifier)
|
||||
s._lowered = Identifier._lower(identifier)
|
||||
return s
|
||||
def __new__(cls, identifier):
|
||||
# According to RFC2812, identifiers have to be in the ASCII range.
|
||||
# However, I think it's best to let the IRCd determine that, and we'll
|
||||
# just assume str. It won't hurt anything, and is more internally
|
||||
# consistent. And who knows, maybe there's another use case for this
|
||||
# weird case convention.
|
||||
s = str.__new__(cls, identifier)
|
||||
s._lowered = Identifier._lower(identifier)
|
||||
return s
|
||||
|
||||
def lower(self):
|
||||
"""Return the identifier converted to lower-case per RFC 2812."""
|
||||
return self._lowered
|
||||
def lower(self):
|
||||
"""Return the identifier converted to lower-case per RFC 2812."""
|
||||
return self._lowered
|
||||
|
||||
@staticmethod
|
||||
def _lower(identifier):
|
||||
"""Returns `identifier` in lower case per RFC 2812."""
|
||||
# The tilde replacement isn't needed for identifiers, but is for
|
||||
# channels, which may be useful at some point in the future.
|
||||
low = identifier.lower().replace('{', '[').replace('}', ']')
|
||||
low = low.replace('|', '\\').replace('^', '~')
|
||||
return low
|
||||
@staticmethod
|
||||
def _lower(identifier):
|
||||
"""Returns `identifier` in lower case per RFC 2812."""
|
||||
# The tilde replacement isn't needed for identifiers, but is for
|
||||
# channels, which may be useful at some point in the future.
|
||||
low = identifier.lower().replace('{', '[').replace('}', ']')
|
||||
low = low.replace('|', '\\').replace('^', '~')
|
||||
return low
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.__str__()
|
||||
)
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (
|
||||
self.__class__.__name__,
|
||||
self.__str__()
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return self._lowered.__hash__()
|
||||
def __hash__(self):
|
||||
return self._lowered.__hash__()
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered < other._lowered
|
||||
return self._lowered < Identifier._lower(other)
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered < other._lowered
|
||||
return self._lowered < Identifier._lower(other)
|
||||
|
||||
def __le__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered <= other._lowered
|
||||
return self._lowered <= Identifier._lower(other)
|
||||
def __le__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered <= other._lowered
|
||||
return self._lowered <= Identifier._lower(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered > other._lowered
|
||||
return self._lowered > Identifier._lower(other)
|
||||
def __gt__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered > other._lowered
|
||||
return self._lowered > Identifier._lower(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered >= other._lowered
|
||||
return self._lowered >= Identifier._lower(other)
|
||||
def __ge__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered >= other._lowered
|
||||
return self._lowered >= Identifier._lower(other)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered == other._lowered
|
||||
return self._lowered == Identifier._lower(other)
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Identifier):
|
||||
return self._lowered == other._lowered
|
||||
return self._lowered == Identifier._lower(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def is_nick(self):
|
||||
"""Returns True if the Identifier is a nickname (as opposed to channel)
|
||||
"""
|
||||
return self and not self.startswith(_channel_prefixes)
|
||||
def is_nick(self):
|
||||
"""Returns True if the Identifier is a nickname (as opposed to channel)
|
||||
"""
|
||||
return self and not self.startswith(_channel_prefixes)
|
||||
|
||||
|
||||
class OutputRedirect(object):
|
||||
|
||||
"""Redirect te output to the terminal and a log file.
|
||||
"""Redirect te output to the terminal and a log file.
|
||||
|
||||
A simplified object used to write to both the terminal and a log file.
|
||||
A simplified object used to write to both the terminal and a log file.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, logpath, stderr=False, quiet=False):
|
||||
"""Create an object which will to to a file and the terminal.
|
||||
def __init__(self, logpath, stderr=False, quiet=False):
|
||||
"""Create an object which will to to a file and the terminal.
|
||||
|
||||
Create an object which will log to the file at ``logpath`` as well as
|
||||
the terminal.
|
||||
If ``stderr`` is given and true, it will write to stderr rather than
|
||||
stdout.
|
||||
If ``quiet`` is given and True, data will be written to the log file
|
||||
only, but not the terminal.
|
||||
Create an object which will log to the file at ``logpath`` as well as
|
||||
the terminal.
|
||||
If ``stderr`` is given and true, it will write to stderr rather than
|
||||
stdout.
|
||||
If ``quiet`` is given and True, data will be written to the log file
|
||||
only, but not the terminal.
|
||||
|
||||
"""
|
||||
self.logpath = logpath
|
||||
self.stderr = stderr
|
||||
self.quiet = quiet
|
||||
"""
|
||||
self.logpath = logpath
|
||||
self.stderr = stderr
|
||||
self.quiet = quiet
|
||||
|
||||
def write(self, string):
|
||||
"""Write the given ``string`` to the logfile and terminal."""
|
||||
if not self.quiet:
|
||||
try:
|
||||
if self.stderr:
|
||||
sys.__stderr__.write(string)
|
||||
else:
|
||||
sys.__stdout__.write(string)
|
||||
except:
|
||||
pass
|
||||
def write(self, string):
|
||||
"""Write the given ``string`` to the logfile and terminal."""
|
||||
if not self.quiet:
|
||||
try:
|
||||
if self.stderr:
|
||||
sys.__stderr__.write(string)
|
||||
else:
|
||||
sys.__stdout__.write(string)
|
||||
except:
|
||||
pass
|
||||
|
||||
with codecs.open(self.logpath, 'ab', encoding="utf8",
|
||||
errors='xmlcharrefreplace') as logfile:
|
||||
try:
|
||||
logfile.write(string)
|
||||
except UnicodeDecodeError:
|
||||
# we got an invalid string, safely encode it to utf-8
|
||||
logfile.write(unicode(string, 'utf8', errors="replace"))
|
||||
with codecs.open(self.logpath, 'ab', encoding="utf8",
|
||||
errors='xmlcharrefreplace') as logfile:
|
||||
try:
|
||||
logfile.write(string)
|
||||
except strDecodeError:
|
||||
# we got an invalid string, safely encode it to utf-8
|
||||
logfile.write(str(string, 'utf8', errors="replace"))
|
||||
|
||||
def flush(self):
|
||||
if self.stderr:
|
||||
sys.__stderr__.flush()
|
||||
else:
|
||||
sys.__stdout__.flush()
|
||||
def flush(self):
|
||||
if self.stderr:
|
||||
sys.__stderr__.flush()
|
||||
else:
|
||||
sys.__stdout__.flush()
|
||||
|
||||
|
||||
# These seems to trace back to when we thought we needed a try/except on prints,
|
||||
|
@ -252,101 +233,101 @@ class OutputRedirect(object):
|
|||
# 4.0^H^H^H5.0^H^H^H6.0^H^H^Hsome version when someone can be bothered.
|
||||
@deprecated
|
||||
def stdout(string):
|
||||
print(string)
|
||||
print(string)
|
||||
|
||||
|
||||
def stderr(string):
|
||||
"""Print the given ``string`` to stderr.
|
||||
"""Print the given ``string`` to stderr.
|
||||
|
||||
This is equivalent to ``print >> sys.stderr, string``
|
||||
This is equivalent to ``print >> sys.stderr, string``
|
||||
|
||||
"""
|
||||
print(string, file=sys.stderr)
|
||||
"""
|
||||
print(string, file=sys.stderr)
|
||||
|
||||
|
||||
def check_pid(pid):
|
||||
"""Check if a process is running with the given ``PID``.
|
||||
"""Check if a process is running with the given ``PID``.
|
||||
|
||||
*Availability: Only on POSIX systems*
|
||||
*Availability: Only on POSIX systems*
|
||||
|
||||
Return ``True`` if there is a process running with the given ``PID``.
|
||||
Return ``True`` if there is a process running with the given ``PID``.
|
||||
|
||||
"""
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except OSError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
"""
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except OSError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def get_hostmask_regex(mask):
|
||||
"""Return a compiled `re.RegexObject` for an IRC hostmask"""
|
||||
mask = re.escape(mask)
|
||||
mask = mask.replace(r'\*', '.*')
|
||||
return re.compile(mask + '$', re.I)
|
||||
"""Return a compiled `re.RegexObject` for an IRC hostmask"""
|
||||
mask = re.escape(mask)
|
||||
mask = mask.replace(r'\*', '.*')
|
||||
return re.compile(mask + '$', re.I)
|
||||
|
||||
|
||||
class SopelMemory(dict):
|
||||
|
||||
"""A simple thread-safe dict implementation.
|
||||
"""A simple thread-safe dict implementation.
|
||||
|
||||
*Availability: 4.0; available as ``Sopel.SopelMemory`` in 3.1.0 - 3.2.0*
|
||||
*Availability: 4.0; available as ``Sopel.SopelMemory`` in 3.1.0 - 3.2.0*
|
||||
|
||||
In order to prevent exceptions when iterating over the values and changing
|
||||
them at the same time from different threads, we use a blocking lock on
|
||||
``__setitem__`` and ``contains``.
|
||||
In order to prevent exceptions when iterating over the values and changing
|
||||
them at the same time from different threads, we use a blocking lock on
|
||||
``__setitem__`` and ``contains``.
|
||||
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
dict.__init__(self, *args)
|
||||
self.lock = threading.Lock()
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
dict.__init__(self, *args)
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.lock.acquire()
|
||||
result = dict.__setitem__(self, key, value)
|
||||
self.lock.release()
|
||||
return result
|
||||
def __setitem__(self, key, value):
|
||||
self.lock.acquire()
|
||||
result = dict.__setitem__(self, key, value)
|
||||
self.lock.release()
|
||||
return result
|
||||
|
||||
def __contains__(self, key):
|
||||
"""Check if a key is in the dict.
|
||||
def __contains__(self, key):
|
||||
"""Check if a key is in the dict.
|
||||
|
||||
It locks it for writes when doing so.
|
||||
It locks it for writes when doing so.
|
||||
|
||||
"""
|
||||
self.lock.acquire()
|
||||
result = dict.__contains__(self, key)
|
||||
self.lock.release()
|
||||
return result
|
||||
"""
|
||||
self.lock.acquire()
|
||||
result = dict.__contains__(self, key)
|
||||
self.lock.release()
|
||||
return result
|
||||
|
||||
def contains(self, key):
|
||||
"""Backwards compatability with 3.x, use `in` operator instead."""
|
||||
return self.__contains__(key)
|
||||
def contains(self, key):
|
||||
"""Backwards compatability with 3.x, use `in` operator instead."""
|
||||
return self.__contains__(key)
|
||||
|
||||
|
||||
class SopelMemoryWithDefault(defaultdict):
|
||||
"""Same as SopelMemory, but subclasses from collections.defaultdict."""
|
||||
def __init__(self, *args):
|
||||
defaultdict.__init__(self, *args)
|
||||
self.lock = threading.Lock()
|
||||
"""Same as SopelMemory, but subclasses from collections.defaultdict."""
|
||||
def __init__(self, *args):
|
||||
defaultdict.__init__(self, *args)
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.lock.acquire()
|
||||
result = defaultdict.__setitem__(self, key, value)
|
||||
self.lock.release()
|
||||
return result
|
||||
def __setitem__(self, key, value):
|
||||
self.lock.acquire()
|
||||
result = defaultdict.__setitem__(self, key, value)
|
||||
self.lock.release()
|
||||
return result
|
||||
|
||||
def __contains__(self, key):
|
||||
"""Check if a key is in the dict.
|
||||
def __contains__(self, key):
|
||||
"""Check if a key is in the dict.
|
||||
|
||||
It locks it for writes when doing so.
|
||||
It locks it for writes when doing so.
|
||||
|
||||
"""
|
||||
self.lock.acquire()
|
||||
result = defaultdict.__contains__(self, key)
|
||||
self.lock.release()
|
||||
return result
|
||||
"""
|
||||
self.lock.acquire()
|
||||
result = defaultdict.__contains__(self, key)
|
||||
self.lock.release()
|
||||
return result
|
||||
|
||||
def contains(self, key):
|
||||
"""Backwards compatability with 3.x, use `in` operator instead."""
|
||||
return self.__contains__(key)
|
||||
def contains(self, key):
|
||||
"""Backwards compatability with 3.x, use `in` operator instead."""
|
||||
return self.__contains__(key)
|
||||
|
|
Loading…
Reference in New Issue
Block a user