supervisely 6.73.323__py3-none-any.whl → 6.73.324__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.
supervisely/_utils.py CHANGED
@@ -471,9 +471,20 @@ def get_or_create_event_loop() -> asyncio.AbstractEventLoop:
471
471
  return loop
472
472
 
473
473
 
474
- def sync_call(coro):
474
+ def run_coroutine(coroutine):
475
475
  """
476
- This function is used to run asynchronous functions in synchronous context.
476
+ Runs an asynchronous coroutine in a synchronous context and waits for its result.
477
+
478
+ This function checks if an event loop is already running:
479
+ - If a loop is running, it schedules the coroutine using `asyncio.run_coroutine_threadsafe()`
480
+ and waits for the result.
481
+ - If no loop is running, it creates one and executes the coroutine with `run_until_complete()`.
482
+
483
+ This ensures compatibility with both synchronous and asynchronous environments
484
+ without creating unnecessary event loops.
485
+
486
+ ⚠️ Note: This method is preferable when working with `asyncio` objects like `Semaphore`,
487
+ since it avoids issues with mismatched event loops.
477
488
 
478
489
  :param coro: Asynchronous function.
479
490
  :type coro: Coroutine
@@ -484,13 +495,13 @@ def sync_call(coro):
484
495
 
485
496
  .. code-block:: python
486
497
 
487
- from supervisely.utils import sync_call
498
+ from supervisely._utils import run_coroutine
488
499
 
489
500
  async def async_function():
490
501
  await asyncio.sleep(1)
491
502
  return "Hello, World!"
492
503
  coro = async_function()
493
- result = sync_call(coro)
504
+ result = run_coroutine(coro)
494
505
  print(result)
495
506
  # Output: Hello, World!
496
507
  """
@@ -498,10 +509,10 @@ def sync_call(coro):
498
509
  loop = get_or_create_event_loop()
499
510
 
500
511
  if loop.is_running():
501
- future = asyncio.run_coroutine_threadsafe(coro, loop=loop)
512
+ future = asyncio.run_coroutine_threadsafe(coroutine, loop=loop)
502
513
  return future.result()
503
514
  else:
504
- return loop.run_until_complete(coro)
515
+ return loop.run_until_complete(coroutine)
505
516
 
506
517
 
507
518
  def get_filename_from_headers(url):
@@ -23,7 +23,7 @@ from typing_extensions import Literal
23
23
 
24
24
  import supervisely.io.env as env
25
25
  import supervisely.io.fs as sly_fs
26
- from supervisely._utils import batched, rand_str
26
+ from supervisely._utils import batched, rand_str, run_coroutine
27
27
  from supervisely.api.module_api import ApiField, ModuleApiBase
