arkindex-base-worker 0.3.7rc10__py3-none-any.whl → 0.4.0__py3-none-any.whl

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 (54) hide show
  1. {arkindex_base_worker-0.3.7rc10.dist-info → arkindex_base_worker-0.4.0.dist-info}/METADATA +16 -20
  2. arkindex_base_worker-0.4.0.dist-info/RECORD +61 -0
  3. {arkindex_base_worker-0.3.7rc10.dist-info → arkindex_base_worker-0.4.0.dist-info}/WHEEL +1 -1
  4. arkindex_worker/cache.py +1 -1
  5. arkindex_worker/image.py +120 -1
  6. arkindex_worker/models.py +6 -0
  7. arkindex_worker/utils.py +85 -4
  8. arkindex_worker/worker/__init__.py +68 -162
  9. arkindex_worker/worker/base.py +39 -34
  10. arkindex_worker/worker/classification.py +34 -18
  11. arkindex_worker/worker/corpus.py +86 -0
  12. arkindex_worker/worker/dataset.py +71 -1
  13. arkindex_worker/worker/element.py +352 -91
  14. arkindex_worker/worker/entity.py +11 -11
  15. arkindex_worker/worker/image.py +21 -0
  16. arkindex_worker/worker/metadata.py +19 -9
  17. arkindex_worker/worker/process.py +92 -0
  18. arkindex_worker/worker/task.py +5 -4
  19. arkindex_worker/worker/training.py +25 -10
  20. arkindex_worker/worker/transcription.py +89 -68
  21. arkindex_worker/worker/version.py +3 -1
  22. tests/__init__.py +8 -0
  23. tests/conftest.py +36 -52
  24. tests/test_base_worker.py +212 -12
  25. tests/test_dataset_worker.py +21 -45
  26. tests/test_elements_worker/{test_classifications.py → test_classification.py} +216 -100
  27. tests/test_elements_worker/test_cli.py +3 -11
  28. tests/test_elements_worker/test_corpus.py +168 -0
  29. tests/test_elements_worker/test_dataset.py +7 -12
  30. tests/test_elements_worker/test_element.py +427 -0
  31. tests/test_elements_worker/test_element_create_multiple.py +715 -0
  32. tests/test_elements_worker/test_element_create_single.py +528 -0
  33. tests/test_elements_worker/test_element_list_children.py +969 -0
  34. tests/test_elements_worker/test_element_list_parents.py +530 -0
  35. tests/test_elements_worker/{test_entities.py → test_entity_create.py} +37 -195
  36. tests/test_elements_worker/test_entity_list_and_check.py +160 -0
  37. tests/test_elements_worker/test_image.py +66 -0
  38. tests/test_elements_worker/test_metadata.py +230 -139
  39. tests/test_elements_worker/test_process.py +89 -0
  40. tests/test_elements_worker/test_task.py +8 -18
  41. tests/test_elements_worker/test_training.py +17 -8
  42. tests/test_elements_worker/test_transcription_create.py +873 -0
  43. tests/test_elements_worker/test_transcription_create_with_elements.py +951 -0
  44. tests/test_elements_worker/test_transcription_list.py +450 -0
  45. tests/test_elements_worker/test_version.py +60 -0
  46. tests/test_elements_worker/test_worker.py +563 -279
  47. tests/test_image.py +432 -209
  48. tests/test_merge.py +1 -2
  49. tests/test_utils.py +66 -3
  50. arkindex_base_worker-0.3.7rc10.dist-info/RECORD +0 -47
  51. tests/test_elements_worker/test_elements.py +0 -2713
  52. tests/test_elements_worker/test_transcriptions.py +0 -2119
  53. {arkindex_base_worker-0.3.7rc10.dist-info → arkindex_base_worker-0.4.0.dist-info}/LICENSE +0 -0
  54. {arkindex_base_worker-0.3.7rc10.dist-info → arkindex_base_worker-0.4.0.dist-info}/top_level.txt +0 -0
tests/test_image.py CHANGED
@@ -1,5 +1,5 @@
1
+ import logging
1
2
  import math
2
- import unittest
3
3
  import uuid
