ebook2text 2.1.2__tar.gz → 2.2.0__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 (43) hide show
  1. {ebook2text-2.1.2 → ebook2text-2.2.0}/PKG-INFO +4 -4
  2. ebook2text-2.2.0/ebook2text/VERSION.py +1 -0
  3. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/chapter_check.py +10 -4
  4. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/convert_file.py +4 -5
  5. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/docx_conversion/__init__.py +1 -1
  6. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/docx_conversion/docx_converter.py +2 -2
  7. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/docx_conversion/docx_image_extractor.py +3 -5
  8. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/epub_conversion/__init__.py +1 -1
  9. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/epub_conversion/epub_converter.py +3 -3
  10. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/epub_conversion/epub_text_extractor.py +1 -5
  11. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/ocr.py +4 -2
  12. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/pdf_conversion/__init__.py +1 -1
  13. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/pdf_conversion/pdf_converter.py +3 -4
  14. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/pdf_conversion/pdf_image_extractor.py +8 -7
  15. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/pdf_conversion/pdf_text_extractor.py +14 -16
  16. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/text_parser.py +1 -1
  17. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text.egg-info/PKG-INFO +4 -4
  18. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text.egg-info/requires.txt +1 -1
  19. {ebook2text-2.1.2 → ebook2text-2.2.0}/pyproject.toml +19 -7
  20. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_pdf_conversion.py +36 -36
  21. ebook2text-2.1.2/ebook2text/VERSION.py +0 -1
  22. {ebook2text-2.1.2 → ebook2text-2.2.0}/README.md +0 -0
  23. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/__init__.py +0 -0
  24. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/_exceptions.py +0 -0
  25. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/_logger.py +0 -0
  26. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/_types.py +0 -0
  27. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/docx_conversion/_namespaces.py +0 -0
  28. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/docx_conversion/docx_text_extractor.py +0 -0
  29. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/pdf_conversion/_enums.py +0 -0
  30. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/pdf_conversion/pdf_line_logic.py +0 -0
  31. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text/text_utilities.py +0 -0
  32. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text.egg-info/SOURCES.txt +0 -0
  33. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text.egg-info/dependency_links.txt +0 -0
  34. {ebook2text-2.1.2 → ebook2text-2.2.0}/ebook2text.egg-info/top_level.txt +0 -0
  35. {ebook2text-2.1.2 → ebook2text-2.2.0}/setup.cfg +0 -0
  36. {ebook2text-2.1.2 → ebook2text-2.2.0}/setup.py +0 -0
  37. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_chapter_check.py +0 -0
  38. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_docx_conversion.py +0 -0
  39. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_epub_conversion.py +0 -0
  40. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_ocr.py +0 -0
  41. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_pdf_image_helpers.py +0 -0
  42. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_text_conversion.py +0 -0
  43. {ebook2text-2.1.2 → ebook2text-2.2.0}/tests/test_text_parser.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: ebook2text
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: Convert common book file types to text for machine learning
5
5
  Author: Ashlynn Antrobus
6
6
  Author-email: Ashlynn Antrobus <ashlynn@prosepal.io>
@@ -21,10 +21,10 @@ Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
23
  Classifier: Topic :: Text Processing
24
- Requires-Python: >=3.9
24
+ Requires-Python: >=3.10
25
25
  Description-Content-Type: text/markdown
26
26
  Requires-Dist: beautifulsoup4>=4.12.3
27
- Requires-Dist: ebooklib-autoupdate>=0.18.1
27
+ Requires-Dist: ebooklib-autoupdate>=0.18.3
28
28
  Requires-Dist: openai>=1.54.3
29
29
  Requires-Dist: pdfminer-six>=20240706
30
30
  Requires-Dist: pillow>=10.4.0
