-
Notifications
You must be signed in to change notification settings - Fork 2
/
stock.py
153 lines (115 loc) · 4.16 KB
/
stock.py
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# coding: utf8
# Copyright 2013-2017 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`.
"""
from __future__ import division, absolute_import, print_function
import errno
import os
import shutil
import subprocess
import time
from . import Action
class NullAction(Action):
"""
A stock action that does nothing.
Useful as a placeholder for several dependencies.
"""
def __init__(self):
Action.__init__(self, None)
def do_execute(self):
pass
class CallSubprocess(Action):
"""
A stock action that calls a subprocess.
Arguments are forwarded exactly to :func:`subprocess.check_call`.
Note: if the process fails,
:func:`~subprocess.check_call` raises a :exc:`subprocess.CalledProcessError`,
which `cannot be pickled <http://bugs.python.org/issue1692335>`__ in Python 2.
So, in that case, this action catches the original exception and
raises a :exc:`CalledProcessError`.
"""
def __init__(self, args, **kwds):
Action.__init__(self, " ".join(args))
self.__args = args
self.__kwds = kwds
def do_execute(self):
# subprocess.CalledProcessError can't be pickled in Python2
# See http://bugs.python.org/issue1692335
try:
subprocess.check_call(self.__args, **self.__kwds)
except subprocess.CalledProcessError as e:
raise CalledProcessError(e.returncode, e.cmd, e.output)
class CalledProcessError(Exception):
"""
Raised by :class:`CallSubprocess`
"""
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):
Action.__init__(self, "mkdir {}".format(name))
self.__name = name
def do_execute(self):
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):
Action.__init__(self, "rm {}".format(name))
self.__name = name
def do_execute(self):
try:
os.unlink(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):
Action.__init__(self, "cp {} {}".format(src, dst))
self.__src = src
self.__dst = dst
def do_execute(self):
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):
Action.__init__(self, "touch {}".format(name))
self.__name = name
def do_execute(self):
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):
Action.__init__(self, "sleep {}".format(secs))
self.__secs = secs
def do_execute(self):
time.sleep(self.__secs)