unstructured

Форк
0
638 строк · 23.3 Кб
1
# pyright: reportPrivateUsage=false
2

3
"""Test-suite for `unstructured.documents.elements` module."""
4

5
from __future__ import annotations
6

7
import json
8
import pathlib
9
from functools import partial
10

11
import pytest
12

13
from unstructured.cleaners.core import clean_prefix
14
from unstructured.cleaners.translate import translate_text
15
from unstructured.documents.coordinates import (
16
    CoordinateSystem,
17
    Orientation,
18
    RelativeCoordinateSystem,
19
)
20
from unstructured.documents.elements import (
21
    UUID,
22
    ConsolidationStrategy,
23
    CoordinatesMetadata,
24
    DataSourceMetadata,
25
    Element,
26
    ElementMetadata,
27
    NoID,
28
    Points,
29
    RegexMetadata,
30
    Text,
31
)
32

33

34
def test_text_id():
35
    text_element = Text(text="hello there!")
36
    assert text_element.id == "c69509590d81db2f37f9d75480c8efed"
37

38

39
def test_text_uuid():
40
    text_element = Text(text="hello there!", element_id=UUID())
41

42
    id = text_element.id
43

44
    assert isinstance(id, str)
45
    assert len(id) == 36
46
    assert id.count("-") == 4
47
    # -- Test that the element is JSON serializable. This shold run without an error --
48
    json.dumps(text_element.to_dict())
49

50

51
def test_element_defaults_to_blank_id():
52
    element = Element()
53
    assert isinstance(element.id, NoID)
54

55

56
def test_element_uuid():
57
    element = Element(element_id=UUID())
58
    assert isinstance(element.id, UUID)
59

60

61
def test_text_element_apply_cleaners():
62
    text_element = Text(text="[1] A Textbook on Crocodile Habitats")
63

64
    text_element.apply(partial(clean_prefix, pattern=r"\[\d{1,2}\]"))
65
    assert str(text_element) == "A Textbook on Crocodile Habitats"
66

67

68
def test_text_element_apply_multiple_cleaners():
69
    cleaners = [
70
        partial(clean_prefix, pattern=r"\[\d{1,2}\]"),
71
        partial(translate_text, target_lang="ru"),
72
    ]
73
    text_element = Text(text="[1] A Textbook on Crocodile Habitats")
74
    text_element.apply(*cleaners)
75
    assert str(text_element) == "Учебник по крокодильным средам обитания"
76

77

78
def test_apply_raises_if_func_does_not_produce_string():
79
    def bad_cleaner(s: str):
80
        return 1
81

82
    text_element = Text(text="[1] A Textbook on Crocodile Habitats")
83

84
    with pytest.raises(ValueError, match="Cleaner produced a non-string output."):
85
        text_element.apply(bad_cleaner)  # pyright: ignore[reportGeneralTypeIssues]
86

87

88
@pytest.mark.parametrize(
89
    ("coordinates", "orientation1", "orientation2", "expected_coords"),
90
    [
91
        (
92
            ((1, 2), (1, 4), (3, 4), (3, 2)),
93
            Orientation.CARTESIAN,
94
            Orientation.CARTESIAN,
95
            ((10, 20), (10, 40), (30, 40), (30, 20)),
96
        ),
97
        (
98
            ((1, 2), (1, 4), (3, 4), (3, 2)),
99
            Orientation.CARTESIAN,
100
            Orientation.SCREEN,
101
            ((10, 1980), (10, 1960), (30, 1960), (30, 1980)),
102
        ),
103
        (
104
            ((1, 2), (1, 4), (3, 4), (3, 2)),
105
            Orientation.SCREEN,
106
            Orientation.CARTESIAN,
107
            ((10, 1980), (10, 1960), (30, 1960), (30, 1980)),
108
        ),
109
        (
110
            ((1, 2), (1, 4), (3, 4), (3, 2)),
111
            Orientation.SCREEN,
112
            Orientation.SCREEN,
113
            ((10, 20), (10, 40), (30, 40), (30, 20)),
114
        ),
115
    ],
116
)
117
def test_convert_coordinates_to_new_system(
118
    coordinates: Points,
119
    orientation1: Orientation,
120
    orientation2: Orientation,
121
    expected_coords: Points,
122
):
123
    coord1 = CoordinateSystem(100, 200)
124
    coord1.orientation = orientation1
