Skip to content

Commit

Permalink
Release 0.2.0: Modernization and bug fixes
Browse files Browse the repository at this point in the history
* Dropped support for Sopel<7.1 + Python<3.8
  * `sopel.module` -> `sopel.plugin`
  * Removed shim around `sopel.formatting.plain()` function
  * Updated `__future__` imports
  * Removed UTF-8 coding comment
* Reorganized to match current Sopel standard (mainly putting "the
  actual plugin" in a `plugin.py` file, not `__init__.py`)
* Updated packaging to use `pyproject.toml` metadata (closes #3)
* Added release automation using PyPI Trusted Publishing
* Fixed `random_start` setting (broken by changes in #1)
* Fixed errors in `sopel-plugins configure rainbow`
  * Wrong setting name (`rainbow` -> `order`)
  * Wrong default value type (`list[int]` -> `list[str]`)
* Use `unicodedata2` if it's installed
  • Loading branch information
dgw committed Sep 25, 2024
1 parent b96782b commit 2500c9b
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 133 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/trusted-publishing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Upload Python Package

on:
release:
types: [published]

permissions:
contents: read

jobs:
upload:
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450
2 changes: 1 addition & 1 deletion COPYING
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
distribute this package, provided that:
* copyright notices are retained unchanged,
* any distribution of this package, whether modified or not,
includes this license text.
includes this license text.
2. Permission is hereby also granted to distribute binary programs
which depend on this package. If the binary program depends on a
modified version of this package, you are encouraged to publicly
Expand Down
31 changes: 31 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,37 @@ spawned from [a tongue-in-cheek
upstream issue](https://github.com/sopel-irc/sopel/issues/1962).


### sopel-rainbow 0.2.0

Changed:

* Dropped support for Sopel<7.1 + Python<3.8
* `sopel.module` -> `sopel.plugin`
* Removed shim around `sopel.formatting.plain()` function
* Updated `__future__` imports
* Removed UTF-8 coding comment

Added:

* Use `unicodedata2` if it's installed

Fixed:

* `random_start` setting (broken by changes in [#1][])
* Errors in `sopel-plugins configure rainbow`
* Wrong setting name (`rainbow` -> `order`)
* Wrong default value type (`list[int]` -> `list[str]`)

Meta:

* Reorganized to match current Sopel standard (mainly putting "the
actual plugin" in a `plugin.py` file, not `__init__.py`)
* Updated packaging to use `pyproject.toml` metadata
* Added release automation using PyPI Trusted Publishing

[#1]: https://github.com/sopel-irc/sopel-rainbow/pull/1


### sopel-rainbow 0.1.1

Fixed:
Expand Down
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@

A Sopel plugin to make things RAINBOW COLORED.

## Installing

## Configuration
Releases are hosted on PyPI, so after installing Sopel, all you need is `pip`:

```shell
$ pip install sopel-rainbow
```

## Configuring

The easiest way to configure `sopel-rainbow` is via Sopel's configuration
wizard—simply run `sopel-plugins configure rainbow` and enter the values for
which it prompts you.

### `order` setting

By default, `sopel-rainbow` outputs colors in the "standard" rainbow `order`,
ROYGBIV, subject to receiving clients' use of the customary meanings for IRC
Expand Down Expand Up @@ -34,6 +47,8 @@ order = # Americans and French can fight over this one
2
```

### `random_start` setting

Starting the rainbow at the beginning of the `order` every time is also
default behavior. If you want the rainbow to start at a random place every
time instead, set the Boolean option `random_start` to `yes` or `on`:
Expand All @@ -43,15 +58,10 @@ time instead, set the Boolean option `random_start` to `yes` or `on`:
random_start = on
```


## Dependencies

Only Sopel itself, version 7.0 or higher.

If installed on a bot using Sopel 7.1+, `sopel-rainbow` will strip control
codes from the input text before applying the rainbow colors. (Sopel 7.0.x
does not offer this feature, so feeding formatted text into the `.rainbow`
command might yield unexpected results.)
* Sopel version 7.1 or higher
* Python 3.8 or higher

This version of `sopel-rainbow` will not work with Sopel 9.0+. A future
release will correct this, sometime before Sopel 9 becomes stable.
Sopel 7.x should still run on Python 2.7 or older Python 3 releases, but it's
not maintained any more; and neither is this plugin tested on anything older.
52 changes: 52 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[build-system]
requires = ["setuptools>=63.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
platforms = ["Linux x86, x86-64"]

[tool.setuptools.packages.find]
include = ["sopel_rainbow", "sopel_rainbow.*"]
namespaces = false

[tool.setuptools.dynamic]
readme = { file=["README.md", "NEWS"], content-type="text/markdown" }

[project]
name = "sopel-rainbow"
version = "0.2.0"
description = "A Sopel plugin to make things RAINBOW COLORED."

authors = [
{ name="dgw", email="[email protected]" },
]

license = { text="EFL-2.0" }
dynamic = ["readme"]

classifiers = [
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"License :: Eiffel Forum License (EFL)",
"License :: OSI Approved :: Eiffel Forum License",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Communications :: Chat :: Internet Relay Chat",
]
keywords = [
"sopel",
"plugin",
"bot",
"irc",
]

requires-python = ">=3.8, <4"
dependencies = [
"sopel>=7.1",
]

[project.urls]
"Homepage" = "https://github.com/sopel-irc/sopel-rainbow"
"Bug Tracker" = "https://github.com/sopel-irc/sopel-rainbow/issues"

[project.entry-points."sopel.plugins"]
"rainbow" = "sopel_rainbow.plugin"
25 changes: 0 additions & 25 deletions setup.cfg

This file was deleted.

25 changes: 0 additions & 25 deletions setup.py

This file was deleted.

72 changes: 0 additions & 72 deletions sopel_rainbow/__init__.py
Original file line number Diff line number Diff line change
@@ -1,72 +0,0 @@
# coding=utf8
"""sopel-rainbow
A Sopel plugin to make things RAINBOW COLORED.
"""
from __future__ import unicode_literals, absolute_import, division, print_function

import itertools
import random
import unicodedata

from sopel import formatting, module
from sopel.config import types


# Remove when dropping support for Sopel < 7.1
if hasattr(formatting, 'plain'):
clean = formatting.plain
else:
clean = lambda t: t


class RainbowSection(types.StaticSection):
order = types.ListAttribute('order', default=[4, 7, 8, 3, 12, 2, 6])
"""The order of color codes to use.
Defaults to a standard ROYGBIV rainbow (assuming readers' clients use
typical IRC color code mappings).
"""
random_start = types.ValidatedAttribute('random_start', bool, default=False)
"""Whether to randomize the start color."""


def configure(config):
config.define_section('rainbow', RainbowSection)
config.rainbow.configure_setting(
'rainbow',
'Specify the order of IRC color codes to use in the "rainbow":'
)
config.rainbow.configure_setting(
'random_start',
'Randomize start position in the rainbow?'
)


def setup(bot):
bot.config.define_section('rainbow', RainbowSection)


@module.commands('rainbow')
def rainbow_cmd(bot, trigger):
"""Make text into a rainbow."""
text = clean(trigger.group(2) or '').strip()

if not text:
bot.reply("I can't make a rainbow out of nothing!")
return module.NOLIMIT

colors = bot.config.rainbow.order
color_cycle = itertools.cycle(colors)

if bot.config.rainbow.random_start:
for _ in range(len(colors)):
next(color_cycle)

bot.say(
''.join(
char if unicodedata.category(char) == 'Zs'
else formatting.color(char, next(color_cycle))
for char in text
)
)
75 changes: 75 additions & 0 deletions sopel_rainbow/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""sopel-rainbow
A Sopel plugin to make things RAINBOW COLORED.
"""
from __future__ import annotations

import itertools
import random

from sopel import formatting, plugin
from sopel.config import types

# TODO: Consider making ud2 an install extra
# or just require it outright; in absolute terms, it isn't a heavy library
# (though relative to just this plugin, it's on the order of 100x the size)
try:
import unicodedata2 as unicodedata
except ImportError:
import unicodedata


class RainbowSection(types.StaticSection):
order = types.ListAttribute(
'order', default=[str(v) for v in (4, 7, 8, 3, 12, 2, 6)])
"""The order of color codes to use.
Defaults to a standard ROYGBIV rainbow (assuming readers' clients use
typical IRC color code mappings).
"""
random_start = types.BooleanAttribute('random_start', default=False)
"""Whether to randomize the start color."""


def configure(config):
config.define_section('rainbow', RainbowSection)
config.rainbow.configure_setting(
'order',
'Specify the order of IRC color codes to use in the "rainbow":'
)
config.rainbow.configure_setting(
'random_start',
'Randomize start position in the rainbow?'
)


def setup(bot):
bot.config.define_section('rainbow', RainbowSection)


@plugin.commands('rainbow')
def rainbow_cmd(bot, trigger):
"""Make text into a rainbow."""
text = formatting.plain(trigger.group(2) or '').strip()

if not text:
bot.reply("I can't make a rainbow out of nothing!")
return plugin.NOLIMIT

colors = bot.config.rainbow.order
color_cycle = itertools.cycle(colors)

if bot.config.rainbow.random_start:
color_cycle = itertools.islice(
# passing all of (iter, start, stop) is important;
# passing only (iter, stop) will return a non-infinite iterator
color_cycle, random.randrange(len(colors)), None,
)

bot.say(
''.join(
char if unicodedata.category(char) == 'Zs'
else formatting.color(char, next(color_cycle))
for char in text
)
)

0 comments on commit 2500c9b

Please sign in to comment.