-
Notifications
You must be signed in to change notification settings - Fork 135
Expand file tree
/
Copy pathentrypoint.py
More file actions
211 lines (174 loc) · 7.55 KB
/
entrypoint.py
File metadata and controls
211 lines (174 loc) · 7.55 KB
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import warnings
from typing import Iterable
from typing import List
from typing import Optional
from typing import TextIO
from typing import Union
from .library import Library
from .middlewares.middleware import Middleware
from .middlewares.parsestack import default_parse_stack
from .middlewares.parsestack import default_unparse_stack
from .splitter import Splitter
from .writer import BibtexFormat
from .writer import write
def _build_parse_stack(
parse_stack: Optional[Iterable[Middleware]],
append_middleware: Optional[Iterable[Middleware]],
) -> List[Middleware]:
if parse_stack is not None and append_middleware is not None:
raise ValueError(
"Provided both parse_stack and append_middleware."
"Only one should be provided."
"(append_middleware should only be used with the default parse_stack,"
"i.e., when the passed parse_stack is None.)"
)
if parse_stack is None:
parse_stack = default_parse_stack(allow_inplace_modification=True)
if append_middleware is None:
return list(parse_stack)
parse_stack_types = [type(m) for m in parse_stack]
append_stack_types = {type(m) for m in append_middleware}
stack_types_intersect = set(parse_stack_types).intersection(append_stack_types)
if len(stack_types_intersect) > 0:
warnings.warn(
"Some middleware passed in append_middleware are "
f"already in the default parse_stack ({stack_types_intersect})."
)
return list(parse_stack) + list(append_middleware)
def _build_unparse_stack(
unparse_stack: Optional[Iterable[Middleware]],
prepend_middleware: Optional[Iterable[Middleware]],
) -> List[Middleware]:
if unparse_stack is not None and prepend_middleware is not None:
raise ValueError(
"Provided both parse_stack and append_middleware."
"Only one should be provided."
"(prepend_middleware should only be used with the default parse_stack,"
"i.e., when the passed parse_stack is None.)"
)
if unparse_stack is None:
unparse_stack = default_unparse_stack(allow_inplace_modification=False)
if prepend_middleware is None:
return list(unparse_stack)
parse_stack_types = [type(m) for m in unparse_stack]
append_stack_types = {type(m) for m in prepend_middleware}
stack_types_intersect = set(parse_stack_types).intersection(append_stack_types)
if len(stack_types_intersect) > 0:
warnings.warn(
"Some middleware passed in append_middleware are "
f"already in the default parse_stack ({stack_types_intersect})."
)
return list(prepend_middleware) + list(unparse_stack)
def parse_string(
bibtex_str: str,
parse_stack: Optional[Iterable[Middleware]] = None,
append_middleware: Optional[Iterable[Middleware]] = None,
library: Optional[Library] = None,
):
"""Parse a BibTeX string.
:param bibtex_str: BibTeX string to parse
:param parse_stack:
List of middleware to apply to the database after splitting.
If ``None`` (default), a default stack will be used providing simple standard functionality.
:param append_middleware:
List of middleware to append to the default stack
(ignored if a not-``None`` parse_stack is passed).
:param library:
Library to add entries to. If ``None`` (default), a new library will be created.
:return: Library: Parsed BibTeX database
"""
splitter = Splitter(bibstr=bibtex_str)
library = splitter.split(library=library)
middleware: Middleware
for middleware in _build_parse_stack(parse_stack, append_middleware):
library = middleware.transform(library=library)
return library
def parse_file(
path: str,
parse_stack: Optional[Iterable[Middleware]] = None,
append_middleware: Optional[Iterable[Middleware]] = None,
encoding: str = "UTF-8",
) -> Library:
"""Parse a BibTeX file
:param path: Path to BibTeX file
:param parse_stack:
List of middleware to apply to the database after splitting.
If ``None`` (default), a default stack will be used providing simple standard functionality.
:param append_middleware:
List of middleware to append to the default stack
(ignored if a not-``None`` parse_stack is passed).
:param encoding: Encoding of the .bib file. Default encoding is ``"UTF-8"``.
:return: Library: Parsed BibTeX library
"""
with open(path, encoding=encoding) as f:
bibtex_str = f.read()
return parse_string(
bibtex_str, parse_stack=parse_stack, append_middleware=append_middleware
)
def parse_url(
url: str,
parse_stack: Optional[Iterable[Middleware]] = None,
append_middleware: Optional[Iterable[Middleware]] = None,
encoding: str = "UTF-8",
) -> Library:
"""Parse a BibTeX file from an URL
:param url: Url to BibTeX file
:param parse_stack:
List of middleware to apply to the database after splitting.
If ``None`` (default), a default stack will be used providing simple standard functionality.
:param append_middleware:
List of middleware to append to the default stack
(ignored if a not-``None`` parse_stack is passed).
:param encoding: Encoding of the .bib file. Default encoding is ``"UTF-8"``.
:return: Library: Parsed BibTeX library
"""
import urllib.request
with urllib.request.urlopen(url) as f:
bibtex_str = f.read().decode(encoding)
return parse_string(
bibtex_str, parse_stack=parse_stack, append_middleware=append_middleware
)
def write_file(
file: Union[str, TextIO],
library: Library,
parse_stack: Optional[Iterable[Middleware]] = None,
append_middleware: Optional[Iterable[Middleware]] = None,
bibtex_format: Optional[BibtexFormat] = None,
) -> None:
"""Write a BibTeX database to a file.
:param file: File to write to. Can be a file name or a file object.
:param library: BibTeX database to serialize.
:param parse_stack: List of middleware to apply to the database before writing.
If None, a default stack will be used.
:param append_middleware: List of middleware to append to the default stack.
Only applicable if `parse_stack` is None.
:param bibtex_format: Customized BibTeX format to use (optional)."""
bibtex_str = write_string(
library=library,
unparse_stack=parse_stack,
prepend_middleware=append_middleware,
bibtex_format=bibtex_format,
)
if isinstance(file, str):
with open(file, "w") as f:
f.write(bibtex_str)
else:
file.write(bibtex_str)
def write_string(
library: Library,
unparse_stack: Optional[Iterable[Middleware]] = None,
prepend_middleware: Optional[Iterable[Middleware]] = None,
bibtex_format: Optional["BibtexFormat"] = None,
) -> str:
"""Serialize a BibTeX database to a string.
:param library: BibTeX database to serialize.
:param unparse_stack: List of middleware to apply to the database before writing.
If None, a default stack will be used.
:param prepend_middleware: List of middleware to prepend to the default stack.
Only applicable if `parse_stack` is None.
:param bibtex_format: Customized BibTeX format to use (optional).
"""
middleware: Middleware
for middleware in _build_unparse_stack(unparse_stack, prepend_middleware):
library = middleware.transform(library=library)
return write(library, bibtex_format=bibtex_format)