synapse-sdk 1.0.0a79__py3-none-any.whl → 1.0.0a80__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 synapse-sdk might be problematic. Click here for more details.

@@ -3,7 +3,7 @@ import json
3
3
  import os
4
4
  import shutil
5
5
  from glob import glob
6
- from typing import Any, Dict
6
+ from typing import Any, Dict, IO
7
7
 
8
8
  from PIL import Image
9
9
  from tqdm import tqdm
@@ -22,14 +22,23 @@ class FromDMToCOCOConverter(FromDMConverter):
22
22
  # 'audio': ['.wav', '.mp3', ...]
23
23
  }
24
24
 
25
- def __init__(self, root_dir, info_dict=None, licenses_list=None, data_type='img', is_categorized_dataset=False):
25
+ def __init__(
26
+ self,
27
+ root_dir=None,
28
+ info_dict=None,
29
+ licenses_list=None,
30
+ data_type='img',
31
+ is_categorized_dataset=False,
32
+ is_single_conversion=False,
33
+ ):
26
34
  """Args:
27
35
  root_dir (str): Root directory containing data.
28
36
  info_dict, licenses_list: COCO metadata.
29
37
  data_type (str): Which data type to use (default: 'img').
30
38
  is_categorized_dataset (bool): Whether to handle train, test, valid splits.
39
+ is_single_conversion (bool): Whether to use single file conversion mode.
31
40
  """
32
- super().__init__(root_dir, is_categorized_dataset)
41
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
33
42
  self.data_type = data_type
34
43
  self.info_dict = info_dict or self._default_info()
35
44
  self.licenses_list = licenses_list or self._default_licenses()
@@ -267,3 +276,47 @@ class FromDMToCOCOConverter(FromDMConverter):
267
276
  shutil.copy(src_path, dst_path)
268
277
  else:
269
278
  print(f'[WARNING] Image not found: {src_path}')
279
+
280
+ def convert_single_file(self, data: Dict[str, Any], original_file: IO) -> Dict[str, Any]:
281
+ """Convert a single DM data dict and corresponding image file object to COCO format.
282
+
283
+ Args:
284
+ data: DM format data dictionary (JSON content)
285
+ original_file: File object for the corresponding original image
286
+
287
+ Returns:
288
+ Dictionary containing COCO format data for the single file
289
+ """
290
+ if not self.is_single_conversion:
291
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
292
+
293
+ self.reset_state()
294
+ self.coco_dict = {
295
+ 'info': self.info_dict,
296
+ 'licenses': self.licenses_list,
297
+ 'images': [],
298
+ 'annotations': [],
299
+ 'categories': [],
300
+ }
301
+
302
+ # Process the image file
303
+ with Image.open(original_file) as im:
304
+ width, height = im.size
305
+
306
+ image_info = {
307
+ 'id': self.img_id,
308
+ 'file_name': getattr(original_file, 'name', 'image.jpg'),
309
+ 'width': width,
310
+ 'height': height,
311
+ 'license': self.license_id,
312
+ }
313
+ self.coco_dict['images'].append(image_info)
314
+
315
+ # Process annotations from the first (and only) image in data
316
+ if 'images' in data and len(data['images']) > 0:
317
+ anns = data['images'][0]
318
+ self._process_polylines(anns)
319
+ self._process_bboxes(anns)
320
+ self._process_keypoints(anns)
321
+
322
+ return self.coco_dict
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import os
3
+ from typing import IO, Any, Dict
3
4
 
4
5
  from synapse_sdk.utils.converters import ToDMConverter
5
6
 
@@ -7,6 +8,9 @@ from synapse_sdk.utils.converters import ToDMConverter
7
8
  class COCOToDMConverter(ToDMConverter):
8
9
  """Convert COCO format annotations to DM (Data Manager) format."""
9
10
 
11
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
12
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
13
+
10
14
  def convert(self):
11
15
  if self.is_categorized_dataset:
12
16
  splits = self._validate_splits(['train', 'valid'], ['test'])
@@ -111,3 +115,101 @@ class COCOToDMConverter(ToDMConverter):
111
115
  dm_json = {'images': [dm_img]}
112
116
  result[img_filename] = (dm_json, img_path)
113
117
  return result
