Skip to content

Commit

Permalink
tools: Second pass at overhauling docstrings/comments
Browse files Browse the repository at this point in the history
Squash after review.
  • Loading branch information
dgw committed Apr 22, 2019
1 parent 5129b60 commit 0ad35e5
Showing 1 changed file with 87 additions and 47 deletions.
134 changes: 87 additions & 47 deletions sopel/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
def get_input(prompt):
"""Get decoded input from the terminal (equivalent to Python 3's ``input``).
:param str prompt: The string to display as a prompt on the terminal
:param str prompt: what to display as a prompt on the terminal
:return: the user's input
:rtype: str
"""
if sys.version_info.major >= 3:
return input(prompt)
Expand All @@ -55,8 +57,8 @@ def get_input(prompt):
def get_raising_file_and_line(tb=None):
"""Get the file and line number where an exception happened.
:param tb: The traceback (uses the most recent exception if not given)
:return: (filename, lineno) tuple
:param tb: the traceback (uses the most recent exception if not given)
:return: a tuple of the filename and line number
:rtype: (str, int)
"""
if not tb:
Expand All @@ -68,14 +70,14 @@ def get_raising_file_and_line(tb=None):


def compile_rule(nick, pattern, alias_nicks):
"""Compile a rule regex and fill in nickname placeholders
"""Compile a rule regex and fill in nickname placeholders.
:param str nick: The nickname to use when replacing ``$nick`` and
:param str nick: the nickname to use when replacing ``$nick`` and
``$nickname`` placeholders in the ``pattern``
:param str pattern: The rule regex pattern
:param list alias_nicks: A list of alternatives that should also be accepted
:param str pattern: the rule regex pattern
:param list alias_nicks: a list of alternatives that should also be accepted
instead of ``nick``
:return: The compiled regex ``pattern``, with placeholders for ``$nick`` and
:return: the compiled regex ``pattern``, with placeholders for ``$nick`` and
``$nickname`` filled in
:rtype: :py:class:`re.Pattern`
Expand Down Expand Up @@ -104,9 +106,9 @@ def compile_rule(nick, pattern, alias_nicks):
def get_command_regexp(prefix, command):
"""Get a compiled regexp object that implements the command.
:param str prefix: The bot's command prefix (interpreted as regex)
:param str command: The name of the command
:return: A compiled regexp object that implements the command.
:param str prefix: the command prefix (interpreted as regex)
:param str command: the name of the command
:return: a compiled regexp object that implements the command
:rtype: :py:class:`re.Pattern`
"""
# Escape all whitespace with a single backslash. This ensures that regexp
Expand All @@ -121,9 +123,9 @@ def get_command_regexp(prefix, command):
def get_command_pattern(prefix, command):
"""Get the uncompiled regex pattern for standard commands.
:param str prefix: The bot's command prefix (interpreted as regex)
:param str command: The name of the command
:return: A regex pattern string implementing the given command
:param str prefix: the command prefix (interpreted as regex)
:param str command: the command name
:return: a regex pattern that will match the given command
:rtype: str
"""
# This regexp matches equivalently and produces the same
Expand All @@ -150,11 +152,11 @@ def get_command_pattern(prefix, command):
def get_nickname_command_regexp(nick, command, alias_nicks):
"""Get a compiled regexp object that implements the nickname command.
:param str nick: The bot's nickname
:param str command: The name of the command
:param list alias_nicks: A list of alternatives that should also be accepted
:param str nick: the bot's nickname
:param str command: the command name
:param list alias_nicks: a list of alternatives that should also be accepted
instead of ``nick``
:return: A compiled regex pattern that implements the given nickname command
:return: a compiled regex pattern that implements the given nickname command
:rtype: :py:class:`re.Pattern`
"""
if isinstance(alias_nicks, unicode):
Expand All @@ -168,8 +170,8 @@ def get_nickname_command_regexp(nick, command, alias_nicks):
def get_nickname_command_pattern(command):
"""Get the uncompiled regex pattern for a nickname command.
:param str command: The name of the command
:return: The uncompiled regex pattern for the given nickname command
:param str command: the command name
:return: a regex pattern that will match the given nickname command
:rtype: str
"""
return r"""
Expand All @@ -192,16 +194,17 @@ def get_nickname_command_pattern(command):
def get_sendable_message(text, max_length=400):
"""Get a sendable ``text`` message, with its excess when needed.
:param str txt: unicode string of text to send
:param str txt: text to send (expects Unicode-encoded string)
:param int max_length: maximum length of the message to be sendable
:return: a tuple of two values, the sendable text and its excess text
:rtype: (str, str)
We're arbitrarily saying that the max is 400 bytes of text when
messages will be split. Otherwise, we'd have to account for the bot's
hostmask, which is hard.
The `max_length` is the max length of text in **bytes**, but we take
care of unicode 2-bytes characters, by working on the unicode string,
The ``max_length`` is the max length of text in **bytes**, but we take
care of Unicode 2-byte characters by working on the Unicode string,
then making sure the bytes version is smaller than the max length.
"""
unicode_max_length = max_length
Expand All @@ -224,7 +227,11 @@ def get_sendable_message(text, max_length=400):


def deprecated(old):
"""Decorator to mark deprecated functions in Sopel's API"""
"""Decorator to mark deprecated functions in Sopel's API
Any time a decorated function is used, a deprecation warning will be printed
to the console/log-file.
"""
def new(*args, **kwargs):
print('Function %s is deprecated.' % old.__name__, file=sys.stderr)
trace = traceback.extract_stack()
Expand All @@ -236,14 +243,12 @@ def new(*args, **kwargs):
return new


