From d609a70ca23396f705077a5d925adf80414bbed2 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:02:59 -0500 Subject: [PATCH 01/10] refactor!(pytest plugin[{hg,git}config]) Remove `set_home` from fixture --- src/libvcs/pytest_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libvcs/pytest_plugin.py b/src/libvcs/pytest_plugin.py index fc626677..ba54f0ae 100644 --- a/src/libvcs/pytest_plugin.py +++ b/src/libvcs/pytest_plugin.py @@ -110,7 +110,7 @@ def set_home( @pytest.fixture @skip_if_git_missing -def gitconfig(user_path: pathlib.Path, set_home: pathlib.Path) -> pathlib.Path: +def gitconfig(user_path: pathlib.Path) -> pathlib.Path: """Return git configuration, pytest fixture.""" gitconfig = user_path / ".gitconfig" user_email = "libvcs@git-pull.com" @@ -145,7 +145,7 @@ def gitconfig(user_path: pathlib.Path, set_home: pathlib.Path) -> pathlib.Path: @pytest.fixture @skip_if_hg_missing -def hgconfig(user_path: pathlib.Path, set_home: pathlib.Path) -> pathlib.Path: +def hgconfig(user_path: pathlib.Path) -> pathlib.Path: """Return Mercurial configuration, pytest fixture.""" hgrc = user_path / ".hgrc" hgrc.write_text( From df589b4bf9ce3597d8b1961e6973b18f7d555bd4 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:13:48 -0500 Subject: [PATCH 02/10] refactor!(pytest plugin[{hg,git}config]) Session scoped, create setters --- src/libvcs/pytest_plugin.py | 57 ++++++++++++++++++++++++------------- tests/test_pytest_plugin.py | 22 +++++++++++++- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/libvcs/pytest_plugin.py b/src/libvcs/pytest_plugin.py index ba54f0ae..7b120dab 100644 --- a/src/libvcs/pytest_plugin.py +++ b/src/libvcs/pytest_plugin.py @@ -108,17 +108,25 @@ def set_home( monkeypatch.setenv("HOME", str(user_path)) -@pytest.fixture +vcs_email = "libvcs@git-pull.com" + + +@pytest.fixture(scope="session") @skip_if_git_missing -def gitconfig(user_path: pathlib.Path) -> pathlib.Path: +def gitconfig( + user_path: pathlib.Path, +) -> pathlib.Path: """Return git configuration, pytest fixture.""" gitconfig = user_path / ".gitconfig" - user_email = "libvcs@git-pull.com" + + if gitconfig.exists(): + return gitconfig + gitconfig.write_text( textwrap.dedent( f""" [user] - email = {user_email} + email = {vcs_email} name = {getpass.getuser()} [color] diff = auto @@ -127,26 +135,26 @@ def gitconfig(user_path: pathlib.Path) -> pathlib.Path: encoding="utf-8", ) - output = run(["git", "config", "--get", "user.email"]) - used_config_file_output = run( - [ - "git", - "config", - "--show-origin", - "--get", - "user.email", - ], - ) - assert str(gitconfig) in used_config_file_output - assert user_email in output, "Should use our fixture config and home directory" - return gitconfig @pytest.fixture +@skip_if_git_missing +def set_gitconfig( + monkeypatch: pytest.MonkeyPatch, + gitconfig: pathlib.Path, +) -> pathlib.Path: + """Set git configuration.""" + monkeypatch.setenv("GIT_CONFIG", str(gitconfig)) + return gitconfig + + +@pytest.fixture(scope="session") @skip_if_hg_missing -def hgconfig(user_path: pathlib.Path) -> pathlib.Path: - """Return Mercurial configuration, pytest fixture.""" +def hgconfig( + user_path: pathlib.Path, +) -> pathlib.Path: + """Return Mercurial configuration.""" hgrc = user_path / ".hgrc" hgrc.write_text( textwrap.dedent( @@ -164,6 +172,17 @@ def hgconfig(user_path: pathlib.Path) -> pathlib.Path: return hgrc +@pytest.fixture +@skip_if_hg_missing +def set_hgconfig( + monkeypatch: pytest.MonkeyPatch, + hgconfig: pathlib.Path, +) -> pathlib.Path: + """Set Mercurial configuration.""" + monkeypatch.setenv("HGRCPATH", str(hgconfig)) + return hgconfig + + @pytest.fixture def projects_path( user_path: pathlib.Path, diff --git a/tests/test_pytest_plugin.py b/tests/test_pytest_plugin.py index 80de63c5..072f677a 100644 --- a/tests/test_pytest_plugin.py +++ b/tests/test_pytest_plugin.py @@ -6,7 +6,8 @@ import pytest -from libvcs.pytest_plugin import CreateRepoPytestFixtureFn +from libvcs._internal.run import run +from libvcs.pytest_plugin import CreateRepoPytestFixtureFn, vcs_email @pytest.mark.skipif(not shutil.which("git"), reason="git is not available") @@ -109,3 +110,22 @@ def test_repo_git_remote_checkout( # Test result = pytester.runpytest(str(first_test_filename)) result.assert_outcomes(passed=1) + + +def test_gitconfig( + gitconfig: pathlib.Path, + set_gitconfig: pathlib.Path, +) -> None: + """Test gitconfig fixture.""" + output = run(["git", "config", "--get", "user.email"]) + used_config_file_output = run( + [ + "git", + "config", + "--show-origin", + "--get", + "user.email", + ], + ) + assert str(gitconfig) in used_config_file_output + assert vcs_email in output, "Should use our fixture config and home directory" From 041c8a2d166d358879478fce3cff18e03a815365 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:19:11 -0500 Subject: [PATCH 03/10] Revert "test(sync[hg]) Remote repository fixture" This reverts commit a70eb87296ef27ab8250b6b2474d2c99ab2cd2a0. --- tests/sync/test_hg.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/sync/test_hg.py b/tests/sync/test_hg.py index 7f32abee..b36d95b2 100644 --- a/tests/sync/test_hg.py +++ b/tests/sync/test_hg.py @@ -8,24 +8,12 @@ from libvcs import exc from libvcs._internal.run import run from libvcs._internal.shortcuts import create_project -from libvcs.pytest_plugin import CreateRepoPytestFixtureFn from libvcs.sync.hg import HgSync if not shutil.which("hg"): pytestmark = pytest.mark.skip(reason="hg is not available") -@pytest.fixture -def hg_remote_repo( - set_home: pathlib.Path, - hgconfig: pathlib.Path, - create_hg_remote_repo: CreateRepoPytestFixtureFn, -) -> pathlib.Path: - """Create a remote hg repository.""" - return create_hg_remote_repo() - - -@pytest.mark.usefixtures("set_home", "hgconfig") def test_hg_sync( tmp_path: pathlib.Path, projects_path: pathlib.Path, From 6413a7f6b6986bfc9f79ef9b9658bd0301ccdc01 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:19:39 -0500 Subject: [PATCH 04/10] Revert "test(sync[hg]) Use test fixture" This reverts commit e1db75d9d9d1ab6bad63c38bf0b8a355c88f4491. --- tests/url/test_hg.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/tests/url/test_hg.py b/tests/url/test_hg.py index 7d31dde6..ac29df22 100644 --- a/tests/url/test_hg.py +++ b/tests/url/test_hg.py @@ -1,11 +1,9 @@ """Tests for mercurial URL module.""" -import pathlib import typing import pytest -from libvcs.pytest_plugin import CreateRepoPytestFixtureFn from libvcs.sync.hg import HgSync from libvcs.url.base import RuleMap from libvcs.url.hg import DEFAULT_RULES, PIP_DEFAULT_RULES, HgBaseURL, HgURL @@ -19,16 +17,6 @@ class HgURLFixture(typing.NamedTuple): hg_url: HgURL -@pytest.fixture -def hg_repo( - set_home: pathlib.Path, - hgconfig: pathlib.Path, - create_hg_remote_repo: CreateRepoPytestFixtureFn, -) -> pathlib.Path: - """Create a remote hg repository.""" - return create_hg_remote_repo() - - TEST_FIXTURES: list[HgURLFixture] = [ HgURLFixture( url="https://bitbucket.com/vcs-python/libvcs", @@ -64,8 +52,8 @@ def test_hg_url( hg_repo: HgSync, ) -> None: """Test HgURL.""" - url = url.format(local_repo=hg_repo) - hg_url.url = hg_url.url.format(local_repo=hg_repo) + url = url.format(local_repo=hg_repo.path) + hg_url.url = hg_url.url.format(local_repo=hg_repo.path) assert HgURL.is_valid(url) == is_valid, f"{url} compatibility should be {is_valid}" assert HgURL(url) == hg_url @@ -133,10 +121,10 @@ class HgURLWithPip(HgURL): _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]}, ) - hg_url_kwargs["url"] = hg_url_kwargs["url"].format(local_repo=hg_repo) - url = url.format(local_repo=hg_repo) + hg_url_kwargs["url"] = hg_url_kwargs["url"].format(local_repo=hg_repo.path) + url = url.format(local_repo=hg_repo.path) hg_url = HgURLWithPip(**hg_url_kwargs) - hg_url.url = hg_url.url.format(local_repo=hg_repo) + hg_url.url = hg_url.url.format(local_repo=hg_repo.path) assert ( HgBaseURL.is_valid(url) != is_valid @@ -198,6 +186,6 @@ def test_hg_to_url( hg_repo: HgSync, ) -> None: """Test HgURL.to_url().""" - hg_url.url = hg_url.url.format(local_repo=hg_repo) + hg_url.url = hg_url.url.format(local_repo=hg_repo.path) assert hg_url.to_url() == expected From 37861b0a83eedc789b873859529ec39b12329dca Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:29:26 -0500 Subject: [PATCH 05/10] tests(url,sync[hg]) Set mercurial configuration --- tests/sync/test_hg.py | 8 ++++++++ tests/url/test_hg.py | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/tests/sync/test_hg.py b/tests/sync/test_hg.py index b36d95b2..20652d00 100644 --- a/tests/sync/test_hg.py +++ b/tests/sync/test_hg.py @@ -14,6 +14,14 @@ pytestmark = pytest.mark.skip(reason="hg is not available") +@pytest.fixture(autouse=True) +def set_hgconfig( + set_hgconfig: pathlib.Path, +) -> pathlib.Path: + """Set mercurial configuration.""" + return set_hgconfig + + def test_hg_sync( tmp_path: pathlib.Path, projects_path: pathlib.Path, diff --git a/tests/url/test_hg.py b/tests/url/test_hg.py index ac29df22..74d66549 100644 --- a/tests/url/test_hg.py +++ b/tests/url/test_hg.py @@ -1,5 +1,6 @@ """Tests for mercurial URL module.""" +import pathlib import typing import pytest @@ -9,6 +10,14 @@ from libvcs.url.hg import DEFAULT_RULES, PIP_DEFAULT_RULES, HgBaseURL, HgURL +@pytest.fixture(autouse=True) +def set_hgconfig( + set_hgconfig: pathlib.Path, +) -> pathlib.Path: + """Set mercurial configuration.""" + return set_hgconfig + + class HgURLFixture(typing.NamedTuple): """Test fixture for HgURL.""" From 6029dbbb02851cb62ed7e2b249f6639594bf4c1d Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:53:02 -0500 Subject: [PATCH 06/10] pytest_plugin(add_doctest_fixtures) Remove `hgconfig` and `gitconfig` (unused) --- src/libvcs/pytest_plugin.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libvcs/pytest_plugin.py b/src/libvcs/pytest_plugin.py index 7b120dab..5f831f7a 100644 --- a/src/libvcs/pytest_plugin.py +++ b/src/libvcs/pytest_plugin.py @@ -709,8 +709,6 @@ def add_doctest_fixtures( doctest_namespace: dict[str, Any], tmp_path: pathlib.Path, set_home: pathlib.Path, - gitconfig: pathlib.Path, - hgconfig: pathlib.Path, create_git_remote_repo: CreateRepoPytestFixtureFn, create_svn_remote_repo: CreateRepoPytestFixtureFn, create_hg_remote_repo: CreateRepoPytestFixtureFn, @@ -723,7 +721,6 @@ def add_doctest_fixtures( return doctest_namespace["tmp_path"] = tmp_path if shutil.which("git"): - doctest_namespace["gitconfig"] = gitconfig doctest_namespace["create_git_remote_repo"] = functools.partial( create_git_remote_repo, remote_repo_post_init=git_remote_repo_single_commit_post_init, @@ -738,7 +735,6 @@ def add_doctest_fixtures( remote_repo_post_init=svn_remote_repo_single_commit_post_init, ) if shutil.which("hg"): - doctest_namespace["hgconfig"] = hgconfig doctest_namespace["create_hg_remote_repo_bare"] = create_hg_remote_repo doctest_namespace["create_hg_remote_repo"] = functools.partial( create_hg_remote_repo, From 22beed0c748e3ff64f2532beea99ccc420b85603 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 17:07:50 -0500 Subject: [PATCH 07/10] chore(pytest_plugin,tests) ruff fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed 2 errors: - src/libvcs/pytest_plugin.py: 1 × RET504 (unnecessary-assign) - tests/sync/test_svn.py: 1 × COM812 (missing-trailing-comma) --- src/libvcs/pytest_plugin.py | 3 +-- tests/sync/test_svn.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libvcs/pytest_plugin.py b/src/libvcs/pytest_plugin.py index 5f831f7a..6968cc4c 100644 --- a/src/libvcs/pytest_plugin.py +++ b/src/libvcs/pytest_plugin.py @@ -509,8 +509,7 @@ def svn_remote_repo( create_svn_remote_repo: CreateRepoPytestFixtureFn, ) -> pathlib.Path: """Pre-made. Local file:// based SVN server.""" - repo_path = create_svn_remote_repo() - return repo_path + return create_svn_remote_repo() @pytest.fixture(scope="session") diff --git a/tests/sync/test_svn.py b/tests/sync/test_svn.py index 4b2fbee1..07ba264b 100644 --- a/tests/sync/test_svn.py +++ b/tests/sync/test_svn.py @@ -31,7 +31,8 @@ def test_svn_sync(tmp_path: pathlib.Path, svn_remote_repo: pathlib.Path) -> None def test_svn_sync_with_files( - tmp_path: pathlib.Path, svn_remote_repo_with_files: pathlib.Path + tmp_path: pathlib.Path, + svn_remote_repo_with_files: pathlib.Path, ) -> None: """Tests for SvnSync.""" repo_name = "my_svn_project" From 6330020d707a2e1fc233a2231b4a1ef0b8471336 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 16:33:10 -0500 Subject: [PATCH 08/10] docs(pytest plugin) Note `set_hgconfig` and `set_gitconfig` --- docs/pytest-plugin.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/pytest-plugin.md b/docs/pytest-plugin.md index 1b08417b..c719f12e 100644 --- a/docs/pytest-plugin.md +++ b/docs/pytest-plugin.md @@ -47,14 +47,22 @@ These fixtures are automatically used when the plugin is enabled and `pytest` is - `/home/${user}` ({func}`user_path`) - Setting your home directory - Patch `$HOME` to point to {func}`user_path` ({func}`set_home`) -- Set default configuration +- Create configuration files: - - `.gitconfig`, via {func}`gitconfig`: - - `.hgrc`, via {func}`hgconfig`: + - `.gitconfig`, via {func}`gitconfig` + - `.hgrc`, via {func}`hgconfig` + +- Set default configuration for VCS: + + - Set {func}`hgconfig` to [`HGRCPATH`] via {func}`set_hgconfig` + - Set {func}`gitconfig` to [`GIT_CONFIG`] via {func}`set_gitconfig` These are set to ensure you can correctly clone and create repositories without without extra warnings. +[`HGRCPATH`]: https://www.mercurial-scm.org/doc/hg.1.html#:~:text=UNIX%2Dlike%20environments.-,HGRCPATH,-If%20not%20set +[`GIT_CONFIG`]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIG + ## Bootstrapping pytest in your `conftest.py` The most common scenario is you will want to configure the above fixtures with `autouse`. From cb7d0a769ad1d8774dd7cb53069b361e990b8e1c Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 16:47:36 -0500 Subject: [PATCH 09/10] docs(pytest-plugin) Rephrase, note configuration setting fixtures --- docs/pytest-plugin.md | 90 +++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/docs/pytest-plugin.md b/docs/pytest-plugin.md index c719f12e..64e8e92e 100644 --- a/docs/pytest-plugin.md +++ b/docs/pytest-plugin.md @@ -1,16 +1,14 @@ (pytest_plugin)= -# `pytest` plugin +# `pytest` Plugin -Create git, svn, and hg repos on the fly in [pytest]. +With libvcs's pytest plugin for [pytest], you can easily create Git, SVN, and Mercurial repositories on the fly. -```{seealso} Using libvcs? +```{seealso} Are you using libvcs? -Do you want more flexibility? Correctness? Power? Defaults changed? [Connect with us] on the tracker, we want to know -your case, we won't stabilize APIs until we're sure everything is by the book. +Looking for more flexibility, correctness, or power? Need different defaults? [Connect with us] on GitHub. We'd love to hear about your use case—APIs won't be stabilized until we're confident everything meets expectations. [connect with us]: https://github.com/vcs-python/libvcs/discussions - ``` ```{module} libvcs.pytest_plugin @@ -21,74 +19,92 @@ your case, we won't stabilize APIs until we're sure everything is by the book. ## Usage -Install `libvcs` via the python package manager of your choosing, e.g. +Install `libvcs` using your preferred Python package manager: ```console $ pip install libvcs ``` -The pytest plugin will automatically be detected via pytest, and the fixtures will be added. +Pytest will automatically detect the plugin, and its fixtures will be available. ## Fixtures -`pytest-vcs` works through providing {ref}`pytest fixtures ` - so read up on -those! +This pytest plugin works by providing {ref}`pytest fixtures `. The plugin's fixtures ensure that a fresh Git, Subversion, or Mercurial repository is available for each test. It utilizes [session-scoped fixtures] to cache initial repositories, improving performance across tests. -The plugin's fixtures guarantee a fresh git repository every test. +[session-scoped fixtures]: https://docs.pytest.org/en/8.3.x/how-to/fixtures.html#fixture-scopes (recommended-fixtures)= -## Recommended fixtures +## Recommended Fixtures -These fixtures are automatically used when the plugin is enabled and `pytest` is run. +When the plugin is enabled and `pytest` is run, these fixtures are automatically used: -- Creating temporary, test directories for: +- Create temporary test directories for: - `/home/` ({func}`home_path`) - `/home/${user}` ({func}`user_path`) -- Setting your home directory - - Patch `$HOME` to point to {func}`user_path` ({func}`set_home`) +- Set the home directory: + - Patch `$HOME` to point to {func}`user_path` using ({func}`set_home`) - Create configuration files: + - `.gitconfig` via {func}`gitconfig` + - `.hgrc` via {func}`hgconfig` +- Set default VCS configurations: + - Use {func}`hgconfig` for [`HGRCPATH`] via {func}`set_hgconfig` + - Use {func}`gitconfig` for [`GIT_CONFIG`] via {func}`set_gitconfig` - - `.gitconfig`, via {func}`gitconfig` - - `.hgrc`, via {func}`hgconfig` +These ensure that repositories can be cloned and created without unnecessary warnings. -- Set default configuration for VCS: +[`HGRCPATH`]: https://www.mercurial-scm.org/doc/hg.1.html#:~:text=UNIX%2Dlike%20environments.-,HGRCPATH,-If%20not%20set +[`GIT_CONFIG`]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIG - - Set {func}`hgconfig` to [`HGRCPATH`] via {func}`set_hgconfig` - - Set {func}`gitconfig` to [`GIT_CONFIG`] via {func}`set_gitconfig` +## Bootstrapping pytest in `conftest.py` - These are set to ensure you can correctly clone and create repositories without without extra - warnings. +To configure the above fixtures with `autouse=True`, add them to your `conftest.py` file or test file, depending on the desired scope. -[`HGRCPATH`]: https://www.mercurial-scm.org/doc/hg.1.html#:~:text=UNIX%2Dlike%20environments.-,HGRCPATH,-If%20not%20set -[`GIT_CONFIG`]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIG +_Why aren't these fixtures added automatically by the plugin?_ This design choice promotes explicitness, adhering to best practices for pytest plugins and Python packages. -## Bootstrapping pytest in your `conftest.py` +### Setting a Temporary Home Directory -The most common scenario is you will want to configure the above fixtures with `autouse`. +To set a temporary home directory, use the {func}`set_home` fixture with `autouse=True`: -_Why doesn't the plugin automatically add them?_ It's part of being a decent pytest plugin and -python package: explicitness. +```python +import pytest + +@pytest.fixture(autouse=True) +def setup(set_home: None): + pass +``` + +### Setting a Default VCS Configuration + +#### Git + +Use the {func}`set_gitconfig` fixture with `autouse=True`: + +```python +import pytest + +@pytest.fixture(autouse=True) +def setup(set_gitconfig: None): + pass +``` -(set_home)= +#### Mercurial -### Setting a temporary home directory +Use the {func}`set_hgconfig` fixture with `autouse=True`: ```python import pytest @pytest.fixture(autouse=True) -def setup( - set_home: None, -): +def setup(set_hgconfig: None): pass ``` -## See examples +## Examples -View libvcs's own [tests/](https://github.com/vcs-python/libvcs/tree/master/tests) +For usage examples, refer to libvcs's own [tests/](https://github.com/vcs-python/libvcs/tree/master/tests). -## API reference +## API Reference ```{eval-rst} .. automodule:: libvcs.pytest_plugin From 43eebcbcc3a137ada7114d613392d9433cce92b5 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 12 Oct 2024 13:52:29 -0500 Subject: [PATCH 10/10] docs(CHANGES) Note pytest fixture revamp --- CHANGES | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES b/CHANGES index 16040888..04dc31ea 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,16 @@ $ pip install --user --upgrade --pre libvcs +## Breaking changes + +### pytest fixtures: Session-scoped `hgconfig` and `gitconfig` (#475) + +These are now set by `set_hgconfig` and `set_gitconfig`, which set `HGRCPATH` and `GIT_CONFIG`, instead of overriding `HOME`. + +## Documentation + +- Updates for pytest plugin documentation. + ## libvcs 0.31.0 (2024-10-12) ### Breaking changes