4
4
  from io import BytesIO
5
5
  from operator import attrgetter
@@ -18,21 +18,52 @@ from arkindex_worker.image import (
18
18
  download_tiles,
19
19
  open_image,
20
20
  polygon_bounding_box,
21
+ resized_images,
21
22
  revert_orientation,
22
23
  trim_polygon,
24
+ update_pillow_image_size_limit,
23
25
  upload_image,
24
26
  )
25
27
  from arkindex_worker.models import Element
28
+ from tests import FIXTURES_DIR
26
29
 
27
- FIXTURES = Path(__file__).absolute().parent / "data"
28
- TILE = FIXTURES / "test_image.jpg"
29
- FULL_IMAGE = FIXTURES / "tiled_image.jpg"
30
- ROTATED_IMAGE = FIXTURES / "rotated_image.jpg"
31
- MIRRORED_IMAGE = FIXTURES / "mirrored_image.jpg"
32
- ROTATED_MIRRORED_IMAGE = FIXTURES / "rotated_mirrored_image.jpg"
30
+ TILE = FIXTURES_DIR / "test_image.jpg"
31
+ FULL_IMAGE = FIXTURES_DIR / "tiled_image.jpg"
32
+ ROTATED_IMAGE = FIXTURES_DIR / "rotated_image.jpg"
33
+ MIRRORED_IMAGE = FIXTURES_DIR / "mirrored_image.jpg"
34
+ ROTATED_MIRRORED_IMAGE = FIXTURES_DIR / "rotated_mirrored_image.jpg"
33
35
  TEST_IMAGE = {"width": 800, "height": 300}
34
36
 
35
37
 
38
+ @pytest.fixture
39
+ def mock_page():
40
+ class Page(Element):
41
+ def open_image(
42
+ self,
43
+ *args,
44
+ max_width: int | None = None,
45
+ max_height: int | None = None,
46
+ use_full_image: bool | None = False,
47
+ **kwargs,
48
+ ) -> Image.Image:
49
+ # Image from Socface (https://socface.site.ined.fr/) project (AD026)
50
+ image = Image.open(FIXTURES_DIR / "AD026_6M_00505_0001_0373.jpg")
51
+ image.thumbnail(size=(max_width or image.width, max_height or image.height))
52
+
53
+ return image
54
+
55
+ return Page(
56
+ id="page_id",
57
+ name="1",
58
+ zone={
59
+ "polygon": [[0, 0], [1000, 0], [1000, 3000], [0, 3000], [0, 0]],
60
+ "image": {"width": 1000, "height": 3000},
61
+ },
62
+ rotation_angle=0,
63
+ mirrored=False,
64
+ )
65
+
66
+
36
67
  def _root_mean_square(img_a, img_b):
37
68
  """
38
69
  Get the root-mean-square difference between two images for fuzzy matching
@@ -45,10 +76,33 @@ def _root_mean_square(img_a, img_b):
45
76
  )
46
77
 
47
78
 
79
+ @pytest.mark.parametrize(
80
+ ("max_image_pixels", "expected_image_pixels"),
81
+ [
82
+ # Pillow Image size limit not updated
83
+ (None, Image.MAX_IMAGE_PIXELS),
84
+ # Pillow Image size limit set to None
85
+ ("0", None),
86
+ (0, None),
87
+ # Update Pillow Image size limit
88
+ ("1", 1),
89
+ (1, 1),
90
+ ],
91
+ )
92
+ def test_update_pillow_image_size_limit(max_image_pixels, expected_image_pixels):
93
+ MAX_IMAGE_PIXELS = Image.MAX_IMAGE_PIXELS
94
+
95
+ @update_pillow_image_size_limit
96
+ def function() -> int | None:
97
+ return Image.MAX_IMAGE_PIXELS
98
+
99
+ assert function(max_image_pixels=max_image_pixels) == expected_image_pixels
100
+ assert Image.MAX_IMAGE_PIXELS == MAX_IMAGE_PIXELS
101
+
102
+
48
103
  def test_download_tiles(responses):
49
104
  expected = Image.open(FULL_IMAGE).convert("RGB")
50
- with TILE.open("rb") as tile:
51
- tile_bytes = tile.read()
105
+ tile_bytes = TILE.read_bytes()
52
106
 
53
107
  responses.add(
54
108
  responses.GET,
@@ -76,9 +130,8 @@ def test_download_tiles_crop(responses):
76
130
  """
