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.
Files changed (20) hide show
  1. {prefect_gitlab-0.3.2/prefect_gitlab.egg-info → prefect_gitlab-0.3.3}/PKG-INFO +2 -3
  2. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/_version.py +3 -3
  3. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/repositories.py +50 -4
  4. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3/prefect_gitlab.egg-info}/PKG-INFO +2 -3
  5. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/pyproject.toml +1 -2
  6. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/test_repositories.py +44 -3
  7. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/LICENSE +0 -0
  8. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/MANIFEST.in +0 -0
  9. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/README.md +0 -0
  10. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/__init__.py +0 -0
  11. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab/credentials.py +0 -0
  12. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/SOURCES.txt +0 -0
  13. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/dependency_links.txt +0 -0
  14. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/entry_points.txt +0 -0
  15. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/requires.txt +0 -0
  16. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/prefect_gitlab.egg-info/top_level.txt +0 -0
  17. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/setup.cfg +0 -0
  18. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/conftest.py +0 -0
  19. {prefect_gitlab-0.3.2 → prefect_gitlab-0.3.3}/tests/test_credentials.py +0 -0
  20. {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.2
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.9
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.2'
32
- __version_tuple__ = version_tuple = (0, 3, 2)
31
+ __version__ = version = '0.3.3'
32
+ __version_tuple__ = version_tuple = (0, 3, 3)
33
33
 
34
- __commit_id__ = commit_id = 'g8a37e7b1b'
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 get_directory(
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.2
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.9
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"
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