sapiopycommons 2025.9.19a762__tar.gz → 2025.9.26a769__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sapiopycommons might be problematic. Click here for more details.

Files changed (126) hide show
  1. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/PKG-INFO +1 -1
  2. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/pyproject.toml +1 -1
  3. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/server.py +7 -0
  4. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_util.py +128 -1
  5. sapiopycommons-2025.9.26a769/tests/file_compression_tests.py +39 -0
  6. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/.gitignore +0 -0
  7. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/LICENSE +0 -0
  8. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/README.md +0 -0
  9. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/__init__.py +0 -0
  10. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/__init__.py +0 -0
  11. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/converter_service_base.py +0 -0
  12. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.py +0 -0
  13. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.pyi +0 -0
  14. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2_grpc.py +0 -0
  15. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.py +0 -0
  16. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.pyi +0 -0
  17. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2_grpc.py +0 -0
  18. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/converter/converter_pb2.py +0 -0
  19. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/converter/converter_pb2.pyi +0 -0
  20. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/converter/converter_pb2_grpc.py +0 -0
  21. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/item/item_container_pb2.py +0 -0
  22. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/item/item_container_pb2.pyi +0 -0
  23. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/item/item_container_pb2_grpc.py +0 -0
  24. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/script/script_pb2.py +0 -0
  25. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/script/script_pb2.pyi +0 -0
  26. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/script/script_pb2_grpc.py +0 -0
  27. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/step_output_pb2.py +0 -0
  28. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/step_output_pb2.pyi +0 -0
  29. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/step_output_pb2_grpc.py +0 -0
  30. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/step_pb2.py +0 -0
  31. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/step_pb2.pyi +0 -0
  32. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/step_pb2_grpc.py +0 -0
  33. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/tool/entry_pb2.py +0 -0
  34. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/tool/entry_pb2.pyi +0 -0
  35. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/tool/entry_pb2_grpc.py +0 -0
  36. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/tool/tool_pb2.py +0 -0
  37. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/tool/tool_pb2.pyi +0 -0
  38. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/plan/tool/tool_pb2_grpc.py +0 -0
  39. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.py +0 -0
  40. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.pyi +0 -0
  41. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2_grpc.py +0 -0
  42. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/protobuf_utils.py +0 -0
  43. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/test_client.py +0 -0
  44. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/tool_of_tools.py +0 -0
  45. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/ai/tool_service_base.py +0 -0
  46. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/callbacks/__init__.py +0 -0
  47. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/callbacks/callback_util.py +0 -0
  48. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/callbacks/field_builder.py +0 -0
  49. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
  50. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/chem/Molecules.py +0 -0
  51. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/chem/__init__.py +0 -0
  52. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/customreport/__init__.py +0 -0
  53. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/customreport/auto_pagers.py +0 -0
  54. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/customreport/column_builder.py +0 -0
  55. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/customreport/custom_report_builder.py +0 -0
  56. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/customreport/term_builder.py +0 -0
  57. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/datatype/__init__.py +0 -0
  58. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/datatype/attachment_util.py +0 -0
  59. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/datatype/data_fields.py +0 -0
  60. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/datatype/pseudo_data_types.py +0 -0
  61. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/__init__.py +0 -0
  62. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/experiment_cache.py +0 -0
  63. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/experiment_handler.py +0 -0
  64. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/experiment_report_util.py +0 -0
  65. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/experiment_step_factory.py +0 -0
  66. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/experiment_tags.py +0 -0
  67. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/plate_designer.py +0 -0
  68. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/eln/step_creation.py +0 -0
  69. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/__init__.py +0 -0
  70. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/assay_plate_reader.py +0 -0
  71. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/complex_data_loader.py +0 -0
  72. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_bridge.py +0 -0
  73. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_bridge_handler.py +0 -0
  74. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_data_handler.py +0 -0
  75. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_text_converter.py +0 -0
  76. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_validator.py +0 -0
  77. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/file_writer.py +0 -0
  78. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/files/temp_files.py +0 -0
  79. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/flowcyto/flow_cyto.py +0 -0
  80. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/flowcyto/flowcyto_data.py +0 -0
  81. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/__init__.py +0 -0
  82. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/accession_service.py +0 -0
  83. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/aliases.py +0 -0
  84. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/audit_log.py +0 -0
  85. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/custom_report_util.py +0 -0
  86. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/data_structure_util.py +0 -0
  87. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/directive_util.py +0 -0
  88. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/exceptions.py +0 -0
  89. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/html_formatter.py +0 -0
  90. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/popup_util.py +0 -0
  91. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/sapio_links.py +0 -0
  92. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/storage_util.py +0 -0
  93. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/general/time_util.py +0 -0
  94. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/multimodal/multimodal.py +0 -0
  95. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/multimodal/multimodal_data.py +0 -0
  96. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/processtracking/__init__.py +0 -0
  97. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/processtracking/custom_workflow_handler.py +0 -0
  98. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/processtracking/endpoints.py +0 -0
  99. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/recordmodel/__init__.py +0 -0
  100. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/recordmodel/record_handler.py +0 -0
  101. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/rules/__init__.py +0 -0
  102. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/rules/eln_rule_handler.py +0 -0
  103. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/rules/on_save_rule_handler.py +0 -0
  104. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/samples/aliquot.py +0 -0
  105. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/sftpconnect/__init__.py +0 -0
  106. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/sftpconnect/sftp_builder.py +0 -0
  107. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/webhook/__init__.py +0 -0
  108. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/webhook/webhook_context.py +0 -0
  109. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/webhook/webhook_handlers.py +0 -0
  110. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/src/sapiopycommons/webhook/webservice_handlers.py +0 -0
  111. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/AF-A0A009IHW8-F1-model_v4.cif +0 -0
  112. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/_do_not_add_init_py_here +0 -0
  113. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/accession_test.py +0 -0
  114. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/aliquot_test.py +0 -0
  115. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/assay_plate_reader/BMGLabtech96.txt +0 -0
  116. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/assay_plate_reader/assay_plate_processing_test.py +0 -0
  117. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/bio_reg_test.py +0 -0
  118. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/chem_test.py +0 -0
  119. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/chem_test_curation_queue.py +0 -0
  120. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/curation_queue_test.sdf +0 -0
  121. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/data_type_models.py +0 -0
  122. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/flowcyto/8_color_ICS.wsp +0 -0
  123. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/flowcyto_test.py +0 -0
  124. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/kappa.chains.fasta +0 -0
  125. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/mafft_test.py +0 -0
  126. {sapiopycommons-2025.9.19a762 → sapiopycommons-2025.9.26a769}/tests/test.gb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2025.9.19a762