77
131
  expected = Image.open(FULL_IMAGE).convert("RGB")
78
132
  tile_bytes = BytesIO()
79
- with TILE.open("rb") as tile:
80
- # Add one extra pixel to each tile to return slightly bigger tiles
81
- ImageOps.pad(Image.open(tile), (181, 241)).save(tile_bytes, format="jpeg")
133
+ # Add one extra pixel to each tile to return slightly bigger tiles
134
+ ImageOps.pad(Image.open(TILE), (181, 241)).save(tile_bytes, format="jpeg")
82
135
 
83
136
  tile_bytes = tile_bytes.getvalue()
84
137
 
@@ -165,74 +218,33 @@ def test_open_image_rotate_mirror(rotation_angle, mirrored, expected_path):
165
218
  assert _root_mean_square(expected, actual) <= 15.0
166
219
 
167
220
 
168
- class TestTrimPolygon(unittest.TestCase):
169
- def test_trim_polygon_partially_outside_image(self):
170
- bad_polygon = [
171
- [99, 200],
172
- [197, 224],
173
- [120, 251],
174
- [232, 350],
175
- [312, 364],
176
- [325, 310],
177
- [318, 295],
178
- [296, 260],
179
- [352, 259],
180
- [106, 210],
181
- [197, 206],
182
- [99, 200],
183
- ]
184
- expected_polygon = [
185
- [99, 200],
186
- [197, 224],
187
- [120, 251],
188
- [232, 300],
189
- [312, 300],
190
- [325, 300],
191
- [318, 295],
192
- [296, 260],
193
- [352, 259],
194
- [106, 210],
195
- [197, 206],
196
- [99, 200],
197
- ]
198
- assert (
199
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
200
- == expected_polygon
201
- )
202
-
203
- def test_trim_polygon_good_polygon(self):
204
- good_polygon = (
205
- (12, 56),
206
- (29, 60),
207
- (35, 61),
208
- (42, 59),
209
- (58, 57),
210
- (65, 61),
211
- (72, 57),
212
- (12, 56),
213
- )
214
- expected_polygon = [
215
- [12, 56],
216
- [29, 60],
217
- [35, 61],
218
- [42, 59],
219
- [58, 57],
220
- [65, 61],
221
- [72, 57],
222
- [12, 56],
223
- ]
224
- assert (
225
- trim_polygon(good_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
226
- == expected_polygon
227
- )
228
-
229
- def test_trim_polygon_invalid_polygon(self):
230
- """
231
- An assertion error is raised the polygon input isn't a list or tuple
232
- """
233
- bad_polygon = {
234
- "polygon": [
235
- [99, 200],
221
+ @pytest.mark.parametrize(
222
+ ("polygon", "error"),
223
+ [
224
+ # Polygon input isn't a list or tuple
225
+ (
226
+ {
227
+ "polygon": [
228
+ [99, 200],
229
+ [25, 224],
230
+ [0, 0],
231
+ [0, 300],
232
+ [102, 300],
233
+ [260, 300],
234
+ [288, 295],
235
+ [296, 260],
236
+ [352, 259],
237
+ [106, 210],
238
+ [197, 206],
239
+ [99, 208],
240
+ ]
241
+ },
242
+ "Input polygon must be a valid list or tuple of points.",
243
+ ),
244
+ # Point coordinates are not integers
245
+ (
246
+ [
247
+ [9.9, 200],
236
248
  [25, 224],
237
249
  [0, 0],
238
250
  [0, 300],
@@ -243,136 +255,161 @@ class TestTrimPolygon(unittest.TestCase):
243
255
  [352, 259],
244
256
  [106, 210],
245
257
  [197, 206],
246
- [99, 208],
247
- ]
248
- }
249
- with pytest.raises(
250
- AssertionError,
251
- match="Input polygon must be a valid list or tuple of points.",
252
- ):
253
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
254
-
255
- def test_trim_polygon_negative_coordinates(self):
256
- """
257
- Negative coordinates are ignored and replaced by 0 with no error being thrown
258
- """
259
- bad_polygon = [
260
- [99, 200],
261
- [25, 224],
262
- [-8, -52],
263
- [-12, 350],
264
- [102, 364],
265
- [260, 310],
266
- [288, 295],
267
- [296, 260],
268
- [352, 259],
269
- [106, 210],
270
- [197, 206],
271
- [99, 200],
272
- ]
273
- expected_polygon = [
274
- [99, 200],
275
- [25, 224],
276
- [0, 0],
277
- [0, 300],
278
- [102, 300],
279
- [260, 300],
280
- [288, 295],
281
- [296, 260],
282
- [352, 259],
283
- [106, 210],
284
- [197, 206],
285
- [99, 200],
286
- ]
287
- assert (
288
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
289
- == expected_polygon
290
- )
258
+ [99, 20.8],
259
+ ],
260
+ "Polygon point coordinates must be integers.",
261
+ ),
262
+ # Point coordinates are not lists or tuples
263
+ (
264
+ [
265
+ [12, 56],
266
+ [29, 60],
267
+ [35, 61],
268
+ "[42, 59]",
269
+ [58, 57],
270
+ [65, 61],
271
+ [72, 57],
272
+ [12, 56],
273
+ ],
274
+ "Polygon points must be tuples or lists.",
275
+ ),
276
+ # Point coordinates are not lists or tuples of length 2
277
+ (
278
+ [
279
+ [12, 56],
280
+ [29, 60, 3],
281
+ [35, 61],
282
+ [42, 59],
283
+ [58, 57],
284
+ [65, 61],
285
+ [72, 57],
286
+ [12, 56],
287
+ ],
288
+ "Polygon points must be tuples or lists of 2 elements.",
289
+ ),
290
+ # None of the polygon's points are inside the image
291
+ (
292
+ [
293
+ [999, 200],
294
+ [1097, 224],
295
+ [1020, 251],
296
+ [1232, 350],
297
+ [1312, 364],
298
+ [1325, 310],
299
+ [1318, 295],
300
+ [1296, 260],
301
+ [1352, 259],
302
+ [1006, 210],
303
+ [997, 206],
304
+ [999, 200],
305
+ ],
306
+ "This polygon is entirely outside the image's bounds.",
307
+ ),
308
+ ],
309
+ )
310
+ def test_trim_polygon_errors(polygon, error):
311
+ with pytest.raises(AssertionError, match=error):
312
+ trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
313
+
314
+
315
+ def test_trim_polygon_negative_coordinates():
316
+ """
317
+ Negative coordinates are ignored and replaced by 0 with no error being thrown
318
+ """
319
+ polygon = [
320
+ [99, 200],
321
+ [25, 224],
322
+ [-8, -52],
323
+ [-12, 350],
324
+ [102, 364],
325
+ [260, 310],
326
+ [288, 295],
327
+ [296, 260],
328
+ [352, 259],
329
+ [106, 210],
330
+ [197, 206],
331
+ [99, 200],
332
+ ]
333
+ expected_polygon = [
334
+ [99, 200],
335
+ [25, 224],
336
+ [0, 0],
337
+ [0, 300],
338
+ [102, 300],
339
+ [260, 300],
340
+ [288, 295],
341
+ [296, 260],
342
+ [352, 259],
343
+ [106, 210],
344
+ [197, 206],
345
+ [99, 200],
346
+ ]
347
+ assert (
348
+ trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
349
+ == expected_polygon
350
+ )
291
351
 
292
- def test_trim_polygon_outside_image_error(self):
293
- """
294
- An assertion error is raised when none of the polygon's points are inside the image
295
- """
296
- bad_polygon = [
297
- [999, 200],
298
- [1097, 224],
299
- [1020, 251],
300
- [1232, 350],
301
- [1312, 364],
302
- [1325, 310],
303
- [1318, 295],
304
- [1296, 260],
305
- [1352, 259],
306
- [1006, 210],
307
- [997, 206],
308
- [999, 200],
309
- ]
310
- with pytest.raises(
311
- AssertionError, match="This polygon is entirely outside the image's bounds."
312
- ):
313
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
314
-
315
- def test_trim_polygon_float_coordinates(self):
316
- """
317
- An assertion error is raised when point coordinates are not integers
318
- """
319
- bad_polygon = [
320
- [9.9, 200],
321
- [25, 224],
322
- [0, 0],
323
- [0, 300],
324
- [102, 300],
325
- [260, 300],
326
- [288, 295],
327
- [296, 260],
328
- [352, 259],
329
- [106, 210],
330
- [197, 206],
331
- [99, 20.8],
332
- ]
333
- with pytest.raises(
334
- AssertionError, match="Polygon point coordinates must be integers."
335
- ):
336
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
337
-
338
- def test_trim_polygon_invalid_points_1(self):
339
- """
340
- An assertion error is raised when point coordinates are not lists or tuples
341
- """
342
- bad_polygon = [
343
- [12, 56],
344
- [29, 60],
345
- [35, 61],
346
- "[42, 59]",
347
- [58, 57],
348
- [65, 61],
349
- [72, 57],
350
- [12, 56],
351
- ]
352
- with pytest.raises(
353
- AssertionError, match="Polygon points must be tuples or lists."
354
- ):
355
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
356
-
357
- def test_trim_polygon_invalid_points_2(self):
358
- """
359
- An assertion error is raised when point coordinates are not lists or tuples of length 2
360
- """
361
- bad_polygon = [
362
- [12, 56],
363
- [29, 60, 3],
364
- [35, 61],
365
- [42, 59],
366
- [58, 57],
367
- [65, 61],
368
- [72, 57],
369
- [12, 56],
370
- ]
371
- with pytest.raises(
372
- AssertionError,
373
- match="Polygon points must be tuples or lists of 2 elements.",
374
- ):
375
- trim_polygon(bad_polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
352
+
353
+ def test_trim_polygon_partially_outside_image():
354
+ polygon = [
355
+ [99, 200],
356
+ [197, 224],
357
+ [120, 251],
358
+ [232, 350],
359
+ [312, 364],
360
+ [325, 310],
361
+ [318, 295],
362
+ [296, 260],
363
+ [352, 259],
364
+ [106, 210],
365
+ [197, 206],
366
+ [99, 200],
367
+ ]
368
+ expected_polygon = [
369
+ [99, 200],
370
+ [197, 224],
371
+ [120, 251],
372
+ [232, 300],
373
+ [312, 300],
374
+ [325, 300],
375
+ [318, 295],
376
+ [296, 260],
377
+ [352, 259],
378
+ [106, 210],
379
+ [197, 206],
380
+ [99, 200],
381
+ ]
382
+ assert (
383
+ trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
384
+ == expected_polygon
385
+ )
386
+
387
+
388
+ def test_trim_polygon():
389
+ polygon = (
390
+ (12, 56),
391
+ (29, 60),
392
+ (35, 61),
393
+ (42, 59),
394
+ (58, 57),
395
+ (65, 61),
396
+ (72, 57),
397
+ (12, 56),
398
+ )
399
+ expected_polygon = [
400
+ [12, 56],
401
+ [29, 60],
402
+ [35, 61],
403
+ [42, 59],
404
+ [58, 57],
405
+ [65, 61],
406
+ [72, 57],
407
+ [12, 56],
408
+ ]
409
+ assert (
410
+ trim_polygon(polygon, TEST_IMAGE["width"], TEST_IMAGE["height"])
411
+ == expected_polygon
412
+ )
376
413
 
377
414
 
378
415
  @pytest.mark.parametrize(
@@ -584,3 +621,189 @@ def test_upload_image(responses):
584
621
 
585
622
  assert len(responses.calls) == 1
586
623
  assert list(map(attrgetter("request.url"), responses.calls)) == [dest_url]
624
+
625
+
626
+ @pytest.mark.parametrize(
627
+ ("max_pixels", "max_bytes", "expected_sizes", "expected_logs"),
628
+ [
629
+ # No limits
630
+ (
631
+ None,
632
+ None,
633
+ [
634
+ (1992, 3000),
635
+ (1793, 2700),
636
+ (1694, 2550),
637
+ (1594, 2400),
638
+ (1494, 2250),
639
+ (1395, 2100),
640
+ (1195, 1800),
641
+ (996, 1500),
642
+ (797, 1200),
643
+ (598, 900),
644
+ ],
645
+ [
646
+ (logging.WARNING, "The image was resized to (1992 x 3000)."),
647
+ (logging.WARNING, "The image was resized to (1793 x 2700)."),
648
+ (logging.WARNING, "The image was resized to (1694 x 2550)."),
649
+ (logging.WARNING, "The image was resized to (1594 x 2400)."),
650
+ (logging.WARNING, "The image was resized to (1494 x 2250)."),
651
+ (logging.WARNING, "The image was resized to (1395 x 2100)."),
652
+ (logging.WARNING, "The image was resized to (1195 x 1800)."),
653
+ (logging.WARNING, "The image was resized to (996 x 1500)."),
654
+ (logging.WARNING, "The image was resized to (797 x 1200)."),
655
+ (logging.WARNING, "The image was resized to (598 x 900)."),
656
+ ],
657
+ ),
658
+ # Image already under the limits
659
+ (
660
+ 10000,
661
+ 4000000, # 4MB
662
+ [
663
+ (1992, 3000),
664
+ (1793, 2700),
665
+ (1694, 2550),
666
+ (1594, 2400),
667
+ (1494, 2250),
668
+ (1395, 2100),
669
+ (1195, 1800),
670
+ (996, 1500),
671
+ (797, 1200),
672
+ (598, 900),
673
+ ],
674
+ [
675
+ (logging.WARNING, "The image was resized to (1992 x 3000)."),
676
+ (logging.WARNING, "The image was resized to (1793 x 2700)."),
677
+ (logging.WARNING, "The image was resized to (1694 x 2550)."),
678
+ (logging.WARNING, "The image was resized to (1594 x 2400)."),
679
+ (logging.WARNING, "The image was resized to (1494 x 2250)."),
680
+ (logging.WARNING, "The image was resized to (1395 x 2100)."),
681
+ (logging.WARNING, "The image was resized to (1195 x 1800)."),
682
+ (logging.WARNING, "The image was resized to (996 x 1500)."),
683
+ (logging.WARNING, "The image was resized to (797 x 1200)."),
684
+ (logging.WARNING, "The image was resized to (598 x 900)."),
685
+ ],
686
+ ),
687
+ # Image above the limits
688
+ (
689
+ None,
690
+ 100000, # 100kB
691
+ [(598, 900)],
692
+ [
693
+ (logging.WARNING, "The image was resized to (1992 x 3000)."),
694
+ (logging.WARNING, "The image size is 773.4 kB."),
695
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
696
+ (logging.WARNING, "The image will be resized."),
697
+ (logging.WARNING, "The image was resized to (1793 x 2700)."),
698
+ (logging.WARNING, "The image size is 616.0 kB."),
699
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
700
+ (logging.WARNING, "The image will be resized."),
701
+ (logging.WARNING, "The image was resized to (1694 x 2550)."),
702
+ (logging.WARNING, "The image size is 546.4 kB."),
703
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
704
+ (logging.WARNING, "The image will be resized."),
705
+ (logging.WARNING, "The image was resized to (1594 x 2400)."),
706
+ (logging.WARNING, "The image size is 479.4 kB."),
707
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
708
+ (logging.WARNING, "The image will be resized."),
709
+ (logging.WARNING, "The image was resized to (1494 x 2250)."),
710
+ (logging.WARNING, "The image size is 416.1 kB."),
711
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
712
+ (logging.WARNING, "The image will be resized."),
713
+ (logging.WARNING, "The image was resized to (1395 x 2100)."),
714
+ (logging.WARNING, "The image size is 360.5 kB."),
715
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
716
+ (logging.WARNING, "The image will be resized."),
717
+ (logging.WARNING, "The image was resized to (1195 x 1800)."),
718
+ (logging.WARNING, "The image size is 258.6 kB."),
719
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
720
+ (logging.WARNING, "The image will be resized."),
721
+ (logging.WARNING, "The image was resized to (996 x 1500)."),
722
+ (logging.WARNING, "The image size is 179.0 kB."),
723
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
724
+ (logging.WARNING, "The image will be resized."),
725
+ (logging.WARNING, "The image was resized to (797 x 1200)."),
726
+ (logging.WARNING, "The image size is 115.7 kB."),
727
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
728
+ (logging.WARNING, "The image will be resized."),
729
+ (logging.WARNING, "The image was resized to (598 x 900)."),
730
+ ],
731
+ ),
732
+ # Image above the limits
733
+ (
734
+ 2000,
735
+ None,
736
+ [(1328, 2000), (1195, 1800), (996, 1500), (797, 1200), (598, 900)],
737
+ [
738
+ (
739
+ logging.WARNING,
740
+ "Maximum image input size supported is (2000 x 2000).",
741
+ ),
742
+ (logging.WARNING, "The image will be resized."),
743
+ (logging.WARNING, "The image was resized to (1328 x 2000)."),
744
+ (logging.WARNING, "The image was resized to (1195 x 1800)."),
745
+ (logging.WARNING, "The image was resized to (996 x 1500)."),
746
+ (logging.WARNING, "The image was resized to (797 x 1200)."),
747
+ (logging.WARNING, "The image was resized to (598 x 900)."),
748
+ ],
749
+ ),
750
+ # Image above the limits
751
+ (
752
+ 2000,
753
+ 100000, # 100kB
754
+ [(598, 900)],
755
+ [
756
+ (
757
+ logging.WARNING,
758
+ "Maximum image input size supported is (2000 x 2000).",
759
+ ),
760
+ (logging.WARNING, "The image will be resized."),
761
+ (logging.WARNING, "The image was resized to (1328 x 2000)."),
762
+ (logging.WARNING, "The image size is 325.5 kB."),
763
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
764
+ (logging.WARNING, "The image will be resized."),
765
+ (logging.WARNING, "The image was resized to (1195 x 1800)."),
766
+ (logging.WARNING, "The image size is 258.6 kB."),
767
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
768
+ (logging.WARNING, "The image will be resized."),
769
+ (logging.WARNING, "The image was resized to (996 x 1500)."),
770
+ (logging.WARNING, "The image size is 179.0 kB."),
771
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
772
+ (logging.WARNING, "The image will be resized."),
773
+ (logging.WARNING, "The image was resized to (797 x 1200)."),
774
+ (logging.WARNING, "The image size is 115.7 kB."),
775
+ (logging.WARNING, "Maximum image input size supported is 100.0 kB."),
776
+ (logging.WARNING, "The image will be resized."),
777
+ (logging.WARNING, "The image was resized to (598 x 900)."),
778
+ ],
779
+ ),
780
+ # Image always above the limits
781
+ (
782
+ 50,
783
+ 50, # 50B
784
+ [],
785
+ [
786
+ (logging.WARNING, "Maximum image input size supported is (50 x 50)."),
787
+ (logging.WARNING, "The image will be resized."),
788
+ (logging.WARNING, "The image was resized to (33 x 50)."),
789
+ (logging.WARNING, "The image size is 1.0 kB."),
790
+ (logging.WARNING, "Maximum image input size supported is 50 Bytes."),
791
+ (logging.WARNING, "The image will be resized."),
792
+ ],
793
+ ),
794
+ ],
795
+ )
796
+ def test_resized_images(
797
+ max_pixels, max_bytes, expected_sizes, expected_logs, mock_page, caplog
798
+ ):
799
+ caplog.set_level(logging.WARNING)
800
+
801
+ assert [
802
+ Image.open(image).size
803
+ for image in resized_images(
804
+ element=mock_page, max_pixels=max_pixels, max_bytes=max_bytes
805
+ )
806
+ ] == expected_sizes
807
+ assert [
808
+ (record.levelno, record.message) for record in caplog.records
809
+ ] == expected_logs