blob: e024b280863593a694e1aaf964b4845c0bba345b [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from typing import Any
import pytest
from pyiceberg.io import FileIO
from pyiceberg.io.pyarrow import PyArrowFileIO
from pyiceberg.manifest import (
DataFile,
DataFileContent,
FileFormat,
ManifestEntry,
ManifestEntryStatus,
_manifests,
)
@pytest.fixture(autouse=True)
def clear_global_manifests_cache() -> None:
# Clear the global cache before each test
cache_clear = getattr(_manifests, "cache_clear", None)
if callable(cache_clear):
# pyiceberg<=0.10: _manifests was lru_cache-decorated.
cache_clear()
return
# pyiceberg>=0.11: manifests are cached in module-level LRUCache.
from pyiceberg import manifest as manifest_module
manifest_cache = getattr(manifest_module, "_manifest_cache", None)
manifest_cache_lock = getattr(manifest_module, "_manifest_cache_lock", None)
if manifest_cache is None:
return
if manifest_cache_lock is None:
manifest_cache.clear()
return
with manifest_cache_lock:
manifest_cache.clear()
def fetch_manifest_entry(
manifest_entry_file: str, io: FileIO, discard_deleted: bool = True
) -> list[ManifestEntry]:
from pyiceberg_core import manifest
bs = io.new_input(manifest_entry_file).open().read()
manifest = manifest.read_manifest_entries(bs)
# TODO: Don't convert the types
# but this is the easiest for now until we
# have the write part in there as well
def _convert_entry(entry: Any) -> ManifestEntry:
data_file_args = {
"content": DataFileContent(entry.data_file.content),
"file_path": entry.data_file.file_path,
"file_format": FileFormat(entry.data_file.file_format),
"partition": [
p.value() if p is not None else None for p in entry.data_file.partition
],
"record_count": entry.data_file.record_count,
"file_size_in_bytes": entry.data_file.file_size_in_bytes,
"column_sizes": entry.data_file.column_sizes,
"value_counts": entry.data_file.value_counts,
"null_value_counts": entry.data_file.null_value_counts,
"nan_value_counts": entry.data_file.nan_value_counts,
"lower_bounds": entry.data_file.lower_bounds,
"upper_bounds": entry.data_file.upper_bounds,
"key_metadata": entry.data_file.key_metadata,
"split_offsets": entry.data_file.split_offsets,
"equality_ids": entry.data_file.equality_ids,
"sort_order_id": entry.data_file.sort_order_id,
}
data_file = (
DataFile.from_args(**data_file_args)
if hasattr(DataFile, "from_args")
else DataFile(**data_file_args)
)
manifest_entry_args = {
"status": ManifestEntryStatus(entry.status),
"snapshot_id": entry.snapshot_id,
"sequence_number": entry.sequence_number,
"file_sequence_number": entry.file_sequence_number,
"data_file": data_file,
}
return (
ManifestEntry.from_args(**manifest_entry_args)
if hasattr(ManifestEntry, "from_args")
else ManifestEntry(**manifest_entry_args)
)
return [
_convert_entry(entry)
# _inherit_from_manifest(, self)
for entry in manifest.entries()
if not discard_deleted or entry.status != ManifestEntryStatus.DELETED
]
def test_read_manifest_entry(generated_manifest_entry_file: str) -> None:
# manifest = ManifestFile.from_args(
# manifest_path=generated_manifest_entry_file,
# manifest_length=0,
# partition_spec_id=0,
# added_snapshot_id=0,
# sequence_number=0,
# partitions=[],
# )
# manifest_entries = manifest.fetch_manifest_entry(PyArrowFileIO())
manifest_entries = fetch_manifest_entry(
f"file://{generated_manifest_entry_file}", PyArrowFileIO()
)
manifest_entry = manifest_entries[0]
assert manifest_entry.status == ManifestEntryStatus.ADDED
assert manifest_entry.snapshot_id == 8744736658442914487
assert manifest_entry.sequence_number == 0
assert isinstance(manifest_entry.data_file, DataFile)
data_file = manifest_entry.data_file
assert data_file.content == DataFileContent.DATA
assert (
data_file.file_path
== "/home/iceberg/warehouse/nyc/taxis_partitioned/data/VendorID=null/00000-633-d8a4223e-dc97-45a1-86e1-adaba6e8abd7-00001.parquet"
)
assert data_file.file_format == FileFormat.PARQUET
assert repr(data_file.partition) == "[1, None]"
assert data_file.record_count == 19513
assert data_file.file_size_in_bytes == 388872
assert data_file.column_sizes == {
1: 53,
2: 98153,
}
assert data_file.value_counts == {
1: 19513,
2: 19513,
}
assert data_file.null_value_counts == {
1: 19513,
2: 0,
}
assert data_file.nan_value_counts == {}
assert data_file.lower_bounds == {
1: b"\x00\x00\x00\x00",
2: b"\x00\x00\x00\x00\x00\x00\x00\x00",
}
assert data_file.upper_bounds == {
1: b"\x00\x00\xe0\xbf",
2: b"\x00\x00\x00\x00\x00\x00\xe0\xbf",
}
assert data_file.key_metadata is None
assert data_file.split_offsets == [4]
assert data_file.equality_ids is None
assert data_file.sort_order_id == 0