125
    coord2 = CoordinateSystem(1000, 2000)
126
    coord2.orientation = orientation2
127
    element = Element(coordinates=coordinates, coordinate_system=coord1)
128

129
    new_coords = element.convert_coordinates_to_new_system(coord2)
130

131
    assert new_coords is not None
132
    for new_coord, expected in zip(new_coords, expected_coords):
133
        assert new_coord == pytest.approx(expected)  # pyright: ignore[reportUnknownMemberType]
134
    element.convert_coordinates_to_new_system(coord2, in_place=True)
135
    assert element.metadata.coordinates is not None
136
    assert element.metadata.coordinates.points is not None
137
    for new_coord, expected in zip(element.metadata.coordinates.points, expected_coords):
138
        assert new_coord == pytest.approx(expected)  # pyright: ignore[reportUnknownMemberType]
139
    assert element.metadata.coordinates.system == coord2
140

141

142
def test_convert_coordinate_to_new_system_none():
143
    element = Element(coordinates=None, coordinate_system=None)
144
    coord = CoordinateSystem(100, 200)
145
    coord.orientation = Orientation.SCREEN
146
    assert element.convert_coordinates_to_new_system(coord) is None
147

148

149
def test_element_constructor_coordinates_all_present():
150
    coordinates = ((1, 2), (1, 4), (3, 4), (3, 2))
151
    coordinate_system = RelativeCoordinateSystem()
152
    element = Element(coordinates=coordinates, coordinate_system=coordinate_system)
153
    expected_coordinates_metadata = CoordinatesMetadata(
154
        points=coordinates,
155
        system=coordinate_system,
156
    )
157
    assert element.metadata.coordinates == expected_coordinates_metadata
158

159

160
def test_element_constructor_coordinates_points_absent():
161
    with pytest.raises(ValueError) as exc_info:
162
        Element(coordinate_system=RelativeCoordinateSystem())
163
    assert (
164
        str(exc_info.value)
165
        == "Coordinates points should not exist without coordinates system and vice versa."
166
    )
167

168

169
def test_element_constructor_coordinates_system_absent():
170
    with pytest.raises(ValueError) as exc_info:
171
        Element(coordinates=((1, 2), (1, 4), (3, 4), (3, 2)))
172
    assert (
173
        str(exc_info.value)
174
        == "Coordinates points should not exist without coordinates system and vice versa."
175
    )
176

177

178
def test_coordinate_metadata_serdes():
179
    coordinates = ((1, 2), (1, 4), (3, 4), (3, 2))
180
    coordinate_system = RelativeCoordinateSystem()
181
    coordinates_metadata = CoordinatesMetadata(points=coordinates, system=coordinate_system)
182
    expected_schema = {
183
        "layout_height": 1,
184
        "layout_width": 1,
185
        "points": ((1, 2), (1, 4), (3, 4), (3, 2)),
186
        "system": "RelativeCoordinateSystem",
187
    }
188
    coordinates_metadata_dict = coordinates_metadata.to_dict()
189
    assert coordinates_metadata_dict == expected_schema
190
    assert CoordinatesMetadata.from_dict(coordinates_metadata_dict) == coordinates_metadata
191

192

193
def test_element_to_dict():
194
    coordinates = ((1, 2), (1, 4), (3, 4), (3, 2))
195
    coordinate_system = RelativeCoordinateSystem()
196
    element = Element(
197
        element_id="awt32t1",
198
        coordinates=coordinates,
199
        coordinate_system=coordinate_system,
200
    )
201

202
    assert element.to_dict() == {
203
        "metadata": {
204
            "coordinates": {
205
                "layout_height": 1,
206
                "layout_width": 1,
207
                "points": ((1, 2), (1, 4), (3, 4), (3, 2)),
208
                "system": "RelativeCoordinateSystem",
209
            },
210
        },
211
        "type": None,
212
        "text": "",
213
        "element_id": "awt32t1",
214
    }
215

216

217
def test_regex_metadata_round_trips_through_JSON():
218
    """metadata.regex_metadata should appear at full depth in JSON."""
219
    regex_metadata = {
220
        "mail-stop": [RegexMetadata(text="MS-107", start=18, end=24)],
221
        "version": [
222
            RegexMetadata(text="current=v1.7.2", start=7, end=21),
223
            RegexMetadata(text="supersedes=v1.7.2", start=22, end=40),
224
        ],
225
    }
