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.
- application_sdk/__init__.py +7 -0
- application_sdk/_build_info.py +62 -0
- application_sdk/_context_errors.py +65 -0
- application_sdk/_install.py +130 -0
- application_sdk/_logging.py +171 -0
- application_sdk/_runtime/__init__.py +29 -0
- application_sdk/_runtime/offload.py +1020 -0
- application_sdk/_runtime/progress.py +757 -0
- application_sdk/clients/__init__.py +63 -0
- application_sdk/clients/_interface.py +58 -0
- application_sdk/clients/_sql_failure_classifier.py +112 -0
- application_sdk/clients/base.py +565 -0
- application_sdk/clients/http_errors.py +28 -0
- application_sdk/clients/models.py +49 -0
- application_sdk/clients/sql.py +1218 -0
- application_sdk/clients/sql_errors.py +192 -0
- application_sdk/clients/sql_typecasters.py +431 -0
- application_sdk/clients/ssl_utils.py +247 -0
- application_sdk/common/__init__.py +53 -0
- application_sdk/common/_generated_tree.py +321 -0
- application_sdk/common/_listing.py +192 -0
- application_sdk/common/aws_utils.py +522 -0
- application_sdk/common/aws_utils_errors.py +88 -0
- application_sdk/common/build_identity.py +120 -0
- application_sdk/common/concurrency.py +45 -0
- application_sdk/common/errors.py +99 -0
- application_sdk/common/filter_matching.py +261 -0
- application_sdk/common/logger.py +11 -0
- application_sdk/common/models.py +31 -0
- application_sdk/common/sql_filters.py +719 -0
- application_sdk/common/sql_filters_errors.py +17 -0
- application_sdk/common/task_queue.py +431 -0
- application_sdk/common/types.py +51 -0
- application_sdk/common/utils.py +87 -0
- application_sdk/constants.py +1120 -0
- application_sdk/contracts/__init__.py +158 -0
- application_sdk/contracts/base.py +1178 -0
- application_sdk/contracts/events.py +251 -0
- application_sdk/contracts/types.py +801 -0
- application_sdk/contracts/types_errors.py +17 -0
- application_sdk/credentials/__init__.py +326 -0
- application_sdk/credentials/errors.py +226 -0
- application_sdk/credentials/extra.py +114 -0
- application_sdk/credentials/ingress.py +283 -0
- application_sdk/credentials/ref.py +376 -0
- application_sdk/credentials/spec.py +182 -0
- application_sdk/credentials/types.py +339 -0
- application_sdk/credentials/utils.py +257 -0
- application_sdk/errors/__init__.py +224 -0
- application_sdk/errors/base.py +504 -0
- application_sdk/errors/categories.py +118 -0
- application_sdk/errors/http.py +81 -0
- application_sdk/errors/leaves.py +526 -0
- application_sdk/errors/wire.py +124 -0
- application_sdk/handler/__init__.py +97 -0
- application_sdk/handler/_warmup.py +198 -0
- application_sdk/handler/asgi.py +252 -0
- application_sdk/handler/base.py +281 -0
- application_sdk/handler/context.py +212 -0
- application_sdk/handler/contracts.py +1166 -0
- application_sdk/handler/manifest.py +81 -0
- application_sdk/handler/metadata_routes.py +699 -0
- application_sdk/handler/object_routes.py +418 -0
- application_sdk/handler/request_contract.py +91 -0
- application_sdk/handler/routes.py +1118 -0
- application_sdk/handler/service_errors.py +37 -0
- application_sdk/server/__init__.py +15 -0
- application_sdk/server/fastapi/models.py +338 -0
- application_sdk/templates/__init__.py +52 -0
- application_sdk/templates/contracts/__init__.py +111 -0
- application_sdk/templates/contracts/base_metadata_extraction.py +32 -0
- application_sdk/templates/contracts/incremental_sql.py +563 -0
- application_sdk/templates/contracts/sql_metadata.py +824 -0
- application_sdk/templates/contracts/sql_query.py +105 -0
- application_sdk/version.py +40 -0
- atlan_application_sdk_api-3.51.0.dist-info/METADATA +230 -0
- atlan_application_sdk_api-3.51.0.dist-info/RECORD +78 -0
- 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))
|