28
28
  from supervisely.io.fs import (
29
29
  ensure_base_path,
@@ -2302,3 +2302,89 @@ class FileApi(ModuleApiBase):
2302
2302
  else:
2303
2303
  raise e
2304
2304
  return res_remote_dir
2305
+
2306
+ def upload_directory_fast(
2307
+ self,
2308
+ team_id: int,
2309
+ local_dir: str,
2310
+ remote_dir: str,
2311
+ change_name_if_conflict: Optional[bool] = True,
2312
+ progress_cb: Optional[Union[tqdm, Callable]] = None,
2313
+ replace_if_conflict: Optional[bool] = False,
2314
+ enable_fallback: Optional[bool] = True,
2315
+ ) -> str:
2316
+ """
2317
+ Upload Directory to Team Files from local path in fast mode.
2318
+ Files are uploaded asynchronously. If an error occurs, the method will fallback to synchronous upload.
2319
+
2320
+ :param team_id: Team ID in Supervisely.
2321
+ :type team_id: int
2322
+ :param local_dir: Path to local Directory.
2323
+ :type local_dir: str
2324
+ :param remote_dir: Path to Directory in Team Files.
2325
+ :type remote_dir: str
2326
+ :param change_name_if_conflict: Checks if given name already exists and adds suffix to the end of the name.
2327
+ :type change_name_if_conflict: bool, optional
2328
+ :param progress_cb: Function for tracking download progress in bytes.
2329
+ :type progress_cb: Progress, optional
2330
+ :param replace_if_conflict: If True, replace existing dir.
2331
+ :type replace_if_conflict: bool, optional
2332
+ :param enable_fallback: If True, the method will fallback to synchronous upload if an error occurs.
2333
+ :type enable_fallback: bool, optional
2334
+ :return: Path to Directory in Team Files
2335
+ :rtype: :class:`str`
2336
+ """
2337
+ coroutine = self.upload_directory_async(
2338
+ team_id=team_id,
2339
+ local_dir=local_dir,
2340
+ remote_dir=remote_dir,
2341
+ change_name_if_conflict=change_name_if_conflict,
2342
+ progress_size_cb=progress_cb,
2343
+ replace_if_conflict=replace_if_conflict,
2344
+ enable_fallback=enable_fallback,
2345
+ )
2346
+ return run_coroutine(coroutine)
2347
+
2348
+ def upload_bulk_fast(
2349
+ self,
2350
+ team_id: int,
2351
+ src_paths: List[str],
2352
+ dst_paths: List[str],
2353
+ semaphore: Optional[asyncio.Semaphore] = None,
2354
+ progress_cb: Optional[Union[tqdm, Callable]] = None,
2355
+ progress_cb_type: Literal["number", "size"] = "size",
2356
+ enable_fallback: Optional[bool] = True,
2357
+ ) -> None:
2358
+ """
2359
+ Upload multiple files from local paths to Team Files in fast mode.
2360
+ Files are uploaded asynchronously. If an error occurs, the method will fallback to synchronous upload.
2361
+
2362
+ :param team_id: Team ID in Supervisely.
2363
+ :type team_id: int
2364
+ :param src_paths: List of local paths to files.
2365
+ :type src_paths: List[str]
2366
+ :param dst_paths: List of paths to save files in Team Files.
2367
+ :type dst_paths: List[str]
2368
+ :param semaphore: Semaphore for limiting the number of simultaneous uploads.
2369
+ :type semaphore: asyncio.Semaphore, optional
2370
+ :param progress_cb: Function for tracking download progress.
2371
+ :type progress_cb: tqdm or callable, optional
2372
+ :param progress_cb_type: Type of progress callback. Can be "number" or "size". Default is "size".
2373
+ "size" is used to track the number of transferred bytes.
2374
+ "number" is used to track the number of transferred files.
2375
+ :type progress_cb_type: Literal["number", "size"], optional
2376
+ :param enable_fallback: If True, the method will fallback to synchronous upload if an error occurs.
2377
+ :type enable_fallback: bool, optional
2378
+ :return: None
2379
+ :rtype: :class:`NoneType`
2380
+ """
2381
+ coroutine = self.upload_bulk_async(
2382
+ team_id=team_id,
2383
+ src_paths=src_paths,
2384
+ dst_paths=dst_paths,
2385
+ semaphore=semaphore,
2386
+ progress_cb=progress_cb,
2387
+ progress_cb_type=progress_cb_type,
2388
+ enable_fallback=enable_fallback,
2389
+ )
2390
+ return run_coroutine(coroutine)
@@ -3,6 +3,20 @@ import concurrent.futures
3
3
 
4
4
 
5
5
  def run_sync(coroutine):
6
+ """
7
+ Runs an asynchronous coroutine in a separate thread and waits for its result.
8
+ It is useful for running async functions in a synchronous
9
+ environment.
10
+
11
+ This method creates a new thread using ThreadPoolExecutor and executes the coroutine
12
+ inside a new event loop.
13
+
14
+ ⚠️ Note: This function creates a new event loop every time it is called,
15
+ which can cause issues when using objects tied to a specific loop (e.g., asyncio.Semaphore).
16
+
17
+ :param coroutine: coroutine to run
18
+ :return: result of coroutine
19
+ """
6
20
  try:
7
21
  with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
8
22
  result = executor.submit(
@@ -39,7 +39,7 @@ from supervisely import (
39
39
  is_production,
40
40
  logger,
41
41
  )
42
- from supervisely._utils import abs_url, get_filename_from_headers, sync_call
42
+ from supervisely._utils import abs_url, get_filename_from_headers
43
43
  from supervisely.api.file_api import FileInfo
44
44
  from supervisely.app import get_synced_data_dir
45
45
  from supervisely.app.widgets import Progress
@@ -60,7 +60,7 @@ from supervisely.nn.utils import ModelSource
60
60
  from supervisely.output import set_directory
61
61
  from supervisely.project.download import (
62
62
  copy_from_cache,
63
- download_async_or_sync,
63
+ download_fast,
64
64
  download_to_cache,
65
65
  get_cache_size,
66
66
  is_cached,
@@ -806,7 +806,7 @@ class TrainApp:
806
806
  with self.progress_bar_main(message="Downloading input data", total=total_images) as pbar:
807
807
  logger.debug("Downloading project data without cache")
808
808
  self.progress_bar_main.show()
809
- download_async_or_sync(
809
+ download_fast(
810
810
  api=self._api,
811
811
  project_id=self.project_id,
812
812
  dest_dir=self.project_dir,
@@ -1619,14 +1619,13 @@ class TrainApp:
1619
1619
  unit_scale=True,
1620
1620
  ) as upload_artifacts_pbar:
1621
1621
  self.progress_bar_main.show()
1622
- remote_dir = sync_call(
1623
- self._api.file.upload_directory_async(
1624
- team_id=self.team_id,
1625
- local_dir=local_demo_dir,
1626
- remote_dir=remote_demo_dir,
1627
- progress_size_cb=upload_artifacts_pbar.update,
1628
- )
1622
+ remote_dir = self._api.file.upload_directory_fast(
1623
+ team_id=self.team_id,
1624
+ local_dir=local_demo_dir,
1625
+ remote_dir=remote_demo_dir,
1626
+ progress_cb=upload_artifacts_pbar.update,
1629
1627
  )
1628
+
1630
1629
  self.progress_bar_main.hide()
1631
1630
 
1632
1631
  def _get_train_val_splits_for_app_state(self) -> Dict:
@@ -1733,13 +1732,11 @@ class TrainApp:
1733
1732
  unit_scale=True,
1734
1733
  ) as upload_artifacts_pbar:
1735
1734
  self.progress_bar_main.show()
1736
- remote_dir = sync_call(
1737
- self._api.file.upload_directory_async(
1738
- team_id=self.team_id,
1739
- local_dir=self.output_dir,
1740
- remote_dir=remote_artifacts_dir,
1741
- progress_size_cb=upload_artifacts_pbar.update,
1742
- )
1735
+ remote_dir = self._api.file.upload_directory_fast(
1736
+ team_id=self.team_id,
1737
+ local_dir=self.output_dir,
1738
+ remote_dir=remote_artifacts_dir,
1739
+ progress_cb=upload_artifacts_pbar.update,
1743
1740
  )
1744
1741
  self.progress_bar_main.hide()
1745
1742
 
@@ -2524,13 +2521,11 @@ class TrainApp:
2524
2521
  logger.debug(f"Uploading {len(export_weights)} export weights of size {size} bytes")
2525
2522
  logger.debug(f"Destination paths: {file_dest_paths}")
2526
2523
  self.progress_bar_main.show()
2527
- sync_call(
2528
- self._api.file.upload_bulk_async(
2529
- team_id=self.team_id,
2530
- src_paths=export_weights.values(),
2531
- dst_paths=file_dest_paths,
2532
- progress_cb=export_upload_main_pbar.update,
2533
- )
2524
+ self._api.file.upload_bulk_fast(
2525
+ team_id=self.team_id,
2526
+ src_paths=export_weights.values(),
2527
+ dst_paths=file_dest_paths,
2528
+ progress_cb=export_upload_main_pbar.update,
2534
2529
  )
2535
2530
 
2536
2531
  self.progress_bar_main.hide()
@@ -281,6 +281,34 @@ def download_async_or_sync(
281
281
  )
282
282
 
283
283
 
284
+ def download_fast(
285
+ api: Api,
286
+ project_id: int,
287
+ dest_dir: str,
288
+ dataset_ids: Optional[List[int]] = None,
289
+ log_progress: bool = True,
290
+ progress_cb: Optional[Union[tqdm, Callable]] = None,
291
+ semaphore: Optional[asyncio.Semaphore] = None,
292
+ **kwargs,
293
+ ) -> None:
294
+ """
295
+ Download project in a fast mode.
296
+ Items are downloaded asynchronously. If an error occurs, the method will fallback to synchronous download.
297
+ Automatically detects project type.
298
+ You can pass :class:`ProjectInfo` as `project_info` kwarg to avoid additional API requests.
299
+ """
300
+ download_async_or_sync(
301
+ api=api,
302
+ project_id=project_id,
303
+ dest_dir=dest_dir,
304
+ dataset_ids=dataset_ids,
305
+ log_progress=log_progress,
306
+ progress_cb=progress_cb,
307
+ semaphore=semaphore,
308
+ **kwargs,
309
+ )
310
+
311
+
284
312
  def _get_cache_dir(project_id: int, dataset_path: str = None) -> str:
285
313
  p = os.path.join(apps_cache_dir(), str(project_id))
286
314
  if dataset_path is not None:
@@ -468,7 +496,7 @@ def _download_project_to_cache(
468
496
  if len(dataset_infos) == 0:
469
497
  logger.debug("No datasets to download")
470
498
  return
471
- download_async_or_sync(
499
+ download_fast(
472
500
  api=api,
473
501
  project_id=project_id,
474
502
  dest_dir=cached_project_dir,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.323
3
+ Version: 6.73.324
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely
@@ -1,6 +1,6 @@
1
1
  supervisely/README.md,sha256=XM-DiMC6To3I9RjQZ0c61905EFRR_jnCUx2q3uNR-X8,3331
2
2
  supervisely/__init__.py,sha256=mtgVKiRSlnRU7yKG0Re130mBL10wCzsNfOfi-w-Kj4c,10833
3
- supervisely/_utils.py,sha256=hYzGRVAh-cB2RmqixHbaJQZHy4byNip4KZm2Gdt8P7k,16849
3
+ supervisely/_utils.py,sha256=KWfbw8XFfV2uxLzhuku_J0UQKpG-D80Hp6UOQD316P8,17460
4
4
  supervisely/function_wrapper.py,sha256=R5YajTQ0GnRp2vtjwfC9hINkzQc0JiyGsu8TER373xY,1912
5
5
  supervisely/sly_logger.py,sha256=z92Vu5hmC0GgTIJO1n6kPDayRW9__8ix8hL6poDZj-Y,6274
6
6
  supervisely/tiny_timer.py,sha256=hkpe_7FE6bsKL79blSs7WBaktuPavEVu67IpEPrfmjE,183
@@ -25,7 +25,7 @@ supervisely/api/annotation_api.py,sha256=kuk4qwojTJxYr2iqAKbW-QhWw_DFc4TsjA2Wc2M
25
25
  supervisely/api/api.py,sha256=6TczKT1t0MWlbArSW31RmeyWP04pqngfUO_NrG5FETE,66287
26
26
  supervisely/api/app_api.py,sha256=RsbVej8WxWVn9cNo5s3Fqd1symsCdsfOaKVBKEUapRY,71927
27
27
  supervisely/api/dataset_api.py,sha256=GH7prDRJKyJlTv_7_Y-RkTwJN7ED4EkXNqqmi3iIdI4,41352
28
- supervisely/api/file_api.py,sha256=xVM4fFeIc52aKnxduCIU7L6Rgd7Rh36rzTJ8hVT8hw4,88925
28
+ supervisely/api/file_api.py,sha256=S2xZAy36YzWA1R41SIlLXX9JwSf9ax18AIAIQGgHGlA,92801
29
29
  supervisely/api/github_api.py,sha256=NIexNjEer9H5rf5sw2LEZd7C1WR-tK4t6IZzsgeAAwQ,623
30
30
  supervisely/api/image_annotation_tool_api.py,sha256=YcUo78jRDBJYvIjrd-Y6FJAasLta54nnxhyaGyanovA,5237
31
31
  supervisely/api/image_api.py,sha256=WIML_6N1qgOWBm3acexmGSWz4hAaSxlYmUtbytROaP8,192375
@@ -95,7 +95,7 @@ supervisely/app/fastapi/offline.py,sha256=CwMMkJ1frD6wiZS-SEoNDtQ1UJcJe1Ob6ohE3r
95
95
  supervisely/app/fastapi/request.py,sha256=NU7rKmxJ1pfkDZ7_yHckRcRAueJRQIqCor11UO2OHr8,766
96
96
  supervisely/app/fastapi/subapp.py,sha256=5lMfFLYBfHzE1OmITHsogB9hScyTJFjGV45AKY67Hkg,45647
97
97
  supervisely/app/fastapi/templating.py,sha256=JOAW8U-14GD47E286mzFi3mZSPbm_csJGqtXWLRM4rc,2929
98
- supervisely/app/fastapi/utils.py,sha256=GZuTWLcVRGVx8TL3jVEYUOZIT2FawbwIe2kAOBLw9ho,398
98
+ supervisely/app/fastapi/utils.py,sha256=t_UquzlFrdkKtAJmH6eJ279pE8Aa3BaIu4XjX-SEaIE,946
99
99
  supervisely/app/fastapi/websocket.py,sha256=TlRSPOAhRItTv1HGvdukK1ZvhRjMUxRa-lJlsRR9rJw,1308
100
100
  supervisely/app/v1/__init__.py,sha256=OdU0PYv6hLwahYoyaLFO8m3cbJSchvPbqxuG1N3T734,848
101
101
  supervisely/app/v1/app_config.md,sha256=-8GKbiQoX25RhEj3EDJ7TxiYuFw5wL2TO3qV5AJLZTs,2536
@@ -979,7 +979,7 @@ supervisely/nn/tracker/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
979
979
  supervisely/nn/tracker/utils/gmc.py,sha256=3JX8979H3NA-YHNaRQyj9Z-xb9qtyMittPEjGw8y2Jo,11557
980
980
  supervisely/nn/tracker/utils/kalman_filter.py,sha256=eSFmCjM0mikHCAFvj-KCVzw-0Jxpoc3Cfc2NWEjJC1Q,17268
981
981
  supervisely/nn/training/__init__.py,sha256=gY4PCykJ-42MWKsqb9kl-skemKa8yB6t_fb5kzqR66U,111
982
- supervisely/nn/training/train_app.py,sha256=yVsMdMlV6OHCRMJ63-hPmTfdsC3Z1-0ohphsMtDMpPw,104944
982
+ supervisely/nn/training/train_app.py,sha256=lFvYxt2Zsd7FrqfeA2C-eNX0tQVLtz3V8om3DwvNFtM,104720
983
983
  supervisely/nn/training/gui/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
984
984
  supervisely/nn/training/gui/classes_selector.py,sha256=8UgzA4aogOAr1s42smwEcDbgaBj_i0JLhjwlZ9bFdIA,3772
985
985
  supervisely/nn/training/gui/gui.py,sha256=CnT_QhihrxdSHKybpI0pXhPLwCaXEana_qdn0DhXByg,25558
@@ -1016,7 +1016,7 @@ supervisely/pointcloud_episodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
1016
1016
  supervisely/pointcloud_episodes/pointcloud_episodes.py,sha256=cRXdtw7bMsbsdVQjxfWxFSESrO-LGiqqsZyyExl2Mbg,3430
1017
1017
  supervisely/project/__init__.py,sha256=hlzdj9Pgy53Q3qdP8LMtGTChvZHQuuShdtui2eRUQeE,2601
1018
1018
  supervisely/project/data_version.py,sha256=6vOz5ovBeCIiMAKUG7lGQ5IXvQnU1GbcnrWxdOvaVlo,19311
1019
- supervisely/project/download.py,sha256=zb8sb4XZ6Qi3CP7fmtLRUAYzaxs_W0WnOfe2x3ZVRMs,24639
1019
+ supervisely/project/download.py,sha256=GQFYN3KCdM_egXDzoyZrzl6Yeg2QshYQNFNlKi8Nh8A,25471
1020
1020
  supervisely/project/pointcloud_episode_project.py,sha256=yiWdNBQiI6f1O9sr1pg8JHW6O-w3XUB1rikJNn3Oung,41866
1021
1021
  supervisely/project/pointcloud_project.py,sha256=Kx1Vaes-krwG3BiRRtHRLQxb9G5m5bTHPN9IzRqmNWo,49399
1022
1022
  supervisely/project/project.py,sha256=nKnnYVrSx_MWh5G_fObaAegkRxLFJg_J074SaduEYGo,205871
@@ -1081,9 +1081,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
1081
1081
  supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
1082
1082
  supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
1083
1083
  supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
1084
- supervisely-6.73.323.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1085
- supervisely-6.73.323.dist-info/METADATA,sha256=uIqQoH6i-OiLhSZSLt6SqL7O1ZWPV0D6ZRJICli80eE,33596
1086
- supervisely-6.73.323.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
1087
- supervisely-6.73.323.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1088
- supervisely-6.73.323.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1089
- supervisely-6.73.323.dist-info/RECORD,,
1084
+ supervisely-6.73.324.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1085
+ supervisely-6.73.324.dist-info/METADATA,sha256=mUZAJc6JxtQ7H7I31gf_QWoBAjl4qXsS-CilUCvf8f8,33596
1086
+ supervisely-6.73.324.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
1087
+ supervisely-6.73.324.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1088
+ supervisely-6.73.324.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1089
+ supervisely-6.73.324.dist-info/RECORD,,