Interpreters

This module contains all the different Interpreters. Interpreters interprete all or a subset of operations of a DiffLog. That way interpreters encapsulate behavior for checks and actions of operations, that can be turned on and off freely.

Interpreters work by implementing callbacks that can review a single operation. When an interpreter gets executed on a DiffLog all operations of the DiffLog will be fed one by one into the interpreter. Most of the interpreters will just raise an exception when they detect an issue in one operation, some just print the operations and others can rewrite the entire DiffLog.

CheckDynamicFilesInterpreter Checks if there are changes to a dynamic file and gives the user the opportunity to interact with them.
CheckLinkBlacklistInterpreter Checks if a operation touches a link that is on the blacklist.
CheckLinkDirsInterpreter Checks if directories need to be created.
CheckLinkExistsInterpreter Checks if links of installed-file really exist in the filesystem.
CheckLinksInterpreter Checks for conflicts between all links.
CheckProfilesInterpreter Checks if profiles can be installed together.
DUIStrategyInterpreter Reorders DiffLog so linking won't be in the order of profiles but instead in the order Delete-Update-Insert.
DetectRootInterpreter Detects if root permission is needed to perform operations.
EventExecInterpreter This interpreter is used to execute the event scripts of a profile.
EventInterpreter This interpreter is the abstract base class for interpreters that work with profile events.
EventPrintInterpreter This interpreter is used to print out what an event will do.
ExecuteInterpreter This interpreter actually executes the operations from the DiffLog.
GainRootInterpreter If root permission is needed to perform the operations, this interpreter restarts the process with "sudo".
Interpreter Base-class for interpreters.
PlainPrintInterpreter Prints add/remove/update-operation without any formating.
PrintInterpreter Pretty-prints log messages and what a operation is going to do.
RootNeededInterpreter Checks if root permission are required to perform all operations.
SkipRootInterpreter Skips all operations that would require root permission.
class interpreters.Interpreter

Bases: object

Base-class for interpreters.

data

The raw DiffLog that is interpreted. Only needed by Interpreters that alter the DiffLog.

Type:list
__init__()

Constructor

call_operation(operation)

Call the implemented behavior for this operation.

This calls a function named like operation["operation"] with the prefix '_op_', if the function was implemented by the interpreter.

Parameters:operation (dict) -- A operation from DiffLog
set_difflog_data(data)

Sets the raw DiffLog content.

Needed by Interpreters that alter the DiffLog.

Parameters:data (list) -- The raw DiffLog that will be set
class interpreters.CheckDynamicFilesInterpreter(dryrun)

Bases: interpreters.Interpreter

Checks if there are changes to a dynamic file and gives the user the opportunity to interact with them.

dryrun

Stores, if --dryrun was set

Type:bool
__init__(dryrun)

Constructor.

Parameters:dryrun (bool) -- Sets, if this is a dryrun
_op_remove_l(dop)

Inspects the target file of the to be removed link.

Parameters:dop (dict) -- The remove-operation of the to be removed link
_op_update_l(dop)

Inspects the target file of the to be updated link.

Parameters:dop (dict) -- The update-operation of the to be updated link
inspect_file(target)

Checks if a file is dynamic and was changed. If so, it calls a small UI to store/undo changes.

Parameters:target (str) -- The full path to the file that will be checked
user_interaction(target)

Provides a small UI for the user to interact with a changed dynamic file.

The user can choose one of the following options to handle the changes:

  • A: Abort and exit uberdot
  • I: Ignore the changes and do nothing
  • D: Show a diff and ask again
  • P: Create a patch file and ask again
  • U: Undo the changes and restore the original file
Parameters:

target (str) -- The full path to the file that the user will interact with

Raises:
  • UserAbortion -- The user decided to abort the whole process
  • PreconditionError -- The patch file could not be written
class interpreters.CheckLinkBlacklistInterpreter(superforce)

Bases: interpreters.Interpreter

Checks if a operation touches a link that is on the blacklist.

superforce

Stores, if --superforce was set

Type:bool
blacklist

A list of file name patterns that are forbidden to touch without superforce flag

Type:list
__init__(superforce)

Constructor.

Loads the blacklist.

Parameters:superforce (bool) -- Sets, if superforce was turned on
_op_add_l(dop)