118
+
119
+ def convert_single_file(self, data: Dict[str, Any], original_file: IO) -> Dict[str, Any]:
120
+ """Convert a single COCO annotation data and corresponding image to DM format.
121
+
122
+ Args:
123
+ data: COCO format data dictionary (JSON content)
124
+ original_file: File object for the corresponding original image
125
+
126
+ Returns:
127
+ Dictionary containing DM format data for the single file
128
+ """
129
+ if not self.is_single_conversion:
130
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
131
+
132
+ images = data.get('images', [])
133
+ annotations = data.get('annotations', [])
134
+ categories = data.get('categories', [])
135
+
136
+ if not images:
137
+ raise ValueError('No images found in COCO data')
138
+
139
+ # Get file name from original_file
140
+ img_path = getattr(original_file, 'name', None)
141
+ if not img_path:
142
+ raise ValueError('original_file must have a "name" attribute representing its path or filename.')
143
+ img_basename = os.path.basename(img_path)
144
+
145
+ # Find the matching image info in COCO 'images' section by comparing file name
146
+ # COCO image dicts might use 'file_name', 'filename', or similar
147
+ matched_img = None
148
+ for img in images:
149
+ for key in ['file_name', 'filename', 'name']:
150
+ if key in img and os.path.basename(img[key]) == img_basename:
151
+ matched_img = img
152
+ break
153
+ if matched_img:
154
+ break
155
+
156
+ if not matched_img:
157
+ raise ValueError(f'No matching image found in COCO data for file: {img_basename}')
158
+
159
+ img_id = matched_img['id']
160
+ print('img_id : ', img_id)
161
+ cat_map = {cat['id']: cat for cat in categories}
162
+ anns = [ann for ann in annotations if ann['image_id'] == img_id]
163
+
164
+ dm_img = {
165
+ 'bounding_box': [],
166
+ 'keypoint': [],
167
+ 'relation': [],
168
+ 'group': [],
169
+ }
170
+
171
+ bbox_ids = []
172
+ for ann in anns:
173
+ cat = cat_map.get(ann['category_id'], {})
174
+ if 'bbox' in ann and ann['bbox']:
175
+ bbox_id = self._generate_unique_id()
176
+ bbox_ids.append(bbox_id)
177
+ dm_img['bounding_box'].append({
178
+ 'id': bbox_id,
179
+ 'classification': cat.get('name', str(ann['category_id'])),
180
+ 'attrs': ann.get('attrs', []),
181
+ 'data': list(ann['bbox']),
182
+ })
183
+
184
+ for ann in anns:
185
+ cat = cat_map.get(ann['category_id'], {})
186
+ attrs = ann.get('attrs', [])
187
+ if 'keypoints' in ann and ann['keypoints']:
188
+ kp_names = cat.get('keypoints', [])
189
+ kps = ann['keypoints']
190
+ keypoint_ids = []
191
+ for idx in range(min(len(kps) // 3, len(kp_names))):
192
+ x, y, _ = kps[idx * 3 : idx * 3 + 3]
193
+ kp_id = self._generate_unique_id()
194
+ keypoint_ids.append(kp_id)
195
+ dm_img['keypoint'].append({
196
+ 'id': kp_id,
197
+ 'classification': kp_names[idx] if idx < len(kp_names) else f'keypoint_{idx}',
198
+ 'attrs': attrs,
199
+ 'data': [x, y],
200
+ })
201
+ group_ids = bbox_ids + keypoint_ids
202
+ if group_ids:
203
+ dm_img['group'].append({
204
+ 'id': self._generate_unique_id(),
205
+ 'classification': cat.get('name', str(ann['category_id'])),
206
+ 'attrs': attrs,
207
+ 'data': group_ids,
208
+ })
209
+
210
+ dm_json = {'images': [dm_img]}
211
+ return {
212
+ 'dm_json': dm_json,
213
+ 'image_path': img_path,
214
+ 'image_name': img_basename,
215
+ }
@@ -3,7 +3,7 @@ import os
3
3
  import shutil
4
4
  import xml.etree.ElementTree as ET
5
5
  from glob import glob
6
- from typing import Any, List, Optional
6
+ from typing import Any, Dict, IO, List, Optional
7
7
 
8
8
  from PIL import Image
9
9
 
@@ -15,8 +15,8 @@ class FromDMToPascalConverter(FromDMConverter):
15
15
 
16
16
  IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
17
17
 
18
- def __init__(self, root_dir: str, is_categorized_dataset: bool = False):
19
- super().__init__(root_dir, is_categorized_dataset)
18
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
19
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
20
20
  self.class_names = set()
21
21
 
22
22
  def find_image_for_base(self, img_dir: str, base: str) -> Optional[str]:
@@ -175,3 +175,48 @@ class FromDMToPascalConverter(FromDMConverter):
175
175
  for c in sorted(self.class_names):
176
176
  f.write(f'{c}\n')
177
177
  print(f'Pascal VOC data exported to {outdir}')
178
+
179
+ def convert_single_file(self, data: Dict[str, Any], original_file: IO) -> Dict[str, Any]:
180
+ """Convert a single DM data dict and corresponding image file object to Pascal VOC format.
181
+
182
+ Args:
183
+ data: DM format data dictionary (JSON content)
184
+ original_file: File object for the corresponding original image
185
+
186
+ Returns:
187
+ Dictionary containing Pascal VOC format data for the single file
188
+ """
189
+ if not self.is_single_conversion:
190
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
191
+
192
+ # Extract image info from file object
193
+ with Image.open(original_file) as img:
194
+ width, height = img.size
195
+ depth = len(img.getbands())
196
+
197
+ # Get filename from original_file
198
+ img_filename = getattr(original_file, 'name', 'image.jpg')
199
+ if img_filename:
200
+ img_filename = os.path.basename(img_filename)
201
+
202
+ # Process annotations from the first (and only) image in data
203
+ if 'images' in data and len(data['images']) > 0:
204
+ img_ann = data['images'][0]
205
+ objects = self.parse_dm_annotations(img_ann)
206
+ else:
207
+ objects = []
208
+
209
+ # Build Pascal VOC XML
210
+ xml_tree = self.build_pascal_xml(img_filename, (width, height, depth), objects)
211
+ xml_filename = os.path.splitext(img_filename)[0] + '.xml'
212
+
213
+ # Convert XML tree to string for easy viewing
214
+ xml_string = ET.tostring(xml_tree.getroot(), encoding='unicode', xml_declaration=True)
215
+
216
+ return {
217
+ 'xml_tree': xml_tree,
218
+ 'xml_content': xml_string,
219
+ 'xml_filename': xml_filename,
220
+ 'image_filename': img_filename,
221
+ 'class_names': sorted(list(self.class_names)),
222
+ }
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import xml.etree.ElementTree as ET
3
- from typing import Any, Dict, List, Optional, Tuple
3
+ from typing import Any, Dict, IO, List, Optional, Tuple
4
4
 
5
5
  from PIL import Image
6
6
 
@@ -12,6 +12,9 @@ class PascalToDMConverter(ToDMConverter):
12
12
 
13
13
  IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
14
14
 
15
+ def __init__(self, root_dir: str = None, is_categorized_dataset: bool = False, is_single_conversion: bool = False):
16
+ super().__init__(root_dir, is_categorized_dataset, is_single_conversion)
17
+
15
18
  def convert(self):
16
19
  """Convert the Pascal VOC dataset to DM format."""
17
20
  if self.is_categorized_dataset:
@@ -133,3 +136,79 @@ class PascalToDMConverter(ToDMConverter):
133
136
  print(f'[WARNING] Error processing {xml_filename}: {e}, skipping.')
134
137
  continue
135
138
  return result
139
+
140
+ def convert_single_file(self, data: str, original_file: IO) -> Dict[str, Any]:
141
+ """Convert a single Pascal VOC XML data and corresponding image to DM format.
142
+
143
+ Args:
144
+ data: Pascal VOC XML content as string
145
+ original_file: File object for the corresponding original image
146
+
147
+ Returns:
148
+ Dictionary containing DM format data for the single file
149
+ """
150
+ if not self.is_single_conversion:
151
+ raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
152
+
153
+ # Get filename from original_file
154
+ img_path = getattr(original_file, 'name', None)
155
+ if not img_path:
156
+ raise ValueError('original_file must have a "name" attribute representing its path or filename.')
157
+
158
+ img_filename = os.path.basename(img_path)
159
+
160
+ # Parse XML data from string
161
+ try:
162
+ root = ET.fromstring(data)
163
+ except ET.ParseError as e:
164
+ raise ValueError(f'Failed to parse Pascal VOC XML data: {e}')
165
+
166
+ # Extract objects from XML
167
+ objects = []
168
+ for obj in root.findall('object'):
169
+ name_elem = obj.find('name')
170
+ bndbox_elem = obj.find('bndbox')
171
+ if name_elem is None or bndbox_elem is None:
172
+ continue
173
+
174
+ class_name = name_elem.text
175
+ xmin_elem = bndbox_elem.find('xmin')
176
+ ymin_elem = bndbox_elem.find('ymin')
177
+ xmax_elem = bndbox_elem.find('xmax')
178
+ ymax_elem = bndbox_elem.find('ymax')
179
+
180
+ if any(elem is None for elem in [xmin_elem, ymin_elem, xmax_elem, ymax_elem]):
181
+ continue
182
+
183
+ xmin = int(float(xmin_elem.text))
184
+ ymin = int(float(ymin_elem.text))
185
+ xmax = int(float(xmax_elem.text))
186
+ ymax = int(float(ymax_elem.text))
187
+ width = xmax - xmin
188
+ height = ymax - ymin
189
+
190
+ objects.append({'classification': class_name, 'data': [xmin, ymin, width, height]})
191
+
192
+ # Prepare DM annotation structure
193
+ dm_img = {
194
+ 'bounding_box': [],
195
+ 'polygon': [],
196
+ 'keypoint': [],
197
+ 'relation': [],
198
+ 'group': [],
199
+ }
200
+
201
+ for obj in objects:
202
+ dm_img['bounding_box'].append({
203
+ 'id': self._generate_unique_id(),
204
+ 'classification': obj['classification'],
205
+ 'attrs': [],
206
+ 'data': obj['data'],
207
+ })
208
+
209
+ dm_json = {'images': [dm_img]}
210
+ return {
211
+ 'dm_json': dm_json,
212
+ 'image_path': img_path,
213
+ 'image_name': img_filename,
214
+ }
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Any, Dict, List, Tuple
2
+ from typing import IO, Any, Dict, List, Tuple
3
3
 
4
4
  import yaml
5
5
  from PIL import Image
@@ -187,12 +187,12 @@ class YOLOToDMConverter(ToDMConverter):
187
187
  result[os.path.basename(img_path)] = (dm_json, img_path)
188
188
  return result
189
189
 
190
- def convert_single_file(self, data: List[str], original_file_path: str, class_names: List[str]) -> Dict[str, Any]:
190
+ def convert_single_file(self, data: List[str], original_file: IO, class_names: List[str]) -> Dict[str, Any]:
191
191
  """Convert a single YOLO label data and corresponding image to DM format.
192
192
 
193
193
  Args:
194
194
  data: List of YOLO label lines (strings from .txt file content)
195
- original_file_path: Path to the corresponding original file
195
+ original_file: File object for the corresponding original image
196
196
  class_names: List of class names corresponding to indices in the label data
197
197
 
198
198
  Returns:
@@ -201,10 +201,14 @@ class YOLOToDMConverter(ToDMConverter):
201
201
  if not self.is_single_conversion:
202
202
  raise RuntimeError('convert_single_file is only available when is_single_conversion=True')
203
203
 
204
- if not os.path.exists(original_file_path):
205
- raise FileNotFoundError(f'Original file not found: {original_file_path}')
204
+ img_path = getattr(original_file, 'name', None)
205
+ if not img_path:
206
+ raise ValueError('original_file must have a "name" attribute representing its path or filename.')
206
207
 
207
- img_size = self._get_image_size(original_file_path)
208
+ if hasattr(self, '_get_image_size'):
209
+ img_size = self._get_image_size(original_file)
210
+ else:
211
+ raise AttributeError('Converter missing _get_image_size method for file objects.')
208
212
 
209
213
  # data is already a list of label lines
210
214
  label_lines = [line.strip() for line in data if line.strip()]
@@ -249,6 +253,6 @@ class YOLOToDMConverter(ToDMConverter):
249
253
  dm_json = {'images': [dm_img]}
250
254
  return {
251
255
  'dm_json': dm_json,
252
- 'image_path': original_file_path,
253
- 'image_name': os.path.basename(original_file_path),
256
+ 'image_path': img_path,
257
+ 'image_name': os.path.basename(img_path),
254
258
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synapse-sdk
3
- Version: 1.0.0a79
3
+ Version: 1.0.0a80
4
4
  Summary: synapse sdk
5
5
  Author-email: datamaker <developer@datamaker.io>
6
6
  License: MIT
@@ -198,17 +198,17 @@ synapse_sdk/utils/network.py,sha256=WI8qn6KlKpHdMi45V57ofKJB8zusJrbQsxT74LwVfsY,
198
198
  synapse_sdk/utils/string.py,sha256=rEwuZ9SAaZLcQ8TYiwNKr1h2u4CfnrQx7SUL8NWmChg,216
199
199
  synapse_sdk/utils/converters/__init__.py,sha256=xQi_n7xS9BNyDiolsxH2jw1CtD6avxMPj2cHnwvidi8,11311
200
200
  synapse_sdk/utils/converters/coco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
- synapse_sdk/utils/converters/coco/from_dm.py,sha256=78aJ_O2_hmkQQ96nrjHY38roETWfBZB8GRHDn7qKEls,9626
202
- synapse_sdk/utils/converters/coco/to_dm.py,sha256=Ve8LrcKVlzNysam3fidcgP5fdm0_UGbBgSPoj2dT_JA,4906
201
+ synapse_sdk/utils/converters/coco/from_dm.py,sha256=B9zvb8Kph9haYLVIZhzneWiHCImFbuWqAaE7g6Nk0lI,11365
202
+ synapse_sdk/utils/converters/coco/to_dm.py,sha256=YmD_NHKSUL8RZbzWX52FgDaJG0uX4I8f8Omp7ilhSec,9054
203
203
  synapse_sdk/utils/converters/dm/__init__.py,sha256=cHvpr4ljImnqeMvi5sevQiBD-R9ZHBWOpoKUUCiaGHE,3333
204
204
  synapse_sdk/utils/converters/dm/from_v1.py,sha256=wlFwGRD21nTAk0TVwFDxb-2w9Q9eQWJ1MBo-7CQcgKQ,16572
205
205
  synapse_sdk/utils/converters/dm/to_v1.py,sha256=jHeQoopht1lyPUsLK8T0xYK6aHVVYvS6JHiesSaPPk0,9430
206
206
  synapse_sdk/utils/converters/pascal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
207
- synapse_sdk/utils/converters/pascal/from_dm.py,sha256=PvP70jvzCbUKJg49BikgSpc5qb7nvVvfDDFmf1oPJWc,8497
208
- synapse_sdk/utils/converters/pascal/to_dm.py,sha256=RBMlVOdc7ev7jEWj7eyoNoO_1cpdhrJ7LhJqaboxBS4,5751
207
+ synapse_sdk/utils/converters/pascal/from_dm.py,sha256=AKOeQoyeSbSOawf-ya9dLx-pZP_MomNcDaCW5ka5_8Y,10378
208
+ synapse_sdk/utils/converters/pascal/to_dm.py,sha256=JkA_OI_IR1ealZPe2uo4hFBcFyOh_VfeyIY43-R4IBA,8614
209
209
  synapse_sdk/utils/converters/yolo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
210
  synapse_sdk/utils/converters/yolo/from_dm.py,sha256=-JDCQLk4g1_FIVoOwZ1Tcs2kWFkhXRCAPVLKLXz6sLU,16180
211
- synapse_sdk/utils/converters/yolo/to_dm.py,sha256=yRCcgnLbejga1cyAVflY4SBXeYYcbK0b8DiNllPb0so,10275
211
+ synapse_sdk/utils/converters/yolo/to_dm.py,sha256=UUGTbBNeG5Ao8PSJbizrZRQJfeAMuSDfZs8SGOyr-YU,10464
212
212
  synapse_sdk/utils/pydantic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  synapse_sdk/utils/pydantic/config.py,sha256=1vYOcUI35GslfD1rrqhFkNXXJOXt4IDqOPSx9VWGfNE,123
214
214
  synapse_sdk/utils/pydantic/errors.py,sha256=0v0T12eQBr1KrFiEOBu6KMaPK4aPEGEC6etPJGoR5b4,1061
@@ -220,9 +220,9 @@ synapse_sdk/utils/storage/providers/gcp.py,sha256=i2BQCu1Kej1If9SuNr2_lEyTcr5M_n
220
220
  synapse_sdk/utils/storage/providers/http.py,sha256=2DhIulND47JOnS5ZY7MZUex7Su3peAPksGo1Wwg07L4,5828
221
221
  synapse_sdk/utils/storage/providers/s3.py,sha256=ZmqekAvIgcQBdRU-QVJYv1Rlp6VHfXwtbtjTSphua94,2573
222
222
  synapse_sdk/utils/storage/providers/sftp.py,sha256=_8s9hf0JXIO21gvm-JVS00FbLsbtvly4c-ETLRax68A,1426
223
- synapse_sdk-1.0.0a79.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
224
- synapse_sdk-1.0.0a79.dist-info/METADATA,sha256=ZpqySA8CqPd1Guvz_q0z8xqNrrrz1mKSVNukdAnNiwU,3805
225
- synapse_sdk-1.0.0a79.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
226
- synapse_sdk-1.0.0a79.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
227
- synapse_sdk-1.0.0a79.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
228
- synapse_sdk-1.0.0a79.dist-info/RECORD,,
223
+ synapse_sdk-1.0.0a80.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
224
+ synapse_sdk-1.0.0a80.dist-info/METADATA,sha256=Z9-Wpg7L2jCgXLMrp_BaJ2Hjlg_IQMBCBOBcpLZZBnQ,3805
225
+ synapse_sdk-1.0.0a80.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
226
+ synapse_sdk-1.0.0a80.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
227
+ synapse_sdk-1.0.0a80.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
228
+ synapse_sdk-1.0.0a80.dist-info/RECORD,,