atlan-application-sdk-api 3.51.0__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.
Files changed (78) hide show
  1. application_sdk/__init__.py +7 -0
  2. application_sdk/_build_info.py +62 -0
  3. application_sdk/_context_errors.py +65 -0
  4. application_sdk/_install.py +130 -0
  5. application_sdk/_logging.py +171 -0
  6. application_sdk/_runtime/__init__.py +29 -0
  7. application_sdk/_runtime/offload.py +1020 -0
  8. application_sdk/_runtime/progress.py +757 -0
  9. application_sdk/clients/__init__.py +63 -0
  10. application_sdk/clients/_interface.py +58 -0
  11. application_sdk/clients/_sql_failure_classifier.py +112 -0
  12. application_sdk/clients/base.py +565 -0
  13. application_sdk/clients/http_errors.py +28 -0
  14. application_sdk/clients/models.py +49 -0
  15. application_sdk/clients/sql.py +1218 -0
  16. application_sdk/clients/sql_errors.py +192 -0
  17. application_sdk/clients/sql_typecasters.py +431 -0
  18. application_sdk/clients/ssl_utils.py +247 -0
  19. application_sdk/common/__init__.py +53 -0
  20. application_sdk/common/_generated_tree.py +321 -0
  21. application_sdk/common/_listing.py +192 -0
  22. application_sdk/common/aws_utils.py +522 -0
  23. application_sdk/common/aws_utils_errors.py +88 -0
  24. application_sdk/common/build_identity.py +120 -0
  25. application_sdk/common/concurrency.py +45 -0
  26. application_sdk/common/errors.py +99 -0
  27. application_sdk/common/filter_matching.py +261 -0
  28. application_sdk/common/logger.py +11 -0
  29. application_sdk/common/models.py +31 -0
  30. application_sdk/common/sql_filters.py +719 -0
  31. application_sdk/common/sql_filters_errors.py +17 -0
  32. application_sdk/common/task_queue.py +431 -0
  33. application_sdk/common/types.py +51 -0
  34. application_sdk/common/utils.py +87 -0
  35. application_sdk/constants.py +1120 -0
  36. application_sdk/contracts/__init__.py +158 -0
  37. application_sdk/contracts/base.py +1178 -0
  38. application_sdk/contracts/events.py +251 -0
  39. application_sdk/contracts/types.py +801 -0
  40. application_sdk/contracts/types_errors.py +17 -0
  41. application_sdk/credentials/__init__.py +326 -0
  42. application_sdk/credentials/errors.py +226 -0
  43. application_sdk/credentials/extra.py +114 -0
  44. application_sdk/credentials/ingress.py +283 -0
  45. application_sdk/credentials/ref.py +376 -0
  46. application_sdk/credentials/spec.py +182 -0
  47. application_sdk/credentials/types.py +339 -0
  48. application_sdk/credentials/utils.py +257 -0
  49. application_sdk/errors/__init__.py +224 -0
  50. application_sdk/errors/base.py +504 -0
  51. application_sdk/errors/categories.py +118 -0
  52. application_sdk/errors/http.py +81 -0
  53. application_sdk/errors/leaves.py +526 -0
  54. application_sdk/errors/wire.py +124 -0
  55. application_sdk/handler/__init__.py +97 -0
  56. application_sdk/handler/_warmup.py +198 -0
  57. application_sdk/handler/asgi.py +252 -0
  58. application_sdk/handler/base.py +281 -0
  59. application_sdk/handler/context.py +212 -0
  60. application_sdk/handler/contracts.py +1166 -0
  61. application_sdk/handler/manifest.py +81 -0
  62. application_sdk/handler/metadata_routes.py +699 -0
  63. application_sdk/handler/object_routes.py +418 -0
  64. application_sdk/handler/request_contract.py +91 -0
  65. application_sdk/handler/routes.py +1118 -0
  66. application_sdk/handler/service_errors.py +37 -0
  67. application_sdk/server/__init__.py +15 -0
  68. application_sdk/server/fastapi/models.py +338 -0
  69. application_sdk/templates/__init__.py +52 -0
  70. application_sdk/templates/contracts/__init__.py +111 -0
  71. application_sdk/templates/contracts/base_metadata_extraction.py +32 -0
  72. application_sdk/templates/contracts/incremental_sql.py +563 -0
  73. application_sdk/templates/contracts/sql_metadata.py +824 -0
  74. application_sdk/templates/contracts/sql_query.py +105 -0
  75. application_sdk/version.py +40 -0
  76. atlan_application_sdk_api-3.51.0.dist-info/METADATA +230 -0
  77. atlan_application_sdk_api-3.51.0.dist-info/RECORD +78 -0
  78. atlan_application_sdk_api-3.51.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,63 @@