3
+ Version: 2025.9.26a769
4
4
  Summary: Official Sapio Python API Utilities Package
5
5
  Project-URL: Homepage, https://github.com/sapiosciences
6
6
  Author-email: Jonathan Steck <jsteck@sapiosciences.com>, Yechen Qiao <yqiao@sapiosciences.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sapiopycommons"
7
- version='2025.09.19a762'
7
+ version='2025.09.26a769'
8
8
  authors = [
9
9
  { name="Jonathan Steck", email="jsteck@sapiosciences.com" },
10
10
  { name="Yechen Qiao", email="yqiao@sapiosciences.com" },
@@ -127,6 +127,13 @@ class SapioGrpcServer:
127
127
  print(f"Registering Tool service: {service.__class__.__name__}")
128
128
  add_ToolServiceServicer_to_server(service, server)
129
129
 
130
+ from grpc_health.v1 import health_pb2, health_pb2_grpc
131
+ from grpc_health.v1.health import HealthServicer
132
+ health_servicer = HealthServicer()
133
+ health_servicer.set("", health_pb2.HealthCheckResponse.ServingStatus.SERVING)
134
+ health_servicer.set("ScriptService", health_pb2.HealthCheckResponse.ServingStatus.SERVING)
135
+ health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
136
+
130
137
  server.add_insecure_port(f"[::]:{self.port}")
131
138
  await server.start()
132
139
  print(f"Server started, listening on {self.port}")
@@ -1,4 +1,7 @@
1
+ import gzip
1
2
  import io
3
+ import tarfile
4
+ import time
2
5
  import warnings
3
6
  import zipfile
4
7
 
@@ -322,7 +325,7 @@ class FileUtil:
322
325
  @staticmethod
323
326
  def zip_files(files: dict[str, str | bytes]) -> bytes:
324
327
  """
325
- Create a zip file for a collection of files.
328
+ Create a .zip file for a collection of files.
326
329
 
327
330
  :param files: A dictionary of file name to file data as a string or bytes.
328
331
  :return: The bytes for a zip file containing the input files.
@@ -335,6 +338,130 @@ class FileUtil:
335
338
  # throws an I/O exception.
336
339
  return zip_buffer.getvalue()
337
340
 
341
+ # FR-47422: Add a function for unzipping files that may have been zipped by the above function.
342
+ @staticmethod
343
+ def unzip_files(zip_file: bytes) -> dict[str, bytes]:
344
+ """
345
+ Decompress a .zip file from an in-memory bytes object and extracts all files into a dictionary.
346
+
347
+ :param zip_file: The bytes of the zip file to be decompressed.
348
+ :return: A dictionary of file name to file bytes for each file in the zip.
349
+ """
350
+ extracted_files: dict[str, bytes] = {}
351
+ with io.BytesIO(zip_file) as zip_buffer:
352
+ with zipfile.ZipFile(zip_buffer, "r") as zip_file:
353
+ for file_name in zip_file.namelist():
354
+ with zip_file.open(file_name) as file:
355
+ extracted_files[file_name] = file.read()
356
+ return extracted_files
357
+
358
+ # FR-47422: Add functions for compressing and decompressing .gz, .tar, and .tar.gz files.
359
+ @staticmethod
360
+ def gzip_file(file_data: bytes | str) -> bytes:
361
+ """
362
+ Create a .gz file for a single file.
363
+
364
+ :param file_data: The file data to be compressed as bytes or a string.
365
+ :return: The bytes of the gzip-compressed file.
366
+ """
367
+ return gzip.compress(file_data.encode() if isinstance(file_data, str) else file_data)
368
+
369
+ @staticmethod
370
+ def ungzip_file(gzip_file: bytes) -> bytes:
371
+ """
372
+ Decompress a .gz file.
373
+
374
+ :param gzip_file: The bytes of the gzip-compressed file.
375
+ :return: The decompressed file data as bytes.
376
+ """
377
+ return gzip.decompress(gzip_file)
378
+
379
+ @staticmethod
380
+ def tar_files(files: dict[str, str | bytes]) -> bytes:
381
+ """
382
+ Create a .tar file for a collection of files.
383
+
384
+ :param files: A dictionary of file name to file data as a string or bytes.
385
+ :return: The bytes for a tar file containing the input files.
386
+ """
387
+ with io.BytesIO() as tar_buffer:
388
+ with tarfile.open(fileobj=tar_buffer, mode="w") as tar:
389
+ for name, data in files.items():
390
+ if isinstance(data, str):
391
+ data: bytes = data.encode('utf-8')
392
+
393
+ tarinfo = tarfile.TarInfo(name=name)
394
+ tarinfo.size = len(data)
395
+ tarinfo.mtime = int(time.time())
396
+
397
+ with io.BytesIO(data) as file:
398
+ tar.addfile(tarinfo=tarinfo, fileobj=file)
399
+
400
+ tar_buffer.seek(0)
401
+ return tar_buffer.getvalue()
402
+
403
+ @staticmethod
404
+ def untar_files(tar_file: bytes) -> dict[str, bytes]:
405
+ """
406
+ Decompress a .tar file from an in-memory bytes object and extracts all files into a dictionary.
407
+
408
+ :param tar_file: The bytes of the tar file to be decompressed.
409
+ :return: A dictionary of file name to file bytes for each file in the tar.
410
+ """
411
+ extracted_files: dict[str, bytes] = {}
412
+ with io.BytesIO(tar_file) as tar_buffer:
413
+ with tarfile.open(fileobj=tar_buffer, mode="r") as tar:
414
+ for member in tar.getmembers():
415
+ if member.isfile():
416
+ file_obj = tar.extractfile(member)
417
+ if file_obj:
418
+ with file_obj:
419
+ extracted_files[member.name] = file_obj.read()
420
+ return extracted_files
421
+
422
+ @staticmethod
423
+ def tar_gzip_files(files: dict[str, str | bytes]) -> bytes:
424
+ """
425
+ Create a .tar.gz file for a collection of files.
426
+
427
+ :param files: A dictionary of file name to file data as a string or bytes.
428
+ :return: The bytes for a tar.gz file containing the input files.
429
+ """
430
+ with io.BytesIO() as tar_buffer:
431
+ with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar:
432
+ for name, data in files.items():
433
+ if isinstance(data, str):
434
+ data: bytes = data.encode('utf-8')
435
+
436
+ tarinfo = tarfile.TarInfo(name=name)
437
+ tarinfo.size = len(data)
438
+ tarinfo.mtime = int(time.time())
439
+
440
+ with io.BytesIO(data) as file:
441
+ tar.addfile(tarinfo=tarinfo, fileobj=file)
442
+
443
+ tar_buffer.seek(0)
444
+ return tar_buffer.getvalue()
445
+
446
+ @staticmethod
447
+ def untar_gzip_files(tar_gzip_file: bytes) -> dict[str, bytes]:
448
+ """
449
+ Decompress a .tar.gz file from an in-memory bytes object and extracts all files into a dictionary.
450
+
451
+ :param tar_gzip_file: The bytes of the tar.gz file to be decompressed.
452
+ :return: A dictionary of file name to file bytes for each file in the tar.gz
453
+ """
454
+ extracted_files: dict[str, bytes] = {}
455
+ with io.BytesIO(tar_gzip_file) as tar_buffer:
456
+ with tarfile.open(fileobj=tar_buffer, mode="r:gz") as tar:
457
+ for member in tar.getmembers():
458
+ if member.isfile():
459
+ file_obj = tar.extractfile(member)
460
+ if file_obj:
461
+ with file_obj:
462
+ extracted_files[member.name] = file_obj.read()
463
+ return extracted_files
464
+
338
465
  # Deprecated functions:
339
466
 
340
467
  # FR-46097 - Add write file request shorthand functions to FileUtil.
@@ -0,0 +1,39 @@
1
+ from sapiopycommons.files.file_util import FileUtil
2
+
3
+ files: dict[str, bytes] = {
4
+ "file1.txt": b"Hello, this is the content of file 1.",
5
+ "file2.txt": b"This is file 2, containing some sample text.",
6
+ "file3.txt": b"File 3 is here with its own unique content.",
7
+ "file4.txt": b"And finally, this is file 4."
8
+ }
9
+
10
+ print("files -> .zip -> files")
11
+ zip_file: bytes = FileUtil.zip_files(files)
12
+ with open("test_output/test.zip", "wb") as f:
13
+ f.write(zip_file)
14
+ unzipped_files: dict[str, bytes] = FileUtil.unzip_files(zip_file)
15
+ for filename, content in unzipped_files.items():
16
+ print(f"\t{filename}: {content.decode('utf-8')}")
17
+
18
+ print("file -> .gz -> file")
19
+ gz_file: bytes = FileUtil.gzip_file(files["file1.txt"])
20
+ with open("test_output/file1.txt.gz", "wb") as f:
21
+ f.write(gz_file)
22
+ ungzipped_file: bytes = FileUtil.ungzip_file(gz_file)
23
+ print(f"\tUngzipped file1.txt: {ungzipped_file.decode('utf-8')}")
24
+
25
+ print("files -> .tar -> files")
26
+ tarfile: bytes = FileUtil.tar_files(files)
27
+ with open("test_output/test.tar", "wb") as f:
28
+ f.write(tarfile)
29
+ untarred_files: dict[str, bytes] = FileUtil.untar_files(tarfile)
30
+ for filename, content in untarred_files.items():
31
+ print(f"\t{filename}: {content.decode('utf-8')}")
32
+
33
+ print("files -> .tar.gz -> files")
34
+ tgz_file: bytes = FileUtil.tar_gzip_files(files)
35
+ with open("test_output/test.tar.gz", "wb") as f:
36
+ f.write(tgz_file)
37
+ untgzipped_files: dict[str, bytes] = FileUtil.untar_gzip_files(tgz_file)
38
+ for filename, content in untgzipped_files.items():
39
+ print(f"\t{filename}: {content.decode('utf-8')}")