bibtexparser 2.0.0b7__tar.gz → 2.0.0b8__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 (34) hide show
  1. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/LICENSE +1 -1
  2. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/PKG-INFO +10 -14
  3. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/README.md +9 -9
  4. bibtexparser-2.0.0b8/bibtexparser/__init__.py +11 -0
  5. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/entrypoint.py +11 -5
  6. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/exceptions.py +2 -1
  7. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/library.py +18 -23
  8. bibtexparser-2.0.0b8/bibtexparser/middlewares/__init__.py +22 -0
  9. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/enclosing.py +13 -17
  10. bibtexparser-2.0.0b8/bibtexparser/middlewares/fieldkeys.py +54 -0
  11. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/interpolate.py +3 -5
  12. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/latex_encoding.py +20 -22
  13. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/middleware.py +12 -13
  14. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/month.py +6 -5
  15. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/names.py +23 -15
  16. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/parsestack.py +4 -10
  17. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/sorting_blocks.py +14 -18
  18. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/middlewares/sorting_entry_fields.py +3 -2
  19. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/model.py +11 -21
  20. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/splitter.py +33 -42
  21. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser/writer.py +13 -19
  22. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser.egg-info/PKG-INFO +10 -14
  23. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser.egg-info/SOURCES.txt +1 -0
  24. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser.egg-info/requires.txt +0 -5
  25. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/setup.py +0 -5
  26. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/tests/test_entrypoint.py +1 -3
  27. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/tests/test_library.py +5 -12
  28. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/tests/test_model.py +11 -18
  29. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/tests/test_writer.py +11 -13
  30. bibtexparser-2.0.0b7/bibtexparser/__init__.py +0 -8
  31. bibtexparser-2.0.0b7/bibtexparser/middlewares/__init__.py +0 -29
  32. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser.egg-info/dependency_links.txt +0 -0
  33. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/bibtexparser.egg-info/top_level.txt +0 -0
  34. {bibtexparser-2.0.0b7 → bibtexparser-2.0.0b8}/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
1
  Metadata-Version: 2.1
2
2
  Name: bibtexparser
3
- Version: 2.0.0b7
3
+ Version: 2.0.0b8
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
@@ -21,10 +21,6 @@ Requires-Dist: pytest; extra == "test"
21
21
  Requires-Dist: pytest-xdist; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
23
23
  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
24
  Provides-Extra: docs
29
25
  Requires-Dist: sphinx; extra == "docs"
30
26
 
@@ -49,11 +45,11 @@ If instead, you want to use v1, install it using:
49
45
  pip install bibtexparser~=1.0
50
46
  ```
51
47
 
52
- Note that all development and maintenance effort is focussed on v2.
48
+ Note that all development and maintenance effort is focussed on v2.
53
49
  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).
50
+ Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1).
55
51
 
56
- The remainder of this README is specific to v2.
52
+ The remainder of this README is specific to v2.
57
53
 
58
54
  ## Documentation
59
55
  Go check out our documentation on [https://bibtexparser.readthedocs.io/en/main/](https://bibtexparser.readthedocs.io/en/main/).
@@ -96,7 +92,7 @@ new_bibtex_string = bibtexparser.write_string(bib_database,
96
92
  )
97
93
  ```
98
94
 
99
- These examples really only show the bare minimum.
95
+ These examples really only show the bare minimum.
100
96
  Consult the documentation for a list of available middleware, parsing options and write-formatting options.
101
97
 
102
98
  ## V2 Architecture and Terminology
@@ -106,21 +102,21 @@ Consult the documentation for a list of available middleware, parsing options an
106
102
  The architecture consists of the following components:
107
103
 
108
104
  #### Library
109
- Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preamples and their metadata (e.g. order).
105
+ Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preamples and their metadata (e.g. order).
110
106
 
111
107
  #### A Splitter
112
108
  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".
109
+ 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
110
 
115
111
  #### 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.
112
+ 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.
117
113
 
118
114
  #### 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.
115
+ Writes the content of a bibtex library to a ``.bib`` file. Optional formatting parameters can be passed using a corresponding dedicated data structure.
120
116
 
121
117
  ## About
122
118
 
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.
119
+ 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
120
 
125
121
  Credits and thanks to the many contributors who helped creating this library, including
