pybiolib 1.2.659__py3-none-any.whl → 1.2.678__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.
@@ -0,0 +1,106 @@
1
+ """Utilities for displaying file trees and formatting file sizes."""
2
+
3
+ from typing import Any, Dict, List, Optional, Union
4
+
5
+ from biolib.biolib_binary_format.utils import LazyLoadedFile
6
+
7
+
8
+ def format_size(size_bytes: int) -> str:
9
+ """Convert bytes to human-readable format (e.g., 2.5KB).
10
+
11
+ Args:
12
+ size_bytes: File size in bytes
13
+
14
+ Returns:
15
+ Human-readable string representation of the file size
16
+ """
17
+ if size_bytes < 1024:
18
+ return f'{size_bytes}B'
19
+ elif size_bytes < 1024 * 1024:
20
+ return f'{size_bytes/1024:.1f}KB'
21
+ elif size_bytes < 1024 * 1024 * 1024:
22
+ return f'{size_bytes/(1024*1024):.1f}MB'
23
+ else:
24
+ return f'{size_bytes/(1024*1024*1024):.1f}GB'
25
+
26
+
27
+ def build_tree_str(
28
+ data: Dict[str, Any],
29
+ prefix: str = '',
30
+ tree_lines: Optional[List[str]] = None,
31
+ blue: str = '\033[34m',
32
+ white: str = '\033[90m', # Changed from white (37m) to dark gray (90m) for better visibility on light backgrounds
33
+ reset: str = '\033[0m',
34
+ ) -> List[str]:
35
+ """Build a string representation of a file tree with color-coded directories and files.
36
+
37
+ Args:
38
+ data: Hierarchical tree structure of directories and files
39
+ prefix: Line prefix for indentation and tree structure characters
40
+ tree_lines: List to accumulate tree lines
41
+ blue: ANSI color code for directories
42
+ white: ANSI color code for files (defaults to dark gray for visibility on light backgrounds)
43
+ reset: ANSI color code to reset color
44
+
45
+ Returns:
46
+ List of tree lines for display
47
+ """
48
+ if tree_lines is None:
49
+ tree_lines = []
50
+
51
+ # Get sorted items, keeping directories first
52
+ items = sorted([(k, v) for k, v in data.items() if k != '__files__'])
53
+ files_list = sorted(data.get('__files__', []), key=lambda f: f['name'])
54
+
55
+ # Add directories
56
+ for i, (key, value) in enumerate(items):
57
+ is_last_dir = i == len(items) - 1 and not files_list
58
+
59
+ if is_last_dir:
60
+ tree_lines.append(f'{prefix}└── {blue}{key}{reset}')
61
+ build_tree_str(value, prefix + ' ', tree_lines, blue, white, reset)
62
+ else:
63
+ tree_lines.append(f'{prefix}├── {blue}{key}{reset}')
64
+ build_tree_str(value, prefix + '│ ', tree_lines, blue, white, reset)
65
+
66
+ # Add files with their sizes
67
+ for i, file in enumerate(files_list):
68
+ is_last = i == len(files_list) - 1
69
+ size_str = format_size(file['size'])
70
+ file_name = file['name']
71
+
72
+ if is_last:
73
+ tree_lines.append(f'{prefix}└── {white}{file_name} ({size_str}){reset}')
74
+ else:
75
+ tree_lines.append(f'{prefix}├── {white}{file_name} ({size_str}){reset}')
76
+
77
+ return tree_lines
78
+
79
+
80
+ def build_tree_from_files(files: List[LazyLoadedFile]) -> Dict[str, Union[Dict[str, Any], List[Dict[str, Any]]]]:
81
+ """Build a hierarchical tree structure from a list of files.
82
+
83
+ Args:
84
+ files: List of files to organize into a tree
85
+
86
+ Returns:
87
+ Hierarchical tree structure of directories and files
88
+ """
89
+ tree: Dict[str, Union[Dict[str, Any], List[Dict[str, Any]]]] = {}
90
+ for file in files:
91
+ parts = file.path.lstrip('/').split('/')
92
+ current: Dict[str, Union[Dict[str, Any], List[Dict[str, Any]]]] = tree
93
+ for i, part in enumerate(parts):
94
+ if i == len(parts) - 1: # This is a file
95
+ if '__files__' not in current:
96
+ current['__files__'] = []
97
+ files_list = current['__files__']
98
+ assert isinstance(files_list, list)
99
+ files_list.append({'name': part, 'size': file.length})
100
+ else: # This is a directory
101
+ if part not in current:
102
+ current[part] = {}
103
+ dir_dict = current[part]
104
+ assert isinstance(dir_dict, dict)
105
+ current = dir_dict
106
+ return tree
biolib/jobs/job.py CHANGED
@@ -9,6 +9,7 @@ from urllib.parse import urlparse
9
9
  import biolib.api.client
