Skip to content

Commit 9a395ec

Browse files
refactor: separate the new domain api into a dedicated client
1 parent 4f42437 commit 9a395ec

16 files changed

+439
-982
lines changed

src/kili/client.py

Lines changed: 6 additions & 312 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,12 @@
55
import os
66
import sys
77
import warnings
8-
from functools import cached_property
98
from typing import Dict, Optional, Union
109

1110
from kili.adapters.authentification import is_api_key_valid
1211
from kili.adapters.http_client import HttpClient
1312
from kili.adapters.kili_api_gateway.kili_api_gateway import KiliAPIGateway
1413
from kili.core.graphql.graphql_client import GraphQLClient, GraphQLClientName
15-
from kili.domain_api import (
16-
AssetsNamespace,
17-
CloudStorageNamespace,
18-
ConnectionsNamespace,
19-
IntegrationsNamespace,
20-
IssuesNamespace,
21-
LabelsNamespace,
22-
NotificationsNamespace,
23-
OrganizationsNamespace,
24-
ProjectsNamespace,
25-
TagsNamespace,
26-
UsersNamespace,
27-
)
2814
from kili.entrypoints.mutations.asset import MutationsAsset
2915
from kili.entrypoints.mutations.issue import MutationsIssue
3016
from kili.entrypoints.mutations.notification import MutationsNotification
@@ -97,9 +83,11 @@ def __init__(
9783
verify: Optional[Union[bool, str]] = None,
9884
client_name: GraphQLClientName = GraphQLClientName.SDK,
9985
graphql_client_params: Optional[Dict[str, object]] = None,
100-
legacy: bool = True,
10186
) -> None:
102-
"""Initialize Kili client.
87+
"""Initialize Kili client (legacy mode).
88+
89+
This client provides access to legacy methods through mixin inheritance.
90+
For the domain-based API, use `from kili.client_domain import Kili` instead.
10391
10492
Args:
10593
api_key: User API key generated
@@ -122,11 +110,6 @@ def __init__(
122110
client_name: For internal use only.
123111
Define the name of the graphQL client whith which graphQL calls will be sent.
124112
graphql_client_params: Parameters to pass to the graphQL client.
125-
legacy: Controls namespace naming and legacy method availability.
126-
When True (default), legacy methods are available and domain namespaces
127-
use the '_ns' suffix (e.g., kili.assets_ns).
128-
When False, legacy methods are not available and domain namespaces
129-
use clean names (e.g., kili.assets).
130113
131114
Returns:
132115
Instance of the Kili client.
@@ -135,15 +118,10 @@ def __init__(
135118
```python
136119
from kili.client import Kili
137120
138-
# Legacy mode (default)
121+
# Legacy API with methods
139122
kili = Kili()
140123
kili.assets() # legacy method
141-
kili.assets_ns # domain namespace
142-
143-
# Modern mode
144-
kili = Kili(legacy=False)
145-
kili.assets # domain namespace (clean name)
146-
# kili.assets() not available
124+
kili.projects() # legacy method
147125
```
148126
"""
149127
api_key = api_key or os.getenv("KILI_API_KEY")
@@ -172,7 +150,6 @@ def __init__(
172150
self.api_endpoint = api_endpoint
173151
self.verify = verify
174152
self.client_name = client_name
175-
self._legacy_mode = legacy
176153
self.http_client = HttpClient(kili_endpoint=api_endpoint, verify=verify, api_key=api_key)
177154
skip_checks = os.getenv("KILI_SDK_SKIP_CHECKS") is not None
178155
if not skip_checks and not is_api_key_valid(
@@ -200,286 +177,3 @@ def __init__(
200177
if not skip_checks:
201178
api_key_use_cases = ApiKeyUseCases(self.kili_api_gateway)
202179
api_key_use_cases.check_expiry_of_key_is_close(api_key)
203-
204-
# Domain API Namespaces - Lazy loaded properties
205-
@cached_property
206-
def assets_ns(self) -> AssetsNamespace:
207-
"""Get the assets domain namespace.
208-
209-
Returns:
210-
AssetsNamespace: Assets domain namespace with lazy loading
211-
212-
Examples:
213-
```python
214-
kili = Kili()
215-
# Namespace is instantiated on first access
216-
assets_ns = kili.assets_ns
217-
```
218-
"""
219-
return AssetsNamespace(self, self.kili_api_gateway)
220-
221-
@cached_property
222-
def labels_ns(self) -> LabelsNamespace:
223-
"""Get the labels domain namespace.
224-
225-
Returns:
226-
LabelsNamespace: Labels domain namespace with lazy loading
227-
228-
Examples:
229-
```python
230-
kili = Kili()
231-
# Namespace is instantiated on first access
232-
labels_ns = kili.labels_ns
233-
```
234-
"""
235-
return LabelsNamespace(self, self.kili_api_gateway)
236-
237-
@cached_property
238-
def projects_ns(self) -> ProjectsNamespace:
239-
"""Get the projects domain namespace.
240-
241-
Returns:
242-
ProjectsNamespace: Projects domain namespace with lazy loading
243-
244-
Examples:
245-
```python
246-
kili = Kili()
247-
# Namespace is instantiated on first access
248-
projects_ns = kili.projects_ns
249-
```
250-
"""
251-
return ProjectsNamespace(self, self.kili_api_gateway)
252-
253-
@cached_property
254-
def users_ns(self) -> UsersNamespace:
255-
"""Get the users domain namespace.
256-
257-
Returns:
258-
UsersNamespace: Users domain namespace with lazy loading
259-
260-
Examples:
261-
```python
262-
kili = Kili()
263-
# Namespace is instantiated on first access
264-
users_ns = kili.users_ns
265-
```
266-
"""
267-
return UsersNamespace(self, self.kili_api_gateway)
268-
269-
@cached_property
270-
def organizations_ns(self) -> OrganizationsNamespace:
271-
"""Get the organizations domain namespace.
272-
273-
Returns:
274-
OrganizationsNamespace: Organizations domain namespace with lazy loading
275-
276-
Examples:
277-
```python
278-
kili = Kili()
279-
# Namespace is instantiated on first access
280-
organizations_ns = kili.organizations_ns
281-
```
282-
"""
283-
return OrganizationsNamespace(self, self.kili_api_gateway)
284-
285-
@cached_property
286-
def issues_ns(self) -> IssuesNamespace:
287-
"""Get the issues domain namespace.
288-
289-
Returns:
290-
IssuesNamespace: Issues domain namespace with lazy loading
291-
292-
Examples:
293-
```python
294-
kili = Kili()
295-
# Namespace is instantiated on first access
296-
issues_ns = kili.issues_ns
297-
```
298-
"""
299-
return IssuesNamespace(self, self.kili_api_gateway)
300-
301-
@cached_property
302-
def notifications_ns(self) -> NotificationsNamespace:
303-
"""Get the notifications domain namespace.
304-
305-
Returns:
306-
NotificationsNamespace: Notifications domain namespace with lazy loading
307-
308-
Examples:
309-
```python
310-
kili = Kili()
311-
# Namespace is instantiated on first access
312-
notifications_ns = kili.notifications_ns
313-
```
314-
"""
315-
return NotificationsNamespace(self, self.kili_api_gateway)
316-
317-
@cached_property
318-
def tags_ns(self) -> TagsNamespace:
319-
"""Get the tags domain namespace.
320-
321-
Returns:
322-
TagsNamespace: Tags domain namespace with lazy loading
323-
324-
Examples:
325-
```python
326-
kili = Kili()
327-
# Namespace is instantiated on first access
328-
tags_ns = kili.tags_ns
329-
```
330-
"""
331-
return TagsNamespace(self, self.kili_api_gateway)
332-
333-
@cached_property
334-
def cloud_storage_ns(self) -> CloudStorageNamespace:
335-
"""Get the cloud storage domain namespace.
336-
337-
Returns:
338-
CloudStorageNamespace: Cloud storage domain namespace with lazy loading
339-
340-
Examples:
341-
```python
342-
kili = Kili()
343-
# Namespace is instantiated on first access
344-
cloud_storage_ns = kili.cloud_storage_ns
345-
```
346-
"""
347-
return CloudStorageNamespace(self, self.kili_api_gateway)
348-
349-
@cached_property
350-
def connections_ns(self) -> ConnectionsNamespace:
351-
"""Get the connections domain namespace.
352-
353-
Returns:
354-
ConnectionsNamespace: Connections domain namespace with lazy loading
355-
356-
Examples:
357-
```python
358-
kili = Kili()
359-
# Namespace is instantiated on first access
360-
connections_ns = kili.connections_ns
361-
```
362-
"""
363-
return ConnectionsNamespace(self, self.kili_api_gateway)
364-
365-
@cached_property
366-
def integrations_ns(self) -> IntegrationsNamespace:
367-
"""Get the integrations domain namespace.
368-
369-
Returns:
370-
IntegrationsNamespace: Integrations domain namespace with lazy loading
371-
372-
Examples:
373-
```python
374-
kili = Kili()
375-
# Namespace is instantiated on first access
376-
integrations_ns = kili.integrations_ns
377-
```
378-
"""
379-
return IntegrationsNamespace(self, self.kili_api_gateway)
380-
381-
def __getattr__(self, name: str):
382-
"""Handle dynamic namespace routing based on legacy mode.
383-
384-
When legacy=False, routes clean namespace names to their _ns counterparts.
385-
When legacy=True, raises AttributeError for clean names to fall back to legacy methods.
386-
387-
Args:
388-
name: The attribute name being accessed
389-
390-
Returns:
391-
The appropriate namespace instance
392-
393-
Raises:
394-
AttributeError: When the attribute is not a recognized namespace or
395-
when trying to access clean names in legacy mode
396-
"""
397-
# Mapping of clean names to _ns property names
398-
namespace_mapping = {
399-
"assets": "assets_ns",
400-
"labels": "labels_ns",
401-
"projects": "projects_ns",
402-
"users": "users_ns",
403-
"organizations": "organizations_ns",
404-
"issues": "issues_ns",
405-
"notifications": "notifications_ns",
406-
"tags": "tags_ns",
407-
"cloud_storage": "cloud_storage_ns",
408-
"connections": "connections_ns",
409-
"integrations": "integrations_ns",
410-
}
411-
412-
# In non-legacy mode, route clean names to _ns properties
413-
if not self._legacy_mode and name in namespace_mapping:
414-
return getattr(self, namespace_mapping[name])
415-
416-
# For legacy mode or unrecognized attributes, raise AttributeError
417-
# This allows legacy methods to be accessible through normal inheritance
418-
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
419-
420-
def __getattribute__(self, name: str):
421-
"""Control access to legacy methods based on legacy mode setting.
422-
423-
When legacy=False, prevents access to legacy methods that conflict with
424-
domain namespace names, providing clear error messages.
425-
426-
Args:
427-
name: The attribute name being accessed
428-
429-
Returns:
430-
The requested attribute
431-
432-
Raises:
433-
AttributeError: When trying to access legacy methods in non-legacy mode
434-
"""
435-
# Get the attribute normally first
436-
attr = super().__getattribute__(name)
437-
438-
# Check if we're in non-legacy mode and trying to access a legacy method
439-
# Use object.__getattribute__ to avoid recursion
440-
try:
441-
legacy_mode = object.__getattribute__(self, "_legacy_mode")
442-
except AttributeError:
443-
# If _legacy_mode is not set yet, default to legacy behavior
444-
legacy_mode = True
445-
446-
if not legacy_mode:
447-
# Legacy method names that conflict with clean namespace names
448-
legacy_method_names = {
449-
"assets",
450-
"projects",
451-
"labels",
452-
"users",
453-
"organizations",
454-
"issues",
455-
"notifications",
456-
"tags",
457-
"cloud_storage",
458-
}
459-
460-
# If it's a callable legacy method, check if it should be blocked
461-
if callable(attr) and name in legacy_method_names:
462-
# Check if this method comes from a legacy mixin class
463-
# by examining the method's __qualname__
464-
if hasattr(attr, "__func__") and hasattr(attr.__func__, "__qualname__"):
465-
qualname = attr.__func__.__qualname__
466-
if any(
467-
mixin_name in qualname
468-
for mixin_name in [
469-
"AssetClientMethods",
470-
"ProjectClientMethods",
471-
"LabelClientMethods",
472-
"UserClientMethods",
473-
"OrganizationClientMethods",
474-
"IssueClientMethods",
475-
"NotificationClientMethods",
476-
"TagClientMethods",
477-
"CloudStorageClientMethods",
478-
]
479-
):
480-
raise AttributeError(
481-
f"Legacy method '{name}()' is not available when legacy=False. "
482-
f"Use 'kili.{name}' (domain namespace) instead of 'kili.{name}()' (legacy method)."
483-
)
484-
485-
return attr

0 commit comments

Comments
 (0)