bibtexparser 2.0.0b4__tar.gz → 2.0.0b5__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 (31) hide show
  1. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/PKG-INFO +2 -2
  2. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/README.md +1 -1
  3. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/__init__.py +2 -0
  4. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/library.py +25 -5
  5. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/names.py +10 -4
  6. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser.egg-info/PKG-INFO +2 -2
  7. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser.egg-info/SOURCES.txt +1 -0
  8. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/setup.py +3 -1
  9. bibtexparser-2.0.0b5/tests/test_library.py +75 -0
  10. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/LICENSE +0 -0
  11. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/entrypoint.py +0 -0
  12. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/exceptions.py +0 -0
  13. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/__init__.py +0 -0
  14. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/enclosing.py +0 -0
  15. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/interpolate.py +0 -0
  16. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/latex_encoding.py +0 -0
  17. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/middleware.py +0 -0
  18. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/month.py +0 -0
  19. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/parsestack.py +0 -0
  20. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/sorting_blocks.py +0 -0
  21. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/middlewares/sorting_entry_fields.py +0 -0
  22. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/model.py +0 -0
  23. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/splitter.py +0 -0
  24. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser/writer.py +0 -0
  25. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser.egg-info/dependency_links.txt +0 -0
  26. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser.egg-info/requires.txt +0 -0
  27. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/bibtexparser.egg-info/top_level.txt +0 -0
  28. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/setup.cfg +0 -0
  29. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/tests/test_entrypoint.py +0 -0
  30. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/tests/test_model.py +0 -0
  31. {bibtexparser-2.0.0b4 → bibtexparser-2.0.0b5}/tests/test_writer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bibtexparser
3
- Version: 2.0.0b4
3
+ Version: 2.0.0b5
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
@@ -35,7 +35,7 @@ Welcome to python-bibtexparser, a parser for `.bib` files with a long history an
35
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:
36
36
 
37
37
  ```bash
38
- pip install python-bibtexparser --pre
38
+ pip install bibtexparser --pre
39
39
  ```
40
40
 
41
41
  Or you can install the latest development version directly from the main branch:
@@ -5,7 +5,7 @@ Welcome to python-bibtexparser, a parser for `.bib` files with a long history an
5
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:
6
6
 
7
7
  ```bash
8
- pip install python-bibtexparser --pre
8
+ pip install bibtexparser --pre
9
9
  ```
10
10
 
11
11
  Or you can install the latest development version directly from the main branch:
@@ -4,3 +4,5 @@ import bibtexparser.model
4
4
  from bibtexparser.entrypoint import parse_file, parse_string, write_file, write_string
5
5
  from bibtexparser.library import Library
6
6
  from bibtexparser.writer import BibtexFormat
7
+
8
+ __version__ = "2.0.0b5"
@@ -24,7 +24,9 @@ class Library:
24
24
  if blocks is not None:
25
25
  self.add(blocks)
26
26
 
27
- def add(self, blocks: Union[List[Block], Block]):
27
+ def add(
28
+ self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = False
29
+ ):
28
30
  """Add blocks to library.
29
31
 
30
32
  The adding is key-safe, i.e., it is made sure that no duplicate keys are added.
@@ -32,14 +34,30 @@ class Library:
32
34
  a DuplicateKeyBlock.
33
35
 
34
36
  :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
38
  """
36
39
  if isinstance(blocks, Block):
37
40
  blocks = [blocks]
38
41
 
42
+ _added_blocks = []
39
43
  for block in blocks:
40
44
  # This may replace block with a DuplicateEntryKeyBlock
41
45
  block = self._add_to_dicts(block)
42
46
  self._blocks.append(block)
47
+ _added_blocks.append(block)
48
+
49
+ if fail_on_duplicate_key:
50
+ duplicate_keys = []
51
+ for original, added in zip(blocks, _added_blocks):
52
+ if not original is added and isinstance(added, DuplicateBlockKeyBlock):
53
+ duplicate_keys.append(added.key)
54
+
55
+ if len(duplicate_keys) > 0:
56
+ raise ValueError(
57
+ f"Duplicate keys found: {duplicate_keys}. "
58
+ f"Duplicate entries have been added to the library as DuplicateBlockKeyBlock."
59
+ f"Use `library.failed_blocks` to access them. "
60
+ )
43
61
 
44
62
  def remove(self, blocks: Union[List[Block], Block]):
