bibtexparser 2.0.0b7__tar.gz → 2.0.0b9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/LICENSE +1 -1
  2. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/PKG-INFO +34 -19
  3. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/README.md +12 -12
  4. bibtexparser-2.0.0b9/bibtexparser/__init__.py +11 -0
  5. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/entrypoint.py +102 -22
  6. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/exceptions.py +2 -1
  7. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/library.py +34 -35
  8. bibtexparser-2.0.0b9/bibtexparser/middlewares/__init__.py +22 -0
  9. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/enclosing.py +13 -17
  10. bibtexparser-2.0.0b9/bibtexparser/middlewares/fieldkeys.py +54 -0
  11. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/interpolate.py +3 -5
  12. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/latex_encoding.py +34 -26
  13. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/middleware.py +12 -13
  14. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/month.py +6 -5
  15. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/names.py +33 -24
  16. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/parsestack.py +4 -10
  17. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/sorting_blocks.py +14 -18
  18. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/sorting_entry_fields.py +3 -2
  19. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/model.py +31 -40
  20. bibtexparser-2.0.0b9/bibtexparser/py.typed +0 -0
  21. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/splitter.py +88 -54
  22. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/writer.py +13 -19
  23. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/PKG-INFO +34 -19
  24. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/SOURCES.txt +2 -0
  25. bibtexparser-2.0.0b9/bibtexparser.egg-info/requires.txt +10 -0
  26. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/setup.py +7 -6
  27. bibtexparser-2.0.0b9/tests/test_entrypoint.py +243 -0
  28. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/tests/test_library.py +5 -12
  29. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/tests/test_model.py +11 -18
  30. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/tests/test_writer.py +11 -13
  31. bibtexparser-2.0.0b7/bibtexparser/__init__.py +0 -8
  32. bibtexparser-2.0.0b7/bibtexparser/middlewares/__init__.py +0 -29
  33. bibtexparser-2.0.0b7/bibtexparser.egg-info/requires.txt +0 -15
  34. bibtexparser-2.0.0b7/tests/test_entrypoint.py +0 -13
  35. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/dependency_links.txt +0 -0
  36. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/top_level.txt +0 -0
  37. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/setup.cfg +0 -0
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: bibtexparser
3
- Version: 2.0.0b7
3
+ Version: 2.0.0b9
4
4
  Summary: Bibtex parser for python 3
5
5
  Home-page: https://github.com/sciunto-org/python-bibtexparser
6
6
  Author: Michael Weiss and other contributors
@@ -10,29 +10,44 @@ Maintainer-email: code@mweiss.ch
10
10
  License: MIT
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
13
17
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
18
  Classifier: License :: OSI Approved :: MIT License
15
19
  Classifier: Operating System :: OS Independent
20
+ Requires-Python: >=3.9
16
21
  Description-Content-Type: text/markdown
17
22
  License-File: LICENSE
18
- Requires-Dist: pylatexenc>=2.10
23
+ Requires-Dist: pylatexenc~=2.10
19
24
  Provides-Extra: test
20
25
  Requires-Dist: pytest; extra == "test"
21
26
  Requires-Dist: pytest-xdist; extra == "test"
22
27
  Requires-Dist: pytest-cov; extra == "test"
23
28
  Requires-Dist: jupyter; extra == "test"
24
- Provides-Extra: lint
25
- Requires-Dist: black==23.3.0; extra == "lint"
26
- Requires-Dist: isort==5.12.0; extra == "lint"
27
- Requires-Dist: docstr-coverage==2.2.0; extra == "lint"
28
29
  Provides-Extra: docs
29
30
  Requires-Dist: sphinx; extra == "docs"
31
+ Dynamic: author
32
+ Dynamic: author-email
33
+ Dynamic: classifier
34
+ Dynamic: description
35
+ Dynamic: description-content-type
36
+ Dynamic: home-page
37
+ Dynamic: license
38
+ Dynamic: license-file
39
+ Dynamic: maintainer
40
+ Dynamic: maintainer-email
41
+ Dynamic: provides-extra
42
+ Dynamic: requires-dist
43
+ Dynamic: requires-python
44
+ Dynamic: summary
30
45
 
31
46
  # python-bibtexparser v2
32
47
 
33
- Welcome to python-bibtexparser, a parser for `.bib` files with a long history and wide adaption.
48
+ Welcome to python-bibtexparser, a parser for `.bib` files with a long history and wide adoption.
34
49
 