226
    metadata = ElementMetadata(regex_metadata=regex_metadata)
227

228
    metadata_json = json.dumps(metadata.to_dict())
229
    deserialized_metadata = ElementMetadata.from_dict(json.loads(metadata_json))
230
    reserialized_metadata_json = json.dumps(deserialized_metadata.to_dict())
231

232
    assert reserialized_metadata_json == metadata_json
233

234

235
class DescribeElementMetadata:
236
    """Unit-test suite for `unstructured.documents.elements.ElementMetadata`."""
237

238
    # -- It can be constructed with known keyword arguments. In particular, including a non-known
239
    # -- keyword argument produces a type-error at development time and raises an exception at
240
    # -- runtime. This catches typos before they reach production.
241

242
    def it_detects_unknown_constructor_args_at_both_development_time_and_runtime(self):
243
        with pytest.raises(TypeError, match="got an unexpected keyword argument 'file_name'"):
244
            ElementMetadata(file_name="memo.docx")  # pyright: ignore[reportGeneralTypeIssues]
245

246
    @pytest.mark.parametrize(
247
        "file_path",
248
        [
249
            pathlib.Path("documents/docx") / "memos" / "memo-2023-11-10.docx",
250
            "documents/docx/memos/memo-2023-11-10.docx",
251
        ],
252
    )
253
    def it_accommodates_either_a_pathlib_Path_or_str_for_its_filename_arg(
254
        self, file_path: pathlib.Path | str
255
    ):
256
        meta = ElementMetadata(filename=file_path)
257

258
        assert meta.file_directory == "documents/docx/memos"
259
        assert meta.filename == "memo-2023-11-10.docx"
260

261
    def it_leaves_both_filename_and_file_directory_None_when_neither_is_specified(self):
262
        meta = ElementMetadata()
263

264
        assert meta.file_directory is None
265
        assert meta.filename is None
266

267
    @pytest.mark.parametrize("file_path", [pathlib.Path("memo.docx"), "memo.docx"])
268
    def and_it_leaves_file_directory_None_when_not_specified_and_filename_is_not_a_path(
269
        self, file_path: pathlib.Path | str
270
    ):
271
        meta = ElementMetadata(filename=file_path)
272

273
        assert meta.file_directory is None
274
        assert meta.filename == "memo.docx"
275

276
    def and_it_splits_off_directory_path_from_its_filename_arg_when_it_is_a_file_path(self):
277
        meta = ElementMetadata(filename="documents/docx/memo-2023-11-11.docx")
278

279
        assert meta.file_directory == "documents/docx"
280
        assert meta.filename == "memo-2023-11-11.docx"
281

282
    def but_it_prefers_a_specified_file_directory_when_filename_also_contains_a_path(self):
283
        meta = ElementMetadata(filename="tmp/staging/memo.docx", file_directory="documents/docx")
284

285
        assert meta.file_directory == "documents/docx"
286
        assert meta.filename == "memo.docx"
287

288
    # -- It knows the types of its known members so type-checking support is available. --
289

290
    def it_knows_the_types_of_its_known_members_so_type_checking_support_is_available(self):
291
        ElementMetadata(
292
            category_depth="2",  # pyright: ignore[reportGeneralTypeIssues]
293
            file_directory=True,  # pyright: ignore[reportGeneralTypeIssues]
294
            text_as_html=42,  # pyright: ignore[reportGeneralTypeIssues]
295
        )
296
        # -- it does not check types at runtime however (choosing to avoid validation overhead) --
297

298
    # -- It only stores a field's value when it is not None. --
299

300
    def it_returns_the_value_of_an_attribute_it_has(self):
301
        meta = ElementMetadata(url="https://google.com")
302
        assert "url" in meta.__dict__
303
        assert meta.url == "https://google.com"
304

305
    def and_it_returns_None_for_a_known_attribute_it_does_not_have(self):
306
        meta = ElementMetadata()
307
        assert "url" not in meta.__dict__
308
        assert meta.url is None
309

310
    def but_it_raises_AttributeError_for_an_unknown_attribute_it_does_not_have(self):
311
        meta = ElementMetadata()
312
        assert "coefficient" not in meta.__dict__
313
        with pytest.raises(AttributeError, match="object has no attribute 'coefficient'"):
314
            meta.coefficient
315

316
    def it_stores_a_non_None_field_value_when_assigned(self):
