Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 291 lines (237 sloc) 8.412 kb
d2da31a @flavioamieiro Adds shebang to dojotools.py
authored
1 #!/usr/bin/env python
2de1f7e Add licensing information
Flávio Amieiro authored
2 #-*- coding: utf-8 -*-
3 """
4
5
6 Dojotools - tools for your coding dojo session
7
8 Copyright (C) 2009 Flávio Amieiro <amieiro.flavio@gmail.com>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; version 2 dated June, 1991.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21
22 If you find any bugs or have any suggestions email: amieiro.flavio@gmail.com
23 """
24
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
25 import os
ad06ffe @flavioamieiro Exit with status code 0 if the user presses ^C
authored
26 import sys
1e6dbcb @flavioamieiro Use Unix style file patterns instead of python regexes for ignored files
authored
27 from fnmatch import fnmatch
a29d7e7 Add git_commit_all as the callable. It adds all files and comits them
Flávio Amieiro authored
28 import subprocess
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
29 from time import ctime
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
30 from optparse import OptionParser
01da81f @flavioamieiro Add a status icon in the taskbar. And a timeout call to Monitor.check()
authored
31 import gtk
32 import gobject
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
33
13b4e9d @flavioamieiro Extract UserInterface to it's own file
authored
34 from ui import UserInterface
d450ced Add run_command.
Flávio Amieiro authored
35
22bdbfc @flavioamieiro Add timer control to the status icon
authored
36 class Timer(object):
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
37
1d0537e @flavioamieiro Put all the code in charge of presentation (UI) into a separate class
authored
38 def __init__(self, round_time=300):
becf71f @flavioamieiro Add the simplest timer possible
authored
39 self.round_time = round_time
22bdbfc @flavioamieiro Add timer control to the status icon
authored
40 self.time_left = self.round_time
41
42 gobject.timeout_add(1000, self.update)
43 self.running = False
44
45 def start(self):
46 self.running = True
47
48 def pause(self):
49 self.running = False
50
51 def update(self):
52 if self.running:
53 if self.time_left:
54 self.time_left -= 1
55 else:
56 self.time_left = self.round_time
57
58 return True
59
60
1d0537e @flavioamieiro Put all the code in charge of presentation (UI) into a separate class
authored
61 class Monitor(object):
62
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
63 def __init__(self, ui, directory, commands, patterns_file, commit=False):
1d0537e @flavioamieiro Put all the code in charge of presentation (UI) into a separate class
authored
64 """
65 'directory' is the directory to be watched for changes.
66
67 --
68
69 'commands' is a list with the commands to run when a file changes
70
71 --
72
517eb28 @flavioamieiro Patterns are now regular expressions. They need to be valid Python regex...
authored
73 'patterns_files' is the path to a file which contains a list that will
74 be used to filter the files we're watching.
1d0537e @flavioamieiro Put all the code in charge of presentation (UI) into a separate class
authored
75 """
76 self.old_sum = 0
77 self.directory = directory
78 self.commands = commands
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
79 self.patterns = self._get_patterns(patterns_file)
1d0537e @flavioamieiro Put all the code in charge of presentation (UI) into a separate class
authored
80 self.ui = ui
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
81 self.commit = commit
1d0537e @flavioamieiro Put all the code in charge of presentation (UI) into a separate class
authored
82
83 gobject.timeout_add(1000, self.check)
84
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
85 def _get_patterns(self, patterns_file):
517eb28 @flavioamieiro Patterns are now regular expressions. They need to be valid Python regex...
authored
86 """
1e6dbcb @flavioamieiro Use Unix style file patterns instead of python regexes for ignored files
authored
87 Reads `patterns_file' and returns a list of patterns found in
88 the patterns file, so they can be used in fnmatch.
89
90 Patterns should be Unix style.
91
92 For more information, type help(fnmatch) in a python shell
517eb28 @flavioamieiro Patterns are now regular expressions. They need to be valid Python regex...
authored
93 """
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
94 try:
95 with open(patterns_file, 'r') as f:
1e6dbcb @flavioamieiro Use Unix style file patterns instead of python regexes for ignored files
authored
96 patterns = [pattern.strip() for pattern in f.readlines()]
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
97 except IOError:
98 sys.stdout.write(
99 'Could not find %s. Patterns will not be ignored\n'
100 % patterns_file
101 )
102 patterns = []
103
517eb28 @flavioamieiro Patterns are now regular expressions. They need to be valid Python regex...
authored
104 # Add patterns_file to the ignored patterns so it won't be tracked
105 patterns += [os.path.basename(patterns_file)]
106
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
107 return patterns
108
537f414 @flavioamieiro Change the order of some methods
authored
109 def _filter_files(self, files):
110 """
111 Filter a list of strings based on each item in 'self.patterns'
112
113 This function must be called every time `check` is called so we
114 will not ignore newly created files in the directory (that is why
115 files is not an instance attribute)
116 """
117 for p in self.patterns:
1e6dbcb @flavioamieiro Use Unix style file patterns instead of python regexes for ignored files
authored
118 files = [f for f in files if not fnmatch(f, p)]
537f414 @flavioamieiro Change the order of some methods
authored
119 return files
120
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
121 def git_commit_all(self):
122 """
123 Adds all files and commits them using git
124 """
125 msg = ctime()
126 process = subprocess.Popen(
127 "git add . && git commit -m '%s'" % msg,
128 shell=True,
129 cwd=self.directory,
130 )
131
132 #if git returns 128 it means 'command not found' or 'not a git repo'
133 if process.wait() == 128:
134 error = ('Impossible to commit to repository. '
135 'Make sure git is installed an this is a valid repository')
136 raise OSError(error)
137
6d03dc2 @flavioamieiro Make run_command a method of the Monitor class
authored
138 def run_command(self, test_cmd):
139 """
fdd649c @flavioamieiro fix typo in run_command's docstring
authored
140 As the name says, runs a command and waits for it to finish
6d03dc2 @flavioamieiro Make run_command a method of the Monitor class
authored
141 """
142 process = subprocess.Popen(
143 test_cmd,
144 shell = True,
145 cwd = self.directory,
146 stdout = subprocess.PIPE,
147 stderr = subprocess.PIPE,
148 )
149
150 output = process.stdout.read()
151 output += process.stderr.read()
152 status = process.wait()
153
49be869 @flavioamieiro Rename and reorder a few methods
authored
154 self.ui.show_command_results(status, output)
6d03dc2 @flavioamieiro Make run_command a method of the Monitor class
authored
155
498fad1 @flavioamieiro Bring filter_files to the Monitor class
authored
156 def check(self):
157 """
158 Monitor self.directory for changes, ignoring files matching any item
418b8d6 @flavioamieiro Remove the git commit option
authored
159 in self.patterns and runs any command in self.commands when a file has
498fad1 @flavioamieiro Bring filter_files to the Monitor class
authored
160 changed.
3d00dff @flavioamieiro Add a 'Monitor' class
authored
161 """
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
162 m_time_list = []
3d00dff @flavioamieiro Add a 'Monitor' class
authored
163 for root, dirs, files in os.walk(self.directory):
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
164 # We must ignore all the files in .git directory because
165 # any commit changes them. Taking this directory into
166 # consideration would cause an infinite loop.
167 if '.git' in root:
168 continue
498fad1 @flavioamieiro Bring filter_files to the Monitor class
authored
169 files = self._filter_files(files)
c929387 @flavioamieiro Add a comment about += operator
authored
170 # Be careful. The += operator works as the extend method
171 # on mutable objects. For more information refer to
172 # http://zephyrfalcon.org/labs/python_pitfalls.html
ba108ef Ran pylint on the code
Flávio Amieiro authored
173 m_time_list += [
174 os.stat(os.path.join(root, f)).st_mtime for f in files
175 ]
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
176
177 new_sum = sum(m_time_list)
3d00dff @flavioamieiro Add a 'Monitor' class
authored
178 if new_sum != self.old_sum:
418b8d6 @flavioamieiro Remove the git commit option
authored
179 for command in self.commands:
180 self.run_command(command)
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
181 if self.commit:
182 self.git_commit_all()
3d00dff @flavioamieiro Add a 'Monitor' class
authored
183 self.old_sum = new_sum
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
184
3d00dff @flavioamieiro Add a 'Monitor' class
authored
185 # This method must return True so gobject.timeout_add runs it again
186 return True
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
187
165e4dd Move the cli args treatment to a function
Flávio Amieiro authored
188
189 def parse_options():
ef49ed5 Add usage information
Flávio Amieiro authored
190 usage = "%prog [OPTIONS] COMMAND ..."
2819817 Add a description to optparse
Flávio Amieiro authored
191 description = """
192 %prog watches a directory for changes. As soon as there are any changes
193 to the files being watched, it runs the commands specified as positional
194 arguments. You can specify as many commands as you wish, but don't forget
195 to use quotes if you command has spaces in it.
196 """.replace(' ', '')
197 parser = OptionParser(usage, description=description)
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
198 parser.add_option(
e5af747 @flavioamieiro Add basic support for Arduino as a UI. Based on the work done by Bernard...
authored
199 '-a',
200 '--arduino',
201 action='store_true',
202 dest = 'arduino',
203 help = (
204 'uses an arduino as a visual output'
205 ),
206 default = False,
207 )
208 parser.add_option(
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
209 '-c',
210 '--commit',
211 action='store_true',
212 dest = 'commit',
213 help = (
214 'if this flag is used, a git commit will '
215 'be issued whenever the files change'
216 ),
217 default = False,
218 )
219 parser.add_option(
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
220 '-d',
221 '--directory',
222 action = 'store',
223 type = 'string',
224 dest = 'directory',
225 help = 'Watch DIRECTORY',
226 metavar = 'DIRECTORY',
227 default = os.path.abspath(os.path.curdir)
228 )
743e1f8 Add a option to tell wich command to run when changes are made to the di...
Flávio Amieiro authored
229
230 parser.add_option(
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
231 '-p',
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
232 '--patterns_file',
233 action = 'store',
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
234 type = 'string',
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
235 dest = 'patterns_file',
517eb28 @flavioamieiro Patterns are now regular expressions. They need to be valid Python regex...
authored
236 help = (
237 'Defines the file with patterns to ignore. '
238 ),
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
239 metavar = 'PATTERNS_FILE',
240 default = None,
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
241 )
41f3081 @flavioamieiro Add command line option to override the round time
authored
242
243 parser.add_option(
244 '-t',
245 '--time',
246 action = 'store',
247 type = 'int',
248 dest = 'round_time',
249 help = 'Define the time of each round',
250 default = 300
251 )
cc46f71 @flavioamieiro Change a return statement for style
authored
252 return parser.parse_args()
165e4dd Move the cli args treatment to a function
Flávio Amieiro authored
253
254
255 if __name__ == '__main__':
256
257 options, args = parse_options()
38328b4 The monitor function now has no default values.
Flávio Amieiro authored
258
517eb28 @flavioamieiro Patterns are now regular expressions. They need to be valid Python regex...
authored
259 if options.patterns_file == None:
260 options.patterns_file = os.path.join(
261 options.directory,
262 '.dojoignore'
263 )
264
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
265 try:
7a39fdb Add command line options for the directory to watch and for the patterns...
Flávio Amieiro authored
266 print 'Monitoring files in %s' % options.directory
010147d @flavioamieiro Reads the patterns to ignore from a .dojoignore file
authored
267 print 'ignoring files in %s' % (options.patterns_file)
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
268 print 'press ^C to quit'
38328b4 The monitor function now has no default values.
Flávio Amieiro authored
269
22bdbfc @flavioamieiro Add timer control to the status icon
authored
270 timer = Timer(options.round_time)
e5af747 @flavioamieiro Add basic support for Arduino as a UI. Based on the work done by Bernard...
authored
271
272 if options.arduino:
273 from arduino_ui import ArduinoUi
274 ui = ArduinoUi()
275 else:
276 ui = UserInterface(timer)
277
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
278 monitor = Monitor(
d2b7e9e @flavioamieiro Instantiate Monitor object using keyword arguments
authored
279 ui = ui,
280 directory = options.directory,
281 commands = args,
282 patterns_file = options.patterns_file,
283 commit = options.commit,
00c9b00 @flavioamieiro Add git commit capabilities back as requested
authored
284 )
01da81f @flavioamieiro Add a status icon in the taskbar. And a timeout call to Monitor.check()
authored
285
286 gtk.main()
38328b4 The monitor function now has no default values.
Flávio Amieiro authored
287
f7f16ec Monitors a directory for changes, and, if changed, calls a function
Flávio Amieiro authored
288 except KeyboardInterrupt:
289 print '\nleaving...'
ad06ffe @flavioamieiro Exit with status code 0 if the user presses ^C
authored
290 sys.exit(0)
Something went wrong with that request. Please try again.