label-studio-sdk 0.0.27__tar.gz → 0.0.29__tar.gz

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 label-studio-sdk might be problematic. Click here for more details.

Files changed (23) hide show
  1. {label-studio-sdk-0.0.27/label_studio_sdk.egg-info → label-studio-sdk-0.0.29}/PKG-INFO +1 -1
  2. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/README.md +5 -1
  3. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/__init__.py +1 -1
  4. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/client.py +38 -13
  5. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/project.py +33 -2
  6. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29/label_studio_sdk.egg-info}/PKG-INFO +1 -1
  7. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk.egg-info/SOURCES.txt +1 -0
  8. label-studio-sdk-0.0.29/tests/import_test.py +35 -0
  9. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/LICENSE +0 -0
  10. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/MANIFEST.in +0 -0
  11. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/docs/__init__.py +0 -0
  12. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/data_manager.py +0 -0
  13. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/users.py +0 -0
  14. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/utils.py +0 -0
  15. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk/workspaces.py +0 -0
  16. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk.egg-info/dependency_links.txt +0 -0
  17. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk.egg-info/requires.txt +0 -0
  18. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/label_studio_sdk.egg-info/top_level.txt +0 -0
  19. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/requirements.txt +0 -0
  20. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/setup.cfg +0 -0
  21. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/setup.py +0 -0
  22. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/tests/__init__.py +0 -0
  23. {label-studio-sdk-0.0.27 → label-studio-sdk-0.0.29}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: label-studio-sdk
3
- Version: 0.0.27
3
+ Version: 0.0.29
4
4
  Summary: Label Studio annotation tool
5
5
  Home-page: https://github.com/heartexlabs/label-studio-sdk
6
6
  Author: Heartex