317
        meta = ElementMetadata()
318
        assert "file_directory" not in meta.__dict__
319
        meta.file_directory = "tmp/"
320
        assert "file_directory" in meta.__dict__
321
        assert meta.file_directory == "tmp/"
322

323
    def it_removes_a_field_when_None_is_assigned_to_it(self):
324
        meta = ElementMetadata(file_directory="tmp/")
325
        assert "file_directory" in meta.__dict__
326
        assert meta.file_directory == "tmp/"
327

328
        meta.file_directory = None
329
        assert "file_directory" not in meta.__dict__
330
        assert meta.file_directory is None
331

332
    # -- It can serialize itself to a dict -------------------------------------------------------
333

334
    def it_can_serialize_itself_to_a_dict(self):
335
        meta = ElementMetadata(
336
            category_depth=1,
337
            file_directory="tmp/",
338
            page_number=2,
339
            text_as_html="<table></table>",
340
            url="https://google.com",
341
        )
342
        assert meta.to_dict() == {
343
            "category_depth": 1,
344
            "file_directory": "tmp/",
345
            "page_number": 2,
346
            "text_as_html": "<table></table>",
347
            "url": "https://google.com",
348
        }
349

350
    def and_it_serializes_a_coordinates_sub_object_to_a_dict_when_it_is_present(self):
351
        meta = ElementMetadata(
352
            category_depth=1,
353
            coordinates=CoordinatesMetadata(
354
                points=((2, 2), (1, 4), (3, 4), (3, 2)),
355
                system=RelativeCoordinateSystem(),
356
            ),
357
            page_number=2,
358
        )
359
        assert meta.to_dict() == {
360
            "category_depth": 1,
361
            "coordinates": {
362
                "layout_height": 1,
363
                "layout_width": 1,
364
                "points": ((2, 2), (1, 4), (3, 4), (3, 2)),
365
                "system": "RelativeCoordinateSystem",
366
            },
367
            "page_number": 2,
368
        }
369

370
    def and_it_serializes_a_data_source_sub_object_to_a_dict_when_it_is_present(self):
371
        meta = ElementMetadata(
372
            category_depth=1,
373
            data_source=DataSourceMetadata(
374
                url="https://www.nih.gov/about-nih/who-we-are/nih-director",
375
                date_created="2023-11-09",
376
            ),
377
            page_number=2,
378
        )
379
        assert meta.to_dict() == {
380
            "category_depth": 1,
381
            "data_source": {
382
                "url": "https://www.nih.gov/about-nih/who-we-are/nih-director",
383
                "date_created": "2023-11-09",
384
            },
385
            "page_number": 2,
386
        }
387

388
    def but_unlike_in_ElementMetadata_unknown_fields_in_sub_objects_are_ignored(self):
389
        """Metadata sub-objects ignore fields they do not explicitly define.
390

391
        This is _not_ the case for ElementMetadata itself where an non-known field is welcomed as a
392
        user-defined ad-hoc metadata field.
393
        """
394
        element_metadata = {
395
            "new_field": "hello",
396
            "data_source": {
397
                "new_field": "world",
398
            },
399
            "coordinates": {
400
                "new_field": "foo",
401
            },
402
        }
403

404
        metadata = ElementMetadata.from_dict(element_metadata)
405
        metadata_dict = metadata.to_dict()
406

407
        assert "new_field" in metadata_dict
408
        assert "new_field" not in metadata_dict["coordinates"]
409
        assert "new_field" not in metadata_dict["data_source"]
410

411
    # -- It can deserialize itself from a dict ---------------------------------------------------
412

413
    def it_can_deserialize_itself_from_a_dict(self):
414
        meta_dict = {
415
            "category_depth": 1,
416
            "coefficient": 0.58,
417
            "coordinates": {
418
                "layout_height": 4,
419
                "layout_width": 2,
420
                "points": ((1, 2), (1, 4), (3, 4), (3, 2)),
421
                "system": "RelativeCoordinateSystem",
422
            },
423
            "data_source": {
424
                "url": "https://www.nih.gov/about-nih/who-we-are/nih-director",
425
                "date_created": "2023-11-09",
426
            },
427
            "languages": ["eng"],
428
        }
429

430
        meta = ElementMetadata.from_dict(meta_dict)
431

432
        # -- known fields present in dict are present in meta --
433
        assert meta.category_depth == 1
434

