sopel-irc / sopel

:robot::speech_balloon: An easy-to-use and highly extensible IRC Bot framework. Formerly Willie.
https://sopel.chat
Other
948 stars 402 forks source link

Inequal 'project' and 'package' name in `EntryPointPlugin` causes various crashes #2593

Closed SnoopJ closed 1 week ago

SnoopJ commented 9 months ago

Description

I have observed that an entrypoint-style Sopel plugin cannot be reloaded if its project name does not exactly match the top-level import name.

I believe this is caused by catching ValueError specifically in the get_version logic, where other exceptions may bubble up out of the importlib machinery.

Log error report

[2024-02-23 19:55:26,265] sopel.bot            ERROR    - Unexpected PackageNotFoundError (sopel_dummy) from SnoopJ. Message was: !reload dummy
Traceback (most recent call last):
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/bot.py", line 695, in call_rule
    rule.execute(sopel, trigger)
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/plugins/rules.py", line 1274, in execute
    exit_code = self._handler(bot, trigger)
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/plugin.py", line 1399, in guarded
    return function(bot, trigger, *args, **kwargs)
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/builtins/reload.py", line 109, in pm_f_reload
    f_reload(bot, trigger)
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/plugin.py", line 1640, in guarded
    return function(bot, trigger, *args, **kwargs)
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/builtins/reload.py", line 53, in f_reload
    bot.reload_plugin(name)
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/bot.py", line 365, in reload_plugin
    meta = plugin_handler.get_meta_description()
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/plugins/handlers.py", line 647, in get_meta_description
    data = super().get_meta_description()
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/plugins/handlers.py", line 321, in get_meta_description
    'version': self.get_version(),
  File "/var/lib/gitea/data/gitea-repositories/snoopj/sopel.git/sopel/plugins/handlers.py", line 620, in get_version
    version = importlib.metadata.version(self.module.__package__)
  File "/home/snoopjedi/.pyenv/versions/3.8.18/lib/python3.8/importlib/metadata.py", line 530, in version
    return distribution(distribution_name).version
  File "/home/snoopjedi/.pyenv/versions/3.8.18/lib/python3.8/importlib/metadata.py", line 503, in distribution
    return Distribution.from_name(distribution_name)
  File "/home/snoopjedi/.pyenv/versions/3.8.18/lib/python3.8/importlib/metadata.py", line 177, in from_name
    raise PackageNotFoundError(name)
importlib.metadata.PackageNotFoundError: sopel_dummy

Reproduction steps

Broken setup.cfg

For a plugin whose top-level import name is sopel_dummy

[metadata]                                                                                                                                                              
name = sopel-dummy-plugin                                                                                                                                               
version = 1.0.0                                                                                                                                                         
description = A dummy plugin for Sopel                                                                                                                                  
author = SnoopJ                                                                                                                                                         
author_email = snoopjedi@gmail.com                                                                                                                                                                                                                                                                
license = Eiffel Forum License, version 2                                                                                                                               
classifiers =                                                                                                                                                           
    Intended Audience :: Developers                                                                                                                                     
    Intended Audience :: System Administrators                                                                                                                          
    License :: Eiffel Forum License (EFL)                                                                                                                               
    License :: OSI Approved :: Eiffel Forum License                                                                                                                     
    Topic :: Communications :: Chat :: Internet Relay Chat                                                                                                              

[options]                                                                                                                                                               
packages = find:                                                                                                                                                        
zip_safe = false                                                                                                                                                        
include_package_data = true                                                                                                                                             
install_requires =                                                                                                                                                      
    sopel>=7.1                                                                                                                                                          

[options.entry_points]                                                                                                                                                  
sopel.plugins =                                                                                                                                                         
    dummy = sopel_dummy

Expected behavior

I expect the plugin to be reloaded

Relevant logs

No response

Notes

No response

Sopel version

973a489

Installation method

pip install

Python version

3.8.18

Operating system

Ubuntu 20.04

IRCd

No response

Relevant plugins

No response

SnoopJ commented 2 months ago

sopel-plugins list is also affected by this

dgw commented 2 months ago

As is the .version command from sopel.builtins.version

Exirel commented 1 month ago

Didn't we agree that reload wasn't an option anymore for non-single file plugins?

dgw commented 1 month ago

Didn't we agree that reload wasn't an option anymore for non-single file plugins?

The issue title is about reloading, but this bug affects a lot more than that.

We've definitely talked about reload not working properly once you move beyond a single .py file (and even then, it can be a little wonky), but nothing yet prevents trying to .reload any type of plugin. I thought #1056 had more talk (from you) than it does about future possibilities after Python 2 support was dropped, but the current state prior to 8.0 was (AFAIK) more like "we'd still like to improve this if it's possible in newer versions of Python".

Meanwhile, there's #2043 about actually making .reload explicitly not work on handlers other than PyFilePlugin, but both of us agreed not to pursue it, eventually. (Among other things, it's not even particularly straightforward to differentiate PyFilePlugin that will reload a single file, and PyFilePlugin that should actually reload an entire folder.)

Exirel commented 1 month ago

Which means that, as described for now, it is a duplicate of #1056 (with added details), which disqualifies it for Sopel 8.0.1 in my opinion.

The version stuff, however, is something to fix, of course (and I think the related PR is doing that).