45
63
  """Remove blocks from library.
@@ -73,19 +91,19 @@ class Library:
73
91
  except ValueError:
74
92
  raise ValueError("Block to replace is not in library.")
75
93
 
76
- self._blocks.insert(index, new_block)
77
94
  block_after_add = self._add_to_dicts(new_block)
95
+ self._blocks.insert(index, block_after_add)
78
96
 
79
97
  if (
80
98
  new_block is not block_after_add
81
- and isinstance(new_block, DuplicateBlockKeyBlock)
99
+ and isinstance(block_after_add, DuplicateBlockKeyBlock)
82
100
  and fail_on_duplicate_key
83
101
  ):
84
102
  # Revert changes to old_block
85
103
  # Don't fail on duplicate key, as this would lead to an infinite recursion
86
104
  # (should never happen for a clean library, but could happen if the user
87
105
  # tampered with the internals of the library).
88
- self.replace(new_block, old_block, fail_on_duplicate_key=False)
106
+ self.replace(block_after_add, old_block, fail_on_duplicate_key=False)
89
107
  raise ValueError("Duplicate key found.")
90
108
 
91
109
  @staticmethod
@@ -160,7 +178,9 @@ class Library:
160
178
  @property
161
179
  def entries(self) -> List[Entry]:
162
180
  """All entry (@article, ...) blocks in the library, preserving order of insertion."""
163
- return list(self._entries_by_key.values())
181
+ # Note: Taking this from the entries dict would be faster, but does not preserve order
182
+ # e.g. in cases where `replace` has been called.
183
+ return [b for b in self._blocks if isinstance(b, Entry)]
164
184
 
165
185
  @property
166
186
  def entries_dict(self) -> Dict[str, Entry]:
@@ -424,11 +424,17 @@ def parse_single_name_into_parts(name, strict=True):
424
424
  else:
425
425
  lcases = cases[0]
426
426
 
427
- #
427
+ def rindex(l, x, default):
428
+ """Returns the index of the rightmost occurence of x in l."""
429
+ for i in range(len(l) - 1, -1, -1):
430
+ if l[i] == x:
431
+ return i
432
+ return default
433
+
434
+ # Check if at least one of the words is lowercase
428
435
  if 0 in lcases:
429
- split = len(lcases) - lcases[::-1].index(0)
430
- if split == len(lcases):
431
- split = 0 # Last cannot be empty.
436
+ # Excluding the last word, find the index of the last lower word
437
+ split = rindex(lcases[:-1], 0, -1) + 1
432
438
  parts.von = sections[0][:split]
433
439
  parts.last = sections[0][split:]
434
440
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bibtexparser
3
- Version: 2.0.0b4
3
+ Version: 2.0.0b5
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
@@ -35,7 +35,7 @@ Welcome to python-bibtexparser, a parser for `.bib` files with a long history an
35
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:
36
36
 
37
37
  ```bash
38
- pip install python-bibtexparser --pre
38
+ pip install bibtexparser --pre
39
39
  ```
40
40
 
41
41
  Or you can install the latest development version directly from the main branch:
@@ -24,5 +24,6 @@ bibtexparser/middlewares/parsestack.py
24
24
  bibtexparser/middlewares/sorting_blocks.py
25
25
  bibtexparser/middlewares/sorting_entry_fields.py
26
26
  tests/test_entrypoint.py
27
+ tests/test_library.py
27
28
  tests/test_model.py
28
29
  tests/test_writer.py
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env python
2
2
  import setuptools
3
3
 
4
- version = "2.0.0b4"
4
+ version = None
5
5
 
6
6
  with open("bibtexparser/__init__.py") as fh:
7
7
  for line in fh:
8
8
  if line.startswith("__version__"):
9
9
  version = line.strip().split()[-1][1:-1]
10
10
  break
11
+ if not version:
12
+ raise RuntimeError("Could not determine version")
11
13
 
12
14
 
13
15
  def load_readme():
@@ -0,0 +1,75 @@
1
+ from copy import deepcopy
2
+
3
+ import pytest
4
+
5
+ from bibtexparser import Library
6
+ from bibtexparser.model import Entry, Field
7
+
8
+
9
+ def get_dummy_entry():
10
+ return Entry(
11
+ entry_type="article",
12
+ key="duplicateKey",
13
+ fields=[
14
+ Field(key="title", value="A title"),
15
+ Field(key="author", value="An author"),
16
+ ],
17
+ )
18
+
19
+
20
+ def test_replace_with_duplicates():
21
+ """Test that replace() works when there are duplicate values. See issue 404."""
22
+ library = Library()
23
+ library.add(get_dummy_entry())
24
+ library.add(get_dummy_entry())
25
+ # Test precondition
26
+ assert len(library.blocks) == 2
27
+ assert len(library.failed_blocks) == 1
28
+
29
+ replacement_entry = get_dummy_entry()
30
+ replacement_entry.fields_dict["title"].value = "A new title"
31
+
32
+ library.replace(
33
+ library.failed_blocks[0], replacement_entry, fail_on_duplicate_key=False
34
+ )
35
+ assert len(library.blocks) == 2
36
+ assert len(library.failed_blocks) == 1
37
+ assert library.failed_blocks[0].ignore_error_block["title"] == "A new title"
38
+
39
+ replacement_entry_2 = get_dummy_entry()
40
+ replacement_entry_2.fields_dict["title"].value = "Another new title"
41
+
42
+ library.replace(
43
+ library.entries[0], replacement_entry_2, fail_on_duplicate_key=False
44
+ )
45
+ assert len(library.blocks) == 2
46
+ assert len(library.failed_blocks) == 1
47
+ # The new block replaces the previous "non-duplicate" and should thus not become a duplicate itself
48
+ assert library.entries[0].fields_dict["title"].value == "Another new title"
49
+
50
+
51
+ def test_replace_fail_on_duplicate():
52
+ library = Library()
53
+ replaceable_entry = get_dummy_entry()
54
+ replaceable_entry.key = "Just a regular entry, to be replaced"
55
+ future_duplicate_entry = get_dummy_entry()
56
+ library.add([replaceable_entry, future_duplicate_entry])
57
+
58
+ with pytest.raises(ValueError):
59
+ library.replace(
60
+ replaceable_entry, get_dummy_entry(), fail_on_duplicate_key=True
61
+ )
62
+
63
+ assert len(library.blocks) == 2
64
+ assert len(library.failed_blocks) == 0
65
+ assert library.entries[0].key == "Just a regular entry, to be replaced"
66
+ assert library.entries[1].key == "duplicateKey"
67
+
68
+
69
+ def test_fail_on_duplicate_add():
70
+ library = Library()
71
+ library.add(get_dummy_entry())
72
+ with pytest.raises(ValueError):
73
+ library.add(get_dummy_entry(), fail_on_duplicate_key=True)
74
+ assert len(library.blocks) == 2
75
+ assert len(library.failed_blocks) == 1
File without changes
File without changes