Checks the to be added symlink for blacklist violations.

Parameters:dop (dict) -- The add-operation whose symlink will be checked
_op_remove_l(dop)

Checks the to be removed symlink for blacklist violations.

Parameters:dop (dict) -- The remove-operation whose symlink will be checked
_op_update_l(dop)

Checks the old and the new symlink for blacklist violations.

Parameters:dop (dict) -- The update-operation whose symlinks will be checked
check_blacklist(file_name, action)

Checks if a file matches a pattern in the blacklist.

Parameters:
  • file_name (str) -- Name of the file
  • action (str) -- The action that is causing the touch of the file
Raises:
  • UserAbortion -- The user decided to not touch the file
  • IntegrityError -- The file was blacklisted and superforce wasn't set
class interpreters.CheckLinkDirsInterpreter(makedirs)

Bases: interpreters.Interpreter

Checks if directories need to be created.

makedirs

Stores, if --makedirs was set

Type:bool
__init__(makedirs)

Constructor

Parameters:makedirs (bool) -- Sets, if directories shall be created
_op_add_l(dop)

Checks if the directory of the to be added link already exists.

Parameters:dop (dict) -- The add-operation whose symlink will be checked
_op_update_l(dop)

Checks if the directory of the to be updated link already exists.

Parameters:dop (dict) -- The update-operation whose symlink will be checked
check_dirname(dirname)

Checks if a directory exists.

Parameters:dirname (str) -- The path to a directory
Raises:PreconditionError -- The directory doesn't exist and makedirs isn't set
class interpreters.CheckLinkExistsInterpreter(force)

Bases: interpreters.Interpreter

Checks if links of installed-file really exist in the filesystem.

force

Stores, if --force was set

Type:bool

A collection of all links that are going to be removed

Type:list
__init__(force)

Constructor

_op_add_l(dop)

Checks if the new link already exists.

Parameters:dop (dict) -- The add-operation that will be checked
Raise:
PreconditionError: The new link already exists or its target does
not exist
_op_remove_l(dop)

Checks if the to be removed link really exists.

Furthermore adds the link to removed_links, because removed links need to be stored for _op_add_l().

Parameters:dop (dict) -- The remove-operation that will be checked
Raises:PreconditionError -- The to be removed link does not exist
_op_update_l(dop)

Checks if the old and the new link already exist.

Furthermore adds the old link to removed_links if old and new link have different names, because removed links need to be stored for _op_add_l().

Parameters:dop (dict) -- The update-operation that will be checked
Raises:PreconditionError -- The old link does not exist, the new link already exists or the new link points to a non-existent file
class interpreters.CheckLinksInterpreter(installed)

Bases: interpreters.Interpreter

Checks for conflicts between all links.

Conflicts are things like duplicates, multiple targets / overwrites, etc.

Parameters:linklist (list) -- list that stores all links, their corresponding profiles and if they are already installed. Links that are already installed and won't be removed, will end up twice in this list.
__init__(installed)

Constructor.

Initializes linklist with all links from the installed-file.

Parameters:installed (dict) -- The installed-file, that was used to create the current DiffLog
_op_add_l(dop)

Checks if the to be added link already occurs in linklist.

This would be forbidden, because a link that is already installed can't be added again (only updated). Similary it would be forbidden to add a link that was already added by another profile in the same run. If everything is valid, the link will be added to the list.

Parameters:dop (dict) -- The add-operation that will be checked
Raises:IntegrityError -- The check failed
_op_remove_l(dop)

Removes link from linklist because links could be removed and added in one run by different profiles.

In that case it would look like the link is added even though it is already installed if we don't remove it here.

Parameters:dop (dict) -- The remove-operation that will be used to remove the link
class interpreters.CheckProfilesInterpreter(installed, parent_arg=None)

Bases: interpreters.Interpreter

Checks if profiles can be installed together. Protects against duplicates and overwrites.

parent_arg

Stores the value of --parent

Type:str
profile_list

A list that stores all profiles, their parents and if they are already installed. Profiles that are still installed in the end, will end up twice in this list.

Type:list
__init__(installed, parent_arg=None)

Constructor.

Initializes profile_list with all profiles from the installed-file.

