Creating a Renamer Plugin

Introduction

Renamer plugins are Python code, discovered by Twisted’s plugin system, that extends Renamer’s functionality without having to modify the core of Renamer.

There are two kinds of pluggable code in Renamer:

  1. Commands that perform actions unrelated to directly renaming a file. An example of this would be the undo command.
  2. Renaming commands that determine new filenames from existing files and ultimately result in an input being renamed. An example of this would be the tvrage command.

Since the topic of creating commands that are not directly related to renaming is so broad, this document will primarily focus on creating a command that performs renaming.

Foundations

Renamer commands are simply Twisted Options subclasses with a few extra bits thrown in, so all the usual Options attributes (such as optParameters) are available for use and should be how you expose additional options for your command.

In almost all cases you will want to inherit from the RenamingCommand base class, as it ensure your subclass provides all the right interfaces as well as invoking your command and performing the actual file renaming.

At the heart of a renaming command is processArgument which accepts one argument and returns a Python dictionary. That dictionary is then used to perform template substitution on the name and prefix command-line options (or, if they’re not given, command-specific defaults.) This process of calling processArgument is repeated for each argument given, letting your command process one argument at a time.

Deferreds

If your command performs a long-running task, such as fetching data from a web server, you can return a Deferred from processArgument that should ultimately return with a Python dictionary to be used in assembling the destination filename.

Sample command

Below is the complete source code of a command that renames files named as POSIX timestamps (e.g. 1287758780.4690211.txt) to a human-readable representation of the timestamp (e.g. 2010-10-22 16-46-20.txt).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import os
import string
import time

from renamer.plugin import RenamingCommand
from renamer.errors import PluginError


class ReadableTimestamps(RenamingCommand):
    # The name of our command, as it will be used from the command-line.
    name = 'timestamp'

    # A brief description of the command's purpose, displayed in --help output.
    description = 'Rename files with POSIX timestamps to human-readble times.'

    # Command-line parameters we support.
    optParameters = [
        ('format', 'f', '%Y-%m-%d %H-%M-%S', 'strftime format.')]

    # The default name template to use if no name template is specified via the
    # command-line or configuration file.
    defaultNameTemplate = string.Template('$time')

    # IRenamerCommand

    def processArgument(self, arg):
        # The extension is not needed as it will be determined from the
        # original file name.
        name, ext = arg.splitext()
        try:
            # Try convert the filename to a floating point number.
            timestamp = float(name)
        except (TypeError, ValueError):
            # If it is not a floating point number then we raise an exception
            # to stop the process.
            raise PluginError('%r is not a valid timestamp' % (name,))
        else:
            # Convert and format the timestamp according to the "format"
            # command-line parameter.
            t = time.localtime(timestamp)
            return {
                'time': time.strftime(self['format'], t)}

Installing the plugin

Now that we’ve constructed our command we need to make it available to Renamer. This process consists of the following simple steps:

  1. Create a directory for your plugins such as MyRenamerPlugins wherever you usually put your source code, beneath this create a directory named renamer and beneath that a directory named plugins.

    Your directory tree should look like:

    .
    |-- MyRenamerPlugins
    |   `-- renamer
    |   |   `-- plugins
    
  2. Add your directory (MyRenamerPlugins in the previous step) to sys.path (typically by adding it to the PYTHONPATH environment variable.)

  3. Put your plugin source code file (with a .py extension) in the MyRenamerPlugins/renamer/plugins directory. It is important to note that this directory must not be a Python package (i.e. it must not contain __init__.py) and will be skipped (i.e. no plugins will be loaded) if it is one.

  4. You can verify that your plugin is visible by running an interactive Python prompt and doing something similar to:

    >>> from renamer.plugins.timestamp import ReadableTimestamps
    >>>
    

    (This obviously assumes that you used the ReadableTimestamps example and stored it in a file called timestamp.py.)

    If your plugin is installed correctly there should be no errors importing the module.

Using the plugin

After your plugin has been installed correctly you can use it:

$ touch 1287758780.4690211.txt
$ rn timestamp 1287758780.4690211.txt
$ ls
2010-10-22 16-46-20.txt

It is possible that you may need to regenerate the Twisted plugin cache if you notice nonsensical errors related to your plugin objects, especially if you’re actively developing a plugin and testing it. Refer to the Twisted documentation regarding plugin caching.