scez 0.0.1__py2.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.

Potentially problematic release.


This version of scez might be problematic. Click here for more details.

scez/__init__.py ADDED
@@ -0,0 +1,36 @@
1
+ """scez – Single Cell Analysis, Easy Mode!"""
2
+
3
+ from . import diffexp as de
4
+ from . import preprocess as pp
5
+ from . import representation as rp
6
+ from . import utils
7
+ import scanpy as sc
8
+ import matplotlib.pyplot as plt
9
+
10
+ import tomli
11
+
12
+ toml_dict = tomli.load(open('pyproject.toml','rb'))
13
+ __version__ = toml_dict['tool']['poetry']['version']
14
+
15
+
16
+ sc.settings.verbosity = 1 # verbosity: errors (0), warnings (1), info (2), hints (3)
17
+ sc.settings.set_figure_params(dpi=100, dpi_save=300, frameon=False, figsize=(5, 5), facecolor='white')
18
+ sc.logging.print_header()
19
+
20
+ # https://stackoverflow.com/questions/21884271/warning-about-too-many-open-figures
21
+ plt.rcParams.update({'figure.max_open_warning': 0})
22
+ plt.close('all')
23
+
24
+ # https://stackoverflow.com/questions/3899980/how-to-change-the-font-size-on-a-matplotlib-plot
25
+
26
+ SMALL_SIZE = 6
27
+ MEDIUM_SIZE = 8
28
+ BIGGER_SIZE = 10
29
+
30
+ plt.rc('font', size=SMALL_SIZE) # controls default text sizes
31
+ plt.rc('axes', titlesize=SMALL_SIZE) # font size of the axes title
32
+ plt.rc('axes', labelsize=MEDIUM_SIZE) # font size of the x and y labels
33
+ plt.rc('xtick', labelsize=SMALL_SIZE) # font size of the tick labels
34
+ plt.rc('ytick', labelsize=SMALL_SIZE) # font size of the tick labels
35
+ plt.rc('legend', fontsize=SMALL_SIZE) # legend font size
36
+ plt.rc('figure', titlesize=BIGGER_SIZE) # font size of the figure title
scez/diffexp.py ADDED
@@ -0,0 +1,199 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+ import pandas as pd
4
+ import seaborn as sns
5
+ import anndata as ad
6
+
7
+ from pydeseq2.dds import DeseqDataSet
8
+ from pydeseq2.default_inference import DefaultInference
9
+ from pydeseq2.ds import DeseqStats
10
+ from .utils import run_adjust_text
11
+ from adpbulk import ADPBulk
12
+
13
+
14
+ def pseudobulk_by_clusters(adt, condition, cluster_col='leiden', method="mean"):
15
+ # initialize the object
16
+ adpb = ADPBulk(adt, [cluster_col, condition], method=method)
17
+
18
+ # perform the pseudobulking
19
+ pseudobulk_matrix = adpb.fit_transform()
20
+
21
+ # retrieve the sample metadata (useful for easy incorporation with edgeR)
22
+ sample_meta = adpb.get_meta()
23
+
24
+ out = ad.AnnData(
25
+ X=pseudobulk_matrix,
26
+ obs=sample_meta.set_index('SampleName')
27
+ )
28
+
29
+ return out
30
+
31
+
32
+ def run_deseq(adata, design, tested_level, ref_level, n_cpus=8):
33
+
34
+ inference = DefaultInference(n_cpus=n_cpus)
35
+
36
+ dds = DeseqDataSet(
37
+ counts=adata.to_df().astype(int),
38
+ metadata=adata.obs,
39
+ design_factors=design, # compare samples based on the "condition"
40
+ refit_cooks=True,
41
+ inference=inference,
42
+ )
43
+
44
+ dds.deseq2()
45
+
46
+ stat_res = DeseqStats(
47
+ dds,
48
+ contrast=[design, tested_level, ref_level],
49
+ inference=inference
50
+ )
51
+ stat_res.summary()
52
+
53
+ df = stat_res.results_df
54
+
55
+ return df
56
+
57
+
58
+ def plot_volcano(df, title=None, labels=None, n_genes=False, side='both',
59
+ font_scale=1, dot_size = 5,
60
+ color = '#1f77b4', color_highlight = '#FFA500',
61
+ ax = None, **kwargs):
62
+ dot_size_highlight = dot_size * 1.1
63
+ annotate_font_size = 5 * font_scale
64
+ scatter_font_size = 8 * font_scale
65
+ label_font_size = 9 * font_scale
66
+ title_font_size = 10 * font_scale
67
+
68
+ if 'name' not in df.columns: df['name'] = df.index.to_list()
69
+ df['-log10(pvalue)'] = - np.log10(df.pvalue)
70
+
71
+ if not ax: fig, ax = plt.subplots(figsize=(3, 3))
72
+
73
+ # Scatter plot
74
+ ax.scatter(
75
+ df['log2FoldChange'],
76
+ df['-log10(pvalue)'],
77
+ alpha=0.9, s=dot_size, c=color,
78
+ **kwargs
79
+ )
80
+
81
+ # Set background color to transparent
82
+ ax.set_facecolor('none')
83
+
84
+ # Set smaller font size
85
+ ax.tick_params(axis='both', which='both', labelsize=scatter_font_size)
86
+
87
+ # Set labels
88
+ ax.set_xlabel('log2FoldChange', fontsize=label_font_size)
89
+ ax.set_ylabel('-log10(pvalue)', fontsize=label_font_size)
90
+
91
+ # Set plot title
92
+ if not title:
93
+ ax.set_title('Volcano Plot', fontsize=title_font_size)
94
+ else:
95
+ ax.set_title(title, fontsize=title_font_size)
96
+
97
+ ax.grid(False)
98
+
99
+ # check if `labels` is provided or set that based on `n_genes` and `side`
100
+ if labels and n_genes:
101
+ # error message if both labels and n_genes are provided and say one of them is allowed
102
+ raise ValueError('Provide either labels or n_genes, not both!')
103
+
104
+ elif n_genes and side == 'positive':
105
+ # Highlight top genes
106
+ top_genes = df.query('log2FoldChange > 0').nlargest(n_genes, '-log10(pvalue)')
107
+ labels = [row['name'] for _, row in top_genes.iterrows()]
108
+
109
+ elif n_genes and side == 'negative':
110
+ # Highlight top genes
111
+ top_genes = df.query('log2FoldChange < 0').nlargest(n_genes, '-log10(pvalue)')
112
+ labels = [row['name'] for _, row in top_genes.iterrows()]
113
+
114
+ elif n_genes and side == 'both':
115
+ # Highlight top genes
116
+ top_genes = df.nlargest(n_genes, '-log10(pvalue)')
117
+ labels = [row['name'] for _, row in top_genes.iterrows()]
118
+
119
+ # Highlight the points from given labels
120
+ if labels:
121
+ for label in labels:
122
+ ax.scatter(
123
+ df.loc[label, 'log2FoldChange'],
124
+ df.loc[label, '-log10(pvalue)'],
125
+ s=dot_size_highlight, c=color_highlight
126
+ )
127
+ run_adjust_text(
128
+ df.loc[labels, 'log2FoldChange'],
129
+ df.loc[labels, '-log10(pvalue)'],
130
+ labels,
131
+ font_size=annotate_font_size, ax=ax, use_arrow=False
132
+ )
133
+
134
+ if not ax:
135
+ plt.tight_layout()
136
+ plt.show()
137
+
138
+
139
+ def plot_top_DEG_violinplot(adata, df, title=None, labels=None, n_genes=False, side='both', font_scale=1, figsize=(10, 4), **kwargs):
140
+ label_font_size = 9 * font_scale
141
+ title_font_size = 10 * font_scale
142
+
143
+ if 'name' not in df.columns: df['name'] = df.index.to_list()
144
+
145
+ if labels and n_genes:
146
+ # error message if both labels and n_genes are provided and say one of them is allowed
147
+ raise ValueError('Provide either labels or n_genes, not both!')
148
+
149
+ if not labels and not n_genes:
150
+ # error message if neither labels nor n_genes are provided
151
+ raise ValueError('Provide either labels or n_genes!')
152
+
153
+ if labels:
154
+ # Highlight the points from given list
155
+ selected_genes = df.loc[labels]
156
+
157
+ elif n_genes and side == 'positive':
158
+ # Highlight top genes
159
+ selected_genes = df.query('log2FoldChange > 0').nlargest(n_genes, '-log10(pvalue)')
160
+
161
+ elif n_genes and side == 'negative':
162
+ # Highlight top genes
163
+ selected_genes = df.query('log2FoldChange < 0').nlargest(n_genes, '-log10(pvalue)')
164
+
165
+ elif n_genes and side == 'both':
166
+ # Highlight top genes
167
+ selected_genes = df.nlargest(n_genes, '-log10(pvalue)')
168
+
169
+ # Filter the single-cell dataset for the selected genes
170
+ subset_adata = adata[:, selected_genes.index]
171
+ subset_adata.var.index = subset_adata.var.index.str.split('_').str[0]
172
+
173
+ # Convert the subset of adata to a DataFrame
174
+ subset_df = subset_adata.to_df()
175
+
176
+ # Merge the DataFrame with .obs to include the 'sample' information
177
+ merged_df = pd.merge(subset_df, adata.obs[['sample']], left_index=True, right_index=True)
178
+
179
+ # Melt the DataFrame to prepare for violin plot
180
+ melted_df = pd.melt(merged_df, id_vars='sample', var_name='Gene', value_name='Counts')
181
+
182
+ # Create a violin plot
183
+ plt.figure(figsize=figsize)
184
+ sns.violinplot(x='Gene', y='Counts', hue='sample', data=melted_df, split=True, inner='quartile', palette='Set2', **kwargs)
185
+ sns.stripplot(x='Gene', y='Counts', hue='sample', data=melted_df, dodge=True, jitter=True, color='black', size=1, alpha=0.3, **kwargs)
186
+
187
+ plt.xticks(rotation=45, ha='right', fontsize=label_font_size)
188
+ plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', fontsize=label_font_size)
189
+
190
+ if not title:
191
+ plt.title('Top Differentially Expressed Genes', fontsize=title_font_size)
192
+ else:
193
+ plt.title(title, fontsize=title_font_size)
194
+ plt.show()
195
+
196
+
197
+ def write_top_DEGs(df, sample_id, result_dir='.', n_hits=200):
198
+ df['-log10(pvalue)'] = - np.log10(df.pvalue)
199
+ df.nlargest(n_hits, '-log10(pvalue)').to_csv(f'{result_dir}/{sample_id}_top_{n_hits}.csv') # Adjust the number as needed
scez/preprocess.py ADDED
@@ -0,0 +1,74 @@
1
+ import pandas as pd
2
+ import scanpy as sc
3
+ import scar
4
+
5
+
6
+ def normalization(adata, target_sum=1e4, max_value=10, final_layer='scaled', keep_initial_layer=True):
7
+ if keep_initial_layer == True:
8
+ adata.layers['raw_counts'] = adata.X.copy()
9
+ elif type(keep_initial_layer) == str:
10
+ adata.layers[keep_initial_layer] = adata.X.copy()
11
+
12
+ # normalize counts to target_sum (default 1e4)
13
+ counts = sc.pp.normalize_total(adata, target_sum=target_sum, inplace=False)
14
+ # log1p transform
15
+ adata.layers["log1p_norm"] = sc.pp.log1p(counts["X"], copy=True)
16
+ # scale counts
17
+ adata.layers['scaled'] = sc.pp.scale(adata, max_value=max_value, copy=True).X
18
+ # set the final layer
19
+ adata.X = adata.layers[final_layer]
20
+
21
+
22
+ def remove_ambient_rna(adata_filtered_feature_bc, adata_raw_feature_bc):
23
+ scar.setup_anndata(
24
+ adata = adata_filtered_feature_bc,
25
+ raw_adata = adata_raw_feature_bc,
26
+ prob = 0.995,
27
+ kneeplot = True
28
+ )
29
+
30
+ adata_scar = scar.model(
31
+ raw_count=adata_filtered_feature_bc.to_df(), # In the case of Anndata object, scar will automatically use the estimated ambient_profile present in adata.uns.
32
+ # ambient_profile=adata_filtered_feature_bc.uns['ambient_profile_Gene Expression'],
33
+ feature_type='mRNA',
34
+ sparsity=1,
35
+ # device=device # Both cpu and cuda are supported.
36
+ )
37
+
38
+ adata_scar.train(
39
+ epochs=200,
40
+ batch_size=64,
41
+ verbose=True
42
+ )
43
+
44
+ # After training, we can infer the native true signal
45
+ adata_scar.inference(batch_size=256) # by defaut, batch_size = None, set a batch_size if getting a memory issue
46
+
47
+ denoised_count = pd.DataFrame(
48
+ adata_scar.native_counts,
49
+ index=adata_filtered_feature_bc.obs_names,
50
+ columns=adata_filtered_feature_bc.var_names
51
+ )
52
+
53
+ adata = adata_filtered_feature_bc.copy()
54
+ adata.layers['raw_counts'] = adata.X
55
+ adata.layers['scar_denoised_counts'] = denoised_count.to_numpy()
56
+
57
+ return adata
58
+
59
+
60
+ def clustering(
61
+ adata
62
+ ):
63
+ pass
64
+ # , n_pcs=50, n_neighbors=30, use_highly_variable='Yes',
65
+ # use_rep=None, resolution=None
66
+
67
+ # if use_highly_variable == 'Yes':
68
+ # sc.pp.highly_variable_genes(adata, min_mean=0.0125, max_mean=3, min_disp=0.5)
69
+ # sc.tl.pca(adata, svd_solver='arpack', use_highly_variable=True)
70
+ # else:
71
+ # sc.pp.pca(adata, n_comps=n_pcs)
72
+ # sc.pp.neighbors(adata, use_rep=use_rep, n_neighbors=n_neighbors)#, n_pcs=n_pcs)
73
+ # sc.tl.umap(adata)
74
+ # sc.tl.leiden(adata, resolution=resolution)
scez/representation.py ADDED
@@ -0,0 +1,44 @@
1
+ from itertools import product
2
+ import matplotlib.pyplot as plt
3
+ import scanpy as sc
4
+ import numpy as np
5
+
6
+
7
+ def optimising_umap_layout(adata, cluster_key='leiden',MIN_DISTS = [0.1, 1, 2], SPREADS = [0.5, 1, 5]):
8
+ # https://scanpy-tutorials.readthedocs.io/en/latest/plotting/advanced.html
9
+ # Copy adata not to modify UMAP in the original adata object
10
+ adata_temp = adata.copy()
11
+
12
+ # Create grid of plots, with a little extra room for the legends
13
+ fig, axes = plt.subplots(
14
+ len(MIN_DISTS), len(SPREADS), figsize=(len(SPREADS) * 3 + 2, len(MIN_DISTS) * 3)
15
+ )
16
+
17
+ # Loop through different umap parameters, recomputting and replotting UMAP for each of them
18
+ for (i, min_dist), (j, spread) in product(enumerate(MIN_DISTS), enumerate(SPREADS)):
19
+ ax = axes[i][j]
20
+ param_str = " ".join(["min_dist =", str(min_dist), "and spread =", str(spread)])
21
+ # Recompute UMAP with new parameters
22
+ sc.tl.umap(adata_temp, min_dist=min_dist, spread=spread)
23
+ # Create plot, placing it in grid
24
+ sc.pl.umap(
25
+ adata_temp,
26
+ color=[cluster_key],
27
+ title=param_str,
28
+ s=40,
29
+ ax=ax,
30
+ show=False,
31
+ )
32
+ plt.tight_layout()
33
+ plt.show()
34
+ plt.close()
35
+ del adata_temp
36
+
37
+
38
+ def random_ordering(adata):
39
+ # Randomly order cells by making a random index and subsetting AnnData based on it
40
+ # Set a random seed to ensure that the cell ordering will be reproducible
41
+ np.random.seed(0)
42
+ random_indices = np.random.permutation(list(range(adata.shape[0])))
43
+
44
+ return random_indices
scez/tests/__init__.py ADDED
File without changes
@@ -0,0 +1,18 @@
1
+ import unittest
2
+ import matplotlib.pyplot as plt
3
+ import scanpy as sc
4
+ import scez
5
+ import tomli
6
+
7
+ toml_dict = tomli.load(open('pyproject.toml','rb'))
8
+ version = toml_dict['tool']['poetry']['version']
9
+
10
+ class TestScezConfig(unittest.TestCase):
11
+ def test_version(self):
12
+ self.assertEqual(scez.__version__, version)
13
+
14
+ def test_scanpy_settings(self):
15
+ self.assertEqual(sc.settings.verbosity, 1)
16
+
17
+ if __name__ == '__main__':
18
+ unittest.main()
scez/utils.py ADDED
@@ -0,0 +1,64 @@
1
+ import pandas as pd
2
+ from matplotlib import pyplot as plt
3
+ from adjustText import adjust_text
4
+
5
+
6
+ def rank_genes_to_df(adata, n=50):
7
+ result = adata.uns['rank_genes_groups']
8
+
9
+ groups = result['names'].dtype.names
10
+
11
+ df = pd.DataFrame(
12
+ {group + '_' + key: result[key][group]
13
+ for group in groups for key in ['names', 'scores']}).head(n)
14
+
15
+ return df
16
+
17
+
18
+ def add_marker_feature(adata, marker, marker_name, clusters_name, thr = 0, figsize=(10, 4)):
19
+
20
+ adata.obs[marker_name] = ''
21
+ adata.obs.loc[adata.to_df().loc[:,marker] <= thr, marker_name] = f'{marker}-'
22
+ adata.obs.loc[adata.to_df().loc[:,marker] > thr, marker_name] = f'{marker}+'
23
+
24
+ df = pd.concat([
25
+ adata.obs.groupby([marker_name,clusters_name]).size()[f'{marker}+'],
26
+ adata.obs.groupby([marker_name,clusters_name]).size()[f'{marker}-']
27
+ ],axis=1).rename(columns={0:f'{marker}+',1:f'{marker}-'})
28
+
29
+ # Make some labels.
30
+ labels = df[f'{marker}+'] / df.sum(axis=1) * 100
31
+ labels = labels.round(decimals=1)
32
+ labels.sort_values(ascending=False,inplace=True)
33
+ df = df.loc[labels.index,]
34
+
35
+ ax = df.plot.bar(stacked=True,rot=0,figsize=figsize)
36
+
37
+ rects = ax.patches
38
+
39
+ for rect, label in zip(rects, labels):
40
+ height = rect.get_height()
41
+ ax.text(
42
+ rect.get_x() + rect.get_width() / 2, height + 5, str(label) + "%",
43
+ ha="center", va="bottom", fontsize=8
44
+ )
45
+
46
+ ax.set_yscale('log')
47
+ ax.set_ylabel('# of cells')
48
+ return ax
49
+
50
+
51
+ def run_adjust_text(x, y, labels, ax=None, use_arrow=True, font_weight='bold', font_size=8):
52
+ texts = [
53
+ plt.text(
54
+ x[i], y[i],
55
+ labels[i],
56
+ fontdict={'weight': font_weight, 'size': font_size},
57
+ ha='center', va='center'
58
+ ) for i in range(len(x))
59
+ ]
60
+
61
+ if use_arrow:
62
+ adjust_text(texts, arrowprops=dict(arrowstyle='->', color='red'), ax = ax)
63
+ else:
64
+ adjust_text(texts, ax = ax)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Abolfazl (Abe)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.1
2
+ Name: scez
3
+ Version: 0.0.1
4
+ Summary: Single Cell Analysis, Easy Mode!
5
+ License: MIT
6
+ Author: Abe Arab
7
+ Author-email: abarbiology@gmail.com
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 2
10
+ Classifier: Programming Language :: Python :: 2.7
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.4
13
+ Classifier: Programming Language :: Python :: 3.5
14
+ Classifier: Programming Language :: Python :: 3.6
15
+ Classifier: Programming Language :: Python :: 3.7
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Project-URL: Source, https://github.com/abearab/scez
22
+ Description-Content-Type: text/markdown
23
+
24
+ ## scez – single cell, easy mode
25
+ [![package](https://github.com/abearab/scez/actions/workflows/main.yml/badge.svg)](https://github.com/abearab/scez/actions/workflows/main.yml)
26
+
27
+ ### Installation
28
+ Make sure you have mamba installed in your base environment. If not, install it with:
29
+ ```bash
30
+ conda install mamba -n base -c conda-forge
31
+ ```
32
+ Then, create a new conda environment with the provided `environment.yml` file and activate it. This will install all necessary dependencies for scez.
33
+ ```bash
34
+ conda env create -f environment.yml
35
+
36
+ conda activate scez
37
+ ```
38
+ Finally, install scez with:
39
+
40
+ ```bash
41
+ pip install scez
42
+ ```
43
+
44
+ Or, if you want to install the latest version from the repository:
45
+ ```bash
46
+ pip install git+https://github.com/abearab/scez.git
47
+ ```
48
+
@@ -0,0 +1,11 @@
1
+ scez/__init__.py,sha256=kmN55oxZUHJfsInsnDWvAtxSNn5KnESir65NGr_ssyo,1356
2
+ scez/diffexp.py,sha256=V8qjqpTZVaoAb-iCa3jYmPDmDBtg5AK9OqNzRow6ic0,6770
3
+ scez/preprocess.py,sha256=3iaxACkmKQjKZdaFBLRAXz1kwkNnhbdnd_gwF8rI4nw,2572
4
+ scez/representation.py,sha256=IW0pwa_yoKf-2a3lbtxRwLTdCjB2PkS9oN4WGCLwED0,1596
5
+ scez/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ scez/tests/test_scez.py,sha256=hoBSYB5k1WtF1hY_kyWXw1W7NolBornmTdAng6tDpjw,452
7
+ scez/utils.py,sha256=lPmt01JILiKoBUhhxZeyhl2CfAzailTp4dIefP4FqGQ,1955
8
+ scez-0.0.1.dist-info/LICENSE,sha256=59TS1D5RmGh66RJikXvFoq_ZW9pGirB4zUOUXLfptJM,1071
9
+ scez-0.0.1.dist-info/METADATA,sha256=H5YLkU2mzh80sL9WrXsNX740nA7tTt4NWQFIHi93Kio,1647
10
+ scez-0.0.1.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
11
+ scez-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py2.py3-none-any