Parameters:
  • installed (dict) -- The installed-file, that was used to create the DiffLog
  • parent_arg (str) -- The value of --parent
_op_add_p(dop)

Checks if a profile is added twice.

Adds the profile to profile_list if the operation is valid.

Parameters:dop (dict) -- The add-operation that will be checked
Raises:IntegrityError -- A profile is added twice or is already installed
_op_update_p(dop)

Checks if profiles will be overwritten.

Parameters:dop (dict) -- The update-operation that will be checked
Raises:IntegrityError -- A profile is already installed as a subprofile of another root profile
get_known(name, is_installed)

Returns the entry of a profile from profile_list. Either for already installed profiles or for to be installed profiles.

Parameters:
  • name (str) -- Name of the profile
  • is_installed (bool) -- True, for lookups of already installed profiles
Returns:

Tuple -- The entry that was found in profile_list. None if no entry was found.

class interpreters.DUIStrategyInterpreter

Bases: interpreters.Interpreter

Reorders DiffLog so linking won't be in the order of profiles but instead in the order Delete-Update-Insert. It also removes log messages because without the old order they are not useful anymore.

profile_deletes

A collection of profile-remove-operations

Type:list
profile_updates

A collection of profile-update-operations

Type:list
profile_adds

A collection of profile-add-operations

Type:list

A collection of link-remove-operations

Type:list

A collection of link-update-operations

Type:list

A collection of link-add-operations

Type:list
__init__()

Constructor

_op_add_l(dop)

Adds the link-add-operation to link_adds.

Parameters:dop (dict) -- The operation that will be added
_op_add_p(dop)

Adds the profile-add-operation to profile_adds.

Parameters:dop (dict) -- The operation that will be added
_op_fin(dop)

Merges the collections of operations in the correct order and overwrites self.data to alter the DiffLog

Parameters:dop (dict) -- Unused in this implementation
_op_remove_l(dop)

Adds the link-remove-operation to link_removes.

Parameters:dop (dict) -- The operation that will be added
_op_remove_p(dop)

Adds the profile-remove-operation to profile_removes.

Parameters:dop (dict) -- The operation that will be added
_op_update_l(dop)

Adds the link-update-operation to link_updates.

Parameters:dop (dict) -- The operation that will be added
_op_update_p(dop)

Adds the profile-update-operation to profile_updates.

Parameters:dop (dict) -- The operation that will be added
class interpreters.DetectRootInterpreter

Bases: interpreters.Interpreter

Detects if root permission is needed to perform operations.

_access(path)

Checks if we have write access for a given path.

Because the path might not be existent at this point, this function goes the full directory tree upwards until it finds a directory that we have write accesss to. If it finds one, it assumes that we have access to all subdirectories as well.

Parameters:path (str) -- The path that will be checked
Returns:bool -- True, if we have access to the path
_op_add_l(dop)

Checks if new links are either created in inaccessible directories or will be owned by other users than the current.

Parameters:dop (dict) -- The add-operation that will be checked
_op_remove_l(dop)

Checks if to be removed links are owned by other users than the current.

Parameters:dop (dict) -- The remove-operation that will be checked
_op_update_l(dop)

Checks if to be updated links are owned by other users than the current or will be moved to inaccessible directories.

Parameters:dop (dict) -- The update-operation that will be checked
_root_detected(dop, description, affected_file)

This method is called when requirement of root permission is detected.

Parameters:
  • dop (dict) -- The operation that requires root permission
  • description (str) -- A description of what the operation does that will require root permission
  • affected_file (str) -- The file that the description refers to
class interpreters.EventExecInterpreter(profiles, installed, event_type)

Bases: interpreters.EventInterpreter

This interpreter is used to execute the event scripts of a profile.

shell

The shell process used to execute all event callbacks

Type:Process
queue_out

Used to push the output of the shell back in realtime

Type:Queue
queue_err

Used to push exceptions during execution back to the main process

Type:Queue
ticks_without_feedback

Counter that stores the time in milliseconds that the main thread is already waiting for the shell script without capturing any output.

Type:int
failures

Counter that stores how many scripts executed with errors.

Type:int
__init__(profiles, installed, event_type)

Constructor.

Creates a thread and queues for listening on the shells stdout and stderr.

