Writing Hooks¶
Note
Whether a hook runs before or after uploading a package is a matter of the JSON configuration file. Aside, they are identical.
Hooks are a fundamental part of dput-ng. Hooks make sure the package you’ve prepared is actually fit to upload given the target & current profile.
In general, one should implement hooks for things that the remote server would ideally check for before accepting a package. Going beyond that is OK, providing you have the user’s go-ahead to do so.
Remember, this isn’t some sort of magical restriction to upload, most remote servers would be happy with almost anything you put there, these are simply to help reduce the time to notice big errors.
Theory of Operation¶
Pre-upload Hooks are a simple function which is invoked with a few objects to help aid in the checking process & reduce code.
Pre-upload hooks will always be run before an upload, and will be given the digested .changes object, the current profile & a way to interface with the user.
Pre-upload hooks (at their core) should preform a single check (as simply as it
can), and either raise a subclass of dput.exceptions.HookException
or return normally.
Post-upload hooks work likewise. They are just simple hooks as well, that are
slightly different to pre-upload hooks. Firstly, register as a hook by placing
the plugin def in the hooks
class. In the event of an error, feel free to
just bail out. There’s not much you can do, and throwing an error is bad form.
For now. This is likely to change.
How a Hook Is Invoked¶
Throughout this overview, we’ll be looking at the
dput.hooks.checksum.validate_checksums()
pre-upload hook. It’s one of the
most simple hooks, and demonstrates the concept very clearly.
To start to understand how this all works, let’s take a step back and
look at how dput.hook.run_hook()
invokes the hook-function.
Basically, run_hook
will grab all the strings in the hooks
key
of the profile. They are just that – simply strings. The hook are looked
up using dput.util.get_obj()
(which calls
dput.util.load_config()
to resolve the .json definition of the hook).
All hooks are declared in the hooks
config class, and look
something like the following:
{
"description": "checksum pre-upload hook",
"path": "dput.hooks.checksum.validate_checksums",
"pre": true
}
Note
Use "pre": true
or "post": true
respectively to decide whether
the hook should run prior or after uploading a package.
For more on this file & how it’s used, check the other ref-doc on config files: Configuration File Overview
Nextly, let’s take a look at the path
key. path
is a
python-importable path to the function to invoke. Let’s take a look
at it a bit more closely:
>>> from dput.hooks.checksum import validate_checksums
>>> validate_checksums
<function validate_checksums at 0x7f9be15e1e60>
As you can see, we’ve imported the target, and it is, in fact, the function that we care about.
Now that we’re clear on how we got here, let’s check back with the
implementation of dput.hooks.checksum.validate_checksums()
:
def validate_checksums(changes, profile, interface):
We’re passed three objects – the changes
, profile
and interface
.
The changes
object is an instance of dput.changes.Changes
,
pre-loaded with the target of this upload action. profile
is a simple
dict, with the current upload profile. interface
is a subclass of
dput.interface.AbstractInterface
, ready to be used to talk
to the user, if something comes up.
What To Do When You Find an Issue¶
During runtime, and for any reason the checker sees fit to do so, the hook
may abort the upload by raising a subclass of a
dput.exceptions.HookException
. In cases where the user aught to
make the decision (lintian errors, etc), please prompt the user for
what to do, rather then blindly raising the error. Remember, the user can’t
override a checker’s failure except by disabling the checker. Moreover, never
prompt for inputs directly. Use the dput.interface.AbstractInterface
interface to prompt for data in a uniform way.
Don’t make people disable you. Be nice.
Let’s take a look at our reference implementation again:
def validate_checksums(changes, profile, interface):
try:
changes.validate_checksums(check_hash=profile["hash"])
except ChangesFileException as e:
raise HashValidationError(
"Bad checksums on %s: %s" % (changes.get_filename(), e)
)
As you can see, the checker verifies the hashsums, catches any Exceptions
thrown by the code it uses, and raises sane error text. The Exception
raised (dput.hooks.checksum.HashValidationError
) is a subclass
of the expected dput.exceptions.HookException
.