{{{project}}} is a command-line application for devlopers who want a quick and easy way to compress their local repositories and copy them to an external storage. It uses plugin architecture to implement so-called actions, which are plugins implemented as standalone Python modules. Actions provide the skeleton of behavior, and the rules provide arguments to the plugins. (See {{{secref(Terminology)}}} for an overview of terms this document uses.)
- Action
- A plugin implemented as a standalone Python module. Actions are detailed in this section.
- Data path
- The current path to a directory under which actions and rules are placed. Actions are placed under the
actions
subdirectory in this path, and rules are placed underrules
. The algorithm picks between two choices, the system directory or the user directory (see this section). - Property
- A configuration variable for an action. It is set by a rule and read by the action.
- Rule
- A configuration file that provides arguments to an action. Rules are detailed in this section.
JBackup is a command-line application. It is divided into several commands, and they are as follows:
- create-rule
- Creates a new rule with a given format and name.
- create-action
- Creates a new action with a given name.
- do
- Runs an action with one or more rules.
- show
- Displays documentation for an action.
- locate
- Prints out the path to an action or a rule.
- complete
- A helper command for shell completion. For internal use.
Here are their syntaxes:
jbackup create-rule
[ -h ] [ -f FORMAT ] RULE- Create a rule named RULE.
jbackup create-action
[ -h ] ACTION- Create an action named ACTION.
jbackup do
[ -h ] ACTION RULE [ RULE … ]- Run the action named ACTION with one or more rules.
jbackup show
[ -h ] ACTION- Print the documentation of ACTION.
jbackup locate
[ -h ] [ –rule ] WHAT- Print the path of an action or rule named WHAT.
jbackup complete
[ -h ] [ –firstarg ] CWORD [ ARG … ]- A helper command for shell completion.
JBackup accepts -h
and --help
option flags. The options --list-actions
, --list-rules
, --path
, and --levels
all return output and then exit. --list-actions
and --list-rules
show the actions and rules, respectively, split into groups of the system-level and user-level ones. --path
lists the system and user data paths. --levels
shows available logging levels.
Actions are based on the protocol design pattern. The Action
protocol defines an interface that all action classes must follow. An action contains a list of properties (see {{{secref(Properties)}}}).
Actions are implemented as Python modules that are loaded dynamically. That is to say, instead of using Python’s builtin import
or from
statements, our own custom loader is used[fn:1]. Essentially, it loads the script as a module, extracts a class whose name matches a pattern, and creates an instance of that class. The pattern for an action is that its name be prefixed with “Action_”. Shown in {{{secref(Action Template)}}} is one such class.
As mentioned above, actions have properties. This next section goes into detail regarding those.
Properties are represented by the ActionProperty
class. A property has a name, value, a type identifier, and a documentation string. A property is mandatory by default. So, unless that property is set, an error is thrown. To make a property optional, one can set the optional
flag. There are actually a total of 5 parameters, two of which are technically optional.
Here is a complete list of ActionProperty
parameters:
- name
- The name of the parameter.
- value
- The initial (or default) value of the property. This is relevent if
optional
is set. - type
- The type of the property. If this is set, type validation is enabled. If the type of the property (
PropertyType
) does not match up with one of the types specified here,PropertyTypeError
is raised. - optional
- If this flag is set, the property is optional. In that case,
value
is used. - doc
- This parameter contains the property’s documentation string.
Here is ActionProperty
’s signature:
The loader
module is used to dynamically load scripts in as modules. It consists of the ModuleProxy
class and a function, load_module_from_file()
. The function returns a ModuleProxy
holding the newly loaded module. The dot operator is overloaded to mock having the module’s attributes.
ModuleProxy
raises AttributeError
when a the user tries to access a nonexistent attribute. But, a “safe” flag can be set to suppress this error.
The loader makes use of something called the data path. The data path (see above) is the directory from which data files are loaded. Data files include actions (both first and third-party) as well as rules. The data path is chosen from a list of predefined paths according to the following rules:
- If the user has root privileges, the system directory is chosen (
/usr/local/etc
). - If the user does not have root privileges, the path becomes
$HOME/.local/etc
, where$HOME
is the user’s home directory.
A rule is a configuration file that provides arguments to actions. They are implemented using the adapter pattern. Rules are represented by the Rule
class. Rule
is the client of the adapter pattern. It holds an instance of ConfigFile
. The Rule
constructor accepts a filename and a string mode. The mode string can be either w
or r
. If it is w
, placeholder data is written to the config file.
ConfigFile
is the interface of the adapter pattern. It defines a function called parse_file()
which accepts a file path and returns a dictionary.
XFile
is the adapter in the adapter pattern, and xlib
is the adaptee. XFile
uses xlib
to plug in the functionality fir a specific configuration file format into the client. XFile
must be compatible with the ConfigFile
protocol.
The template
subpackage creates actions and rules. In the case of actions, they are created using templates. write_action_file()
accepts a path to the file to be created, and the name of the action. Rules are written via write_rule_file()
, which accepts a path to the file to be created, and the name of the rule.
The logging
module contains functions and classes that create and manipulate loggers. It uses Python’s builtin logging
module. The primary and most important function is get_logger()
. It accepts three arguments, the name for the logger being the first.
[fn:1] {{{secref(Loader)}}} goes into detail about how modules are loaded dynamically.