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.
- partis_basis-0.0.4.data/purelib/partis/basis/__main__.py +331 -0
- partis_basis-0.0.4.dist-info/LICENSE.txt +50 -0
- partis_basis-0.0.4.dist-info/METADATA +66 -0
- partis_basis-0.0.4.dist-info/RECORD +7 -0
- partis_basis-0.0.4.dist-info/WHEEL +6 -0
- partis_basis-0.0.4.dist-info/entry_points.txt +3 -0
- partis_basis-0.0.4.dist-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
1
|
+
partis
|