# from
# This class was taken from the page below, which no longer exists. The current
# site has nothing related to it, and it was never captured by archive.org.
# Maybe the original author can provide a current link (asked on Twitter)
# http://parand.com/say/index.php/2007/07/13/simple-multi-dimensional-dictionaries-in-python/
# A simple class to make multi-dimensional dict easy to use
class Ddict(dict):
"""Class for multi-dimensional ``dict``.
A simple helper class to ease the creation of multi-dimensional ``dict``\\s.
"""
"""A simple class to make multi-dimensional ``dict``\\s easy to use."""
def __init__(self, default=None):
self.default = default

Expand All @@ -262,6 +267,9 @@ class Identifier(unicode):
This case insensitivity includes the case convention conventions regarding
``[]``, ``{}``, ``|``, ``\\``, ``^`` and ``~`` described in RFC 2812.
"""
# May want to tweak this and update documentation accordingly when dropping
# Python 2 support, since in py3 plain str is Unicode and a "unicode" type
# no longer exists. Probably lots of code will need tweaking, tbh.

def __new__(cls, identifier):
# According to RFC2812, identifiers have to be in the ASCII range.
Expand All @@ -274,12 +282,22 @@ def __new__(cls, identifier):
return s

def lower(self):
"""Return the identifier converted to lower-case per RFC 2812."""
"""Get the RFC 2812-compliant lowercase version of this identifier.
:return: RFC 2812-compliant lowercase version of the
:py:class:`Identifier` instance
:rtype: str
"""
return self._lowered

@staticmethod
def _lower(identifier):
"""Returns `identifier` in lower case per RFC 2812."""
"""Convert an identifier to lowercase per RFC 2812.
:param str identifier: the identifier (nickname or channel) to convert
:return: RFC 2812-compliant lowercase version of ``identifier``
:rtype: str
"""
if isinstance(identifier, Identifier):
return identifier._lowered
# The tilde replacement isn't needed for identifiers, but is for
Expand Down Expand Up @@ -326,7 +344,10 @@ def __ne__(self, other):
return not (self == other)

def is_nick(self):
"""Returns True if the Identifier is a nickname (as opposed to channel)
"""Check if the Identifier is a nickname (i.e. not a channel)
:return: ``True`` if this :py:class:`Identifier` is a nickname;
``False`` if it appears to be a channel
"""
return self and not self.startswith(_channel_prefixes)