435
        # -- known sub-object fields present in dict are present in meta --
436
        assert meta.coordinates == CoordinatesMetadata(
437
            points=((1, 2), (1, 4), (3, 4), (3, 2)),
438
            system=RelativeCoordinateSystem(),
439
        )
440
        assert meta.data_source == DataSourceMetadata(
441
            url="https://www.nih.gov/about-nih/who-we-are/nih-director",
442
            date_created="2023-11-09",
443
        )
444

445
        # -- known fields absent from dict report None but are not present in meta --
446
        assert meta.file_directory is None
447
        assert "file_directory" not in meta.__dict__
448

449
        # -- non-known fields present in dict are present in meta (we have no way to tell whether
450
        # -- they are "ad-hoc" or not because we lack indication of user-intent)
451
        assert meta.coefficient == 0.58
452

453
        # -- ad-hoc fields absent from dict raise on attempted access --
454
        with pytest.raises(AttributeError, match="ntMetadata' object has no attribute 'quotient'"):
455
            meta.quotient
456

457
        # -- but that can be worked around by end-user --
458
        assert (meta.quotient if hasattr(meta, "quotient") else None) is None
459

460
        # -- mutating a mutable (collection) field does not affect the original value --
461
        assert isinstance(meta.languages, list)
462
        assert meta.languages == ["eng"]
463
        meta.languages.append("spa")
464
        assert meta.languages == ["eng", "spa"]
465
        assert meta_dict["languages"] == ["eng"]
466

467
    # -- It allows downstream users to add an arbitrary new member by assignment. ----------------
468

469
    def it_allows_an_end_user_to_add_an_arbitrary_field(self):
470
        meta = ElementMetadata()
471
        meta.foobar = 7
472
        assert "foobar" in meta.__dict__
473
        assert meta.foobar == 7
474

475
    def and_fields_so_added_appear_in_the_metadata_JSON(self):
476
        meta = ElementMetadata()
477
        meta.foobar = 7
478
        assert meta.to_dict() == {"foobar": 7}
479

480
    def and_it_removes_an_end_user_field_when_it_is_assigned_None(self):
481
        meta = ElementMetadata()
482
        meta.foobar = 7
483
        assert "foobar" in meta.__dict__
484
        meta.foobar = None
485
        assert "foobar" not in meta.__dict__
486
        with pytest.raises(
487
            AttributeError, match="'ElementMetadata' object has no attribute 'foobar'"
488
        ):
489
            meta.foobar
490

491
    # -- It can update itself from another instance ----------------------------------------------
492

493
    def it_can_update_itself_from_another_instance(self):
494
        meta = ElementMetadata(category_depth=1, page_number=1)
495
        meta.coefficient = 0.58
496
        meta.stem_length = 18
497
        other = ElementMetadata(file_directory="tmp/", page_number=2)
498
        other.quotient = 1.4
499
        other.stem_length = 20
500

501
        meta.update(other)
502

503
        # -- known-fields present on self but not other are unchanged --
504
        assert meta.category_depth == 1
505
        # -- known-fields present on other but not self are added --
506
        assert meta.file_directory == "tmp/"
507
        # -- known-fields present on both self and other are updated --
508
        assert meta.page_number == 2
509
        # -- ad-hoc-fields present on self but not other are unchanged --
510
        assert meta.coefficient == 0.58
511
        # -- ad-hoc-fields present on other but not self are added --
512
        assert meta.quotient == 1.4
513
        # -- ad-hoc-fields present on both self and other are updated --
514
        assert meta.stem_length == 20
515
        # -- other is left unchanged --
516
        assert other.category_depth is None
517
        assert other.file_directory == "tmp/"
518
        assert other.page_number == 2
519
        assert other.text_as_html is None
520
        assert other.url is None
521
        assert other.quotient == 1.4
522
        assert other.stem_length == 20
523
        with pytest.raises(AttributeError, match="etadata' object has no attribute 'coefficient'"):
524
            other.coefficient
525

526
    def but_it_raises_on_attempt_to_update_from_a_non_ElementMetadata_object(self):
527
        meta = ElementMetadata()
528
        with pytest.raises(ValueError, match=r"ate\(\)' must be an instance of 'ElementMetadata'"):
529
            meta.update({"coefficient": "0.56"})  # pyright: ignore[reportGeneralTypeIssues]
530

531
    # -- It knows when it is equal to another instance -------------------------------------------