_op_fin(dop)

Logs a summary of the executed scripts. If one or more scripts failed, it aborts the program

listen_for_script_output()

Runnable of thread_out. Waits for the shell to push something to stdout or stderr and prints it. All catched exceptions will be stored in queue_err to handle on the main thread. Also resets ticks_without_feedback.

run_script(script_path, profilename)

Execute script for the given profile.

Parameters:
  • script_name (str) -- The name of the script that was generated for an event
  • profilename (str) -- The name of the profile that triggered the event
class interpreters.EventInterpreter(profiles, installed, event_type)

Bases: interpreters.Interpreter

This interpreter is the abstract base class for interpreters that work with profile events. Implements _op_* depending on self.event_type.

profiles

A list of profiles after their execution.

Type:list
installed

A copy of the old installed-file that is used to lookup if a profile had Uninstall-events set

Type:dict
event_type

A specific type ("after" or "before") that determines which events this interpreter shall look for

Type:str
__init__(profiles, installed, event_type)

Constructor.

Sets _op_add_p and _op_update_p depending on event_type.

Parameters:
  • profiles (list) -- A list of profiles after their execution.
  • installed (dict) -- A copy of the old installed-file that is used to lookup if a profile had Uninstall-events set
  • event_type (str) -- A specific type ("after" or "before") that determines which events this interpreter shall look for
_op_remove_p(dop)

Checks if a profile has an uninstall-event set, that matches event_type. If so, it calls start_event().

Parameters:dop (dict) -- The remove-operation that triggers the event
event_handler(event_name)

Returns a function that can be used to interprete add_p- and update_p-operations.

The returned function checks for a given operation, if the profile has an event set that matches event_type and event_name. If so, it calls start_event().

Parameters:event_name (str) -- Name of the event that shall be interpreted by the returned function
get_profile(profilename)

Gets a profile from self.profiles by it's name.

Parameters:profilename (str) -- Name of the profile that will be searched for.
Returns:Profile -- The corresponding profile
run_script(script_path, profilename)

Used to handle script execution of an event. Depending on the subclass this might execute or just print out the script.

Parameters:
  • script_path (str) -- The path of the script that was generated for an event
  • profilename (str) -- The name of the profile whose event is executed
start_event(profile_name, event_name)

Finds the generated script for a specific profile and event. Calls run_script() for the found script.

Parameters:
  • profile_name (str) -- The name of the profile for which the generated script is searched
  • event_name (str) -- The name of the event for which the generated script is searched
class interpreters.EventPrintInterpreter(profiles, installed, event_type)

Bases: interpreters.EventInterpreter

This interpreter is used to print out what an event will do.

More precisly this prints out the generated shell script that would be executed by an event line by line.

run_script(script_path, profilename)

Print the script line by line for an event of a given profile.

class interpreters.ExecuteInterpreter(installed, force)

Bases: interpreters.Interpreter

This interpreter actually executes the operations from the DiffLog.

It can create/delete links in the filesystem and modify the installed-file.

installed

The installed-file that will be updated

Type:dict
force

Stores, if --force was set

Type:bool

Create a symlink in the filesystem.

Parameters:
  • name (str) -- The full path of the link that will be created
  • target (str) -- The full path of the file that the link will point to
  • uid (int) -- The UID of the owner of the link
  • gid (int) -- The GID of the owner of the link
  • permission (int) -- The permissions of the target
  • secure (bool) -- Wether target should have same owner as name
Raises:

UnkownError -- The link could not be created

Remove a symlink. If the directory is empty, it removes the directory as well. Does this recursively for all parent directories.

Parameters:path (str) -- The path to the symlink, that will be removed
__init__(installed, force)

Constructor.

Updates the version number of the installed-file.

Parameters:
  • installed (dict) -- The installed-file that will be updated
  • force (bool) -- The value of --force
static _makedirs(filename)

Custom os.makedirs() that keeps the owner of the directory.

This means that it will create the directory with the same owner as of the deepest parent directory that already exists instead of using current user as owner. This is needed, because otherwise directories won't be accessible by the user, if some links would be created with root permissions.

Parameters:filename (str) -- The full path of the file that needs its directories created
_op_add_l(dop)

