| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,8 @@ | |||
| 19 | 19 | "GIT_REPO", | |
| 20 | 20 | "GIT_DAEMON_PORT", | |
| 21 | 21 | "xfail_if_raises", | |
| 22 | + "symlinks_supported", | ||
| 23 | + "requires_symlinks", | ||
| 22 | 24 | ] | |
| 23 | 25 | ||
| 24 | 26 | import contextlib | |
@@ -29,6 +31,7 @@ | |||
| 29 | 31 | import logging | |
| 30 | 32 | import os | |
| 31 | 33 | import os.path as osp | |
| 34 | + from stat import S_ISLNK, ST_MODE | ||
| 32 | 35 | import subprocess | |
| 33 | 36 | import sys | |
| 34 | 37 | import tempfile | |
@@ -491,6 +494,28 @@ def _executable(self, basename): | |||
| 491 | 494 | raise RuntimeError(f"no regular file or symlink {path!r}") | |
| 492 | 495 | ||
| 493 | 496 | ||
| 497 | + def symlinks_supported() -> bool: | ||
| 498 | + """Check whether this process can actually create a symlink. | ||
| 499 | + | ||
| 500 | + On Windows the platform alone doesn't decide it: creating a symlink needs either | ||
| 501 | + Developer Mode or SeCreateSymbolicLinkPrivilege, and an unprivileged process gets | ||
| 502 | + OSError (WinError 1314) instead. | ||
| 503 | + """ | ||
| 504 | + with tempfile.TemporaryDirectory(prefix="gitpython-symlink-check-") as temp_dir: | ||
| 505 | + link_path = osp.join(temp_dir, "link") | ||
| 506 | + try: | ||
| 507 | + os.symlink("missing-target", link_path) | ||
| 508 | + except (NotImplementedError, OSError): | ||
| 509 | + return False | ||
| 510 | + return S_ISLNK(os.lstat(link_path)[ST_MODE]) | ||
| 511 | + | ||
| 512 | + | ||
| 513 | + requires_symlinks = pytest.mark.skipif( | ||
| 514 | + not symlinks_supported(), | ||
| 515 | + reason="symlinks are unavailable, or need privileges this process doesn't have", | ||
| 516 | + ) | ||
| 517 | + | ||
| 518 | + | ||
| 494 | 519 | @contextlib.contextmanager | |
| 495 | 520 | def xfail_if_raises( | |
| 496 | 521 | condition: bool, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -40,7 +40,7 @@ | |||
| 40 | 40 | from git.util import Actor, cwd, hex_to_bin, rmtree | |
| 41 | 41 | ||
| 42 | 42 | from test.lib import TestBase, VirtualEnvironment, fixture, fixture_path, with_rw_directory, with_rw_repo, PathLikeMock | |
| 43 | - from test.lib.helper import xfail_if_raises | ||
| 43 | + from test.lib.helper import symlinks_supported, xfail_if_raises | ||
| 44 | 44 | ||
| 45 | 45 | HOOKS_SHEBANG = "#!/usr/bin/env sh\n" | |
| 46 | 46 | ||
@@ -175,19 +175,6 @@ def _decode(stdout): | |||
| 175 | 175 | _win_bash_status = WinBashStatus.check() | |
| 176 | 176 | ||
| 177 | 177 | ||
| 178 | - def _windows_supports_symlinks(): | ||
| 179 | - if sys.platform != "win32": | ||
| 180 | - return False | ||
| 181 | - | ||
| 182 | - with tempfile.TemporaryDirectory(prefix="gitpython-symlink-check-") as temp_dir: | ||
| 183 | - link_path = osp.join(temp_dir, "link") | ||
| 184 | - try: | ||
| 185 | - os.symlink("missing-target", link_path) | ||
| 186 | - except (NotImplementedError, OSError): | ||
| 187 | - return False | ||
| 188 | - return S_ISLNK(os.lstat(link_path)[ST_MODE]) | ||
| 189 | - | ||
| 190 | - | ||
| 191 | 178 | def _make_hook(git_dir, name, content, make_exec=True): | |
| 192 | 179 | """A helper to create a hook""" | |
| 193 | 180 | hp = hook_path(name, git_dir) | |
@@ -655,7 +642,7 @@ def _count_existing(self, repo, files): | |||
| 655 | 642 | @with_rw_repo("0.1.6") | |
| 656 | 643 | def test_index_mutation(self, rw_repo): | |
| 657 | 644 | with xfail_if_raises( | |
| 658 | - sys.platform == "win32" and (Git().config("core.symlinks") == "true" or _windows_supports_symlinks()), | ||
| 645 | + sys.platform == "win32" and (Git().config("core.symlinks") == "true" or symlinks_supported()), | ||
| 659 | 646 | raises=(FileNotFoundError, GitCommandError), | |
| 660 | 647 | reason="Assumes symlinks are not created on Windows and opens a symlink to a nonexistent target.", | |
| 661 | 648 | ): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,10 +6,11 @@ | |||
| 6 | 6 | import os | |
| 7 | 7 | import subprocess | |
| 8 | 8 | ||
| 9 | - from test.lib import TestBase, VirtualEnvironment, with_rw_directory | ||
| 9 | + from test.lib import TestBase, VirtualEnvironment, requires_symlinks, with_rw_directory | ||
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | 12 | class TestInstallation(TestBase): | |
| 13 | + @requires_symlinks | ||
| 13 | 14 | @with_rw_directory | |
| 14 | 15 | def test_installation(self, rw_dir): | |
| 15 | 16 | venv, run = self._set_up_venv(rw_dir) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,7 +28,7 @@ | |||
| 28 | 28 | import git.refs as refs | |
| 29 | 29 | from git.util import Actor | |
| 30 | 30 | ||
| 31 | - from test.lib import TestBase, with_rw_repo, PathLikeMock | ||
| 31 | + from test.lib import TestBase, requires_symlinks, with_rw_repo, PathLikeMock | ||
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | 34 | class TestRefs(TestBase): | |
@@ -780,6 +780,7 @@ def test_symbolic_reference_log_append_rejects_path_traversal(self): | |||
| 780 | 780 | ) | |
| 781 | 781 | assert not outside_path.exists() | |
| 782 | 782 | ||
| 783 | + @requires_symlinks | ||
| 783 | 784 | def test_symbolic_reference_set_reference_rejects_symlink_escape(self): | |
| 784 | 785 | with tempfile.TemporaryDirectory() as tmp_dir: | |
| 785 | 786 | base_dir = Path(tmp_dir) | |
@@ -791,10 +792,7 @@ def test_symbolic_reference_set_reference_rejects_symlink_escape(self): | |||
| 791 | 792 | refs_heads_dir = Path(repo.common_dir) / "refs" / "heads" | |
| 792 | 793 | refs_heads_dir.mkdir(parents=True, exist_ok=True) | |
| 793 | 794 | symlink_path = refs_heads_dir / "link_out" | |
| 794 | - try: | ||
| 795 | - symlink_path.symlink_to(outside_dir, target_is_directory=True) | ||
| 796 | - except (OSError, NotImplementedError) as ex: | ||
| 797 | - self.skipTest("symlinks unavailable on this platform: %s" % ex) | ||
| 795 | + symlink_path.symlink_to(outside_dir, target_is_directory=True) | ||
| 798 | 796 | if osp.realpath(symlink_path / "escaped") == osp.abspath(symlink_path / "escaped"): | |
| 799 | 797 | self.skipTest("realpath does not resolve directory symlinks on this platform") | |
| 800 | 798 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -43,7 +43,7 @@ | |||
| 43 | 43 | from git.repo.fun import touch | |
| 44 | 44 | from git.util import bin_to_hex, cwd, cygpath, join_path_native, rmfile, rmtree | |
| 45 | 45 | ||
| 46 | - from test.lib import TestBase, fixture, with_rw_directory, with_rw_repo, PathLikeMock | ||
| 46 | + from test.lib import TestBase, fixture, requires_symlinks, with_rw_directory, with_rw_repo, PathLikeMock | ||
| 47 | 47 | ||
| 48 | 48 | ||
| 49 | 49 | def iter_flatten(lol): | |
@@ -1433,6 +1433,7 @@ def test_ignored_items_reported(self): | |||
| 1433 | 1433 | ["included_file.txt", "ignored_file.txt", "included_dir/file.txt", "ignored_dir/file.txt"] | |
| 1434 | 1434 | ) == ["ignored_file.txt", "ignored_dir/file.txt"] | |
| 1435 | 1435 | ||
| 1436 | + @requires_symlinks | ||
| 1436 | 1437 | def test_ignored_raises_error_w_symlink(self): | |
| 1437 | 1438 | with tempfile.TemporaryDirectory() as tdir: | |
| 1438 | 1439 | tmp_dir = pathlib.Path(tdir) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -40,7 +40,7 @@ | |||
| 40 | 40 | rmtree, | |
| 41 | 41 | ) | |
| 42 | 42 | ||
| 43 | - from test.lib import TestBase, with_rw_repo | ||
| 43 | + from test.lib import TestBase, requires_symlinks, with_rw_repo | ||
| 44 | 44 | ||
| 45 | 45 | ||
| 46 | 46 | @pytest.fixture | |
@@ -113,6 +113,7 @@ def test_deletes_dir_with_readonly_files(self, tmp_path): | |||
| 113 | 113 | sys.platform == "cygwin", | |
| 114 | 114 | reason="Cygwin can't set the permissions that make the test meaningful.", | |
| 115 | 115 | ) | |
| 116 | + @requires_symlinks | ||
| 116 | 117 | def test_avoids_changing_permissions_outside_tree(self, tmp_path, request): | |
| 117 | 118 | # Automatically works on Windows, but on Unix requires either special handling | |
| 118 | 119 | # or refraining from attempting to fix PermissionError by making chmod calls. | |
| Back | FazBrowse Home | New Git URL |
0 commit comments