pybiolib 0.2.951__py3-none-any.whl → 1.2.1890__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- biolib/__init__.py +357 -11
- biolib/_data_record/data_record.py +380 -0
- biolib/_index/__init__.py +0 -0
- biolib/_index/index.py +55 -0
- biolib/_index/query_result.py +103 -0
- biolib/_internal/__init__.py +0 -0
- biolib/_internal/add_copilot_prompts.py +58 -0
- biolib/_internal/add_gui_files.py +81 -0
- biolib/_internal/data_record/__init__.py +1 -0
- biolib/_internal/data_record/data_record.py +85 -0
- biolib/_internal/data_record/push_data.py +116 -0
- biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
- biolib/_internal/errors.py +5 -0
- biolib/_internal/file_utils.py +125 -0
- biolib/_internal/fuse_mount/__init__.py +1 -0
- biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
- biolib/_internal/http_client.py +159 -0
- biolib/_internal/lfs/__init__.py +1 -0
- biolib/_internal/lfs/cache.py +51 -0
- biolib/_internal/libs/__init__.py +1 -0
- biolib/_internal/libs/fusepy/__init__.py +1257 -0
- biolib/_internal/push_application.py +488 -0
- biolib/_internal/runtime.py +22 -0
- biolib/_internal/string_utils.py +13 -0
- biolib/_internal/templates/__init__.py +1 -0
- biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
- biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
- biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
- biolib/_internal/templates/gitignore_template/.gitignore +10 -0
- biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
- biolib/_internal/templates/gui_template/App.tsx +53 -0
- biolib/_internal/templates/gui_template/Dockerfile +27 -0
- biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
- biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
- biolib/_internal/templates/gui_template/index.css +5 -0
- biolib/_internal/templates/gui_template/index.html +13 -0
- biolib/_internal/templates/gui_template/index.tsx +10 -0
- biolib/_internal/templates/gui_template/package.json +27 -0
- biolib/_internal/templates/gui_template/tsconfig.json +24 -0
- biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
- biolib/_internal/templates/gui_template/vite.config.mts +10 -0
- biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
- biolib/_internal/templates/init_template/Dockerfile +14 -0
- biolib/_internal/templates/init_template/requirements.txt +1 -0
- biolib/_internal/templates/init_template/run.py +12 -0
- biolib/_internal/templates/init_template/run.sh +4 -0
- biolib/_internal/templates/templates.py +25 -0
- biolib/_internal/tree_utils.py +106 -0
- biolib/_internal/utils/__init__.py +65 -0
- biolib/_internal/utils/auth.py +46 -0
- biolib/_internal/utils/job_url.py +33 -0
- biolib/_internal/utils/multinode.py +263 -0
- biolib/_runtime/runtime.py +157 -0
- biolib/_session/session.py +44 -0
- biolib/_shared/__init__.py +0 -0
- biolib/_shared/types/__init__.py +74 -0
- biolib/_shared/types/account.py +12 -0
- biolib/_shared/types/account_member.py +8 -0
- biolib/_shared/types/app.py +9 -0
- biolib/_shared/types/data_record.py +40 -0
- biolib/_shared/types/experiment.py +32 -0
- biolib/_shared/types/file_node.py +17 -0
- biolib/_shared/types/push.py +6 -0
- biolib/_shared/types/resource.py +37 -0
- biolib/_shared/types/resource_deploy_key.py +11 -0
- biolib/_shared/types/resource_permission.py +14 -0
- biolib/_shared/types/resource_version.py +19 -0
- biolib/_shared/types/result.py +14 -0
- biolib/_shared/types/typing.py +10 -0
- biolib/_shared/types/user.py +19 -0
- biolib/_shared/utils/__init__.py +7 -0
- biolib/_shared/utils/resource_uri.py +75 -0
- biolib/api/__init__.py +6 -0
- biolib/api/client.py +168 -0
- biolib/app/app.py +252 -49
- biolib/app/search_apps.py +45 -0
- biolib/biolib_api_client/api_client.py +126 -31
- biolib/biolib_api_client/app_types.py +24 -4
- biolib/biolib_api_client/auth.py +31 -8
- biolib/biolib_api_client/biolib_app_api.py +147 -52
- biolib/biolib_api_client/biolib_job_api.py +161 -141
- biolib/biolib_api_client/job_types.py +21 -5
- biolib/biolib_api_client/lfs_types.py +7 -23
- biolib/biolib_api_client/user_state.py +56 -0
- biolib/biolib_binary_format/__init__.py +1 -4
- biolib/biolib_binary_format/file_in_container.py +105 -0
- biolib/biolib_binary_format/module_input.py +24 -7
- biolib/biolib_binary_format/module_output_v2.py +149 -0
- biolib/biolib_binary_format/remote_endpoints.py +34 -0
- biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
- biolib/biolib_binary_format/saved_job.py +3 -2
- biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
- biolib/biolib_binary_format/system_status_update.py +3 -2
- biolib/biolib_binary_format/utils.py +175 -0
- biolib/biolib_docker_client/__init__.py +11 -2
- biolib/biolib_errors.py +36 -0
- biolib/biolib_logging.py +27 -10
- biolib/cli/__init__.py +38 -0
- biolib/cli/auth.py +46 -0
- biolib/cli/data_record.py +164 -0
- biolib/cli/index.py +32 -0
- biolib/cli/init.py +421 -0
- biolib/cli/lfs.py +101 -0
- biolib/cli/push.py +50 -0
- biolib/cli/run.py +63 -0
- biolib/cli/runtime.py +14 -0
- biolib/cli/sdk.py +16 -0
- biolib/cli/start.py +56 -0
- biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
- biolib/compute_node/job_worker/cache_state.py +66 -88
- biolib/compute_node/job_worker/cache_types.py +1 -6
- biolib/compute_node/job_worker/docker_image_cache.py +112 -37
- biolib/compute_node/job_worker/executors/__init__.py +0 -3
- biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
- biolib/compute_node/job_worker/executors/docker_types.py +9 -1
- biolib/compute_node/job_worker/executors/types.py +19 -9
- biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
- biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
- biolib/compute_node/job_worker/job_storage.py +108 -0
- biolib/compute_node/job_worker/job_worker.py +397 -212
- biolib/compute_node/job_worker/large_file_system.py +87 -38
- biolib/compute_node/job_worker/network_alloc.py +99 -0
- biolib/compute_node/job_worker/network_buffer.py +240 -0
- biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
- biolib/compute_node/job_worker/utils.py +9 -24
- biolib/compute_node/remote_host_proxy.py +400 -98
- biolib/compute_node/utils.py +31 -9
- biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
- biolib/compute_node/webserver/proxy_utils.py +28 -0
- biolib/compute_node/webserver/webserver.py +130 -44
- biolib/compute_node/webserver/webserver_types.py +2 -6
- biolib/compute_node/webserver/webserver_utils.py +77 -12
- biolib/compute_node/webserver/worker_thread.py +183 -42
- biolib/experiments/__init__.py +0 -0
- biolib/experiments/experiment.py +356 -0
- biolib/jobs/__init__.py +1 -0
- biolib/jobs/job.py +741 -0
- biolib/jobs/job_result.py +185 -0
- biolib/jobs/types.py +50 -0
- biolib/py.typed +0 -0
- biolib/runtime/__init__.py +14 -0
- biolib/sdk/__init__.py +91 -0
- biolib/tables.py +34 -0
- biolib/typing_utils.py +2 -7
- biolib/user/__init__.py +1 -0
- biolib/user/sign_in.py +54 -0
- biolib/utils/__init__.py +162 -0
- biolib/utils/cache_state.py +94 -0
- biolib/utils/multipart_uploader.py +194 -0
- biolib/utils/seq_util.py +150 -0
- biolib/utils/zip/remote_zip.py +640 -0
- pybiolib-1.2.1890.dist-info/METADATA +41 -0
- pybiolib-1.2.1890.dist-info/RECORD +177 -0
- {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
- pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
- README.md +0 -17
- biolib/app/app_result.py +0 -68
- biolib/app/utils.py +0 -62
- biolib/biolib-js/0-biolib.worker.js +0 -1
- biolib/biolib-js/1-biolib.worker.js +0 -1
- biolib/biolib-js/2-biolib.worker.js +0 -1
- biolib/biolib-js/3-biolib.worker.js +0 -1
- biolib/biolib-js/4-biolib.worker.js +0 -1
- biolib/biolib-js/5-biolib.worker.js +0 -1
- biolib/biolib-js/6-biolib.worker.js +0 -1
- biolib/biolib-js/index.html +0 -10
- biolib/biolib-js/main-biolib.js +0 -1
- biolib/biolib_api_client/biolib_account_api.py +0 -21
- biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
- biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
- biolib/biolib_binary_format/module_output.py +0 -58
- biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
- biolib/biolib_push.py +0 -114
- biolib/cli.py +0 -203
- biolib/cli_utils.py +0 -273
- biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
- biolib/compute_node/enclave/__init__.py +0 -2
- biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
- biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
- biolib/compute_node/job_worker/executors/base_executor.py +0 -18
- biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
- biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
- biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
- biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
- biolib/lfs.py +0 -196
- biolib/pyppeteer/.circleci/config.yml +0 -100
- biolib/pyppeteer/.coveragerc +0 -3
- biolib/pyppeteer/.gitignore +0 -89
- biolib/pyppeteer/.pre-commit-config.yaml +0 -28
- biolib/pyppeteer/CHANGES.md +0 -253
- biolib/pyppeteer/CONTRIBUTING.md +0 -26
- biolib/pyppeteer/LICENSE +0 -12
- biolib/pyppeteer/README.md +0 -137
- biolib/pyppeteer/docs/Makefile +0 -177
- biolib/pyppeteer/docs/_static/custom.css +0 -28
- biolib/pyppeteer/docs/_templates/layout.html +0 -10
- biolib/pyppeteer/docs/changes.md +0 -1
- biolib/pyppeteer/docs/conf.py +0 -299
- biolib/pyppeteer/docs/index.md +0 -21
- biolib/pyppeteer/docs/make.bat +0 -242
- biolib/pyppeteer/docs/reference.md +0 -211
- biolib/pyppeteer/docs/server.py +0 -60
- biolib/pyppeteer/poetry.lock +0 -1699
- biolib/pyppeteer/pyppeteer/__init__.py +0 -135
- biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
- biolib/pyppeteer/pyppeteer/browser.py +0 -401
- biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
- biolib/pyppeteer/pyppeteer/command.py +0 -22
- biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
- biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
- biolib/pyppeteer/pyppeteer/coverage.py +0 -346
- biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
- biolib/pyppeteer/pyppeteer/dialog.py +0 -79
- biolib/pyppeteer/pyppeteer/domworld.py +0 -597
- biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
- biolib/pyppeteer/pyppeteer/errors.py +0 -48
- biolib/pyppeteer/pyppeteer/events.py +0 -63
- biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
- biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
- biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
- biolib/pyppeteer/pyppeteer/helpers.py +0 -245
- biolib/pyppeteer/pyppeteer/input.py +0 -371
- biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
- biolib/pyppeteer/pyppeteer/launcher.py +0 -683
- biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
- biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
- biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
- biolib/pyppeteer/pyppeteer/multimap.py +0 -82
- biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
- biolib/pyppeteer/pyppeteer/options.py +0 -8
- biolib/pyppeteer/pyppeteer/page.py +0 -1728
- biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
- biolib/pyppeteer/pyppeteer/target.py +0 -147
- biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
- biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
- biolib/pyppeteer/pyppeteer/tracing.py +0 -93
- biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
- biolib/pyppeteer/pyppeteer/util.py +0 -18
- biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
- biolib/pyppeteer/pyppeteer/worker.py +0 -101
- biolib/pyppeteer/pyproject.toml +0 -97
- biolib/pyppeteer/spell.txt +0 -137
- biolib/pyppeteer/tox.ini +0 -72
- biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
- biolib/start_cli.py +0 -7
- biolib/utils.py +0 -47
- biolib/validators/validate_app_version.py +0 -183
- biolib/validators/validate_argument.py +0 -134
- biolib/validators/validate_module.py +0 -323
- biolib/validators/validate_zip_file.py +0 -40
- biolib/validators/validator_utils.py +0 -103
- pybiolib-0.2.951.dist-info/LICENSE +0 -21
- pybiolib-0.2.951.dist-info/METADATA +0 -61
- pybiolib-0.2.951.dist-info/RECORD +0 -153
- pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
- /LICENSE → /pybiolib-1.2.1890.dist-info/licenses/LICENSE +0 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
# pylint: skip-file
|
|
2
|
+
"""
|
|
3
|
+
Code from:
|
|
4
|
+
https://github.com/gtsystem/python-remotezip
|
|
5
|
+
&
|
|
6
|
+
https://github.com/uktrade/stream-unzip
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import io
|
|
10
|
+
import zipfile
|
|
11
|
+
|
|
12
|
+
from functools import partial
|
|
13
|
+
from struct import Struct
|
|
14
|
+
import zlib
|
|
15
|
+
|
|
16
|
+
__all__ = ['RemoteIOError', 'RemoteZip']
|
|
17
|
+
|
|
18
|
+
from biolib._internal.http_client import HttpClient
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RemoteZipError(Exception):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
class OutOfBound(RemoteZipError):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RemoteIOError(RemoteZipError):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RangeNotSupported(RemoteZipError):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PartialBuffer:
|
|
37
|
+
def __init__(self, buffer, offset, size, stream):
|
|
38
|
+
self.buffer = buffer if stream else io.BytesIO(buffer.read())
|
|
39
|
+
self.offset = offset
|
|
40
|
+
self.size = size
|
|
41
|
+
self.position = offset
|
|
42
|
+
self.stream = stream
|
|
43
|
+
|
|
44
|
+
def __repr__(self):
|
|
45
|
+
return "<PartialBuffer off=%s size=%s stream=%s>" % (self.offset, self.size, self.stream)
|
|
46
|
+
|
|
47
|
+
def read(self, size=0):
|
|
48
|
+
if size == 0:
|
|
49
|
+
size = self.offset + self.size - self.position
|
|
50
|
+
|
|
51
|
+
content = self.buffer.read(size)
|
|
52
|
+
self.position = self.offset + self.buffer.tell()
|
|
53
|
+
return content
|
|
54
|
+
|
|
55
|
+
def close(self):
|
|
56
|
+
if not self.buffer.closed:
|
|
57
|
+
self.buffer.close()
|
|
58
|
+
if hasattr(self.buffer, 'release_conn'):
|
|
59
|
+
self.buffer.release_conn()
|
|
60
|
+
|
|
61
|
+
def seek(self, offset, whence):
|
|
62
|
+
if whence == 2:
|
|
63
|
+
self.position = self.size + self.offset + offset
|
|
64
|
+
elif whence == 0:
|
|
65
|
+
self.position = offset
|
|
66
|
+
else:
|
|
67
|
+
self.position += offset
|
|
68
|
+
|
|
69
|
+
relative_position = self.position - self.offset
|
|
70
|
+
|
|
71
|
+
if relative_position < 0 or relative_position >= self.size:
|
|
72
|
+
raise OutOfBound("Position out of buffer bound")
|
|
73
|
+
|
|
74
|
+
if self.stream:
|
|
75
|
+
buff_pos = self.buffer.tell()
|
|
76
|
+
if relative_position < buff_pos:
|
|
77
|
+
raise OutOfBound("Negative seek not supported")
|
|
78
|
+
|
|
79
|
+
skip_bytes = relative_position - buff_pos
|
|
80
|
+
if skip_bytes == 0:
|
|
81
|
+
return self.position
|
|
82
|
+
self.buffer.read(skip_bytes)
|
|
83
|
+
else:
|
|
84
|
+
self.buffer.seek(relative_position)
|
|
85
|
+
|
|
86
|
+
return self.position
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class RemoteIO(io.IOBase):
|
|
90
|
+
def __init__(self, fetch_fun, initial_buffer_size=64*1024):
|
|
91
|
+
self.fetch_fun = fetch_fun
|
|
92
|
+
self.initial_buffer_size = initial_buffer_size
|
|
93
|
+
self.buffer = None
|
|
94
|
+
self.file_size = None
|
|
95
|
+
self.position = None
|
|
96
|
+
self._seek_succeeded = False
|
|
97
|
+
self.member_pos2size = None
|
|
98
|
+
self._last_member_pos = None
|
|
99
|
+
|
|
100
|
+
def set_pos2size(self, pos2size):
|
|
101
|
+
self.member_pos2size = pos2size
|
|
102
|
+
|
|
103
|
+
def read(self, size=0):
|
|
104
|
+
if size == 0:
|
|
105
|
+
size = self.file_size - self.buffer.position
|
|
106
|
+
|
|
107
|
+
if not self._seek_succeeded:
|
|
108
|
+
if self.member_pos2size is None:
|
|
109
|
+
fetch_size = size
|
|
110
|
+
stream = False
|
|
111
|
+
else:
|
|
112
|
+
try:
|
|
113
|
+
fetch_size = self.member_pos2size[self.buffer.position]
|
|
114
|
+
self._last_member_pos = self.buffer.position
|
|
115
|
+
except KeyError:
|
|
116
|
+
if self._last_member_pos and self._last_member_pos < self.buffer.position:
|
|
117
|
+
fetch_size = self.member_pos2size[self._last_member_pos]
|
|
118
|
+
fetch_size -= (self.buffer.position - self._last_member_pos)
|
|
119
|
+
else:
|
|
120
|
+
raise OutOfBound("Attempt to seek outside boundary of current zip member")
|
|
121
|
+
stream = True
|
|
122
|
+
|
|
123
|
+
self._seek_succeeded = True
|
|
124
|
+
self.buffer.close()
|
|
125
|
+
self.buffer = self.fetch_fun((self.buffer.position, self.buffer.position + fetch_size -1), stream=stream)
|
|
126
|
+
|
|
127
|
+
return self.buffer.read(size)
|
|
128
|
+
|
|
129
|
+
def seekable(self):
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
def seek(self, offset, whence=0):
|
|
133
|
+
if whence == 2 and self.file_size is None:
|
|
134
|
+
size = self.initial_buffer_size
|
|
135
|
+
self.buffer = self.fetch_fun((-size, None), stream=False)
|
|
136
|
+
self.file_size = self.buffer.size + self.buffer.position
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
pos = self.buffer.seek(offset, whence)
|
|
140
|
+
self._seek_succeeded = True
|
|
141
|
+
return pos
|
|
142
|
+
except OutOfBound:
|
|
143
|
+
self._seek_succeeded = False
|
|
144
|
+
return self.buffer.position # we ignore the issue here, we will check if buffer is fine during read
|
|
145
|
+
|
|
146
|
+
def tell(self):
|
|
147
|
+
return self.buffer.position
|
|
148
|
+
|
|
149
|
+
def close(self):
|
|
150
|
+
if self.buffer:
|
|
151
|
+
self.buffer.close()
|
|
152
|
+
self.buffer = None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class RemoteZip(zipfile.ZipFile):
|
|
156
|
+
def __init__(self, url, initial_buffer_size=64*1024):
|
|
157
|
+
self.url = url
|
|
158
|
+
|
|
159
|
+
rio = RemoteIO(self.fetch_fun, initial_buffer_size)
|
|
160
|
+
super(RemoteZip, self).__init__(rio)
|
|
161
|
+
rio.set_pos2size(self.get_position2size())
|
|
162
|
+
|
|
163
|
+
def get_central_directory(self):
|
|
164
|
+
return {
|
|
165
|
+
file.filename: {
|
|
166
|
+
attribute: getattr(file, attribute) for attribute in zipfile.ZipInfo.__slots__
|
|
167
|
+
} for file in self.infolist()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
def get_position2size(self):
|
|
171
|
+
ilist = self.infolist()
|
|
172
|
+
if len(ilist) == 0:
|
|
173
|
+
return {}
|
|
174
|
+
|
|
175
|
+
position2size = {ilist[-1].header_offset: self.start_dir - ilist[-1].header_offset}
|
|
176
|
+
for i in range(len(ilist) - 1):
|
|
177
|
+
m1, m2 = ilist[i: i+2]
|
|
178
|
+
position2size[m1.header_offset] = m2.header_offset - m1.header_offset
|
|
179
|
+
|
|
180
|
+
return position2size
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def make_buffer(io_buffer, content_range_header, stream):
|
|
184
|
+
range_min, range_max = content_range_header.split("/")[0][6:].split("-")
|
|
185
|
+
range_min, range_max = int(range_min), int(range_max)
|
|
186
|
+
return PartialBuffer(io_buffer, range_min, range_max - range_min + 1, stream)
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def make_header(range_min, range_max):
|
|
190
|
+
if range_max is None:
|
|
191
|
+
return "bytes=%s%s" % (range_min, '' if range_min < 0 else '-')
|
|
192
|
+
return "bytes=%s-%s" % (range_min, range_max)
|
|
193
|
+
|
|
194
|
+
def fetch_fun(self, data_range, stream=False):
|
|
195
|
+
range_header = self.make_header(*data_range)
|
|
196
|
+
try:
|
|
197
|
+
response = HttpClient.request(url=self.url, headers={'Range': range_header})
|
|
198
|
+
if 'Content-Range' not in response.headers:
|
|
199
|
+
raise RangeNotSupported("The server doesn't support range requests")
|
|
200
|
+
|
|
201
|
+
return self.make_buffer(io.BytesIO(response.content), response.headers['Content-Range'], stream=False)
|
|
202
|
+
except IOError as e:
|
|
203
|
+
raise RemoteIOError(str(e))
|
|
204
|
+
|
|
205
|
+
def stream_unzip(self, zipfile_chunks, password=None, chunk_size=65536):
|
|
206
|
+
local_file_header_signature = b'\x50\x4b\x03\x04'
|
|
207
|
+
local_file_header_struct = Struct('<H2sHHHIIIHH')
|
|
208
|
+
zip64_version = 45
|
|
209
|
+
zip64_compressed_size = 4294967295
|
|
210
|
+
zip64_size_signature = b'\x01\x00'
|
|
211
|
+
aes_extra_signature = b'\x01\x99'
|
|
212
|
+
central_directory_signature = b'\x50\x4b\x01\x02'
|
|
213
|
+
central_directory_info = self.get_central_directory()
|
|
214
|
+
|
|
215
|
+
def next_or_truncated_error(it):
|
|
216
|
+
try:
|
|
217
|
+
return next(it)
|
|
218
|
+
except StopIteration:
|
|
219
|
+
raise TruncatedDataError from None
|
|
220
|
+
|
|
221
|
+
def get_byte_readers(iterable):
|
|
222
|
+
# Return functions to return/"replace" bytes from/to the iterable
|
|
223
|
+
# - _yield_all: yields chunks as they come up (often for a "body")
|
|
224
|
+
# - _get_num: returns a single `bytes` of a given length
|
|
225
|
+
# - _return_unused: puts "unused" bytes "back", to be retrieved by a yield/get call
|
|
226
|
+
|
|
227
|
+
chunk = b''
|
|
228
|
+
offset = 0
|
|
229
|
+
it = iter(iterable)
|
|
230
|
+
|
|
231
|
+
def _yield_all():
|
|
232
|
+
nonlocal chunk, offset
|
|
233
|
+
|
|
234
|
+
while True:
|
|
235
|
+
if offset == len(chunk):
|
|
236
|
+
try:
|
|
237
|
+
chunk = next(it)
|
|
238
|
+
except StopIteration:
|
|
239
|
+
break
|
|
240
|
+
else:
|
|
241
|
+
offset = 0
|
|
242
|
+
to_yield = min(len(chunk) - offset, chunk_size)
|
|
243
|
+
offset = offset + to_yield
|
|
244
|
+
yield chunk[offset - to_yield:offset]
|
|
245
|
+
|
|
246
|
+
def _yield_num(num):
|
|
247
|
+
nonlocal chunk, offset
|
|
248
|
+
|
|
249
|
+
while num:
|
|
250
|
+
if offset == len(chunk):
|
|
251
|
+
chunk = next_or_truncated_error(it)
|
|
252
|
+
offset = 0
|
|
253
|
+
to_yield = min(num, len(chunk) - offset, chunk_size)
|
|
254
|
+
offset = offset + to_yield
|
|
255
|
+
num -= to_yield
|
|
256
|
+
yield chunk[offset - to_yield:offset]
|
|
257
|
+
|
|
258
|
+
def _get_num(num):
|
|
259
|
+
return b''.join(_yield_num(num))
|
|
260
|
+
|
|
261
|
+
def _return_unused(num_unused):
|
|
262
|
+
nonlocal offset
|
|
263
|
+
offset -= num_unused
|
|
264
|
+
|
|
265
|
+
return _yield_all, _get_num, _return_unused
|
|
266
|
+
|
|
267
|
+
def get_decompressor_none(num_bytes):
|
|
268
|
+
num_decompressed = 0
|
|
269
|
+
num_unused = 0
|
|
270
|
+
|
|
271
|
+
def _decompress(compressed_chunk):
|
|
272
|
+
nonlocal num_decompressed, num_unused
|
|
273
|
+
to_yield = min(len(compressed_chunk), num_bytes - num_decompressed)
|
|
274
|
+
num_decompressed += to_yield
|
|
275
|
+
num_unused = len(compressed_chunk) - to_yield
|
|
276
|
+
yield compressed_chunk[:to_yield]
|
|
277
|
+
|
|
278
|
+
def _is_done():
|
|
279
|
+
return num_decompressed == num_bytes
|
|
280
|
+
|
|
281
|
+
def _num_unused():
|
|
282
|
+
return num_unused
|
|
283
|
+
|
|
284
|
+
return _decompress, _is_done, _num_unused
|
|
285
|
+
|
|
286
|
+
def get_decompressor_deflate():
|
|
287
|
+
dobj = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
|
|
288
|
+
|
|
289
|
+
def _decompress_single(compressed_chunk):
|
|
290
|
+
try:
|
|
291
|
+
return dobj.decompress(compressed_chunk, chunk_size)
|
|
292
|
+
except zlib.error as e:
|
|
293
|
+
raise DeflateError() from e
|
|
294
|
+
|
|
295
|
+
def _decompress(compressed_chunk):
|
|
296
|
+
uncompressed_chunk = _decompress_single(compressed_chunk)
|
|
297
|
+
if uncompressed_chunk:
|
|
298
|
+
yield uncompressed_chunk
|
|
299
|
+
|
|
300
|
+
while dobj.unconsumed_tail and not dobj.eof:
|
|
301
|
+
uncompressed_chunk = _decompress_single(dobj.unconsumed_tail)
|
|
302
|
+
if uncompressed_chunk:
|
|
303
|
+
yield uncompressed_chunk
|
|
304
|
+
|
|
305
|
+
def _is_done():
|
|
306
|
+
return dobj.eof
|
|
307
|
+
|
|
308
|
+
def _num_unused():
|
|
309
|
+
return len(dobj.unused_data)
|
|
310
|
+
|
|
311
|
+
return _decompress, _is_done, _num_unused
|
|
312
|
+
|
|
313
|
+
def get_decompressor_deflate64():
|
|
314
|
+
uncompressed_chunks, is_done, num_bytes_unconsumed = stream_inflate64()
|
|
315
|
+
|
|
316
|
+
def _decompress(compressed_chunk):
|
|
317
|
+
yield from uncompressed_chunks((compressed_chunk,))
|
|
318
|
+
|
|
319
|
+
return _decompress, is_done, num_bytes_unconsumed
|
|
320
|
+
|
|
321
|
+
def yield_file(yield_all, get_num, return_unused):
|
|
322
|
+
|
|
323
|
+
def get_flag_bits(flags):
|
|
324
|
+
for b in flags:
|
|
325
|
+
for i in range(8):
|
|
326
|
+
yield (b >> i) & 1
|
|
327
|
+
|
|
328
|
+
def parse_extra(extra):
|
|
329
|
+
extra_offset = 0
|
|
330
|
+
while extra_offset <= len(extra) - 4:
|
|
331
|
+
extra_signature = extra[extra_offset:extra_offset + 2]
|
|
332
|
+
extra_offset += 2
|
|
333
|
+
extra_data_size, = Struct('<H').unpack(extra[extra_offset:extra_offset + 2])
|
|
334
|
+
extra_offset += 2
|
|
335
|
+
extra_data = extra[extra_offset:extra_offset + extra_data_size]
|
|
336
|
+
extra_offset += extra_data_size
|
|
337
|
+
yield (extra_signature, extra_data)
|
|
338
|
+
|
|
339
|
+
def get_extra_value(extra, if_true, signature, exception_if_missing, min_length, exception_if_too_short):
|
|
340
|
+
if if_true:
|
|
341
|
+
try:
|
|
342
|
+
value = extra[signature]
|
|
343
|
+
except KeyError:
|
|
344
|
+
raise exception_if_missing()
|
|
345
|
+
|
|
346
|
+
if len(value) < min_length:
|
|
347
|
+
raise exception_if_too_short()
|
|
348
|
+
else:
|
|
349
|
+
value = None
|
|
350
|
+
|
|
351
|
+
return value
|
|
352
|
+
|
|
353
|
+
def decrypt_weak_decompress(chunks, decompress, is_done, num_unused):
|
|
354
|
+
key_0 = 305419896
|
|
355
|
+
key_1 = 591751049
|
|
356
|
+
key_2 = 878082192
|
|
357
|
+
crc32 = zlib.crc32
|
|
358
|
+
bytes_c = bytes
|
|
359
|
+
|
|
360
|
+
def update_keys(byte):
|
|
361
|
+
nonlocal key_0, key_1, key_2
|
|
362
|
+
key_0 = ~crc32(bytes_c((byte,)), ~key_0) & 0xFFFFFFFF
|
|
363
|
+
key_1 = (key_1 + (key_0 & 0xFF)) & 0xFFFFFFFF
|
|
364
|
+
key_1 = ((key_1 * 134775813) + 1) & 0xFFFFFFFF
|
|
365
|
+
key_2 = ~crc32(bytes_c((key_1 >> 24,)), ~key_2) & 0xFFFFFFFF
|
|
366
|
+
|
|
367
|
+
def decrypt(chunk):
|
|
368
|
+
chunk = bytearray(chunk)
|
|
369
|
+
for i, byte in enumerate(chunk):
|
|
370
|
+
temp = key_2 | 2
|
|
371
|
+
byte ^= ((temp * (temp ^ 1)) >> 8) & 0xFF
|
|
372
|
+
update_keys(byte)
|
|
373
|
+
chunk[i] = byte
|
|
374
|
+
return bytes(chunk)
|
|
375
|
+
|
|
376
|
+
for byte in password:
|
|
377
|
+
update_keys(byte)
|
|
378
|
+
|
|
379
|
+
if decrypt(get_num(12))[11] != mod_time >> 8:
|
|
380
|
+
raise IncorrectZipCryptoPasswordError()
|
|
381
|
+
|
|
382
|
+
while not is_done():
|
|
383
|
+
yield from decompress(decrypt(next_or_truncated_error(chunks)))
|
|
384
|
+
|
|
385
|
+
return_unused(num_unused())
|
|
386
|
+
|
|
387
|
+
def decrypt_aes_decompress(chunks, decompress, is_done, num_unused, key_length_raw):
|
|
388
|
+
try:
|
|
389
|
+
key_length, salt_length = {1: (16, 8), 2: (24, 12), 3: (32, 16)}[key_length_raw]
|
|
390
|
+
except KeyError:
|
|
391
|
+
raise InvalidAESKeyLengthError(key_length_raw)
|
|
392
|
+
|
|
393
|
+
salt = get_num(salt_length)
|
|
394
|
+
password_verification_length = 2
|
|
395
|
+
|
|
396
|
+
keys = PBKDF2(password, salt, 2 * key_length + password_verification_length, 1000)
|
|
397
|
+
if keys[-password_verification_length:] != get_num(password_verification_length):
|
|
398
|
+
raise IncorrectAESPasswordError()
|
|
399
|
+
|
|
400
|
+
decrypter = AES.new(
|
|
401
|
+
keys[:key_length], AES.MODE_CTR,
|
|
402
|
+
counter=Counter.new(nbits=128, little_endian=True)
|
|
403
|
+
)
|
|
404
|
+
hmac = HMAC.new(keys[key_length:key_length * 2], digestmod=SHA1)
|
|
405
|
+
|
|
406
|
+
while not is_done():
|
|
407
|
+
chunk = next_or_truncated_error(chunks)
|
|
408
|
+
yield from decompress(decrypter.decrypt(chunk))
|
|
409
|
+
hmac.update(chunk[:len(chunk) - num_unused()])
|
|
410
|
+
|
|
411
|
+
return_unused(num_unused())
|
|
412
|
+
|
|
413
|
+
if get_num(10) != hmac.digest()[:10]:
|
|
414
|
+
raise HMACIntegrityError()
|
|
415
|
+
|
|
416
|
+
def decrypt_none_decompress(chunks, decompress, is_done, num_unused):
|
|
417
|
+
while not is_done():
|
|
418
|
+
yield from decompress(next_or_truncated_error(chunks))
|
|
419
|
+
|
|
420
|
+
return_unused(num_unused())
|
|
421
|
+
|
|
422
|
+
def get_crc_32_expected_from_data_descriptor(is_zip64, file_size_stored_as_long_long):
|
|
423
|
+
dd_optional_signature = get_num(4)
|
|
424
|
+
dd_so_far_num = \
|
|
425
|
+
0 if dd_optional_signature == b'PK\x07\x08' else \
|
|
426
|
+
4
|
|
427
|
+
dd_so_far = dd_optional_signature[:dd_so_far_num]
|
|
428
|
+
# Have to check both if zip64 and if we store as long long (8), since some zip64 store only as long (4)
|
|
429
|
+
dd_remaining = \
|
|
430
|
+
(20 - dd_so_far_num) if is_zip64 and file_size_stored_as_long_long else \
|
|
431
|
+
(12 - dd_so_far_num)
|
|
432
|
+
dd = dd_so_far + get_num(dd_remaining)
|
|
433
|
+
crc_32_expected, = Struct('<I').unpack(dd[:4])
|
|
434
|
+
return crc_32_expected
|
|
435
|
+
|
|
436
|
+
def get_crc_32_expected_from_file_header():
|
|
437
|
+
return crc_32_expected
|
|
438
|
+
|
|
439
|
+
def read_data_and_crc_32_ignore(get_crc_32_expected, chunks):
|
|
440
|
+
yield from chunks
|
|
441
|
+
get_crc_32_expected()
|
|
442
|
+
|
|
443
|
+
def read_data_and_crc_32_verify(get_crc_32_expected, chunks):
|
|
444
|
+
crc_32_actual = zlib.crc32(b'')
|
|
445
|
+
for chunk in chunks:
|
|
446
|
+
crc_32_actual = zlib.crc32(chunk, crc_32_actual)
|
|
447
|
+
yield chunk
|
|
448
|
+
|
|
449
|
+
if crc_32_actual != get_crc_32_expected():
|
|
450
|
+
raise CRC32IntegrityError()
|
|
451
|
+
|
|
452
|
+
version, flags, compression_raw, mod_time, mod_date, crc_32_expected, compressed_size_raw, uncompressed_size_raw, file_name_len, extra_field_len = \
|
|
453
|
+
local_file_header_struct.unpack(get_num(local_file_header_struct.size))
|
|
454
|
+
|
|
455
|
+
flag_bits = tuple(get_flag_bits(flags))
|
|
456
|
+
if (
|
|
457
|
+
flag_bits[4] # Enhanced deflating
|
|
458
|
+
or flag_bits[5] # Compressed patched
|
|
459
|
+
or flag_bits[6] # Strong encrypted
|
|
460
|
+
or flag_bits[13] # Masked header values
|
|
461
|
+
):
|
|
462
|
+
raise UnsupportedFlagsError(flag_bits)
|
|
463
|
+
|
|
464
|
+
file_name = get_num(file_name_len)
|
|
465
|
+
file_name_str = file_name.decode()
|
|
466
|
+
|
|
467
|
+
# Get these attributes from central directory as they are incorrect in the File Header
|
|
468
|
+
uncompressed_size_raw = central_directory_info[file_name_str]['file_size']
|
|
469
|
+
extract_version = central_directory_info[file_name_str]['extract_version']
|
|
470
|
+
central_directory_extra = central_directory_info[file_name_str]['extra']
|
|
471
|
+
|
|
472
|
+
# Zip64 Extra field requires 20 bytes to store Header (2) + Field Length (2) + File size (8) + Compressed size (8)
|
|
473
|
+
# The length of the filesize field determines if the length of the data descriptor is 12 or 20 bytes.
|
|
474
|
+
if zip64_size_signature in central_directory_extra and len(central_directory_extra) >= 20:
|
|
475
|
+
file_size_stored_as_long_long = True
|
|
476
|
+
else:
|
|
477
|
+
file_size_stored_as_long_long = False
|
|
478
|
+
|
|
479
|
+
extra = dict(parse_extra(get_num(extra_field_len)))
|
|
480
|
+
|
|
481
|
+
is_weak_encrypted = flag_bits[0] and compression_raw != 99
|
|
482
|
+
is_aes_encrypted = flag_bits[0] and compression_raw == 99
|
|
483
|
+
aes_extra = get_extra_value(extra, is_aes_encrypted, aes_extra_signature, MissingAESExtraError, 7,
|
|
484
|
+
TruncatedAESExtraError)
|
|
485
|
+
is_aes_2_encrypted = is_aes_encrypted and aes_extra[0:2] == b'\x02\x00'
|
|
486
|
+
|
|
487
|
+
if is_weak_encrypted and password is None:
|
|
488
|
+
raise MissingZipCryptoPasswordError()
|
|
489
|
+
|
|
490
|
+
if is_aes_encrypted and password is None:
|
|
491
|
+
raise MissingAESPasswordError()
|
|
492
|
+
|
|
493
|
+
compression = \
|
|
494
|
+
Struct('<H').unpack(aes_extra[5:7])[0] if is_aes_encrypted else \
|
|
495
|
+
compression_raw
|
|
496
|
+
|
|
497
|
+
if compression not in (0, 8, 9):
|
|
498
|
+
raise UnsupportedCompressionTypeError(compression)
|
|
499
|
+
|
|
500
|
+
has_data_descriptor = flag_bits[3]
|
|
501
|
+
is_zip64 = compressed_size_raw == zip64_compressed_size and uncompressed_size_raw == zip64_compressed_size \
|
|
502
|
+
or extract_version == zip64_version
|
|
503
|
+
zip64_extra = get_extra_value(extra, not has_data_descriptor and is_zip64, zip64_size_signature,
|
|
504
|
+
MissingZip64ExtraError, 16, TruncatedZip64ExtraError)
|
|
505
|
+
|
|
506
|
+
# zip64_extra can be None in some cases where is_zip64 is True so it is necessary to check.
|
|
507
|
+
uncompressed_size = \
|
|
508
|
+
None if has_data_descriptor and compression in (8, 9) else \
|
|
509
|
+
Struct('<Q').unpack(zip64_extra[:8])[0] if is_zip64 and zip64_extra else \
|
|
510
|
+
uncompressed_size_raw
|
|
511
|
+
|
|
512
|
+
decompressor = \
|
|
513
|
+
get_decompressor_none(uncompressed_size) if compression == 0 else \
|
|
514
|
+
get_decompressor_deflate() if compression == 8 else \
|
|
515
|
+
get_decompressor_deflate64()
|
|
516
|
+
|
|
517
|
+
decompressed_bytes = \
|
|
518
|
+
decrypt_weak_decompress(yield_all(), *decompressor) if is_weak_encrypted else \
|
|
519
|
+
decrypt_aes_decompress(yield_all(), *decompressor,
|
|
520
|
+
key_length_raw=aes_extra[4]) if is_aes_encrypted else \
|
|
521
|
+
decrypt_none_decompress(yield_all(), *decompressor)
|
|
522
|
+
|
|
523
|
+
get_crc_32_expected = \
|
|
524
|
+
partial(get_crc_32_expected_from_data_descriptor, is_zip64, file_size_stored_as_long_long) \
|
|
525
|
+
if has_data_descriptor else get_crc_32_expected_from_file_header
|
|
526
|
+
|
|
527
|
+
crc_checked_bytes = \
|
|
528
|
+
read_data_and_crc_32_ignore(get_crc_32_expected, decompressed_bytes) if is_aes_2_encrypted else \
|
|
529
|
+
read_data_and_crc_32_verify(get_crc_32_expected, decompressed_bytes)
|
|
530
|
+
|
|
531
|
+
return file_name, uncompressed_size, crc_checked_bytes
|
|
532
|
+
|
|
533
|
+
def all():
|
|
534
|
+
yield_all, get_num, return_unused = get_byte_readers(zipfile_chunks)
|
|
535
|
+
|
|
536
|
+
while True:
|
|
537
|
+
signature = get_num(len(local_file_header_signature))
|
|
538
|
+
if signature == local_file_header_signature:
|
|
539
|
+
yield yield_file(yield_all, get_num, return_unused)
|
|
540
|
+
elif signature == central_directory_signature:
|
|
541
|
+
for _ in yield_all():
|
|
542
|
+
pass
|
|
543
|
+
break
|
|
544
|
+
else:
|
|
545
|
+
raise UnexpectedSignatureError(signature)
|
|
546
|
+
|
|
547
|
+
for file_name, file_size, unzipped_chunks in all():
|
|
548
|
+
yield file_name, file_size, unzipped_chunks
|
|
549
|
+
for _ in unzipped_chunks:
|
|
550
|
+
raise UnfinishedIterationError()
|
|
551
|
+
|
|
552
|
+
class UnzipError(Exception):
|
|
553
|
+
pass
|
|
554
|
+
|
|
555
|
+
class InvalidOperationError(UnzipError):
|
|
556
|
+
pass
|
|
557
|
+
|
|
558
|
+
class UnfinishedIterationError(InvalidOperationError):
|
|
559
|
+
pass
|
|
560
|
+
|
|
561
|
+
class UnzipValueError(UnzipError, ValueError):
|
|
562
|
+
pass
|
|
563
|
+
|
|
564
|
+
class DataError(UnzipValueError):
|
|
565
|
+
pass
|
|
566
|
+
|
|
567
|
+
class UncompressError(UnzipValueError):
|
|
568
|
+
pass
|
|
569
|
+
|
|
570
|
+
class DeflateError(UncompressError):
|
|
571
|
+
pass
|
|
572
|
+
|
|
573
|
+
class UnsupportedFeatureError(DataError):
|
|
574
|
+
pass
|
|
575
|
+
|
|
576
|
+
class UnsupportedFlagsError(UnsupportedFeatureError):
|
|
577
|
+
pass
|
|
578
|
+
|
|
579
|
+
class UnsupportedCompressionTypeError(UnsupportedFeatureError):
|
|
580
|
+
pass
|
|
581
|
+
|
|
582
|
+
class TruncatedDataError(DataError):
|
|
583
|
+
pass
|
|
584
|
+
|
|
585
|
+
class UnexpectedSignatureError(DataError):
|
|
586
|
+
pass
|
|
587
|
+
|
|
588
|
+
class MissingExtraError(DataError):
|
|
589
|
+
pass
|
|
590
|
+
|
|
591
|
+
class MissingZip64ExtraError(MissingExtraError):
|
|
592
|
+
pass
|
|
593
|
+
|
|
594
|
+
class MissingAESExtraError(MissingExtraError):
|
|
595
|
+
pass
|
|
596
|
+
|
|
597
|
+
class TruncatedExtraError(DataError):
|
|
598
|
+
pass
|
|
599
|
+
|
|
600
|
+
class TruncatedZip64ExtraError(TruncatedExtraError):
|
|
601
|
+
pass
|
|
602
|
+
|
|
603
|
+
class TruncatedAESExtraError(TruncatedExtraError):
|
|
604
|
+
pass
|
|
605
|
+
|
|
606
|
+
class InvalidExtraError(TruncatedExtraError):
|
|
607
|
+
pass
|
|
608
|
+
|
|
609
|
+
class InvalidAESKeyLengthError(TruncatedExtraError):
|
|
610
|
+
pass
|
|
611
|
+
|
|
612
|
+
class IntegrityError(DataError):
|
|
613
|
+
pass
|
|
614
|
+
|
|
615
|
+
class HMACIntegrityError(IntegrityError):
|
|
616
|
+
pass
|
|
617
|
+
|
|
618
|
+
class CRC32IntegrityError(IntegrityError):
|
|
619
|
+
pass
|
|
620
|
+
|
|
621
|
+
class PasswordError(UnzipValueError):
|
|
622
|
+
pass
|
|
623
|
+
|
|
624
|
+
class MissingPasswordError(UnzipValueError):
|
|
625
|
+
pass
|
|
626
|
+
|
|
627
|
+
class MissingZipCryptoPasswordError(MissingPasswordError):
|
|
628
|
+
pass
|
|
629
|
+
|
|
630
|
+
class MissingAESPasswordError(MissingPasswordError):
|
|
631
|
+
pass
|
|
632
|
+
|
|
633
|
+
class IncorrectPasswordError(PasswordError):
|
|
634
|
+
pass
|
|
635
|
+
|
|
636
|
+
class IncorrectZipCryptoPasswordError(IncorrectPasswordError):
|
|
637
|
+
pass
|
|
638
|
+
|
|
639
|
+
class IncorrectAESPasswordError(IncorrectPasswordError):
|
|
640
|
+
pass
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pybiolib
|
|
3
|
+
Version: 1.2.1890
|
|
4
|
+
Summary: BioLib Python Client
|
|
5
|
+
Project-URL: Homepage, https://github.com/biolib
|
|
6
|
+
Author-email: biolib <hello@biolib.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: biolib
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.6.3
|
|
13
|
+
Requires-Dist: appdirs>=1.4.3
|
|
14
|
+
Requires-Dist: click>=8.0.0
|
|
15
|
+
Requires-Dist: docker>=5.0.3
|
|
16
|
+
Requires-Dist: importlib-metadata>=1.6.1
|
|
17
|
+
Requires-Dist: pyyaml>=5.3.1
|
|
18
|
+
Requires-Dist: rich>=12.4.4
|
|
19
|
+
Requires-Dist: typing-extensions>=4.1.0; python_version < '3.11'
|
|
20
|
+
Provides-Extra: compute-node
|
|
21
|
+
Requires-Dist: flask>=2.0.1; extra == 'compute-node'
|
|
22
|
+
Requires-Dist: gunicorn>=20.1.0; extra == 'compute-node'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# PyBioLib
|
|
26
|
+
|
|
27
|
+
PyBioLib is a Python package for running BioLib applications from Python scripts and the command line.
|
|
28
|
+
|
|
29
|
+
### Python Example
|
|
30
|
+
```python
|
|
31
|
+
# pip3 install -U pybiolib
|
|
32
|
+
import biolib
|
|
33
|
+
samtools = biolib.load('samtools/samtools')
|
|
34
|
+
print(samtools.cli(args='--help'))
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Command Line Example
|
|
38
|
+
```bash
|
|
39
|
+
pip3 install -U pybiolib
|
|
40
|
+
biolib run samtools/samtools --help
|
|
41
|
+
```
|