532

533
    def it_is_equal_to_another_instance_with_the_same_known_field_values(self):
534
        meta = ElementMetadata(
535
            category_depth=1,
536
            coordinates=CoordinatesMetadata(
537
                points=((1, 2), (1, 4), (3, 4), (3, 2)),
538
                system=RelativeCoordinateSystem(),
539
            ),
540
            data_source=DataSourceMetadata(
541
                url="https://www.nih.gov/about-nih/who-we-are/nih-director",
542
                date_created="2023-11-08",
543
            ),
544
            file_directory="tmp/",
545
            languages=["eng"],
546
            page_number=2,
547
            text_as_html="<table></table>",
548
            url="https://google.com",
549
        )
550
        assert meta == ElementMetadata(
551
            category_depth=1,
552
            coordinates=CoordinatesMetadata(
553
                points=((1, 2), (1, 4), (3, 4), (3, 2)),
554
                system=RelativeCoordinateSystem(),
555
            ),
556
            data_source=DataSourceMetadata(
557
                url="https://www.nih.gov/about-nih/who-we-are/nih-director",
558
                date_created="2023-11-08",
559
            ),
560
            file_directory="tmp/",
561
            languages=["eng"],
562
            page_number=2,
563
            text_as_html="<table></table>",
564
            url="https://google.com",
565
        )
566

567
    def but_it_is_never_equal_to_a_non_ElementMetadata_object(self):
568
        class NotElementMetadata:
569
            pass
570

571
        meta = ElementMetadata()
572
        other = NotElementMetadata()
573

574
        # -- all the "fields" are the same --
575
        assert meta.__dict__ == other.__dict__
576
        # -- but it is rejected solely because its type is different --
577
        assert meta != other
578

579
    def it_is_equal_to_another_instance_with_the_same_ad_hoc_field_values(self):
580
        meta = ElementMetadata(category_depth=1)
581
        meta.coefficient = 0.58
582
        other = ElementMetadata(category_depth=1)
583
        other.coefficient = 0.58
584

585
        assert meta == other
586

587
    def but_it_is_not_equal_to_an_instance_with_ad_hoc_fields_that_differ(self):
588
        meta = ElementMetadata(category_depth=1)
589
        meta.coefficient = 0.58
590
        other = ElementMetadata(category_depth=1)
591
        other.coefficient = 0.72
592

593
        assert meta != other
594

595
    def it_is_not_equal_when_a_list_field_contains_different_items(self):
596
        meta = ElementMetadata(languages=["eng"])
597
        assert meta != ElementMetadata(languages=["eng", "spa"])
598

599
    def and_it_is_not_equal_when_the_coordinates_sub_object_field_differs(self):
600
        meta = ElementMetadata(
601
            coordinates=CoordinatesMetadata(
602
                points=((1, 2), (1, 4), (3, 4), (3, 2)),
603
                system=RelativeCoordinateSystem(),
604
            )
605
        )
606
        assert meta != ElementMetadata(
607
            coordinates=CoordinatesMetadata(
608
                points=((2, 2), (2, 4), (3, 4), (4, 2)),
609
                system=RelativeCoordinateSystem(),
610
            )
611
        )
612

613
    def and_it_is_not_equal_when_the_data_source_sub_object_field_differs(self):
614
        meta = ElementMetadata(
615
            data_source=DataSourceMetadata(
616
                url="https://www.nih.gov/about-nih/who-we-are/nih-director",
617
                date_created="2023-11-08",
618
            )
619
        )
620
        assert meta != ElementMetadata(
621
            data_source=DataSourceMetadata(
622
                url="https://www.nih.gov/about-nih/who-we-are/nih-director",
623
                date_created="2023-11-09",
624
            )
625
        )
626

627
    # -- There is a consolidation-strategy for all known fields ----------------------------------
628

629
    def it_can_find_the_consolidation_strategy_for_each_of_its_known_fields(self):
630
        metadata = ElementMetadata()
631
        metadata_field_names = sorted(metadata._known_field_names)
632
        consolidation_strategies = ConsolidationStrategy.field_consolidation_strategies()
633

634
        for field_name in metadata_field_names:
635
            assert field_name in consolidation_strategies, (
636
                f"ElementMetadata field `.{field_name}` does not have a consolidation strategy."
637
                f" Add one in `ConsolidationStrategy.field_consolidation_strategies()."
638
            )
639

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.