Skip to content

Commit 916d40f

Browse files
committed
Remove outdated namespace package definition and update docs
See https://realpython.com/python-namespace-package. This setup is backwards-compatible, so plugins using the old pkgutil-based setup will continue working fine. This setup has an advantage where external plugins will now be able to import modules from 'beetsplug' package for typing purposes. Previously, mypy could not resolve these modules due to presence of `__init__.py`.
1 parent a1c0ebd commit 916d40f

File tree

4 files changed

+38
-45
lines changed

4 files changed

+38
-45
lines changed

beetsplug/__init__.py

-20
This file was deleted.

docs/changelog.rst

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Bug fixes:
7474
For packagers:
7575

7676
* The minimum supported Python version is now 3.9.
77+
* External plugin developers: ``beetsplug/__init__.py`` file can be removed
78+
from your plugin as beets now uses native/implicit namespace package setup.
7779

7880
Other changes:
7981

docs/dev/plugins.rst

+34-25
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,55 @@
33
Writing Plugins
44
---------------
55

6-
A beets plugin is just a Python module inside the ``beetsplug`` namespace
7-
package. (Check out this `Stack Overflow question about namespace packages`_ if
8-
you haven't heard of them.) So, to make one, create a directory called
9-
``beetsplug`` and put two files in it: one called ``__init__.py`` and one called
10-
``myawesomeplugin.py`` (but don't actually call it that). Your directory
11-
structure should look like this::
6+
A beets plugin is just a Python module or package inside the ``beetsplug``
7+
namespace package. (Check out this `Stack Overflow question about namespace
8+
packages`_ if you haven't heard of them.) So, to make one, create a directory
9+
called ``beetsplug`` and add either your plugin module::
1210

1311
beetsplug/
14-
__init__.py
1512
myawesomeplugin.py
1613

17-
.. _Stack Overflow question about namespace packages:
18-
https://stackoverflow.com/questions/1675734/how-do-i-create-a-namespace-package-in-python/1676069#1676069
14+
or your plugin subpackage::
1915

20-
Then, you'll need to put this stuff in ``__init__.py`` to make ``beetsplug`` a
21-
namespace package::
16+
beetsplug/
17+
myawesomeplugin/
18+
__init__.py
19+
myawesomeplugin.py
20+
21+
.. attention::
2222

23-
from pkgutil import extend_path
24-
__path__ = extend_path(__path__, __name__)
23+
You do not anymore need to add a ``__init__.py`` file to the ``beetsplug``
24+
directory. Python treats your plugin as a namespace package automatically,
25+
thus we do not depend on ``pkgutil``-based setup in the ``__init__.py``
26+
file anymore.
2527

26-
That's all for ``__init__.py``; you can can leave it alone. The meat of your
27-
plugin goes in ``myawesomeplugin.py``. There, you'll have to import the
28-
``beets.plugins`` module and define a subclass of the ``BeetsPlugin`` class
29-
found therein. Here's a skeleton of a plugin file::
28+
The meat of your plugin goes in ``myawesomeplugin.py``. There, you'll have to
29+
import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example
30+
31+
.. code-block:: python
3032
3133
from beets.plugins import BeetsPlugin
3234
33-
class MyPlugin(BeetsPlugin):
35+
class MyAwesomePlugin(BeetsPlugin):
3436
pass
3537
3638
Once you have your ``BeetsPlugin`` subclass, there's a variety of things your
3739
plugin can do. (Read on!)
3840

3941
To use your new plugin, make sure the directory that contains your
40-
``beetsplug`` directory is in the Python
41-
path (using ``PYTHONPATH`` or by installing in a `virtualenv`_, for example).
42-
Then, as described above, edit your ``config.yaml`` to include
43-
``plugins: myawesomeplugin`` (substituting the name of the Python module
44-
containing your plugin).
42+
``beetsplug`` directory is in the Python path (using ``PYTHONPATH`` or by
43+
installing in a `virtualenv`_, for example). Then, add your plugin to beets
44+
configuration
4545

46+
.. code-block:: yaml
47+
48+
# config.yaml
49+
plugins:
50+
- myawesomeplugin
51+
52+
and you're good to go!
53+
54+
.. _Stack Overflow question about namespace packages: https://stackoverflow.com/a/27586272/9582674
4655
.. _virtualenv: https://pypi.org/project/virtualenv
4756

4857
.. _add_subcommands:
@@ -249,13 +258,13 @@ The events currently available are:
249258
during a ``beet import`` interactive session. Plugins can use this event for
250259
:ref:`appending choices to the prompt <append_prompt_choices>` by returning a
251260
list of ``PromptChoices``. Parameters: ``task`` and ``session``.
252-
261+
253262
* `mb_track_extract`: called after the metadata is obtained from
254263
MusicBrainz. The parameter is a ``dict`` containing the tags retrieved from
255264
MusicBrainz for a track. Plugins must return a new (potentially empty)
256265
``dict`` with additional ``field: value`` pairs, which the autotagger will
257266
apply to the item, as flexible attributes if ``field`` is not a hardcoded
258-
field. Fields already present on the track are overwritten.
267+
field. Fields already present on the track are overwritten.
259268
Parameter: ``data``
260269

261270
* `mb_album_extract`: Like `mb_track_extract`, but for album tags. Overwrites

setup.cfg

+2
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,5 @@ allow_any_generics = false
3737
# FIXME: Would be better to actually type the libraries (if under our control),
3838
# or write our own stubs. For now, silence errors
3939
ignore_missing_imports = true
40+
namespace_packages = true
41+
explicit_package_bases = true

0 commit comments

Comments
 (0)