35
- Bibtexparser is available in two versions: V1 and V2. For new projects, we recommend using v2 which, in the long run, will provide an overall more robust and faster experience. **For now, however, note that v2 is an early beta, and does not contain all features of v1**. Install v2 using pip:
50
+ Bibtexparser is available in two versions: V1 and V2. For new projects, we recommend using v2 which, in the long run, will provide an overall more robust and faster experience. **For now, however, note that v2 is still in beta stage, and does not contain all features of v1**. Install v2 using pip:
36
51
 
37
52
  ```bash
38
53
  pip install bibtexparser --pre
@@ -49,11 +64,11 @@ If instead, you want to use v1, install it using:
49
64
  pip install bibtexparser~=1.0
50
65
  ```
51
66
 
52
- Note that all development and maintenance effort is focussed on v2.
67
+ Note that all development and maintenance effort is focussed on v2.
53
68
  Small PRs for v1 are still accepted, but only as long as they are backwards compatible and don't introduce much additional technical debt.
54
- Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1).
69
+ Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1).
55
70
 
56
- The remainder of this README is specific to v2.
71
+ The remainder of this README is specific to v2.
57
72
 
58
73
  ## Documentation
59
74
  Go check out our documentation on [https://bibtexparser.readthedocs.io/en/main/](https://bibtexparser.readthedocs.io/en/main/).
@@ -91,12 +106,12 @@ bib_database = bibtexparser.parse_string(bibtex_string,
91
106
 
92
107
  # Let's transform it back to a bibtex_string.
93
108
  new_bibtex_string = bibtexparser.write_string(bib_database,
94
- # Revert aboves transfomration
109
+ # Revert above transformation
95
110
  prepend_middleware=[MergeNameParts(), MergeCoAuthors()]
96
111
  )
97
112
  ```
98
113
 
99
- These examples really only show the bare minimum.
114
+ These examples really only show the bare minimum.
100
115
  Consult the documentation for a list of available middleware, parsing options and write-formatting options.
101
116
 
102
117
  ## V2 Architecture and Terminology
@@ -106,21 +121,21 @@ Consult the documentation for a list of available middleware, parsing options an
106
121
  The architecture consists of the following components:
107
122
 
108
123
  #### Library
109
- Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preamples and their metadata (e.g. order).
124
+ Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preambles and their metadata (e.g. order).
110
125
 
111
126
  #### A Splitter
112
127
  Splits a bibtex string into basic blocks (Entry, String, Preamble, ...), with correspondingly split content (e.g. fields on Entry, key-value on String, ...).
113
- The splitter aims to be forgiving when facing invalid bibtex: A line starting with a block definition (`@....`) ends the previous block, even if not yet every bracket is closed, failing the parsing of the previous block. Correspondingly, one block type is "ParsingFailedBlock".
128
+ The splitter aims to be forgiving when facing invalid bibtex: A line starting with a block definition (``@....``) ends the previous block, even if not yet every bracket is closed, failing the parsing of the previous block. Correspondingly, one block type is "ParsingFailedBlock".
114
129
 
115
130
  #### Middleware
116
- Middleware layers transform a library and its blocks, for example by decoding latex special characters, interpolating string references, resoling crossreferences or re-ordering blocks. Thus, the choice of middleware allows to customize parsing and writing to ones specific usecase. Note: Middlewares, by default, no not mutate their input, but return a modified copy.
131
+ Middleware layers transform a library and its blocks, for example by decoding latex special characters, interpolating string references, resolving crossreferences or re-ordering blocks. Thus, the choice of middleware allows to customize parsing and writing to ones specific usecase. Note: Middlewares, by default, do not mutate their input, but return a modified copy.
117
132
 
118
133
  #### Writer
119
- Writes the content of a bibtex library to a `.bib` file. Optional formatting parameters can be passed using a corresponding dedicated data structure.
134
+ Writes the content of a bibtex library to a ``.bib`` file. Optional formatting parameters can be passed using a corresponding dedicated data structure.
120
135
 
121
136
  ## About
122
137
 
123
- Since 2022, `bibtexparser` is primarily written and maintained by Michael Weiss ([@MiWeiss](https://github.com/MiWeiss/)). In 2024, Tom de Geus ([@tdegeus](https://github.com/tdegeus)) joined as co-maintainer.
138
+ Since 2022, `bibtexparser` is primarily written and maintained by Michael Weiss ([@MiWeiss](https://github.com/MiWeiss/)). In 2024, Tom de Geus ([@tdegeus](https://github.com/tdegeus)) joined as co-maintainer.
124
139
 
125
140
  Credits and thanks to the many contributors who helped creating this library, including
126
141
  François Boulogne ([@sciunto](https://github.com/sciunto/), creator of the first version) and Olivier Mangin ([@omangin](https://github.com/omangin/), long-term contributor).
@@ -1,8 +1,8 @@
1
1
  # python-bibtexparser v2
2
2
 
3
- Welcome to python-bibtexparser, a parser for `.bib` files with a long history and wide adaption.
3
+ Welcome to python-bibtexparser, a parser for `.bib` files with a long history and wide adoption.
4
4
 
5
- Bibtexparser is available in two versions: V1 and V2. For new projects, we recommend using v2 which, in the long run, will provide an overall more robust and faster experience. **For now, however, note that v2 is an early beta, and does not contain all features of v1**. Install v2 using pip:
5
+ Bibtexparser is available in two versions: V1 and V2. For new projects, we recommend using v2 which, in the long run, will provide an overall more robust and faster experience. **For now, however, note that v2 is still in beta stage, and does not contain all features of v1**. Install v2 using pip:
6
6
 
7
7
  ```bash
8
8
  pip install bibtexparser --pre
@@ -19,11 +19,11 @@ If instead, you want to use v1, install it using:
19
19
  pip install bibtexparser~=1.0
20
20
  ```
21
21
 
22
- Note that all development and maintenance effort is focussed on v2.
22
+ Note that all development and maintenance effort is focussed on v2.
23
23
  Small PRs for v1 are still accepted, but only as long as they are backwards compatible and don't introduce much additional technical debt.
24
- Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1).
24
+ Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1).
25
25
 
26
- The remainder of this README is specific to v2.
26
+ The remainder of this README is specific to v2.
27
27
 
28
28
  ## Documentation
29
29
  Go check out our documentation on [https://bibtexparser.readthedocs.io/en/main/](https://bibtexparser.readthedocs.io/en/main/).
@@ -61,12 +61,12 @@ bib_database = bibtexparser.parse_string(bibtex_string,
61
61
 
62
62
  # Let's transform it back to a bibtex_string.
63
63
  new_bibtex_string = bibtexparser.write_string(bib_database,
64
- # Revert aboves transfomration
64
+ # Revert above transformation
65
65
  prepend_middleware=[MergeNameParts(), MergeCoAuthors()]
66
66
  )
67
67
  ```
68
68
 
69
- These examples really only show the bare minimum.
69
+ These examples really only show the bare minimum.
70
70
  Consult the documentation for a list of available middleware, parsing options and write-formatting options.
71
71
 
72
72
  ## V2 Architecture and Terminology
@@ -76,21 +76,21 @@ Consult the documentation for a list of available middleware, parsing options an
76
76
  The architecture consists of the following components:
77
77
 
78
78
  #### Library
79
- Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preamples and their metadata (e.g. order).
79
+ Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preambles and their metadata (e.g. order).
80
80
 
81
81
  #### A Splitter
82
82
  Splits a bibtex string into basic blocks (Entry, String, Preamble, ...), with correspondingly split content (e.g. fields on Entry, key-value on String, ...).
83
- The splitter aims to be forgiving when facing invalid bibtex: A line starting with a block definition (`@....`) ends the previous block, even if not yet every bracket is closed, failing the parsing of the previous block. Correspondingly, one block type is "ParsingFailedBlock".
83
+ The splitter aims to be forgiving when facing invalid bibtex: A line starting with a block definition (``@....``) ends the previous block, even if not yet every bracket is closed, failing the parsing of the previous block. Correspondingly, one block type is "ParsingFailedBlock".
84
84
 
85
85
  #### Middleware
86
- Middleware layers transform a library and its blocks, for example by decoding latex special characters, interpolating string references, resoling crossreferences or re-ordering blocks. Thus, the choice of middleware allows to customize parsing and writing to ones specific usecase. Note: Middlewares, by default, no not mutate their input, but return a modified copy.
86
+ Middleware layers transform a library and its blocks, for example by decoding latex special characters, interpolating string references, resolving crossreferences or re-ordering blocks. Thus, the choice of middleware allows to customize parsing and writing to ones specific usecase. Note: Middlewares, by default, do not mutate their input, but return a modified copy.
87
87
 
88
88
  #### Writer
89
- Writes the content of a bibtex library to a `.bib` file. Optional formatting parameters can be passed using a corresponding dedicated data structure.
89
+ Writes the content of a bibtex library to a ``.bib`` file. Optional formatting parameters can be passed using a corresponding dedicated data structure.
90
90
 
91
91
  ## About
92
92
 
93
- Since 2022, `bibtexparser` is primarily written and maintained by Michael Weiss ([@MiWeiss](https://github.com/MiWeiss/)). In 2024, Tom de Geus ([@tdegeus](https://github.com/tdegeus)) joined as co-maintainer.
93
+ Since 2022, `bibtexparser` is primarily written and maintained by Michael Weiss ([@MiWeiss](https://github.com/MiWeiss/)). In 2024, Tom de Geus ([@tdegeus](https://github.com/tdegeus)) joined as co-maintainer.
94
94
 
95
95
  Credits and thanks to the many contributors who helped creating this library, including
96
96
  François Boulogne ([@sciunto](https://github.com/sciunto/), creator of the first version) and Olivier Mangin ([@omangin](https://github.com/omangin/), long-term contributor).
@@ -0,0 +1,11 @@
1
+ import bibtexparser.exceptions
2
+ import bibtexparser.middlewares
3
+ import bibtexparser.model
4
+ from bibtexparser.entrypoint import parse_file
5
+ from bibtexparser.entrypoint import parse_string
6
+ from bibtexparser.entrypoint import write_file
7
+ from bibtexparser.entrypoint import write_string
8
+ from bibtexparser.library import Library
9
+ from bibtexparser.writer import BibtexFormat
10
+
11
+ __version__ = "2.0.0b9"
@@ -1,11 +1,18 @@
1
+ import codecs
1
2
  import warnings
2
- from typing import Iterable, List, Optional, TextIO, Union
3
+ from typing import Iterable
4
+ from typing import List
5
+ from typing import Optional
6
+ from typing import TextIO
7
+ from typing import Union
3
8
 
4
9
  from .library import Library
5
10
  from .middlewares.middleware import Middleware
6
- from .middlewares.parsestack import default_parse_stack, default_unparse_stack
11
+ from .middlewares.parsestack import default_parse_stack
12
+ from .middlewares.parsestack import default_unparse_stack
7
13
  from .splitter import Splitter
8
- from .writer import BibtexFormat, write
14
+ from .writer import BibtexFormat
15
+ from .writer import write
9
16
 
10
17
 
11
18
  def _build_parse_stack(
@@ -14,9 +21,9 @@ def _build_parse_stack(
14
21
  ) -> List[Middleware]:
15
22
  if parse_stack is not None and append_middleware is not None:
16
23
  raise ValueError(
17
- "Provided both parse_stack and append_middleware."
18
- "Only one should be provided."
19
- "(append_middleware should only be used with the default parse_stack,"
24
+ "Provided both parse_stack and append_middleware. "
25
+ "Only one should be provided. "
26
+ "(append_middleware should only be used with the default parse_stack, "
20
27
  "i.e., when the passed parse_stack is None.)"
21
28
  )
22
29
 
@@ -27,7 +34,7 @@ def _build_parse_stack(
27
34
  return list(parse_stack)
28
35
 
29
36
  parse_stack_types = [type(m) for m in parse_stack]
30
- append_stack_types = set([type(m) for m in append_middleware])
37
+ append_stack_types = {type(m) for m in append_middleware}
31
38
  stack_types_intersect = set(parse_stack_types).intersection(append_stack_types)
32
39
  if len(stack_types_intersect) > 0:
33
40
  warnings.warn(
@@ -44,10 +51,10 @@ def _build_unparse_stack(
44
51
  ) -> List[Middleware]:
45
52
  if unparse_stack is not None and prepend_middleware is not None:
46
53
  raise ValueError(
47
- "Provided both parse_stack and append_middleware."
48
- "Only one should be provided."
49
- "(prepend_middleware should only be used with the default parse_stack,"
50
- "i.e., when the passed parse_stack is None.)"
54
+ "Provided both unparse_stack and prepend_middleware. "
55
+ "Only one should be provided. "
56
+ "(prepend_middleware should only be used with the default unparse_stack, "
57
+ "i.e., when the passed unparse_stack is None.)"
51
58
  )
52
59
 
53
60
  if unparse_stack is None:
@@ -57,7 +64,7 @@ def _build_unparse_stack(
57
64
  return list(unparse_stack)
58
65
 
59
66
  parse_stack_types = [type(m) for m in unparse_stack]
60
- append_stack_types = set([type(m) for m in prepend_middleware])
67
+ append_stack_types = {type(m) for m in prepend_middleware}
61
68
  stack_types_intersect = set(parse_stack_types).intersection(append_stack_types)
62
69
  if len(stack_types_intersect) > 0:
63
70
  warnings.warn(
@@ -68,6 +75,52 @@ def _build_unparse_stack(
68
75
  return list(prepend_middleware) + list(unparse_stack)
69
76
 
70
77
 
78
+ def _handle_deprecated_write_params(
79
+ unparse_stack: Optional[Iterable[Middleware]],
80
+ prepend_middleware: Optional[Iterable[Middleware]],
81
+ kwargs: dict,
82
+ function_name: str,
83
+ ) -> tuple[Optional[Iterable[Middleware]], Optional[Iterable[Middleware]]]:
84
+ """Handle deprecated parameter names for write functions.
85
+
86
+ :param unparse_stack: Current unparse_stack value
87
+ :param prepend_middleware: Current prepend_middleware value
88
+ :param kwargs: Dictionary of keyword arguments to check for deprecated params
89
+ :param function_name: Name of the calling function (for error messages)
90
+ :return: Tuple of (unparse_stack, prepend_middleware) with deprecated values migrated
91
+ """
92
+ if "parse_stack" in kwargs:
93
+ warnings.warn(
94
+ "Parameter 'parse_stack' is deprecated. Use 'unparse_stack' instead.",
95
+ DeprecationWarning,
96
+ stacklevel=3,
97
+ )
98
+ if unparse_stack is not None:
99
+ raise ValueError(
100
+ "Cannot provide both 'parse_stack' (deprecated) and 'unparse_stack'. "
101
+ "Use 'unparse_stack' instead."
102
+ )
103
+ unparse_stack = kwargs.pop("parse_stack")
104
+
105
+ if "append_middleware" in kwargs:
106
+ warnings.warn(
107
+ "Parameter 'append_middleware' is deprecated. Use 'prepend_middleware' instead.",
108
+ DeprecationWarning,
109
+ stacklevel=3,
110
+ )
111
+ if prepend_middleware is not None:
112
+ raise ValueError(
113
+ "Cannot provide both 'append_middleware' (deprecated) and 'prepend_middleware'. "
114
+ "Use 'prepend_middleware' instead."
115
+ )
116
+ prepend_middleware = kwargs.pop("append_middleware")
117
+
118
+ if kwargs:
119
+ raise TypeError(f"{function_name}() got unexpected keyword arguments: {', '.join(kwargs)}")
120
+
121
+ return unparse_stack, prepend_middleware
122
+
123
+
71
124
  def parse_string(
72
125
  bibtex_str: str,
73
126
  parse_stack: Optional[Iterable[Middleware]] = None,
@@ -119,7 +172,13 @@ def parse_file(
119
172
 
120
173
  :param encoding: Encoding of the .bib file. Default encoding is ``"UTF-8"``.
121
174
  :return: Library: Parsed BibTeX library
175
+ :raises LookupError: If the specified encoding is not recognized.
122
176
  """
177
+ try:
178
+ codecs.lookup(encoding)
179
+ except LookupError:
180
+ raise LookupError(f"Unknown encoding: {encoding!r}")
181
+
123
182
  with open(path, encoding=encoding) as f:
124
183
  bibtex_str = f.read()
125
184
  return parse_string(
@@ -130,27 +189,39 @@ def parse_file(
130
189
  def write_file(
131
190
  file: Union[str, TextIO],
132
191
  library: Library,
133
- parse_stack: Optional[Iterable[Middleware]] = None,
134
- append_middleware: Optional[Iterable[Middleware]] = None,
192
+ unparse_stack: Optional[Iterable[Middleware]] = None,
193
+ prepend_middleware: Optional[Iterable[Middleware]] = None,
135
194
  bibtex_format: Optional[BibtexFormat] = None,
195
+ encoding: str = "UTF-8",
196
+ **kwargs,
136
197
  ) -> None:
137
198
  """Write a BibTeX database to a file.
138
199
 
139
200
  :param file: File to write to. Can be a file name or a file object.
140
201
  :param library: BibTeX database to serialize.
141
- :param parse_stack: List of middleware to apply to the database before writing.
202
+ :param unparse_stack: List of middleware to apply to the database before writing.
142
203
  If None, a default stack will be used.
143
- :param append_middleware: List of middleware to append to the default stack.
144
- Only applicable if `parse_stack` is None.
145
- :param bibtex_format: Customized BibTeX format to use (optional)."""
204
+ :param prepend_middleware: List of middleware to prepend to the default stack.
205
+ Only applicable if `unparse_stack` is None.
206
+ :param bibtex_format: Customized BibTeX format to use (optional).
207
+ :param encoding: Encoding of the .bib file. Default encoding is ``"UTF-8"``.
208
+
209
+ .. deprecated:: (next version)
210
+ Parameters 'parse_stack' and 'append_middleware' are deprecated, will be deleted soon.
211
+ Use 'unparse_stack' and 'prepend_middleware' instead.
212
+ """
213
+ unparse_stack, prepend_middleware = _handle_deprecated_write_params(
214
+ unparse_stack, prepend_middleware, kwargs, "write_file"
215
+ )
216
+
146
217
  bibtex_str = write_string(
147
218
  library=library,
148
- unparse_stack=parse_stack,
149
- prepend_middleware=append_middleware,
219
+ unparse_stack=unparse_stack,
220
+ prepend_middleware=prepend_middleware,
150
221
  bibtex_format=bibtex_format,
151
222
  )
152
223
  if isinstance(file, str):
153
- with open(file, "w") as f:
224
+ with open(file, "w", encoding=encoding) as f:
154
225
  f.write(bibtex_str)
155
226
  else:
156
227
  file.write(bibtex_str)
@@ -161,6 +232,7 @@ def write_string(
161
232
  unparse_stack: Optional[Iterable[Middleware]] = None,
162
233
  prepend_middleware: Optional[Iterable[Middleware]] = None,
163
234
  bibtex_format: Optional["BibtexFormat"] = None,
235
+ **kwargs,
164
236
  ) -> str:
165
237
  """Serialize a BibTeX database to a string.
166
238
 
@@ -168,9 +240,17 @@ def write_string(
168
240
  :param unparse_stack: List of middleware to apply to the database before writing.
169
241
  If None, a default stack will be used.
170
242
  :param prepend_middleware: List of middleware to prepend to the default stack.
171
- Only applicable if `parse_stack` is None.
243
+ Only applicable if `unparse_stack` is None.
172
244
  :param bibtex_format: Customized BibTeX format to use (optional).
245
+
246
+ .. deprecated:: (next version)
247
+ Parameters 'parse_stack' and 'append_middleware' are deprecated.
248
+ Use 'unparse_stack' and 'prepend_middleware' instead.
173
249
  """
250
+ unparse_stack, prepend_middleware = _handle_deprecated_write_params(
251
+ unparse_stack, prepend_middleware, kwargs, "write_string"
252
+ )
253
+
174
254
  middleware: Middleware
175
255
  for middleware in _build_unparse_stack(unparse_stack, prepend_middleware):
176
256
  library = middleware.transform(library=library)
@@ -1,4 +1,5 @@
1
- from typing import List, Optional
1
+ from typing import List
2
+ from typing import Optional
2
3
 
3
4
 
4
5
  class ParsingException(Exception):
@@ -1,15 +1,15 @@
1
- from typing import Dict, List, Union
2
-
3
- from .model import (
4
- Block,
5
- DuplicateBlockKeyBlock,
6
- Entry,
7
- ExplicitComment,
8
- ImplicitComment,
9
- ParsingFailedBlock,
10
- Preamble,
11
- String,
12
- )
1
+ from typing import Dict
2
+ from typing import List
3
+ from typing import Union
4
+
5
+ from .model import Block
6
+ from .model import DuplicateBlockKeyBlock
7
+ from .model import Entry
8
+ from .model import ExplicitComment
9
+ from .model import ImplicitComment
10
+ from .model import ParsingFailedBlock
11
+ from .model import Preamble
12
+ from .model import String
13
13
 
14
14
  # TODO Use functools.lru_cache for library properties (which create lists when called)
15
15
 
@@ -24,9 +24,7 @@ class Library:
24
24
  if blocks is not None:
25
25
  self.add(blocks)
26
26
 
27
- def add(
28
- self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = False
29
- ):
27
+ def add(self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = False):
30
28
  """Add blocks to library.
31
29
 
32
30
  The adding is key-safe, i.e., it is made sure that no duplicate keys are added.
@@ -34,7 +32,8 @@ class Library:
34
32
  a DuplicateKeyBlock.
35
33
 
36
34
  :param blocks: Block or list of blocks to add.
37
- :param fail_on_duplicate_key: If True, raises ValueError if a block was replaced with a DuplicateKeyBlock.
35
+ :param fail_on_duplicate_key:
36
+ If True, raises ValueError if a block was replaced with a DuplicateKeyBlock.
38
37
  """
39
38
  if isinstance(blocks, Block):
40
39
  blocks = [blocks]
@@ -49,7 +48,7 @@ class Library:
49
48
  if fail_on_duplicate_key:
50
49
  duplicate_keys = []
51
50
  for original, added in zip(blocks, _added_blocks):
52
- if not original is added and isinstance(added, DuplicateBlockKeyBlock):
51
+ if original is not added and isinstance(added, DuplicateBlockKeyBlock):
53
52
  duplicate_keys.append(added.key)
54
53
 
55
54
  if len(duplicate_keys) > 0:
@@ -74,9 +73,7 @@ class Library:
74
73
  elif isinstance(block, String):
75
74
  del self._strings_by_key[block.key]
76
75
 
77
- def replace(
78
- self, old_block: Block, new_block: Block, fail_on_duplicate_key: bool = True
79
- ):
76
+ def replace(self, old_block: Block, new_block: Block, fail_on_duplicate_key: bool = True):
80
77
  """Replace a block with another block, at the same position.
81
78
 
82
79
  :param old_block: Block to replace.
@@ -110,18 +107,22 @@ class Library:
110
107
  def _cast_to_duplicate(
111
108
  prev_block_with_same_key: Union[Entry, String], duplicate: Union[Entry, String]
112
109
  ):
113
- assert isinstance(prev_block_with_same_key, type(duplicate)) or isinstance(
114
- duplicate, type(prev_block_with_same_key)
115
- ), (
116
- "Internal BibtexParser Error. Duplicate blocks share no common type."
117
- f"Found {type(prev_block_with_same_key)} and {type(duplicate)}, but both should be"
118
- f"either instance of String or instance of Entry."
119
- f"Please report this issue at the bibtexparser issue tracker.",
120
- )
121
-
122
- assert (
123
- prev_block_with_same_key.key == duplicate.key
124
- ), "Internal BibtexParser Error. Duplicate blocks have different keys."
110
+ if not (
111
+ isinstance(prev_block_with_same_key, type(duplicate))
112
+ or isinstance(duplicate, type(prev_block_with_same_key))
113
+ ):
114
+ raise ValueError(
115
+ "Internal BibtexParser Error. Duplicate blocks share no common type. "
116
+ f"Found {type(prev_block_with_same_key)} and {type(duplicate)}, but both should be "
117
+ "either instance of String or instance of Entry. "
118
+ "Please report this issue at the bibtexparser issue tracker."
119
+ )
120
+
121
+ if prev_block_with_same_key.key != duplicate.key:
122
+ raise ValueError(
123
+ "Internal BibtexParser Error. Duplicate blocks have different keys. "
124
+ "Please report this issue at the bibtexparser issue tracker."
125
+ )
125
126
 
126
127
  return DuplicateBlockKeyBlock(
127
128
  start_line=duplicate.start_line,
@@ -196,7 +197,5 @@ class Library:
196
197
  def comments(self) -> List[Union[ExplicitComment, ImplicitComment]]:
197
198
  """All comment blocks in the library, preserving order of insertion."""
198
199
  return [
199
- block
200
- for block in self._blocks
201
- if isinstance(block, (ExplicitComment, ImplicitComment))
200
+ block for block in self._blocks if isinstance(block, (ExplicitComment, ImplicitComment))
202
201
  ]
@@ -0,0 +1,22 @@
1
+ from bibtexparser.middlewares.enclosing import AddEnclosingMiddleware
2
+ from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware
3
+ from bibtexparser.middlewares.fieldkeys import NormalizeFieldKeys
4
+ from bibtexparser.middlewares.interpolate import ResolveStringReferencesMiddleware
5
+ from bibtexparser.middlewares.latex_encoding import LatexDecodingMiddleware
6
+ from bibtexparser.middlewares.latex_encoding import LatexEncodingMiddleware
7
+ from bibtexparser.middlewares.middleware import BlockMiddleware
8
+ from bibtexparser.middlewares.middleware import LibraryMiddleware
9
+ from bibtexparser.middlewares.month import MonthAbbreviationMiddleware
10
+ from bibtexparser.middlewares.month import MonthIntMiddleware
11
+ from bibtexparser.middlewares.month import MonthLongStringMiddleware
12
+ from bibtexparser.middlewares.names import MergeCoAuthors
13
+ from bibtexparser.middlewares.names import MergeNameParts
14
+ from bibtexparser.middlewares.names import NameParts
15
+ from bibtexparser.middlewares.names import SeparateCoAuthors
16
+ from bibtexparser.middlewares.names import SplitNameParts
17
+ from bibtexparser.middlewares.sorting_blocks import SortBlocksByTypeAndKeyMiddleware
18
+ from bibtexparser.middlewares.sorting_entry_fields import SortFieldsAlphabeticallyMiddleware
19
+ from bibtexparser.middlewares.sorting_entry_fields import SortFieldsCustomMiddleware
20
+
21
+ from .parsestack import default_parse_stack
22
+ from .parsestack import default_unparse_stack
@@ -1,6 +1,10 @@
1
- from typing import Tuple, Union
1
+ from typing import Tuple
2
+ from typing import Union
2
3
 
3
- from bibtexparser.model import Entry, Field, String
4
+ from bibtexparser.library import Library
5
+ from bibtexparser.model import Entry
6
+ from bibtexparser.model import Field
7
+ from bibtexparser.model import String
4
8
 
5
9
  from .middleware import BlockMiddleware
6
10
 
@@ -51,7 +55,7 @@ class RemoveEnclosingMiddleware(BlockMiddleware):
51
55
  return value, "no-enclosing"
52
56
 
53
57
  # docstr-coverage: inherited
54
- def transform_entry(self, entry: Entry, library: "Library") -> Entry:
58
+ def transform_entry(self, entry: Entry, library: Library) -> Entry:
55
59
  field: Field
56
60
  metadata = dict()
57
61
  for field in entry.fields:
@@ -62,7 +66,7 @@ class RemoveEnclosingMiddleware(BlockMiddleware):
62
66
  return entry
63
67
 
64
68
  # docstr-coverage: inherited
65
- def transform_string(self, string: String, library: "Library") -> String:
69
+ def transform_string(self, string: String, library: Library) -> String:
66
70
  stripped, enclosing = self._strip_enclosing(string.value)
67
71
  string.value = stripped
68
72
  string.parser_metadata[self.metadata_key()] = enclosing
@@ -101,8 +105,7 @@ class AddEnclosingMiddleware(BlockMiddleware):
101
105
 
102
106
  if default_enclosing not in ("{", '"'):
103
107
  raise ValueError(
104
- "default_enclosing must be either '{' or '\"'"
105
- f"not '{default_enclosing}'"
108
+ "default_enclosing must be either '{' or '\"'" f"not '{default_enclosing}'"
106
109
  )
107
110
  self._default_enclosing = default_enclosing
108
111
  self._reuse_previous_enclosing = reuse_previous_enclosing
@@ -113,9 +116,7 @@ class AddEnclosingMiddleware(BlockMiddleware):
113
116
  def metadata_key(cls) -> str:
114
117
  return "remove_enclosing"
115
118
 
116
- def _enclose(
117
- self, value: str, metadata_enclosing: str, apply_int_rule: bool
118
- ) -> str:
119
+ def _enclose(self, value: str, metadata_enclosing: str, apply_int_rule: bool) -> str:
119
120
  enclosing = self._default_enclosing
120
121
  if self._reuse_previous_enclosing and metadata_enclosing is not None:
121
122
  enclosing = metadata_enclosing
@@ -129,8 +130,7 @@ class AddEnclosingMiddleware(BlockMiddleware):
129
130
  if enclosing == "no-enclosing":
130
131
  return value
131
132
  raise ValueError(
132
- f"enclosing must be either '{{' or '\"' or 'no-enclosing', "
133
- f"not '{enclosing}'"
133
+ f"enclosing must be either '{{' or '\"' or 'no-enclosing', " f"not '{enclosing}'"
134
134
  )
135
135
 
136
136
  # docstr-coverage: inherited
@@ -142,13 +142,9 @@ class AddEnclosingMiddleware(BlockMiddleware):
142
142
  for field in entry.fields:
143
143
  apply_int_rule = field.key in ENTRY_POTENTIALLY_INT_FIELDS
144
144
  prev_encoding = (
145
- metadata_enclosing.get(field.key, None)
146
- if metadata_enclosing is not None
147
- else None
148
- )
149
- field.value = self._enclose(
150
- field.value, prev_encoding, apply_int_rule=apply_int_rule
145
+ metadata_enclosing.get(field.key, None) if metadata_enclosing is not None else None
151
146
  )
147
+ field.value = self._enclose(field.value, prev_encoding, apply_int_rule=apply_int_rule)
152
148
  return entry
153
149
 
154
150
  # docstr-coverage: inherited