diff --git a/beets/test/helper.py b/beets/test/helper.py index 4effa47f8..c0d785d6e 100644 --- a/beets/test/helper.py +++ b/beets/test/helper.py @@ -675,7 +675,7 @@ class ImportSessionFixture(ImportSession): >>> importer.run() This imports ``/path/to/import`` into `lib`. It skips the first - album and imports thesecond one with metadata from the tags. For the + album and imports the second one with metadata from the tags. For the remaining albums, the metadata from the autotagger will be applied. """ diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 32a63b216..ac4e3bc3c 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -1130,7 +1130,10 @@ def get_temp_filename( tempdir = get_module_tempdir(module) tempdir.mkdir(parents=True, exist_ok=True) - _, filename = tempfile.mkstemp(dir=tempdir, prefix=prefix, suffix=suffix) + descriptor, filename = tempfile.mkstemp( + dir=tempdir, prefix=prefix, suffix=suffix + ) + os.close(descriptor) return bytestring_path(filename) diff --git a/beetsplug/__init__.py b/beetsplug/__init__.py deleted file mode 100644 index ad573cdb3..000000000 --- a/beetsplug/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file is part of beets. -# Copyright 2016, Adrian Sampson. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -"""A namespace package for beets plugins.""" - -# Make this a namespace package. -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) diff --git a/docs/changelog.rst b/docs/changelog.rst index bcb8135b8..feb73bc58 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -22,6 +22,9 @@ New features: Bug fixes: +* :doc:`plugins/fetchart`: Fix fetchart bug where a tempfile could not be deleted due to never being + properly closed. + :bug:`5521` * :doc:`plugins/lyrics`: LRCLib will fallback to plain lyrics if synced lyrics are not found and `synced` flag is set to `yes`. * Synchronise files included in the source distribution with what we used to @@ -74,6 +77,8 @@ Bug fixes: For packagers: * The minimum supported Python version is now 3.9. +* External plugin developers: ``beetsplug/__init__.py`` file can be removed + from your plugin as beets now uses native/implicit namespace package setup. Other changes: diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index fa7fa645e..96e69153d 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -3,47 +3,57 @@ Writing Plugins --------------- -A beets plugin is just a Python module inside the ``beetsplug`` namespace -package. (Check out this `Stack Overflow question about namespace packages`_ if -you haven't heard of them.) So, to make one, create a directory called -``beetsplug`` and put two files in it: one called ``__init__.py`` and one called -``myawesomeplugin.py`` (but don't actually call it that). Your directory -structure should look like this:: +A beets plugin is just a Python module or package inside the ``beetsplug`` +namespace package. (Check out `this article`_ and `this Stack Overflow +question`_ if you haven't heard about namespace packages.) So, to make one, +create a directory called ``beetsplug`` and add either your plugin module:: beetsplug/ - __init__.py myawesomeplugin.py -.. _Stack Overflow question about namespace packages: - https://stackoverflow.com/questions/1675734/how-do-i-create-a-namespace-package-in-python/1676069#1676069 +or your plugin subpackage:: -Then, you'll need to put this stuff in ``__init__.py`` to make ``beetsplug`` a -namespace package:: + beetsplug/ + myawesomeplugin/ + __init__.py + myawesomeplugin.py - from pkgutil import extend_path - __path__ = extend_path(__path__, __name__) +.. attention:: -That's all for ``__init__.py``; you can can leave it alone. The meat of your -plugin goes in ``myawesomeplugin.py``. There, you'll have to import the -``beets.plugins`` module and define a subclass of the ``BeetsPlugin`` class -found therein. Here's a skeleton of a plugin file:: + You do not anymore need to add a ``__init__.py`` file to the ``beetsplug`` + directory. Python treats your plugin as a namespace package automatically, + thus we do not depend on ``pkgutil``-based setup in the ``__init__.py`` + file anymore. + +The meat of your plugin goes in ``myawesomeplugin.py``. There, you'll have to +import ``BeetsPlugin`` from ``beets.plugins`` and subclass it, for example + +.. code-block:: python from beets.plugins import BeetsPlugin - class MyPlugin(BeetsPlugin): + class MyAwesomePlugin(BeetsPlugin): pass Once you have your ``BeetsPlugin`` subclass, there's a variety of things your plugin can do. (Read on!) -To use your new plugin, make sure the directory that contains your -``beetsplug`` directory is in the Python -path (using ``PYTHONPATH`` or by installing in a `virtualenv`_, for example). -Then, as described above, edit your ``config.yaml`` to include -``plugins: myawesomeplugin`` (substituting the name of the Python module -containing your plugin). +To use your new plugin, package your plugin (see how to do this with `poetry`_ +or `setuptools`_, for example) and install it into your ``beets`` virtual +environment. Then, add your plugin to beets configuration -.. _virtualenv: https://pypi.org/project/virtualenv +.. code-block:: yaml + + # config.yaml + plugins: + - myawesomeplugin + +and you're good to go! + +.. _this article: https://realpython.com/python-namespace-package/#setting-up-some-namespace-packages +.. _this Stack Overflow question: https://stackoverflow.com/a/27586272/9582674 +.. _poetry: https://python-poetry.org/docs/pyproject/#packages +.. _setuptools: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#finding-simple-packages .. _add_subcommands: @@ -249,13 +259,13 @@ The events currently available are: during a ``beet import`` interactive session. Plugins can use this event for :ref:`appending choices to the prompt ` by returning a list of ``PromptChoices``. Parameters: ``task`` and ``session``. - + * `mb_track_extract`: called after the metadata is obtained from MusicBrainz. The parameter is a ``dict`` containing the tags retrieved from MusicBrainz for a track. Plugins must return a new (potentially empty) ``dict`` with additional ``field: value`` pairs, which the autotagger will apply to the item, as flexible attributes if ``field`` is not a hardcoded - field. Fields already present on the track are overwritten. + field. Fields already present on the track are overwritten. Parameter: ``data`` * `mb_album_extract`: Like `mb_track_extract`, but for album tags. Overwrites diff --git a/docs/faq.rst b/docs/faq.rst index b740c6503..ac7818ab2 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -174,9 +174,8 @@ pages. …report a bug in beets? ----------------------- -We use the `issue tracker `__ -on GitHub. `Enter a new issue `__ -there to report a bug. Please follow these guidelines when reporting an issue: +We use the `issue tracker`_ on GitHub where you can `open a new ticket`_. +Please follow these guidelines when reporting an issue: - Most importantly: if beets is crashing, please `include the traceback `__. Tracebacks can be more @@ -206,6 +205,7 @@ If you've never reported a bug before, Mozilla has some well-written `general guidelines for good bug reports`_. +.. _issue tracker: https://github.com/beetbox/beets/issues .. _general guidelines for good bug reports: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Bug_writing_guidelines @@ -343,11 +343,11 @@ read the file. You can also use specialized programs for checking file integrity---for example, type ``metaflac --list music.flac`` to check FLAC files. -If beets still complains about a file that seems to be valid, `file a -bug `__ and we'll look into -it. There's always a possibility that there's a bug "upstream" in the -`Mutagen `__ library used by beets, -in which case we'll forward the bug to that project's tracker. +If beets still complains about a file that seems to be valid, `open a new +ticket`_ and we'll look into it. There's always a possibility that there's +a bug "upstream" in the `Mutagen `__ +library used by beets, in which case we'll forward the bug to that project's +tracker. .. _importhang: @@ -398,3 +398,5 @@ try `this Super User answer`_. .. _this Super User answer: https://superuser.com/a/284361/4569 .. _pip: https://pip.pypa.io/en/stable/ +.. _open a new ticket: + https://github.com/beetbox/beets/issues/new?template=bug-report.md diff --git a/docs/guides/tagger.rst b/docs/guides/tagger.rst index 68ad908e8..bf1ecbd8a 100644 --- a/docs/guides/tagger.rst +++ b/docs/guides/tagger.rst @@ -76,7 +76,8 @@ all of these limitations. Musepack, Windows Media, Opus, and AIFF files are supported. (Do you use some other format? Please `file a feature request`_!) -.. _file a feature request: https://github.com/beetbox/beets/issues/new +.. _file a feature request: + https://github.com/beetbox/beets/issues/new?template=feature-request.md Now that that's out of the way, let's tag some music. diff --git a/setup.cfg b/setup.cfg index 8e3d7e3b8..ba580f2c9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,3 +37,5 @@ allow_any_generics = false # FIXME: Would be better to actually type the libraries (if under our control), # or write our own stubs. For now, silence errors ignore_missing_imports = true +namespace_packages = true +explicit_package_bases = true diff --git a/test/plugins/lyrics_pages.py b/test/plugins/lyrics_pages.py index 84c9e2441..2d681e111 100644 --- a/test/plugins/lyrics_pages.py +++ b/test/plugins/lyrics_pages.py @@ -456,16 +456,6 @@ lyrics_pages = [ LyricsPage.make( "https://www.musica.com/letras.asp?letra=59862", """ - Lady Madonna, children at your feet - Wonder how you manage to make ends meet - Who finds the money when you pay the rent? - Did you think that money was heaven sent? - - Friday night arrives without a suitcase - Sunday morning creeping like a nun - Monday's child has learned to tie his bootlace - See how they run - Lady Madonna, baby at your breast Wonders how you manage to feed the rest diff --git a/test/plugins/test_importadded.py b/test/plugins/test_importadded.py index d48ec6c46..608afb399 100644 --- a/test/plugins/test_importadded.py +++ b/test/plugins/test_importadded.py @@ -57,7 +57,7 @@ class ImportAddedTest(PluginMixin, ImportTestCase): os.path.getmtime(mfile.path) for mfile in self.import_media ) self.matcher = AutotagStub().install() - self.matcher.macthin = AutotagStub.GOOD + self.matcher.matching = AutotagStub.IDENT self.importer = self.setup_importer() self.importer.add_choice(importer.action.APPLY) diff --git a/test/test_importer.py b/test/test_importer.py index ad6b837f5..a28b646cf 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -440,7 +440,7 @@ class ImportTest(ImportTestCase): self.prepare_album_for_import(1) self.setup_importer() self.matcher = AutotagStub().install() - self.matcher.macthin = AutotagStub.GOOD + self.matcher.matching = AutotagStub.IDENT def tearDown(self): super().tearDown()