@@ -0,0 +1 @@
1
+ __version__ = "2.2.0"
@@ -48,9 +48,15 @@ NOT_CHAPTER = {
48
48
  def roman_to_int(roman_num: str) -> int:
49
49
  """
50
50
  Convert a Roman numeral to an integer.
51
+
51
52
  Arguments:
52
53
  roman_num (str): A string representing the Roman numeral.
53
- Returns int: The integer value of the Roman numeral.
54
+
55
+ Returns:
56
+ int: The integer value of the Roman numeral.
57
+
58
+ Raises:
59
+ ValueError if string is not a Roman numeral.
54
60
  """
55
61
  if not isinstance(roman_num, str):
56
62
  raise TypeError("Input must be a string")
@@ -61,7 +67,7 @@ def roman_to_int(roman_num: str) -> int:
61
67
  if roman.count(numeral) > 1:
62
68
  raise ValueError("Not roman numeral")
63
69
 
64
- ROMAN_NUMERALS = {
70
+ roman_numerals = {
65
71
  "I": 1,
66
72
  "V": 5,
67
73
  "X": 10,
@@ -77,7 +83,7 @@ def roman_to_int(roman_num: str) -> int:
77
83
  previous_char: str = ""
78
84
 
79
85
  for char in reversed(roman):
80
- if char not in ROMAN_NUMERALS:
86
+ if char not in roman_numerals:
81
87
  raise ValueError("Not roman numeral")
82
88
 
83
89
  consecutive_count = (
@@ -86,7 +92,7 @@ def roman_to_int(roman_num: str) -> int:
86
92
  if consecutive_count > 3:
87
93
  raise ValueError("Not roman numeral")
88
94
 
89
- value = ROMAN_NUMERALS[char]
95
+ value = roman_numerals[char]
90
96
  if value >= prev_value:
91
97
  total += value
92
98
  else:
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Union
3
2
 
4
3
  from ebook2text.docx_conversion import DocxConverter, initialize_docx_converter
5
4
  from ebook2text.epub_conversion import EpubConverter, initialize_epub_converter
@@ -9,7 +8,7 @@ from ebook2text.text_parser import TextParser
9
8
 
10
9
  def _initialize_converter(
11
10
  file_path: Path, metadata: dict, extension: str
12
- ) -> Union[DocxConverter, EpubConverter, PDFConverter, TextParser]:
11
+ ) -> DocxConverter | EpubConverter | PDFConverter | TextParser:
13
12
  """
14
13
  Initialize the appropriate converter based on the file extension.
15
14
 
@@ -51,8 +50,8 @@ def convert_file(
51
50
  metadata: dict,
52
51
  *,
53
52
  save_file: bool = True,
54
- save_path: Union[Path, None] = None,
55
- ) -> Union[str, None]:
53
+ save_path: Path | None = None,
54
+ ) -> str | None:
56
55
  """
57
56
  Converts a book to a text file with 3 asterisks for chapter breaks
58
57
  Args:
@@ -77,4 +76,4 @@ def convert_file(
77
76
  for content in converter.parse_file():
78
77
  if content:
79
78
  converter.write_text(content, save_path)
80
- return
79
+ return None
@@ -1,5 +1,5 @@
1
+ from collections.abc import Generator
1
2
  from pathlib import Path
2
- from typing import Generator
3
3
 
4
4
  from ebook2text.docx_conversion.docx_converter import DocxConverter
5
5
  from ebook2text.docx_conversion.docx_image_extractor import DocxImageExtractor
@@ -1,5 +1,5 @@
1
+ from collections.abc import Generator
1
2
  from pathlib import Path
2
- from typing import Generator, Tuple
3
3
 
4
4
  import docx
5
5
  from docx.oxml.exceptions import XmlchemyError
@@ -211,7 +211,7 @@ class DocxConverter:
211
211
 
212
212
  def _process_text(
213
213
  self, paragraph_text: str, current_para_index: int
214
- ) -> Tuple[str, int]:
214
+ ) -> tuple[str, int]:
215
215
  """
216
216
  Process a paragraph's text to determine if it starts a new chapter and
217
217
  format it accordingly.
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
1
  from ebook2text import logger
4
2
  from ebook2text._types import Paragraph
5
3
  from ebook2text.docx_conversion._namespaces import docx_ns_map
@@ -11,7 +9,7 @@ class DocxImageExtractor:
11
9
  A class dedicated to extracting images from docx Paragraph objects.
12
10
  """
13
11
 
14
- def extract_images(self, paragraph: Paragraph) -> List[str]:
12
+ def extract_images(self, paragraph: Paragraph) -> list[str]:
15
13
  """
16
14
  Extracts and converts images found in the paragraph into
17
15
  base64-encoded strings.
@@ -48,8 +46,8 @@ class DocxImageExtractor:
48
46
  blips = paragraph._p.findall(".//a:blip", namespaces=namespace)
49
47
  for blip in blips:
50
48
  try:
51
- rId = blip.attrib[f"{{{namespace['r']}}}embed"]
52
- image_part = paragraph.part.related_parts[rId]
49
+ r_id = blip.attrib[f"{{{namespace['r']}}}embed"]
50
+ image_part = paragraph.part.related_parts[r_id]
53
51
  blob = image_part.blob
54
52
  image_blobs.append(blob)
55
53
  except (KeyError, ValueError, AttributeError) as e:
@@ -1,5 +1,5 @@
1
+ from collections.abc import Generator
1
2
  from pathlib import Path
2
- from typing import Generator
3
3
 
4
4
  from ebook2text.epub_conversion.epub_converter import EpubConverter
5
5
  from ebook2text.epub_conversion.epub_text_extractor import EpubTextExtractor
@@ -1,5 +1,5 @@
1
+ from collections.abc import Generator
1
2
  from pathlib import Path
2
- from typing import Generator
3
3
 
4
4
  import ebooklib
5
5
  from bs4 import BeautifulSoup
@@ -82,9 +82,9 @@ class EpubConverter:
82
82
  Returns:
83
83
  str: String containing the text of the chapter.
84
84
  """
85
- TEXT_ELEMENTS = ["p", "img", "h1", "h2", "h3", "h4", "h5", "h6"]
85
+ text_elements = ["p", "img", "h1", "h2", "h3", "h4", "h5", "h6"]
86
86
  soup = BeautifulSoup(item.content, "html.parser")
87
- elements: ResultSet[Tag] = soup.find_all(TEXT_ELEMENTS)
87
+ elements: ResultSet[Tag] = soup.find_all(text_elements)
88
88
 
89
89
  for i, element in enumerate(elements[: self.max_lines_to_check]):
90
90
  text = self.text_extractor.extract_text(element, self.epub_book)
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from ebook2text._types import EpubBook, Tag
4
2
  from ebook2text.ocr import encode_image_bytes, run_ocr
5
3
 
@@ -9,9 +7,7 @@ class EpubTextExtractor:
9
7
  Extracts text from EPUB elements, handling image OCR.
10
8
  """
11
9
 
12
- def extract_text(
13
- self, element: Tag, book: Optional[EpubBook] = None
14
- ) -> str:
10
+ def extract_text(self, element: Tag, book: EpubBook | None = None) -> str:
15
11
  """
16
12
  Extracts text from an element, using OCR for images.
17
13
 
@@ -9,7 +9,9 @@ from . import logger
9
9
  from ._exceptions import NoResponseError
10
10
 
11
11
  load_dotenv()
12
- api_key: str = os.getenv("OPENAI_API_KEY", "")
12
+ api_key: str = os.environ["OPENAI_API_KEY"]
13
+ MODEL: str = os.environ["OPENAI_MODEL"]
14
+
13
15
  CLIENT: OpenAI = OpenAI(api_key=api_key)
14
16
 
15
17
  GPT_REFUSALS = [
@@ -96,7 +98,7 @@ def run_ocr(
96
98
 
97
99
  try:
98
100
  response: ChatCompletion = client.chat.completions.create(
99
- model="gpt-4o-mini", messages=payload, max_tokens=10
101
+ model=MODEL, messages=payload, max_tokens=10
100
102
  )
101
103
  if response.choices and response.choices[0].message.content:
102
104
  answer: str = response.choices[0].message.content
@@ -1,5 +1,5 @@
1
+ from collections.abc import Generator
1
2
  from pathlib import Path
2
- from typing import Generator
3
3
 
4
4
  from ebook2text.pdf_conversion.pdf_converter import PDFConverter
5
5
  from ebook2text.pdf_conversion.pdf_image_extractor import PDFImageExtractor
@@ -1,6 +1,6 @@
1
1
  import re
2
+ from collections.abc import Generator
2
3
  from pathlib import Path
3
- from typing import Generator, List
4
4
 
5
5
  from pdfminer.high_level import extract_pages
6
6
  from pdfminer.pdfparser import PDFSyntaxError
@@ -40,7 +40,7 @@ class PDFConverter:
40
40
  self._max_lines_to_check = 6
41
41
  self._chapter_separator = "***\n"
42
42
 
43
- self._page: List[str] = []
43
+ self._page: list[str] = []
44
44
 
45
45
  def _read_file(self, file_path: Path) -> Generator[LTPage, None, None]:
46
46
  """
@@ -64,8 +64,7 @@ class PDFConverter:
64
64
 
65
65
  def _is_title_page(self, page_lines: list[str]) -> bool:
66
66
  """
67
- Determines if the page is a title page by comparing the number of lines in
68
- the page to a threshold.
67
+ Determines if title page by comparing page length to threshold.
69
68
  """
70
69
  return len(page_lines) < self._max_lines_to_check
71
70
 
@@ -2,7 +2,6 @@ import base64
2
2
  import itertools
3
3
  from io import BytesIO
4
4
  from pathlib import Path
5
- from typing import List, Tuple
6
5
 
7
6
  from pdfminer.pdfdocument import PDFDocument
8
7
  from pdfminer.pdfinterp import resolve1
@@ -83,7 +82,7 @@ class PDFImageExtractor:
83
82
  def __init__(self, file_path: Path) -> None:
84
83
  self.file_path = file_path
85
84
 
86
- def extract_images(self, obj_nums: List[int]) -> List[str]:
85
+ def extract_images(self, obj_nums: list[int]) -> list[str]:
87
86
  """
88
87
  Processes and extracts images from PDF objects based on the provided
89
88
  object numbers.
@@ -187,14 +186,16 @@ class PDFImageExtractor:
187
186
  """
188
187
  if attempt > 1:
189
188
  logger.warning(
190
- f"Unable to extract image from object: {obj_num} or {obj_num - 1}"
189
+ f"Unable to extract image from object: {obj_num} "
190
+ f"or {obj_num - 1}"
191
191
  )
192
192
  return ""
193
193
  try:
194
194
  obj = resolve1(self.document.getobj(obj_num))
195
195
  if not isinstance(obj, PDFStream):
196
196
  raise TypeError(
197
- f"Invalid object. Received {type(obj)} instead of PDFStream"
197
+ "Invalid object. "
198
+ f"Received {type(obj)} instead of PDFStream"
198
199
  )
199
200
  if _convert_psliteral_to_str(obj.get("Filter")) == "DCTDecode":
200
201
  return self._transcode_to_png(obj.get_data())
@@ -216,7 +217,7 @@ class PDFImageExtractor:
216
217
  logger.exception(f"Exception: {e}")
217
218
  return ""
218
219
 
219
- def _find_next_image(self, obj_num: int, attempt: int) -> Tuple[int, int]:
220
+ def _find_next_image(self, obj_num: int, attempt: int) -> tuple[int, int]:
220
221
  """
221
222
  Finds the next image object in the PDF document.
222
223
 
@@ -238,7 +239,7 @@ class PDFImageExtractor:
238
239
 
239
240
  def _parse_image_data(
240
241
  self, stream: PDFStream
241
- ) -> Tuple[int, int, str, bytes]:
242
+ ) -> tuple[int, int, str, bytes]:
242
243
  """
243
244
  Parses image data from a PDF object.
244
245
 
@@ -261,7 +262,7 @@ class PDFImageExtractor:
261
262
  stream = _expand_bits(stream.get_data(), bit_depth)
262
263
  return width, height, mode, stream
263
264
 
264
- def _extract_color_data(self, stream: PDFStream) -> Tuple[str, int]:
265
+ def _extract_color_data(self, stream: PDFStream) -> tuple[str, int]:
265
266
  """
266
267
  Extracts color data from a PDF object.
267
268
 
@@ -1,5 +1,3 @@
1
- from typing import List, Tuple, Union
2
-
3
1
  from pdfminer.pdfdocument import PDFSyntaxError
4
2
 
5
3
  from ebook2text import logger
@@ -24,7 +22,7 @@ class PDFTextExtractor:
24
22
  self._image_obj_nums: list[int] = []
25
23
  self._pdf_text_list: list[str] = []
26
24
 
27
- def _match_objects(self, obj_type: str, obj_data: Union[int, str]) -> None:
25
+ def _match_objects(self, obj_type: str, obj_data: int | str) -> None:
28
26
  """
29
27
  Matches objects to their respective types.
30
28
 
@@ -46,16 +44,17 @@ class PDFTextExtractor:
46
44
  """
47
45
  Extracts text and image data from a PDF page.
48
46
 
49
- This function receives an LTPage object extracted by PDFMiner and returns
50
- a tuple containing a list of image object numbers and a list of strings
51
- containing the text from the page.
47
+ This function receives an LTPage object extracted by PDFMiner and
48
+ returns a tuple containing a list of image object numbers and a list
49
+ of strings containing the text from the page.
52
50
 
53
51
  Args:pdf
54
52
  page (LTPage): The page to extract text and image data from.
55
53
 
56
54
  Returns:
57
- tuple[list[int], list[str]]: A tuple containing a list of image object
58
- numbers and a list of strings containing the text from the page.
55
+ tuple[list[int], list[str]]: A tuple containing a list of image
56
+ object numbers and a list of strings containing the text from
57
+ the page.
59
58
  """
60
59
 
61
60
  for element in page:
@@ -63,7 +62,7 @@ class PDFTextExtractor:
63
62
  self._match_objects(obj_type, obj_data)
64
63
  return self._image_obj_nums, self._pdf_text_list
65
64
 
66
- def extract_text(self, page: LTPage) -> List[str]:
65
+ def extract_text(self, page: LTPage) -> list[str]:
67
66
  """
68
67
  Extracts text from a PDF page, including OCR text from images.
69
68
 
@@ -82,8 +81,8 @@ class PDFTextExtractor:
82
81
  page (LTPage): The PDF page represented as an LTPage object.
83
82
 
84
83
  Returns:
85
- List[str]: The extracted text content from the PDF page, including OCR
86
- text from images.
84
+ List[str]: The extracted text content from the PDF page, including
85
+ OCR text from images.
87
86
  """
88
87
  image_obj_nums, pdf_text_list = self._extract_element_data(page)
89
88
  self._image_obj_nums = []
@@ -99,7 +98,7 @@ class PDFTextExtractor:
99
98
  raise PDFConversionError from e
100
99
  return [ocr_text] + pdf_text_list
101
100
 
102
- def _process_element(self, element: LTItem) -> Tuple[str, Union[int, str]]:
101
+ def _process_element(self, element: LTItem) -> tuple[str, int | str]:
103
102
  """
104
103
  Processes a PDF layout element to identify its type and extract
105
104
  relevant data.
@@ -128,17 +127,16 @@ class PDFTextExtractor:
128
127
  return self._process_element(child)
129
128
  return "other", ""
130
129
 
131
- def _extract_image_text(self, image_obj_nums: List[int]) -> str:
130
+ def _extract_image_text(self, image_obj_nums: list[int]) -> str:
132
131
  """
133
- Collect list of Base64 encoded images and process with LLM to extract text.
134
-
132
+ Collect list of Base64 encoded images and extract text with LLM.
135
133
  Args:
136
134
  image_obj_nums (List[int]): List of image object numbers.
137
135
 
138
136
  Returns:
139
137
  str: Extracted text from images.
140
138
  """
141
- base64_images: List[str] = self.image_extractor.extract_images(
139
+ base64_images: list[str] = self.image_extractor.extract_images(
142
140
  image_obj_nums
143
141
  )
144
142
  return run_ocr(base64_images)
@@ -1,5 +1,5 @@
1
+ from collections.abc import Generator
1
2
  from pathlib import Path
2
- from typing import Generator
3
3
 
4
4
  from ebook2text._exceptions import TextConversionError
5
5
  from ebook2text._logger import logger
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: ebook2text
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: Convert common book file types to text for machine learning
5
5
  Author: Ashlynn Antrobus
6
6
  Author-email: Ashlynn Antrobus <ashlynn@prosepal.io>
@@ -21,10 +21,10 @@ Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
23
  Classifier: Topic :: Text Processing
24
- Requires-Python: >=3.9
24
+ Requires-Python: >=3.10
25
25
  Description-Content-Type: text/markdown
26
26
  Requires-Dist: beautifulsoup4>=4.12.3
27
- Requires-Dist: ebooklib-autoupdate>=0.18.1
27
+ Requires-Dist: ebooklib-autoupdate>=0.18.3
28
28
  Requires-Dist: openai>=1.54.3
29
29
  Requires-Dist: pdfminer-six>=20240706
30
30
  Requires-Dist: pillow>=10.4.0
@@ -1,5 +1,5 @@
1
1
  beautifulsoup4>=4.12.3
2
- ebooklib-autoupdate>=0.18.1
2
+ ebooklib-autoupdate>=0.18.3
3
3
  openai>=1.54.3
4
4
  pdfminer-six>=20240706
5
5
  pillow>=10.4.0
@@ -21,10 +21,10 @@ classifiers = [
21
21
  "Topic :: Scientific/Engineering :: Artificial Intelligence",
22
22
  "Topic :: Text Processing"
23
23
  ]
24
- requires-python = ">=3.9"
24
+ requires-python = ">=3.10"
25
25
  dependencies = [
26
26
  "beautifulsoup4>=4.12.3",
27
- "ebooklib-autoupdate>=0.18.1",
27
+ "ebooklib-autoupdate>=0.18.3",
28
28
  "openai>=1.54.3",
29
29
  "pdfminer-six>=20240706",
30
30
  "pillow>=10.4.0",
@@ -42,15 +42,27 @@ build-backend = "setuptools.build_meta"
42
42
 
43
43
  [tool.ruff]
44
44
  line-length = 79
45
- force-exclude = true
45
+ target-version = "py310"
46
46
  exclude = [".git", "__pycache__", ".venv", "dist", "build", ".vscode", ".egg-info"]
47
47
 
48
- [tool.isort]
49
- line_length = 79
48
+ [tool.ruff.lint]
49
+ select = ["E", "F", "I", "N", "UP"]
50
+
51
+ [tool.ruff.lint.per-file-ignores]
52
+ "**/{tests,docs,tools}/*" = ["F401", "I001", "E501", "N806"]
53
+ "**/VERSION.py" = ["N999"]
54
+
55
+ [tool.ruff.format]
56
+ docstring-code-format = true
57
+ docstring-code-line-length = "dynamic"
58
+ line-ending = "lf"
50
59
 
51
60
  [tool.mypy]
52
- disable_error_code = "import-untyped"
53
- exclude = "ebooklib"
61
+ mypy_path = "$MYPY_CONFIG_FILE_DIR/ebook2text"
62
+ exclude_gitignore = true
63
+ ignore_missing_imports = true
64
+ follow_imports = "silent"
65
+ follow_untyped_imports = false
54
66
 
55
67
  [tool.pytest.ini_options]
56
68
  testpaths = ["tests"]
@@ -91,21 +91,21 @@ class TestPDFConverter:
91
91
  def test_parse_file_text_extraction(self, pdf_converter):
92
92
  """Test that parse_file extracts only the main content and excludes unwanted sections."""
93
93
  parsed_text = "".join(pdf_converter.parse_file())
94
- assert (
95
- "Sample Title" not in parsed_text
96
- ), "Title text should be removed from output"
97
- assert (
98
- "Introduction" not in parsed_text
99
- ), "Introduction section should be removed from output"
100
- assert (
101
- "Chapter 1" not in parsed_text
102
- ), "Chapter 1 header should be removed from output"
103
- assert (
104
- "Chapter 2" not in parsed_text
105
- ), "Chapter 2 header should be removed from output"
106
- assert (
107
- "First chapter paragraph text." in parsed_text
108
- ), "Text after Chapter 1 should be included"
94
+ assert "Sample Title" not in parsed_text, (
95
+ "Title text should be removed from output"
96
+ )
97
+ assert "Introduction" not in parsed_text, (
98
+ "Introduction section should be removed from output"
99
+ )
100
+ assert "Chapter 1" not in parsed_text, (
101
+ "Chapter 1 header should be removed from output"
102
+ )
103
+ assert "Chapter 2" not in parsed_text, (
104
+ "Chapter 2 header should be removed from output"
105
+ )
106
+ assert "First chapter paragraph text." in parsed_text, (
107
+ "Text after Chapter 1 should be included"
108
+ )
109
109
  assert (
110
110
  "Lorem ipsum odor amet, consectetuer adipiscing elit."
111
111
  in parsed_text
@@ -113,9 +113,9 @@ class TestPDFConverter:
113
113
 
114
114
  def test_chapter_separator_insertion(self, pdf_converter):
115
115
  parsed_text = "".join(pdf_converter.parse_file())
116
- assert (
117
- pdf_converter._chapter_separator in parsed_text
118
- ), "Chapter separator should be inserted between chapters"
116
+ assert pdf_converter._chapter_separator in parsed_text, (
117
+ "Chapter separator should be inserted between chapters"
118
+ )
119
119
 
120
120
  def test_page_combining(self, pdf_converter):
121
121
  page_lines = [
@@ -135,29 +135,29 @@ class TestPDFConverter:
135
135
  cleaned_text = pdf_converter.remove_extra_whitespace(
136
136
  text_with_whitespace
137
137
  )
138
- assert (
139
- cleaned_text == "Line 1\nLine 2 Line 3"
140
- ), "Extra whitespace should be removed"
138
+ assert cleaned_text == "Line 1\nLine 2 Line 3", (
139
+ "Extra whitespace should be removed"
140
+ )
141
141
 
142
142
  def test_image_processing(self, pdf_converter_with_images):
143
143
  """Test parsing with an image-based PDF containing 'Chapter 1' as an image."""
144
144
  parsed_text = "".join(pdf_converter_with_images.parse_file())
145
145
 
146
- assert (
147
- "Sample Title" not in parsed_text
148
- ), "Title text should be removed from output"
149
- assert (
150
- "Introduction" not in parsed_text
151
- ), "Introduction section should be removed from output"
152
- assert (
153
- "Chapter 1" not in parsed_text
154
- ), "Chapter 1 header should be removed from output"
155
- assert (
156
- "Chapter 2" not in parsed_text
157
- ), "Chapter 2 header should be removed from output"
158
- assert (
159
- "First chapter paragraph text." in parsed_text
160
- ), "Text after Chapter 1 should be included"
146
+ assert "Sample Title" not in parsed_text, (
147
+ "Title text should be removed from output"
148
+ )
149
+ assert "Introduction" not in parsed_text, (
150
+ "Introduction section should be removed from output"
151
+ )
152
+ assert "Chapter 1" not in parsed_text, (
153
+ "Chapter 1 header should be removed from output"
154
+ )
155
+ assert "Chapter 2" not in parsed_text, (
156
+ "Chapter 2 header should be removed from output"
157
+ )
158
+ assert "First chapter paragraph text." in parsed_text, (
159
+ "Text after Chapter 1 should be included"
160
+ )
161
161
  assert (
162
162
  "Lorem ipsum odor amet, consectetuer adipiscing elit."
163
163
  in parsed_text
@@ -1 +0,0 @@
1
- __version__ = "2.1.2"
File without changes
File without changes
File without changes
File without changes