10
10
  from biolib import utils
11
11
  from biolib._internal.http_client import HttpClient
12
+ from biolib._internal.tree_utils import build_tree_from_files, build_tree_str
12
13
  from biolib._internal.utils import open_browser_window_from_notebook
13
14
  from biolib.api.client import ApiClient
14
15
  from biolib.biolib_api_client import BiolibApiClient, CreatedJobDict
@@ -56,7 +57,48 @@ class Job:
56
57
  return f"Job for {self._job_dict['app_uri']} created at {self._job_dict['created_at']} ({self._uuid})"
57
58
 
58
59
  def __repr__(self):
59
- return f'Job: {self._uuid}'
60
+ # Get job status and shareable link
61
+ status = self.get_status()
62
+ shareable_link = self.get_shareable_link()
63
+
64
+ # ANSI color codes for terminal output
65
+ blue = '\033[34m'
66
+ white = '\033[37m'
67
+ reset = '\033[0m'
68
+
69
+ # Start with the header section
70
+ output_lines = [
71
+ '--- BioLib Result ---',
72
+ f'ID: {self._uuid}',
73
+ f'Status: {status}',
74
+ f'Link: {shareable_link}',
75
+ ]
76
+
77
+ # Only show output files if the job is not pending
78
+ if not self.is_pending():
79
+ output_lines.append('Output Files:')
80
+
81
+ try:
82
+ # Get files from the job
83
+ files = self.list_output_files()
84
+
85
+ # If no files, indicate that
86
+ if not files:
87
+ output_lines.append('No output files')
88
+ return '\n'.join(output_lines)
89
+
90
+ # If more than 25 files, show simplified message
91
+ if len(files) > 25:
92
+ output_lines.append(f'{len(files)} output files in result.')
93
+ return '\n'.join(output_lines)
94
+
95
+ # Build the tree representation
96
+ tree_data = build_tree_from_files(files)
97
+ output_lines.extend(build_tree_str(tree_data, blue=blue, white=white, reset=reset))
98
+ except Exception:
99
+ output_lines.append('Error accessing output files')
100
+
101
+ return '\n'.join(output_lines)
60
102
 
61
103
  @property
62
104
  def id(self) -> str: # pylint: disable=invalid-name
@@ -128,6 +170,21 @@ class Job:
128
170
  self,
129
171
  path_filter: Optional[PathFilter] = None,
130
172
  ) -> List[LazyLoadedFile]:
173
+ """List output files from the job.
174
+
175
+ Args:
176
+ path_filter (PathFilter, optional): Filter to apply to the output files.
177
+ Can be a string glob pattern or a callable that takes a path string and returns a boolean.
178
+
179
+ Returns:
180
+ List[LazyLoadedFile]: List of output files.
181
+
182
+ Example::
183
+ >>> job = biolib.get_job("job_id")
184
+ >>> output_files = job.list_output_files()
185
+ >>> # Filter files with a glob pattern
186
+ >>> output_files = job.list_output_files("*.pdb")
187
+ """
131
188
  return self.result.list_output_files(path_filter=path_filter)
132
189
 
133
190
  def get_output_file(self, filename: str) -> LazyLoadedFile:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pybiolib
