hpcflow-new2 0.2.0a179__py3-none-any.whl → 0.2.0a181__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.
- hpcflow/_version.py +1 -1
- hpcflow/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/sdk/__init__.py +4 -1
- hpcflow/sdk/app.py +160 -15
- hpcflow/sdk/cli.py +14 -0
- hpcflow/sdk/cli_common.py +83 -0
- hpcflow/sdk/config/__init__.py +4 -0
- hpcflow/sdk/config/callbacks.py +25 -2
- hpcflow/sdk/config/cli.py +4 -1
- hpcflow/sdk/config/config.py +188 -14
- hpcflow/sdk/config/config_file.py +91 -3
- hpcflow/sdk/config/errors.py +33 -0
- hpcflow/sdk/core/__init__.py +2 -0
- hpcflow/sdk/core/actions.py +492 -35
- hpcflow/sdk/core/cache.py +22 -0
- hpcflow/sdk/core/command_files.py +221 -5
- hpcflow/sdk/core/commands.py +57 -0
- hpcflow/sdk/core/element.py +407 -8
- hpcflow/sdk/core/environment.py +92 -0
- hpcflow/sdk/core/errors.py +245 -61
- hpcflow/sdk/core/json_like.py +72 -14
- hpcflow/sdk/core/loop.py +122 -21
- hpcflow/sdk/core/loop_cache.py +34 -9
- hpcflow/sdk/core/object_list.py +172 -26
- hpcflow/sdk/core/parallel.py +14 -0
- hpcflow/sdk/core/parameters.py +478 -25
- hpcflow/sdk/core/rule.py +31 -1
- hpcflow/sdk/core/run_dir_files.py +12 -2
- hpcflow/sdk/core/task.py +407 -80
- hpcflow/sdk/core/task_schema.py +70 -9
- hpcflow/sdk/core/test_utils.py +35 -0
- hpcflow/sdk/core/utils.py +101 -4
- hpcflow/sdk/core/validation.py +13 -1
- hpcflow/sdk/core/workflow.py +316 -96
- hpcflow/sdk/core/zarr_io.py +23 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +9 -0
- hpcflow/sdk/helper/helper.py +28 -0
- hpcflow/sdk/helper/watcher.py +33 -0
- hpcflow/sdk/log.py +40 -0
- hpcflow/sdk/persistence/__init__.py +14 -4
- hpcflow/sdk/persistence/base.py +289 -23
- hpcflow/sdk/persistence/json.py +29 -0
- hpcflow/sdk/persistence/pending.py +217 -107
- hpcflow/sdk/persistence/store_resource.py +58 -2
- hpcflow/sdk/persistence/utils.py +8 -0
- hpcflow/sdk/persistence/zarr.py +68 -1
- hpcflow/sdk/runtime.py +52 -10
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/jobscript.py +198 -9
- hpcflow/sdk/submission/jobscript_info.py +13 -0
- hpcflow/sdk/submission/schedulers/__init__.py +60 -0
- hpcflow/sdk/submission/schedulers/direct.py +53 -0
- hpcflow/sdk/submission/schedulers/sge.py +45 -7
- hpcflow/sdk/submission/schedulers/slurm.py +45 -8
- hpcflow/sdk/submission/schedulers/utils.py +4 -0
- hpcflow/sdk/submission/shells/__init__.py +11 -1
- hpcflow/sdk/submission/shells/base.py +32 -1
- hpcflow/sdk/submission/shells/bash.py +36 -1
- hpcflow/sdk/submission/shells/os_version.py +18 -6
- hpcflow/sdk/submission/shells/powershell.py +22 -0
- hpcflow/sdk/submission/submission.py +88 -3
- hpcflow/sdk/typing.py +10 -1
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/METADATA +3 -3
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/RECORD +70 -70
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a181.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Models of data stores as resources.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from abc import ABC, abstractmethod
|
2
6
|
import copy
|
3
7
|
import json
|
@@ -13,6 +17,12 @@ class StoreResource(ABC):
|
|
13
17
|
A `PersistentStore` maps workflow data across zero or more store resources. Updates to
|
14
18
|
persistent workflow data that live in the same store resource are performed together.
|
15
19
|
|
20
|
+
Parameters
|
21
|
+
----------
|
22
|
+
app: App
|
23
|
+
The main application context.
|
24
|
+
name:
|
25
|
+
The store name.
|
16
26
|
"""
|
17
27
|
|
18
28
|
def __init__(self, app, name: str) -> None:
|
@@ -26,6 +36,9 @@ class StoreResource(ABC):
|
|
26
36
|
|
27
37
|
@property
|
28
38
|
def logger(self):
|
39
|
+
"""
|
40
|
+
The logger.
|
41
|
+
"""
|
29
42
|
return self.app.persistence_logger
|
30
43
|
|
31
44
|
@abstractmethod
|
@@ -37,6 +50,14 @@ class StoreResource(ABC):
|
|
37
50
|
pass
|
38
51
|
|
39
52
|
def open(self, action):
|
53
|
+
"""
|
54
|
+
Open the store.
|
55
|
+
|
56
|
+
Parameters
|
57
|
+
----------
|
58
|
+
action: str
|
59
|
+
What we are opening the store for; typically either ``read`` or ``update``.
|
60
|
+
"""
|
40
61
|
if action == "read":
|
41
62
|
# reuse "update" data if set, rather than re-loading from disk -- but copy,
|
42
63
|
# so changes made in the "read" scope do not update!
|
@@ -64,6 +85,15 @@ class StoreResource(ABC):
|
|
64
85
|
pass
|
65
86
|
|
66
87
|
def close(self, action):
|
88
|
+
"""
|
89
|
+
Close the store for a particular action.
|
90
|
+
|
91
|
+
Parameters
|
92
|
+
----------
|
93
|
+
action: str
|
94
|
+
What we are closing the store for.
|
95
|
+
Should match a previous call to :py:meth:`close`.
|
96
|
+
"""
|
67
97
|
if action == "read":
|
68
98
|
self.logger.debug(f"{self!r}: closing read.")
|
69
99
|
elif action == "update":
|
@@ -88,7 +118,22 @@ class StoreResource(ABC):
|
|
88
118
|
|
89
119
|
|
90
120
|
class JSONFileStoreResource(StoreResource):
|
91
|
-
"""
|
121
|
+
"""
|
122
|
+
For caching reads and writes to a JSON file.
|
123
|
+
|
124
|
+
Parameters
|
125
|
+
----------
|
126
|
+
app: App
|
127
|
+
The main application context.
|
128
|
+
name:
|
129
|
+
The store name.
|
130
|
+
filename:
|
131
|
+
The name of the JSON file.
|
132
|
+
path:
|
133
|
+
The path to the directory containing the JSON file.
|
134
|
+
fs:
|
135
|
+
The filesystem that the JSON file resides within.
|
136
|
+
"""
|
92
137
|
|
93
138
|
def __init__(self, app, name: str, filename: str, path: Union[str, Path], fs):
|
94
139
|
self.filename = filename
|
@@ -114,7 +159,18 @@ class JSONFileStoreResource(StoreResource):
|
|
114
159
|
|
115
160
|
|
116
161
|
class ZarrAttrsStoreResource(StoreResource):
|
117
|
-
"""
|
162
|
+
"""
|
163
|
+
For caching reads and writes to Zarr attributes on groups and arrays.
|
164
|
+
|
165
|
+
Parameters
|
166
|
+
----------
|
167
|
+
app: App
|
168
|
+
The main application context.
|
169
|
+
name:
|
170
|
+
The store name.
|
171
|
+
open_call:
|
172
|
+
How to actually perform an open on the underlying resource.
|
173
|
+
"""
|
118
174
|
|
119
175
|
def __init__(self, app, name: str, open_call: Callable):
|
120
176
|
self.open_call = open_call
|
hpcflow/sdk/persistence/utils.py
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
Miscellaneous persistence-related helpers.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from getpass import getpass
|
2
6
|
|
3
7
|
from hpcflow.sdk.core.errors import WorkflowNotFoundError
|
4
8
|
|
5
9
|
|
6
10
|
def ask_pw_on_auth_exc(f, *args, add_pw_to=None, **kwargs):
|
11
|
+
"""
|
12
|
+
Run the given function on the given arguments and add a password if the function
|
13
|
+
fails with an SSHException.
|
14
|
+
"""
|
7
15
|
from paramiko.ssh_exception import SSHException
|
8
16
|
|
9
17
|
try:
|
hpcflow/sdk/persistence/zarr.py
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Persistence model based on writing Zarr arrays.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from __future__ import annotations
|
2
6
|
|
3
7
|
import copy
|
@@ -132,6 +136,10 @@ def append_items_to_ragged_array(arr, items):
|
|
132
136
|
|
133
137
|
@dataclass
|
134
138
|
class ZarrStoreTask(StoreTask):
|
139
|
+
"""
|
140
|
+
Represents a task in a Zarr persistent store.
|
141
|
+
"""
|
142
|
+
|
135
143
|
def encode(self) -> Tuple[int, np.ndarray, Dict]:
|
136
144
|
"""Prepare store task data for the persistent store."""
|
137
145
|
wk_task = {"id_": self.id_, "element_IDs": np.array(self.element_IDs)}
|
@@ -147,6 +155,10 @@ class ZarrStoreTask(StoreTask):
|
|
147
155
|
|
148
156
|
@dataclass
|
149
157
|
class ZarrStoreElement(StoreElement):
|
158
|
+
"""
|
159
|
+
Represents an element in a Zarr persistent store.
|
160
|
+
"""
|
161
|
+
|
150
162
|
def encode(self, attrs: Dict) -> List:
|
151
163
|
"""Prepare store elements data for the persistent store.
|
152
164
|
|
@@ -180,6 +192,10 @@ class ZarrStoreElement(StoreElement):
|
|
180
192
|
|
181
193
|
@dataclass
|
182
194
|
class ZarrStoreElementIter(StoreElementIter):
|
195
|
+
"""
|
196
|
+
Represents an element iteration in a Zarr persistent store.
|
197
|
+
"""
|
198
|
+
|
183
199
|
def encode(self, attrs: Dict) -> List:
|
184
200
|
"""Prepare store element iteration data for the persistent store.
|
185
201
|
|
@@ -216,6 +232,10 @@ class ZarrStoreElementIter(StoreElementIter):
|
|
216
232
|
|
217
233
|
@dataclass
|
218
234
|
class ZarrStoreEAR(StoreEAR):
|
235
|
+
"""
|
236
|
+
Represents an element action run in a Zarr persistent store.
|
237
|
+
"""
|
238
|
+
|
219
239
|
def encode(self, attrs: Dict, ts_fmt: str) -> Tuple[List, Tuple[np.datetime64]]:
|
220
240
|
"""Prepare store EAR data for the persistent store.
|
221
241
|
|
@@ -268,6 +288,10 @@ class ZarrStoreEAR(StoreEAR):
|
|
268
288
|
|
269
289
|
@dataclass
|
270
290
|
class ZarrStoreParameter(StoreParameter):
|
291
|
+
"""
|
292
|
+
Represents a parameter in a Zarr persistent store.
|
293
|
+
"""
|
294
|
+
|
271
295
|
_encoders = { # keys are types
|
272
296
|
np.ndarray: _encode_numpy_array,
|
273
297
|
np.ma.core.MaskedArray: _encode_masked_array,
|
@@ -301,6 +325,10 @@ class ZarrStoreParameter(StoreParameter):
|
|
301
325
|
|
302
326
|
|
303
327
|
class ZarrPersistentStore(PersistentStore):
|
328
|
+
"""
|
329
|
+
A persistent store implemented using Zarr.
|
330
|
+
"""
|
331
|
+
|
304
332
|
_name = "zarr"
|
305
333
|
_features = PersistentStoreFeatures(
|
306
334
|
create=True,
|
@@ -346,6 +374,9 @@ class ZarrPersistentStore(PersistentStore):
|
|
346
374
|
yield attrs
|
347
375
|
|
348
376
|
def remove_replaced_dir(self) -> None:
|
377
|
+
"""
|
378
|
+
Remove the directory containing replaced workflow details.
|
379
|
+
"""
|
349
380
|
with self.using_resource("attrs", "update") as md:
|
350
381
|
if "replaced_workflow" in md:
|
351
382
|
self.logger.debug("removing temporarily renamed pre-existing workflow.")
|
@@ -353,6 +384,9 @@ class ZarrPersistentStore(PersistentStore):
|
|
353
384
|
md["replaced_workflow"] = None
|
354
385
|
|
355
386
|
def reinstate_replaced_dir(self) -> None:
|
387
|
+
"""
|
388
|
+
Reinstate the directory containing replaced workflow details.
|
389
|
+
"""
|
356
390
|
with self.using_resource("attrs", "read") as md:
|
357
391
|
if "replaced_workflow" in md:
|
358
392
|
self.logger.debug(
|
@@ -380,6 +414,9 @@ class ZarrPersistentStore(PersistentStore):
|
|
380
414
|
compressor: Optional[Union[str, None]] = "blosc",
|
381
415
|
compressor_kwargs: Optional[Dict[str, Any]] = None,
|
382
416
|
) -> None:
|
417
|
+
"""
|
418
|
+
Write an empty persistent workflow.
|
419
|
+
"""
|
383
420
|
attrs = {
|
384
421
|
"name": name,
|
385
422
|
"ts_fmt": ts_fmt,
|
@@ -796,6 +833,9 @@ class ZarrPersistentStore(PersistentStore):
|
|
796
833
|
|
797
834
|
@property
|
798
835
|
def zarr_store(self) -> zarr.storage.Store:
|
836
|
+
"""
|
837
|
+
The underlying store object.
|
838
|
+
"""
|
799
839
|
if self._zarr_store is None:
|
800
840
|
self._zarr_store = self._get_zarr_store(self.path, self.fs)
|
801
841
|
return self._zarr_store
|
@@ -1139,18 +1179,30 @@ class ZarrPersistentStore(PersistentStore):
|
|
1139
1179
|
return list(range(len(base_arr)))
|
1140
1180
|
|
1141
1181
|
def get_ts_fmt(self):
|
1182
|
+
"""
|
1183
|
+
Get the format for timestamps.
|
1184
|
+
"""
|
1142
1185
|
with self.using_resource("attrs", action="read") as attrs:
|
1143
1186
|
return attrs["ts_fmt"]
|
1144
1187
|
|
1145
1188
|
def get_ts_name_fmt(self):
|
1189
|
+
"""
|
1190
|
+
Get the format for timestamps to use in names.
|
1191
|
+
"""
|
1146
1192
|
with self.using_resource("attrs", action="read") as attrs:
|
1147
1193
|
return attrs["ts_name_fmt"]
|
1148
1194
|
|
1149
1195
|
def get_creation_info(self):
|
1196
|
+
"""
|
1197
|
+
Get information about the creation of the workflow.
|
1198
|
+
"""
|
1150
1199
|
with self.using_resource("attrs", action="read") as attrs:
|
1151
1200
|
return copy.deepcopy(attrs["creation_info"])
|
1152
1201
|
|
1153
1202
|
def get_name(self):
|
1203
|
+
"""
|
1204
|
+
Get the name of the workflow.
|
1205
|
+
"""
|
1154
1206
|
with self.using_resource("attrs", action="read") as attrs:
|
1155
1207
|
return attrs["name"]
|
1156
1208
|
|
@@ -1163,6 +1215,8 @@ class ZarrPersistentStore(PersistentStore):
|
|
1163
1215
|
include_rechunk_backups=False,
|
1164
1216
|
):
|
1165
1217
|
"""
|
1218
|
+
Convert the persistent store to zipped form.
|
1219
|
+
|
1166
1220
|
Parameters
|
1167
1221
|
----------
|
1168
1222
|
path:
|
@@ -1299,6 +1353,9 @@ class ZarrPersistentStore(PersistentStore):
|
|
1299
1353
|
backup: Optional[bool] = True,
|
1300
1354
|
status: Optional[bool] = True,
|
1301
1355
|
):
|
1356
|
+
"""
|
1357
|
+
Rechunk the parameter data to be stored more efficiently.
|
1358
|
+
"""
|
1302
1359
|
arr = self._get_parameter_base_array()
|
1303
1360
|
return self._rechunk_arr(arr, chunk_size, backup, status)
|
1304
1361
|
|
@@ -1308,13 +1365,21 @@ class ZarrPersistentStore(PersistentStore):
|
|
1308
1365
|
backup: Optional[bool] = True,
|
1309
1366
|
status: Optional[bool] = True,
|
1310
1367
|
):
|
1368
|
+
"""
|
1369
|
+
Rechunk the run data to be stored more efficiently.
|
1370
|
+
"""
|
1311
1371
|
arr = self._get_EARs_arr()
|
1312
1372
|
return self._rechunk_arr(arr, chunk_size, backup, status)
|
1313
1373
|
|
1314
1374
|
|
1315
1375
|
class ZarrZipPersistentStore(ZarrPersistentStore):
|
1316
1376
|
"""A store designed mainly as an archive format that can be uploaded to data
|
1317
|
-
repositories such as Zenodo.
|
1377
|
+
repositories such as Zenodo.
|
1378
|
+
|
1379
|
+
Note
|
1380
|
+
----
|
1381
|
+
Archive format persistent stores cannot be updated without being unzipped first.
|
1382
|
+
"""
|
1318
1383
|
|
1319
1384
|
_name = "zip"
|
1320
1385
|
_features = PersistentStoreFeatures(
|
@@ -1333,6 +1398,8 @@ class ZarrZipPersistentStore(ZarrPersistentStore):
|
|
1333
1398
|
|
1334
1399
|
def unzip(self, path=".", log=None):
|
1335
1400
|
"""
|
1401
|
+
Expand the persistent store.
|
1402
|
+
|
1336
1403
|
Parameters
|
1337
1404
|
----------
|
1338
1405
|
path:
|
hpcflow/sdk/runtime.py
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Information about the Python runtime.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from importlib import import_module
|
2
6
|
import logging
|
3
7
|
import os
|
@@ -15,17 +19,16 @@ class RunTimeInfo:
|
|
15
19
|
"""Get useful run-time information, including the executable name used to
|
16
20
|
invoke the CLI, in the case a PyInstaller-built executable was used.
|
17
21
|
|
18
|
-
|
22
|
+
Parameters
|
19
23
|
----------
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
be equal to the virtual environment directory.
|
24
|
+
name:
|
25
|
+
Application name.
|
26
|
+
package_name:
|
27
|
+
Application package name.
|
28
|
+
version:
|
29
|
+
Application version.
|
30
|
+
logger:
|
31
|
+
Where to write logging versions.
|
29
32
|
"""
|
30
33
|
|
31
34
|
def __init__(self, name, package_name, version, logger):
|
@@ -34,22 +37,35 @@ class RunTimeInfo:
|
|
34
37
|
sys._MEIPASS if is_frozen else os.path.dirname(os.path.abspath(__file__))
|
35
38
|
)
|
36
39
|
|
40
|
+
#: Application name.
|
37
41
|
self.name = name.split(".")[0] # if name is given as __name__ # TODO: what?
|
42
|
+
#: Application package name.
|
38
43
|
self.package_name = package_name
|
44
|
+
#: Application version.
|
39
45
|
self.version = version
|
46
|
+
#: Whether this is a frozen application.
|
40
47
|
self.is_frozen = is_frozen
|
48
|
+
#: Working directory.
|
41
49
|
self.working_dir = os.getcwd()
|
50
|
+
#: Where to write log messages.
|
42
51
|
self.logger = logger
|
52
|
+
#: Host that this is running on.
|
43
53
|
self.hostname = socket.gethostname()
|
44
54
|
|
55
|
+
#: Whether this application is inside iPython.
|
45
56
|
self.in_ipython = False
|
57
|
+
#: Whether this application is being used interactively.
|
46
58
|
self.is_interactive = False
|
59
|
+
#: Whether this application is being used in test mode.
|
47
60
|
self.in_pytest = False # set in `conftest.py`
|
61
|
+
#: Whether this application is being run from the CLI.
|
48
62
|
self.from_CLI = False # set in CLI
|
49
63
|
|
50
64
|
if self.is_frozen:
|
65
|
+
#: The bundle directory, if frozen.
|
51
66
|
self.bundle_dir = Path(bundle_dir)
|
52
67
|
else:
|
68
|
+
#: The path to Python itself.
|
53
69
|
self.python_executable_path = Path(sys.executable)
|
54
70
|
|
55
71
|
try:
|
@@ -61,16 +77,30 @@ class RunTimeInfo:
|
|
61
77
|
if hasattr(sys, "ps1"):
|
62
78
|
self.is_interactive = True
|
63
79
|
|
80
|
+
#: The Python version.
|
64
81
|
self.python_version = platform.python_version()
|
82
|
+
#: Whether the application is in a virtual environment.
|
65
83
|
self.is_venv = hasattr(sys, "real_prefix") or sys.base_prefix != sys.prefix
|
84
|
+
#: Whether the application is in a Conda virtual environment.
|
66
85
|
self.is_conda_venv = "CONDA_PREFIX" in os.environ
|
67
86
|
|
87
|
+
#: From `sys.prefix`. If running in a virtual environment, this will point to the
|
88
|
+
#: environment directory. If not running in a virtual environment, this will
|
89
|
+
#: point to the Python installation root.
|
68
90
|
self.sys_prefix = getattr(sys, "prefix", None)
|
91
|
+
#: From `sys.base_prefix`. This will be equal to `sys_prefix` (`sys.prefix`) if
|
92
|
+
#: not running within a virtual environment. However, if running within a virtual
|
93
|
+
#: environment, this will be the Python installation directory, and `sys_prefix`
|
94
|
+
#: will be equal to the virtual environment directory.
|
69
95
|
self.sys_base_prefix = getattr(sys, "base_prefix", None)
|
96
|
+
#: The old base prefix, from `sys.real_prefix`. Compatibility version of
|
97
|
+
#: :py:attr:`sys_base_prefix`.
|
70
98
|
self.sys_real_prefix = getattr(sys, "real_prefix", None)
|
99
|
+
#: The Conda prefix, if defined.
|
71
100
|
self.conda_prefix = os.environ.get("CONDA_PREFIX")
|
72
101
|
|
73
102
|
try:
|
103
|
+
#: The virtual environment path.
|
74
104
|
self.venv_path = self._set_venv_path()
|
75
105
|
except ValueError:
|
76
106
|
self.venv_path = None
|
@@ -97,6 +127,9 @@ class RunTimeInfo:
|
|
97
127
|
# warnings.warn(msg)
|
98
128
|
|
99
129
|
def to_dict(self):
|
130
|
+
"""
|
131
|
+
Serialize this class as a dictionary.
|
132
|
+
"""
|
100
133
|
out = {
|
101
134
|
"name": self.name,
|
102
135
|
"package_name": self.package_name,
|
@@ -157,12 +190,21 @@ class RunTimeInfo:
|
|
157
190
|
return out
|
158
191
|
|
159
192
|
def get_activate_env_command(self):
|
193
|
+
"""
|
194
|
+
Get the command to activate the virtual environment.
|
195
|
+
"""
|
160
196
|
pass
|
161
197
|
|
162
198
|
def get_deactivate_env_command(self):
|
199
|
+
"""
|
200
|
+
Get the command to deactivate the virtual environment.
|
201
|
+
"""
|
163
202
|
pass
|
164
203
|
|
165
204
|
def show(self):
|
205
|
+
"""
|
206
|
+
Display the information known by this class as a human-readable table.
|
207
|
+
"""
|
166
208
|
tab = Table(show_header=False, box=None)
|
167
209
|
tab.add_column()
|
168
210
|
tab.add_column()
|