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
154
155
156
157
158
159
160
161
162
163
164
165
|
# -*- coding: utf-8 -*-
#------------------------------------------------------------------------------
# file: $Id$
# auth: Philip J Grabner <grabner@cadit.com>
# date: 2013/08/26
# copy: (C) Copyright 2013 Cadit Health Inc., All Rights Reserved.
#------------------------------------------------------------------------------
import sys, os, logging, time, argparse, gettext
import six
import iniherit, iniherit.parser
log = logging.getLogger(__name__)
# todo: reading from 'STDIN' and watching results in an unexpected
# behavior: if reading from a real pipe, it comes up as empty the
# non-first time. if reading from tty, you need to re-type the
# data... it should probably buffer it!
#------------------------------------------------------------------------------
def isstr(obj):
return isinstance(obj, six.string_types)
#------------------------------------------------------------------------------
def _(message, *args, **kw):
if args or kw:
return gettext.gettext(message).format(*args, **kw)
return gettext.gettext(message)
#------------------------------------------------------------------------------
class WatchingLoader(iniherit.Loader):
def __init__(self, options):
self.options = options
self.files = set()
def load(self, name, encoding):
log.debug(_('loading file "%s"'), name)
self.files.add(name)
return iniherit.Loader.load(self, name, encoding)
#------------------------------------------------------------------------------
def flatten(input, output, loader=None):
cfg = iniherit.RawConfigParser(loader=loader)
cfg.optionxform = str
if isstr(input):
cfg.read(input)
else:
cfg.readfp(input)
out = iniherit.parser.CP.RawConfigParser()
out.optionxform = str
cfg._apply(cfg, out)
if not isstr(output):
out.write(output)
else:
with open(output, 'wb') as fp:
out.write(fp)
#------------------------------------------------------------------------------
def getFilestats(files):
# todo: perhaps use an md5 checksum instead?...
ret = dict()
for filename in files:
try:
stat = os.stat(filename)
if stat:
mtime = stat.st_mtime
else:
mtime = None
except (OSError, IOError):
mtime = None
ret[filename] = mtime
return ret
#------------------------------------------------------------------------------
def run(options):
try:
while True:
loader = WatchingLoader(options)
flatten(options.input, options.output, loader)
if not options.watch:
return 0
filestats = getFilestats(loader.files)
changed = set()
while True:
time.sleep(options.interval)
log.debug(_('checking for changes...'))
newstats = getFilestats(loader.files)
for k, v in filestats.items():
if newstats.get(k) != v:
changed.add(k)
if len(changed) > 0:
break
if len(changed) == 1:
log.info(_('"%s" changed; updating output...'), list(changed)[0])
else:
log.info(_('%d files changed; updating output...'), len(changed))
continue
except KeyboardInterrupt:
return 0
#------------------------------------------------------------------------------
def main(argv=None):
cli = argparse.ArgumentParser(
description = _(
'Flatten inherited attributes in INI files. With the "--watch"'
' option, all input files can also be monitored for changes, and'
' any change will cause the output file to be automatically'
' updated.'
)
)
cli.add_argument(
_('-v'), _('--verbose'),
dest='verbose', action='count', default=0,
help=_('increase verbosity (can be specified multiple times)'))
cli.add_argument(
_('-w'), _('--watch'),
dest='watch', action='store_true', default=False,
help=_('watch all input files for changes and automatically'
' generate a new output file when a change is detected'
' (only useful when used with "--output")'))
cli.add_argument(
_('-i'), _('--watch-interval'),
dest='interval', type=float, default=2.0,
help=_('number of seconds (with decimal precision)'
' to wait between checks for changes (only useful when'
' used with "--watch") [defaults to %(default)s]'))
cli.add_argument(
metavar=_('INPUT'),
dest='input', nargs='?', default=sys.stdin,
help=_('set input filename; if unspecified or "-", reads input'
' from STDIN, in which case all inherits are taken relative'
' to the current working directory.'))
cli.add_argument(
metavar=_('OUTPUT'),
dest='output', nargs='?', default=sys.stdout,
help=_('set output filename; if unspecified or "-", writes output'
' to STDOUT.'))
options = cli.parse_args(argv)
if options.input == '-':
options.input = sys.stdin
if options.output == '-':
options.output = sys.stdout
if options.verbose > 0:
logging.basicConfig()
rootlog = logging.getLogger()
if options.verbose == 1:
rootlog.setLevel(logging.INFO)
elif options.verbose > 1:
rootlog.setLevel(logging.DEBUG)
return run(options)
#------------------------------------------------------------------------------
# end of $Id$
#------------------------------------------------------------------------------
|