prefect-gitlab 0.3.2__tar.gz → 0.3.3__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.
- {prefect_gitlab-0.3.2/prefect_gitlab.egg-info → prefect_gitlab-0.3.3}/PKG-INFO +2 -3
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/_version.py +3 -3
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/repositories.py +50 -4
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3/prefect_gitlab.egg-info}/PKG-INFO +2 -3
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/pyproject.toml +1 -2
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/test_repositories.py +44 -3
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/LICENSE +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/MANIFEST.in +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/README.md +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/__init__.py +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/credentials.py +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/SOURCES.txt +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/dependency_links.txt +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/entry_points.txt +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/requires.txt +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/top_level.txt +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/setup.cfg +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/conftest.py +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/test_credentials.py +0 -0
- {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prefect-gitlab
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A Prefect collection for working with GitLab repositories.
|
|
5
5
|
Author-email: "Prefect Technologies, Inc." <help@prefect.io>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -11,13 +11,12 @@ Classifier: Intended Audience :: Developers
|
|
|
11
11
|
Classifier: Intended Audience :: System Administrators
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
18
|
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.10
|
|
21
20
|
Description-Content-Type: text/markdown
|
|
22
21
|
License-File: LICENSE
|
|
23
22
|
Requires-Dist: prefect>=3.0.0
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.3.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
31
|
+
__version__ = version = '0.3.3'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 3)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gd77914141'
|
|
@@ -43,6 +43,7 @@ Examples:
|
|
|
43
43
|
|
|
44
44
|
import io
|
|
45
45
|
import shutil
|
|
46
|
+
import subprocess
|
|
46
47
|
import urllib.parse
|
|
47
48
|
from pathlib import Path
|
|
48
49
|
from tempfile import TemporaryDirectory
|
|
@@ -51,8 +52,8 @@ from typing import Optional, Tuple, Union
|
|
|
51
52
|
from pydantic import Field
|
|
52
53
|
from tenacity import retry, stop_after_attempt, wait_fixed, wait_random
|
|
53
54
|
|
|
55
|
+
from prefect._internal.compatibility.async_dispatch import async_dispatch
|
|
54
56
|
from prefect.filesystems import ReadableDeploymentStorage
|
|
55
|
-
from prefect.utilities.asyncutils import sync_compatible
|
|
56
57
|
from prefect.utilities.processutils import run_process
|
|
57
58
|
from prefect_gitlab.credentials import GitLabCredentials
|
|
58
59
|
|
|
@@ -135,7 +136,6 @@ class GitLabRepository(ReadableDeploymentStorage):
|
|
|
135
136
|
|
|
136
137
|
return str(content_source), str(content_destination)
|
|
137
138
|
|
|
138
|
-
@sync_compatible
|
|
139
139
|
@retry(
|
|
140
140
|
stop=stop_after_attempt(MAX_CLONE_ATTEMPTS),
|
|
141
141
|
wait=wait_fixed(CLONE_RETRY_MIN_DELAY_SECONDS)
|
|
@@ -145,13 +145,14 @@ class GitLabRepository(ReadableDeploymentStorage):
|
|
|
145
145
|
),
|
|
146
146
|
reraise=True,
|
|
147
147
|
)
|
|
148
|
-
async def
|
|
148
|
+
async def aget_directory(
|
|
149
149
|
self, from_path: Optional[str] = None, local_path: Optional[str] = None
|
|
150
150
|
) -> None:
|
|
151
151
|
"""
|
|
152
152
|
Clones a GitLab project specified in `from_path` to the provided `local_path`;
|
|
153
153
|
defaults to cloning the repository reference configured on the Block to the
|
|
154
|
-
present working directory.
|
|
154
|
+
present working directory. Async version.
|
|
155
|
+
|
|
155
156
|
Args:
|
|
156
157
|
from_path: If provided, interpreted as a subdirectory of the underlying
|
|
157
158
|
repository that will be copied to the provided local path.
|
|
@@ -184,3 +185,48 @@ class GitLabRepository(ReadableDeploymentStorage):
|
|
|
184
185
|
shutil.copytree(
|
|
185
186
|
src=content_source, dst=content_destination, dirs_exist_ok=True
|
|
186
187
|
)
|
|
188
|
+
|
|
189
|
+
@retry(
|
|
190
|
+
stop=stop_after_attempt(MAX_CLONE_ATTEMPTS),
|
|
191
|
+
wait=wait_fixed(CLONE_RETRY_MIN_DELAY_SECONDS)
|
|
192
|
+
+ wait_random(
|
|
193
|
+
CLONE_RETRY_MIN_DELAY_JITTER_SECONDS,
|
|
194
|
+
CLONE_RETRY_MAX_DELAY_JITTER_SECONDS,
|
|
195
|
+
),
|
|
196
|
+
reraise=True,
|
|
197
|
+
)
|
|
198
|
+
@async_dispatch(aget_directory)
|
|
199
|
+
def get_directory(
|
|
200
|
+
self, from_path: Optional[str] = None, local_path: Optional[str] = None
|
|
201
|
+
) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Clones a GitLab project specified in `from_path` to the provided `local_path`;
|
|
204
|
+
defaults to cloning the repository reference configured on the Block to the
|
|
205
|
+
present working directory.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
from_path: If provided, interpreted as a subdirectory of the underlying
|
|
209
|
+
repository that will be copied to the provided local path.
|
|
210
|
+
local_path: A local path to clone to; defaults to present working directory.
|
|
211
|
+
"""
|
|
212
|
+
cmd = ["git", "clone", self._create_repo_url()]
|
|
213
|
+
if self.reference:
|
|
214
|
+
cmd += ["-b", self.reference]
|
|
215
|
+
|
|
216
|
+
if self.git_depth is not None:
|
|
217
|
+
cmd += ["--depth", str(self.git_depth)]
|
|
218
|
+
|
|
219
|
+
with TemporaryDirectory(suffix="prefect") as tmp_dir:
|
|
220
|
+
cmd.append(tmp_dir)
|
|
221
|
+
|
|
222
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
223
|
+
if result.returncode != 0:
|
|
224
|
+
raise OSError(f"Failed to pull from remote:\n {result.stderr}")
|
|
225
|
+
|
|
226
|
+
content_source, content_destination = self._get_paths(
|
|
227
|
+
dst_dir=local_path, src_dir=tmp_dir, sub_directory=from_path
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
shutil.copytree(
|
|
231
|
+
src=content_source, dst=content_destination, dirs_exist_ok=True
|
|
232
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prefect-gitlab
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A Prefect collection for working with GitLab repositories.
|
|
5
5
|
Author-email: "Prefect Technologies, Inc." <help@prefect.io>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -11,13 +11,12 @@ Classifier: Intended Audience :: Developers
|
|
|
11
11
|
Classifier: Intended Audience :: System Administrators
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
18
|
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.10
|
|
21
20
|
Description-Content-Type: text/markdown
|
|
22
21
|
License-File: LICENSE
|
|
23
22
|
Requires-Dist: prefect>=3.0.0
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
name = "prefect-gitlab"
|
|
7
7
|
description = "A Prefect collection for working with GitLab repositories."
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
10
|
license = { text = "Apache License 2.0" }
|
|
11
11
|
keywords = ["prefect"]
|
|
12
12
|
authors = [{ name = "Prefect Technologies, Inc.", email = "help@prefect.io" }]
|
|
@@ -16,7 +16,6 @@ classifiers = [
|
|
|
16
16
|
"Intended Audience :: System Administrators",
|
|
17
17
|
"License :: OSI Approved :: Apache Software License",
|
|
18
18
|
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
-
"Programming Language :: Python :: 3.9",
|
|
20
19
|
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
22
21
|
"Programming Language :: Python :: 3.12",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from tempfile import TemporaryDirectory
|
|
4
|
-
from typing import Set, Tuple
|
|
4
|
+
from typing import Coroutine, Set, Tuple
|
|
5
|
+
from unittest.mock import AsyncMock
|
|
5
6
|
|
|
6
7
|
import prefect_gitlab
|
|
7
8
|
import pytest
|
|
@@ -9,8 +10,6 @@ from prefect_gitlab.credentials import GitLabCredentials
|
|
|
9
10
|
from prefect_gitlab.repositories import GitLabRepository # noqa: E402
|
|
10
11
|
from pydantic import SecretStr
|
|
11
12
|
|
|
12
|
-
from prefect.testing.utilities import AsyncMock
|
|
13
|
-
|
|
14
13
|
|
|
15
14
|
class TestGitLab:
|
|
16
15
|
def setup_test_directory(
|
|
@@ -253,3 +252,45 @@ class TestGitLab:
|
|
|
253
252
|
print(mock.call_count)
|
|
254
253
|
# Verify that the function retried the expected number of times
|
|
255
254
|
assert mock.call_count == MAX_CLONE_ATTEMPTS
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class TestGitLabRepositoryAsyncDispatch:
|
|
258
|
+
"""Tests for GitLabRepository.get_directory migrated from @sync_compatible to @async_dispatch.
|
|
259
|
+
|
|
260
|
+
These tests verify the critical behavior from issue #15008 where
|
|
261
|
+
@sync_compatible would incorrectly return coroutines in sync context.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
def test_get_directory_sync_context_returns_none_not_coroutine(self, monkeypatch):
|
|
265
|
+
"""get_directory must return None (not coroutine) in sync context.
|
|
266
|
+
|
|
267
|
+
This is the critical regression test for issues #14712 and #14625.
|
|
268
|
+
"""
|
|
269
|
+
import subprocess
|
|
270
|
+
from unittest.mock import MagicMock
|
|
271
|
+
|
|
272
|
+
mock_result = MagicMock()
|
|
273
|
+
mock_result.returncode = 0
|
|
274
|
+
mock_result.stderr = ""
|
|
275
|
+
monkeypatch.setattr(subprocess, "run", MagicMock(return_value=mock_result))
|
|
276
|
+
|
|
277
|
+
g = GitLabRepository(repository="prefect")
|
|
278
|
+
result = g.get_directory()
|
|
279
|
+
|
|
280
|
+
assert not isinstance(result, Coroutine), "sync context returned coroutine"
|
|
281
|
+
assert result is None
|
|
282
|
+
|
|
283
|
+
async def test_get_directory_async_context_returns_coroutine(self, monkeypatch):
|
|
284
|
+
"""get_directory should dispatch to async and return coroutine in async context."""
|
|
285
|
+
|
|
286
|
+
class p:
|
|
287
|
+
returncode = 0
|
|
288
|
+
|
|
289
|
+
mock = AsyncMock(return_value=p())
|
|
290
|
+
monkeypatch.setattr(prefect_gitlab.repositories, "run_process", mock)
|
|
291
|
+
|
|
292
|
+
g = GitLabRepository(repository="prefect")
|
|
293
|
+
result = g.get_directory()
|
|
294
|
+
|
|
295
|
+
assert isinstance(result, Coroutine)
|
|
296
|
+
await result # should complete without error
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|