OLD | NEW |
1 # This file is part of Adblock Plus <https://adblockplus.org/>, | 1 # This file is part of Adblock Plus <https://adblockplus.org/>, |
2 # Copyright (C) 2006-2017 eyeo GmbH | 2 # Copyright (C) 2006-2017 eyeo GmbH |
3 # | 3 # |
4 # Adblock Plus is free software: you can redistribute it and/or modify | 4 # Adblock Plus is free software: you can redistribute it and/or modify |
5 # it under the terms of the GNU General Public License version 3 as | 5 # it under the terms of the GNU General Public License version 3 as |
6 # published by the Free Software Foundation. | 6 # published by the Free Software Foundation. |
7 # | 7 # |
8 # Adblock Plus is distributed in the hope that it will be useful, | 8 # Adblock Plus is distributed in the hope that it will be useful, |
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 # GNU General Public License for more details. | 11 # GNU General Public License for more details. |
12 # | 12 # |
13 # You should have received a copy of the GNU General Public License | 13 # You should have received a copy of the GNU General Public License |
14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
15 | 15 |
| 16 """Combine filter list fragments to produce filter lists.""" |
| 17 |
16 from __future__ import unicode_literals | 18 from __future__ import unicode_literals |
17 | 19 |
18 import base64 | 20 import base64 |
19 import hashlib | 21 import hashlib |
20 import itertools | 22 import itertools |
21 import logging | 23 import logging |
22 import time | 24 import time |
23 | 25 |
24 from .parser import parse_filterlist, Comment, Metadata | 26 from .parser import parse_filterlist, Comment, Metadata |
25 from .sources import NotFound | 27 from .sources import NotFound |
26 | 28 |
27 __all__ = ['render_filterlist', 'IncludeError', 'MissingHeader'] | 29 __all__ = ['IncludeError', 'MissingHeader', 'render_filterlist'] |
28 | 30 |
29 _logger = logging.getLogger(__name__) | 31 _logger = logging.getLogger(__name__) |
30 | 32 |
31 | 33 |
32 class IncludeError(Exception): | 34 class IncludeError(Exception): |
33 """Error in processing include instruction. | 35 """Error in processing include instruction.""" |
34 | |
35 :param error: Description of the error. | |
36 :param stack: A list of the names of included files. | |
37 """ | |
38 | 36 |
39 def __init__(self, error, stack): | 37 def __init__(self, error, stack): |
40 stack_str = ' from '.join(map("'{}'".format, reversed(stack))) | 38 stack_str = ' from '.join(map("'{}'".format, reversed(stack))) |
41 if stack_str: | 39 if stack_str: |
42 error = '{} when including {}'.format(error, stack_str) | 40 error = '{} when including {}'.format(error, stack_str) |
43 Exception.__init__(self, error) | 41 Exception.__init__(self, error) |
44 | 42 |
45 | 43 |
46 class MissingHeader(Exception): | 44 class MissingHeader(Exception): |
47 """First line of the result is not a valid header.""" | 45 """First line of the result is not a valid header.""" |
48 | 46 |
49 | 47 |
50 def _get_and_parse_fragment(name, sources, default_source, include_stack=[]): | 48 def _get_and_parse_fragment(name, sources, default_source, include_stack=[]): |
51 """Retrieve and parse fragment. | 49 """Retrieve and parse fragment. |
52 | 50 |
53 :returns: a tuple `(lines_iterator, inherited_source)` where | 51 Returns |
54 `inherited_source` is the default source to use for included | 52 ------- |
55 fragments. | 53 tuple (iterator of str, Source) |
| 54 First part is the content of the fragment line by line; second part is |
| 55 the default source to be used for included fragments. |
| 56 |
56 """ | 57 """ |
57 if ':' in name: | 58 if ':' in name: |
58 source_name, name_in_source = name.split(':', 1) | 59 source_name, name_in_source = name.split(':', 1) |
59 try: | 60 try: |
60 source = sources[source_name] | 61 source = sources[source_name] |
61 except KeyError: | 62 except KeyError: |
62 raise IncludeError("Unknown source: '{}'".format(source_name), | 63 raise IncludeError("Unknown source: '{}'".format(source_name), |
63 include_stack) | 64 include_stack) |
64 else: | 65 else: |
65 source, name_in_source = default_source, name | 66 source, name_in_source = default_source, name |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
159 """Validate the final list.""" | 160 """Validate the final list.""" |
160 first_line, rest = _first_and_rest(lines) | 161 first_line, rest = _first_and_rest(lines) |
161 if first_line.type != 'header': | 162 if first_line.type != 'header': |
162 raise MissingHeader('No header found at the beginning of the input.') | 163 raise MissingHeader('No header found at the beginning of the input.') |
163 return itertools.chain([first_line], rest) | 164 return itertools.chain([first_line], rest) |
164 | 165 |
165 | 166 |
166 def render_filterlist(name, sources, top_source=None): | 167 def render_filterlist(name, sources, top_source=None): |
167 """Produce filter list from fragments. | 168 """Produce filter list from fragments. |
168 | 169 |
169 :param name: Name of the top level fragment. | 170 Parameters |
170 :param sources: A mapping of source names to sources for getting fragments. | 171 ---------- |
171 :param top_source: Default source used for getting top fragment. | 172 name : str |
172 :returns: Iterable of of filter list lines (see `line_type` in parser.py). | 173 Name of the top level fragment. |
173 :raises IncludeError: When an include error can't be processed. | 174 sources : dict of str -> Source |
174 :raises ParseError: When any of the fragments contain lines that can't | 175 Sources for loading included fragments. |
175 be parsed. | 176 top_source : Source |
176 :raises MissingHeader: If the top level fragment doesn't start with a valid | 177 The source used to load the top level fragment. |
177 header. | 178 |
| 179 Returns |
| 180 ------- |
| 181 iterable of namedtuple (see `_line_type` in parser.py) |
| 182 Rendered filter list. |
| 183 |
| 184 Raises |
| 185 ------ |
| 186 IncludeError |
| 187 When an include error can't be processed. |
| 188 ParseError |
| 189 When any of the fragments contain lines that can't be parsed. |
| 190 MissingHeader |
| 191 If the top level fragment doesn't start with a valid header. This would |
| 192 lead to rendering an invalid filter list, so we immediately abort. |
| 193 |
178 """ | 194 """ |
179 _logger.info('Rendering: %s', name) | 195 _logger.info('Rendering: %s', name) |
180 lines, default_source = _get_and_parse_fragment(name, sources, top_source) | 196 lines, default_source = _get_and_parse_fragment(name, sources, top_source) |
181 lines = _process_includes(sources, default_source, [name], lines) | 197 lines = _process_includes(sources, default_source, [name], lines) |
182 for proc in [_process_timestamps, _insert_version, _remove_duplicates, | 198 for proc in [_process_timestamps, _insert_version, _remove_duplicates, |
183 _insert_checksum, _validate]: | 199 _insert_checksum, _validate]: |
184 lines = proc(lines) | 200 lines = proc(lines) |
185 return lines | 201 return lines |
OLD | NEW |