Adds a link to the filesystem and adds a link entry of the corresponding profile in the installed-file.

Parameters:dop (dict) -- The add-operation that will be executed
_op_add_p(dop)

Adds a profile entry of the installed-file.

Parameters:dop (dict) -- The add-operation that will be executed
_op_remove_l(dop)

Removes a link from the filesystem and removes the links entry of the corresponding profile in the installed-file.

Parameters:dop (dict) -- The remove-operation that will be executed
_op_remove_p(dop)

Removes a profile entry of the installed-file.

Parameters:dop (dict) -- The remove-operation that will be executed
_op_update_l(dop)

Updates a link in the filesystem and updates the links entry of the corresponding profile in the installed-file.

Parameters:dop (dict) -- The update-operation that will be executed
_op_update_p(dop)

Updates a profile entry of the installed-file.

Parameters:dop (dict) -- The update-operation that will be executed
_op_update_s(dop)

Updates the script_path of the onUninstall-script for a profile.

Parameters:dop (dict) -- The update-operation that will be executed
class interpreters.GainRootInterpreter

Bases: interpreters.RootNeededInterpreter

If root permission is needed to perform the operations, this interpreter restarts the process with "sudo".

_op_fin(dop)

Replace the process if root permission is needed with the same call of uberdot, but prepend it with "sudo".

Parameters:dop (dict) -- Unused in this implementation
class interpreters.PlainPrintInterpreter

Bases: interpreters.Interpreter

Prints add/remove/update-operation without any formating.

__init__()

Constructor.

Maps _op_* functions to print().

_op_fin(dop)

Print "]" to show the end of an array.

Parameters:dop (dict) -- Unused in this implementation
_op_start(dop)

Print "[" to show the start of an array.

Parameters:dop (dict) -- Unused in this implementation
class interpreters.PrintInterpreter

Bases: interpreters.Interpreter

Pretty-prints log messages and what a operation is going to do.

_op_add_l(dop)

Logs/Prints out that a link was added.

Parameters:dop (dict) -- The add-operation that will be logged
_op_add_p(dop)

Logs/Prints out that a profile was added.

Parameters:dop (dict) -- The add-operation that will be logged
_op_info(dop)

Logs/Prints out an info-operation.

Parameters:dop (dict) -- The info-operation that will be logged
_op_remove_l(dop)

Logs/Prints out that a link was removed.

Parameters:dop (dict) -- The remove-operation that will be logged
_op_remove_p(dop)

Logs/Prints out that a profile was removed.

Parameters:dop (dict) -- The remove-operation that will be logged
_op_start(dop)

Logs/Prints out the start of the linking process.

Parameters:dop (dict) -- Unused in this implementation
_op_update_l(dop)

Logs/Prints out that a link was updated.

The message is generated according to what changed in the updated link.

Parameters:dop (dict) -- The update-operation that will be logged
_op_update_p(dop)

Logs/Prints out that a profile was updated.

Parameters:dop (dict) -- The update-operation that will be logged
class interpreters.RootNeededInterpreter

Bases: interpreters.DetectRootInterpreter

Checks if root permission are required to perform all operations. Prints out all such operations.

content

A list of tuples with (dop, description, affected_file) that stores which operations require root permission, which file or directory they affect and a description of what the operation would exactly require root permission for

Type:list
__init__()

Constructor

_root_detected(dop, description, affected_file)

Logs and prints out the operation that needs root permission.

Parameters:
  • dop (dict) -- Unused in this implementation
  • description (str) -- A description of what the operation does that will require root permission
  • affected_file (str) -- The file that the description refers to
class interpreters.SkipRootInterpreter

Bases: interpreters.DetectRootInterpreter

Skips all operations that would require root permission.

skipped

A list of all operations that will be skipped

Type:list
skipped_reasons

A list of tuples that counts how often a description occured

Type:list
__init__()

Constructor

_op_fin(dop)

Remove all operations from difflog that are collected in self.skip.

Parameters:dop (dict) -- Unused in this implementation
_root_detected(dop, description, affected_file)

Stores which operations needs to be skipped.

Parameters:
  • dop (dict) -- The operation that will be skipped
  • description (str) -- A description of what the operation does that will require root permission
  • affected_file (str) -- Used to determine if description refers to a file or a directory