2 from __future__ import with_statement
5 from dbuscron.bus import DbusBus
9 r = re.compile(r'\\x('+h+r'{2})|\\u('+h+'{4})')
13 (r'\x' in value or r'\u' in value)):
17 lambda m: chr(int(m.group(1), 16))
18 if m.group(1) is not None else
19 unichr(int(m.group(2), 16))
23 unescape = unescape_()
27 head, tail = args[0], args[1:]
29 for t in product(*tail):
35 class CrontabParserError(SyntaxError):
36 def __init__(self, message, lineno, expected=None):
38 if isinstance(expected, (tuple, list)):
39 exp = ' (expected %s or %s)' % (', '.join(expected[:-1]), expected[-1])
43 msg = '%s%s at line %d' % (message, exp, lineno)
45 SyntaxError.__init__(self, msg)
47 def CrontabParser(*filenames):
48 for filename in filenames:
49 if not os.path.exists(filename):
52 if os.path.isfile(filename):
53 parser_class = FileParser
55 elif os.path.isdir(filename):
56 parser_class = DirectoryParser
59 raise SystemError("I can parse only directories or simple files.")
61 return parser_class(filename)
63 raise SystemError("Can't find any config file or directory.")
65 class FileParser(object):
66 __fields_sep = re.compile(r'\s+')
67 __envvar_sep = re.compile(r'\s*=\s*')
70 'type_' : ('signal', 'method_call', 'method_return', 'error'),
72 'interface_' : re.compile(r'^[a-zA-Z][a-zA-Z0-9_.]+$'),
73 'path_' : re.compile(r'^/[a-zA-Z0-9_/]+$'),
74 'member_' : re.compile(r'^[a-zA-Z][a-zA-Z0-9_]+$'),
75 'destination_' : None,
89 def __init__(self, fname):
90 self.__bus = DbusBus()
91 self.__filename = fname
92 self.__environ = dict()
98 def _iterate_file(self, filename):
99 # bus type sender interface path member destination args command
101 with open(filename) as f:
106 if not line or line.startswith('#'):
109 parts = self.__fields_sep.split(line, 8)
111 parts = self.__envvar_sep.split(line, 1)
113 self.__environ[parts[0]] = parts[1]
116 raise CrontabParserError('Unexpected number of records', lineno)
118 rule = [('s', 'S'), self.__fields_chk['type_'], (None,), (None,), (None,), (None,), (None,), (None,)]
120 for p in range(0, 8):
122 rule[p] = parts[p].split(',')
126 for r in product(*rule):
129 r[0] = self.__bus.system
131 r[0] = self.__bus.session
133 raise CrontabParserError('Unexpected bus value', lineno, expected=('S', 's', '*'))
136 r[7] = map(unescape, r[7].split(';'))
139 for i, f in enumerate(self.__fields):
140 if r[i] is not None and self.__fields_chk[f]:
141 if isinstance(self.__fields_chk[f], tuple):
142 if r[i] not in self.__fields_chk[f]:
143 raise CrontabParserError('Unexpected %s value' % (f.strip('_')), lineno,
144 expected=self.__fields_chk[f])
146 if not self.__fields_chk[f].match(r[i]):
147 raise CrontabParserError('Incorrect %s value' % (f.strip('_')), lineno)
153 return self._iterate_file(self.__filename)
155 class DirectoryParser(CrontabParser):
157 def __init__(self, dirname, recursive=False):
158 self.__dirname = dirname
159 self.__recursive = recursive
160 super(DirectoryParser, self).__init__(None)
162 def _dirwalker_plain(self):
163 for i in os.listdir(self.__dirname):
164 if os.path.isfile(i):
167 def _dirwalker_recursive(self):
168 for r, d, f in os.walk(self.__dirname):
175 dirwalker = self._dirwalker_recursive
177 dirwalker = self._dirwalker_plain
179 for fname in dirwalker():
180 fullname = os.path.join(self.__dirname, fname)
181 self.__filename = fullname
182 for item in self._iterate_file(fullname):
185 def OptionsParser(args=None, help=u'', **opts):
187 from optparse import OptionParser
189 parser = OptionParser(usage=help, version="%prog " + dbuscron.__version__)
190 for opt, desc in opts.iteritems():
191 names = desc.pop('names')
193 parser.add_option(*names, **desc)
195 return parser.parse_args(args)[0]