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,192 @@
1
+ """Race-safe directory listing, and what a listing is allowed to yield.
2
+
3
+ Wraps ``os.scandir`` with a metadata-flush barrier so directories
4
+ written immediately before the listing return their full contents:
5
+
6
+ - ``pathlib.Path.rglob`` silently swallows ``OSError`` mid-walk
7
+ (cpython#146646), conflating partial errors with empty trees.
8
+ ``os.scandir`` surfaces the error instead.
9
+ - On macOS APFS under concurrent I/O the directory btree can lag a
10
+ finished write. ``F_FULLFSYNC`` on the directory FD forces the
11
+ commit before the listing reads it. On Linux ``os.fsync`` covers
12
+ the equivalent NFS / FUSE case. On Windows the barrier is a no-op.
13
+
14
+ This module also owns :data:`INTERNAL_DIRNAMES` — the directory names
15
+ the SDK uses for its own working files, which must never surface as
16
+ artifacts. It lives here rather than beside the writer that creates
17
+ them because *excluding* them is a property of every tree walk, and
18
+ there is more than one walker: ``safe_list_directory`` below and
19
+ ``storage.batch.upload_prefix``. Both consult this one definition, so
20
+ "invisible to a listing" and "invisible to a prefix upload" cannot
21
+ drift apart. Keeping the vocabulary in this stdlib-only module also
22
+ means neither walker takes on a new import to honour it.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import os
28
+ import sys
29
+ from collections.abc import Iterator
30
+ from pathlib import Path
31
+
32
+ # Darwin-only fcntl op: flush kernel buffer + drive cache. Value from
33
+ # <sys/fcntl.h>; not exported by Python's fcntl module.
34
+ _DARWIN_F_FULLFSYNC = 51
35
+
36
+ #: Directory an atomic write stages its partial file in, sited as a
37
+ #: sibling of the artifact rather than as a suffix beside it — a
38
+ #: ``foo.json.tmp`` next to ``foo.json`` is picked up by every walk of
39
+ #: the artifact directory, which is the leak this name exists to avoid.
40
+ #: Written by :mod:`application_sdk.common.atomic` (FND-318).
41
+ PARTIAL_DIRNAME = ".sdk-partial"
42
+
43
+ #: Directory a ``Writer`` stages its whole output in until ``close()``
44
+ #: publishes it, sited as a sibling of the output directory (FND-317).
45
+ #: A sibling is out of reach of a walk of the *output* directory, but
46
+ #: not of a walk of the run root a level up — which is what a prefix
47
+ #: upload of a whole run does — so it belongs in the set below.
48
+ #: Re-exported as ``_STAGING_ROOT_DIRNAME`` by ``storage.formats``,
49
+ #: which owns the staging behaviour; defined here so the exclusion
50
+ #: stays a single fact and this module keeps its stdlib-only imports.
51
+ WRITER_STAGING_DIRNAME = ".sdk-writer-staging"
52
+
53
+ #: Directory ``download_prefix(..., sync=True)`` keeps its index in — the
54
+ #: size and etag each mirrored file was downloaded at, which is how a
55
+ #: later sync knows a local file is already current without re-reading it.
56
+ #: It sits inside the mirrored tree, so a walk of that tree (or a prefix
57
+ #: upload of it) must not surface it.
58
+ SYNC_INDEX_DIRNAME = ".sdk-sync"
59
+
60
+ #: Every directory name that holds SDK working files rather than
61
+ #: artifacts. An explicit set rather than a ``.sdk-`` prefix rule: a
62
+ #: denylist that silently swallowed a customer directory because its
63
+ #: name matched a convention would be a data-loss bug, and the set is
64
+ #: short enough that adding to it is a one-line change. Add here when
65
+ #: introducing a new SDK-internal working directory — every walker
66
+ #: picks it up with no further edit.
67
+ INTERNAL_DIRNAMES: frozenset[str] = frozenset(
68
+ {PARTIAL_DIRNAME, WRITER_STAGING_DIRNAME, SYNC_INDEX_DIRNAME}
69
+ )
70
+
71
+
72
+ def is_internal_dirname(name: str) -> bool:
73
+ """Return ``True`` when *name* is an SDK working directory, not an artifact."""
74
+ return name in INTERNAL_DIRNAMES
75
+
76
+
77
+ def has_internal_component(relpath: str) -> bool:
78
+ """Return ``True`` when a POSIX relative path passes through an SDK working directory.
79
+
80
+ The key-side counterpart of :func:`prune_internal_dirs`: a walker never
81
+ descends into these directories, so a mirrored object whose relative key
82
+ lands inside one -- ``.sdk-sync/index.json`` under a synced prefix, say --
83
+ would collide with the SDK's own files there and then be invisible to every
84
+ walk. Callers that map keys into a tree check with this and refuse.
85
+ """
86
+ return any(is_internal_dirname(part) for part in relpath.split("/")[:-1])
87
+
88
+
89
+ def prune_internal_dirs(dirnames: list[str]) -> None:
90
+ """Drop SDK working directories from ``os.walk``'s ``dirnames``, in place.
91
+
92
+ ``os.walk`` re-reads ``dirnames`` after yielding to decide where to
93
+ descend, so mutating it in place — rather than filtering the copy —
94
+ is what actually stops the descent.
95
+ """
96
+ dirnames[:] = [name for name in dirnames if not is_internal_dirname(name)]
97
+
98
+
99
+ def _flush_directory_metadata(path: Path) -> None:
100
+ """Best-effort metadata-flush barrier before listing.
101
+
102
+ macOS: ``fcntl(F_FULLFSYNC)`` — Darwin's ``fsync`` does not flush
103
+ the drive cache. Linux: ``os.fsync(fd)`` on the directory FD;
104
+ covers NFS / FUSE where same-process read-after-write isn't
105
+ automatic. Windows: no-op.
106
+
107
+ Any ``OSError`` is swallowed; ``os.scandir`` below is the
108
+ correctness layer.
109
+ """
110
+ if sys.platform == "win32" or not hasattr(os, "O_DIRECTORY"):
111
+ return
112
+ try:
113
+ fd = os.open(path, os.O_DIRECTORY)
114
+ except OSError:
115
+ return
116
+ try:
117
+ if sys.platform == "darwin":
118
+ import fcntl # noqa: PLC0415
119
+
120
+ try:
121
+ fcntl.fcntl(fd, _DARWIN_F_FULLFSYNC)
122
+ return
123
+ except OSError: # conformance: ignore[E002] fall through to portable fsync on devices that don't honor F_FULLFSYNC
124
+ pass
125
+ try:
126
+ os.fsync(fd)
127
+ except OSError: # conformance: ignore[E002] best-effort barrier; real errors surface via os.scandir
128
+ pass
129
+ finally:
130
+ os.close(fd)
131
+
132
+
133
+ def _scandir_recursive(path: Path) -> Iterator[Path]:
134
+ """Yield every regular file under ``path``, iteratively.
135
+
136
+ Iterative descent (explicit stack) rather than recursive
137
+ ``yield from``: bounds open directory FDs to one at any moment,
138
+ has no recursion-limit ceiling, and matches the approach used
139
+ internally by ``pathlib.rglob``, ``black``, and ``ruff``.
140
+
141
+ Symlinks are not followed: prevents loops on cyclic structures
142
+ and excludes symlink-to-file entries (a behaviour change vs
143
+ ``Path.rglob`` — see ``safe_list_directory``).
144
+
145
+ :data:`INTERNAL_DIRNAMES` are not descended into. The test is on
146
+ the entry name during descent, never on ``path`` itself, so a
147
+ caller that deliberately points the walk *at* one of these
148
+ directories still gets its contents.
149
+ """
150
+ stack: list[Path] = [path]
151
+ while stack:
152
+ current = stack.pop()
153
+ with os.scandir(current) as it:
154
+ for entry in it:
155
+ if entry.is_dir(follow_symlinks=False):
156
+ if is_internal_dirname(entry.name):
157
+ continue
158
+ stack.append(Path(entry.path))
159
+ elif entry.is_file(follow_symlinks=False):
160
+ yield Path(entry.path)
161
+
162
+
163
+ def safe_list_directory(path: Path) -> list[Path]:
164
+ """Recursively list every regular file under ``path``, race-safely.
165
+
166
+ Wraps a metadata-flush barrier with an ``os.scandir`` descent.
167
+ Surfaces ``OSError`` rather than swallowing it like
168
+ ``pathlib.rglob`` does (cpython#146646), and forces APFS directory
169
+ metadata to commit before reading on macOS.
170
+
171
+ Args:
172
+ path: A directory path. Must exist and be a directory.
173
+
174
+ Returns:
175
+ A list of ``Path`` objects, one per regular file under the
176
+ tree. Order is filesystem-dependent.
177
+
178
+ Raises:
179
+ OSError: On missing path, non-directory, or any traversal
180
+ error. Unlike ``pathlib.Path.rglob``, errors are surfaced.
181
+
182
+ Behaviour change vs ``Path.rglob``: symlink-to-file entries are
183
+ NOT included (``follow_symlinks=False`` on every check). New
184
+ callers needing symlink-following should add an opt-in parameter
185
+ rather than changing the default.
186
+
187
+ Directories named in :data:`INTERNAL_DIRNAMES` are skipped — an
188
+ in-flight atomic write must not surface as an artifact in a
189
+ ``FileReference`` directory or anywhere else this listing feeds.
190
+ """
191
+ _flush_directory_metadata(path)
192
+ return list(_scandir_recursive(path))