@@ -17,7 +17,7 @@ If you want to take action not supported natively by the SDK, you can [call the
17
17
  This is the first release of the Label Studio SDK. It supports Label Studio Enterprise, Label Studio Teams, and Label Studio Community.
18
18
 
19
19
  - **Find a bug?** [Create a GitHub issue](https://github.com/heartexlabs/label-studio-sdk/issues)!
20
- - **Have a question?** [Join the Slack Community](https://slack.labelstudio.heartex.com/?source=github-sdk)!
20
+ - **Have a question?** [Join the Slack Community](https://slack.labelstud.io/?source=github-sdk)!
21
21
  - **Want to contribute?** [See the contributing guide](https://github.com/heartexlabs/label-studio-sdk/CONTRIBUTING.md)
22
22
 
23
23
  ## Quickstart
@@ -43,6 +43,10 @@ The Label Studio SDK includes the following:
43
43
 
44
44
  For all the details, see the [reference documentation](https://labelstud.io/sdk) or [review the code directly](https://github.com/heartexlabs/label-studio-sdk/tree/master/label_studio_sdk).
45
45
 
46
+ ## Error logging
47
+
48
+ To see error logs, you can use `stderr` (we use `logger.error()` for error output). If you use console to run SDK commands, you will see all errors there.
49
+
46
50
  ## Contribute to the SDK
47
51
 
48
52
  If you want to extend the SDK:
@@ -5,4 +5,4 @@ from .project import Project
5
5
  from .utils import parse_config
6
6
 
7
7
 
8
- __version__ = '0.0.27'
8
+ __version__ = '0.0.29'
@@ -1,5 +1,6 @@
1
1
  """ .. include::../docs/client.md
2
2
  """
3
+ import json
3
4
  import warnings
4
5
  import logging
5
6
  import requests
@@ -7,11 +8,12 @@ import requests
7
8
  from typing import Optional
8
9
  from pydantic import BaseModel, constr, root_validator
9
10
  from requests.adapters import HTTPAdapter
11
+ from types import SimpleNamespace
10
12
 
11
13
  logger = logging.getLogger(__name__)
12
14
 
13
15
  MAX_RETRIES = 3
14
- TIMEOUT = (1.0, 180.0)
16
+ TIMEOUT = (10.0, 180.0)
15
17
  HEADERS = {}
16
18
 
17
19
 
@@ -42,7 +44,7 @@ class Client(object):
42
44
  cookies: dict = None,
43
45
  oidc_token=None,
44
46
  versions=None,
45
- make_request_raise=True
47
+ make_request_raise=True,
46
48
  ):
47
49
  """Initialize the client. Do this before using other Label Studio SDK classes and methods in your script.
48
50
 
@@ -95,7 +97,6 @@ class Client(object):
95
97
  self.versions = versions if versions else self.get_versions()
96
98
  self.is_enterprise = 'label-studio-enterprise-backend' in self.versions
97
99
 
98
-
99
100
  def get_versions(self):
100
101
  """Call /version api and get all Label Studio component versions
101
102
 
@@ -259,7 +260,7 @@ class Client(object):
259
260
  users.append(User(**user_data))
260
261
  return users
261
262
 
262
- def create_user(self, user):
263
+ def create_user(self, user, exist_ok=True):
263
264
  """Create a new user
264
265
 
265
266
  Parameters
@@ -267,6 +268,8 @@ class Client(object):
267
268
  user: User or dict
268
269
  User instance, you can initialize it this way:
269
270
  User(username='x', email='x@x.xx', first_name='X', last_name='Z')
271
+ exist_ok: bool
272
+ True by default, it won't print error if user exists and exist_ok=True
270
273
 
271
274
  Returns
272
275
  -------
@@ -278,7 +281,7 @@ class Client(object):
278
281
 
279
282
  payload = (
280
283
  {
281
- 'username': user.username,
284
+ 'username': user.username if user.username else user.email,
282
285
  'email': user.email,
283
286
  'first_name': user.first_name,
284
287
  'last_name': user.last_name,
@@ -288,13 +291,16 @@ class Client(object):
288
291
  else user
289
292
  )
290
293
 
291
- try:
292
- response = self.make_request('POST', '/api/users', json=payload)
293
- user_data = response.json()
294
- user_data['client'] = self
294
+ response = self.make_request('POST', '/api/users', json=payload, raise_exceptions=False)
295
+ user_data = response.json()
296
+ user_data['client'] = self
297
+
298
+ if response.status_code < 400:
295
299
  return User(**user_data)
296
- except requests.HTTPError as e:
297
- logger.error('Create user error:' + str(e.response.json()))
300
+ else:
301
+ if 'already exists' in response.text and exist_ok is True:
302
+ return None
303
+ logger.error('Create user error: ' + str(response.json()))
298
304
  return None
299
305
 
300
306
  def get_workspaces(self):
@@ -367,6 +373,11 @@ class Client(object):
367
373
  """
368
374
  if 'timeout' not in kwargs:
369
375
  kwargs['timeout'] = TIMEOUT
376
+
377
+ raise_exceptions = self.make_request_raise
378
+ if 'raise_exceptions' in kwargs: # kwargs have higher priority
379
+ raise_exceptions = kwargs.pop('raise_exceptions')
380
+
370
381
  logger.debug(f'{method}: {url} with args={args}, kwargs={kwargs}')
371
382
  response = self.session.request(
372
383
  method,
@@ -376,8 +387,22 @@ class Client(object):
376
387
  *args,
377
388
  **kwargs,
378
389
  )
379
- if self.make_request_raise:
380
- response.raise_for_status()
390
+
391
+ if raise_exceptions:
392
+ if response.status_code >= 400:
393
+ try:
394
+ content = json.dumps(json.loads(response.content), indent=2)
395
+ except:
396
+ content = response.text
397
+
398
+ logger.error(
399
+ f'\n--------------------------------------------\n'
400
+ f'Request URL: {response.url}\n'
401
+ f'Response status code: {response.status_code}\n'
402
+ f'Response content:\n{content}\n\n'
403
+ f'SDK error traceback:')
404
+ response.raise_for_status()
405
+
381
406
  return response
382
407
 
383
408
  def sync_storage(self, storage_type, storage_id):
@@ -4,6 +4,7 @@ import os
4
4
  import json
5
5
  import logging
6
6
  import pathlib
7
+ import time
7
8
 
8
9
  from enum import Enum, auto
9
10
  from random import sample, shuffle
@@ -447,7 +448,7 @@ class Project(Client):
447
448
  session=client.session,
448
449
  extra_headers=client.headers,
449
450
  versions=client.versions,
450
- make_request_raise=client.make_request_raise
451
+ make_request_raise=client.make_request_raise,
451
452
  )
452
453
  if params and isinstance(params, dict):
453
454
  # TODO: validate project parameters
@@ -518,7 +519,37 @@ class Project(Client):
518
519
  raise TypeError(
519
520
  f'Not supported type provided as "tasks" argument: {type(tasks)}'
520
521
  )
521
- return response.json()['task_ids']
522
+ response = response.json()
523
+
524
+ if 'import' in response:
525
+ # check import status
526
+ timeout = 300
527
+ fibonacci_backoff = [1, 1]
528
+
529
+ start_time = time.time()
530
+
531
+ while True:
532
+ import_status = self.make_request(
533
+ method='GET',
534
+ url=f'/api/projects/{self.id}/imports/{response["import"]}',
535
+ ).json()
536
+
537
+ if import_status['status'] == 'completed':
538
+ return import_status['task_ids']
539
+
540
+ if import_status['status'] == 'failed':
541
+ raise LabelStudioException(import_status['error'])
542
+
543
+ if time.time() - start_time >= timeout:
544
+ raise LabelStudioException('Import timeout')
545
+
546
+ time.sleep(fibonacci_backoff[0])
547
+ fibonacci_backoff = [
548
+ fibonacci_backoff[1],
549
+ fibonacci_backoff[0] + fibonacci_backoff[1],
550
+ ]
551
+
552
+ return response['task_ids']
522
553
 
523
554
  def export_tasks(
524
555
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: label-studio-sdk
3
- Version: 0.0.27
3
+ Version: 0.0.29
4
4
  Summary: Label Studio annotation tool
5
5
  Home-page: https://github.com/heartexlabs/label-studio-sdk
6
6
  Author: Heartex
@@ -17,4 +17,5 @@ label_studio_sdk.egg-info/dependency_links.txt
17
17
  label_studio_sdk.egg-info/requires.txt
18
18
  label_studio_sdk.egg-info/top_level.txt
19
19
  tests/__init__.py
20
+ tests/import_test.py
20
21
  tests/test_client.py
@@ -0,0 +1,35 @@
1
+ LABEL_STUDIO_URL = 'https://fb-lsdv-5218.dev.heartex.com'
2
+ API_KEY = 'aaf73cc7ac1e681212a9919d99e907965a32ff37'
3
+
4
+ # Import the SDK and the client module
5
+ from label_studio_sdk import Client
6
+
7
+ # Connect to the Label Studio API and check the connection
8
+ ls = Client(url=LABEL_STUDIO_URL, api_key=API_KEY)
9
+ ls.check_connection()
10
+
11
+ project = ls.start_project(
12
+ title='Image Project',
13
+ label_config='''
14
+ <View>
15
+
16
+ <Header value="Select label and click the image to start"/>
17
+ <Image name="image" value="$image" zoom="true"/>
18
+
19
+ <PolygonLabels name="label" toName="image"
20
+ strokeWidth="3" pointSize="small"
21
+ opacity="0.9">
22
+ <Label value="Airplane" background="red"/>
23
+ <Label value="Car" background="blue"/>
24
+ </PolygonLabels>
25
+
26
+ </View>
27
+ '''
28
+ )
29
+
30
+ project.import_tasks(
31
+ [
32
+ {'image': 'https://data.heartex.net/open-images/train_0/mini/0045dd96bf73936c.jpg'},
33
+ {'image': 'https://data.heartex.net/open-images/train_0/mini/0083d02f6ad18b38.jpg'}
34
+ ]
35
+ )