Expand All @@ -340,10 +361,10 @@ class OutputRedirect(object):
def __init__(self, logpath, stderr=False, quiet=False):
"""Create an object which will log to both a file and the terminal.
:param str logpath: Path to the log file
:param bool stderr: Write output to stderr if ``True``, or to stdout
:param str logpath: path to the log file
:param bool stderr: write output to stderr if ``True``, or to stdout
otherwise
:param bool quiet: Write to the log file only if ``True`` (and not to
:param bool quiet: write to the log file only if ``True`` (and not to
the terminal)
Create an object which will log to the file at ``logpath`` as well as
Expand All @@ -356,7 +377,7 @@ def __init__(self, logpath, stderr=False, quiet=False):
def write(self, string):
"""Write the given ``string`` to the logfile and terminal.
:param str string: The string to write
:param str string: the string to write
"""
if not self.quiet:
try:
Expand Down Expand Up @@ -394,7 +415,7 @@ def stdout(string):
def stderr(string):
"""Print the given ``string`` to stderr.
:param str string: String to print
:param str string: the string to output
This is equivalent to ``print >> sys.stderr, string``
"""
Expand All @@ -404,12 +425,16 @@ def stderr(string):
def check_pid(pid):
"""Check if a process is running with the given ``PID``.
:param int pid: The PID to check
:param int pid: PID to check
:return bool: ``True`` if the given PID is running, ``False`` otherwise
*Availability: Only on POSIX systems*
*Availability: POSIX systems only.*
Return ``True`` if there is a process running with the given ``PID``.
.. note::
Matching the :py:func:`os.kill` behavior this function needs on Windows
was rejected in
`Python issue #14480 <https://bugs.python.org/issue14480>`_, so
:py:func:`check_pid` cannot be used on Windows systems.
"""
try:
os.kill(pid, 0)
Expand All @@ -422,8 +447,8 @@ def check_pid(pid):
def get_hostmask_regex(mask):
"""Get a compiled regex pattern for an IRC hostmask
:param str mask: The hostmask that the pattern should match
:return: A compiled regex pattern matching the given ``mask``
:param str mask: the hostmask that the pattern should match
:return: a compiled regex pattern matching the given ``mask``
:rtype: :py:class:`re.Pattern`
"""
mask = re.escape(mask)
Expand All @@ -432,15 +457,16 @@ def get_hostmask_regex(mask):


class SopelMemory(dict):
"""A simple thread-safe dict implementation.
"""A simple thread-safe ``dict`` implementation.
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
them at the same time from different threads, we use a blocking lock in
``__setitem__`` and ``contains``.
.. versionadded:: 3.1
As ``Willie.WillieMemory``
.. versionchanged:: 4.0
Moved from ``Willie.WillieMemory`` to ``tools.WillieMemory``
Moved to ``tools.WillieMemory``
.. versionchanged:: 6.0
Renamed from ``WillieMemory`` to ``SopelMemory``
"""
Expand All @@ -449,6 +475,10 @@ def __init__(self, *args):
self.lock = threading.Lock()

def __setitem__(self, key, value):
"""Set a key equal to a value.
The dict is locked for other writes while doing so.
"""
self.lock.acquire()
result = dict.__setitem__(self, key, value)
self.lock.release()
Expand All @@ -468,6 +498,8 @@ def __contains__(self, key):
def contains(self, key):
"""Check if ``key`` is in the memory
:param str key: key to check for
.. deprecated:: 7.0
Will be removed in Sopel 8. If you aren't already using the ``in``
operator, you should be.
Expand All @@ -479,14 +511,19 @@ class SopelMemoryWithDefault(defaultdict):
"""Same as SopelMemory, but subclasses from collections.defaultdict.
.. versionadded:: 4.3
As ``WillieMemoryWithDefault``
.. versionchanged:: 6.0
Renamed from ``WillieMemoryWithDefault`` to ``SopelMemoryWithDefault``
Renamed to ``SopelMemoryWithDefault``
"""
def __init__(self, *args):
defaultdict.__init__(self, *args)
self.lock = threading.Lock()

def __setitem__(self, key, value):
"""Set a key equal to a value.
The dict is locked for other writes while doing so.
"""
self.lock.acquire()
result = defaultdict.__setitem__(self, key, value)
self.lock.release()
Expand All @@ -502,9 +539,12 @@ def __contains__(self, key):
self.lock.release()
return result

@deprecated
def contains(self, key):
"""Check if ``key`` is in the memory
:param str key: key to check for
.. deprecated:: 7.0
Will be removed in Sopel 8. If you aren't already using the ``in``
operator, you should be.
Expand Down

0 comments on commit 0ad35e5

Please sign in to comment.