nkululeko 0.86.2__py3-none-any.whl → 0.86.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.
- nkululeko/constants.py +1 -1
- nkululeko/demo.py +52 -2
- nkululeko/experiment.py +2 -1
- nkululeko/feat_extract/feats_import.py +1 -0
- nkululeko/models/model_tuned.py +27 -11
- nkululeko/reporting/reporter.py +1 -1
- nkululeko/utils/util.py +6 -0
- {nkululeko-0.86.2.dist-info → nkululeko-0.86.4.dist-info}/METADATA +11 -1
- {nkululeko-0.86.2.dist-info → nkululeko-0.86.4.dist-info}/RECORD +12 -12
- {nkululeko-0.86.2.dist-info → nkululeko-0.86.4.dist-info}/LICENSE +0 -0
- {nkululeko-0.86.2.dist-info → nkululeko-0.86.4.dist-info}/WHEEL +0 -0
- {nkululeko-0.86.2.dist-info → nkululeko-0.86.4.dist-info}/top_level.txt +0 -0
nkululeko/constants.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
VERSION="0.86.
|
1
|
+
VERSION="0.86.4"
|
2
2
|
SAMPLING_RATE = 16000
|
nkululeko/demo.py
CHANGED
@@ -20,16 +20,20 @@ Options: \n
|
|
20
20
|
import argparse
|
21
21
|
import configparser
|
22
22
|
import os
|
23
|
+
import pandas as pd
|
23
24
|
|
24
25
|
from nkululeko.constants import VERSION
|
25
26
|
from nkululeko.experiment import Experiment
|
26
27
|
import nkululeko.glob_conf as glob_conf
|
27
28
|
from nkululeko.utils.util import Util
|
29
|
+
from transformers import pipeline
|
28
30
|
|
29
31
|
|
30
32
|
def main(src_dir):
|
31
|
-
parser = argparse.ArgumentParser(
|
32
|
-
|
33
|
+
parser = argparse.ArgumentParser(
|
34
|
+
description="Call the nkululeko DEMO framework.")
|
35
|
+
parser.add_argument("--config", default="exp.ini",
|
36
|
+
help="The base configuration")
|
33
37
|
parser.add_argument(
|
34
38
|
"--file", help="A file that should be processed (16kHz mono wav)"
|
35
39
|
)
|
@@ -79,6 +83,52 @@ def main(src_dir):
|
|
79
83
|
f" {VERSION}"
|
80
84
|
)
|
81
85
|
|
86
|
+
def print_pipe(files, outfile):
|
87
|
+
"""
|
88
|
+
Prints the pipeline output for a list of files, and optionally writes the results to an output file.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
files (list): A list of file paths to process through the pipeline.
|
92
|
+
outfile (str, optional): The path to an output file to write the pipeline results to.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
None
|
96
|
+
"""
|
97
|
+
results = []
|
98
|
+
for file in files:
|
99
|
+
result = pipe(file, top_k=1)
|
100
|
+
if result[0]["score"] != result[0]["score"]: # Check for NaN
|
101
|
+
print(f"ERROR: NaN value in pipeline output for file: {file}")
|
102
|
+
else:
|
103
|
+
results.append(f"{file}, {result[0]['label']}")
|
104
|
+
print("\n".join(results))
|
105
|
+
|
106
|
+
if outfile is not None:
|
107
|
+
with open(outfile, "w") as f:
|
108
|
+
f.write("\n".join(results))
|
109
|
+
|
110
|
+
if util.get_model_type() == "finetune":
|
111
|
+
model_path = os.path.join(
|
112
|
+
util.get_exp_dir(), "models", "run_0", "torch")
|
113
|
+
pipe = pipeline("audio-classification", model=model_path)
|
114
|
+
if args.file is not None:
|
115
|
+
print_pipe([args.file], args.outfile)
|
116
|
+
elif args.list is not None:
|
117
|
+
# read audio files from list
|
118
|
+
print(f"Reading files from {args.list}")
|
119
|
+
list_file = pd.read_csv(args.list, header="infer")
|
120
|
+
files = list_file.iloc[:, 0].tolist()
|
121
|
+
print_pipe(files, args.outfile)
|
122
|
+
elif args.folder is not None:
|
123
|
+
# read audio files from folder
|
124
|
+
from nkululeko.utils.files import find_files
|
125
|
+
|
126
|
+
files = find_files(args.folder, relative=True, ext=["wav", "mp3"])
|
127
|
+
print_pipe(files, args.outfile)
|
128
|
+
else:
|
129
|
+
print("ERROR: input mic currently is not supported for finetuning")
|
130
|
+
return
|
131
|
+
|
82
132
|
# load the experiment
|
83
133
|
expr.load(f"{util.get_save_name()}")
|
84
134
|
if args.folder is not None:
|
nkululeko/experiment.py
CHANGED
@@ -115,7 +115,8 @@ class Experiment:
|
|
115
115
|
self.util.debug(f"Target labels (from config): {labels}")
|
116
116
|
else:
|
117
117
|
self.labels = auto_labels
|
118
|
-
|
118
|
+
# print autolabel no matter it is specified or not
|
119
|
+
self.util.debug(f"Target labels (from database): {auto_labels}")
|
119
120
|
glob_conf.set_labels(self.labels)
|
120
121
|
self.util.debug(f"loaded databases {dbs}")
|
121
122
|
|
@@ -35,6 +35,7 @@ class ImportSet(Featureset):
|
|
35
35
|
if not os.path.isfile(feat_import_file):
|
36
36
|
self.util.error(f"no import file: {feat_import_file}")
|
37
37
|
df = audformat.utils.read_csv(feat_import_file)
|
38
|
+
df = self.util.make_segmented_index(df)
|
38
39
|
df = df[df.index.isin(self.data_df.index)]
|
39
40
|
feat_df = pd.concat([feat_df, df])
|
40
41
|
if feat_df.shape[0] == 0:
|
nkululeko/models/model_tuned.py
CHANGED
@@ -54,19 +54,23 @@ class TunedModel(BaseModel):
|
|
54
54
|
self.learning_rate = float(
|
55
55
|
self.util.config_val("MODEL", "learning_rate", "0.0001")
|
56
56
|
)
|
57
|
-
self.max_duration = float(
|
57
|
+
self.max_duration = float(
|
58
|
+
self.util.config_val("MODEL", "max_duration", "8.0"))
|
58
59
|
self.df_train, self.df_test = df_train, df_test
|
59
60
|
self.epoch_num = int(self.util.config_val("EXP", "epochs", 1))
|
61
|
+
self.util.debug(f"num of epochs: {self.epoch_num}")
|
60
62
|
drop = self.util.config_val("MODEL", "drop", False)
|
61
63
|
self.drop = 0.1
|
62
64
|
if drop:
|
63
65
|
self.drop = float(drop)
|
64
66
|
self.util.debug(f"init: training with dropout: {self.drop}")
|
67
|
+
self.push = eval(self.util.config_val("MODEL", "push_to_hub", "False"))
|
65
68
|
self._init_model()
|
66
69
|
|
67
70
|
def _init_model(self):
|
68
71
|
model_path = "facebook/wav2vec2-large-robust-ft-swbd-300h"
|
69
|
-
pretrained_model = self.util.config_val(
|
72
|
+
pretrained_model = self.util.config_val(
|
73
|
+
"MODEL", "pretrained_model", model_path)
|
70
74
|
self.num_layers = None
|
71
75
|
self.sampling_rate = 16000
|
72
76
|
self.max_duration_sec = self.max_duration
|
@@ -131,6 +135,10 @@ class TunedModel(BaseModel):
|
|
131
135
|
tokenizer = transformers.Wav2Vec2CTCTokenizer("./vocab.json")
|
132
136
|
tokenizer.save_pretrained(".")
|
133
137
|
|
138
|
+
# uoload tokenizer to hub if true
|
139
|
+
if self.push:
|
140
|
+
tokenizer.push_to_hub(self.util.get_name())
|
141
|
+
|
134
142
|
feature_extractor = transformers.Wav2Vec2FeatureExtractor(
|
135
143
|
feature_size=1,
|
136
144
|
sampling_rate=16000,
|
@@ -260,7 +268,8 @@ class TunedModel(BaseModel):
|
|
260
268
|
else:
|
261
269
|
criterion = torch.nn.CrossEntropyLoss()
|
262
270
|
else:
|
263
|
-
self.util.error(
|
271
|
+
self.util.error(
|
272
|
+
f"criterion {criterion} not supported for classifier")
|
264
273
|
else:
|
265
274
|
self.criterion = self.util.config_val("MODEL", "loss", "ccc")
|
266
275
|
if criterion == "1-ccc":
|
@@ -270,10 +279,11 @@ class TunedModel(BaseModel):
|
|
270
279
|
elif criterion == "mae":
|
271
280
|
criterion = torch.nn.L1Loss()
|
272
281
|
else:
|
273
|
-
self.util.error(
|
282
|
+
self.util.error(
|
283
|
+
f"criterion {criterion} not supported for regressor")
|
274
284
|
|
275
285
|
# set push_to_hub value, default false
|
276
|
-
push = eval(self.util.config_val("MODEL", "push_to_hub", "False"))
|
286
|
+
# push = eval(self.util.config_val("MODEL", "push_to_hub", "False"))
|
277
287
|
|
278
288
|
class Trainer(transformers.Trainer):
|
279
289
|
def compute_loss(
|
@@ -309,7 +319,8 @@ class TunedModel(BaseModel):
|
|
309
319
|
elif metrics_for_best_model == "MAE":
|
310
320
|
greater_is_better = False
|
311
321
|
else:
|
312
|
-
self.util.error(
|
322
|
+
self.util.error(
|
323
|
+
f"unknown metric/measure: {metrics_for_best_model}")
|
313
324
|
|
314
325
|
training_args = transformers.TrainingArguments(
|
315
326
|
output_dir=model_root,
|
@@ -319,7 +330,8 @@ class TunedModel(BaseModel):
|
|
319
330
|
gradient_accumulation_steps=self.accumulation_steps,
|
320
331
|
evaluation_strategy="steps",
|
321
332
|
num_train_epochs=self.epoch_num,
|
322
|
-
fp16=self.device
|
333
|
+
fp16=self.device != "cpu",
|
334
|
+
use_cpu=self.device == "cpu",
|
323
335
|
save_steps=num_steps,
|
324
336
|
eval_steps=num_steps,
|
325
337
|
logging_steps=num_steps,
|
@@ -331,8 +343,9 @@ class TunedModel(BaseModel):
|
|
331
343
|
load_best_model_at_end=True,
|
332
344
|
remove_unused_columns=False,
|
333
345
|
report_to="none",
|
334
|
-
push_to_hub=push,
|
346
|
+
push_to_hub=self.push,
|
335
347
|
hub_model_id=f"{self.util.get_name()}",
|
348
|
+
overwrite_output_dir=True,
|
336
349
|
)
|
337
350
|
|
338
351
|
trainer = Trainer(
|
@@ -440,7 +453,7 @@ class TunedModel(BaseModel):
|
|
440
453
|
self.clf = pickle.load(handle)
|
441
454
|
|
442
455
|
|
443
|
-
@dataclasses.dataclass
|
456
|
+
@ dataclasses.dataclass
|
444
457
|
class ModelOutput(transformers.file_utils.ModelOutput):
|
445
458
|
|
446
459
|
logits: torch.FloatTensor = None
|
@@ -448,7 +461,7 @@ class ModelOutput(transformers.file_utils.ModelOutput):
|
|
448
461
|
cnn_features: torch.FloatTensor = None
|
449
462
|
|
450
463
|
|
451
|
-
@dataclasses.dataclass
|
464
|
+
@ dataclasses.dataclass
|
452
465
|
class ModelOutputReg(transformers.file_utils.ModelOutput):
|
453
466
|
|
454
467
|
logits: torch.FloatTensor
|
@@ -517,7 +530,10 @@ class Model(Wav2Vec2PreTrainedModel):
|
|
517
530
|
)
|
518
531
|
outputs = torch.sum(hidden_states, dim=1)
|
519
532
|
attention_sum = torch.sum(attention_mask, dim=1)
|
520
|
-
|
533
|
+
|
534
|
+
epsilon = 1e-6 # to avoid division by zero and numerical instability
|
535
|
+
outputs = outputs / (torch.reshape(attention_sum, (-1, 1)) +
|
536
|
+
epsilon)
|
521
537
|
|
522
538
|
return outputs
|
523
539
|
|
nkululeko/reporting/reporter.py
CHANGED
@@ -315,7 +315,7 @@ class Reporter:
|
|
315
315
|
plt.savefig(plot_path)
|
316
316
|
self.util.debug(f"plotted epoch progression to {plot_path}")
|
317
317
|
plt.close(fig)
|
318
|
-
fig.clear()
|
318
|
+
# fig.clear()
|
319
319
|
|
320
320
|
def plot_epoch_progression(self, reports, out_name):
|
321
321
|
fig_dir = self.util.get_path("fig_dir")
|
nkululeko/utils/util.py
CHANGED
@@ -35,6 +35,7 @@ class Util:
|
|
35
35
|
if has_config:
|
36
36
|
try:
|
37
37
|
import nkululeko.glob_conf as glob_conf
|
38
|
+
|
38
39
|
self.config = glob_conf.config
|
39
40
|
self.got_data_roots = self.config_val(
|
40
41
|
"DATA", "root_folders", False)
|
@@ -160,6 +161,8 @@ class Util:
|
|
160
161
|
if len(df) == 0:
|
161
162
|
return df
|
162
163
|
if not isinstance(df.index, pd.MultiIndex):
|
164
|
+
self.debug(
|
165
|
+
"converting to segmented index, this might take a while...")
|
163
166
|
df.index = audformat.utils.to_segmented_index(
|
164
167
|
df.index, allow_nat=False)
|
165
168
|
return df
|
@@ -196,6 +199,9 @@ class Util:
|
|
196
199
|
return_string = return_string + "_" + mt
|
197
200
|
return return_string.replace("__", "_")
|
198
201
|
|
202
|
+
def get_model_type(self):
|
203
|
+
return self.config["MODEL"]["type"]
|
204
|
+
|
199
205
|
def get_model_description(self):
|
200
206
|
mt = ""
|
201
207
|
mt = f'{self.config["MODEL"]["type"]}'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nkululeko
|
3
|
-
Version: 0.86.
|
3
|
+
Version: 0.86.4
|
4
4
|
Summary: Machine learning audio prediction experiments based on templates
|
5
5
|
Home-page: https://github.com/felixbur/nkululeko
|
6
6
|
Author: Felix Burkhardt
|
@@ -237,6 +237,7 @@ There's my [blog](http://blog.syntheticspeech.de/?s=nkululeko) with tutorials:
|
|
237
237
|
* [Run multiple experiments in one go](http://blog.syntheticspeech.de/2022/03/28/how-to-run-multiple-experiments-in-one-go-with-nkululeko/)
|
238
238
|
* [Compare several MLP layer layouts with each other](http://blog.syntheticspeech.de/2022/04/11/how-to-compare-several-mlp-layer-layouts-with-each-other/)
|
239
239
|
* [Import features from outside the software](http://blog.syntheticspeech.de/2022/10/18/how-to-import-features-from-outside-the-nkululeko-software/)
|
240
|
+
* [Export acoustic features](http://blog.syntheticspeech.de/2024/05/30/nkululeko-export-acoustic-features/)
|
240
241
|
* [Explore feature importance](http://blog.syntheticspeech.de/2023/02/20/nkululeko-show-feature-importance/)
|
241
242
|
* [Plot distributions for feature values](http://blog.syntheticspeech.de/2023/02/16/nkululeko-how-to-plot-distributions-of-feature-values/)
|
242
243
|
* [Show feature importance](http://blog.syntheticspeech.de/2023/02/20/nkululeko-show-feature-importance/)
|
@@ -334,6 +335,15 @@ F. Burkhardt, Johannes Wagner, Hagen Wierstorf, Florian Eyben and Björn Schulle
|
|
334
335
|
Changelog
|
335
336
|
=========
|
336
337
|
|
338
|
+
Version 0.86.4
|
339
|
+
--------------
|
340
|
+
* add finetuning to the demo module
|
341
|
+
|
342
|
+
Version 0.86.3
|
343
|
+
--------------
|
344
|
+
* bugfixed: nan in finetuned model and double saving
|
345
|
+
* import features now get multiindex automatically
|
346
|
+
|
337
347
|
Version 0.86.2
|
338
348
|
--------------
|
339
349
|
* plots epoch progression for finetuned models now
|
@@ -2,11 +2,11 @@ nkululeko/__init__.py,sha256=62f8HiEzJ8rG2QlTFJXUCMpvuH3fKI33DoJSj33mscc,63
|
|
2
2
|
nkululeko/aug_train.py,sha256=YhuZnS_WVWnun9G-M6g5n6rbRxoVREz6Zh7k6qprFNQ,3194
|
3
3
|
nkululeko/augment.py,sha256=4MG0apTAG5RgkuJrYEjGgDdbodZWi_HweSPNI1JJ5QA,3051
|
4
4
|
nkululeko/cacheddataset.py,sha256=lIJ6hUo5LoxSrzXtWV8mzwO7wRtUETWnOQ4ws2XfL1E,969
|
5
|
-
nkululeko/constants.py,sha256=
|
6
|
-
nkululeko/demo.py,sha256=
|
5
|
+
nkululeko/constants.py,sha256=QtEoU6iCjnUpcJT-FOh4bU4miJ_D0z26OpSub4oEY1c,39
|
6
|
+
nkululeko/demo.py,sha256=WSKr-W5uJ9DQfemK923g7Hd5V3kgAn03Er0JX1Pa45I,5142
|
7
7
|
nkululeko/demo_feats.py,sha256=sAeGFojhEj9WEDFtG3SzPBmyYJWLF2rkbpp65m8Ujo4,2025
|
8
8
|
nkululeko/demo_predictor.py,sha256=es56xbT8ifkS_vnrlb5NTZT54gNmeUtNlA4zVA_gnN8,4757
|
9
|
-
nkululeko/experiment.py,sha256=
|
9
|
+
nkululeko/experiment.py,sha256=huhHLQfnzxRJlQi2SY61XMWbC8xEWpe31yq9spBUk-4,31041
|
10
10
|
nkululeko/explore.py,sha256=lDzRoW_Taa5u4BBABZLD89BcQWnYlrftJR4jgt1yyj0,2609
|
11
11
|
nkululeko/export.py,sha256=mHeEAAmtZuxdyebLlbSzPrHSi9OMgJHbk35d3DTxRBc,4632
|
12
12
|
nkululeko/feature_extractor.py,sha256=8mssYKmo4LclVI-hiLmJEDZ0ZPyDavFG2YwtXcrGzwM,3976
|
@@ -55,7 +55,7 @@ nkululeko/feat_extract/feats_auddim.py,sha256=VlzKKXTXa5kjLgQBWyEFy-daIyU1SkOwCC
|
|
55
55
|
nkululeko/feat_extract/feats_audmodel.py,sha256=VjBNgAoxsHJhwr6Kwt9CxX6SaCM4RK_OV-GU2W5-bhU,3187
|
56
56
|
nkululeko/feat_extract/feats_clap.py,sha256=nR6eEIRdsMHcfmD1bNtt5WfDvkxKjvEbukSSrXHm-HU,3489
|
57
57
|
nkululeko/feat_extract/feats_hubert.py,sha256=cLoUzSLjSYBkQnftjacSL7ES3O7Ysh_KrPYvZtLX_TU,5196
|
58
|
-
nkululeko/feat_extract/feats_import.py,sha256=
|
58
|
+
nkululeko/feat_extract/feats_import.py,sha256=WiU5lCkJsmFNTDyPV0qIh8mJssa6bpgP7AYw_ClKfWM,1674
|
59
59
|
nkululeko/feat_extract/feats_mld.py,sha256=Vvu7GZOkn7Vda8eIOXqHjg78zegkFe3vTUaCXyVM0eA,2021
|
60
60
|
nkululeko/feat_extract/feats_mos.py,sha256=KXNt7QYEfxkvr6UyVhig2aWQBaIvovlrR4gPuP03gmo,4174
|
61
61
|
nkululeko/feat_extract/feats_opensmile.py,sha256=g6ZsAxjjGGvGfrr5fngWC-NJ8E7CP1kYZwrlodZJzzU,4028
|
@@ -88,7 +88,7 @@ nkululeko/models/model_svm.py,sha256=rsME3KvKvNG7bdE5lbvYUu85WZhaASZxxmdNDIVJRZ4
|
|
88
88
|
nkululeko/models/model_svr.py,sha256=_YZeksqB3eBENGlg3g9RwYFlk9rQQ-XCeNBKLlGGVoE,725
|
89
89
|
nkululeko/models/model_tree.py,sha256=rf16faUm4o2LJgkoYpeY998b8DQIvXZ73_m1IS3TnnE,417
|
90
90
|
nkululeko/models/model_tree_reg.py,sha256=IgQcPTE-304HQLYSKPF8Z4ot_Ur9dH01fZjS0nXke_M,428
|
91
|
-
nkululeko/models/model_tuned.py,sha256=
|
91
|
+
nkululeko/models/model_tuned.py,sha256=vmNBkqvEH-4nnhY1REXDA9kA4vpZJzeRmGJFq7E3bLM,21340
|
92
92
|
nkululeko/models/model_xgb.py,sha256=Thgx5ESdIok4v72mKh4plxpo4smGcKALWNCJTDScY0M,447
|
93
93
|
nkululeko/models/model_xgr.py,sha256=aGBtNGLWjOE_2rICGYGFxmT8DtnHYsIl1lIpMtghHsY,418
|
94
94
|
nkululeko/reporting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -96,7 +96,7 @@ nkululeko/reporting/defines.py,sha256=IsY1YgKRMaABpylVKjBJgJ5bNCEbGCVA_E6pivraqS
|
|
96
96
|
nkululeko/reporting/latex_writer.py,sha256=qiCRSmB4KOD_za4oHu5x-PhwjZohzfo8wecMOwlXZwc,1886
|
97
97
|
nkululeko/reporting/report.py,sha256=W0rcigDdjBvxZQ3pZja_gvToILYvaZ1BFtnN2qFRfYI,1060
|
98
98
|
nkululeko/reporting/report_item.py,sha256=siWeGNgo4bAE46YBMNcsdf3jTMTy76BO9Fi6DTvDig4,533
|
99
|
-
nkululeko/reporting/reporter.py,sha256=
|
99
|
+
nkululeko/reporting/reporter.py,sha256=4dXRwJ-CZ49NlF_kv9hfDjZT3bbWNNMyNpKEBLKs3Ew,13447
|
100
100
|
nkululeko/reporting/result.py,sha256=nSN5or-Py2GPRWHkWpGRh7UCi1W0er7WLEHz8fYLk-A,742
|
101
101
|
nkululeko/segmenting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
102
102
|
nkululeko/segmenting/seg_inaspeechsegmenter.py,sha256=pmLHuXsaqvcdYxB4PSW9l1mbQWZZBJFhi_CGabqydas,1947
|
@@ -104,9 +104,9 @@ nkululeko/segmenting/seg_silero.py,sha256=lLytS38KzARS17omwv8VBw-zz60RVSXGSvZ5Ev
|
|
104
104
|
nkululeko/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
105
105
|
nkululeko/utils/files.py,sha256=UiGAtZRWYjHSvlmPaTMtzyNNGE6qaLaxQkybctS7iRM,4021
|
106
106
|
nkululeko/utils/stats.py,sha256=1yUq0FTOyqkU8TwUocJRYdJaqMU5SlOBBRUun9STo2M,2829
|
107
|
-
nkululeko/utils/util.py,sha256=
|
108
|
-
nkululeko-0.86.
|
109
|
-
nkululeko-0.86.
|
110
|
-
nkululeko-0.86.
|
111
|
-
nkululeko-0.86.
|
112
|
-
nkululeko-0.86.
|
107
|
+
nkululeko/utils/util.py,sha256=ILpfNuaeq-hy1bUkRhVrzO2wG9z9Upaozs9EBoIaMG0,14123
|
108
|
+
nkululeko-0.86.4.dist-info/LICENSE,sha256=0zGP5B_W35yAcGfHPS18Q2B8UhvLRY3dQq1MhpsJU_U,1076
|
109
|
+
nkululeko-0.86.4.dist-info/METADATA,sha256=D1y8wrwDr0gLVdafV4E_GcER5yrt3IaKUdqJ8huMCwA,37480
|
110
|
+
nkululeko-0.86.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
111
|
+
nkululeko-0.86.4.dist-info/top_level.txt,sha256=DPFNNSHPjUeVKj44dVANAjuVGRCC3MusJ08lc2a8xFA,10
|
112
|
+
nkululeko-0.86.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|