Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ Deletions and renames that exercise library behavior or prepare a fixture for
reuse must remain strict. If a cached fixture cannot be cleaned up, rebuild it
at a fresh location before handing it to another test.

## Network-dependent tests

Mark tests that use the network with `pytest.mark.net`, including tests whose
fixtures start a local Git daemon or download packages. This includes binding,
listening on, and connecting to loopback sockets, not just internet access.
Network-restricted package builders, such as SUSE and openSUSE build servers, can
exclude these tests with `pytest -m 'not net'` after installing test dependencies.
If only some parameters use the network, mark those with
`pytest.param(..., marks=pytest.mark.net)` so the other cases still run. Local
filesystem clones and mocked network calls do not need this marker.

## Fuzzing Test Specific Documentation

For details related to contributing to the fuzzing test suite and OSS-Fuzz integration, please
Expand Down
51 changes: 21 additions & 30 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ build-backend = "setuptools.build_meta"
name = "GitPython"
description = "GitPython is a Python library used to interact with Git repositories"
authors = [
{name = "Sebastian Thiel", email = "byronimo@gmail.com"},
{name = "Michael Trier", email = "mtrier@gmail.com"},
{ name = "Sebastian Thiel", email = "byronimo@gmail.com" },
{ name = "Michael Trier", email = "mtrier@gmail.com" },
]
readme = "README.md"
requires-python = ">=3.8"
Expand Down Expand Up @@ -51,10 +51,10 @@ doc = [
include-package-data = true

[tool.setuptools.dynamic]
version = {file = "VERSION"}
dependencies = {file = "requirements.txt"}
optional-dependencies."test" = {file = "test-requirements.txt"}
optional-dependencies."gix" = {file = "gix-requirements.txt"}
version = { file = "VERSION" }
dependencies = { file = "requirements.txt" }
optional-dependencies."test" = { file = "test-requirements.txt" }
optional-dependencies."gix" = { file = "gix-requirements.txt" }

[tool.setuptools.packages.find]
include = ["git", "git.*"]
Expand All @@ -64,7 +64,7 @@ addopts = "--cov=git --cov-report=term -rfEX"
filterwarnings = "ignore::DeprecationWarning"
python_files = "test_*.py"
tmp_path_retention_policy = "failed"
testpaths = "test" # Space separated list of paths from root e.g test tests doc/testing.
testpaths = "test" # Space separated list of paths from root e.g test tests doc/testing.
# --cov coverage
# --cov-report term # send report to terminal term-missing -> terminal with line numbers html xml
# --cov-report term-missing # to terminal with line numbers
Expand All @@ -74,6 +74,9 @@ testpaths = "test" # Space separated list of paths from root e.g test tests doc
# -ra # test summary: list all non-passing (fail, error, skip, xfail, xpass)
# --ignore-glob=**/gitdb/* # ignore glob paths
# filterwarnings ignore::WarningType # ignores those warnings
markers = [
"net: tests requiring network access, including local/loopback sockets; skip with -m 'not net' on network-restricted builders",
]

[tool.mypy]
files = ["git/", "test/test_typing.py", "test/test_remote_progress.py"]
Expand All @@ -100,14 +103,8 @@ include = [
"test/test_git.py",
"test/test_remote_progress.py",
]
extraPaths = [
"gitdb",
"smmap",
]
exclude = [
"gitdb",
"smmap",
]
extraPaths = ["gitdb", "smmap"]
exclude = ["gitdb", "smmap"]

[tool.coverage.run]
source = ["git"]
Expand All @@ -119,39 +116,33 @@ omit = ["*/git/ext/*"]
[tool.ruff]
line-length = 120
# Exclude a variety of commonly ignored directories.
exclude = [
"git/ext/",
"gitdb/",
"smmap/",
"build",
"dist",
]
exclude = ["git/ext/", "gitdb/", "smmap/", "build", "dist"]
# Enable Pyflakes `E` and `F` codes by default.
lint.select = [
"E",
"W", # See: https://pypi.org/project/pycodestyle
"F", # See: https://pypi.org/project/pyflakes
"W", # See: https://pypi.org/project/pycodestyle
"F", # See: https://pypi.org/project/pyflakes
# "I", # See: https://pypi.org/project/isort/
# "S", # See: https://pypi.org/project/flake8-bandit
# "UP", # See: https://docs.astral.sh/ruff/rules/#pyupgrade-up
]
lint.extend-select = [
# "A", # See: https://pypi.org/project/flake8-builtins
"B", # See: https://pypi.org/project/flake8-bugbear
"C4", # See: https://pypi.org/project/flake8-comprehensions
"TC004", # See: https://docs.astral.sh/ruff/rules/runtime-import-in-type-checking-block/
"B", # See: https://pypi.org/project/flake8-bugbear
"C4", # See: https://pypi.org/project/flake8-comprehensions
"TC004", # See: https://docs.astral.sh/ruff/rules/runtime-import-in-type-checking-block/
]
lint.ignore = [
# If it becomes necessary to ignore any rules, list them here.
]
lint.unfixable = [
"F401", # Module imported but unused
"F401", # Module imported but unused
]

[tool.ruff.lint.per-file-ignores]
"test/**" = [
"B018", # useless-expression
"B018", # useless-expression
]
"fuzzing/fuzz-targets/**" = [
"E402", # environment setup must happen before the `git` module is imported, thus cannot happen at top of file
"E402", # environment setup must happen before the `git` module is imported, thus cannot happen at top of file
]
12 changes: 7 additions & 5 deletions test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/

import gc
from io import BytesIO
import os.path as osp
import sys
from io import BytesIO
from unittest import skipIf
from unittest.mock import patch

from gitdb import OInfo
import pytest

import git.objects.base as base
from git import Repo
from git.objects import Blob, Commit, TagObject, Tree
import git.objects.base as base
from git.objects.util import get_object_type_by_name
from git.util import hex_to_bin

from test.lib import TestBase as _TestBase, with_rw_and_rw_remote_repo, with_rw_repo
from gitdb import OInfo
from test.lib import TestBase as _TestBase
from test.lib import with_rw_and_rw_remote_repo, with_rw_repo
from test.lib.fixtures import fixture_repo, writable_fixture


Expand Down Expand Up @@ -122,6 +123,7 @@ def test_with_rw_repo(self, rw_repo: Repo):
assert osp.isdir(osp.join(rw_repo.working_tree_dir, "lib"))
assert osp.isdir(rw_repo.working_dir)

@pytest.mark.net
@skipIf(sys.platform == "win32", "FIXME: Freezes! sometimes...")
@with_rw_and_rw_remote_repo("0.1.6")
def test_with_rw_remote_and_rw_repo(self, rw_repo, rw_remote_repo):
Expand Down
21 changes: 14 additions & 7 deletions test/test_cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@

"""Cleanup failures must not fail, skip, or hide the result of a test."""

from contextlib import nullcontext, suppress
import errno
import gc
import os
from pathlib import Path
import socket
import sys
from types import SimpleNamespace
import weakref
from contextlib import nullcontext, suppress
from pathlib import Path
from types import SimpleNamespace

import pytest

from git import Repo
from git.util import rmtree
from test import cleanup
from test.cleanup import TemporaryDirectory, cleanup_directory
from test.lib import requires_symlinks, with_rw_and_rw_remote_repo, with_rw_directory, with_rw_repo
from test.lib.helper import git_daemon_launched
from test.lib import fixtures, requires_symlinks, with_rw_and_rw_remote_repo, with_rw_directory, with_rw_repo
from test.lib.fixtures import fixture_repo, writable_fixture
from test.lib import fixtures
from test.lib.helper import git_daemon_launched


@pytest.fixture
Expand Down Expand Up @@ -183,6 +182,7 @@ def source_repo():
yield repo


@pytest.mark.net
def test_git_daemon_releases_port_and_can_restart(tmp_path):
with socket.socket() as listener:
listener.bind(("127.0.0.1", 0))
Expand All @@ -202,7 +202,14 @@ def test_git_daemon_releases_port_and_can_restart(tmp_path):
pass


@pytest.mark.parametrize("decorator", [with_rw_directory, with_rw_repo("HEAD"), with_rw_and_rw_remote_repo("HEAD")])
@pytest.mark.parametrize(
"decorator",
[
with_rw_directory,
with_rw_repo("HEAD"),
pytest.param(with_rw_and_rw_remote_repo("HEAD"), marks=pytest.mark.net),
],
)
def test_decorators_preserve_success_with_locked_cleanup(source_repo, tmp_path, monkeypatch, locked_file, decorator):
monkeypatch.setattr(cleanup.tempfile, "tempdir", str(tmp_path))
directories = []
Expand Down
18 changes: 8 additions & 10 deletions test/test_clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,18 @@
import os
import os.path as osp
import pathlib
import re
import sys
from unittest import skip
from unittest import mock
from pathlib import Path
from unittest import mock, skip

import pytest

import git
from git import Git, GitCommandError, Repo
from git.exc import UnsafeOptionError, UnsafeProtocolError

from test.lib import PathLikeMock, TestBase, with_rw_directory, with_rw_repo
from test.lib.fixtures import writable_fixture
from test.lib import TestBase, with_rw_directory, with_rw_repo, PathLikeMock

from pathlib import Path
import re

import git
import pytest


@pytest.fixture
Expand Down Expand Up @@ -145,6 +142,7 @@ def test_clone_from_with_path_contains_unicode(self):
except UnicodeEncodeError:
self.fail("Raised UnicodeEncodeError")

@pytest.mark.net
@with_rw_directory
@skip(
"""The referenced repository was removed, and one needs to set up a new
Expand Down
3 changes: 3 additions & 0 deletions test/test_installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
import subprocess
import sys

import pytest

from git import __version__, _backend
from test.lib import TestCase, requires_symlinks
from test.lib.fixtures import ROOT, writable_fixture


class TestInstallation(TestCase):
@pytest.mark.net
@requires_symlinks
def test_installation(self):
# Virtualenv executables and its source symlink need their final location.
Expand Down
7 changes: 5 additions & 2 deletions test/test_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
import gc
import os
import os.path as osp
from pathlib import Path
import random
import socket
import sys
import tempfile
import threading
import time
from pathlib import Path
from unittest import mock, skipIf

import pytest
Expand All @@ -34,14 +34,14 @@
from git.exc import UnsafeOptionError, UnsafeProtocolError
from git.util import IterableList
from test.cleanup import cleanup_directory
from test.lib.fixtures import fixture_repo, writable_fixture
from test.lib import (
GIT_DAEMON_PORT,
TestBase,
fixture,
with_rw_and_rw_remote_repo,
with_rw_repo,
)
from test.lib.fixtures import fixture_repo, writable_fixture

# Make sure we have repeatable results.
random.seed(0)
Expand Down Expand Up @@ -441,6 +441,7 @@ def _assert_push_and_pull(self, remote, rw_repo, remote_repo):
TagReference.delete(rw_repo, new_tag, other_tag)
remote.push(":%s" % other_tag.path, kill_after_timeout=10.0)

@pytest.mark.net
@skipIf(sys.platform == "win32", "FIXME: Freezes!")
@with_rw_and_rw_remote_repo("0.1.6")
def test_base(self, rw_repo, remote_repo):
Expand Down Expand Up @@ -1031,6 +1032,7 @@ def test_ls_remote_unsafe_options_allowed(self, rw_repo):
with self.assertRaises(GitCommandError):
rw_repo.git.ls_remote(".", **unsafe_option, allow_unsafe_options=True)

@pytest.mark.net
@with_rw_and_rw_remote_repo("0.1.6")
def test_fetch_unsafe_branch_name(self, rw_repo, remote_repo):
# Create branch with a name containing a NBSP
Expand All @@ -1048,6 +1050,7 @@ def test_fetch_unsafe_branch_name(self, rw_repo, remote_repo):
Head.delete(remote_repo, bad_branch_name)


@pytest.mark.net
@pytest.mark.skipif(sys.platform == "win32", reason="kill_after_timeout is not supported on Windows")
@pytest.mark.parametrize("protocol", ["git", "http", "https"])
@pytest.mark.parametrize("operation", ["fetch", "pull", "push"])
Expand Down
Loading