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.
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/LICENSE +1 -1
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/PKG-INFO +34 -19
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/README.md +12 -12
- bibtexparser-2.0.0b9/bibtexparser/__init__.py +11 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/entrypoint.py +102 -22
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/exceptions.py +2 -1
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/library.py +34 -35
- bibtexparser-2.0.0b9/bibtexparser/middlewares/__init__.py +22 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/enclosing.py +13 -17
- bibtexparser-2.0.0b9/bibtexparser/middlewares/fieldkeys.py +54 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/interpolate.py +3 -5
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/latex_encoding.py +34 -26
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/middleware.py +12 -13
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/month.py +6 -5
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/names.py +33 -24
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/parsestack.py +4 -10
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/sorting_blocks.py +14 -18
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/middlewares/sorting_entry_fields.py +3 -2
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/model.py +31 -40
- bibtexparser-2.0.0b9/bibtexparser/py.typed +0 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/splitter.py +88 -54
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser/writer.py +13 -19
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/PKG-INFO +34 -19
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/SOURCES.txt +2 -0
- bibtexparser-2.0.0b9/bibtexparser.egg-info/requires.txt +10 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/setup.py +7 -6
- bibtexparser-2.0.0b9/tests/test_entrypoint.py +243 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/tests/test_library.py +5 -12
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/tests/test_model.py +11 -18
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/tests/test_writer.py +11 -13
- bibtexparser-2.0.0b7/bibtexparser/__init__.py +0 -8
- bibtexparser-2.0.0b7/bibtexparser/middlewares/__init__.py +0 -29
- bibtexparser-2.0.0b7/bibtexparser.egg-info/requires.txt +0 -15
- bibtexparser-2.0.0b7/tests/test_entrypoint.py +0 -13
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/dependency_links.txt +0 -0
- {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b9}/bibtexparser.egg-info/top_level.txt +0 -0
- {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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: bibtexparser
|
|
3
|
-
Version: 2.0.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 (
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 (
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
48
|
-
"Only one should be provided."
|
|
49
|
-
"(prepend_middleware should only be used with the default
|
|
50
|
-
"i.e., when the passed
|
|
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 =
|
|
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
|
-
|
|
134
|
-
|
|
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
|
|
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
|
|
144
|
-
Only applicable if `
|
|
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=
|
|
149
|
-
prepend_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 `
|
|
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,15 +1,15 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
1
|
+
from typing import Tuple
|
|
2
|
+
from typing import Union
|
|
2
3
|
|
|
3
|
-
from bibtexparser.
|
|
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:
|
|
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:
|
|
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
|