3
- Version: 1.2.659
3
+ Version: 1.2.678
4
4
  Summary: BioLib Python Client
5
5
  License: MIT
6
6
  Keywords: biolib
@@ -19,6 +19,7 @@ biolib/_internal/llm_instructions/.github/prompts/biolib_run_apps.prompt.md,sha2
19
19
  biolib/_internal/llm_instructions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  biolib/_internal/push_application.py,sha256=Ffj3iZ0nma4KHTB-PKCeFvhCHHhAEhgKClxnfBUpL8U,12296
21
21
  biolib/_internal/runtime.py,sha256=BiHl4klUHr36MCpqKaUso4idHeBZfPAahLYRQrabFqA,486
22
+ biolib/_internal/tree_utils.py,sha256=_Q_6_NDtIiROcefymqxEVddjqti6Mt3OZ4U0GcDW61s,3904
22
23
  biolib/_internal/types/__init__.py,sha256=xLgOQJFh3GRtiqIJq7MaqHReZx4pp34_zcaFQ_JjuJ4,198
23
24
  biolib/_internal/types/app.py,sha256=Mz2QGD_jESX-K9JYnLWPo4YA__Q_1FQQTk9pvidCohU,118
24
25
  biolib/_internal/types/data_record.py,sha256=9r_vdhVs60YTnzU4XQFXfDrfS2P2MqD3BH2xa7lk6ck,852
@@ -107,7 +108,7 @@ biolib/compute_node/webserver/worker_thread.py,sha256=7uD9yQPhePYvP2HCJ27EeZ_h6p
107
108
  biolib/experiments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
109
  biolib/experiments/experiment.py,sha256=pBtnOHz0kKoFxlIGf08o8ZCEOze-CljwfOsTdhvCTCk,8646
109
110
  biolib/jobs/__init__.py,sha256=aIb2H2DHjQbM2Bs-dysFijhwFcL58Blp0Co0gimED3w,32
110
- biolib/jobs/job.py,sha256=rcQPLrUsVHdzZ0g_T9f3m_mmVF0m-aYi_fKYTbQATaA,24174
111
+ biolib/jobs/job.py,sha256=nHSYUIUcvujfh6Q85j1qLGIWzXKwp4kEou8ywhngJlQ,26276
111
112
  biolib/jobs/job_result.py,sha256=rALHiKYNaC9lHi_JJqBob1RubzNLwG9Z386kwRJjd2M,5885
112
113
  biolib/jobs/types.py,sha256=ezvaoTANsWazK6PmfpYcqezdfjP7MNBEBfqIZGoZhz8,997
113
114
  biolib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -125,8 +126,8 @@ biolib/utils/cache_state.py,sha256=u256F37QSRIVwqKlbnCyzAX4EMI-kl6Dwu6qwj-Qmag,3
125
126
  biolib/utils/multipart_uploader.py,sha256=XvGP1I8tQuKhAH-QugPRoEsCi9qvbRk-DVBs5PNwwJo,8452
126
127
  biolib/utils/seq_util.py,sha256=Ozk0blGtPur_D9MwShD02r_mphyQmgZkx-lOHOwnlIM,6730
127
128
  biolib/utils/zip/remote_zip.py,sha256=0wErYlxir5921agfFeV1xVjf29l9VNgGQvNlWOlj2Yc,23232
128
- pybiolib-1.2.659.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
129
- pybiolib-1.2.659.dist-info/METADATA,sha256=qpaLR_QUpfGpkRX0F9kNFxh5_I5GGlh1eLnIfzopGs4,1570
130
- pybiolib-1.2.659.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
131
- pybiolib-1.2.659.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
132
- pybiolib-1.2.659.dist-info/RECORD,,
129
+ pybiolib-1.2.678.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
130
+ pybiolib-1.2.678.dist-info/METADATA,sha256=x4HkSXSKyAR_YIOCHMl7SfrdBptlfAuj2XLTsKjW_cw,1570
131
+ pybiolib-1.2.678.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
132
+ pybiolib-1.2.678.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
133
+ pybiolib-1.2.678.dist-info/RECORD,,