From af94e2787f21ede485867a0a1f4f3664a915c433 Mon Sep 17 00:00:00 2001 From: dgw Date: Tue, 31 Oct 2023 17:50:31 -0500 Subject: [PATCH 1/3] privileges, plugin, tools.target, docs: channel privileges as IntFlag I figure getting "rewrite sopel/privileges.py (97%)" is the most justified I'll ever be in my life for adding a copyright comment. :D --- docs/source/plugin/privileges.rst | 37 ++--- sopel/plugin.py | 13 +- sopel/privileges.py | 234 +++++++++++++++++------------- sopel/tools/target.py | 18 +-- 4 files changed, 169 insertions(+), 133 deletions(-) diff --git a/docs/source/plugin/privileges.rst b/docs/source/plugin/privileges.rst index 39d7996659..910fe300fa 100644 --- a/docs/source/plugin/privileges.rst +++ b/docs/source/plugin/privileges.rst @@ -11,7 +11,7 @@ IRC users can have privileges in a **channel**, given by MODE messages such as: This will give both OP and Voice privileges to the user named "Nickname" in the "#example" channel (and only in this channel). When Sopel receives a MODE message it registers and updates its knowledge of a user's privileges in a -channel, which can be used by plugins in various way. +channel, which can be used by plugins in various ways. Access rights ============= @@ -22,9 +22,10 @@ Privileged users A plugin can limit who can trigger its callables using the :func:`~sopel.plugin.require_privilege` decorator:: - from sopel import plugin, privileges + from sopel import plugin + from sopel.privileges import AccessLevel - @plugin.require_privilege(privileges.OP) + @plugin.require_privilege(AccessLevel.OP) @plugin.require_chanmsg @plugin.command('chanopcommand') def chanop_command(bot, trigger): @@ -34,7 +35,7 @@ This way, only users with OP privileges or above in a channel can use the command ``chanopcommand`` in that channel: other users will be ignored by the bot. It is possible to tell these users why with the ``message`` parameter:: - @plugin.require_privilege(privileges.OP, 'You need +o privileges.') + @plugin.require_privilege(AccessLevel.OP, 'You need +o privileges.') .. important:: @@ -50,7 +51,7 @@ Sometimes, you may want the bot to be a privileged user in a channel to allow a command. For that, there is the :func:`~sopel.plugin.require_bot_privilege` decorator:: - @plugin.require_bot_privilege(privileges.OP) + @plugin.require_bot_privilege(AccessLevel.OP) @plugin.require_chanmsg @plugin.command('opbotcommand') def change_topic(bot, trigger): @@ -63,13 +64,13 @@ the user who invokes the command. As with ``require_privilege``, you can provide an error message:: @plugin.require_bot_privilege( - privileges.OP, 'The bot needs +o privileges.') + AccessLevel.OP, 'The bot needs +o privileges.') And you **can** use both ``require_privilege`` and ``require_bot_privilege`` on the same plugin callable:: - @plugin.require_privilege(privileges.VOICE) - @plugin.require_bot_privilege(privileges.OP) + @plugin.require_privilege(AccessLevel.VOICE) + @plugin.require_bot_privilege(AccessLevel.OP) @plugin.require_chanmsg @plugin.command('special') def special_command(bot, trigger): @@ -92,7 +93,7 @@ Sometimes, a command should be used only by users who are authenticated via IRC services. On IRC networks that provide such information to IRC clients, this is possible with the :func:`~sopel.plugin.require_account` decorator:: - @plugin.require_privilege(privileges.VOICE) + @plugin.require_privilege(AccessLevel.VOICE) @plugin.require_account @plugin.require_chanmsg @plugin.command('danger') @@ -130,15 +131,15 @@ then you can get that user's privileges through the **channel**'s user_privileges = channel.privileges[trigger.nick] You can check the user's privileges manually using bitwise operators. Here -for example, we check if the user is voiced (+v) or above:: +for example, we check if the user is voiced (``+v``) or above:: - from sopel import privileges + from sopel.privileges import AccessLevel - if user_privileges & privileges.VOICE: + if user_privileges & AccessLevel.VOICE: # user is voiced - elif user_privileges > privileges.VOICE: + elif user_privileges > AccessLevel.VOICE: # not voiced, but higher privileges - # like privileges.HALFOP or privileges.OP + # like AccessLevel.HALFOP or AccessLevel.OP else: # no privilege @@ -146,9 +147,9 @@ Another option is to use dedicated methods from the ``channel`` object:: if channel.is_voiced('Nickname'): # user is voiced - elif channel.has_privilege('Nickname', privileges.VOICE): + elif channel.has_privilege('Nickname', AccessLevel.VOICE): # not voiced, but higher privileges - # like privileges.HALFOP or privileges.OP + # like AccessLevel.HALFOP or AccessLevel.OP else: # no privilege @@ -158,14 +159,14 @@ You can also iterate over the list of users and filter them by privileges:: op_users = [ user for nick, user in channel.users - if channel.is_op(nick, privileges.OP) + if channel.is_op(nick, AccessLevel.OP) ] # get users with OP privilege or above op_or_higher_users = [ user for nick, user in channel.users - if channel.has_privileges(nick, privileges.OP) + if channel.has_privileges(nick, AccessLevel.OP) ] .. seealso:: diff --git a/sopel/plugin.py b/sopel/plugin.py index 37f06fa572..b957797af0 100644 --- a/sopel/plugin.py +++ b/sopel/plugin.py @@ -24,7 +24,14 @@ ) # import and expose privileges as shortcut -from sopel.privileges import ADMIN, HALFOP, OP, OPER, OWNER, VOICE +from sopel.privileges import AccessLevel + +VOICE = AccessLevel.VOICE +HALFOP = AccessLevel.HALFOP +OP = AccessLevel.OP +ADMIN = AccessLevel.ADMIN +OWNER = AccessLevel.OWNER +OPER = AccessLevel.OPER if TYPE_CHECKING: @@ -1457,7 +1464,7 @@ def guarded(bot, trigger, *args, **kwargs): def require_privilege( - level: int, + level: AccessLevel, message: Optional[str] = None, reply: bool = False, ) -> Callable: @@ -1581,7 +1588,7 @@ def guarded(bot, trigger, *args, **kwargs): def require_bot_privilege( - level: int, + level: AccessLevel, message: Optional[str] = None, reply: bool = False, ) -> Callable: diff --git a/sopel/privileges.py b/sopel/privileges.py index 553db307a3..23053e036c 100644 --- a/sopel/privileges.py +++ b/sopel/privileges.py @@ -1,146 +1,174 @@ -"""Constants for user privileges in channels. +"""Constants for user privileges in channels.""" +# Copyright 2023 dgw, technobabbl.es +# +# Licensed under the Eiffel Forum License 2. +from __future__ import annotations -Privilege levels -================ +import enum -Historically, there were two user privileges in channels: -* :data:`OP`: channel operator, or chanop, set and unset by ``+o`` and ``-o`` -* :data:`VOICE`: the privilege to send messages to a channel with the - ``+m`` mode, set and unset by ``+v`` and ``-v`` +__all__ = [ + 'AccessLevel', +] -Since then, other privileges have been adopted by IRC servers and clients: -* :data:`HALFOP`: intermediate level between Voiced and OP, set and unset by - ``+h`` and ``-h`` -* :data:`ADMIN`: channel admin, above OP and below OWNER, set and unset by - ``+a`` and ``-a`` -* :data:`OWNER`: channel owner, above ADMIN and OP, set and unset by ``+q`` and - ``-q`` +class AccessLevel(enum.IntFlag): + """Enumeration of available user privilege levels. -.. important:: + Privilege levels + ================ - Not all IRC networks support these added privilege modes. If you are - writing a plugin for public distribution, ensure your code behaves sensibly - if only +v (voice) and +o (op) modes exist. + Historically, there were two user privilege levels in a channel: -Compare privileges -================== + * :data:`~AccessLevel.OP`: channel operator, set and unset by modes ``+o`` + and ``-o`` + * :data:`~AccessLevel.VOICE`: the privilege to send messages to a channel + with the ``+m`` mode, set and unset by modes ``+v`` and ``-v`` -This module represents privileges as powers of two, with higher values assigned -to higher-level privileges:: + Since then, other privileges have been adopted by IRC servers and clients: - >>> from sopel.privileges import VOICE, HALFOP, OP, ADMIN, OWNER - >>> VOICE < HALFOP < OP < ADMIN < OWNER - True + * :data:`~AccessLevel.HALFOP`: intermediate level between VOICE and OP, set + and unset by modes ``+h`` and ``-h`` + * :data:`~AccessLevel.ADMIN`: channel admin, above OP and below OWNER, set + and unset by modes ``+a`` and ``-a`` + * :data:`~AccessLevel.OWNER`: channel owner, above ADMIN and OP, set and + unset by modes ``+q`` and ``-q`` -Then a user's privileges are represented as a sum of privilege levels:: + .. important:: - >>> VOICE - 1 - >>> OP - 4 - >>> priv = VOICE | OP - >>> priv - 5 + Not all IRC networks support these added privilege modes. If you are + writing a plugin for public distribution, ensure your code behaves + sensibly if only +v (voice) and +o (op) modes exist. -This allows to use comparators and bitwise operators to compare privileges:: + Comparing privileges + ==================== - >>> priv >= OP - True - >>> bool(priv & HALFOP) - False + This class represents privileges as powers of two, with higher values + assigned to higher-level privileges:: -In that case, ``priv`` contains both VOICE and OP privileges, but not HALFOP. -""" -from __future__ import annotations + >>> from sopel.privileges import AccessLevel + >>> AccessLevel.VOICE < AccessLevel.HALFOP < AccessLevel.OP \\ + ... < AccessLevel.ADMIN < AccessLevel.OWNER + True + Then a user's privileges are represented as a sum of privilege levels:: -__all__ = [ - 'VOICE', - 'HALFOP', - 'OP', - 'ADMIN', - 'OWNER', - 'OPER', -] + >>> AccessLevel.VOICE + 1 + >>> AccessLevel.OP + 4 + >>> priv = AccessLevel.VOICE | AccessLevel.OP + >>> priv + 5 + + This allows using comparators and bitwise operators to compare privileges. + Here, ``priv`` contains both VOICE and OP privileges, but not HALFOP:: + + >>> priv >= AccessLevel.OP + True + >>> bool(priv & AccessLevel.HALFOP) + False + + .. important:: + + Do not refer directly to the integer value of a privilege level in your + code; the values may change. Use the appropriate member of this class as + a reference point instead. + + """ + # values should behave as ints, but their string representations should + # still look like Enum + __str__ = enum.Enum.__str__ + + VOICE = enum.auto() + """Privilege level for the +v channel permission + + .. versionadded:: 4.1 + .. versionchanged:: 8.0 + + Constant moved from :mod:`sopel.plugin` to + :class:`sopel.privileges.AccessLevel`. + + """ + + HALFOP = enum.auto() + """Privilege level for the +h channel permission + + .. versionadded:: 4.1 + .. versionchanged:: 8.0 + + Constant moved from :mod:`sopel.plugin` to + :class:`sopel.privileges.AccessLevel`. + .. important:: -VOICE = 1 -"""Privilege level for the +v channel permission + Not all IRC networks support this privilege mode. If you are writing a + plugin for public distribution, ensure your code behaves sensibly if + only ``+v`` (voice) and ``+o`` (op) modes exist. -.. versionadded:: 4.1 -.. versionchanged:: 8.0 - Moved into :mod:`sopel.privileges`. -""" + """ -HALFOP = 2 -"""Privilege level for the +h channel permission + OP = enum.auto() + """Privilege level for the +o channel permission -.. versionadded:: 4.1 -.. versionchanged:: 8.0 - Moved into :mod:`sopel.privileges`. + .. versionadded:: 4.1 + .. versionchanged:: 8.0 -.. important:: + Constant moved from :mod:`sopel.plugin` to + :class:`sopel.privileges.AccessLevel`. - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if only - ``+v`` (voice) and ``+o`` (op) modes exist. + """ -""" + ADMIN = enum.auto() + """Privilege level for the +a channel permission -OP = 4 -"""Privilege level for the +o channel permission + .. versionadded:: 4.1 + .. versionchanged:: 8.0 -.. versionadded:: 4.1 -.. versionchanged:: 8.0 - Moved into :mod:`sopel.privileges`. -""" + Constant moved from :mod:`sopel.plugin` to + :class:`sopel.privileges.AccessLevel`. -ADMIN = 8 -"""Privilege level for the +a channel permission + .. important:: -.. versionadded:: 4.1 -.. versionchanged:: 8.0 - Moved into :mod:`sopel.privileges`. + Not all IRC networks support this privilege mode. If you are writing a + plugin for public distribution, ensure your code behaves sensibly if + only ``+v`` (voice) and ``+o`` (op) modes exist. -.. important:: + """ - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if only - ``+v`` (voice) and ``+o`` (op) modes exist. + OWNER = enum.auto() + """Privilege level for the +q channel permission -""" + .. versionadded:: 4.1 + .. versionchanged:: 8.0 -OWNER = 16 -"""Privilege level for the +q channel permission + Constant moved from :mod:`sopel.plugin` to + :class:`sopel.privileges.AccessLevel`. -.. versionadded:: 4.1 -.. versionchanged:: 8.0 - Moved into :mod:`sopel.privileges`. + .. important:: -.. important:: + Not all IRC networks support this privilege mode. If you are writing a + plugin for public distribution, ensure your code behaves sensibly if + only ``+v`` (voice) and ``+o`` (op) modes exist. - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if only - ``+v`` (voice) and ``+o`` (op) modes exist. + """ -""" + OPER = enum.auto() + """Privilege level for the +y/+Y channel permission -OPER = 32 -"""Privilege level for the +y/+Y channel permissions + Note: Except for these (non-standard) channel modes, Sopel does not monitor + or store any user's OPER status. -Note: Except for these (non-standard) channel modes, Sopel does not monitor or -store any user's OPER status. + .. versionadded:: 7.0 + .. versionchanged:: 8.0 -.. versionadded:: 7.0 -.. versionchanged:: 8.0 - Moved into :mod:`sopel.privileges`. + Constant moved from :mod:`sopel.plugin` to + :class:`sopel.privileges.AccessLevel`. -.. important:: + .. important:: - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if only - ``+v`` (voice) and ``+o`` (op) modes exist. + Not all IRC networks support this privilege mode. If you are writing a + plugin for public distribution, ensure your code behaves sensibly if + only ``+v`` (voice) and ``+o`` (op) modes exist. -""" + """ diff --git a/sopel/tools/target.py b/sopel/tools/target.py index f7f34d0574..7e8a21ec4f 100644 --- a/sopel/tools/target.py +++ b/sopel/tools/target.py @@ -4,7 +4,7 @@ import functools from typing import Any, Optional, TYPE_CHECKING, Union -from sopel import privileges +from sopel.privileges import AccessLevel from sopel.tools import memories from sopel.tools.identifiers import Identifier, IdentifierFactory @@ -158,7 +158,7 @@ def __init__( This maps nickname :class:`~sopel.tools.identifiers.Identifier`\\s to bitwise integer values. This can be compared to appropriate constants - from :mod:`sopel.privileges`. + from :class:`sopel.privileges.AccessLevel`. """ self.topic: str = '' """The topic of the channel.""" @@ -203,7 +203,7 @@ def add_user(self, user: User, privs: int = 0) -> None: :param user: the new user to add :param privs: privilege bitmask (see constants in - :mod:`sopel.privileges`) + :class:`sopel.privileges.AccessLevel`) Called when a new user JOINs the channel. """ @@ -274,7 +274,7 @@ def is_oper(self, nick: str) -> bool: """ identifier = self.make_identifier(nick) - return bool(self.privileges.get(identifier, 0) & privileges.OPER) + return bool(self.privileges.get(identifier, 0) & AccessLevel.OPER) def is_owner(self, nick: str) -> bool: """Tell if a user has the OWNER privilege level. @@ -306,7 +306,7 @@ def is_owner(self, nick: str) -> bool: """ identifier = self.make_identifier(nick) - return bool(self.privileges.get(identifier, 0) & privileges.OWNER) + return bool(self.privileges.get(identifier, 0) & AccessLevel.OWNER) def is_admin(self, nick: str) -> bool: """Tell if a user has the ADMIN privilege level. @@ -338,7 +338,7 @@ def is_admin(self, nick: str) -> bool: """ identifier = self.make_identifier(nick) - return bool(self.privileges.get(identifier, 0) & privileges.ADMIN) + return bool(self.privileges.get(identifier, 0) & AccessLevel.ADMIN) def is_op(self, nick: str) -> bool: """Tell if a user has the OP privilege level. @@ -364,7 +364,7 @@ def is_op(self, nick: str) -> bool: """ identifier = self.make_identifier(nick) - return bool(self.privileges.get(identifier, 0) & privileges.OP) + return bool(self.privileges.get(identifier, 0) & AccessLevel.OP) def is_halfop(self, nick: str) -> bool: """Tell if a user has the HALFOP privilege level. @@ -396,7 +396,7 @@ def is_halfop(self, nick: str) -> bool: """ identifier = self.make_identifier(nick) - return bool(self.privileges.get(identifier, 0) & privileges.HALFOP) + return bool(self.privileges.get(identifier, 0) & AccessLevel.HALFOP) def is_voiced(self, nick: str) -> bool: """Tell if a user has the VOICE privilege level. @@ -423,7 +423,7 @@ def is_voiced(self, nick: str) -> bool: """ identifier = self.make_identifier(nick) - return bool(self.privileges.get(identifier, 0) & privileges.VOICE) + return bool(self.privileges.get(identifier, 0) & AccessLevel.VOICE) def rename_user(self, old: Identifier, new: Identifier) -> None: """Rename a user. From bb854aa0bfba805e5d5fa3d06349dcc18d466cfe Mon Sep 17 00:00:00 2001 From: dgw Date: Wed, 1 Nov 2023 13:49:25 -0500 Subject: [PATCH 2/3] docs, privileges: move general overview of IRC privileges to doc page The history and brief overview of how IRC privileges work is not part of the module/class documentation. It's more appropriate to explain in the doc page about privileges as a general concept. --- docs/source/plugin/privileges.rst | 23 +++++++++++++++++++++++ sopel/privileges.py | 25 ------------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/docs/source/plugin/privileges.rst b/docs/source/plugin/privileges.rst index 910fe300fa..50ec6b31b6 100644 --- a/docs/source/plugin/privileges.rst +++ b/docs/source/plugin/privileges.rst @@ -13,6 +13,29 @@ This will give both OP and Voice privileges to the user named "Nickname" in the message it registers and updates its knowledge of a user's privileges in a channel, which can be used by plugins in various ways. +Historically, these two privilege levels ("op" and "voiced") were the only +channel privileges available: + +* :attr:`~sopel.privileges.AccessLevel.OP`: channel operator, set and unset by + modes ``+o`` and ``-o`` +* :attr:`~sopel.privileges.AccessLevel.VOICE`: the privilege to send messages to + a channel with the ``+m`` mode, set and unset by modes ``+v`` and ``-v`` + +Since then, other privileges have been adopted by IRC servers and clients: + +* :attr:`~sopel.privileges.AccessLevel.HALFOP`: intermediate level between VOICE + and OP, set and unset by modes ``+h`` and ``-h`` +* :attr:`~sopel.privileges.AccessLevel.ADMIN`: channel admin, above OP and below + OWNER, set and unset by modes ``+a`` and ``-a`` +* :attr:`~sopel.privileges.AccessLevel.OWNER`: channel owner, above ADMIN and OP, + set and unset by modes ``+q`` and ``-q`` + +.. important:: + + Not all IRC networks support these added privilege modes. If you are writing + a plugin for public distribution, ensure your code behaves sensibly if only + ``+v`` (voice) and ``+o`` (op) modes exist. + Access rights ============= diff --git a/sopel/privileges.py b/sopel/privileges.py index 23053e036c..00e5c6d7c4 100644 --- a/sopel/privileges.py +++ b/sopel/privileges.py @@ -15,31 +15,6 @@ class AccessLevel(enum.IntFlag): """Enumeration of available user privilege levels. - Privilege levels - ================ - - Historically, there were two user privilege levels in a channel: - - * :data:`~AccessLevel.OP`: channel operator, set and unset by modes ``+o`` - and ``-o`` - * :data:`~AccessLevel.VOICE`: the privilege to send messages to a channel - with the ``+m`` mode, set and unset by modes ``+v`` and ``-v`` - - Since then, other privileges have been adopted by IRC servers and clients: - - * :data:`~AccessLevel.HALFOP`: intermediate level between VOICE and OP, set - and unset by modes ``+h`` and ``-h`` - * :data:`~AccessLevel.ADMIN`: channel admin, above OP and below OWNER, set - and unset by modes ``+a`` and ``-a`` - * :data:`~AccessLevel.OWNER`: channel owner, above ADMIN and OP, set and - unset by modes ``+q`` and ``-q`` - - .. important:: - - Not all IRC networks support these added privilege modes. If you are - writing a plugin for public distribution, ensure your code behaves - sensibly if only +v (voice) and +o (op) modes exist. - Comparing privileges ==================== From 813651c03a39f1a9402ad599532cacb6ccf1691d Mon Sep 17 00:00:00 2001 From: dgw Date: Wed, 1 Nov 2023 23:23:11 -0500 Subject: [PATCH 3/3] docs, privileges: more streamlining of how to work with privs/modes Super-duper deemphasized that privileges are integer values. Removed the actual values from code examples, to show logic only. Condensed admonitions about nonstandard privilege levels/modes into a single location, with cross-references. --- docs/source/plugin/privileges.rst | 14 +++++++----- sopel/privileges.py | 37 +++++++++---------------------- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/docs/source/plugin/privileges.rst b/docs/source/plugin/privileges.rst index 50ec6b31b6..54dc8ba8c8 100644 --- a/docs/source/plugin/privileges.rst +++ b/docs/source/plugin/privileges.rst @@ -21,7 +21,10 @@ channel privileges available: * :attr:`~sopel.privileges.AccessLevel.VOICE`: the privilege to send messages to a channel with the ``+m`` mode, set and unset by modes ``+v`` and ``-v`` -Since then, other privileges have been adopted by IRC servers and clients: +.. _nonstandard privilege levels: + +Over time, IRC servers and clients have adopted various combinations of +nonstandard, less formally defined privilege levels: * :attr:`~sopel.privileges.AccessLevel.HALFOP`: intermediate level between VOICE and OP, set and unset by modes ``+h`` and ``-h`` @@ -30,11 +33,10 @@ Since then, other privileges have been adopted by IRC servers and clients: * :attr:`~sopel.privileges.AccessLevel.OWNER`: channel owner, above ADMIN and OP, set and unset by modes ``+q`` and ``-q`` -.. important:: - - Not all IRC networks support these added privilege modes. If you are writing - a plugin for public distribution, ensure your code behaves sensibly if only - ``+v`` (voice) and ``+o`` (op) modes exist. +**It's important to note that not all IRC networks support these nonstandard +privileges, and the ones that do may not support all of them.** If you are +writing a plugin for public distribution, ensure your code behaves sensibly when +only the standardized ``+v`` (voice) and ``+o`` (op) modes exist. Access rights ============= diff --git a/sopel/privileges.py b/sopel/privileges.py index 00e5c6d7c4..fc530ac948 100644 --- a/sopel/privileges.py +++ b/sopel/privileges.py @@ -15,26 +15,17 @@ class AccessLevel(enum.IntFlag): """Enumeration of available user privilege levels. - Comparing privileges - ==================== - - This class represents privileges as powers of two, with higher values - assigned to higher-level privileges:: + This class represents privileges as comparable, combinable flags. Lower + privilege levels compare as *less than* (``<``) higher ones:: >>> from sopel.privileges import AccessLevel >>> AccessLevel.VOICE < AccessLevel.HALFOP < AccessLevel.OP \\ ... < AccessLevel.ADMIN < AccessLevel.OWNER True - Then a user's privileges are represented as a sum of privilege levels:: + A user's privileges are represented as a combination of privilege levels:: - >>> AccessLevel.VOICE - 1 - >>> AccessLevel.OP - 4 >>> priv = AccessLevel.VOICE | AccessLevel.OP - >>> priv - 5 This allows using comparators and bitwise operators to compare privileges. Here, ``priv`` contains both VOICE and OP privileges, but not HALFOP:: @@ -46,9 +37,9 @@ class AccessLevel(enum.IntFlag): .. important:: - Do not refer directly to the integer value of a privilege level in your - code; the values may change. Use the appropriate member of this class as - a reference point instead. + Do not hard-code the value of a privilege level in your code; the values + may change. Always reference or compare to the appropriate member of + this class directly. """ # values should behave as ints, but their string representations should @@ -77,9 +68,7 @@ class AccessLevel(enum.IntFlag): .. important:: - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if - only ``+v`` (voice) and ``+o`` (op) modes exist. + Beware: This is one of the `nonstandard privilege levels`_. """ @@ -105,9 +94,7 @@ class AccessLevel(enum.IntFlag): .. important:: - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if - only ``+v`` (voice) and ``+o`` (op) modes exist. + Beware: This is one of the `nonstandard privilege levels`_. """ @@ -122,9 +109,7 @@ class AccessLevel(enum.IntFlag): .. important:: - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if - only ``+v`` (voice) and ``+o`` (op) modes exist. + Beware: This is one of the `nonstandard privilege levels`_. """ @@ -142,8 +127,6 @@ class AccessLevel(enum.IntFlag): .. important:: - Not all IRC networks support this privilege mode. If you are writing a - plugin for public distribution, ensure your code behaves sensibly if - only ``+v`` (voice) and ``+o`` (op) modes exist. + Beware: This is one of the `nonstandard privilege levels`_. """