Mercurial > feed-push
comparison feed-push @ 32:5348758c622d draft
don't keep file descriptors open
Signed-off-by: Changaco <changaco ατ changaco δοτ net>
author | Changaco <changaco ατ changaco δοτ net> |
---|---|
date | Sun, 05 Aug 2012 14:47:19 +0200 |
parents | f5c5e6f4edc8 |
children | adde3e451237 |
comparison
equal
deleted
inserted
replaced
31:f5c5e6f4edc8 | 32:5348758c622d |
---|---|
76 log(LOG_DEBUG, 'ignoring event '+gamin_events.get(event, str(event))+' on '+path) | 76 log(LOG_DEBUG, 'ignoring event '+gamin_events.get(event, str(event))+' on '+path) |
77 | 77 |
78 | 78 |
79 # Config parsing | 79 # Config parsing |
80 | 80 |
81 def parse_config_file(config_fd): | 81 def parse_config_file(config_path): |
82 feeds_paths = config_to_feed_paths_to_commands[config_fd.name] = {} | 82 try: |
83 config_fd = open(config_path) | |
84 except IOError as e: | |
85 return log('failed to open "'+line+'": '+str(e)) | |
86 feeds_paths = config_to_feed_paths_to_commands[config_path] = {} | |
83 cmd = [] | 87 cmd = [] |
84 log('parsing config file '+config_fd.name) | 88 log('parsing config file '+config_path) |
85 config_fd.seek(0) | 89 config_fd.seek(0) |
86 for i, line in enumerate(config_fd): | 90 for i, line in enumerate(config_fd): |
87 line = line.strip() | 91 line = line.strip() |
88 if len(line) == 0 or line[0] == '#': | 92 if len(line) == 0 or line[0] == '#': |
89 continue | 93 continue |
90 if line[0] == '%': | 94 if line[0] == '%': |
91 if isinstance(cmd, str): | 95 if isinstance(cmd, str): |
92 cmd = [] | 96 cmd = [] |
93 cmd.append(line[1:].rstrip(';')) | 97 cmd.append(line[1:].rstrip(';')) |
94 elif not cmd: | 98 elif not cmd: |
95 log(LOG_ERR, 'missing command in file '+config_fd.name+' before line '+str(i)) | 99 log(LOG_ERR, 'missing command in file '+config_path+' before line '+str(i)) |
96 return | 100 return |
97 else: | 101 else: |
98 cmd = '; '.join(cmd) | 102 cmd = '; '.join(cmd) |
99 for feed_path in glob(line): | 103 for feed_path in glob(line): |
100 feed_path = abspath(feed_path) | 104 feed_path = abspath(feed_path) |
101 dict_append(feeds_paths, feed_path, cmd) | 105 dict_append(feeds_paths, feed_path, cmd) |
102 if not feed_path in path_to_feed_fd: | 106 if not feed_path in watched_feeds: |
103 monitor.watch_file(feed_path, handler(handle_feed_change)) | 107 monitor.watch_file(feed_path, handler(handle_feed_change)) |
108 watched_feeds.add(feed_path) | |
104 log('now watching '+feed_path) | 109 log('now watching '+feed_path) |
110 config_fd.close() | |
105 | 111 |
106 | 112 |
107 # Gamin callbacks | 113 # Gamin callbacks |
108 | 114 |
109 def handler(f): | 115 def handler(f): |
116 | 122 |
117 def handle_config_change(path, event): | 123 def handle_config_change(path, event): |
118 path = abspath(path) | 124 path = abspath(path) |
119 if os.path.isdir(path): | 125 if os.path.isdir(path): |
120 ignore_event(path, event) | 126 ignore_event(path, event) |
121 elif not path in path_to_config_fd: | 127 elif not path in config_to_feed_paths_to_commands: |
122 open_config(path, event) | 128 open_config(path, event) |
123 elif event in [gamin.GAMChanged, gamin.GAMDeleted]: | 129 elif event in [gamin.GAMChanged, gamin.GAMDeleted]: |
124 update_config(path, event) | 130 update_config(path, event) |
125 else: | 131 else: |
126 ignore_event(path, event) | 132 ignore_event(path, event) |
127 | 133 |
128 def open_config(path, event): | 134 def open_config(path, event): |
129 if event in [gamin.GAMCreated, gamin.GAMExists]: | 135 if event in [gamin.GAMCreated, gamin.GAMExists]: |
130 if (not path.endswith('.conf') or path[0] == '.') and not hasattr(global_args.config, 'read'): | 136 if (not path.endswith('.conf') or path[0] == '.') and not hasattr(global_args.config, 'read'): |
131 return log('ignoring '+path+' (not a valid config file name)') | 137 return log('ignoring '+path+' (not a valid config file name)') |
132 try: | 138 parse_config_file(path) |
133 config_fd = path_to_config_fd[path] = open(path) | |
134 except IOError as e: | |
135 return log('failed to open "'+line+'" '+str(e)) | |
136 parse_config_file(config_fd) | |
137 else: | 139 else: |
138 ignore_event(path, event) | 140 ignore_event(path, event) |
139 | 141 |
140 def update_config(path, event): | 142 def update_config(path, event): |
141 feeds_paths = set(concat(d.keys() for d in config_to_feed_paths_to_commands.values())) | 143 feeds_paths = set(concat(d.keys() for d in config_to_feed_paths_to_commands.values())) |
142 if event == gamin.GAMChanged: | 144 if event == gamin.GAMChanged: |
143 parse_config_file(path_to_config_fd[path]) | 145 parse_config_file(path) |
144 elif event == gamin.GAMDeleted: | 146 elif event == gamin.GAMDeleted: |
145 log('removing actions from deleted config file '+path) | 147 log('removing actions from deleted config file '+path) |
146 config_to_feed_paths_to_commands.pop(path) | 148 config_to_feed_paths_to_commands.pop(path) |
147 path_to_config_fd.pop(path).close() | |
148 new_feeds_paths = set(concat(d.keys() for d in config_to_feed_paths_to_commands.values())) | 149 new_feeds_paths = set(concat(d.keys() for d in config_to_feed_paths_to_commands.values())) |
149 for feed_path in feeds_paths.difference(new_feeds_paths): | 150 for feed_path in feeds_paths.difference(new_feeds_paths): |
150 monitor.stop_watch(feed_path) | 151 monitor.stop_watch(feed_path) |
152 watched_feeds.discard(feed_path) | |
151 log('stopped watching '+feed_path) | 153 log('stopped watching '+feed_path) |
152 if feed_path in path_to_feed_fd: | |
153 path_to_feed_fd.pop(feed_path).close() | |
154 | 154 |
155 def handle_feed_change(path, event): | 155 def handle_feed_change(path, event): |
156 if path not in path_to_feed_fd: | 156 if event in [gamin.GAMCreated, gamin.GAMExists, gamin.GAMChanged]: |
157 if event in [gamin.GAMCreated, gamin.GAMExists, gamin.GAMChanged]: | 157 try: |
158 try: | 158 feed_fd = open(path) |
159 path_to_feed_fd[path] = open(path) | 159 except IOError as e: |
160 except IOError as e: | 160 return log('failed to open "'+path+'": '+str(e)) |
161 return log('failed to open "'+path+'": '+str(e)) | |
162 handle_feed_change(path, gamin.GAMChanged) | |
163 else: | |
164 ignore_event(path, event) | |
165 elif event == gamin.GAMCreated: | |
166 path_to_feed_fd.pop(path).close() | |
167 handle_feed_change(path, gamin.GAMCreated) | |
168 elif event == gamin.GAMChanged: | |
169 feed_fd = path_to_feed_fd[path] | |
170 feed_fd.seek(0) | |
171 feed = feedparser.parse(feed_fd.read()) | 161 feed = feedparser.parse(feed_fd.read()) |
162 feed_fd.close() | |
172 i = 0 | 163 i = 0 |
173 for entry in reversed(feed.entries): | 164 for entry in reversed(feed.entries): |
174 if entry.id in state['id_cache'].get(feed_fd.name, []) or \ | 165 if entry.id in state['id_cache'].get(path, []) or \ |
175 not global_args.flood and calendar.timegm(entry.published_parsed) < time.time() - 86400: | 166 not global_args.flood and calendar.timegm(entry.published_parsed) < time.time() - 86400: |
176 continue | 167 continue |
177 i += 1 | 168 i += 1 |
178 for feed_path_to_commands in config_to_feed_paths_to_commands.values(): | 169 for feed_path_to_commands in config_to_feed_paths_to_commands.values(): |
179 for cmd in feed_path_to_commands.get(path, []): | 170 for cmd in feed_path_to_commands.get(path, []): |
180 run_command(format_cmd(cmd, feed=feed.feed, entry=entry), entry.content[0].value) | 171 run_command(format_cmd(cmd, feed=feed.feed, entry=entry), entry.content[0].value) |
181 state['id_cache'][feed_fd.name] = [entry.id for entry in feed.entries] | 172 state['id_cache'][path] = [entry.id for entry in feed.entries] |
182 save_state() | 173 save_state() |
183 if i == 0: | 174 if i == 0: |
184 log('GAMChanged: no new entry in %s' % path) | 175 log('no new entry in %s' % path) |
185 elif event == gamin.GAMDeleted: | |
186 path_to_feed_fd.pop(path).close() | |
187 else: | 176 else: |
188 ignore_event(path, event) | 177 ignore_event(path, event) |
189 | 178 |
190 def save_state(): | 179 def save_state(): |
191 global_args.state_file.truncate(0) | 180 global_args.state_file.truncate(0) |
313 state.update(json.loads(saved_state)) | 302 state.update(json.loads(saved_state)) |
314 del saved_state | 303 del saved_state |
315 | 304 |
316 import gamin | 305 import gamin |
317 monitor = gamin.WatchMonitor() | 306 monitor = gamin.WatchMonitor() |
318 path_to_feed_fd = {} | 307 watched_feeds = set() |
319 path_to_config_fd = {} | |
320 config_to_feed_paths_to_commands = {} | 308 config_to_feed_paths_to_commands = {} |
321 if hasattr(global_args.config, 'read'): | 309 if hasattr(global_args.config, 'read'): |
322 os.chdir(os.path.dirname(global_args.config.name)) | 310 os.chdir(os.path.dirname(global_args.config.name)) |
323 monitor.watch_file(global_args.config.name, handler(handle_config_change)) | 311 monitor.watch_file(global_args.config.name, handler(handle_config_change)) |
324 else: | 312 else: |