partis-basis 0.0.4__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,331 @@
1
+ from __future__ import annotations
2
+ import sys
3
+ import os
4
+ from pathlib import Path
5
+ import re
6
+ import shutil
7
+ import json
8
+ import argparse
9
+ import shlex
10
+ from subprocess import check_output, check_call
11
+
12
+ _PY = f"{'.'.join(str(v) for v in sys.version_info[:2])}"
13
+ APPS = ['bash', 'fish', 'manage']
14
+
15
+ PASS_ENV = frozenset([
16
+ 'USER',
17
+ 'LOGNAME',
18
+ 'LANG',
19
+ 'LANGUAGE',
20
+ 'LC_CTYPE',
21
+ 'TERM',
22
+ 'COLORFGBG',
23
+ 'COLORTERM',
24
+ 'LSCOLORS',
25
+ 'LS_COLORS',
26
+ 'DISPLAY',
27
+ 'NO_COLOR',
28
+ 'PY_COLORS',
29
+ 'CLICOLOR',
30
+ 'CLICOLOR_FORCE',
31
+ 'FORCE_COLOR',
32
+ 'NOHM_MIN_LOG_LEVEL',
33
+ 'BASIS_DEBUG',
34
+ 'XLA_PYTHON_CLIENT_ALLOCATOR',
35
+ 'XLA_PYTHON_CLIENT_PREALLOCATE',
36
+ 'TF_CPP_MIN_LOG_LEVEL',
37
+ 'TF_CPP_MAX_VLOG_LEVEL',
38
+ 'TF_CPP_VMODULE',
39
+ 'PJRT_NPROC',
40
+ 'XLA_FLAGS'])
41
+
42
+ MPI_ENV_REC = re.compile('|'.join([
43
+ # OpenMPI:
44
+ '(OMPI_*\w+)',
45
+ '(PMIX_*\w+)',
46
+ '(OPAL_*\w+)',
47
+ '(ORTE_*\w+)',
48
+
49
+ # MPICH:
50
+ '(MPIR_*\w+)',
51
+ '(PMI_*\w+)',
52
+ '(PMIX_*\w+)',
53
+ '(HYDRA_*\w+)',
54
+
55
+ # Intel MPI:
56
+ '(I_MPI_*\w+)',
57
+ '(MPIR_*\w+)',
58
+ '(PMI_*\w+)',
59
+ '(PMIX_*\w+)',
60
+ '(HYDRA_*\w+)',
61
+
62
+ # MVAPICH2:
63
+ '(MV2_*\w+)',
64
+ '(MPIR_*\w+)',
65
+ '(PMI_*\w+)',
66
+ '(PMIX_*\w+)',
67
+ '(HYDRA_*\w+)']))
68
+
69
+ DEFAULT_IMAGE = "library://kcdodd/basis/ubuntu-22.04-basis:0.0.4"
70
+
71
+
72
+ #===============================================================================
73
+ def _basis(*,
74
+ image: str|None = None,
75
+ basis: Path|None = None,
76
+ create: bool = True) -> tuple[str, Path, Path]:
77
+
78
+ if basis is None:
79
+ venv: str|Path = os.environ.get('VIRTUAL_ENV', '')
80
+
81
+ if venv:
82
+ venv = Path(venv).resolve()
83
+
84
+ if not (venv and venv.exists()):
85
+ raise FileNotFoundError(f"No active virtual environment: {venv}")
86
+
87
+ basis = venv/'.basis'
88
+
89
+ default = basis/'mods'/'environment'/'.version'
90
+ created = False
91
+
92
+ if create and not basis.exists():
93
+ basis.mkdir(parents=True)
94
+ created = True
95
+
96
+ basis_config_file = basis/'.config'/'basis'
97
+
98
+ if basis_config_file.exists():
99
+ basis_config = json.loads(basis_config_file.read_text())
100
+ _image = basis_config['image']
101
+
102
+ if image is None:
103
+ # image originally used to create basis directory
104
+ image = _image
105
+
106
+ elif image != _image:
107
+ print("Warning: Basis directory originally created using different image, behavior may be undefined.")
108
+ print(f" original imag: {_image!r}")
109
+
110
+ else:
111
+ basis_config_file.parent.mkdir(exist_ok=True, parents=True)
112
+
113
+ if created:
114
+ if image is None:
115
+ image = DEFAULT_IMAGE
116
+
117
+ basis_config_file.write_text(json.dumps({
118
+ 'image': str(image)}))
119
+
120
+ else:
121
+ # fixup environment created without config file
122
+ # NOTE: hardcoded assumption that all older versions created with this image
123
+ basis_config_file.write_text(json.dumps({
124
+ 'image': "library://kcdodd/basis/ubuntu-22.04-basis:0.0.2"}))
125
+
126
+ assert isinstance(image, str)
127
+
128
+ return image, basis, default
129
+
130
+ #===============================================================================
131
+ def init(image, basis, cwd, python):
132
+ name = 'init'
133
+
134
+ singularity = shutil.which('singularity')
135
+
136
+ if not singularity:
137
+ print("Error: Program 'singularity' not found")
138
+ return 1
139
+
140
+ image, basis, default = _basis(basis=basis, image=image)
141
+ env_dir = basis/'envs'/name
142
+
143
+ if env_dir.is_dir() and any(env_dir.iterdir()):
144
+ print(f"Cannot initialize non-empty directory: {env_dir}")
145
+ return 1
146
+
147
+ pwd = str(cwd)
148
+ env = {
149
+ k:v
150
+ for k,v in os.environ.items()
151
+ if k in PASS_ENV or MPI_ENV_REC.fullmatch(k)}
152
+
153
+ def _run(*cmd):
154
+ check_call([
155
+ singularity,
156
+ 'run',
157
+ '--app', 'manage',
158
+ '--no-home',
159
+ '--pwd', str(pwd),
160
+ '--bind', f"{basis}:/basis",
161
+ str(image),
162
+ *cmd],
163
+ env=env)
164
+
165
+ _run('create', name, str(python))
166
+
167
+ default.write_text(f'#%Module\nset ModulesVersion {name}')
168
+
169
+ return 0
170
+
171
+ #===============================================================================
172
+ def run(image, basis, app, work, cwd, cmd):
173
+ singularity = shutil.which('singularity')
174
+ nvccli = shutil.which('nvidia-container-cli')
175
+
176
+ if not singularity:
177
+ print("Error: Program 'singularity' not found")
178
+ return 1
179
+
180
+ if nvccli:
181
+ runhelp = check_output([singularity, 'run', '--help']).decode('utf-8')
182
+
183
+ if '--nvccli' not in runhelp:
184
+ print("Warning: singularity version must be >= 3.9 to utilize nvidia-container-toolkit found on host")
185
+ nvccli = None
186
+
187
+ work = work.resolve()
188
+
189
+ image, basis, default = _basis(basis=basis, image=image)
190
+
191
+ env = {
192
+ k:v
193
+ for k,v in os.environ.items()
194
+ if k in PASS_ENV or MPI_ENV_REC.fullmatch(k)}
195
+
196
+ pwd = str(cwd)
197
+
198
+ extra_args = []
199
+
200
+ if nvccli:
201
+ extra_args.extend(['--nv', '--nvccli'])
202
+
203
+ cmd_singularity = [
204
+ singularity,
205
+ 'run',
206
+ '--app',
207
+ app,
208
+ *extra_args,
209
+ '--no-home',
210
+ '--pwd', str(pwd),
211
+ '--bind', f"{basis}:/basis",
212
+ '--bind', f"{work}:/work",
213
+ '--bind', f"{pwd}:{pwd}",
214
+ str(image),
215
+ *cmd]
216
+
217
+ print(f"image: {image}")
218
+ print(f" app: {app}")
219
+ print(f"basis: {basis}")
220
+ print(f" work: {work}")
221
+ print(f" cwd: {cwd}")
222
+ print(f" exec: {shlex.join(cmd)}")
223
+ print("Entering container...")
224
+
225
+ # flush stdout before replacing process
226
+ sys.stdout.flush()
227
+ sys.stderr.flush()
228
+ os.execve(cmd_singularity[0], cmd_singularity, env)
229
+
230
+ #===============================================================================
231
+ def _cuda_info(nvccli):
232
+ info = check_output([nvccli, 'info', '--csv']).decode('utf-8')
233
+ lines = info.splitlines()
234
+ nvrm_version, cuda_version = lines[1].split(',')
235
+
236
+ nvrm_version = tuple(int(v) for v in nvrm_version.split('.'))
237
+ cuda_version = tuple(int(v) for v in cuda_version.split('.'))
238
+
239
+ return nvrm_version, cuda_version
240
+
241
+ #===============================================================================
242
+ def main():
243
+
244
+ parser = argparse.ArgumentParser(
245
+ description="partis-basis runner for singularity container based environments")
246
+
247
+ parser.add_argument(
248
+ '-i', '--image',
249
+ help="",
250
+ type=str,
251
+ default=None)
252
+
253
+ parser.add_argument(
254
+ '-a', '--app',
255
+ help=f"{', '.join(APPS)}",
256
+ choices=APPS,
257
+ default='bash')
258
+
259
+ parser.add_argument(
260
+ '-b', '--basis',
261
+ help="",
262
+ type=Path,
263
+ default=None)
264
+
265
+ parser.add_argument(
266
+ '--init',
267
+ dest='init',
268
+ action='store_true',
269
+ help="Create and initialize basis directory if it doesn't exist")
270
+
271
+ parser.add_argument(
272
+ '--python',
273
+ type=str,
274
+ default=_PY)
275
+
276
+ parser.add_argument(
277
+ '-w', '--work',
278
+ help="",
279
+ type=Path,
280
+ default=Path('/'))
281
+
282
+ parser.add_argument(
283
+ '-c', '--cwd',
284
+ help="",
285
+ type=Path,
286
+ default=Path.cwd())
287
+
288
+ # cmd (positional, collects all remaining)
289
+ # Using REMAINDER so everything after this argument is captured "as-is."
290
+ parser.add_argument(
291
+ 'cmd',
292
+ nargs='*',
293
+ help="Command and its arguments")
294
+
295
+ args = parser.parse_args()
296
+
297
+ if args.init:
298
+ image = args.image
299
+
300
+ if image is None:
301
+ image = DEFAULT_IMAGE
302
+
303
+ if init(
304
+ image = image,
305
+ basis = args.basis,
306
+ cwd = args.cwd,
307
+ python = args.python):
308
+
309
+ return 1
310
+
311
+ return run(
312
+ image = args.image,
313
+ basis = args.basis,
314
+ app = args.app,
315
+ work = args.work,
316
+ cwd = args.cwd,
317
+ cmd = args.cmd)
318
+
319
+ #===============================================================================
320
+ def find_basis(start):
321
+ tmp = start
322
+ while tmp.name:
323
+ if (tmp/'.basis').exists():
324
+ return tmp/'.basis'
325
+
326
+ tmp = tmp.parent
327
+
328
+ return None
329
+
330
+ #===============================================================================
331
+ exit(main())
@@ -0,0 +1,50 @@
1
+ #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
2
+ partis-basis
3
+
4
+ Copyright (c) 2023, Nanohmics, Inc.
5
+ All rights reserved.
6
+
7
+ #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
8
+
9
+ Software Notice
10
+
11
+ Nanohmics, Inc. shall not be liable to any party for direct, indirect,
12
+ special, incidental, or consequential damages arising out of the use of
13
+ this software and/or its documentation. Nanohmics, Inc. specifically
14
+ disclaims any warranties, including, but not limited to, the implied
15
+ warranties of merchantability and fitness for a particular purpose. The
16
+ software and accompanying documentation, if any, provided hereunder is
17
+ provided "AS IS". Nanohmics, Inc. has no obligation to provide maintenance,
18
+ support, updates, enhancements, or modifications.
19
+
20
+ #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
21
+
22
+ Redistribution and use in source and binary forms, with or without
23
+ modification, are permitted provided that the following conditions are
24
+ met:
25
+
26
+ * Redistributions of source code must retain the above copyright
27
+ notice, this list of conditions and the following disclaimer.
28
+
29
+ * Redistributions in binary form must reproduce the above
30
+ copyright notice, this list of conditions and the following
31
+ disclaimer in the documentation and/or other materials provided
32
+ with the distribution.
33
+
34
+ * Neither the name of the copyright holder nor the names of any
35
+ contributors may be used to endorse or promote products derived
36
+ from this software without specific prior written permission.
37
+
38
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
41
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
42
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
44
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
+
50
+ #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.1
2
+ Name: partis-basis
3
+ Version: 0.0.4
4
+ Requires-Python: >=3.7.2
5
+ Author: Nanohmics Inc
6
+ Summary:
7
+ License-File: LICENSE.txt
8
+ Description-Content-Type: text/markdown
9
+
10
+ CLI utility for using Singularity container as virtual environment (requires singularity to already be installed).
11
+
12
+ ```
13
+ $ basis --help
14
+ Usage: basis [OPTIONS] [CMD]...
15
+
16
+ Options:
17
+ -i, --image TEXT
18
+ -a, --app TEXT {bash, fish, jupyter, code}
19
+ -b, --basis PATH
20
+ -w, --work PATH
21
+ -c, --cwd PATH
22
+ --help Show this message and exit.
23
+ ```
24
+
25
+ The first time it is run it will download a pre-build Singularity image.
26
+ You must also tell it where to create the basis directory on the first run, which
27
+ will become a virtual home inside the container.
28
+
29
+ ```bash
30
+ $ basis -b ./.basis
31
+ image: library://kcdodd/basis/ubuntu-22.04-basis:0.0.1
32
+ app: fish
33
+ basis: /path/to/cwd/.basis
34
+ work: /
35
+ cwd: /path/to/cwd
36
+ cmd:
37
+ Entering container...
38
+ INFO: Using cached image
39
+ Loading python/3.11.0/gcc-system
40
+ Loading requirement: gcc/system/default
41
+ Creating environment: /basis/envs/default
42
+ Creating modulefile /basis/mods/environment/default
43
+ Loading environment: /basis/envs/default
44
+ Saved modules to /basis/envs/default/modules
45
+ gcc/system/default python/3.11.0/gcc-system environment/default
46
+ Loading environment: /basis/envs/default
47
+ Welcome to fish, the friendly interactive shell
48
+ Type help for instructions on how to use fish
49
+ >? ok | 10:43:13 | 0ms
50
+ >[S] user@hostname | (default) | /work/path/to/cwd
51
+ >$ echo $HOME
52
+ /basis
53
+ >? ok | 10:55:36 | 0ms
54
+ >[S] user@hostname | (default) | /work/path/to/cwd
55
+ >$ which python
56
+ /basis/envs/default/bin/python
57
+ >? ok | 10:59:06 | 6ms
58
+ >[S] user@hostname | (default) | /work/path/to/cwd
59
+ >$ exit
60
+ ... Exited container
61
+ ```
62
+
63
+ Safely delete basis directory to start over.
64
+
65
+ ```
66
+ rm -r ./.basis
@@ -0,0 +1,7 @@
1
+ partis_basis-0.0.4.data/purelib/partis/basis/__main__.py,sha256=rP-Am6T6X5Lj91QUugS2qAd_lgXqjHFw5r870WSA1s4,7529
2
+ partis_basis-0.0.4.dist-info/METADATA,sha256=f31zNX1bAjZfSoFuVdECJxt-rW8Sd4GkBIaK_uvQbmw,1786
3
+ partis_basis-0.0.4.dist-info/LICENSE.txt,sha256=qf-NluVHxh7dSBPryN3syTEA_225NVd9r158uA_mR7c,2416
4
+ partis_basis-0.0.4.dist-info/top_level.txt,sha256=T72LF-bUK7nMDf1GQgbzvgZ2YrJElwwRd9h7HwVQBIQ,6
5
+ partis_basis-0.0.4.dist-info/entry_points.txt,sha256=RvrdAbgkHvUSP_dSAm8dumDtAfIHV2nCbq6bRpm7BZY,54
6
+ partis_basis-0.0.4.dist-info/WHEEL,sha256=OmYjmrwnZ_TEyF-NYtpim7LB9zEz0oAjizteYuA5HsA,133
7
+ partis_basis-0.0.4.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: partis.pyproj.dist_file.dist_binary.dist_binary_wheel
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+ Build:
6
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ basis = partis.basis.__main__:main
3
+
@@ -0,0 +1 @@
1
+ partis