126
122
  François Boulogne ([@sciunto](https://github.com/sciunto/), creator of the first version) and Olivier Mangin ([@omangin](https://github.com/omangin/), long-term contributor).
@@ -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/).
@@ -66,7 +66,7 @@ new_bibtex_string = bibtexparser.write_string(bib_database,
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, preamples 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, 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.
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.0b8"
@@ -1,11 +1,17 @@
1
1
  import warnings
2
- from typing import Iterable, List, Optional, TextIO, Union
2
+ from typing import Iterable
3
+ from typing import List
4
+ from typing import Optional
5
+ from typing import TextIO
6
+ from typing import Union
3
7
 
4
8
  from .library import Library
5
9
  from .middlewares.middleware import Middleware
6
- from .middlewares.parsestack import default_parse_stack, default_unparse_stack
10
+ from .middlewares.parsestack import default_parse_stack
11
+ from .middlewares.parsestack import default_unparse_stack
7
12
  from .splitter import Splitter
8
- from .writer import BibtexFormat, write
13
+ from .writer import BibtexFormat
14
+ from .writer import write
9
15
 
10
16
 
11
17
  def _build_parse_stack(
@@ -27,7 +33,7 @@ def _build_parse_stack(
27
33
  return list(parse_stack)
28
34
 
29
35
  parse_stack_types = [type(m) for m in parse_stack]
30
- append_stack_types = set([type(m) for m in append_middleware])
36
+ append_stack_types = {type(m) for m in append_middleware}
31
37
  stack_types_intersect = set(parse_stack_types).intersection(append_stack_types)
32
38
  if len(stack_types_intersect) > 0:
33
39
  warnings.warn(
@@ -57,7 +63,7 @@ def _build_unparse_stack(
57
63
  return list(unparse_stack)
58
64
 
59
65
  parse_stack_types = [type(m) for m in unparse_stack]
60
- append_stack_types = set([type(m) for m in prepend_middleware])
66
+ append_stack_types = {type(m) for m in prepend_middleware}
61
67
  stack_types_intersect = set(parse_stack_types).intersection(append_stack_types)
62
68
  if len(stack_types_intersect) > 0:
63
69
  warnings.warn(
@@ -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.
@@ -196,7 +193,5 @@ class Library:
196
193
  def comments(self) -> List[Union[ExplicitComment, ImplicitComment]]:
197
194
  """All comment blocks in the library, preserving order of insertion."""
198
195
  return [
199
- block
200
- for block in self._blocks
201
- if isinstance(block, (ExplicitComment, ImplicitComment))
196
+ block for block in self._blocks if isinstance(block, (ExplicitComment, ImplicitComment))
202
197
  ]
@@ -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
@@ -0,0 +1,54 @@
1
+ import logging
2
+ from typing import Dict
3
+ from typing import List
4
+ from typing import Set
5
+
6
+ from bibtexparser.library import Library
7
+ from bibtexparser.model import Entry
8
+ from bibtexparser.model import Field
9
+
10
+ from .middleware import BlockMiddleware
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class NormalizeFieldKeys(BlockMiddleware):
16
+ """Normalize field keys to lowercase.
17
+
18
+ In case of conflicts (e.g. both 'author' and 'Author' exist in the same entry),
19
+ a warning is emitted, and the last value wins.
20
+
21
+ Some other middlewares, such as `SeparateCoAuthors`, assume lowercase key names.
22
+ """
23
+
24
+ def __init__(self, allow_inplace_modification: bool = True):
25
+ super().__init__(
26
+ allow_inplace_modification=allow_inplace_modification,
27
+ allow_parallel_execution=True,
28
+ )
29
+
30
+ # docstr-coverage: inherited
31
+ def transform_entry(self, entry: Entry, library: "Library") -> Entry:
32
+ seen_normalized_keys: Set[str] = set()
33
+ new_fields_dict: Dict[str, Field] = {}
34
+ for field in entry.fields:
35
+ normalized_key: str = field.key.lower()
36
+ # if the normalized key is already present, apply "last one wins"
37
+ # otherwise preserve insertion order
38
+ # if a key is overwritten, emit a detailed warning
39
+ # if performance is a concern, we could emit a warning with only {entry.key}
40
+ # to remove "seen_normalized_keys" and this if statement
41
+ if normalized_key in seen_normalized_keys:
42
+ logger.warning(
43
+ f"NormalizeFieldKeys: in entry '{entry.key}': "
44
+ + f"duplicate normalized key '{normalized_key}' "
45
+ + f"(original '{field.key}'); overriding previous value"
46
+ )
47
+ seen_normalized_keys.add(normalized_key)
48
+ field.key = normalized_key
49
+ new_fields_dict[normalized_key] = field
50
+
51
+ new_fields: List[Field] = list(new_fields_dict.values())
52
+ entry.fields = new_fields
53
+
54
+ return entry
@@ -3,7 +3,8 @@ from copy import deepcopy
3
3
  from typing import Any
4
4
 
5
5
  from bibtexparser.library import Library
6
- from bibtexparser.model import Entry, Field
6
+ from bibtexparser.model import Entry
7
+ from bibtexparser.model import Field
7
8
 
8
9
  from .enclosing import REMOVED_ENCLOSING_KEY
9
10
  from .middleware import LibraryMiddleware
@@ -41,10 +42,7 @@ class ResolveStringReferencesMiddleware(LibraryMiddleware):
41
42
  raised_enclosing_warning = False
42
43
  for entry in library.entries:
43
44
  resolved_fields = list()
44
- if (
45
- not raised_enclosing_warning
46
- and REMOVED_ENCLOSING_KEY in entry.parser_metadata
47
- ):
45
+ if not raised_enclosing_warning and REMOVED_ENCLOSING_KEY in entry.parser_metadata:
48
46
  raised_enclosing_warning = True
49
47
  warnings.warn(
50
48
  (
@@ -1,23 +1,29 @@
1
1
  import abc
2
2
  import logging
3
3
  import re
4
- from typing import List, Optional, Tuple
4
+ from typing import List
5
+ from typing import Optional
6
+ from typing import Tuple
5
7
 
6
8
  import pylatexenc
7
- from pylatexenc.latex2text import LatexNodes2Text, MacroTextSpec
8
- from pylatexenc.latexencode import (
9
- RULE_REGEX,
10
- UnicodeToLatexConversionRule,
11
- UnicodeToLatexEncoder,
12
- )
9
+ from pylatexenc.latex2text import LatexNodes2Text
10
+ from pylatexenc.latex2text import MacroTextSpec
11
+ from pylatexenc.latexencode import RULE_REGEX
12
+ from pylatexenc.latexencode import UnicodeToLatexConversionRule
13
+ from pylatexenc.latexencode import UnicodeToLatexEncoder
13
14
 
14
15
  from bibtexparser.exceptions import PartialMiddlewareException
15
16
  from bibtexparser.library import Library
16
- from bibtexparser.model import Block, Entry, MiddlewareErrorBlock, String
17
+ from bibtexparser.model import Block
18
+ from bibtexparser.model import Entry
19
+ from bibtexparser.model import MiddlewareErrorBlock
20
+ from bibtexparser.model import String
17
21
 
18
22
  from .middleware import BlockMiddleware
19
23
  from .names import NameParts
20
24
 
25
+ logger = logging.getLogger(__name__)
26
+
21
27
 
22
28
  class _PyStringTransformerMiddleware(BlockMiddleware, abc.ABC):
23
29
  """Abstract utility class allowing to modify python-strings"""
@@ -33,9 +39,7 @@ class _PyStringTransformerMiddleware(BlockMiddleware, abc.ABC):
33
39
  raise NotImplementedError("called abstract method")
34
40
 
35
41
  # docstr-coverage: inherited
36
- def _transform_all_strings(
37
- self, list_of_strings: List[str], errors: List[str]
38
- ) -> List[str]:
42
+ def _transform_all_strings(self, list_of_strings: List[str], errors: List[str]) -> List[str]:
39
43
  """Called for every python (value, not key) string found on Entry and String blocks"""
40
44
  res = []
41
45
  for s in list_of_strings:
@@ -52,14 +56,12 @@ class _PyStringTransformerMiddleware(BlockMiddleware, abc.ABC):
52
56
  field.value, e = self._transform_python_value_string(field.value)
53
57
  errors.append(e)
54
58
  elif isinstance(field.value, NameParts):
55
- field.value.first = self._transform_all_strings(
56
- field.value.first, errors
57
- )
59
+ field.value.first = self._transform_all_strings(field.value.first, errors)
58
60
  field.value.last = self._transform_all_strings(field.value.last, errors)
59
61
  field.value.von = self._transform_all_strings(field.value.von, errors)
60
62
  field.value.jr = self._transform_all_strings(field.value.jr, errors)
61
63
  else:
62
- logging.info(
64
+ logger.info(
63
65
  f" [{self.metadata_key()}] Cannot python-str transform field {field.key}"
64
66
  f" with value type {type(field.value)}"
65
67
  )
@@ -76,7 +78,7 @@ class _PyStringTransformerMiddleware(BlockMiddleware, abc.ABC):
76
78
  if isinstance(string.value, str):
77
79
  string.value = self._transform_python_value_string(string.value)
78
80
  else:
79
- logging.info(
81
+ logger.info(
80
82
  f" [{self.metadata_key()}] Cannot python-str transform string {string.key}"
81
83
  f" with value type {type(string.value)}"
82
84
  )
@@ -162,9 +164,7 @@ class LatexDecodingMiddleware(_PyStringTransformerMiddleware):
162
164
  allow_parallel_execution=True,
163
165
  )
164
166
 
165
- if decoder is not None and (
166
- keep_braced_groups is not None or keep_math_mode is not None
167
- ):
167
+ if decoder is not None and (keep_braced_groups is not None or keep_math_mode is not None):
168
168
  raise ValueError(
169
169
  "Cannot specify both encoder and one of "
170
170
  "`keep_braced_groups` or `keep_braced_groups`."
@@ -174,9 +174,7 @@ class LatexDecodingMiddleware(_PyStringTransformerMiddleware):
174
174
 
175
175
  # Defaults (not specified as defaults in args,
176
176
  # to make sure we can identify if they were specified)
177
- keep_braced_groups = (
178
- keep_braced_groups if keep_braced_groups is not None else False
179
- )
177
+ keep_braced_groups = keep_braced_groups if keep_braced_groups is not None else False
180
178
  keep_math_mode = keep_math_mode if keep_math_mode is not None else True
181
179
 
182
180
  if decoder is None:
@@ -1,17 +1,18 @@
1
1
  import abc
2
2
  import logging
3
3
  from copy import deepcopy
4
- from typing import Collection, Union
4
+ from typing import Collection
5
+ from typing import Union
5
6
 
6
7
  from bibtexparser.library import Library
7
- from bibtexparser.model import (
8
- Block,
9
- Entry,
10
- ExplicitComment,
11
- ImplicitComment,
12
- Preamble,
13
- String,
14
- )
8
+ from bibtexparser.model import Block
9
+ from bibtexparser.model import Entry
10
+ from bibtexparser.model import ExplicitComment
11
+ from bibtexparser.model import ImplicitComment
12
+ from bibtexparser.model import Preamble
13
+ from bibtexparser.model import String
14
+
15
+ logger = logging.getLogger(__name__)
15
16
 
16
17
 
17
18
  class Middleware(abc.ABC):
@@ -94,9 +95,7 @@ class BlockMiddleware(Middleware, abc.ABC):
94
95
  blocks.extend(transformed)
95
96
  # Case 4: Something else. Error.
96
97
  else:
97
- raise TypeError(
98
- f"Illegal output type from transform_block: {type(transformed)}"
99
- )
98
+ raise TypeError(f"Illegal output type from transform_block: {type(transformed)}")
100
99
  return Library(blocks=blocks)
101
100
 
102
101
  def transform_block(
@@ -131,7 +130,7 @@ class BlockMiddleware(Middleware, abc.ABC):
131
130
  elif isinstance(block, ImplicitComment):
132
131
  return self.transform_implicit_comment(block, library)
133
132
 
134
- logging.warning(f"Unknown block type {type(block)}")
133
+ logger.warning(f"Unknown block type {type(block)}")
135
134
  return block
136
135
 
137
136
  def transform_entry(
@@ -1,9 +1,12 @@
1
1
  import abc
2
2
  from collections import OrderedDict
3
- from typing import Tuple, Union
3
+ from typing import Tuple
4
+ from typing import Union
4
5
 
5
6
  from bibtexparser.library import Library
6
- from bibtexparser.model import Block, Entry, Field
7
+ from bibtexparser.model import Block
8
+ from bibtexparser.model import Entry
9
+ from bibtexparser.model import Field
7
10
 
8
11
  from .middleware import BlockMiddleware
9
12
 
@@ -31,9 +34,7 @@ class _MonthInterpolator(BlockMiddleware, abc.ABC):
31
34
  return entry
32
35
 
33
36
  @abc.abstractmethod
34
- def resolve_month_field_val(
35
- self, month_field: Field
36
- ) -> Tuple[Union[str, int], str]:
37
+ def resolve_month_field_val(self, month_field: Field) -> Tuple[Union[str, int], str]:
37
38
  """Transform the month field.
38
39
 
39
40
  Args: