diff --git a/.github/workflows/reuseable_cibuildwheel.yml b/.github/workflows/reuseable_cibuildwheel.yml index f327399..66ee111 100644 --- a/.github/workflows/reuseable_cibuildwheel.yml +++ b/.github/workflows/reuseable_cibuildwheel.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: os: [ubuntu-24.04] # renovate: github-runner - arch: [aarch64, ppc64le, s390x, armv7l, x86_64, i686] + arch: [aarch64, ppc64le, s390x, x86_64, i686] build: [manylinux, musllinux] qemu_arch: [aarch64 ppc64le s390x armv7l] include: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce084e7..6b802f5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -119,7 +119,7 @@ repos: hooks: - id: codespell args: - - -L=lang + - -L=lang,TE,Characts - --check-filenames - --write-changes diff --git a/.vscode/settings.json b/.vscode/settings.json index f8b1473..4e5ad36 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -103,6 +103,11 @@ "numbers": "cpp", "semaphore": "cpp", "codecvt": "cpp", - "source_location": "cpp" + "source_location": "cpp", + "complex": "cpp", + "csignal": "cpp", + "regex": "cpp", + "typeindex": "cpp", + "valarray": "cpp" } } diff --git a/pyproject.toml b/pyproject.toml index 862f167..e1b1a3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ write_to = "src/pybit7z/_version.py" build-verbosity = 1 test-command = "pytest {project}/tests" test-extras = ["test"] -test-skip = ["*-win_arm64", "*-macosx_universal2:arm64"] +test-skip = ["*-win_arm64", "*-macosx_arm64"] [tool.cibuildwheel.linux] before-build = [ @@ -148,6 +148,8 @@ extend-select = [ "PD", # pandas-vet ] ignore = [ + "UP006", # pyupgrade removes f-strings + "UP007", # pyupgrade removes dict comprehensions "PLR09", # Too many <...> "PLR2004", # Magic value used in comparison "ISC001", # Conflicts with formatter @@ -162,7 +164,7 @@ isort.required-imports = ["from __future__ import annotations"] [tool.pylint] py-version = "3.8" -ignore-paths = [".*/_version.py"] +ignore-paths = [".*/_version.py", ".*/*.pyi"] extension-pkg-allow-list = ["pybit7z._core"] reports.output-format = "colorized" similarities.ignore-imports = "yes" diff --git a/src/_core/CMakeLists.txt b/src/_core/CMakeLists.txt index 1ceb9f1..df7ad6d 100644 --- a/src/_core/CMakeLists.txt +++ b/src/_core/CMakeLists.txt @@ -28,7 +28,7 @@ target_include_interface_directories( ${target_name_internal} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/git_version) -if(VCPKG_TARGET_ARCHITECTURE MATCHES [[64]]) +if(NOT VCPKG_TARGET_ARCHITECTURE MATCHES [[86]]) set(bit7z_suffix 64) endif() @@ -39,8 +39,7 @@ target_link_libraries(${target_name_internal} # parts that need to be verified and tested in the target target_name_internal set(target_name _core) -pybind11_add_module(${target_name} MODULE ${internal_srcs} ${internal_hdrs} - src/pybind.cpp) +pybind11_add_module(${target_name} MODULE src/pybind.cpp) warn_target(${target_name}) harden_target(${target_name}) sanitize_target(${target_name}) diff --git a/src/_core/include/_core.hpp b/src/_core/include/_core.hpp index 22f85d8..bf3b3e3 100644 --- a/src/_core/include/_core.hpp +++ b/src/_core/include/_core.hpp @@ -1,4 +1,3 @@ #pragma once #include "_version.hpp" -#include "pybit7z.hpp" diff --git a/src/_core/include/pybit7z.hpp b/src/_core/include/pybit7z.hpp index 82dc938..31452cd 100644 --- a/src/_core/include/pybit7z.hpp +++ b/src/_core/include/pybit7z.hpp @@ -3,13 +3,19 @@ #include namespace _core { -#ifdef WIN32 -#if defined(_MSC_VER) -constexpr auto default_lib7zip = "7zip.dll"; -#else -constexpr auto default_lib7zip = "lib7zip.dll"; -#endif -#else -constexpr auto default_lib7zip = "lib7zip.so"; -#endif + +std::string& default_library_path(); + +class Bit7zipSingleton { +public: + static const bit7z::Bit7zLibrary& getInstance(); + +private: + Bit7zipSingleton() = default; + ~Bit7zipSingleton() = default; + + Bit7zipSingleton(const Bit7zipSingleton&) = delete; + Bit7zipSingleton& operator=(const Bit7zipSingleton&) = delete; +}; + } // namespace _core diff --git a/src/_core/src/pybind.cpp b/src/_core/src/pybind.cpp index cc54894..3d1dafa 100644 --- a/src/_core/src/pybind.cpp +++ b/src/_core/src/pybind.cpp @@ -1,19 +1,1648 @@ +#include +#include #include +#include +#include #include "_core.hpp" +#include "pybit7z.hpp" namespace py = pybind11; +using namespace py::literals; PYBIND11_MODULE(_core, m) { m.doc() = R"pbdoc( Pybind11 _core plugin ----------------------- .. currentmodule:: _core - )pbdoc"; + )pbdoc"; m.def("version", []() { return _core::ProjectVersion(); }, R"pbdoc( - The _core plugin version. - )pbdoc"); + The _core plugin version. + )pbdoc"); - m.attr("default_lib7zip") = _core::default_lib7zip; + m.def( + "set_lib7zip_path", + [](const std::string &path = std::string()) { + if (path.empty()) { + return _core::default_library_path(); + } + _core::default_library_path() = path; + return _core::default_library_path(); + }, + py::arg("lib7zip_path") = std::string(), + py::doc(R"pbdoc(Configure the path to the 7zip library.)pbdoc")); + + m.def( + "set_large_page_mode", + []() { const_cast(_core::Bit7zipSingleton::getInstance()).setLargePageMode(); }, + py::doc(R"pbdoc(Enable large page mode for 7zip library. This can improve performance on some systems.)pbdoc")); + + // Exception handling + py::register_exception(m, "BitException"); + + // CompressionLevel enum bindings + py::enum_(m, "BitCompressionLevel", R"pbdoc(Compression level for 7zip library)pbdoc") + .value("Nothing", bit7z::BitCompressionLevel::None) + .value("Fastest", bit7z::BitCompressionLevel::Fastest) + .value("Fast", bit7z::BitCompressionLevel::Fast) + .value("Normal", bit7z::BitCompressionLevel::Normal) + .value("Max", bit7z::BitCompressionLevel::Max) + .value("Ultra", bit7z::BitCompressionLevel::Ultra) + .export_values(); + + // CompressionMethod enum bindings + py::enum_(m, + "BitCompressionMethod", + R"pbdoc(Compression method by bit7z when creating archives.)pbdoc") + .value("Copy", bit7z::BitCompressionMethod::Copy) + .value("Deflate", bit7z::BitCompressionMethod::Deflate) + .value("Deflate64", bit7z::BitCompressionMethod::Deflate64) + .value("BZip2", bit7z::BitCompressionMethod::BZip2) + .value("Lzma", bit7z::BitCompressionMethod::Lzma) + .value("Lzma2", bit7z::BitCompressionMethod::Lzma2) + .value("Ppmd", bit7z::BitCompressionMethod::Ppmd) + .export_values(); + + // FormatFeatures enum bindings + py::enum_(m, "FormatFeatures", R"pbdoc(Features of a format supported by bit7z)pbdoc") + .value("MultipleFiles", bit7z::FormatFeatures::MultipleFiles, R"pbdoc(Archive supports multiple files.)pbdoc") + .value("SolidArchive", bit7z::FormatFeatures::SolidArchive, R"pbdoc(Archive supports solid mode.)pbdoc") + .value("CompressionLevel", + bit7z::FormatFeatures::CompressionLevel, + R"pbdoc(Archive supports compression level.)pbdoc") + .value("Encryption", bit7z::FormatFeatures::Encryption, R"pbdoc(Archive supports encryption.)pbdoc") + .value("HeaderEncryption", + bit7z::FormatFeatures::HeaderEncryption, + R"pbdoc(Archive supports encrypted headers.)pbdoc") + .value("MultipleMethods", + bit7z::FormatFeatures::MultipleMethods, + R"pbdoc(Archive supports multiple compression methods.)pbdoc"); + + // bit7z:: DeletePolicy enum bindings + py::enum_(m, "DeletePolicy", R"pbdoc(Delete policy for archive items.)pbdoc") + .value("ItemOnly", bit7z::DeletePolicy::ItemOnly) + .value("RecurseDirs", bit7z::DeletePolicy::RecurseDirs) + .export_values(); + + // bit7z::BitInFormat class bindings + py::class_(m, + "BitInFormat", + R"pbdoc(The BitInFormat class specifies an extractable archive format.)pbdoc") + .def("value", &bit7z::BitInFormat::value, py::doc(R"pbdoc(the value of the format in the 7z SDK.)pbdoc")) + .def("__eq__", &bit7z::BitInFormat::operator==) + .def("__ne__", &bit7z::BitInFormat::operator!=); + + // bit7z::BitInOutFormat class bindings + py::class_( + m, + "BitInOutFormat", + R"pbdoc(The BitInOutFormat class specifies a format available for creating new archives and extract old ones.)pbdoc") + .def("extension", + &bit7z::BitInOutFormat::extension, + py::doc(R"pbdoc(the default file extension of the archive format.)pbdoc")) + .def("features", + &bit7z::BitInOutFormat::features, + py::doc(R"pbdoc(the bitset of the features supported by the format.)pbdoc")) + .def("has_feature", + &bit7z::BitInOutFormat::hasFeature, + py::doc(R"pbdoc(Checks if the format has a specific feature (see FormatFeatures enum) +Args: + feature (FormatFeatures): the feature to check +Returns: + bool: a boolean value indicating whether the format has the given feature.)pbdoc")) + .def("default_method", + &bit7z::BitInOutFormat::defaultMethod, + py::doc(R"pbdoc(the default method used for compressing the archive format.)pbdoc")); + + // Expose format constants as module attributes + m.attr("FormatAuto") = py::cast(bit7z::BitFormat::Auto, py::return_value_policy::reference); + m.attr("FormatRar") = py::cast(bit7z::BitFormat::Rar, py::return_value_policy::reference); + m.attr("FormatArj") = py::cast(bit7z::BitFormat::Arj, py::return_value_policy::reference); + m.attr("FormatZ") = py::cast(bit7z::BitFormat::Z, py::return_value_policy::reference); + m.attr("FormatLzh") = py::cast(bit7z::BitFormat::Lzh, py::return_value_policy::reference); + m.attr("FormatCab") = py::cast(bit7z::BitFormat::Cab, py::return_value_policy::reference); + m.attr("FormatNsis") = py::cast(bit7z::BitFormat::Nsis, py::return_value_policy::reference); + m.attr("FormatLzma") = py::cast(bit7z::BitFormat::Lzma, py::return_value_policy::reference); + m.attr("FormatLzma86") = py::cast(bit7z::BitFormat::Lzma86, py::return_value_policy::reference); + m.attr("FormatPpmd") = py::cast(bit7z::BitFormat::Ppmd, py::return_value_policy::reference); + m.attr("FormatVhdx") = py::cast(bit7z::BitFormat::Vhdx, py::return_value_policy::reference); + m.attr("FormatCoff") = py::cast(bit7z::BitFormat::COFF, py::return_value_policy::reference); + m.attr("FormatExt") = py::cast(bit7z::BitFormat::Ext, py::return_value_policy::reference); + m.attr("FormatVmdk") = py::cast(bit7z::BitFormat::VMDK, py::return_value_policy::reference); + m.attr("FormatVdi") = py::cast(bit7z::BitFormat::VDI, py::return_value_policy::reference); + m.attr("FormatQcow") = py::cast(bit7z::BitFormat::QCow, py::return_value_policy::reference); + m.attr("FormatGpt") = py::cast(bit7z::BitFormat::GPT, py::return_value_policy::reference); + m.attr("FormatRar5") = py::cast(bit7z::BitFormat::Rar5, py::return_value_policy::reference); + m.attr("FormatIHex") = py::cast(bit7z::BitFormat::IHex, py::return_value_policy::reference); + m.attr("FormatHxs") = py::cast(bit7z::BitFormat::Hxs, py::return_value_policy::reference); + m.attr("FormatTE") = py::cast(bit7z::BitFormat::TE, py::return_value_policy::reference); + m.attr("FormatUEFIc") = py::cast(bit7z::BitFormat::UEFIc, py::return_value_policy::reference); + m.attr("FormatUEFIs") = py::cast(bit7z::BitFormat::UEFIs, py::return_value_policy::reference); + m.attr("FormatSquashFS") = py::cast(bit7z::BitFormat::SquashFS, py::return_value_policy::reference); + m.attr("FormatCramFS") = py::cast(bit7z::BitFormat::CramFS, py::return_value_policy::reference); + m.attr("FormatAPM") = py::cast(bit7z::BitFormat::APM, py::return_value_policy::reference); + m.attr("FormatMslz") = py::cast(bit7z::BitFormat::Mslz, py::return_value_policy::reference); + m.attr("FormatFlv") = py::cast(bit7z::BitFormat::Flv, py::return_value_policy::reference); + m.attr("FormatSwf") = py::cast(bit7z::BitFormat::Swf, py::return_value_policy::reference); + m.attr("FormatSwfc") = py::cast(bit7z::BitFormat::Swfc, py::return_value_policy::reference); + m.attr("FormatNtfs") = py::cast(bit7z::BitFormat::Ntfs, py::return_value_policy::reference); + m.attr("FormatFat") = py::cast(bit7z::BitFormat::Fat, py::return_value_policy::reference); + m.attr("FormatMbr") = py::cast(bit7z::BitFormat::Mbr, py::return_value_policy::reference); + m.attr("FormatVhd") = py::cast(bit7z::BitFormat::Vhd, py::return_value_policy::reference); + m.attr("FormatPe") = py::cast(bit7z::BitFormat::Pe, py::return_value_policy::reference); + m.attr("FormatElf") = py::cast(bit7z::BitFormat::Elf, py::return_value_policy::reference); + m.attr("FormatMacho") = py::cast(bit7z::BitFormat::Macho, py::return_value_policy::reference); + m.attr("FormatUdf") = py::cast(bit7z::BitFormat::Udf, py::return_value_policy::reference); + m.attr("FormatXar") = py::cast(bit7z::BitFormat::Xar, py::return_value_policy::reference); + m.attr("FormatMub") = py::cast(bit7z::BitFormat::Mub, py::return_value_policy::reference); + m.attr("FormatHfs") = py::cast(bit7z::BitFormat::Hfs, py::return_value_policy::reference); + m.attr("FormatDmg") = py::cast(bit7z::BitFormat::Dmg, py::return_value_policy::reference); + m.attr("FormatCompound") = py::cast(bit7z::BitFormat::Compound, py::return_value_policy::reference); + m.attr("FormatIso") = py::cast(bit7z::BitFormat::Iso, py::return_value_policy::reference); + m.attr("FormatChm") = py::cast(bit7z::BitFormat::Chm, py::return_value_policy::reference); + m.attr("FormatSplit") = py::cast(bit7z::BitFormat::Split, py::return_value_policy::reference); + m.attr("FormatRpm") = py::cast(bit7z::BitFormat::Rpm, py::return_value_policy::reference); + m.attr("FormatDeb") = py::cast(bit7z::BitFormat::Deb, py::return_value_policy::reference); + m.attr("FormatCpio") = py::cast(bit7z::BitFormat::Cpio, py::return_value_policy::reference); + m.attr("FormatZip") = py::cast(bit7z::BitFormat::Zip, py::return_value_policy::reference); + m.attr("FormatBZip2") = py::cast(bit7z::BitFormat::BZip2, py::return_value_policy::reference); + m.attr("FormatSevenZip") = py::cast(bit7z::BitFormat::SevenZip, py::return_value_policy::reference); + m.attr("FormatXz") = py::cast(bit7z::BitFormat::Xz, py::return_value_policy::reference); + m.attr("FormatWim") = py::cast(bit7z::BitFormat::Wim, py::return_value_policy::reference); + m.attr("FormatTar") = py::cast(bit7z::BitFormat::Tar, py::return_value_policy::reference); + m.attr("FormatGZip") = py::cast(bit7z::BitFormat::GZip, py::return_value_policy::reference); + + // BitProperty enum bindings + py::enum_( + m, + "BitProperty", + R"pbdoc(The BitProperty enum represents the archive/item properties that 7-zip can read or write.)pbdoc") + .value("NoProperty", bit7z::BitProperty::NoProperty) + .value("MainSubfile", bit7z::BitProperty::MainSubfile) + .value("HandlerItemIndex", bit7z::BitProperty::HandlerItemIndex) + .value("Path", bit7z::BitProperty::Path) + .value("Name", bit7z::BitProperty::Name) + .value("Extension", bit7z::BitProperty::Extension) + .value("IsDir", bit7z::BitProperty::IsDir) + .value("Size", bit7z::BitProperty::Size) + .value("PackSize", bit7z::BitProperty::PackSize) + .value("Attrib", bit7z::BitProperty::Attrib) + .value("CTime", bit7z::BitProperty::CTime) + .value("ATime", bit7z::BitProperty::ATime) + .value("MTime", bit7z::BitProperty::MTime) + .value("Solid", bit7z::BitProperty::Solid) + .value("Commented", bit7z::BitProperty::Commented) + .value("Encrypted", bit7z::BitProperty::Encrypted) + .value("SplitBefore", bit7z::BitProperty::SplitBefore) + .value("SplitAfter", bit7z::BitProperty::SplitAfter) + .value("DictionarySize", bit7z::BitProperty::DictionarySize) + .value("CRC", bit7z::BitProperty::CRC) + .value("Type", bit7z::BitProperty::Type) + .value("IsAnti", bit7z::BitProperty::IsAnti) + .value("Method", bit7z::BitProperty::Method) + .value("HostOS", bit7z::BitProperty::HostOS) + .value("FileSystem", bit7z::BitProperty::FileSystem) + .value("User", bit7z::BitProperty::User) + .value("Group", bit7z::BitProperty::Group) + .value("Block", bit7z::BitProperty::Block) + .value("Comment", bit7z::BitProperty::Comment) + .value("Position", bit7z::BitProperty::Position) + .value("Prefix", bit7z::BitProperty::Prefix) + .value("NumSubDirs", bit7z::BitProperty::NumSubDirs) + .value("NumSubFiles", bit7z::BitProperty::NumSubFiles) + .value("UnpackVer", bit7z::BitProperty::UnpackVer) + .value("Volume", bit7z::BitProperty::Volume) + .value("IsVolume", bit7z::BitProperty::IsVolume) + .value("Offset", bit7z::BitProperty::Offset) + .value("Links", bit7z::BitProperty::Links) + .value("NumBlocks", bit7z::BitProperty::NumBlocks) + .value("NumVolumes", bit7z::BitProperty::NumVolumes) + .value("TimeType", bit7z::BitProperty::TimeType) + .value("Bit64", bit7z::BitProperty::Bit64) + .value("BigEndian", bit7z::BitProperty::BigEndian) + .value("Cpu", bit7z::BitProperty::Cpu) + .value("PhySize", bit7z::BitProperty::PhySize) + .value("HeadersSize", bit7z::BitProperty::HeadersSize) + .value("Checksum", bit7z::BitProperty::Checksum) + .value("Characters", bit7z::BitProperty::Characts) + .value("Va", bit7z::BitProperty::Va) + .value("Id", bit7z::BitProperty::Id) + .value("ShortName", bit7z::BitProperty::ShortName) + .value("CreatorApp", bit7z::BitProperty::CreatorApp) + .value("SectorSize", bit7z::BitProperty::SectorSize) + .value("PosixAttrib", bit7z::BitProperty::PosixAttrib) + .value("SymLink", bit7z::BitProperty::SymLink) + .value("Error", bit7z::BitProperty::Error) + .value("TotalSize", bit7z::BitProperty::TotalSize) + .value("FreeSpace", bit7z::BitProperty::FreeSpace) + .value("ClusterSize", bit7z::BitProperty::ClusterSize) + .value("VolumeName", bit7z::BitProperty::VolumeName) + .value("LocalName", bit7z::BitProperty::LocalName) + .value("Provider", bit7z::BitProperty::Provider) + .value("NtSecure", bit7z::BitProperty::NtSecure) + .value("IsAltStream", bit7z::BitProperty::IsAltStream) + .value("IsAux", bit7z::BitProperty::IsAux) + .value("IsDeleted", bit7z::BitProperty::IsDeleted) + .value("IsTree", bit7z::BitProperty::IsTree) + .value("Sha1", bit7z::BitProperty::Sha1) + .value("Sha256", bit7z::BitProperty::Sha256) + .value("ErrorType", bit7z::BitProperty::ErrorType) + .value("NumErrors", bit7z::BitProperty::NumErrors) + .value("ErrorFlags", bit7z::BitProperty::ErrorFlags) + .value("WarningFlags", bit7z::BitProperty::WarningFlags) + .value("Warning", bit7z::BitProperty::Warning) + .value("NumStreams", bit7z::BitProperty::NumStreams) + .value("NumAltStreams", bit7z::BitProperty::NumAltStreams) + .value("AltStreamsSize", bit7z::BitProperty::AltStreamsSize) + .value("VirtualSize", bit7z::BitProperty::VirtualSize) + .value("UnpackSize", bit7z::BitProperty::UnpackSize) + .value("TotalPhySize", bit7z::BitProperty::TotalPhySize) + .value("VolumeIndex", bit7z::BitProperty::VolumeIndex) + .value("SubType", bit7z::BitProperty::SubType) + .value("ShortComment", bit7z::BitProperty::ShortComment) + .value("CodePage", bit7z::BitProperty::CodePage) + .value("IsNotArcType", bit7z::BitProperty::IsNotArcType) + .value("PhySizeCantBeDetected", bit7z::BitProperty::PhySizeCantBeDetected) + .value("ZerosTailIsAllowed", bit7z::BitProperty::ZerosTailIsAllowed) + .value("TailSize", bit7z::BitProperty::TailSize) + .value("EmbeddedStubSize", bit7z::BitProperty::EmbeddedStubSize) + .value("NtReparse", bit7z::BitProperty::NtReparse) + .value("HardLink", bit7z::BitProperty::HardLink) + .value("INode", bit7z::BitProperty::INode) + .value("StreamId", bit7z::BitProperty::StreamId) + .value("ReadOnly", bit7z::BitProperty::ReadOnly) + .value("OutName", bit7z::BitProperty::OutName) + .value("CopyLink", bit7z::BitProperty::CopyLink) + .export_values(); + + // bit7z::BitPropVariantType enum bindings + + py::enum_( + m, + "BitPropVariantType", + R"pbdoc(The BitPropVariantType enum represents the possible types that a BitPropVariant can store.)pbdoc") + .value("Empty", bit7z::BitPropVariantType::Empty) + .value("Bool", bit7z::BitPropVariantType::Bool) + .value("String", bit7z::BitPropVariantType::String) + .value("UInt8", bit7z::BitPropVariantType::UInt8) + .value("UInt16", bit7z::BitPropVariantType::UInt16) + .value("UInt32", bit7z::BitPropVariantType::UInt32) + .value("UInt64", bit7z::BitPropVariantType::UInt64) + .value("Int8", bit7z::BitPropVariantType::Int8) + .value("Int16", bit7z::BitPropVariantType::Int16) + .value("Int32", bit7z::BitPropVariantType::Int32) + .value("Int64", bit7z::BitPropVariantType::Int64) + .value("FileTime", bit7z::BitPropVariantType::FileTime) + .export_values(); + + py::class_( + m, + "BitPropVariant", + R"pbdoc(The BitPropVariant struct is a light extension to the WinAPI PROPVARIANT struct providing useful getters.)pbdoc") + .def(py::init<>()) + .def(py::init(), py::arg("value")) + .def(py::init(), py::arg("value")) + .def("get_string", &bit7z::BitPropVariant::getString) + .def("get_native_string", &bit7z::BitPropVariant::getNativeString) + .def("get_uint64", &bit7z::BitPropVariant::getUInt64) + .def("get_int64", &bit7z::BitPropVariant::getInt64) + .def("get_bool", &bit7z::BitPropVariant::getBool) + .def("get_file_time", &bit7z::BitPropVariant::getTimePoint) + .def("is_string", &bit7z::BitPropVariant::isString) + .def("is_bool", &bit7z::BitPropVariant::isBool) + .def("is_int8", &bit7z::BitPropVariant::isInt8) + .def("is_int32", &bit7z::BitPropVariant::isInt32) + .def("is_int16", &bit7z::BitPropVariant::isInt16) + .def("is_int64", &bit7z::BitPropVariant::isInt64) + .def("is_uint8", &bit7z::BitPropVariant::isUInt8) + .def("is_uint16", &bit7z::BitPropVariant::isUInt16) + .def("is_uint32", &bit7z::BitPropVariant::isUInt32) + .def("is_uint64", &bit7z::BitPropVariant::isUInt64) + .def("is_file_time", &bit7z::BitPropVariant::isFileTime) + .def("type", &bit7z::BitPropVariant::type, py::doc(R"pbdoc(Returns the type of the variant.)pbdoc")) + .def("clear", &bit7z::BitPropVariant::clear, py::doc(R"pbdoc(Clears the variant.)pbdoc")); + + // bit7z::BitGenericItem class bindings + py::class_( + m, + "BitGenericItem", + + R"pbdoc(The BitGenericItem interface class represents a generic item (either inside or outside an archive).)pbdoc") + .def("is_dir", &bit7z::BitGenericItem::isDir) + .def("size", &bit7z::BitGenericItem::size, py::doc(R"pbdoc(the uncompressed size of the item.)pbdoc")) + .def( + "name", + &bit7z::BitGenericItem::name, + py::doc( + R"pbdoc(the name of the item, if available or inferable from the path, or an empty string otherwise.)pbdoc")) + .def("path", &bit7z::BitGenericItem::path, py::doc(R"pbdoc(the path of the item.)pbdoc")) + .def("attributes", &bit7z::BitGenericItem::attributes, py::doc(R"pbdoc(the item attributes.)pbdoc")); + + // bit7z::BitArchiveItem class bindings + py::class_( + m, + "BitArchiveItem", + R"pbdoc(The BitArchiveItem class represents a generic item inside an archive.)pbdoc") + .def("index", &bit7z::BitArchiveItem::index, py::doc(R"pbdoc(the index of the item in the archive.)pbdoc")) + .def( + "extension", + &bit7z::BitArchiveItem::extension, + py::doc( + R"pbdoc(the extension of the item, if available or if it can be inferred from the name; otherwise it returns an empty string (e.g., when the item is a folder).)pbdoc")) + .def( + "native_path", + &bit7z::BitArchiveItem::nativePath, + py::doc( + R"pbdoc(the path of the item in the archive, if available or inferable from the name, or an empty string otherwise.)pbdoc")) + .def("creation_time", &bit7z::BitArchiveItem::creationTime) + .def("last_access_time", &bit7z::BitArchiveItem::lastAccessTime) + .def("last_write_time", &bit7z::BitArchiveItem::lastWriteTime) + .def("attributes", &bit7z::BitArchiveItem::attributes, py::doc(R"pbdoc(the item attributes.)pbdoc")) + .def("pack_size", &bit7z::BitArchiveItem::packSize, py::doc(R"pbdoc(the compressed size of the item.)pbdoc")) + .def("crc", &bit7z::BitArchiveItem::crc, py::doc(R"pbdoc(the CRC of the item.)pbdoc")) + .def("is_encrypted", + &bit7z::BitArchiveItem::isEncrypted, + py::doc(R"pbdoc(true if and only if the item is encrypted.)pbdoc")); + + // bit7z::BitArchiveItemOffset class bindings + py::class_( + m, + "BitArchiveItemOffset", + R"pbdoc(The BitArchiveItemOffset class represents an archived item but doesn't store its properties.)pbdoc") + .def("__eq__", &bit7z::BitArchiveItemOffset::operator==) + .def("__ne__", &bit7z::BitArchiveItemOffset::operator!=) + .def("__iadd__", [](bit7z::BitArchiveItemOffset &self, int val) { return self.operator++(val); }) + .def("item_property", + &bit7z::BitArchiveItemOffset::itemProperty, + py::doc(R"pbdoc(Gets the specified item property. + +Args: + property_id (bit7z::BitProperty): The ID of the property to get. + +Returns: + BitPropVariant: the value of the item property, if available, or an empty BitPropVariant. +)pbdoc")); + + // bit7z::BitArchiveItemInfo class bindings + py::class_( + m, + "BitArchiveItemInfo", + + R"pbdoc(The BitArchiveItemInfo class represents an archived item and that stores all its properties for later use.)pbdoc") + .def("item_property", + &bit7z::BitArchiveItemInfo::itemProperty, + py::doc(R"pbdoc(Gets the specified item property. +Args: + property_id (bit7z::BitProperty): The ID of the property to get. +Returns: + BitPropVariant: the value of the item property, if available, or an empty BitPropVariant. +)pbdoc")) + .def( + "item_properties", + &bit7z::BitArchiveItemInfo::itemProperties, + py::doc( + R"pbdoc(a map of all the available (i.e., non-empty) item properties and their respective values.)pbdoc")); + + py::enum_( + m, + "OverwriteMode", + R"pbdoc(Enumeration representing how a handler should deal when an output file already exists.)pbdoc") + .value("Nothing", + bit7z::OverwriteMode::None, + R"pbdoc(The handler will throw an exception if the output file or buffer already exists.)pbdoc") + .value("Overwrite", + bit7z::OverwriteMode::Overwrite, + R"pbdoc(The handler will overwrite the old file or buffer with the new one.)pbdoc") + .value("Skip", + bit7z::OverwriteMode::Skip, + R"pbdoc(The handler will skip writing to the output file or buffer.)pbdoc") + .export_values(); + + // bit7z:: BitAbstractArchiveHandler class bindings + py::class_(m, + "BitAbstractArchiveHandler", + R"pbdoc(Abstract class representing a generic archive handler.)pbdoc") + .def("format", + &bit7z::BitAbstractArchiveHandler::format, + py::doc(R"pbdoc(the format used by the handler for extracting or compressing.)pbdoc")) + .def("password", + &bit7z::BitAbstractArchiveHandler::password, + py::doc(R"pbdoc(the password used to open, extract, or encrypt the archive.)pbdoc")) + .def( + "retainDirectories", + &bit7z::BitAbstractArchiveHandler::retainDirectories, + py::doc( + R"pbdoc(a boolean value indicating whether the directory structure must be preserved while extracting or compressing the archive.)pbdoc")) + .def("is_password_defined", + &bit7z::BitAbstractArchiveHandler::isPasswordDefined, + py::doc(R"pbdoc(a boolean value indicating whether a password is defined or not.)pbdoc")) + .def("total_callback", + &bit7z::BitAbstractArchiveHandler::totalCallback, + py::doc(R"pbdoc(the current total callback.)pbdoc")) + .def("progress_callback", + &bit7z::BitAbstractArchiveHandler::progressCallback, + py::doc(R"pbdoc(the current progress callback.)pbdoc")) + .def("ratio_callback", + &bit7z::BitAbstractArchiveHandler::ratioCallback, + py::doc(R"pbdoc(the current ratio callback.)pbdoc")) + .def("file_callback", + &bit7z::BitAbstractArchiveHandler::fileCallback, + py::doc(R"pbdoc(the current file callback.)pbdoc")) + .def("password_callback", + &bit7z::BitAbstractArchiveHandler::passwordCallback, + py::doc(R"pbdoc(the current password callback.)pbdoc")) + .def("overwrite_mode", + &bit7z::BitAbstractArchiveHandler::overwriteMode, + py::doc(R"pbdoc(the overwrite mode.)pbdoc")) + .def("set_password", + &bit7z::BitAbstractArchiveHandler::setPassword, + py::arg("password"), + py ::doc(R"pbdoc(Sets up a password to be used by the archive handler. + +The password will be used to encrypt/decrypt archives by using the default cryptographic method of the archive format. + +Args: + password: the password to be used. + +Note: + Calling this method when the input archive is not encrypted does not have any effect on the extraction process. + Calling this method when the output format doesn't support archive encryption (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't throw exceptions, and it has no effects on compression operations). + After a password has been set, it will be used for every subsequent operation. To disable the use of the password, you need to call the clear_password method, which is equivalent to calling set_password(L"").)pbdoc")) + .def("clear_password", + &bit7z::BitAbstractArchiveHandler::clearPassword, + py::doc(R"pbdoc(Clear the current password used by the handler. + +Calling clear_password() will disable the encryption/decryption of archives. + +Note: + This is equivalent to calling set_password(L"").)pbdoc")) + .def("set_retain_directories", + &bit7z::BitAbstractArchiveHandler::setRetainDirectories, + py::arg("retain"), + py::doc(R"pbdoc(Sets whether the operations' output will preserve the input's directory structure or not. + +Args: + retain: the setting for preserving or not the input directory structure)pbdoc")) + .def("set_total_callback", + &bit7z::BitAbstractArchiveHandler::setTotalCallback, + py::arg("callback"), + py::doc(R"pbdoc(Sets the function to be called when the total size of an operation is available. + +Args: + callback: the total callback to be used. +)pbdoc")) + .def("set_progress_callback", + &bit7z::BitAbstractArchiveHandler::setProgressCallback, + py::arg("callback"), + py::doc(R"pbdoc(Sets the function to be called when the processed size of the ongoing operation is updated. + +Args: + callback: the progress callback to be used. +Note: + The completion percentage of the current operation can be obtained by calculating int((100.0 * processed_size) / total_size). +)pbdoc")) + .def( + "set_ratio_callback", + &bit7z::BitAbstractArchiveHandler::setRatioCallback, + py::arg("callback"), + py::doc( + R"pbdoc(Sets the function to be called when the input processed size and current output size of the ongoing operation are known. + +Args: + callback: the ratio callback to be used. +Note: + The ratio percentage of a compression operation can be obtained by calculating int((100.0 * output_size) / input_size). +)pbdoc")) + .def("set_file_callback", + &bit7z::BitAbstractArchiveHandler::setFileCallback, + py::arg("callback"), + py::doc(R"pbdoc(Sets the function to be called when the current file being processed changes. + +Args: + callback: the file callback to be used. +)pbdoc")) + .def("set_password_callback", + &bit7z::BitAbstractArchiveHandler::setPasswordCallback, + py::arg("callback"), + py::doc(R"pbdoc(Sets the function to be called when a password is needed to complete the ongoing operation. + +Args: + callback: the password callback to be used. +)pbdoc")) + .def("set_overwrite_mode", + &bit7z::BitAbstractArchiveHandler::setOverwriteMode, + py::arg("mode"), + py::doc( + R"pbdoc(Sets how the handler should behave when it tries to output to an existing file or buffer. +Args: + mode: the OverwriteMode to be used by the handler.)pbdoc")); + + // bit7z::BitAbstractArchiveOpener class bindings + py::class_(m, "BitAbstractArchiveOpener") + .def("extraction_format", + &bit7z::BitAbstractArchiveOpener::extractionFormat, + py::doc(R"pbdoc(the archive format used by the archive opener.)pbdoc")); + + // bit7z::UpdateMode enum bindings + py::enum_(m, "UpdateMode") + .value("Nothing", bit7z::UpdateMode::None) + .value("Append", bit7z::UpdateMode::Append) + .value("Update", bit7z::UpdateMode::Update); + + // bit7z::BitAbstractArchiveCreator class bindings + py::class_( + m, + "BitAbstractArchiveCreator", + R"pbdoc(Abstract class representing a generic archive creator.)pbdoc") + .def("compression_format", + &bit7z::BitAbstractArchiveCreator::compressionFormat, + py::doc(R"pbdoc(the format used for creating/updating an archive.)pbdoc")) + .def("crypt_headers", + &bit7z::BitAbstractArchiveCreator::cryptHeaders, + py::doc(R"pbdoc(whether the creator crypts also the headers of archives or not.)pbdoc")) + .def("compression_method", + &bit7z::BitAbstractArchiveCreator::compressionMethod, + py::doc(R"pbdoc(the compression method used for creating/updating an archive.)pbdoc")) + .def("dictionary_size", + &bit7z::BitAbstractArchiveCreator::dictionarySize, + py::doc(R"pbdoc(the dictionary size used for creating/updating an archive.)pbdoc")) + .def("word_size", + &bit7z::BitAbstractArchiveCreator::wordSize, + py::doc(R"pbdoc(the word size used for creating/updating an archive.)pbdoc")) + .def("solid_mode", + &bit7z::BitAbstractArchiveCreator::solidMode, + py::doc(R"pbdoc(whether the archive creator uses solid compression or not.)pbdoc")) + .def("update_mode", + &bit7z::BitAbstractArchiveCreator::updateMode, + py::doc(R"pbdoc(the update mode used when updating existing archives.)pbdoc")) + .def( + "volume_size", + &bit7z::BitAbstractArchiveCreator::volumeSize, + py::doc( + R"pbdoc(the volume size (in bytes) used when creating multi-volume archives (a 0 value means that all files are going in a single archive).)pbdoc")) + .def( + "threads_count", + &bit7z::BitAbstractArchiveCreator::threadsCount, + py::doc( + R"pbdoc(the number of threads used when creating/updating an archive (a 0 value means that it will use the 7-zip default value).)pbdoc")) + .def("store_symbolic_links", + &bit7z::BitAbstractArchiveCreator::storeSymbolicLinks, + py::doc(R"pbdoc(whether the archive creator stores symbolic links as links in the output archive.)pbdoc")) + .def("set_password", + static_cast( + &bit7z::BitAbstractArchiveCreator::setPassword), + py::arg("password"), + py::doc(R"pydoc(Sets up a password for the output archives. + +When setting a password, the produced archives will be encrypted using the default cryptographic method of the output format. The option "crypt headers" remains unchanged, in contrast with what happens when calling the set_password(tstring, bool) method. + +Args: + password: the password to be used when creating/updating archives. + +Note: + Calling set_password when the output format doesn't support archive encryption (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't throw exceptions, and it has no effects on compression operations). + After a password has been set, it will be used for every subsequent operation. To disable the use of the password, you need to call the clearPassword method (inherited from BitAbstractArchiveHandler), which is equivalent to set_password(L"").)pydoc")) + .def("set_password", + static_cast( + &bit7z::BitAbstractArchiveCreator::setPassword), + py::arg("password"), + py::arg("crypt_headers"), + py::doc(R"pydoc(Sets up a password for the output archive. + +When setting a password, the produced archive will be encrypted using the default cryptographic method of the output format. If the format is 7z, and the option "cryptHeaders" is set to true, the headers of the archive will be encrypted, resulting in a password request every time the output file will be opened. + +Args: + password: the password to be used when creating/updating archives. + crypt_headers: if true, the headers of the output archives will be encrypted (valid only when using the 7z format). + +Note: + Calling set_password when the output format doesn't support archive encryption (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't throw exceptions, and it has no effects on compression operations). + Calling set_password with "cryptHeaders" set to true does not have effects on formats different from 7z. + After a password has been set, it will be used for every subsequent operation. To disable the use of the password, you need to call the clearPassword method (inherited from BitAbstractArchiveHandler), which is equivalent to set_password(L"").)pydoc")) + .def("set_compression_level", + &bit7z::BitAbstractArchiveCreator::setCompressionLevel, + py::arg("level"), + py::doc(R"pydoc(Sets the compression level to be used when creating/updating an archive. + +Args: + level: the compression level desired.)pydoc")) + .def("set_compression_method", + &bit7z::BitAbstractArchiveCreator::setCompressionMethod, + py::arg("method"), + py::doc(R"pydoc(Sets the compression method to be used when creating/updating an archive. + +Args: + method: the compression method desired.)pydoc")) + .def("set_dictionary_size", + &bit7z::BitAbstractArchiveCreator::setDictionarySize, + py::arg("dictionary_size"), + py::doc(R"pydoc(Sets the dictionary size to be used when creating/updating an archive. + +Args: + dictionary_size: the dictionary size desired.)pydoc")) + .def("set_word_size", + &bit7z::BitAbstractArchiveCreator::setWordSize, + py::arg("word_size"), + py::doc(R"pydoc(Sets the word size to be used when creating/updating an archive. + +Args: + word_size: the word size desired.)pydoc")) + .def("set_solid_mode", + &bit7z::BitAbstractArchiveCreator::setSolidMode, + py::arg("solid_mode"), + py::doc(R"pydoc(Sets whether the archive creator uses solid compression or not. + +Args: + solid_mode: the solid mode desired. +Note: + Setting the solid compression mode to true has effect only when using the 7z format with multiple input files.)pydoc")) + .def("set_update_mode", + static_cast( + &bit7z::BitAbstractArchiveCreator::setUpdateMode), + py::arg("mode"), + py::doc(R"pydoc(Sets whether and how the creator can update existing archives or not. + +Args: + mode: the desired update mode. + +Note: + If set to UpdateMode::None, a subsequent compression operation may throw an exception if it targets an existing archive.)pydoc")) + .def("set_volume_size", + &bit7z::BitAbstractArchiveCreator::setVolumeSize, + py::arg("volume_size"), + py::doc(R"pydoc(Sets the volumeSize (in bytes) of the output archive volumes. + +Args: + volume_size: The dimension of a volume. + +Note: + This setting has effects only when the destination archive is on the filesystem. +)pydoc")) + .def("set_threads_count", + &bit7z::BitAbstractArchiveCreator::setThreadsCount, + py::arg("threads_count"), + py::doc(R"pydoc(Sets the number of threads to be used when creating/updating an archive. + +Args: + threads_count: the number of threads desired. +)pydoc")) + .def("set_store_symbolic_links", + &bit7z::BitAbstractArchiveCreator::setStoreSymbolicLinks, + py::arg("store_symbolic_links"), + py::doc(R"pydoc(Sets whether the creator will store symbolic links as links in the output archive. + +Args: + store_symbolic_links: if true, symbolic links will be stored as links. +)pydoc")); + + // bit7z::BitInputArchive + py::class_(m, "BitInputArchive") + .def("detected_format", + &bit7z::BitInputArchive::detectedFormat, + py::doc(R"pydoc(the detected format of the file.)pydoc")) + .def("archive_property", + &bit7z::BitInputArchive::archiveProperty, + py::doc(R"pydoc(Gets the specified archive property. + +Args: + property: the property to be retrieved. + +Returns: + the current value of the archive property or an empty BitPropVariant if no value is specified.)pydoc")) + .def("item_property", + &bit7z::BitInputArchive::itemProperty, + py::arg("index"), + py::arg("property"), + py::doc(R"pydoc(Gets the specified item property. + +Args: + index: the index of the item to retrieve the property from. + property: the property to be retrieved. + +Returns: + the current value of the item property or an empty BitPropVariant if no value is specified.)pydoc")) + .def("items_count", + &bit7z::BitInputArchive::itemsCount, + py::doc(R"pydoc(the number of items in the archive.)pydoc")) + .def("is_item_folder", + &bit7z::BitInputArchive::isItemFolder, + py::arg("index"), + py::doc(R"pydoc(Whether the item at the given index is a folder. +Args: + index: the index of an item in the archive. + +Returns: + true if and only if the item at the given index is a folder.)pydoc")) + .def("is_item_encrypted", + &bit7z::BitInputArchive::isItemEncrypted, + py::arg("index"), + py::doc(R"pydoc(Whether the item at the given index is encrypted. + +Args: + index: the index of an item in the archive. + +Returns: + true if and only if the item at the given index is encrypted.)pydoc")) + .def("archive_path", + &bit7z::BitInputArchive::archivePath, + py::doc(R"pydoc(the path to the archive (the empty string for buffer/stream archives).)pydoc")) + .def("extract_to", + static_cast( + &bit7z::BitInputArchive::extractTo), + py::arg("path"), + py::doc(R"pydoc(Extracts the archive to the chosen directory. + +Args: + outDir: the output directory where the extracted files will be put.)pydoc")) + .def("extract_to", + static_cast &) const>( + &bit7z::BitInputArchive::extractTo), + py::arg("out_dir"), + py::arg("indices"), + py::doc(R"pydoc(Extracts the specified items to the chosen directory. + +Args: + out_dir: the output directory where the extracted files will be put. + indices: the array of indices of the files in the archive that must be extracted.)pydoc")) + .def( + "extract_to", + [](bit7z::BitInputArchive &self, uint32_t index) -> py::bytes { + std::vector out_buffer; + self.extractTo(out_buffer, index); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("index"), + py::doc(R"pydoc(Extracts a file to the output buffer. + +Args: + index: the index of the file to be extracted.)pydoc")) + .def( + "extract_to", + [](bit7z::BitInputArchive &self) -> std::map { + std::map> out_buffer; + self.extractTo(out_buffer); + std::map result; + for (auto const &item : out_buffer) { + result[item.first] = + py::bytes(reinterpret_cast(item.second.data()), item.second.size()); + } + return result; + }, + py::doc( + R"pydoc(Extracts the content of the archive to a map of memory buffers, where the keys are the paths of the files (inside the archive), and the values are their decompressed contents.)pydoc")) + .def("test", &bit7z::BitInputArchive::test, py::doc(R"pydoc(Tests the archive without extracting its content. + +If the archive is not valid, a BitException is thrown!)pydoc")) + .def("test_item", + &bit7z::BitInputArchive::testItem, + py::arg("index"), + py::doc(R"pydoc(Tests the item at the given index inside the archive without extracting it. + +If the archive is not valid, or there's no item at the given index, a BitException is thrown!)pydoc")) + .def("contains", + &bit7z::BitInputArchive::contains, + py::arg("path"), + py::doc(R"pydoc(Find if there is an item in the archive that has the given path. + +Args: + path: the path of the file or folder to be checked. + +Returns: + true if and only if the archive contains the specified file or folder.)pydoc")) + .def("item_at", + &bit7z::BitInputArchive::itemAt, + py::arg("index"), + py::doc(R"pydoc(Retrieve the item at the given index. + +Args: + index: the index of the item to be retrieved. + +Returns: + the item at the given index within the archive.)pydoc")); + + // FilterPolicy enum bindings + py::enum_(m, "FilterPolicy") + .value("Include", + bit7z::FilterPolicy::Include, + R"pydoc(Extract/compress the items that match the pattern.)pydoc") + .value("Exclude", + bit7z::FilterPolicy::Exclude, + R"pydoc(Do not extract/compress the items that match the pattern.)pydoc") + .export_values(); + + // bit7z::BitOutputArchive + py::class_(m, "BitOutputArchive") + .def("add_items", + static_cast &)>( + &bit7z::BitOutputArchive::addItems), + py::arg("paths"), + py::doc(R"pydoc(Adds all the items that can be found by indexing the given vector of filesystem paths. + +Args: + paths: the paths to be added to the archive. +)pydoc")) + .def( + "add_items", + static_cast &)>( + &bit7z::BitOutputArchive::addItems), + py::arg("files"), + py::doc( + R"pydoc(Adds all the items that can be found by indexing the keys of the given map of filesystem paths; the corresponding mapped values are the user-defined paths wanted inside the output archive. + +Args: + files: the map of file paths and their contents to be added to the archive. +)pydoc")) + .def("add_file", + static_cast( + &bit7z::BitOutputArchive::addFile), + py::arg("in_file"), + py::arg("name"), + py::doc( + R"pydoc(Adds the given file path, with an optional user-defined path to be used in the output archive. + +Args: + in_file: the path to the filesystem file to be added to the output archive. + name: (optional) user-defined path to be used inside the output archive. +Note: + If a directory path is given, a BitException is thrown. +)pydoc")) + .def( + "add_file", + [](bit7z::BitOutputArchive &self, py::bytes input, const std::string &path) { + auto input_str = input.cast(); + std::vector input_bytes(input_str.begin(), input_str.end()); + self.addFile(input_bytes, path); + }, + py::arg("input"), + py::arg("name"), + py::doc( + R"pydoc(Adds the given memory buffer, with an optional user-defined path to be used in the output archive. + +Args: + input: the memory buffer to be added to the output archive. + name: user-defined path to be used inside the output archive.)pydoc")) + .def("add_files", + static_cast &)>( + &bit7z::BitOutputArchive::addFiles), + py::arg("in_files"), + py::doc(R"pydoc(Adds all the files in the given vector of filesystem paths. + +Args: + in_files: the paths to be added to the archive. +Note: + Paths to directories are ignored. +)pydoc")) + .def("add_files", + static_cast( + &bit7z::BitOutputArchive::addFiles), + py::arg("in_dir"), + py::arg("filter"), + py::arg("recursive"), + py::doc(R"pydoc(Adds all the files inside the given directory path that match the given wildcard filter. + +Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: (optional) the filter pattern to be used to select the files to be added. + recursive: (optional) if true, the directory will be searched recursively. +Note: + If a file path is given, a BitException is thrown. +)pydoc")) + .def( + "add_files", + static_cast( + &bit7z::BitOutputArchive::addFiles), + py::arg("in_dir"), + py::arg("filter"), + py::arg("recursive"), + py::arg("policy"), + py::doc( + R"pydoc(Adds all the files inside the given directory path that match the given wildcard filter, with the specified filter policy. + +Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: (optional) the wildcard filter to be used for searching the files. + recursive: (optional) recursively search the files in the given directory and all of its subdirectories. + policy: (optional) the filtering policy to be applied to the matched items. +)pydoc")) + .def("add_directory", + &bit7z::BitOutputArchive::addDirectory, + py::arg("in_dir"), + py::doc( + R"pydoc(Adds the given directory path and all its content. +Args: + in_dir: the path of the directory to be added to the archive.)pydoc")) + .def("add_directory_contents", + static_cast( + &bit7z::BitOutputArchive::addDirectoryContents), + py::arg("in_dir"), + py::arg("filter"), + py::arg("recursive"), + py::doc(R"pydoc(Adds the contents of the given directory path. + +This function iterates through the specified directory and adds its contents based on the provided wildcard filter. Optionally, the operation can be recursive, meaning it will include subdirectories and their contents. + +Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: the wildcard filter to be used for searching the files. + recursive: recursively search the files in the given directory and all of its subdirectories. +)pydoc")) + .def("add_directory_contents", + static_cast( + &bit7z::BitOutputArchive::addDirectoryContents), + py::arg("in_dir"), + py::arg("filter"), + py::arg("recursive"), + py::arg("policy"), + py::doc( + R"pydoc(Adds the contents of the given directory path. + +This function iterates through the specified directory and adds its contents based on the provided wildcard filter and policy. Optionally, the operation can be recursive, meaning it will include subdirectories and their contents. + +Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: the wildcard filter to be used for searching the files. + recursive: recursively search the files in the given directory and all of its subdirectories. + policy: the filtering policy to be applied to the matched items. +)pydoc")) + .def("compress_to", + static_cast(&bit7z::BitOutputArchive::compressTo), + py::arg("out_file"), + py::doc(R"pydoc(Compresses all the items added to this object to the specified archive file path. + +Args: + out_file: the output archive file path. + +Note: + If this object was created by passing an input archive file path, and this latter is the same as the outFile path parameter, the file will be updated. +)pydoc")) + .def( + "compress_to", + [](bit7z::BitOutputArchive &self) -> py::bytes { + std::vector out_buffer; + self.compressTo(out_buffer); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::doc(R"pydoc(Compresses all the items added to this object to the specified buffer.)pydoc")) + .def("items_count", + &bit7z::BitOutputArchive::itemsCount, + py::doc(R"pydoc(the number of items in the archive.)pydoc")); + + // bit7z::BitArchiveReader class bindings + py::class_(m, "BitArchiveReader") + .def(py::init([](const std::string &in_archive, + const bit7z::BitInFormat &format, + const std::string &password = std::string()) { + return new bit7z::BitArchiveReader(_core::Bit7zipSingleton::getInstance(), + in_archive, + format, + password); + }), + py::arg("in_archive"), + py::arg("format"), + py::arg("password") = std::string(), + py::doc(R"pydoc(Constructs a BitArchiveReader object, opening the input file archive. + +Args: + in_archive: the path to the archive to be read. + format: the format of the input archive. + password: the password needed for opening the input archive.)pydoc")) + .def( + py::init([](py::bytes in_archive, + const bit7z::BitInFormat &format, + const std::string &password = std::string()) { + std::string in_archive_str = py::cast(in_archive); + std::vector in_buffer(in_archive_str.begin(), in_archive_str.end()); + return new bit7z::BitArchiveReader(_core::Bit7zipSingleton::getInstance(), in_buffer, format, password); + }), + py::arg("in_archive"), + py::arg("format"), + py::arg("password") = std::string(), + py::doc(R"pydoc(Constructs a BitArchiveReader object, opening the input memory buffer archive. + +Args: + in_archive: the input buffer containing the archive to be read. + format: the format of the input archive. + password: the password needed for opening the input archive.)pydoc")) + .def("items", + &bit7z::BitArchiveReader::items, + py::doc(R"pydoc(the list of all the archive items as BitArchiveItem objects.)pydoc")) + .def( + "archive_properties", + &bit7z::BitArchiveReader::archiveProperties, + py::doc( + R"pydoc(a map of all the available (i.e., non-empty) archive properties and their respective values.)pydoc")) + .def("folders_count", + &bit7z::BitArchiveReader::foldersCount, + py::doc(R"pydoc(the number of folders in the archive.)pydoc")) + .def("files_count", + &bit7z::BitArchiveReader::filesCount, + py::doc(R"pydoc(the number of files in the archive.)pydoc")) + .def("size", + &bit7z::BitArchiveReader::size, + py::doc(R"pydoc(the total uncompressed size of the archive content.)pydoc")) + .def("pack_size", + &bit7z::BitArchiveReader::packSize, + py::doc(R"pydoc(the total compressed size of the archive content.)pydoc")) + .def("has_encrypted_items", + &bit7z::BitArchiveReader::hasEncryptedItems, + py::doc(R"pydoc(true if and only if the archive has at least one encrypted item.)pydoc")) + .def("is_encrypted", + static_cast(&bit7z::BitArchiveReader::isEncrypted), + py::doc(R"pydoc(true if and only if the archive has only encrypted items.)pydoc")) + .def("volumes_count", + &bit7z::BitArchiveReader::volumesCount, + py::doc(R"pydoc(the number of volumes in the archive.)pydoc")) + .def("is_multi_volume", + &bit7z::BitArchiveReader::isMultiVolume, + py::doc(R"pydoc(true if and only if the archive is composed by multiple volumes.)pydoc")) + .def("is_solid", + &bit7z::BitArchiveReader::isSolid, + py::doc(R"pydoc(true if and only if the archive was created using solid compression.)pydoc")) + .def_static( + "is_header_encrypted", + [](const std::string &in_archive, const bit7z::BitInFormat &format) -> bool { + return bit7z::BitArchiveReader::isHeaderEncrypted(_core::Bit7zipSingleton::getInstance(), + in_archive, + format); + }, + py::arg("in_archive"), + py::arg("format"), + py::doc(R"pydoc(Checks if the given archive is header-encrypted or not.)pydoc")) + .def_static( + "is_header_encrypted", + [](py::bytes in_archive, const bit7z::BitInFormat &format) -> bool { + std::string in_archive_str = py::cast(in_archive); + std::vector in_buffer(in_archive_str.begin(), in_archive_str.end()); + return bit7z::BitArchiveReader::isHeaderEncrypted(_core::Bit7zipSingleton::getInstance(), + in_buffer, + format); + }, + py::arg("in_archive"), + py::arg("format"), + py::doc(R"pydoc(Checks if the given memory buffer archive is header-encrypted or not.)pydoc")); + + // bit7z::BitArchiveWriter class bindings + py::class_(m, + "BitArchiveWriter") + .def( + py::init([](const bit7z::BitInOutFormat &format) { + return new bit7z::BitArchiveWriter(_core::Bit7zipSingleton::getInstance(), format); + }), + py::arg("format"), + py::doc( + R"pydoc(Constructs an empty BitArchiveWriter object that can write archives of the specified format.)pydoc")) + .def(py::init([](const std::string &in_archive, + const bit7z::BitInOutFormat &format, + const std::string &password = std::string()) { + return new bit7z::BitArchiveWriter(_core::Bit7zipSingleton::getInstance(), + in_archive, + format, + password); + }), + py::arg("in_archive"), + py::arg("format"), + py::arg("password") = std::string(), + py::doc(R"pydoc(Constructs a BitArchiveWriter object, reading the given archive file path.)pydoc")) + .def( + py::init([](py::bytes in_archive, + const bit7z::BitInOutFormat &format, + const std::string &password = std::string()) { + std::string in_archive_str = py::cast(in_archive); + std::vector in_buffer(in_archive_str.begin(), in_archive_str.end()); + return new bit7z::BitArchiveWriter(_core::Bit7zipSingleton::getInstance(), in_buffer, format, password); + }), + py::arg("in_archive"), + py::arg("format"), + py::arg("password") = std::string(), + py::doc(R"pydoc(Constructs a BitArchiveWriter object, reading the given memory buffer archive.)pydoc")); + + // BitExtractor class bindings + using BitStringExtractInput = const std::string &; + using BitStringExtractor = bit7z::BitExtractor; + py::class_ bitStringExtractor(m, "BitStringExtractor"); + bitStringExtractor + .def(py::init([](const bit7z::BitInFormat &format) { + return new BitStringExtractor(_core::Bit7zipSingleton::getInstance(), format); + }), + py::arg("format"), + py::doc(R"pydoc(Constructs a BitStringExtractor object, opening the input archive.)pydoc")) + .def("extract", + static_cast( + &BitStringExtractor::extract), + py::arg("in_archive"), + py::arg("out_dir"), + py::doc(R"pydoc(Extracts the given archive to the chosen directory.)pydoc")) + .def( + "extract", + [](BitStringExtractor &self, const std::string &input, uint32_t index) -> py::bytes { + std::vector out_buffer; + self.extract(input, out_buffer, index); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_archive"), + py::arg("index"), + py::doc(R"pydoc(Extracts the specified item from the given archive to a memory buffer.)pydoc")) + .def( + "extract", + [](BitStringExtractor &self, const std::string &input) -> std::map { + std::map> out_buffer; + self.extract(input, out_buffer); + std::map result; + for (const auto &item : out_buffer) { + result[item.first] = + py::bytes(reinterpret_cast(item.second.data()), item.second.size()); + } + return result; + }, + py::arg("in_archive"), + py::doc(R"pydoc(Extracts all the items from the given archive to a dictionary of memory buffers.)pydoc")) + .def( + "extract_matching", + static_cast(&BitStringExtractor::extractMatching), + py::arg("in_archive"), + py::arg("pattern"), + py::arg("out_dir"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts the files in the archive that match the given wildcard pattern to the chosen directory.)pydoc")) + .def( + "extract_matching", + [](BitStringExtractor &self, + const std::string &input, + const std::string &pattern, + bit7z::FilterPolicy filter_policy) -> py::bytes { + std::vector out_buffer; + self.extractMatching(input, pattern, out_buffer, filter_policy); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_archive"), + py::arg("pattern"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts to the output buffer the first file in the archive matching the given wildcard pattern.)pydoc")) + .def("extract_items", + &BitStringExtractor::extractItems, + py::arg("in_archive"), + py::arg("indices"), + py::arg("out_dir") = std::string()) + .def( + "extract_matching_regex", + static_cast( + &BitStringExtractor::extractMatchingRegex), + py::arg("in_archive"), + py::arg("regex"), + py::arg("out_dir"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts the files in the archive that match the given regex pattern to the chosen directory.)pydoc")) + .def( + "extract_matching_regex", + [](BitStringExtractor &self, + const std::string &input, + const std::string ®ex, + bit7z::FilterPolicy filter_policy) -> py::bytes { + std::vector out_buffer; + self.extractMatchingRegex(input, regex, out_buffer, filter_policy); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_archive"), + py::arg("regex"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts to the output buffer the first file in the archive matching the given regex pattern.)pydoc")) + .def("test", + &BitStringExtractor::test, + py::arg("in_archive"), + py::doc(R"pydoc(Tests the given archive without extracting its content. + +If the archive is not valid, a BitException is thrown! + +Args: + in_archive: the input archive to be tested.)pydoc")); + + m.attr("BitFileExtractor") = bitStringExtractor; + + // bit7z::BitMemExtractor class bindings + using BitMemExtractorInput = const std::vector &; + using BitMemExtractor = bit7z::BitExtractor &>; + py::class_(m, "BitMemExtractor") + .def(py::init([](const bit7z::BitInFormat &format) { + return new BitMemExtractor(_core::Bit7zipSingleton::getInstance(), format); + }), + py::arg("format"), + py::doc(R"pydoc(Constructs a BitMemExtractor object, opening the input archive.)pydoc")) + .def("extract", + static_cast( + &BitMemExtractor::extract), + py::arg("in_archive"), + py::arg("out_dir") = std::string(), + py::doc(R"pydoc(Extracts the given archive to the chosen directory.)pydoc")) + .def( + "extract", + [](BitMemExtractor &self, py::bytes input, uint32_t index) -> py::bytes { + std::vector out_buffer; + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + self.extract(in_buffer, out_buffer, index); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_archive"), + py::arg("index"), + py::doc(R"pydoc(Extracts the specified item from the given archive to a memory buffer.)pydoc")) + .def( + "extract", + [](BitMemExtractor &self, py::bytes input) -> std::map { + std::map> out_buffer; + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + self.extract(in_buffer, out_buffer); + std::map result; + for (const auto &item : out_buffer) { + result[item.first] = + py::bytes(reinterpret_cast(item.second.data()), item.second.size()); + } + return result; + }, + py::arg("in_archive"), + py::doc(R"pydoc(Extracts all the items from the given archive to a dictionary of memory buffers.)pydoc")) + .def( + "extract_matching", + static_cast(&BitMemExtractor::extractMatching), + py::arg("in_archive"), + py::arg("pattern"), + py::arg("out_dir"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts the files in the archive that match the given wildcard pattern to the chosen directory.)pydoc")) + .def( + "extract_matching", + [](BitMemExtractor &self, py::bytes input, py::bytes pattern, bit7z::FilterPolicy filter_policy) + -> py::bytes { + std::vector out_buffer; + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + std::string pattern_str = py::cast(pattern); + self.extractMatching(in_buffer, pattern_str, out_buffer, filter_policy); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_archive"), + py::arg("pattern"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts to the output buffer the first file in the archive matching the given wildcard pattern.)pydoc")) + .def("extract_items", + &BitMemExtractor::extractItems, + py::arg("in_archive"), + py::arg("indices"), + py::arg("out_dir") = std::string(), + py::doc(R"pydoc(Extracts the specified items from the given archive to the chosen directory.)pydoc")) + .def( + "extract_matching_regex", + static_cast(&BitMemExtractor::extractMatchingRegex), + py::arg("in_archive"), + py::arg("regex"), + py::arg("out_dir"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts the files in the archive that match the given regex pattern to the chosen directory.)pydoc")) + .def( + "extract_matching_regex", + [](BitMemExtractor &self, py::bytes input, py::bytes regex, bit7z::FilterPolicy filter_policy) + -> py::bytes { + std::vector out_buffer; + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + std::string regex_str = py::cast(regex); + self.extractMatchingRegex(in_buffer, regex_str, out_buffer, filter_policy); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_archive"), + py::arg("regex"), + py::arg("filter_policy"), + py::doc( + R"pydoc(Extracts to the output buffer the first file in the archive matching the given regex pattern.)pydoc")) + .def("test", + &BitMemExtractor::test, + py::arg("in_archive"), + py::doc(R"pydoc(Tests the given archive without extracting its content. + +If the archive is not valid, a BitException is thrown! + +Args: + in_archive: the input archive to be tested.)pydoc")); + + // BitCompressor class bindings + using BitStringCompressInput = const std::string &; + using BitStringCompressor = bit7z::BitCompressor; + py::class_(m, "BitStringCompressor", py::is_final()) + .def(py::init([](const bit7z::BitInOutFormat &format) { + return new BitStringCompressor(_core::Bit7zipSingleton::getInstance(), format); + }), + py::arg("format"), + py::doc(R"pydoc(Constructs a BitStringCompressor object, creating a new archive.)pydoc")) + .def("compress_file", + static_cast(&BitStringCompressor::compressFile), + py::arg("in_file"), + py::arg("out_file"), + py::arg("input_name") = std::string(), + py::doc(R"pydoc(Compresses the given file to the chosen archive. + +Args: + in_file: the input file to be compressed. + out_file: the path (relative or absolute) to the output archive file. + input_name: the name of the input file in the archive (optional).)pydoc")) + .def( + "compress_file", + [](BitStringCompressor &self, const std::string &in_file, const std::string &input_name) -> py::bytes { + std::vector out_buffer; + self.compressFile(in_file, out_buffer, input_name); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("in_file"), + py::arg("input_name") = std::string(), + py::doc(R"pydoc(Compresses the given file to a memory buffer. + +Args: + in_file: the input file to be compressed. + input_name: the name of the input file in the archive (optional).)pydoc")); + + py::class_(m, "BitFileCompressor", py::is_final()) + .def(py::init([](const bit7z::BitInOutFormat &format) { + return new bit7z::BitFileCompressor(_core::Bit7zipSingleton::getInstance(), format); + }), + py::arg("format"), + py::doc(R"pydoc(Constructs a BitFileCompressor object, creating a new archive.)pydoc")) + .def("compress", + static_cast &, const std::string &) + const>(&bit7z::BitFileCompressor::compress), + py::arg("in_files"), + py::arg("out_archive"), + py::doc(R"pydoc(Compresses the given files or directories. + +The items in the first argument must be the relative or absolute paths to files or directories existing on the filesystem. + +Args: + in_files: the input files to be compressed. + out_archive: the path (relative or absolute) to the output archive file.)pydoc")) + .def("compress", + static_cast &, + const std::string &) const>( + &bit7z::BitFileCompressor::compress), + py::arg("in_files"), + py::arg("out_archive"), + py::doc(R"pydoc(Compresses the given files or directories using the specified aliases. + +The items in the first argument must be the relative or absolute paths to files or directories existing on the filesystem. Each pair in the map must follow the following format: {"path to file in the filesystem", "alias path in the archive"}. + + +Args: + in_files: a map of paths and corresponding aliases. + out_archive: the path (relative or absolute) to the output archive file.)pydoc")) + .def("compress_files", + static_cast &, const std::string &) + const>(&bit7z::BitFileCompressor::compressFiles), + py::arg("in_files"), + py::arg("out_archive"), + py::doc(R"pydoc(Compresses a group of files. + +Args: + in_files: the input files to be compressed. + out_archive: the path (relative or absolute) to the output archive file. +Note: + Any path to a directory or to a not-existing file will be ignored!)pydoc")) + .def("compress_files", + static_cast(&bit7z::BitFileCompressor::compressFiles), + py::arg("in_dir"), + py::arg("out_archive"), + py::arg("recursive") = true, + py::arg("filter_pattern") = std::string("*"), + py::doc(R"pydoc(Compresses all the files in the given directory. + +Args: + in_dir: the path (relative or absolute) to the input directory. + out_archive: the path (relative or absolute) to the output archive file. + recursive: (optional) if true, it searches files inside the sub-folders of in_dir. + filter_pattern: the wildcard pattern to filter the files to be compressed (optional).)pydoc")) + .def("compress_directory", + &bit7z::BitFileCompressor::compressDirectory, + py::arg("in_dir"), + py::arg("out_archive"), + py::doc(R"pydoc(Compresses an entire directory. + +Args: + in_dir: the path (relative or absolute) to the input directory. + out_archive: the path (relative or absolute) to the output archive file. +Note: + This method is equivalent to compress_files with filter set to "".)pydoc")) + .def("compress_directory_contents", + &bit7z::BitFileCompressor::compressDirectoryContents, + py::arg("in_dir"), + py::arg("out_archive"), + py::arg("recursive") = true, + py::arg("filter_pattern") = std::string("*"), + py::doc(R"pydoc(Compresses the contents of a directory. + +Args: + in_dir: the path (relative or absolute) to the input directory. + out_archive: the path (relative or absolute) to the output archive file. + recursive: (optional) if true, it searches files inside the sub-folders of in_dir. + filter_pattern: the wildcard pattern to filter the files to be compressed (optional). +Note: + Unlike compress_files, this method includes also the metadata of the sub-folders.)pydoc")); + + // bit7z::BitMemCompressor class bindings + using BitMemCompressorInput = const std::vector &; + using BitMemCompressor = bit7z::BitCompressor; + py::class_(m, "BitMemCompressor") + .def(py::init([](const bit7z::BitInOutFormat &format) { + return new BitMemCompressor(_core::Bit7zipSingleton::getInstance(), format); + }), + py::arg("format"), + py::doc(R"pydoc(Constructs a BitMemCompressor object, creating a new archive.)pydoc")) + .def( + "compress_file", + [](BitMemCompressor &self, py::bytes input, const std::string &out_file, const std::string &input_name) { + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + self.compressFile(in_buffer, out_file, input_name); + }, + py::arg("input"), + py::arg("out_file"), + py::arg("input_name") = std::string(), + py::doc(R"pydoc(Compresses the given memory buffer to the chosen archive. + +Args: + input: the input memory buffer to be compressed. + out_file: the path (relative or absolute) to the output archive file. + input_name: (optional) the name to give to the compressed file inside the output archive.)pydoc")) + .def( + "compress_file", + [](BitMemCompressor &self, py::bytes input, const std::string &input_name) -> py::bytes { + std::vector out_buffer; + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + self.compressFile(in_buffer, out_buffer, input_name); + return py::bytes(reinterpret_cast(out_buffer.data()), out_buffer.size()); + }, + py::arg("input"), + py::arg("input_name") = std::string(), + py::doc(R"pydoc(Compresses the given memory buffer to a memory buffer. + +Args: + input: the input memory buffer to be compressed. + input_name: (optional) the name to give to the compressed file inside the output archive.)pydoc")); + + // bit7z::BitArchiveEditor class bindings + py::class_(m, "BitArchiveEditor") + .def(py::init( + [](const std::string &in_archive, const bit7z::BitInOutFormat &format, const std::string &password) { + return new bit7z::BitArchiveEditor(_core::Bit7zipSingleton::getInstance(), + in_archive, + format, + password); + }), + py::arg("in_archive"), + py::arg("format"), + py::arg("password") = std::string(), + py::doc(R"pydoc(Constructs a BitArchiveEditor object, reading the given archive file path.)pydoc")) + .def("set_update_mode", + &bit7z::BitArchiveEditor::setUpdateMode, + py::arg("update_mode"), + py::doc(R"pydoc(Sets how the editor performs the update of the items in the archive. + +Args: + mode: the desired update mode (either UpdateMode::Append or UpdateMode::Overwrite). + +Note: + BitArchiveEditor doesn't support UpdateMode::Nothing.)pydoc")) + .def("rename_item", + static_cast( + &bit7z::BitArchiveEditor::renameItem), + py::arg("index"), + py::arg("new_path"), + py::doc(R"pydoc(Requests to change the path of the item at the specified index with the given one. + +Args: + index: the index of the item to be renamed. + new_path: the new path of the item.)pydoc")) + .def("rename_item", + static_cast( + &bit7z::BitArchiveEditor::renameItem), + py::arg("old_path"), + py::arg("new_path"), + py::doc(R"pydoc(Requests to change the path of the item from oldPath to the newPath. + +Args: + old_path: the current path of the item to be renamed. + new_path: the new path of the item.)pydoc")) + .def( + "update_item", + static_cast( + &bit7z::BitArchiveEditor::updateItem), + py::arg("index"), + py::arg("in_file"), + py::doc( + R"pydoc(Requests to update the content of the item at the specified index with the data from the given file. + +Args: + index: the index of the item to be updated. + in_file: the path of the file to be used for the update.)pydoc")) + .def( + "update_item", + [](bit7z::BitArchiveEditor &self, uint32_t index, py::bytes input) { + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + self.updateItem(index, in_buffer); + }, + py::arg("index"), + py::arg("in_buffer"), + py::doc( + R"pydoc(Requests to update the content of the item at the specified index with the data from the given buffer. + +Args: + index: the index of the item to be updated. + in_buffer: the buffer containing the new data for the item.)pydoc")) + .def( + "update_item", + static_cast( + &bit7z::BitArchiveEditor::updateItem), + py::arg("item_path"), + py::arg("in_file"), + py::doc( + R"pydoc(Requests to update the content of the item at the specified path with the data from the given file. + +Args: + item_path: the path of the item to be updated. + in_file: the path of the file to be used for the update.)pydoc")) + .def( + "update_item", + [](bit7z::BitArchiveEditor &self, const std::string &item_path, py::bytes input) { + std::string input_str = py::cast(input); + std::vector in_buffer(input_str.begin(), input_str.end()); + self.updateItem(item_path, in_buffer); + }, + py::arg("item_path"), + py::arg("in_buffer"), + py::doc( + R"pydoc(Requests to update the content of the item at the specified path with the data from the given buffer. + +Args: + item_path: the path of the item to be updated. + in_buffer: the buffer containing the new data for the item.)pydoc")) + .def("delete_item", + static_cast( + &bit7z::BitArchiveEditor::deleteItem), + py::arg("index"), + py::arg("policy"), + py::doc(R"pydoc(Marks as deleted the item at the given index. + +Args: + index: the index of the item to be deleted. + policy: the policy to be used when deleting items. + +Exceptions: + BitException if the index is invalid. + +Note: + By default, if the item is a folder, only its metadata is deleted, not the files within it. If instead the policy is set to DeletePolicy::RecurseDirs, then the items within the folder will also be deleted.)pydoc")) + .def("delete_item", + static_cast( + &bit7z::BitArchiveEditor::deleteItem), + py::arg("item_path"), + py::arg("policy"), + py::doc(R"pydoc(Marks as deleted the archive's item(s) with the specified path. + +Args: + item_path: the path (in the archive) of the item to be deleted. + policy: the policy to be used when deleting items. + +Exceptions: + BitException if the specified path is empty or invalid, or if no matching item could be found. + +Note: + By default, if the marked item is a folder, only its metadata will be deleted, not the files within it. To delete the folder contents as well, set the policy to DeletePolicy::RecurseDirs. + The specified path must not begin with a path separator. + A path with a trailing separator will _only_ be considered if the policy is DeletePolicy::RecurseDirs, and will only match folders; with DeletePolicy::ItemOnly, no item will match a path with a trailing separator. + Generally, archives may contain multiple items with the same paths. If this is the case, all matching items will be marked as deleted according to the specified policy.)pydoc")) + .def( + "apply_changes", + &bit7z::BitArchiveEditor::applyChanges, + py::doc( + R"pydoc(Applies the requested changes (i.e., rename/update/delete operations) to the input archive.)pydoc")); } diff --git a/src/_core/src/pybit7z.cpp b/src/_core/src/pybit7z.cpp index 69c4e94..023a310 100644 --- a/src/_core/src/pybit7z.cpp +++ b/src/_core/src/pybit7z.cpp @@ -1 +1,26 @@ #include "pybit7z.hpp" + +namespace _core { + +std::string& default_library_path() { +#ifdef WIN32 +#if defined(_MSC_VER) + constexpr auto default_lib7zip = "7zip.dll"; +#else + constexpr auto default_lib7zip = "lib7zip.dll"; +#endif +#elif __APPLE__ + constexpr auto default_lib7zip = "lib7zip.dylib"; +#else + constexpr auto default_lib7zip = "lib7zip.so"; +#endif + static std::string default_path(default_lib7zip); + return default_path; +} + +const bit7z::Bit7zLibrary& Bit7zipSingleton::getInstance() { + static const bit7z::Bit7zLibrary instance(default_library_path()); + return instance; +} + +} // namespace _core diff --git a/src/_core/tests/test_core.cpp b/src/_core/tests/test_core.cpp index efa8322..3ccf802 100644 --- a/src/_core/tests/test_core.cpp +++ b/src/_core/tests/test_core.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "_core.hpp" +#include "pybit7z.hpp" #include "utils.hpp" TEST(_core, version) { @@ -13,8 +14,7 @@ using _core_pybit7z_test = test::utils::rc_dir_test; TEST_F(_core_pybit7z_test, compress) { using namespace bit7z; - Bit7zLibrary lib{_core::default_lib7zip}; - BitFileCompressor compressor{lib, BitFormat::Zip}; + BitFileCompressor compressor{_core::Bit7zipSingleton::getInstance(), BitFormat::Zip}; std::vector files = {this->tests_dir.string() + "/test_core.cpp"}; @@ -40,15 +40,14 @@ TEST_F(_core_pybit7z_test, compress) { // Compressing a single file into a buffer std::vector buffer; - BitFileCompressor compressor2{lib, BitFormat::BZip2}; + BitFileCompressor compressor2{_core::Bit7zipSingleton::getInstance(), BitFormat::BZip2}; compressor2.compressFile(files[0], buffer); } TEST_F(_core_pybit7z_test, archive_writer) { using namespace bit7z; - Bit7zLibrary lib{_core::default_lib7zip}; - BitArchiveWriter archive{lib, BitFormat::SevenZip}; + BitArchiveWriter archive{_core::Bit7zipSingleton::getInstance(), BitFormat::SevenZip}; // Adding the items to be compressed (no compression is performed here) archive.addFile(this->tests_dir.string() + "/test_core.cpp"); @@ -58,3 +57,22 @@ TEST_F(_core_pybit7z_test, archive_writer) { // Compressing the added items to the output archive archive.compressTo(this->system_test_tmp_dir_.string() + "/output.7z"); } + +TEST_F(_core_pybit7z_test, bzip) { + using namespace bit7z; + + BitFileCompressor compressor{_core::Bit7zipSingleton::getInstance(), BitFormat::BZip2}; + BitFileExtractor extractor{_core::Bit7zipSingleton::getInstance(), BitFormat::BZip2}; + + auto file = this->tests_dir / "test_core.cpp"; + std::vector files = {file.string()}; + + auto archive_file_name = file.filename().string() + ".bz2"; + // Bzip requires output file to have the same name as the file entry + compressor.compress(files, this->system_test_tmp_dir_.string() + "/" + archive_file_name); + + extractor.extract(this->system_test_tmp_dir_.string() + "/" + archive_file_name, + this->system_test_tmp_dir_.string() + "/" + archive_file_name + ".extracted"); + EXPECT_TRUE(std::filesystem::exists(this->system_test_tmp_dir_.string() + "/" + archive_file_name + ".extracted" + + "/" + file.filename().string())); +} diff --git a/src/pybit7z/__init__.py b/src/pybit7z/__init__.py index d26b1fc..8af1549 100644 --- a/src/pybit7z/__init__.py +++ b/src/pybit7z/__init__.py @@ -6,6 +6,20 @@ from __future__ import annotations +from pathlib import Path + +from importlib_metadata import distribution + +from pybit7z import _core + from ._version import version as __version__ __all__ = ["__version__"] + +if not Path(_core.set_lib7zip_path()).exists(): + lib7zip_path = ( + distribution(__package__).locate_file(__package__) / _core.set_lib7zip_path() + ) + if lib7zip_path.exists(): + _core.set_lib7zip_path(str(lib7zip_path)) + _core.set_large_page_mode() diff --git a/src/pybit7z/_core.pyi b/src/pybit7z/_core.pyi index 51fb416..5c15c39 100644 --- a/src/pybit7z/_core.pyi +++ b/src/pybit7z/_core.pyi @@ -1,6 +1,2282 @@ -"""Pybind11 _core plugin interfaces""" +""" + +Pybind11 _core plugin +----------------------- +.. currentmodule:: _core + +""" from __future__ import annotations +import datetime +import typing + +__all__ = [ + "CRC", + "ATime", + "AltStreamsSize", + "Attrib", + "BZip2", + "BigEndian", + "Bit64", + "BitAbstractArchiveCreator", + "BitAbstractArchiveHandler", + "BitAbstractArchiveOpener", + "BitArchiveEditor", + "BitArchiveItem", + "BitArchiveItemInfo", + "BitArchiveItemOffset", + "BitArchiveReader", + "BitArchiveWriter", + "BitCompressionLevel", + "BitCompressionMethod", + "BitException", + "BitFileCompressor", + "BitFileExtractor", + "BitGenericItem", + "BitInFormat", + "BitInOutFormat", + "BitInputArchive", + "BitMemCompressor", + "BitMemExtractor", + "BitOutputArchive", + "BitPropVariant", + "BitPropVariantType", + "BitProperty", + "BitStringCompressor", + "BitStringExtractor", + "Block", + "Bool", + "CTime", + "Characters", + "Checksum", + "ClusterSize", + "CodePage", + "Comment", + "Commented", + "Copy", + "CopyLink", + "Cpu", + "CreatorApp", + "Deflate", + "Deflate64", + "DeletePolicy", + "DictionarySize", + "EmbeddedStubSize", + "Empty", + "Encrypted", + "Error", + "ErrorFlags", + "ErrorType", + "Exclude", + "Extension", + "Fast", + "Fastest", + "FileSystem", + "FileTime", + "FilterPolicy", + "FormatAPM", + "FormatArj", + "FormatAuto", + "FormatBZip2", + "FormatCab", + "FormatChm", + "FormatCoff", + "FormatCompound", + "FormatCpio", + "FormatCramFS", + "FormatDeb", + "FormatDmg", + "FormatElf", + "FormatExt", + "FormatFat", + "FormatFeatures", + "FormatFlv", + "FormatGZip", + "FormatGpt", + "FormatHfs", + "FormatHxs", + "FormatIHex", + "FormatIso", + "FormatLzh", + "FormatLzma", + "FormatLzma86", + "FormatMacho", + "FormatMbr", + "FormatMslz", + "FormatMub", + "FormatNsis", + "FormatNtfs", + "FormatPe", + "FormatPpmd", + "FormatQcow", + "FormatRar", + "FormatRar5", + "FormatRpm", + "FormatSevenZip", + "FormatSplit", + "FormatSquashFS", + "FormatSwf", + "FormatSwfc", + "FormatTE", + "FormatTar", + "FormatUEFIc", + "FormatUEFIs", + "FormatUdf", + "FormatVdi", + "FormatVhd", + "FormatVhdx", + "FormatVmdk", + "FormatWim", + "FormatXar", + "FormatXz", + "FormatZ", + "FormatZip", + "FreeSpace", + "Group", + "HandlerItemIndex", + "HardLink", + "HeadersSize", + "HostOS", + "INode", + "Id", + "Include", + "Int8", + "Int16", + "Int32", + "Int64", + "IsAltStream", + "IsAnti", + "IsAux", + "IsDeleted", + "IsDir", + "IsNotArcType", + "IsTree", + "IsVolume", + "ItemOnly", + "Links", + "LocalName", + "Lzma", + "Lzma2", + "MTime", + "MainSubfile", + "Max", + "Method", + "Name", + "NoProperty", + "Normal", + "Nothing", + "NtReparse", + "NtSecure", + "NumAltStreams", + "NumBlocks", + "NumErrors", + "NumStreams", + "NumSubDirs", + "NumSubFiles", + "NumVolumes", + "Offset", + "OutName", + "Overwrite", + "OverwriteMode", + "PackSize", + "Path", + "PhySize", + "PhySizeCantBeDetected", + "Position", + "PosixAttrib", + "Ppmd", + "Prefix", + "Provider", + "ReadOnly", + "RecurseDirs", + "SectorSize", + "Sha1", + "Sha256", + "ShortComment", + "ShortName", + "Size", + "Skip", + "Solid", + "SplitAfter", + "SplitBefore", + "StreamId", + "String", + "SubType", + "SymLink", + "TailSize", + "TimeType", + "TotalPhySize", + "TotalSize", + "Type", + "UInt8", + "UInt16", + "UInt32", + "UInt64", + "Ultra", + "UnpackSize", + "UnpackVer", + "UpdateMode", + "User", + "Va", + "VirtualSize", + "Volume", + "VolumeIndex", + "VolumeName", + "Warning", + "WarningFlags", + "ZerosTailIsAllowed", + "set_large_page_mode", + "set_lib7zip_path", + "version", +] + +class BitAbstractArchiveCreator(BitAbstractArchiveHandler): + """ + Abstract class representing a generic archive creator. + """ + def compression_format(self) -> BitInOutFormat: + """ + the format used for creating/updating an archive. + """ + def compression_method(self) -> BitCompressionMethod: + """ + the compression method used for creating/updating an archive. + """ + def crypt_headers(self) -> bool: + """ + whether the creator crypts also the headers of archives or not. + """ + def dictionary_size(self) -> int: + """ + the dictionary size used for creating/updating an archive. + """ + def set_compression_level(self, level: BitCompressionLevel) -> None: + """ + Sets the compression level to be used when creating/updating an archive. + + Args: + level: the compression level desired. + """ + def set_compression_method(self, method: BitCompressionMethod) -> None: + """ + Sets the compression method to be used when creating/updating an archive. + + Args: + method: the compression method desired. + """ + def set_dictionary_size(self, dictionary_size: int) -> None: + """ + Sets the dictionary size to be used when creating/updating an archive. + + Args: + dictionary_size: the dictionary size desired. + """ + @typing.overload + def set_password(self, password: str) -> None: + """ + Sets up a password for the output archives. + + When setting a password, the produced archives will be encrypted using the default cryptographic method of the output format. The option "crypt headers" remains unchanged, in contrast with what happens when calling the set_password(tstring, bool) method. + + Args: + password: the password to be used when creating/updating archives. + + Note: + Calling set_password when the output format doesn't support archive encryption (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't throw exceptions, and it has no effects on compression operations). + After a password has been set, it will be used for every subsequent operation. To disable the use of the password, you need to call the clearPassword method (inherited from BitAbstractArchiveHandler), which is equivalent to set_password(L""). + """ + @typing.overload + def set_password(self, password: str, crypt_headers: bool) -> None: + """ + Sets up a password for the output archive. + + When setting a password, the produced archive will be encrypted using the default cryptographic method of the output format. If the format is 7z, and the option "cryptHeaders" is set to true, the headers of the archive will be encrypted, resulting in a password request every time the output file will be opened. + + Args: + password: the password to be used when creating/updating archives. + crypt_headers: if true, the headers of the output archives will be encrypted (valid only when using the 7z format). + + Note: + Calling set_password when the output format doesn't support archive encryption (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't throw exceptions, and it has no effects on compression operations). + Calling set_password with "cryptHeaders" set to true does not have effects on formats different from 7z. + After a password has been set, it will be used for every subsequent operation. To disable the use of the password, you need to call the clearPassword method (inherited from BitAbstractArchiveHandler), which is equivalent to set_password(L""). + """ + def set_solid_mode(self, solid_mode: bool) -> None: + """ + Sets whether the archive creator uses solid compression or not. + + Args: + solid_mode: the solid mode desired. + Note: + Setting the solid compression mode to true has effect only when using the 7z format with multiple input files. + """ + def set_store_symbolic_links(self, store_symbolic_links: bool) -> None: + """ + Sets whether the creator will store symbolic links as links in the output archive. + + Args: + store_symbolic_links: if true, symbolic links will be stored as links. + """ + def set_threads_count(self, threads_count: int) -> None: + """ + Sets the number of threads to be used when creating/updating an archive. + + Args: + threads_count: the number of threads desired. + """ + def set_update_mode(self, mode: UpdateMode) -> None: + """ + Sets whether and how the creator can update existing archives or not. + + Args: + mode: the desired update mode. + + Note: + If set to UpdateMode::None, a subsequent compression operation may throw an exception if it targets an existing archive. + """ + def set_volume_size(self, volume_size: int) -> None: + """ + Sets the volumeSize (in bytes) of the output archive volumes. + + Args: + volume_size: The dimension of a volume. + + Note: + This setting has effects only when the destination archive is on the filesystem. + """ + def set_word_size(self, word_size: int) -> None: + """ + Sets the word size to be used when creating/updating an archive. + + Args: + word_size: the word size desired. + """ + def solid_mode(self) -> bool: + """ + whether the archive creator uses solid compression or not. + """ + def store_symbolic_links(self) -> bool: + """ + whether the archive creator stores symbolic links as links in the output archive. + """ + def threads_count(self) -> int: + """ + the number of threads used when creating/updating an archive (a 0 value means that it will use the 7-zip default value). + """ + def update_mode(self) -> UpdateMode: + """ + the update mode used when updating existing archives. + """ + def volume_size(self) -> int: + """ + the volume size (in bytes) used when creating multi-volume archives (a 0 value means that all files are going in a single archive). + """ + def word_size(self) -> int: + """ + the word size used for creating/updating an archive. + """ + +class BitAbstractArchiveHandler: + """ + Abstract class representing a generic archive handler. + """ + def clear_password(self) -> None: + """ + Clear the current password used by the handler. + + Calling clear_password() will disable the encryption/decryption of archives. + + Note: + This is equivalent to calling set_password(L""). + """ + def file_callback(self) -> typing.Callable[[str], None]: + """ + the current file callback. + """ + def format(self) -> BitInFormat: + """ + the format used by the handler for extracting or compressing. + """ + def is_password_defined(self) -> bool: + """ + a boolean value indicating whether a password is defined or not. + """ + def overwrite_mode(self) -> OverwriteMode: + """ + the overwrite mode. + """ + def password(self) -> str: + """ + the password used to open, extract, or encrypt the archive. + """ + def password_callback(self) -> typing.Callable[[], str]: + """ + the current password callback. + """ + def progress_callback(self) -> typing.Callable[[int], bool]: + """ + the current progress callback. + """ + def ratio_callback(self) -> typing.Callable[[int, int], None]: + """ + the current ratio callback. + """ + def retainDirectories(self) -> bool: + """ + a boolean value indicating whether the directory structure must be preserved while extracting or compressing the archive. + """ + def set_file_callback(self, callback: typing.Callable[[str], None]) -> None: + """ + Sets the function to be called when the current file being processed changes. + + Args: + callback: the file callback to be used. + """ + def set_overwrite_mode(self, mode: OverwriteMode) -> None: + """ + Sets how the handler should behave when it tries to output to an existing file or buffer. + Args: + mode: the OverwriteMode to be used by the handler. + """ + def set_password(self, password: str) -> None: + """ + Sets up a password to be used by the archive handler. + + The password will be used to encrypt/decrypt archives by using the default cryptographic method of the archive format. + + Args: + password: the password to be used. + + Note: + Calling this method when the input archive is not encrypted does not have any effect on the extraction process. + Calling this method when the output format doesn't support archive encryption (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't throw exceptions, and it has no effects on compression operations). + After a password has been set, it will be used for every subsequent operation. To disable the use of the password, you need to call the clear_password method, which is equivalent to calling set_password(L""). + """ + def set_password_callback(self, callback: typing.Callable[[], str]) -> None: + """ + Sets the function to be called when a password is needed to complete the ongoing operation. + + Args: + callback: the password callback to be used. + """ + def set_progress_callback(self, callback: typing.Callable[[int], bool]) -> None: + """ + Sets the function to be called when the processed size of the ongoing operation is updated. + + Args: + callback: the progress callback to be used. + Note: + The completion percentage of the current operation can be obtained by calculating int((100.0 * processed_size) / total_size). + """ + def set_ratio_callback(self, callback: typing.Callable[[int, int], None]) -> None: + """ + Sets the function to be called when the input processed size and current output size of the ongoing operation are known. + + Args: + callback: the ratio callback to be used. + Note: + The ratio percentage of a compression operation can be obtained by calculating int((100.0 * output_size) / input_size). + """ + def set_retain_directories(self, retain: bool) -> None: + """ + Sets whether the operations' output will preserve the input's directory structure or not. + + Args: + retain: the setting for preserving or not the input directory structure + """ + def set_total_callback(self, callback: typing.Callable[[int], None]) -> None: + """ + Sets the function to be called when the total size of an operation is available. + + Args: + callback: the total callback to be used. + """ + def total_callback(self) -> typing.Callable[[int], None]: + """ + the current total callback. + """ + +class BitAbstractArchiveOpener(BitAbstractArchiveHandler): + def extraction_format(self) -> BitInFormat: + """ + the archive format used by the archive opener. + """ + +class BitArchiveEditor(BitArchiveWriter): + def __init__( + self, in_archive: str, format: BitInOutFormat, password: str = "" + ) -> None: + """ + Constructs a BitArchiveEditor object, reading the given archive file path. + """ + def apply_changes(self) -> None: + """ + Applies the requested changes (i.e., rename/update/delete operations) to the input archive. + """ + @typing.overload + def delete_item(self, index: int, policy: DeletePolicy) -> None: + """ + Marks as deleted the item at the given index. + + Args: + index: the index of the item to be deleted. + policy: the policy to be used when deleting items. + + Exceptions: + BitException if the index is invalid. + + Note: + By default, if the item is a folder, only its metadata is deleted, not the files within it. If instead the policy is set to DeletePolicy::RecurseDirs, then the items within the folder will also be deleted. + """ + @typing.overload + def delete_item(self, item_path: str, policy: DeletePolicy) -> None: + """ + Marks as deleted the archive's item(s) with the specified path. + + Args: + item_path: the path (in the archive) of the item to be deleted. + policy: the policy to be used when deleting items. + + Exceptions: + BitException if the specified path is empty or invalid, or if no matching item could be found. + + Note: + By default, if the marked item is a folder, only its metadata will be deleted, not the files within it. To delete the folder contents as well, set the policy to DeletePolicy::RecurseDirs. + The specified path must not begin with a path separator. + A path with a trailing separator will _only_ be considered if the policy is DeletePolicy::RecurseDirs, and will only match folders; with DeletePolicy::ItemOnly, no item will match a path with a trailing separator. + Generally, archives may contain multiple items with the same paths. If this is the case, all matching items will be marked as deleted according to the specified policy. + """ + @typing.overload + def rename_item(self, index: int, new_path: str) -> None: + """ + Requests to change the path of the item at the specified index with the given one. + + Args: + index: the index of the item to be renamed. + new_path: the new path of the item. + """ + @typing.overload + def rename_item(self, old_path: str, new_path: str) -> None: + """ + Requests to change the path of the item from oldPath to the newPath. + + Args: + old_path: the current path of the item to be renamed. + new_path: the new path of the item. + """ + def set_update_mode(self, update_mode: UpdateMode) -> None: + """ + Sets how the editor performs the update of the items in the archive. + + Args: + mode: the desired update mode (either UpdateMode::Append or UpdateMode::Overwrite). + + Note: + BitArchiveEditor doesn't support UpdateMode::Nothing. + """ + @typing.overload + def update_item(self, index: int, in_file: str) -> None: + """ + Requests to update the content of the item at the specified index with the data from the given file. + + Args: + index: the index of the item to be updated. + in_file: the path of the file to be used for the update. + """ + @typing.overload + def update_item(self, index: int, in_buffer: bytes) -> None: + """ + Requests to update the content of the item at the specified index with the data from the given buffer. + + Args: + index: the index of the item to be updated. + in_buffer: the buffer containing the new data for the item. + """ + @typing.overload + def update_item(self, item_path: str, in_file: str) -> None: + """ + Requests to update the content of the item at the specified path with the data from the given file. + + Args: + item_path: the path of the item to be updated. + in_file: the path of the file to be used for the update. + """ + @typing.overload + def update_item(self, item_path: str, in_buffer: bytes) -> None: + """ + Requests to update the content of the item at the specified path with the data from the given buffer. + + Args: + item_path: the path of the item to be updated. + in_buffer: the buffer containing the new data for the item. + """ + +class BitArchiveItem(BitGenericItem): + """ + The BitArchiveItem class represents a generic item inside an archive. + """ + def attributes(self) -> int: + """ + the item attributes. + """ + def crc(self) -> int: + """ + the CRC of the item. + """ + def creation_time(self) -> datetime.datetime: ... + def extension(self) -> str: + """ + the extension of the item, if available or if it can be inferred from the name; otherwise it returns an empty string (e.g., when the item is a folder). + """ + def index(self) -> int: + """ + the index of the item in the archive. + """ + def is_encrypted(self) -> bool: + """ + true if and only if the item is encrypted. + """ + def last_access_time(self) -> datetime.datetime: ... + def last_write_time(self) -> datetime.datetime: ... + def native_path(self) -> str: + """ + the path of the item in the archive, if available or inferable from the name, or an empty string otherwise. + """ + def pack_size(self) -> int: + """ + the compressed size of the item. + """ + +class BitArchiveItemInfo(BitArchiveItem): + """ + The BitArchiveItemInfo class represents an archived item and that stores all its properties for later use. + """ + def item_properties(self) -> dict[BitProperty, BitPropVariant]: + """ + a map of all the available (i.e., non-empty) item properties and their respective values. + """ + def item_property(self, arg0: BitProperty) -> BitPropVariant: + """ + Gets the specified item property. + Args: + property_id (bit7z::BitProperty): The ID of the property to get. + Returns: + BitPropVariant: the value of the item property, if available, or an empty BitPropVariant. + """ + +class BitArchiveItemOffset(BitArchiveItem): + """ + The BitArchiveItemOffset class represents an archived item but doesn't store its properties. + """ + + def __iadd__(self, arg0: int) -> BitArchiveItemOffset: ... + def item_property(self, arg0: BitProperty) -> BitPropVariant: + """ + Gets the specified item property. + + Args: + property_id (bit7z::BitProperty): The ID of the property to get. + + Returns: + BitPropVariant: the value of the item property, if available, or an empty BitPropVariant. + """ + +class BitArchiveReader(BitAbstractArchiveOpener, BitInputArchive): + @staticmethod + @typing.overload + def is_header_encrypted(in_archive: str, format: BitInFormat) -> bool: + """ + Checks if the given archive is header-encrypted or not. + """ + @staticmethod + @typing.overload + def is_header_encrypted(in_archive: bytes, format: BitInFormat) -> bool: + """ + Checks if the given memory buffer archive is header-encrypted or not. + """ + @typing.overload + def __init__( + self, in_archive: str, format: BitInFormat, password: str = "" + ) -> None: + """ + Constructs a BitArchiveReader object, opening the input file archive. + + Args: + in_archive: the path to the archive to be read. + format: the format of the input archive. + password: the password needed for opening the input archive. + """ + @typing.overload + def __init__( + self, in_archive: bytes, format: BitInFormat, password: str = "" + ) -> None: + """ + Constructs a BitArchiveReader object, opening the input memory buffer archive. + + Args: + in_archive: the input buffer containing the archive to be read. + format: the format of the input archive. + password: the password needed for opening the input archive. + """ + def archive_properties(self) -> dict[BitProperty, BitPropVariant]: + """ + a map of all the available (i.e., non-empty) archive properties and their respective values. + """ + def files_count(self) -> int: + """ + the number of files in the archive. + """ + def folders_count(self) -> int: + """ + the number of folders in the archive. + """ + def has_encrypted_items(self) -> bool: + """ + true if and only if the archive has at least one encrypted item. + """ + def is_encrypted(self) -> bool: + """ + true if and only if the archive has only encrypted items. + """ + def is_multi_volume(self) -> bool: + """ + true if and only if the archive is composed by multiple volumes. + """ + def is_solid(self) -> bool: + """ + true if and only if the archive was created using solid compression. + """ + def items(self) -> list[BitArchiveItemInfo]: + """ + the list of all the archive items as BitArchiveItem objects. + """ + def pack_size(self) -> int: + """ + the total compressed size of the archive content. + """ + def size(self) -> int: + """ + the total uncompressed size of the archive content. + """ + def volumes_count(self) -> int: + """ + the number of volumes in the archive. + """ + +class BitArchiveWriter(BitAbstractArchiveCreator, BitOutputArchive): + @typing.overload + def __init__(self, format: BitInOutFormat) -> None: + """ + Constructs an empty BitArchiveWriter object that can write archives of the specified format. + """ + @typing.overload + def __init__( + self, in_archive: str, format: BitInOutFormat, password: str = "" + ) -> None: + """ + Constructs a BitArchiveWriter object, reading the given archive file path. + """ + @typing.overload + def __init__( + self, in_archive: bytes, format: BitInOutFormat, password: str = "" + ) -> None: + """ + Constructs a BitArchiveWriter object, reading the given memory buffer archive. + """ + +class BitCompressionLevel: + """ + Compression level for 7zip library + + Members: + + Nothing + + Fastest + + Fast + + Normal + + Max + + Ultra + """ + + Fast: typing.ClassVar[BitCompressionLevel] # value = + Fastest: typing.ClassVar[ + BitCompressionLevel + ] # value = + Max: typing.ClassVar[BitCompressionLevel] # value = + Normal: typing.ClassVar[ + BitCompressionLevel + ] # value = + Nothing: typing.ClassVar[ + BitCompressionLevel + ] # value = + Ultra: typing.ClassVar[ + BitCompressionLevel + ] # value = + __members__: typing.ClassVar[ + dict[str, BitCompressionLevel] + ] # value = {'Nothing': , 'Fastest': , 'Fast': , 'Normal': , 'Max': , 'Ultra': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class BitCompressionMethod: + """ + Compression method by bit7z when creating archives. + + Members: + + Copy + + Deflate + + Deflate64 + + BZip2 + + Lzma + + Lzma2 + + Ppmd + """ + + BZip2: typing.ClassVar[ + BitCompressionMethod + ] # value = + Copy: typing.ClassVar[ + BitCompressionMethod + ] # value = + Deflate: typing.ClassVar[ + BitCompressionMethod + ] # value = + Deflate64: typing.ClassVar[ + BitCompressionMethod + ] # value = + Lzma: typing.ClassVar[ + BitCompressionMethod + ] # value = + Lzma2: typing.ClassVar[ + BitCompressionMethod + ] # value = + Ppmd: typing.ClassVar[ + BitCompressionMethod + ] # value = + __members__: typing.ClassVar[ + dict[str, BitCompressionMethod] + ] # value = {'Copy': , 'Deflate': , 'Deflate64': , 'BZip2': , 'Lzma': , 'Lzma2': , 'Ppmd': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class BitException(Exception): + pass + +class BitFileCompressor(BitStringCompressor): + def __init__(self, format: BitInOutFormat) -> None: + """ + Constructs a BitFileCompressor object, creating a new archive. + """ + @typing.overload + def compress(self, in_files: list[str], out_archive: str) -> None: + """ + Compresses the given files or directories. + + The items in the first argument must be the relative or absolute paths to files or directories existing on the filesystem. + + Args: + in_files: the input files to be compressed. + out_archive: the path (relative or absolute) to the output archive file. + """ + @typing.overload + def compress(self, in_files: dict[str, str], out_archive: str) -> None: + """ + Compresses the given files or directories using the specified aliases. + + The items in the first argument must be the relative or absolute paths to files or directories existing on the filesystem. Each pair in the map must follow the following format: {"path to file in the filesystem", "alias path in the archive"}. + + + Args: + in_files: a map of paths and corresponding aliases. + out_archive: the path (relative or absolute) to the output archive file. + """ + def compress_directory(self, in_dir: str, out_archive: str) -> None: + """ + Compresses an entire directory. + + Args: + in_dir: the path (relative or absolute) to the input directory. + out_archive: the path (relative or absolute) to the output archive file. + Note: + This method is equivalent to compress_files with filter set to "". + """ + def compress_directory_contents( + self, + in_dir: str, + out_archive: str, + recursive: bool = True, + filter_pattern: str = "*", + ) -> None: + """ + Compresses the contents of a directory. + + Args: + in_dir: the path (relative or absolute) to the input directory. + out_archive: the path (relative or absolute) to the output archive file. + recursive: (optional) if true, it searches files inside the sub-folders of in_dir. + filter_pattern: the wildcard pattern to filter the files to be compressed (optional). + Note: + Unlike compress_files, this method includes also the metadata of the sub-folders. + """ + @typing.overload + def compress_files(self, in_files: list[str], out_archive: str) -> None: + """ + Compresses a group of files. + + Args: + in_files: the input files to be compressed. + out_archive: the path (relative or absolute) to the output archive file. + Note: + Any path to a directory or to a not-existing file will be ignored! + """ + @typing.overload + def compress_files( + self, + in_dir: str, + out_archive: str, + recursive: bool = True, + filter_pattern: str = "*", + ) -> None: + """ + Compresses all the files in the given directory. + + Args: + in_dir: the path (relative or absolute) to the input directory. + out_archive: the path (relative or absolute) to the output archive file. + recursive: (optional) if true, it searches files inside the sub-folders of in_dir. + filter_pattern: the wildcard pattern to filter the files to be compressed (optional). + """ + +class BitGenericItem: + """ + The BitGenericItem interface class represents a generic item (either inside or outside an archive). + """ + def attributes(self) -> int: + """ + the item attributes. + """ + def is_dir(self) -> bool: ... + def name(self) -> str: + """ + the name of the item, if available or inferable from the path, or an empty string otherwise. + """ + def path(self) -> str: + """ + the path of the item. + """ + def size(self) -> int: + """ + the uncompressed size of the item. + """ + +class BitInFormat: + """ + The BitInFormat class specifies an extractable archive format. + """ + + def value(self) -> int: + """ + the value of the format in the 7z SDK. + """ + +class BitInOutFormat(BitInFormat): + """ + The BitInOutFormat class specifies a format available for creating new archives and extract old ones. + """ + def default_method(self) -> BitCompressionMethod: + """ + the default method used for compressing the archive format. + """ + def extension(self) -> str: + """ + the default file extension of the archive format. + """ + def features(self) -> FormatFeatures: + """ + the bitset of the features supported by the format. + """ + def has_feature(self, arg0: FormatFeatures) -> bool: + """ + Checks if the format has a specific feature (see FormatFeatures enum) + Args: + feature (FormatFeatures): the feature to check + Returns: + bool: a boolean value indicating whether the format has the given feature. + """ + +class BitInputArchive: + def archive_path(self) -> str: + """ + the path to the archive (the empty string for buffer/stream archives). + """ + def archive_property(self, arg0: BitProperty) -> BitPropVariant: + """ + Gets the specified archive property. + + Args: + property: the property to be retrieved. + + Returns: + the current value of the archive property or an empty BitPropVariant if no value is specified. + """ + def contains(self, path: str) -> bool: + """ + Find if there is an item in the archive that has the given path. + + Args: + path: the path of the file or folder to be checked. + + Returns: + true if and only if the archive contains the specified file or folder. + """ + def detected_format(self) -> BitInFormat: + """ + the detected format of the file. + """ + @typing.overload + def extract_to(self, path: str) -> None: + """ + Extracts the archive to the chosen directory. + + Args: + outDir: the output directory where the extracted files will be put. + """ + @typing.overload + def extract_to(self, out_dir: str, indices: list[int]) -> None: + """ + Extracts the specified items to the chosen directory. + + Args: + out_dir: the output directory where the extracted files will be put. + indices: the array of indices of the files in the archive that must be extracted. + """ + @typing.overload + def extract_to(self, index: int) -> bytes: + """ + Extracts a file to the output buffer. + + Args: + index: the index of the file to be extracted. + """ + @typing.overload + def extract_to(self) -> dict[str, bytes]: + """ + Extracts the content of the archive to a map of memory buffers, where the keys are the paths of the files (inside the archive), and the values are their decompressed contents. + """ + def is_item_encrypted(self, index: int) -> bool: + """ + Whether the item at the given index is encrypted. + + Args: + index: the index of an item in the archive. + + Returns: + true if and only if the item at the given index is encrypted. + """ + def is_item_folder(self, index: int) -> bool: + """ + Whether the item at the given index is a folder. + Args: + index: the index of an item in the archive. + + Returns: + true if and only if the item at the given index is a folder. + """ + def item_at(self, index: int) -> BitArchiveItemOffset: + """ + Retrieve the item at the given index. + + Args: + index: the index of the item to be retrieved. + + Returns: + the item at the given index within the archive. + """ + def item_property(self, index: int, property: BitProperty) -> BitPropVariant: + """ + Gets the specified item property. + + Args: + index: the index of the item to retrieve the property from. + property: the property to be retrieved. + + Returns: + the current value of the item property or an empty BitPropVariant if no value is specified. + """ + def items_count(self) -> int: + """ + the number of items in the archive. + """ + def test(self) -> None: + """ + Tests the archive without extracting its content. + + If the archive is not valid, a BitException is thrown! + """ + def test_item(self, index: int) -> None: + """ + Tests the item at the given index inside the archive without extracting it. + + If the archive is not valid, or there's no item at the given index, a BitException is thrown! + """ + +class BitMemCompressor(BitAbstractArchiveCreator): + def __init__(self, format: BitInOutFormat) -> None: + """ + Constructs a BitMemCompressor object, creating a new archive. + """ + @typing.overload + def compress_file(self, input: bytes, out_file: str, input_name: str = "") -> None: + """ + Compresses the given memory buffer to the chosen archive. + + Args: + input: the input memory buffer to be compressed. + out_file: the path (relative or absolute) to the output archive file. + input_name: (optional) the name to give to the compressed file inside the output archive. + """ + @typing.overload + def compress_file(self, input: bytes, input_name: str = "") -> bytes: + """ + Compresses the given memory buffer to a memory buffer. + + Args: + input: the input memory buffer to be compressed. + input_name: (optional) the name to give to the compressed file inside the output archive. + """ + +class BitMemExtractor(BitAbstractArchiveOpener): + def __init__(self, format: BitInFormat) -> None: + """ + Constructs a BitMemExtractor object, opening the input archive. + """ + @typing.overload + def extract(self, in_archive: list[int], out_dir: str = "") -> None: + """ + Extracts the given archive to the chosen directory. + """ + @typing.overload + def extract(self, in_archive: bytes, index: int) -> bytes: + """ + Extracts the specified item from the given archive to a memory buffer. + """ + @typing.overload + def extract(self, in_archive: bytes) -> dict[str, bytes]: + """ + Extracts all the items from the given archive to a dictionary of memory buffers. + """ + def extract_items( + self, in_archive: list[int], indices: list[int], out_dir: str = "" + ) -> None: + """ + Extracts the specified items from the given archive to the chosen directory. + """ + @typing.overload + def extract_matching( + self, + in_archive: list[int], + pattern: str, + out_dir: str, + filter_policy: FilterPolicy, + ) -> None: + """ + Extracts the files in the archive that match the given wildcard pattern to the chosen directory. + """ + @typing.overload + def extract_matching( + self, in_archive: bytes, pattern: bytes, filter_policy: FilterPolicy + ) -> bytes: + """ + Extracts to the output buffer the first file in the archive matching the given wildcard pattern. + """ + @typing.overload + def extract_matching_regex( + self, + in_archive: list[int], + regex: str, + out_dir: str, + filter_policy: FilterPolicy, + ) -> None: + """ + Extracts the files in the archive that match the given regex pattern to the chosen directory. + """ + @typing.overload + def extract_matching_regex( + self, in_archive: bytes, regex: bytes, filter_policy: FilterPolicy + ) -> bytes: + """ + Extracts to the output buffer the first file in the archive matching the given regex pattern. + """ + def test(self, in_archive: list[int]) -> None: + """ + Tests the given archive without extracting its content. + + If the archive is not valid, a BitException is thrown! + + Args: + in_archive: the input archive to be tested. + """ + +class BitOutputArchive: + def add_directory(self, in_dir: str) -> None: + """ + Adds the given directory path and all its content. + Args: + in_dir: the path of the directory to be added to the archive. + """ + @typing.overload + def add_directory_contents(self, in_dir: str, filter: str, recursive: bool) -> None: + """ + Adds the contents of the given directory path. + + This function iterates through the specified directory and adds its contents based on the provided wildcard filter. Optionally, the operation can be recursive, meaning it will include subdirectories and their contents. + + Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: the wildcard filter to be used for searching the files. + recursive: recursively search the files in the given directory and all of its subdirectories. + """ + @typing.overload + def add_directory_contents( + self, in_dir: str, filter: str, recursive: FilterPolicy, policy: bool + ) -> None: + """ + Adds the contents of the given directory path. + + This function iterates through the specified directory and adds its contents based on the provided wildcard filter and policy. Optionally, the operation can be recursive, meaning it will include subdirectories and their contents. + + Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: the wildcard filter to be used for searching the files. + recursive: recursively search the files in the given directory and all of its subdirectories. + policy: the filtering policy to be applied to the matched items. + """ + @typing.overload + def add_file(self, in_file: str, name: str) -> None: + """ + Adds the given file path, with an optional user-defined path to be used in the output archive. + + Args: + in_file: the path to the filesystem file to be added to the output archive. + name: (optional) user-defined path to be used inside the output archive. + Note: + If a directory path is given, a BitException is thrown. + """ + @typing.overload + def add_file(self, input: bytes, name: str) -> None: + """ + Adds the given memory buffer, with an optional user-defined path to be used in the output archive. + + Args: + input: the memory buffer to be added to the output archive. + name: user-defined path to be used inside the output archive. + """ + @typing.overload + def add_files(self, in_files: list[str]) -> None: + """ + Adds all the files in the given vector of filesystem paths. + + Args: + in_files: the paths to be added to the archive. + Note: + Paths to directories are ignored. + """ + @typing.overload + def add_files(self, in_dir: str, filter: str, recursive: bool) -> None: + """ + Adds all the files inside the given directory path that match the given wildcard filter. + + Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: (optional) the filter pattern to be used to select the files to be added. + recursive: (optional) if true, the directory will be searched recursively. + Note: + If a file path is given, a BitException is thrown. + """ + @typing.overload + def add_files( + self, in_dir: str, filter: str, recursive: FilterPolicy, policy: bool + ) -> None: + """ + Adds all the files inside the given directory path that match the given wildcard filter, with the specified filter policy. + + Args: + in_dir: the directory where to search for files to be added to the output archive. + filter: (optional) the wildcard filter to be used for searching the files. + recursive: (optional) recursively search the files in the given directory and all of its subdirectories. + policy: (optional) the filtering policy to be applied to the matched items. + """ + @typing.overload + def add_items(self, paths: list[str]) -> None: + """ + Adds all the items that can be found by indexing the given vector of filesystem paths. + + Args: + paths: the paths to be added to the archive. + """ + @typing.overload + def add_items(self, files: dict[str, str]) -> None: + """ + Adds all the items that can be found by indexing the keys of the given map of filesystem paths; the corresponding mapped values are the user-defined paths wanted inside the output archive. + + Args: + files: the map of file paths and their contents to be added to the archive. + """ + @typing.overload + def compress_to(self, out_file: str) -> None: + """ + Compresses all the items added to this object to the specified archive file path. + + Args: + out_file: the output archive file path. + + Note: + If this object was created by passing an input archive file path, and this latter is the same as the outFile path parameter, the file will be updated. + """ + @typing.overload + def compress_to(self) -> bytes: + """ + Compresses all the items added to this object to the specified buffer. + """ + def items_count(self) -> int: + """ + the number of items in the archive. + """ + +class BitPropVariant: + """ + The BitPropVariant struct is a light extension to the WinAPI PROPVARIANT struct providing useful getters. + """ + @typing.overload + def __init__(self) -> None: ... + @typing.overload + def __init__(self, value: bool) -> None: ... + @typing.overload + def __init__(self, value: int) -> None: ... + def clear(self) -> None: + """ + Clears the variant. + """ + def get_bool(self) -> bool: ... + def get_file_time(self) -> datetime.datetime: ... + def get_int64(self) -> int: ... + def get_native_string(self) -> str: ... + def get_string(self) -> str: ... + def get_uint64(self) -> int: ... + def is_bool(self) -> bool: ... + def is_file_time(self) -> bool: ... + def is_int16(self) -> bool: ... + def is_int32(self) -> bool: ... + def is_int64(self) -> bool: ... + def is_int8(self) -> bool: ... + def is_string(self) -> bool: ... + def is_uint16(self) -> bool: ... + def is_uint32(self) -> bool: ... + def is_uint64(self) -> bool: ... + def is_uint8(self) -> bool: ... + def type(self) -> BitPropVariantType: + """ + Returns the type of the variant. + """ + +class BitPropVariantType: + """ + The BitPropVariantType enum represents the possible types that a BitPropVariant can store. + + Members: + + Empty + + Bool + + String + + UInt8 + + UInt16 + + UInt32 + + UInt64 + + Int8 + + Int16 + + Int32 + + Int64 + + FileTime + """ + + Bool: typing.ClassVar[BitPropVariantType] # value = + Empty: typing.ClassVar[BitPropVariantType] # value = + FileTime: typing.ClassVar[ + BitPropVariantType + ] # value = + Int16: typing.ClassVar[BitPropVariantType] # value = + Int32: typing.ClassVar[BitPropVariantType] # value = + Int64: typing.ClassVar[BitPropVariantType] # value = + Int8: typing.ClassVar[BitPropVariantType] # value = + String: typing.ClassVar[ + BitPropVariantType + ] # value = + UInt16: typing.ClassVar[ + BitPropVariantType + ] # value = + UInt32: typing.ClassVar[ + BitPropVariantType + ] # value = + UInt64: typing.ClassVar[ + BitPropVariantType + ] # value = + UInt8: typing.ClassVar[BitPropVariantType] # value = + __members__: typing.ClassVar[ + dict[str, BitPropVariantType] + ] # value = {'Empty': , 'Bool': , 'String': , 'UInt8': , 'UInt16': , 'UInt32': , 'UInt64': , 'Int8': , 'Int16': , 'Int32': , 'Int64': , 'FileTime': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class BitProperty: + """ + The BitProperty enum represents the archive/item properties that 7-zip can read or write. + + Members: + + NoProperty + + MainSubfile + + HandlerItemIndex + + Path + + Name + + Extension + + IsDir + + Size + + PackSize + + Attrib + + CTime + + ATime + + MTime + + Solid + + Commented + + Encrypted + + SplitBefore + + SplitAfter + + DictionarySize + + CRC + + Type + + IsAnti + + Method + + HostOS + + FileSystem + + User + + Group + + Block + + Comment + + Position + + Prefix + + NumSubDirs + + NumSubFiles + + UnpackVer + + Volume + + IsVolume + + Offset + + Links + + NumBlocks + + NumVolumes + + TimeType + + Bit64 + + BigEndian + + Cpu + + PhySize + + HeadersSize + + Checksum + + Characters + + Va + + Id + + ShortName + + CreatorApp + + SectorSize + + PosixAttrib + + SymLink + + Error + + TotalSize + + FreeSpace + + ClusterSize + + VolumeName + + LocalName + + Provider + + NtSecure + + IsAltStream + + IsAux + + IsDeleted + + IsTree + + Sha1 + + Sha256 + + ErrorType + + NumErrors + + ErrorFlags + + WarningFlags + + Warning + + NumStreams + + NumAltStreams + + AltStreamsSize + + VirtualSize + + UnpackSize + + TotalPhySize + + VolumeIndex + + SubType + + ShortComment + + CodePage + + IsNotArcType + + PhySizeCantBeDetected + + ZerosTailIsAllowed + + TailSize + + EmbeddedStubSize + + NtReparse + + HardLink + + INode + + StreamId + + ReadOnly + + OutName + + CopyLink + """ + + ATime: typing.ClassVar[BitProperty] # value = + AltStreamsSize: typing.ClassVar[ + BitProperty + ] # value = + Attrib: typing.ClassVar[BitProperty] # value = + BigEndian: typing.ClassVar[BitProperty] # value = + Bit64: typing.ClassVar[BitProperty] # value = + Block: typing.ClassVar[BitProperty] # value = + CRC: typing.ClassVar[BitProperty] # value = + CTime: typing.ClassVar[BitProperty] # value = + Characters: typing.ClassVar[BitProperty] # value = + Checksum: typing.ClassVar[BitProperty] # value = + ClusterSize: typing.ClassVar[BitProperty] # value = + CodePage: typing.ClassVar[BitProperty] # value = + Comment: typing.ClassVar[BitProperty] # value = + Commented: typing.ClassVar[BitProperty] # value = + CopyLink: typing.ClassVar[BitProperty] # value = + Cpu: typing.ClassVar[BitProperty] # value = + CreatorApp: typing.ClassVar[BitProperty] # value = + DictionarySize: typing.ClassVar[ + BitProperty + ] # value = + EmbeddedStubSize: typing.ClassVar[ + BitProperty + ] # value = + Encrypted: typing.ClassVar[BitProperty] # value = + Error: typing.ClassVar[BitProperty] # value = + ErrorFlags: typing.ClassVar[BitProperty] # value = + ErrorType: typing.ClassVar[BitProperty] # value = + Extension: typing.ClassVar[BitProperty] # value = + FileSystem: typing.ClassVar[BitProperty] # value = + FreeSpace: typing.ClassVar[BitProperty] # value = + Group: typing.ClassVar[BitProperty] # value = + HandlerItemIndex: typing.ClassVar[ + BitProperty + ] # value = + HardLink: typing.ClassVar[BitProperty] # value = + HeadersSize: typing.ClassVar[BitProperty] # value = + HostOS: typing.ClassVar[BitProperty] # value = + INode: typing.ClassVar[BitProperty] # value = + Id: typing.ClassVar[BitProperty] # value = + IsAltStream: typing.ClassVar[BitProperty] # value = + IsAnti: typing.ClassVar[BitProperty] # value = + IsAux: typing.ClassVar[BitProperty] # value = + IsDeleted: typing.ClassVar[BitProperty] # value = + IsDir: typing.ClassVar[BitProperty] # value = + IsNotArcType: typing.ClassVar[BitProperty] # value = + IsTree: typing.ClassVar[BitProperty] # value = + IsVolume: typing.ClassVar[BitProperty] # value = + Links: typing.ClassVar[BitProperty] # value = + LocalName: typing.ClassVar[BitProperty] # value = + MTime: typing.ClassVar[BitProperty] # value = + MainSubfile: typing.ClassVar[BitProperty] # value = + Method: typing.ClassVar[BitProperty] # value = + Name: typing.ClassVar[BitProperty] # value = + NoProperty: typing.ClassVar[BitProperty] # value = + NtReparse: typing.ClassVar[BitProperty] # value = + NtSecure: typing.ClassVar[BitProperty] # value = + NumAltStreams: typing.ClassVar[ + BitProperty + ] # value = + NumBlocks: typing.ClassVar[BitProperty] # value = + NumErrors: typing.ClassVar[BitProperty] # value = + NumStreams: typing.ClassVar[BitProperty] # value = + NumSubDirs: typing.ClassVar[BitProperty] # value = + NumSubFiles: typing.ClassVar[BitProperty] # value = + NumVolumes: typing.ClassVar[BitProperty] # value = + Offset: typing.ClassVar[BitProperty] # value = + OutName: typing.ClassVar[BitProperty] # value = + PackSize: typing.ClassVar[BitProperty] # value = + Path: typing.ClassVar[BitProperty] # value = + PhySize: typing.ClassVar[BitProperty] # value = + PhySizeCantBeDetected: typing.ClassVar[ + BitProperty + ] # value = + Position: typing.ClassVar[BitProperty] # value = + PosixAttrib: typing.ClassVar[BitProperty] # value = + Prefix: typing.ClassVar[BitProperty] # value = + Provider: typing.ClassVar[BitProperty] # value = + ReadOnly: typing.ClassVar[BitProperty] # value = + SectorSize: typing.ClassVar[BitProperty] # value = + Sha1: typing.ClassVar[BitProperty] # value = + Sha256: typing.ClassVar[BitProperty] # value = + ShortComment: typing.ClassVar[BitProperty] # value = + ShortName: typing.ClassVar[BitProperty] # value = + Size: typing.ClassVar[BitProperty] # value = + Solid: typing.ClassVar[BitProperty] # value = + SplitAfter: typing.ClassVar[BitProperty] # value = + SplitBefore: typing.ClassVar[BitProperty] # value = + StreamId: typing.ClassVar[BitProperty] # value = + SubType: typing.ClassVar[BitProperty] # value = + SymLink: typing.ClassVar[BitProperty] # value = + TailSize: typing.ClassVar[BitProperty] # value = + TimeType: typing.ClassVar[BitProperty] # value = + TotalPhySize: typing.ClassVar[BitProperty] # value = + TotalSize: typing.ClassVar[BitProperty] # value = + Type: typing.ClassVar[BitProperty] # value = + UnpackSize: typing.ClassVar[BitProperty] # value = + UnpackVer: typing.ClassVar[BitProperty] # value = + User: typing.ClassVar[BitProperty] # value = + Va: typing.ClassVar[BitProperty] # value = + VirtualSize: typing.ClassVar[BitProperty] # value = + Volume: typing.ClassVar[BitProperty] # value = + VolumeIndex: typing.ClassVar[BitProperty] # value = + VolumeName: typing.ClassVar[BitProperty] # value = + Warning: typing.ClassVar[BitProperty] # value = + WarningFlags: typing.ClassVar[BitProperty] # value = + ZerosTailIsAllowed: typing.ClassVar[ + BitProperty + ] # value = + __members__: typing.ClassVar[ + dict[str, BitProperty] + ] # value = {'NoProperty': , 'MainSubfile': , 'HandlerItemIndex': , 'Path': , 'Name': , 'Extension': , 'IsDir': , 'Size': , 'PackSize': , 'Attrib': , 'CTime': , 'ATime': , 'MTime': , 'Solid': , 'Commented': , 'Encrypted': , 'SplitBefore': , 'SplitAfter': , 'DictionarySize': , 'CRC': , 'Type': , 'IsAnti': , 'Method': , 'HostOS': , 'FileSystem': , 'User': , 'Group': , 'Block': , 'Comment': , 'Position': , 'Prefix': , 'NumSubDirs': , 'NumSubFiles': , 'UnpackVer': , 'Volume': , 'IsVolume': , 'Offset': , 'Links': , 'NumBlocks': , 'NumVolumes': , 'TimeType': , 'Bit64': , 'BigEndian': , 'Cpu': , 'PhySize': , 'HeadersSize': , 'Checksum': , 'Characters': , 'Va': , 'Id': , 'ShortName': , 'CreatorApp': , 'SectorSize': , 'PosixAttrib': , 'SymLink': , 'Error': , 'TotalSize': , 'FreeSpace': , 'ClusterSize': , 'VolumeName': , 'LocalName': , 'Provider': , 'NtSecure': , 'IsAltStream': , 'IsAux': , 'IsDeleted': , 'IsTree': , 'Sha1': , 'Sha256': , 'ErrorType': , 'NumErrors': , 'ErrorFlags': , 'WarningFlags': , 'Warning': , 'NumStreams': , 'NumAltStreams': , 'AltStreamsSize': , 'VirtualSize': , 'UnpackSize': , 'TotalPhySize': , 'VolumeIndex': , 'SubType': , 'ShortComment': , 'CodePage': , 'IsNotArcType': , 'PhySizeCantBeDetected': , 'ZerosTailIsAllowed': , 'TailSize': , 'EmbeddedStubSize': , 'NtReparse': , 'HardLink': , 'INode': , 'StreamId': , 'ReadOnly': , 'OutName': , 'CopyLink': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class BitStringCompressor(BitAbstractArchiveCreator): + def __init__(self, format: BitInOutFormat) -> None: + """ + Constructs a BitStringCompressor object, creating a new archive. + """ + @typing.overload + def compress_file(self, in_file: str, out_file: str, input_name: str = "") -> None: + """ + Compresses the given file to the chosen archive. + + Args: + in_file: the input file to be compressed. + out_file: the path (relative or absolute) to the output archive file. + input_name: the name of the input file in the archive (optional). + """ + @typing.overload + def compress_file(self, in_file: str, input_name: str = "") -> bytes: + """ + Compresses the given file to a memory buffer. + + Args: + in_file: the input file to be compressed. + input_name: the name of the input file in the archive (optional). + """ + +class BitStringExtractor(BitAbstractArchiveOpener): + def __init__(self, format: BitInFormat) -> None: + """ + Constructs a BitStringExtractor object, opening the input archive. + """ + @typing.overload + def extract(self, in_archive: str, out_dir: str) -> None: + """ + Extracts the given archive to the chosen directory. + """ + @typing.overload + def extract(self, in_archive: str, index: int) -> bytes: + """ + Extracts the specified item from the given archive to a memory buffer. + """ + @typing.overload + def extract(self, in_archive: str) -> dict[str, bytes]: + """ + Extracts all the items from the given archive to a dictionary of memory buffers. + """ + def extract_items( + self, in_archive: str, indices: list[int], out_dir: str = "" + ) -> None: ... + @typing.overload + def extract_matching( + self, in_archive: str, pattern: str, out_dir: str, filter_policy: FilterPolicy + ) -> None: + """ + Extracts the files in the archive that match the given wildcard pattern to the chosen directory. + """ + @typing.overload + def extract_matching( + self, in_archive: str, pattern: str, filter_policy: FilterPolicy + ) -> bytes: + """ + Extracts to the output buffer the first file in the archive matching the given wildcard pattern. + """ + @typing.overload + def extract_matching_regex( + self, in_archive: str, regex: str, out_dir: str, filter_policy: FilterPolicy + ) -> None: + """ + Extracts the files in the archive that match the given regex pattern to the chosen directory. + """ + @typing.overload + def extract_matching_regex( + self, in_archive: str, regex: str, filter_policy: FilterPolicy + ) -> bytes: + """ + Extracts to the output buffer the first file in the archive matching the given regex pattern. + """ + def test(self, in_archive: str) -> None: + """ + Tests the given archive without extracting its content. + + If the archive is not valid, a BitException is thrown! + + Args: + in_archive: the input archive to be tested. + """ + +class DeletePolicy: + """ + Delete policy for archive items. + + Members: + + ItemOnly + + RecurseDirs + """ + + ItemOnly: typing.ClassVar[DeletePolicy] # value = + RecurseDirs: typing.ClassVar[DeletePolicy] # value = + __members__: typing.ClassVar[ + dict[str, DeletePolicy] + ] # value = {'ItemOnly': , 'RecurseDirs': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class FilterPolicy: + """ + Members: + + Include : Extract/compress the items that match the pattern. + + Exclude : Do not extract/compress the items that match the pattern. + """ + + Exclude: typing.ClassVar[FilterPolicy] # value = + Include: typing.ClassVar[FilterPolicy] # value = + __members__: typing.ClassVar[ + dict[str, FilterPolicy] + ] # value = {'Include': , 'Exclude': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class FormatFeatures: + """ + Features of a format supported by bit7z + + Members: + + MultipleFiles : Archive supports multiple files. + + SolidArchive : Archive supports solid mode. + + CompressionLevel : Archive supports compression level. + + Encryption : Archive supports encryption. + + HeaderEncryption : Archive supports encrypted headers. + + MultipleMethods : Archive supports multiple compression methods. + """ + + CompressionLevel: typing.ClassVar[ + FormatFeatures + ] # value = + Encryption: typing.ClassVar[ + FormatFeatures + ] # value = + HeaderEncryption: typing.ClassVar[ + FormatFeatures + ] # value = + MultipleFiles: typing.ClassVar[ + FormatFeatures + ] # value = + MultipleMethods: typing.ClassVar[ + FormatFeatures + ] # value = + SolidArchive: typing.ClassVar[ + FormatFeatures + ] # value = + __members__: typing.ClassVar[ + dict[str, FormatFeatures] + ] # value = {'MultipleFiles': , 'SolidArchive': , 'CompressionLevel': , 'Encryption': , 'HeaderEncryption': , 'MultipleMethods': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class OverwriteMode: + """ + Enumeration representing how a handler should deal when an output file already exists. + + Members: + + Nothing : The handler will throw an exception if the output file or buffer already exists. + + Overwrite : The handler will overwrite the old file or buffer with the new one. + + Skip : The handler will skip writing to the output file or buffer. + """ + + Nothing: typing.ClassVar[OverwriteMode] # value = + Overwrite: typing.ClassVar[OverwriteMode] # value = + Skip: typing.ClassVar[OverwriteMode] # value = + __members__: typing.ClassVar[ + dict[str, OverwriteMode] + ] # value = {'Nothing': , 'Overwrite': , 'Skip': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +class UpdateMode: + """ + Members: + + Nothing + + Append + + Update + """ + + Append: typing.ClassVar[UpdateMode] # value = + Nothing: typing.ClassVar[UpdateMode] # value = + Update: typing.ClassVar[UpdateMode] # value = + __members__: typing.ClassVar[ + dict[str, UpdateMode] + ] # value = {'Nothing': , 'Append': , 'Update': } + def __eq__(self, other: typing.Any) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __ne__(self, other: typing.Any) -> bool: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + +def set_large_page_mode() -> None: + """ + Enable large page mode for 7zip library. This can improve performance on some systems. + """ + +def set_lib7zip_path(lib7zip_path: str = "") -> str: + """ + Configure the path to the 7zip library. + """ + def version() -> str: - """_core plugin version.""" + """ + The _core plugin version. + """ + +ATime: BitProperty # value = +AltStreamsSize: BitProperty # value = +Attrib: BitProperty # value = +BZip2: BitCompressionMethod # value = +BigEndian: BitProperty # value = +Bit64: BitProperty # value = +Block: BitProperty # value = +Bool: BitPropVariantType # value = +CRC: BitProperty # value = +CTime: BitProperty # value = +Characters: BitProperty # value = +Checksum: BitProperty # value = +ClusterSize: BitProperty # value = +CodePage: BitProperty # value = +Comment: BitProperty # value = +Commented: BitProperty # value = +Copy: BitCompressionMethod # value = +CopyLink: BitProperty # value = +Cpu: BitProperty # value = +CreatorApp: BitProperty # value = +Deflate: BitCompressionMethod # value = +Deflate64: BitCompressionMethod # value = +DictionarySize: BitProperty # value = +EmbeddedStubSize: BitProperty # value = +Empty: BitPropVariantType # value = +Encrypted: BitProperty # value = +Error: BitProperty # value = +ErrorFlags: BitProperty # value = +ErrorType: BitProperty # value = +Exclude: FilterPolicy # value = +Extension: BitProperty # value = +Fast: BitCompressionLevel # value = +Fastest: BitCompressionLevel # value = +FileSystem: BitProperty # value = +FileTime: BitPropVariantType # value = +FormatAPM: BitInFormat # value = +FormatArj: BitInFormat # value = +FormatAuto: BitInFormat # value = +FormatBZip2: BitInOutFormat # value = +FormatCab: BitInFormat # value = +FormatChm: BitInFormat # value = +FormatCoff: BitInFormat # value = +FormatCompound: BitInFormat # value = +FormatCpio: BitInFormat # value = +FormatCramFS: BitInFormat # value = +FormatDeb: BitInFormat # value = +FormatDmg: BitInFormat # value = +FormatElf: BitInFormat # value = +FormatExt: BitInFormat # value = +FormatFat: BitInFormat # value = +FormatFlv: BitInFormat # value = +FormatGZip: BitInOutFormat # value = +FormatGpt: BitInFormat # value = +FormatHfs: BitInFormat # value = +FormatHxs: BitInFormat # value = +FormatIHex: BitInFormat # value = +FormatIso: BitInFormat # value = +FormatLzh: BitInFormat # value = +FormatLzma: BitInFormat # value = +FormatLzma86: BitInFormat # value = +FormatMacho: BitInFormat # value = +FormatMbr: BitInFormat # value = +FormatMslz: BitInFormat # value = +FormatMub: BitInFormat # value = +FormatNsis: BitInFormat # value = +FormatNtfs: BitInFormat # value = +FormatPe: BitInFormat # value = +FormatPpmd: BitInFormat # value = +FormatQcow: BitInFormat # value = +FormatRar: BitInFormat # value = +FormatRar5: BitInFormat # value = +FormatRpm: BitInFormat # value = +FormatSevenZip: BitInOutFormat # value = +FormatSplit: BitInFormat # value = +FormatSquashFS: BitInFormat # value = +FormatSwf: BitInFormat # value = +FormatSwfc: BitInFormat # value = +FormatTE: BitInFormat # value = +FormatTar: BitInOutFormat # value = +FormatUEFIc: BitInFormat # value = +FormatUEFIs: BitInFormat # value = +FormatUdf: BitInFormat # value = +FormatVdi: BitInFormat # value = +FormatVhd: BitInFormat # value = +FormatVhdx: BitInFormat # value = +FormatVmdk: BitInFormat # value = +FormatWim: BitInOutFormat # value = +FormatXar: BitInFormat # value = +FormatXz: BitInOutFormat # value = +FormatZ: BitInFormat # value = +FormatZip: BitInOutFormat # value = +FreeSpace: BitProperty # value = +Group: BitProperty # value = +HandlerItemIndex: BitProperty # value = +HardLink: BitProperty # value = +HeadersSize: BitProperty # value = +HostOS: BitProperty # value = +INode: BitProperty # value = +Id: BitProperty # value = +Include: FilterPolicy # value = +Int16: BitPropVariantType # value = +Int32: BitPropVariantType # value = +Int64: BitPropVariantType # value = +Int8: BitPropVariantType # value = +IsAltStream: BitProperty # value = +IsAnti: BitProperty # value = +IsAux: BitProperty # value = +IsDeleted: BitProperty # value = +IsDir: BitProperty # value = +IsNotArcType: BitProperty # value = +IsTree: BitProperty # value = +IsVolume: BitProperty # value = +ItemOnly: DeletePolicy # value = +Links: BitProperty # value = +LocalName: BitProperty # value = +Lzma: BitCompressionMethod # value = +Lzma2: BitCompressionMethod # value = +MTime: BitProperty # value = +MainSubfile: BitProperty # value = +Max: BitCompressionLevel # value = +Method: BitProperty # value = +Name: BitProperty # value = +NoProperty: BitProperty # value = +Normal: BitCompressionLevel # value = +Nothing: OverwriteMode # value = +NtReparse: BitProperty # value = +NtSecure: BitProperty # value = +NumAltStreams: BitProperty # value = +NumBlocks: BitProperty # value = +NumErrors: BitProperty # value = +NumStreams: BitProperty # value = +NumSubDirs: BitProperty # value = +NumSubFiles: BitProperty # value = +NumVolumes: BitProperty # value = +Offset: BitProperty # value = +OutName: BitProperty # value = +Overwrite: OverwriteMode # value = +PackSize: BitProperty # value = +Path: BitProperty # value = +PhySize: BitProperty # value = +PhySizeCantBeDetected: BitProperty # value = +Position: BitProperty # value = +PosixAttrib: BitProperty # value = +Ppmd: BitCompressionMethod # value = +Prefix: BitProperty # value = +Provider: BitProperty # value = +ReadOnly: BitProperty # value = +RecurseDirs: DeletePolicy # value = +SectorSize: BitProperty # value = +Sha1: BitProperty # value = +Sha256: BitProperty # value = +ShortComment: BitProperty # value = +ShortName: BitProperty # value = +Size: BitProperty # value = +Skip: OverwriteMode # value = +Solid: BitProperty # value = +SplitAfter: BitProperty # value = +SplitBefore: BitProperty # value = +StreamId: BitProperty # value = +String: BitPropVariantType # value = +SubType: BitProperty # value = +SymLink: BitProperty # value = +TailSize: BitProperty # value = +TimeType: BitProperty # value = +TotalPhySize: BitProperty # value = +TotalSize: BitProperty # value = +Type: BitProperty # value = +UInt16: BitPropVariantType # value = +UInt32: BitPropVariantType # value = +UInt64: BitPropVariantType # value = +UInt8: BitPropVariantType # value = +Ultra: BitCompressionLevel # value = +UnpackSize: BitProperty # value = +UnpackVer: BitProperty # value = +User: BitProperty # value = +Va: BitProperty # value = +VirtualSize: BitProperty # value = +Volume: BitProperty # value = +VolumeIndex: BitProperty # value = +VolumeName: BitProperty # value = +Warning: BitProperty # value = +WarningFlags: BitProperty # value = +ZerosTailIsAllowed: BitProperty # value = +BitFileExtractor = BitStringExtractor diff --git a/tests/test_bit7z.py b/tests/test_bit7z.py new file mode 100644 index 0000000..91d9569 --- /dev/null +++ b/tests/test_bit7z.py @@ -0,0 +1,309 @@ +from __future__ import annotations + +import datetime +import random +import string +import tempfile +from pathlib import Path + +import pytest + +from pybit7z import _core + + +@pytest.fixture +def temp_dir(): + """Fixture to provide temporary directory""" + try: + with tempfile.TemporaryDirectory() as tmp_dir: + yield Path(tmp_dir) + except Exception: + ... # Ignore errors from cleanup + + +@pytest.fixture +def large_file(temp_dir): + """Fixture to create a large test file (10MB)""" + file_path = temp_dir / "large_file.dat" + size_mb = 10 + chunk_size = 1024 * 1024 # 1MB chunks + + with file_path.open("wb") as f: + for _ in range(size_mb): + data = "".join( + random.choices(string.ascii_letters + string.digits, k=chunk_size) + ).encode() + f.write(data) + + return file_path + + +def test_format_features(): + """Test format features detection""" + fmt_7z = _core.FormatSevenZip + assert isinstance(fmt_7z, _core.BitInOutFormat) + + # Test format properties + assert fmt_7z.has_feature(_core.FormatFeatures.CompressionLevel) + assert fmt_7z.has_feature(_core.FormatFeatures.Encryption) + assert fmt_7z.has_feature(_core.FormatFeatures.MultipleFiles) + + assert fmt_7z.extension() == ".7z" + + +def test_basic_compression(temp_dir): + """Test basic file compression using BitFileCompressor""" + # Create a test file + test_file: Path = temp_dir / "test.txt" + test_file.write_text("Hello, bit7z!") + + # Create output archive path + archive_path: Path = temp_dir / "test.7z" + + # Create a compressor for 7z format + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + + # Set compression options + compressor.set_compression_level(_core.BitCompressionLevel.Normal) + compressor.set_compression_method(_core.BitCompressionMethod.Lzma2) + + # Compress the file + compressor.compress_files([str(test_file)], str(archive_path)) + + assert archive_path.exists(), "Archive was not created" + assert archive_path.stat().st_size > 0, "Archive is empty" + + +def test_archive_extraction(temp_dir): + """Test archive extraction using BitFileExtractor""" + # Create a test archive first + test_file = temp_dir / "test.txt" + test_file.write_text("Hello from bit7z!") + + archive_path = temp_dir / "test.7z" + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + compressor.compress([str(test_file)], str(archive_path)) + + # Now extract it to a different directory + extract_dir: Path = temp_dir / "extracted" + extract_dir.mkdir() + + extractor = _core.BitFileExtractor(_core.FormatSevenZip) + extractor.extract(str(archive_path), str(extract_dir)) + + # Verify extraction + extracted_file: Path = extract_dir / "test.txt" + assert extracted_file.exists(), "File was not extracted" + assert ( + extracted_file.read_text() == "Hello from bit7z!" + ), "Extracted content doesn't match" + + +def test_error_handling(temp_dir): + """Test error handling and exceptions""" + # Test non-existent file + nonexistent_file = temp_dir / "nonexistent.txt" + archive_path = temp_dir / "test.7z" + + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + + with pytest.raises(_core.BitException): + compressor.compress([str(nonexistent_file)], str(archive_path)) + + # Test invalid password extraction + test_file: Path = temp_dir / "secret.txt" + test_file.write_text("Secret content") + + # Create password-protected archive + compressor.set_password("correct_password") + compressor.compress([str(test_file)], str(archive_path)) + + extract_dir: Path = temp_dir / "extracted" + extract_dir.mkdir() + + extractor = _core.BitFileExtractor(_core.FormatSevenZip) + extractor.set_password("wrong_password") + + with pytest.raises(_core.BitException, match="wrong password"): + extractor.extract(str(archive_path), str(extract_dir)) + + +def test_large_file_handling(temp_dir, large_file): + """Test handling of large files""" + + # Test different compression levels with large file + for level in [ + _core.BitCompressionLevel.Fastest, + _core.BitCompressionLevel.Normal, + _core.BitCompressionLevel.Max, + ]: + level_archive = temp_dir / f"large_{level}.7z" + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + compressor.set_compression_level(level) + + compressor.compress([str(large_file)], str(level_archive)) + assert ( + level_archive.exists() + ), f"Large file archive not created for level {level}" + + # Extract and verify + for level in [ + _core.BitCompressionLevel.Fastest, + _core.BitCompressionLevel.Normal, + _core.BitCompressionLevel.Max, + ]: + level_archive = temp_dir / f"large_{level}.7z" + extract_dir: Path = temp_dir / "extracted_large" + + extractor = _core.BitFileExtractor(_core.FormatSevenZip) + extractor.extract(str(level_archive), str(extract_dir)) + + extracted_file: Path = extract_dir / large_file.name + assert extracted_file.exists(), "Large file was not extracted" + assert ( + extracted_file.stat().st_size == large_file.stat().st_size + ), "Extracted file size mismatch" + + +def test_multi_file_archive(temp_dir): + """Test creating and extracting multi-file archives""" + # Create multiple test files + files_data = { + "text1.txt": "Content 1", + "text2.txt": "Content 2", + "subdir/text3.txt": "Content 3", + "subdir/text4.txt": "Content 4", + } + + for file_path, content in files_data.items(): + full_path: Path = temp_dir / file_path + full_path.parent.mkdir(exist_ok=True) + full_path.write_text(content) + + # Create archive + archive_path = temp_dir / "multi.7z" + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + compressor.compress([str(temp_dir)], str(archive_path)) + + # Extract to new location + extract_dir = temp_dir / "extracted_multi" + extractor = _core.BitFileExtractor(_core.FormatSevenZip) + extractor.extract(str(archive_path), str(extract_dir)) + + # Verify all files + for file_path, content in files_data.items(): + extracted_file: Path = extract_dir / temp_dir.name / file_path + assert extracted_file.exists(), f"File {file_path} not extracted" + assert extracted_file.read_text() == content, f"Content mismatch in {file_path}" + + +def test_compression_methods_comparison(temp_dir, large_file): + """Test and compare different compression methods""" + methods = [ + _core.BitCompressionMethod.Lzma2, + _core.BitCompressionMethod.Lzma, + _core.BitCompressionMethod.BZip2, + _core.BitCompressionMethod.Copy, + ] + + results = {} + for method in methods: + archive_path: Path = temp_dir / f"test_{method}.7z" + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + compressor.set_compression_method(method) + + compressor.compress([str(large_file)], str(archive_path)) + results[method] = archive_path.stat().st_size + + # Verify that COPY method creates the largest archive + assert results[_core.BitCompressionMethod.Copy] >= max( + size + for method, size in results.items() + if method != _core.BitCompressionMethod.Copy + ), "COPY method should create the largest archive" + + +def test_archive_metadata(temp_dir): + """Test archive metadata and properties""" + # Create test file with known timestamp + test_file: Path = temp_dir / "test.txt" + test_file.write_text("Test content") + + archive_path = temp_dir / "test.7z" + compressor = _core.BitFileCompressor(_core.FormatSevenZip) + compressor.compress([str(test_file)], str(archive_path)) + + # Read archive + reader = _core.BitArchiveReader(str(archive_path), _core.FormatAuto) + items = reader.items() + + assert len(items) == 1, "Expected exactly one item in archive" + item = items[0] + + # Check metadata + assert item.name() == "test.txt" + assert item.size() > 0 + assert not item.is_dir() + assert item.crc() + + # Time values should be reasonable + assert isinstance(item.creation_time(), datetime.datetime) + assert isinstance(item.last_write_time(), datetime.datetime) + + # Extract and terminate holded resources + extracted_dir = temp_dir / "extracted" + reader.extract_to(str(extracted_dir)) + + +def test_mem_to_bytes(): + """Test stream compression and extraction""" + # Save compressed data + test_data = b"Stream compression test data" * 1000 + + # Compress to memory + mem_compressor = _core.BitMemCompressor(_core.FormatSevenZip) + compressed_data = mem_compressor.compress_file(test_data) + + # Extract to memory + mem_extractor = _core.BitMemExtractor(_core.FormatSevenZip) + extracted_data = mem_extractor.extract(compressed_data) + + assert ( + extracted_data["[Content]"] == test_data + ), "Stream compression/extraction failed" + assert len(compressed_data) < len(test_data), "Compression did not reduce data size" + + +def test_different_formats(temp_dir): + """Test compression with different archive formats""" + test_file: Path = temp_dir / "test.txt" + test_file.write_text("Testing different formats") + + formats = [ + (_core.FormatSevenZip, "test.7z"), + (_core.FormatZip, "test.zip"), + (_core.FormatBZip2, "test.txt.bz2"), + (_core.FormatGZip, "test.gz"), + ] + + for format_type, filename in formats: + archive_path: Path = temp_dir / filename + compressor = _core.BitFileCompressor(format_type) + compressor.compress([str(test_file)], str(archive_path)) + + assert archive_path.exists(), f"Archive {filename} was not created" + assert ( + archive_path.suffix == format_type.extension() + ), f"Wrong extension for {filename}" + + # Test extraction + extract_dir = temp_dir / f"extracted_{filename}" + + extractor = _core.BitFileExtractor(format_type) + extractor.extract(str(archive_path), str(extract_dir)) + + extracted_file: Path = extract_dir / "test.txt" + assert extracted_file.exists(), f"File was not extracted from {filename}" + assert ( + extracted_file.read_text() == "Testing different formats" + ), f"Content mismatch in {filename}" diff --git a/vcpkg.json b/vcpkg.json index d5a9df7..b263a26 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -44,7 +44,7 @@ "dependencies": [ { "name": "gtest", - "version>=": "1.14.0" + "version>=": "1.15.2" } ] }