spacr 0.3.2__py3-none-any.whl → 0.3.22__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.
- spacr/core.py +105 -1
- spacr/deep_spacr.py +171 -25
- spacr/io.py +80 -121
- spacr/ml.py +153 -66
- spacr/plot.py +429 -7
- spacr/settings.py +6 -5
- spacr/submodules.py +7 -6
- spacr/toxo.py +9 -4
- spacr/utils.py +152 -13
- {spacr-0.3.2.dist-info → spacr-0.3.22.dist-info}/METADATA +28 -25
- {spacr-0.3.2.dist-info → spacr-0.3.22.dist-info}/RECORD +15 -15
- {spacr-0.3.2.dist-info → spacr-0.3.22.dist-info}/LICENSE +0 -0
- {spacr-0.3.2.dist-info → spacr-0.3.22.dist-info}/WHEEL +0 -0
- {spacr-0.3.2.dist-info → spacr-0.3.22.dist-info}/entry_points.txt +0 -0
- {spacr-0.3.2.dist-info → spacr-0.3.22.dist-info}/top_level.txt +0 -0
spacr/utils.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import os, re, sqlite3, torch, torchvision, random, string, shutil, cv2, tarfile, glob, psutil, platform, gzip, subprocess, time, requests
|
1
|
+
import os, re, sqlite3, torch, torchvision, random, string, shutil, cv2, tarfile, glob, psutil, platform, gzip, subprocess, time, requests, ast
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
import pandas as pd
|
@@ -37,6 +37,7 @@ from torchvision import models
|
|
37
37
|
from torchvision.models.resnet import ResNet18_Weights, ResNet34_Weights, ResNet50_Weights, ResNet101_Weights, ResNet152_Weights
|
38
38
|
import torchvision.transforms as transforms
|
39
39
|
from torchvision.models import resnet50
|
40
|
+
from torchvision.utils import make_grid
|
40
41
|
|
41
42
|
import seaborn as sns
|
42
43
|
import matplotlib.pyplot as plt
|
@@ -66,13 +67,84 @@ from huggingface_hub import list_repo_files
|
|
66
67
|
import umap.umap_ as umap
|
67
68
|
#import umap
|
68
69
|
|
70
|
+
def load_settings(csv_file_path, show=False, setting_key='setting_key', setting_value='setting_value'):
|
71
|
+
"""
|
72
|
+
Convert a CSV file with 'settings_key' and 'settings_value' columns into a dictionary.
|
73
|
+
Handles special cases where values are lists, tuples, booleans, None, integers, floats, and nested dictionaries.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
csv_file_path (str): The path to the CSV file.
|
77
|
+
show (bool): Whether to display the dataframe (for debugging).
|
78
|
+
setting_key (str): The name of the column that contains the setting keys.
|
79
|
+
setting_value (str): The name of the column that contains the setting values.
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
dict: A dictionary where 'settings_key' are the keys and 'settings_value' are the values.
|
83
|
+
"""
|
84
|
+
# Read the CSV file into a DataFrame
|
85
|
+
df = pd.read_csv(csv_file_path)
|
86
|
+
|
87
|
+
if show:
|
88
|
+
display(df)
|
89
|
+
|
90
|
+
# Ensure the columns 'setting_key' and 'setting_value' exist
|
91
|
+
if setting_key not in df.columns or setting_value not in df.columns:
|
92
|
+
raise ValueError(f"CSV file must contain {setting_key} and {setting_value} columns.")
|
93
|
+
|
94
|
+
def parse_value(value):
|
95
|
+
"""Parse the string value into the appropriate Python data type."""
|
96
|
+
# Handle empty values
|
97
|
+
if pd.isna(value) or value == '':
|
98
|
+
return None
|
99
|
+
|
100
|
+
# Handle boolean values
|
101
|
+
if value == 'True':
|
102
|
+
return True
|
103
|
+
if value == 'False':
|
104
|
+
return False
|
105
|
+
|
106
|
+
# Handle lists, tuples, dictionaries, and other literals
|
107
|
+
if value.startswith(('(', '[', '{')): # If it starts with (, [ or {, use ast.literal_eval
|
108
|
+
try:
|
109
|
+
parsed_value = ast.literal_eval(value)
|
110
|
+
# If parsed_value is a dict, recursively parse its values
|
111
|
+
if isinstance(parsed_value, dict):
|
112
|
+
parsed_value = {k: parse_value(v) for k, v in parsed_value.items()}
|
113
|
+
return parsed_value
|
114
|
+
except (ValueError, SyntaxError):
|
115
|
+
pass # If there's an error, return the value as-is
|
116
|
+
|
117
|
+
# Handle numeric values (integers and floats)
|
118
|
+
try:
|
119
|
+
if '.' in value:
|
120
|
+
return float(value) # If it contains a dot, convert to float
|
121
|
+
return int(value) # Otherwise, convert to integer
|
122
|
+
except ValueError:
|
123
|
+
pass # If it's not a valid number, return the value as-is
|
124
|
+
|
125
|
+
# Return the original value if no other type matched
|
126
|
+
return value
|
127
|
+
|
128
|
+
# Convert the DataFrame to a dictionary, with parsing of each value
|
129
|
+
result_dict = {key: parse_value(value) for key, value in zip(df[setting_key], df[setting_value])}
|
130
|
+
|
131
|
+
return result_dict
|
132
|
+
|
133
|
+
|
69
134
|
def save_settings(settings, name='settings', show=False):
|
70
135
|
|
71
136
|
settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
|
72
137
|
if show:
|
73
138
|
display(settings_df)
|
74
|
-
|
75
|
-
|
139
|
+
|
140
|
+
if isinstance(settings['src'], list):
|
141
|
+
src = settings['src'][0]
|
142
|
+
name = f"{name}_list"
|
143
|
+
else:
|
144
|
+
src = settings['src']
|
145
|
+
|
146
|
+
settings_csv = os.path.join(src,'settings',f'{name}.csv')
|
147
|
+
os.makedirs(os.path.join(src,'settings'), exist_ok=True)
|
76
148
|
settings_df.to_csv(settings_csv, index=False)
|
77
149
|
|
78
150
|
def print_progress(files_processed, files_to_process, n_jobs, time_ls=None, batch_size=None, operation_type=""):
|
@@ -2987,7 +3059,6 @@ def preprocess_image(image_path, image_size=224, channels=[1,2,3], normalize=Tru
|
|
2987
3059
|
input_tensor = transform(image).unsqueeze(0)
|
2988
3060
|
return image, input_tensor
|
2989
3061
|
|
2990
|
-
|
2991
3062
|
class SaliencyMapGenerator:
|
2992
3063
|
def __init__(self, model):
|
2993
3064
|
self.model = model
|
@@ -3008,17 +3079,63 @@ class SaliencyMapGenerator:
|
|
3008
3079
|
saliency = X.grad.abs()
|
3009
3080
|
return saliency
|
3010
3081
|
|
3011
|
-
def
|
3082
|
+
def compute_saliency_and_predictions(self, X):
|
3083
|
+
self.model.eval()
|
3084
|
+
X.requires_grad_()
|
3085
|
+
|
3086
|
+
# Forward pass to get predictions (logits)
|
3087
|
+
scores = self.model(X).squeeze()
|
3088
|
+
|
3089
|
+
# Get predicted class (0 or 1 for binary classification)
|
3090
|
+
predictions = (scores > 0).long()
|
3091
|
+
|
3092
|
+
# Compute saliency maps
|
3093
|
+
self.model.zero_grad()
|
3094
|
+
target_scores = scores * (2 * predictions - 1)
|
3095
|
+
target_scores.backward(torch.ones_like(target_scores))
|
3096
|
+
|
3097
|
+
saliency = X.grad.abs()
|
3098
|
+
|
3099
|
+
return saliency, predictions
|
3100
|
+
|
3101
|
+
def plot_saliency_grid(self, X, saliency, predictions, mode='mean'):
|
3012
3102
|
N = X.shape[0]
|
3103
|
+
rows = (N + 7) // 8 # Ensure we can handle batches of different sizes
|
3104
|
+
fig, axs = plt.subplots(rows, 8, figsize=(16, rows * 2))
|
3105
|
+
|
3013
3106
|
for i in range(N):
|
3014
|
-
|
3015
|
-
|
3016
|
-
|
3017
|
-
|
3018
|
-
|
3019
|
-
|
3020
|
-
|
3021
|
-
|
3107
|
+
ax = axs[i // 8, i % 8]
|
3108
|
+
|
3109
|
+
if mode == 'mean':
|
3110
|
+
saliency_map = saliency[i].mean(dim=0).cpu().numpy() # Mean saliency over channels
|
3111
|
+
ax.imshow(X[i].permute(1, 2, 0).detach().cpu().numpy()) # Added .detach() here
|
3112
|
+
ax.imshow(saliency_map, cmap='jet', alpha=0.5)
|
3113
|
+
|
3114
|
+
elif mode == 'channel':
|
3115
|
+
# Plot individual channels in a loop if the image has multiple channels
|
3116
|
+
for j in range(X.shape[1]):
|
3117
|
+
saliency_map = saliency[i, j].cpu().numpy()
|
3118
|
+
ax.imshow(saliency_map, cmap='jet')
|
3119
|
+
ax.axis('off')
|
3120
|
+
|
3121
|
+
elif mode == '3-channel' and X.shape[1] == 3:
|
3122
|
+
saliency_map = saliency[i].cpu().numpy().transpose(1, 2, 0)
|
3123
|
+
ax.imshow(saliency_map)
|
3124
|
+
|
3125
|
+
elif mode == '2-channel' and X.shape[1] == 2:
|
3126
|
+
saliency_map = saliency[i].cpu().numpy().transpose(1, 2, 0)
|
3127
|
+
ax.imshow(saliency_map)
|
3128
|
+
|
3129
|
+
# Add class label in top-left corner
|
3130
|
+
ax.text(5, 25, str(predictions[i].item()), fontsize=12, color='white', weight='bold',
|
3131
|
+
bbox=dict(facecolor='black', alpha=0.7, boxstyle='round,pad=0.2'))
|
3132
|
+
ax.axis('off')
|
3133
|
+
|
3134
|
+
# Turn off unused axes
|
3135
|
+
for j in range(N, rows * 8):
|
3136
|
+
fig.delaxes(axs[j // 8, j % 8])
|
3137
|
+
|
3138
|
+
plt.tight_layout(pad=0)
|
3022
3139
|
plt.show()
|
3023
3140
|
|
3024
3141
|
def preprocess_image(image_path, normalize=True, image_size=224, channels=[1,2,3]):
|
@@ -4548,3 +4665,25 @@ def download_models(repo_id="einarolafsson/models", local_dir=None, retries=5, d
|
|
4548
4665
|
time.sleep(delay)
|
4549
4666
|
|
4550
4667
|
raise Exception("Failed to download model files after multiple attempts.")
|
4668
|
+
|
4669
|
+
def generate_cytoplasm_mask(nucleus_mask, cell_mask):
|
4670
|
+
|
4671
|
+
"""
|
4672
|
+
Generates a cytoplasm mask from nucleus and cell masks.
|
4673
|
+
|
4674
|
+
Parameters:
|
4675
|
+
- nucleus_mask (np.array): Binary or segmented mask of the nucleus (non-zero values represent nucleus).
|
4676
|
+
- cell_mask (np.array): Binary or segmented mask of the whole cell (non-zero values represent cell).
|
4677
|
+
|
4678
|
+
Returns:
|
4679
|
+
- cytoplasm_mask (np.array): Mask for the cytoplasm (1 for cytoplasm, 0 for nucleus and pathogens).
|
4680
|
+
"""
|
4681
|
+
|
4682
|
+
# Make sure the nucleus and cell masks are numpy arrays
|
4683
|
+
nucleus_mask = np.array(nucleus_mask)
|
4684
|
+
cell_mask = np.array(cell_mask)
|
4685
|
+
|
4686
|
+
# Generate cytoplasm mask
|
4687
|
+
cytoplasm_mask = np.where(np.logical_or(nucleus_mask != 0), 0, cell_mask)
|
4688
|
+
|
4689
|
+
return cytoplasm_mask
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: spacr
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.22
|
4
4
|
Summary: Spatial phenotype analysis of crisp screens (SpaCr)
|
5
5
|
Home-page: https://github.com/EinarOlafsson/spacr
|
6
6
|
Author: Einar Birnir Olafsson
|
@@ -10,34 +10,41 @@ Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
11
11
|
Description-Content-Type: text/x-rst
|
12
12
|
License-File: LICENSE
|
13
|
-
Requires-Dist: torch<3.0,>=2.0
|
14
|
-
Requires-Dist: torchvision<1.0,>=0.1
|
15
|
-
Requires-Dist: torch-geometric<3.0,>=2.5
|
16
13
|
Requires-Dist: numpy<2.0,>=1.26.4
|
17
|
-
Requires-Dist: bottleneck<2.0,>=1.3.6
|
18
|
-
Requires-Dist: numexpr<3.0,>=2.8.4
|
19
14
|
Requires-Dist: pandas<3.0,>=2.2.1
|
20
|
-
Requires-Dist:
|
15
|
+
Requires-Dist: scipy<2.0,>=1.12.0
|
16
|
+
Requires-Dist: cellpose<4.0,>=3.0.6
|
21
17
|
Requires-Dist: scikit-image<1.0,>=0.22.0
|
22
18
|
Requires-Dist: scikit-learn<2.0,>=1.4.1
|
23
|
-
Requires-Dist: seaborn<1.0,>=0.13.2
|
24
|
-
Requires-Dist: matplotlib<4.0,>=3.8.3
|
25
|
-
Requires-Dist: shap<1.0,>=0.45.0
|
26
|
-
Requires-Dist: pillow<11.0,>=10.2.0
|
27
|
-
Requires-Dist: imageio<3.0,>=2.34.0
|
28
|
-
Requires-Dist: scipy<2.0,>=1.12.0
|
29
|
-
Requires-Dist: ipywidgets<9.0,>=8.1.2
|
30
19
|
Requires-Dist: mahotas<2.0,>=1.4.13
|
31
20
|
Requires-Dist: btrack<1.0,>=0.6.5
|
32
21
|
Requires-Dist: trackpy<1.0,>=0.6.2
|
33
|
-
Requires-Dist:
|
34
|
-
Requires-Dist:
|
22
|
+
Requires-Dist: statsmodels<1.0,>=0.14.1
|
23
|
+
Requires-Dist: shap<1.0,>=0.45.0
|
24
|
+
Requires-Dist: torch<3.0,>=2.0
|
25
|
+
Requires-Dist: torchvision<1.0,>=0.1
|
26
|
+
Requires-Dist: torch-geometric<3.0,>=2.5
|
27
|
+
Requires-Dist: torchcam<1.0,>=0.4.0
|
28
|
+
Requires-Dist: transformers<5.0,>=4.45.2
|
29
|
+
Requires-Dist: segmentation-models-pytorch>=0.3.3
|
30
|
+
Requires-Dist: monai>=1.3.0
|
31
|
+
Requires-Dist: captum<1.0,>=0.7.0
|
32
|
+
Requires-Dist: seaborn<1.0,>=0.13.2
|
33
|
+
Requires-Dist: matplotlib<4.0,>=3.8.3
|
34
|
+
Requires-Dist: adjustText<2.0,>=1.2.0
|
35
|
+
Requires-Dist: bottleneck<2.0,>=1.3.6
|
36
|
+
Requires-Dist: numexpr<3.0,>=2.8.4
|
35
37
|
Requires-Dist: opencv-python-headless<5.0,>=4.9.0.80
|
38
|
+
Requires-Dist: pillow<11.0,>=10.2.0
|
39
|
+
Requires-Dist: tifffile>=2023.4.12
|
40
|
+
Requires-Dist: nd2reader<4.0,>=3.3.0
|
41
|
+
Requires-Dist: czifile
|
42
|
+
Requires-Dist: imageio<3.0,>=2.34.0
|
43
|
+
Requires-Dist: pingouin<1.0,>=0.5.5
|
36
44
|
Requires-Dist: umap-learn<1.0,>=0.5.6
|
37
45
|
Requires-Dist: ttkthemes<4.0,>=3.2.2
|
38
46
|
Requires-Dist: xgboost<3.0,>=2.0.3
|
39
47
|
Requires-Dist: PyWavelets<2.0,>=1.6.0
|
40
|
-
Requires-Dist: torchcam<1.0,>=0.4.0
|
41
48
|
Requires-Dist: ttf-opensans>=2020.10.30
|
42
49
|
Requires-Dist: customtkinter<6.0,>=5.2.2
|
43
50
|
Requires-Dist: biopython<2.0,>=1.80
|
@@ -50,19 +57,15 @@ Requires-Dist: tables<4.0,>=3.8.0
|
|
50
57
|
Requires-Dist: rapidfuzz<4.0,>=3.9
|
51
58
|
Requires-Dist: keyring<16.0,>=15.1
|
52
59
|
Requires-Dist: screeninfo<1.0,>=0.8.1
|
53
|
-
Requires-Dist: ipykernel
|
54
|
-
Requires-Dist: gdown
|
55
60
|
Requires-Dist: fastremap>=1.14.1
|
56
|
-
Requires-Dist: monai>=1.3.0
|
57
61
|
Requires-Dist: pytz>=2023.3.post1
|
58
|
-
Requires-Dist: segmentation-models-pytorch>=0.3.3
|
59
|
-
Requires-Dist: tifffile>=2023.4.12
|
60
62
|
Requires-Dist: tqdm>=4.65.0
|
61
63
|
Requires-Dist: wandb>=0.16.2
|
62
64
|
Requires-Dist: openai<2.0,>=1.50.2
|
63
|
-
Requires-Dist:
|
64
|
-
Requires-Dist:
|
65
|
-
Requires-Dist:
|
65
|
+
Requires-Dist: gdown
|
66
|
+
Requires-Dist: IPython<9.0,>=8.18.1
|
67
|
+
Requires-Dist: ipykernel
|
68
|
+
Requires-Dist: ipywidgets<9.0,>=8.1.2
|
66
69
|
Requires-Dist: huggingface-hub<0.25,>=0.24.0
|
67
70
|
Provides-Extra: dev
|
68
71
|
Requires-Dist: pytest<3.11,>=3.9; extra == "dev"
|
@@ -8,26 +8,26 @@ spacr/app_measure.py,sha256=_K7APYIeOKpV6e_LcqabBjvEi7mfq9Fch8175x1x0k8,162
|
|
8
8
|
spacr/app_sequencing.py,sha256=DjG26jy4cpddnV8WOOAIiExtOe9MleVMY4MFa5uTo5w,157
|
9
9
|
spacr/app_umap.py,sha256=ZWAmf_OsIKbYvolYuWPMYhdlVe-n2CADoJulAizMiEo,153
|
10
10
|
spacr/cellpose.py,sha256=zv4BzhaP2O-mtQ-pUfYvpOyxgn1ke_bDWgdHD5UWm9I,13942
|
11
|
-
spacr/core.py,sha256=
|
12
|
-
spacr/deep_spacr.py,sha256=
|
11
|
+
spacr/core.py,sha256=G_x-w7FRIHNfSOoPaIZPSf_A7mVj7PA7o9HQZ4nIu5o,48231
|
12
|
+
spacr/deep_spacr.py,sha256=iPJhwhNQKF0_PmQ3RXi_gK7BKvIb5m54DeYZRlXBqMU,46081
|
13
13
|
spacr/gui.py,sha256=ndmWP4F0QO5j6DM6MNzoGtzv_7Yj4LTW2SLi9URBZIQ,8055
|
14
14
|
spacr/gui_core.py,sha256=OJQxzpehIyDzjSjIsvxSHat4NIjkqjX0VZAUQTnzEzg,40921
|
15
15
|
spacr/gui_elements.py,sha256=3ru8FPZtXCZSj7167GJj18-Zo6TVebhAzkit-mmqmTI,135342
|
16
16
|
spacr/gui_utils.py,sha256=76utRICvY0k_6X8CA1P_TmYBJARp4b87OkI9t39tldA,45822
|
17
|
-
spacr/io.py,sha256=
|
17
|
+
spacr/io.py,sha256=Xy1Drm5NPhxvwE1nyJVd2SQu3yTynlnvSrjlRuFXwBw,143371
|
18
18
|
spacr/logger.py,sha256=lJhTqt-_wfAunCPl93xE65Wr9Y1oIHJWaZMjunHUeIw,1538
|
19
19
|
spacr/measure.py,sha256=8MRjQdB-2n8JVLjEpF3cxvfT-Udug27uJ2ErJJ5t1ic,56000
|
20
20
|
spacr/mediar.py,sha256=FwLvbLQW5LQzPgvJZG8Lw7GniA2vbZx6Jv6vIKu7I5c,14743
|
21
|
-
spacr/ml.py,sha256=
|
21
|
+
spacr/ml.py,sha256=3XiQUfhhseCz9cZXhaVkCCv_qfqoZCdXGnO_p3ulwo4,47131
|
22
22
|
spacr/openai.py,sha256=5vBZ3Jl2llYcW3oaTEXgdyCB2aJujMUIO5K038z7w_A,1246
|
23
|
-
spacr/plot.py,sha256=
|
23
|
+
spacr/plot.py,sha256=eZcs-CQrDTENXVeMY8y8N8VZnmPePO-kAWdoaweFmW8,105540
|
24
24
|
spacr/sequencing.py,sha256=t18mgpK6rhWuB1LtFOsPxqgpFXxuUmrD06ecsaVQ0Gw,19655
|
25
|
-
spacr/settings.py,sha256=
|
25
|
+
spacr/settings.py,sha256=YExChD7DWY_cJyaPGKDTpFajsXXi5ZQ8P0XR9ZQf8CE,73560
|
26
26
|
spacr/sim.py,sha256=1xKhXimNU3ukzIw-3l9cF3Znc_brW8h20yv8fSTzvss,71173
|
27
|
-
spacr/submodules.py,sha256=
|
27
|
+
spacr/submodules.py,sha256=AB7s6-cULsaqz-haAaCtXfGEIi8uPZGT4xoCslUJC3Y,18391
|
28
28
|
spacr/timelapse.py,sha256=FSYpUtAVy6xc3lwprRYgyDTT9ysUhfRQ4zrP9_h2mvg,39465
|
29
|
-
spacr/toxo.py,sha256=
|
30
|
-
spacr/utils.py,sha256=
|
29
|
+
spacr/toxo.py,sha256=us3pQyULtMTyfTq0MWPn4QJTTmQ6BwAJKChNf75jo3I,10082
|
30
|
+
spacr/utils.py,sha256=brlNXsDcsKyHjJ2IodB0KyMQkEpQfMBp5QZCCb0vdz8,198459
|
31
31
|
spacr/version.py,sha256=axH5tnGwtgSnJHb5IDhiu4Zjk5GhLyAEDRe-rnaoFOA,409
|
32
32
|
spacr/resources/MEDIAR/.gitignore,sha256=Ff1q9Nme14JUd-4Q3jZ65aeQ5X4uttptssVDgBVHYo8,152
|
33
33
|
spacr/resources/MEDIAR/LICENSE,sha256=yEj_TRDLUfDpHDNM0StALXIt6mLqSgaV2hcCwa6_TcY,1065
|
@@ -150,9 +150,9 @@ spacr/resources/icons/umap.png,sha256=dOLF3DeLYy9k0nkUybiZMe1wzHQwLJFRmgccppw-8b
|
|
150
150
|
spacr/resources/images/plate1_E01_T0001F001L01A01Z01C02.tif,sha256=Tl0ZUfZ_AYAbu0up_nO0tPRtF1BxXhWQ3T3pURBCCRo,7958528
|
151
151
|
spacr/resources/images/plate1_E01_T0001F001L01A02Z01C01.tif,sha256=m8N-V71rA1TT4dFlENNg8s0Q0YEXXs8slIn7yObmZJQ,7958528
|
152
152
|
spacr/resources/images/plate1_E01_T0001F001L01A03Z01C03.tif,sha256=Pbhk7xn-KUP6RSIhJsxQcrHFImBm3GEpLkzx7WOc-5M,7958528
|
153
|
-
spacr-0.3.
|
154
|
-
spacr-0.3.
|
155
|
-
spacr-0.3.
|
156
|
-
spacr-0.3.
|
157
|
-
spacr-0.3.
|
158
|
-
spacr-0.3.
|
153
|
+
spacr-0.3.22.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
|
154
|
+
spacr-0.3.22.dist-info/METADATA,sha256=wHP5zD5dSSsxLHNjlr3-OALEmhzL7gexG_uqX6M0OWc,5949
|
155
|
+
spacr-0.3.22.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
156
|
+
spacr-0.3.22.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
|
157
|
+
spacr-0.3.22.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
|
158
|
+
spacr-0.3.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|