1
+ """Base client interfaces for Atlan applications.
2
+
3
+ Public API::
4
+
5
+ from application_sdk.clients import (
6
+ ClientInterface, BaseClient,
7
+ BaseSQLClient, AsyncBaseSQLClient, DatabaseConfig,
8
+ RedisClient, RedisClientAsync,
9
+ AzureClient, AzureAuthProvider,
10
+ get_ssl_context, create_ssl_context_with_custom_certs,
11
+ )
12
+ """
13
+
14
+ import importlib
15
+ from typing import TYPE_CHECKING
16
+
17
+ from application_sdk.clients._interface import ClientInterface as ClientInterface
18
+ from application_sdk.clients.base import BaseClient as BaseClient
19
+ from application_sdk.clients.models import DatabaseConfig as DatabaseConfig
20
+ from application_sdk.clients.sql import AsyncBaseSQLClient as AsyncBaseSQLClient
21
+ from application_sdk.clients.sql import BaseSQLClient as BaseSQLClient
22
+ from application_sdk.clients.ssl_utils import (
23
+ create_ssl_context_with_custom_certs as create_ssl_context_with_custom_certs,
24
+ )
25
+ from application_sdk.clients.ssl_utils import get_ssl_context as get_ssl_context
26
+
27
+ if TYPE_CHECKING:
28
+ from application_sdk.clients.azure.auth import AzureAuthProvider
29
+ from application_sdk.clients.azure.client import AzureClient
30
+ from application_sdk.clients.redis import RedisClient, RedisClientAsync
31
+
32
+ # Symbols backed by optional extras ([azure] and [distributed_lock]) — lazy-loaded
33
+ # so that `from application_sdk.clients import BaseSQLClient` never pulls azure-* or redis.
34
+ _LAZY: dict[str, tuple[str, str]] = {
35
+ "AzureAuthProvider": ("application_sdk.clients.azure.auth", "AzureAuthProvider"),
36
+ "AzureClient": ("application_sdk.clients.azure.client", "AzureClient"),
37
+ "RedisClient": ("application_sdk.clients.redis", "RedisClient"),
38
+ "RedisClientAsync": ("application_sdk.clients.redis", "RedisClientAsync"),
39
+ }
40
+
41
+ __all__ = [
42
+ "AsyncBaseSQLClient",
43
+ "AzureAuthProvider",
44
+ "AzureClient",
45
+ "BaseClient",
46
+ "BaseSQLClient",
47
+ "ClientInterface",
48
+ "DatabaseConfig",
49
+ "RedisClient",
50
+ "RedisClientAsync",
51
+ "create_ssl_context_with_custom_certs",
52
+ "get_ssl_context",
53
+ ]
54
+
55
+
56
+ def __getattr__(name: str):
57
+ target = _LAZY.get(name)
58
+ if target is None:
59
+ raise AttributeError(
60
+ f"module 'application_sdk.clients' has no attribute {name!r}"
61
+ )
62
+ mod_name, attr = target
63
+ return getattr(importlib.import_module(mod_name), attr)
@@ -0,0 +1,58 @@
1
+ from abc import ABC, abstractmethod
2
+ from contextlib import AbstractAsyncContextManager
3
+ from types import TracebackType
4
+ from typing import Any, Self
5
+
6
+ from application_sdk.clients.sql_errors import AbstractClientLoadError
7
+
8
+
9
+ class ClientInterface(AbstractAsyncContextManager["ClientInterface"], ABC):
10
+ """Base interface class for implementing client connections.
11
+
12
+ This abstract class defines the required methods that any client implementation
13
+ must provide for establishing and managing connections to data sources.
14
+
15
+ A client is an async context manager: ``async with`` calls ``close()`` on
16
+ exit, including when the block raises. Prefer it over calling ``close()``
17
+ by hand::
18
+
19
+ async with MyClient() as client:
20
+ await client.load(credentials=credentials)
21
+ ...
22
+ """
23
+
24
+ @abstractmethod
25
+ async def load(self, *args: Any, **kwargs: Any) -> None:
26
+ """Establish the client connection.
27
+
28
+ This method should handle the initialization and connection setup
29
+ for the specific client implementation.
30
+
31
+ Raises:
32
+ AbstractClientLoadError: If the subclass does not implement this method.
33
+ """
34
+
35
+ raise AbstractClientLoadError()
36
+
37
+ async def close(self, *args: Any, **kwargs: Any) -> None:
38
+ """Close the client connection.
39
+
40
+ This method should properly terminate the connection and clean up
41
+ any resources used by the client. By default, it does nothing.
42
+ Subclasses should override this method if cleanup is needed, and
43
+ call ``super().close()`` so the base class releases its resources.
44
+ """
45
+ return
46
+
47
+ async def __aenter__(self) -> Self:
48
+ """Return the client. ``load()`` is not called; it needs credentials."""
49
+ return self
50
+
51
+ async def __aexit__(
52
+ self,
53
+ exc_type: type[BaseException] | None,
54
+ exc_val: BaseException | None,
55
+ exc_tb: TracebackType | None,
56
+ ) -> None:
57
+ """Close the client, whether or not the block raised."""
58
+ await self.close()
@@ -0,0 +1,112 @@
1
+ """Classify a failed SQL connect by what actually went wrong.
2
+
3
+ One reader for every SQL connect surface: ``BaseSQLClient.load`` /
4
+ ``AsyncBaseSQLClient.load`` and ``SqlApp.prime_sql_auth`` all ask the same
5
+ question of a driver exception — was it the credentials, the clock, or the
6
+ network path — and answer it here. Each caller builds its own typed leaf from
7
+ the verdict, so the wording can differ per surface while the decision cannot.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import enum
13
+
14
+
15
+ class SqlFailureKind(enum.Enum):
16
+ """What a SQL connect failure was, as far as the driver error can tell."""
17
+
18
+ AUTH = "auth"
19
+ TIMEOUT = "timeout"
20
+ SOURCE_UNAVAILABLE = "source_unavailable"
21
+ UNCLASSIFIED = "unclassified"
22
+
23
+
24
+ def root_cause(exc: BaseException) -> BaseException:
25
+ """Walk ``__cause__`` to the innermost exception.
26
+
27
+ The SDK's SQL client rewraps driver exceptions, and SQLAlchemy rewraps the
28
+ DBAPI error, so the outermost exception describes a wrapper and the
29
+ innermost describes what actually happened. Cycle-guarded — a hand-built
30
+ ``__cause__`` loop would otherwise hang the worker.
31
+ """
32
+ seen = {id(exc)}
33
+ while exc.__cause__ is not None and id(exc.__cause__) not in seen:
34
+ exc = exc.__cause__
35
+ seen.add(id(exc))
36
+ return exc
37
+
38
+
39
+ # ODBC renders SQLSTATE HYT00 as "Login timeout expired" — no "timeout" in the
40
+ # class name and no "timed out" in the message, so a generic substring check
41
+ # misses it. Match the driver phrasings explicitly.
42
+ _TIMEOUT_MSG_HINTS = ("timed out", "login timeout", "timeout expired", "hyt00")
43
+
44
+ # Driver error_type for MySQL ``Access denied`` is ``OperationalError`` — that
45
+ # class is overloaded across both auth and network failures, so message content
46
+ # has to be the tiebreaker.
47
+ _AUTH_CLASS_HINTS = ("authentication", "accessdenied", "authfail")
48
+ _AUTH_MSG_HINTS = (
49
+ "access denied",
50
+ "1045",
51
+ "authentication failed",
52
+ "authentication error",
53
+ "login failed",
54
+ "password authentication failed",
55
+ "invalid credentials",
56
+ # Thrift SASL handshake (impyla, PyHive): the server refused the sign-in.
57
+ # Its transport error class says nothing about auth, so only the message can.
58
+ # "TSocket read 0 bytes" and "Bad status: 3" are left out: impalad sends
59
+ # them on a rejected login, but other Thrift servers send them for a dropped
60
+ # connection or a mechanism mismatch, so they are the connector's call.
61
+ # "Could not start SASL" is left out too: the client raises it before any
62
+ # credential reaches the server, e.g. when no local SASL mechanism exists.
63
+ "authentication failure",
64
+ "bad sasl result",
65
+ )
66
+
67
+ # Network / DNS / TLS / connection-refused / generic OperationalError without
68
+ # an auth-keyword message.
69
+ _NETWORK_CLASS_HINTS = (
70
+ "connectionerror",
71
+ "connectionrefused",
72
+ "gaierror",
73
+ "sslerror",
74
+ # ``ssl.SSLCertVerificationError`` — its name does not contain "sslerror".
75
+ "sslcertverificationerror",
76
+ "oserror",
77
+ "operationalerror",
78
+ "interfaceerror",
79
+ # Match wrapped SDK error classnames surfaced as error_type.
80
+ # "dependencyunavailable" is retained for legacy wrappers that predate the
81
+ # SourceUnavailableError migration.
82
+ "sourceunavailable",
83
+ "dependencyunavailable",
84
+ "socketerror",
85
+ )
86
+
87
+
88
+ def classify_sql_failure(error_type: str, error_message: str) -> SqlFailureKind:
89
+ """Map a driver exception's class name and message to a failure kind.
90
+
91
+ Checked in order — timeout, then auth, then network — because the classes
92
+ overlap: a login timeout is an ``OperationalError`` and so is a rejected
93
+ password, and the message is what tells them apart.
94
+ """
95
+ type_lower = error_type.lower()
96
+ msg_lower = error_message.lower()
97
+
98
+ if "timeout" in type_lower or any(h in msg_lower for h in _TIMEOUT_MSG_HINTS):
99
+ return SqlFailureKind.TIMEOUT
100
+ if any(h in type_lower for h in _AUTH_CLASS_HINTS) or any(
101
+ h in msg_lower for h in _AUTH_MSG_HINTS
102
+ ):
103
+ return SqlFailureKind.AUTH
104
+ if any(h in type_lower for h in _NETWORK_CLASS_HINTS):
105
+ return SqlFailureKind.SOURCE_UNAVAILABLE
106
+ return SqlFailureKind.UNCLASSIFIED
107
+
108
+
109
+ def classify_sql_exception(exc: BaseException) -> SqlFailureKind:
110
+ """Classify a live exception by its root cause's class name and message."""
111
+ root = root_cause(exc)
112
+ return classify_sql_failure(type(root).__name__, str(root))