ActionTree/stock.py
# coding: utf8
# Copyright 2015-2018 Vincent Jacques <vincent@vincent-jacques.net>
"""
Stock actions are predefined common tasks (manipulating the filesystem, calling an external program, etc.)
They all specialize :class:`.Action`.
"""
import errno
import os
import shutil
import subprocess
import time
from . import Action
DEFAULT = object()
class NullAction(Action):
"""
A stock action that does nothing.
Useful as a placeholder for several dependencies.
"""
def __init__(self, label=None, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, label, *args, **kwds)
def do_execute(self, dependency_statuses):
pass
class CallSubprocess(Action):
"""
A stock action that calls a subprocess.
"""
def __init__(self, command, kwargs={}, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, " ".join(command) if label is DEFAULT else label, *args, **kwds)
self.__command = command
self.__kwargs = kwargs
def do_execute(self, dependency_statuses):
subprocess.check_call(self.__command, **self.__kwargs)
class CreateDirectory(Action):
"""
A stock action that creates a directory.
No error will be raised if the directory already exists.
If the directory to create is nested, intermediate directories will be created as well.
:param str name: the directory to create, passed to :func:`os.makedirs`.
"""
def __init__(self, name, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, "mkdir {}".format(name) if label is DEFAULT else label, *args, **kwds)
self.__name = name
def do_execute(self, dependency_statuses):
try:
os.makedirs(self.__name)
except OSError as e:
if e.errno != errno.EEXIST or not os.path.isdir(self.__name):
raise
class DeleteFile(Action):
"""
A stock action that deletes a file.
No error will be raise if the file doesn't exist.
:param str name: the name of the file to delete, passed to :func:`os.unlink`.
"""
def __init__(self, name, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, "rm {}".format(name) if label is DEFAULT else label, *args, **kwds)
self.__name = name
def do_execute(self, dependency_statuses):
try:
os.unlink(self.__name)
except OSError as e:
if e.errno != errno.ENOENT:
raise
class DeleteDirectory(Action):
"""
A stock action that deletes a directory (recursively).
No error will be raise if the directory doesn't exist.
:param str name: the name of the directory to delete, passed to :func:`shutil.rmtree`.
"""
def __init__(self, name, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, "rm -r {}".format(name) if label is DEFAULT else label, *args, **kwds)
self.__name = name
def do_execute(self, dependency_statuses):
try:
shutil.rmtree(self.__name)
except OSError as e:
if e.errno != errno.ENOENT:
raise
class CopyFile(Action):
"""
A stock action that copies a file. Arguments are passed to :func:`shutil.copy`.
:param str src: the file to copy
:param str dst: the destination
"""
def __init__(self, src, dst, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, "cp {} {}".format(src, dst) if label is DEFAULT else label, *args, **kwds)
self.__src = src
self.__dst = dst
def do_execute(self, dependency_statuses):
shutil.copy(self.__src, self.__dst)
class TouchFile(Action):
"""
A stock action that touches a file.
If the file already exists, its modification time will be modified.
Else, it will be created, empty.
Note that the containing directory must exist.
You might want to ensure that by adding a :class:`CreateDirectory` as a dependency.
:param str name: the name of the file to touch. Passed to :func:`open` and/or :func:`os.utime`.
"""
def __init__(self, name, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, "touch {}".format(name) if label is DEFAULT else label, *args, **kwds)
self.__name = name
def do_execute(self, dependency_statuses):
open(self.__name, "ab").close() # Create the file if needed
os.utime(self.__name, None) # Actually change its time
class Sleep(Action):
"""
A stock action that sleeps for a certain duration.
:param float secs: seconds to sleep, passed to :func:`time.sleep`.
"""
def __init__(self, secs, label=DEFAULT, *args, **kwds):
"""
@todoc
"""
Action.__init__(self, "sleep {}".format(secs) if label is DEFAULT else label, *args, **kwds)
self.__secs = secs
def do_execute(self, dependency_statuses):
time.sleep(self.__secs)