baldwin 0.0.8__tar.gz → 0.0.9__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.

Potentially problematic release.


This version of baldwin might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: baldwin
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Simple tracking of your home directory with easy-to-read diffs.
5
5
  License: MIT
6
6
  Keywords: command line,file management,git,version control
@@ -16,14 +16,13 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Typing :: Typed
19
- Provides-Extra: erdantic
20
19
  Requires-Dist: binaryornot (>=0.4.4,<0.5.0)
21
20
  Requires-Dist: click (>=8.1.8,<9.0.0)
22
- Requires-Dist: erdantic (<2.0) ; extra == "erdantic"
21
+ Requires-Dist: colorlog (>=6.9.0,<7.0.0)
23
22
  Requires-Dist: gitpython (>=3.1.44,<4.0.0)
24
23
  Requires-Dist: platformdirs (>=4.3.6,<5.0.0)
25
24
  Requires-Dist: tomlkit (>=0.13.2,<0.14.0)
26
- Requires-Dist: typing-extensions (>=4.13.1,<5.0.0)
25
+ Requires-Dist: typing-extensions (>=4.13.2,<5.0.0)
27
26
  Project-URL: Documentation, https://baldwin.readthedocs.org
28
27
  Project-URL: Homepage, https://tatsh.github.io/baldwin/
29
28
  Project-URL: Issues, https://github.com/Tatsh/baldwin/issues
@@ -32,6 +31,7 @@ Description-Content-Type: text/markdown
32
31
 
33
32
  # Simple home directory versioning
34
33
 
34
+ [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
35
35
  [![QA](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml)
36
36
  [![Tests](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml)
37
37
  [![Coverage Status](https://coveralls.io/repos/github/Tatsh/baldwin/badge.svg?branch=master)](https://coveralls.io/github/Tatsh/baldwin?branch=master)
@@ -39,7 +39,9 @@ Description-Content-Type: text/markdown
39
39
  ![PyPI - Version](https://img.shields.io/pypi/v/baldwin)
40
40
  ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/Tatsh/baldwin)
41
41
  ![GitHub](https://img.shields.io/github/license/Tatsh/baldwin)
42
- ![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.8/master)
42
+ ![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.9/master)
43
+
44
+ [![@Tatsh](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fpublic.api.bsky.app%2Fxrpc%2Fapp.bsky.actor.getProfile%2F%3Factor%3Ddid%3Aplc%3Auq42idtvuccnmtl57nsucz72%26query%3D%24.followersCount%26style%3Dsocial%26logo%3Dbluesky%26label%3DFollow%2520%40Tatsh&query=%24.followersCount&style=social&logo=bluesky&label=Follow%20%40Tatsh)](https://bsky.app/profile/tatsh.bsky.social)
43
45
 
44
46
  This is a conversion of my simple scripts to version my home directory with very specific excludes
45
47
  and formatting every file upon commit so that readable diffs can be generated.
@@ -1,5 +1,6 @@
1
1
  # Simple home directory versioning
2
2
 
3
+ [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
3
4
  [![QA](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/qa.yml)
4
5
  [![Tests](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml/badge.svg)](https://github.com/Tatsh/baldwin/actions/workflows/tests.yml)
5
6
  [![Coverage Status](https://coveralls.io/repos/github/Tatsh/baldwin/badge.svg?branch=master)](https://coveralls.io/github/Tatsh/baldwin?branch=master)
@@ -7,7 +8,9 @@
7
8
  ![PyPI - Version](https://img.shields.io/pypi/v/baldwin)
8
9
  ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/Tatsh/baldwin)
9
10
  ![GitHub](https://img.shields.io/github/license/Tatsh/baldwin)
10
- ![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.8/master)
11
+ ![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/Tatsh/baldwin/v0.0.9/master)
12
+
13
+ [![@Tatsh](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fpublic.api.bsky.app%2Fxrpc%2Fapp.bsky.actor.getProfile%2F%3Factor%3Ddid%3Aplc%3Auq42idtvuccnmtl57nsucz72%26query%3D%24.followersCount%26style%3Dsocial%26logo%3Dbluesky%26label%3DFollow%2520%40Tatsh&query=%24.followersCount&style=social&logo=bluesky&label=Follow%20%40Tatsh)](https://bsky.app/profile/tatsh.bsky.social)
11
14
 
12
15
  This is a conversion of my simple scripts to version my home directory with very specific excludes
13
16
  and formatting every file upon commit so that readable diffs can be generated.
@@ -0,0 +1,4 @@
1
+ """baldwin module."""
2
+ from __future__ import annotations
3
+
4
+ __version__ = '0.0.9'
@@ -1,4 +1,6 @@
1
1
  """Main entry point for the Baldwin CLI."""
2
+ from __future__ import annotations
3
+
2
4
  from .main import baldwin as main
3
5
 
4
6
  main()
@@ -1,5 +1,6 @@
1
1
  """Baldwin library."""
2
- from collections.abc import Iterable
2
+ from __future__ import annotations
3
+
3
4
  from dataclasses import dataclass
4
5
  from datetime import datetime, timezone
5
6
  from importlib import resources
@@ -7,7 +8,7 @@ from itertools import chain
7
8
  from pathlib import Path
8
9
  from shlex import quote
9
10
  from shutil import which
10
- from typing import Any, Literal
11
+ from typing import TYPE_CHECKING, Any, Literal
11
12
  import logging
12
13
  import os
13
14
  import subprocess as sp
@@ -17,6 +18,9 @@ from git import Actor, Repo
17
18
  import platformdirs
18
19
  import tomlkit
19
20
 
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Iterable
23
+
20
24
  log = logging.getLogger(__name__)
21
25
 
22
26
 
@@ -61,16 +65,24 @@ def init() -> None:
61
65
 
62
66
  def auto_commit() -> None:
63
67
  """Automated commit of changed and untracked files."""
68
+ def can_open(file: Path) -> bool:
69
+ """Check if a file can be opened."""
70
+ try:
71
+ with file.open('rb'):
72
+ pass
73
+ except OSError:
74
+ return False
75
+ return True
76
+
64
77
  repo = get_repo()
65
- diff_items = [Path.home() / e.a_path for e in repo.index.diff(None)
66
- if e.a_path is not None] # pragma: no cover
78
+ diff_items = [Path.home() / e.a_path for e in repo.index.diff(None) if e.a_path is not None]
67
79
  items_to_add = [
68
80
  *[p for p in diff_items if p.exists()], *[
69
- x for x in (Path.home() / y
70
- for y in repo.untracked_files) if x.is_file() and not is_binary(str(x))
81
+ x for x in (Path.home() / y for y in repo.untracked_files)
82
+ if can_open(x) and x.is_file() and not is_binary(str(x))
71
83
  ]
72
84
  ]
73
- items_to_remove = [p for p in diff_items if not p.exists()] # pragma: no cover
85
+ items_to_remove = [p for p in diff_items if not p.exists()]
74
86
  if items_to_add:
75
87
  format_(items_to_add)
76
88
  repo.index.add(items_to_add)
@@ -96,7 +108,13 @@ def repo_info() -> RepoInfo:
96
108
 
97
109
 
98
110
  def install_units() -> None:
99
- """Install systemd units for automatic committing."""
111
+ """
112
+ Install systemd units for automatic committing.
113
+
114
+ Raises
115
+ ------
116
+ FileNotFoundError
117
+ """
100
118
  bw = which('bw')
101
119
  if not bw:
102
120
  raise FileNotFoundError
@@ -105,8 +123,9 @@ def install_units() -> None:
105
123
  Description=Home directory VCS commit
106
124
 
107
125
  [Service]
108
- Type=oneshot
126
+ Environment=NO_COLOR=1
109
127
  ExecStart={bw} auto-commit
128
+ Type=oneshot
110
129
  """)
111
130
  log.debug('Wrote to `%s`.', service_file)
112
131
  timer_file = Path('~/.config/systemd/user/home-vcs.timer').expanduser()
@@ -176,7 +195,11 @@ def format_(filenames: Iterable[Path | str] | None = None,
176
195
  for d in repo.index.diff(None) if d.a_path is not None),
177
196
  *(x for x in (Path.home() / y for y in repo.untracked_files)
178
197
  if x.is_file() and not is_binary(str(x))))
179
- if not (filenames := list(filenames)) or not (prettier := which('prettier')):
198
+ if not (filenames := list(filenames)):
199
+ log.debug('No files to format.')
200
+ return
201
+ if not (prettier := which('prettier')):
202
+ log.debug('Prettier not found in PATH.')
180
203
  return
181
204
  with resources.path('baldwin.resources', 'prettier.config.json') as default_config_file:
182
205
  config_file = get_config().get('baldwin', {
@@ -1,3 +1,6 @@
1
+ """Commands."""
2
+ from __future__ import annotations
3
+
1
4
  import logging
2
5
 
3
6
  import click
@@ -11,6 +14,7 @@ from .lib import (
11
14
  repo_info,
12
15
  set_git_env_vars,
13
16
  )
17
+ from .utils import setup_logging
14
18
 
15
19
  log = logging.getLogger(__name__)
16
20
 
@@ -22,7 +26,7 @@ __all__ = ('baldwin', 'git')
22
26
  def baldwin(*, debug: bool = False) -> None:
23
27
  """Manage a home directory with Git."""
24
28
  set_git_env_vars()
25
- logging.basicConfig(level=logging.DEBUG if debug else logging.ERROR)
29
+ setup_logging(debug=debug)
26
30
 
27
31
 
28
32
  @click.command(context_settings={
@@ -0,0 +1,46 @@
1
+ """Utilities."""
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ import logging.config
6
+
7
+ log = logging.getLogger(__name__)
8
+
9
+
10
+ def setup_logging(*,
11
+ debug: bool = False,
12
+ force_color: bool = False,
13
+ no_color: bool = False) -> None: # pragma: no cover
14
+ """Set up logging configuration."""
15
+ logging.config.dictConfig({
16
+ 'disable_existing_loggers': True,
17
+ 'root': {
18
+ 'level': 'DEBUG' if debug else 'INFO',
19
+ 'handlers': ['console'],
20
+ },
21
+ 'formatters': {
22
+ 'default': {
23
+ '()': 'colorlog.ColoredFormatter',
24
+ 'force_color': force_color,
25
+ 'format': (
26
+ '%(light_cyan)s%(asctime)s%(reset)s | %(log_color)s%(levelname)-8s%(reset)s | '
27
+ '%(light_green)s%(name)s%(reset)s:%(light_red)s%(funcName)s%(reset)s:'
28
+ '%(blue)s%(lineno)d%(reset)s - %(message)s'),
29
+ 'no_color': no_color,
30
+ }
31
+ },
32
+ 'handlers': {
33
+ 'console': {
34
+ 'class': 'colorlog.StreamHandler',
35
+ 'formatter': 'default',
36
+ }
37
+ },
38
+ 'loggers': {
39
+ 'baldwin': {
40
+ 'level': 'DEBUG' if debug else 'INFO',
41
+ 'handlers': ['console'],
42
+ 'propagate': False,
43
+ }
44
+ },
45
+ 'version': 1
46
+ })
@@ -20,15 +20,12 @@ keywords = ["command line", "file management", "git", "version control"]
20
20
  license = "MIT"
21
21
  name = "baldwin"
22
22
  readme = "README.md"
23
- version = "0.0.8"
23
+ version = "0.0.9"
24
24
 
25
25
  [[project.authors]]
26
26
  email = "audvare@gmail.com"
27
27
  name = "Andrew Udvare"
28
28
 
29
- [project.optional-dependencies]
30
- erdantic = ["erdantic<2.0"]
31
-
32
29
  [project.scripts]
33
30
  bw = "baldwin.main:baldwin"
34
31
  hgit = "baldwin.main:git"
@@ -39,22 +36,21 @@ documentation = "https://baldwin.readthedocs.org"
39
36
  homepage = "https://tatsh.github.io/baldwin/"
40
37
  repository = "https://github.com/Tatsh/baldwin"
41
38
 
42
- [tool]
43
-
44
39
  [tool.commitizen]
45
40
  tag_format = "v$version"
46
41
  version_files = [
47
42
  ".wiswa.jsonnet",
48
43
  "CITATION.cff",
49
44
  "README.md",
50
- "package.json",
51
45
  "baldwin/__init__.py",
46
+ "docs/index.rst",
47
+ "man/baldwin.1",
48
+ "package.json",
52
49
  ]
53
50
  version_provider = "pep621"
54
51
 
55
- [tool.coverage]
56
-
57
52
  [tool.coverage.report]
53
+ exclude_also = ["if TYPE_CHECKING:"]
58
54
  omit = ["conftest.py", "tests.py", "tests/test_*.py", "__main__.py"]
59
55
  show_missing = true
60
56
 
@@ -84,28 +80,27 @@ strict_optional = true
84
80
  warn_unreachable = true
85
81
 
86
82
  [tool.poetry]
87
- include = ["man"]
83
+ include = ["man", { format = "sdist", path = "tests" }]
88
84
 
89
85
  [tool.poetry.dependencies]
90
86
  binaryornot = "^0.4.4"
91
87
  click = "^8.1.8"
88
+ colorlog = "^6.9.0"
92
89
  gitpython = "^3.1.44"
93
90
  platformdirs = "^4.3.6"
94
91
  python = ">=3.10,<3.14"
95
92
  tomlkit = "^0.13.2"
96
- typing-extensions = "^4.13.1"
97
-
98
- [tool.poetry.group]
93
+ typing-extensions = "^4.13.2"
99
94
 
100
95
  [tool.poetry.group.dev]
101
96
  optional = true
102
97
 
103
98
  [tool.poetry.group.dev.dependencies]
104
99
  binaryornot-stubs = "^0"
105
- commitizen = "^4.5.0"
100
+ commitizen = "^4.7.0"
106
101
  djlint = "^1.36.4"
107
102
  mypy = ">=1.12,<1.16"
108
- ruff = "^0.11.2"
103
+ ruff = "^0.11.9"
109
104
  yapf = "^0.43.0"
110
105
 
111
106
  [tool.poetry.group.docs]
@@ -118,16 +113,23 @@ docutils = "^0.21.2"
118
113
  esbonio = "^0.16.5"
119
114
  numpydoc = "^1.8.0"
120
115
  restructuredtext-lint = "^1.4.0"
121
- sphinx = "<8.2.0"
122
116
  sphinx-click = "^6.0.0"
123
117
  sphinx-datatables = "^0.2.1"
124
118
  sphinx-hoverxref = "^1.4.2"
125
- sphinx-immaterial = "^0.13.4"
119
+ sphinx-immaterial = "^0.13.5"
126
120
 
127
121
  [tool.poetry.group.docs.dependencies.enum-tools]
128
122
  extras = ["sphinx"]
129
123
  version = "^0.12.0"
130
124
 
125
+ [[tool.poetry.group.docs.dependencies.sphinx]]
126
+ python = ">=3.11"
127
+ version = "^8.2.0"
128
+
129
+ [[tool.poetry.group.docs.dependencies.sphinx]]
130
+ python = "<3.11"
131
+ version = "^7.2.5"
132
+
131
133
  [tool.poetry.group.tests]
132
134
  optional = true
133
135
 
@@ -160,8 +162,6 @@ reportUnnecessaryTypeIgnoreComment = "none"
160
162
  typeCheckingMode = "off"
161
163
  useLibraryCodeForTypes = false
162
164
 
163
- [tool.pytest]
164
-
165
165
  [tool.pytest.ini_options]
166
166
  mock_use_standalone_module = true
167
167
  norecursedirs = ["node_modules"]
@@ -191,6 +191,7 @@ extend-select = [
191
191
  "CPY",
192
192
  "D",
193
193
  "DJ",
194
+ "DOC",
194
195
  "DTZ",
195
196
  "E",
196
197
  "EM",
@@ -198,6 +199,7 @@ extend-select = [
198
199
  "EXE",
199
200
  "F",
200
201
  "FA",
202
+ "FAST",
201
203
  "FBT",
202
204
  "FIX",
203
205
  "FLY",
@@ -229,11 +231,12 @@ extend-select = [
229
231
  "SLOT",
230
232
  "T10",
231
233
  "T20",
232
- "TCH",
234
+ "TC",
233
235
  "TD",
234
236
  "TID",
235
237
  "TRY",
236
238
  "UP",
239
+ "W",
237
240
  "YTT",
238
241
  ]
239
242
  ignore = [
@@ -245,17 +248,11 @@ ignore = [
245
248
  "C901",
246
249
  "COM812",
247
250
  "CPY001",
248
- "D100",
249
- "D101",
250
- "D102",
251
- "D103",
252
- "D104",
253
- "D105",
254
- "D106",
255
- "D107",
251
+ "D201",
256
252
  "D203",
257
253
  "D204",
258
254
  "D212",
255
+ "DOC201",
259
256
  "EM101",
260
257
  "N818",
261
258
  "PLR0912",
@@ -284,6 +281,8 @@ multiline-quotes = "double"
284
281
  case-sensitive = true
285
282
  combine-as-imports = true
286
283
  from-first = true
284
+ required-imports = ["from __future__ import annotations"]
285
+ section-order = ["future", "standard-library", "third-party", "local-folder"]
287
286
 
288
287
  [tool.ruff.lint.pep8-naming]
289
288
  extend-ignore-names = ["test_*"]
@@ -0,0 +1,24 @@
1
+ """Configuration for Pytest."""
2
+ from __future__ import annotations
3
+
4
+ from typing import NoReturn
5
+ import os
6
+
7
+ from click.testing import CliRunner
8
+ import pytest
9
+
10
+ if os.getenv('_PYTEST_RAISE', '0') != '0': # pragma no cover
11
+
12
+ @pytest.hookimpl(tryfirst=True)
13
+ def pytest_exception_interact(call: pytest.CallInfo[None]) -> NoReturn:
14
+ assert call.excinfo is not None
15
+ raise call.excinfo.value
16
+
17
+ @pytest.hookimpl(tryfirst=True)
18
+ def pytest_internalerror(excinfo: pytest.ExceptionInfo[BaseException]) -> NoReturn:
19
+ raise excinfo.value
20
+
21
+
22
+ @pytest.fixture
23
+ def runner() -> CliRunner:
24
+ return CliRunner()
@@ -0,0 +1,10 @@
1
+ [tool]
2
+
3
+ [tool.ruff]
4
+ extend = "../pyproject.toml"
5
+
6
+ [tool.ruff.lint]
7
+ extend-ignore = ["ARG001", "ARG002", "D100", "D103", "INP001", "PLC0415", "PLR2004", "S105", "S106"]
8
+
9
+ [tool.ruff.lint.pep8-naming]
10
+ extend-ignore-names = ["test_*"]
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from baldwin.main import baldwin
6
+
7
+ if TYPE_CHECKING:
8
+ from click.testing import CliRunner
9
+ from pytest_mock import MockerFixture
10
+
11
+
12
+ def test_commit(runner: CliRunner, mocker: MockerFixture) -> None:
13
+ path = mocker.patch('baldwin.lib.Path')
14
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
15
+ mocker.patch('baldwin.lib.resources')
16
+ which = mocker.patch('baldwin.lib.which')
17
+ which.return_value = None # Disable format
18
+ repo = mocker.patch('baldwin.lib.Repo')
19
+ repo.return_value.untracked_files = ['untracked1']
20
+ changed_file = mocker.MagicMock()
21
+ changed_file.a_path = 'changed1'
22
+ deleted_file = mocker.MagicMock()
23
+ deleted_file.a_path = 'deleted1'
24
+ repo.return_value.index.diff.return_value = [changed_file, deleted_file]
25
+ path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
26
+ runner.invoke(baldwin, ('auto-commit',))
27
+ assert repo.return_value.index.add.called
28
+ assert repo.return_value.index.remove.called
29
+ assert repo.return_value.index.commit.called
30
+
31
+
32
+ def test_commit_no_files(runner: CliRunner, mocker: MockerFixture) -> None:
33
+ mocker.patch('baldwin.lib.Path')
34
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
35
+ mocker.patch('baldwin.lib.resources')
36
+ which = mocker.patch('baldwin.lib.which')
37
+ which.return_value = None # Disable format
38
+ repo = mocker.patch('baldwin.lib.Repo')
39
+ repo.return_value.untracked_files = []
40
+ repo.return_value.index.diff.return_value = []
41
+ runner.invoke(baldwin, ('auto-commit',))
42
+ assert not repo.return_value.index.add.called
43
+ assert not repo.return_value.index.remove.called
44
+ assert not repo.return_value.index.commit.called
45
+
46
+
47
+ def test_commit_no_add(runner: CliRunner, mocker: MockerFixture) -> None:
48
+ path = mocker.patch('baldwin.lib.Path')
49
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
50
+ mocker.patch('baldwin.lib.resources')
51
+ which = mocker.patch('baldwin.lib.which')
52
+ which.return_value = None # Disable format
53
+ repo = mocker.patch('baldwin.lib.Repo')
54
+ repo.return_value.untracked_files = []
55
+ deleted_file = mocker.MagicMock()
56
+ deleted_file.a_path = 'deleted1'
57
+ repo.return_value.index.diff.return_value = [deleted_file]
58
+ path.home.return_value.__truediv__.return_value.exists.return_value = False
59
+ runner.invoke(baldwin, ('auto-commit',))
60
+ assert not repo.return_value.index.add.called
61
+ assert repo.return_value.index.remove.called
62
+ assert repo.return_value.index.commit.called
63
+
64
+
65
+ def test_commit_no_delete(runner: CliRunner, mocker: MockerFixture) -> None:
66
+ mocker.patch('baldwin.lib.Path')
67
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
68
+ mocker.patch('baldwin.lib.resources')
69
+ which = mocker.patch('baldwin.lib.which')
70
+ which.return_value = None # Disable format
71
+ repo = mocker.patch('baldwin.lib.Repo')
72
+ repo.return_value.untracked_files = ['untracked1']
73
+ changed_file = mocker.MagicMock()
74
+ changed_file.a_path = 'changed1'
75
+ repo.return_value.index.diff.return_value = [changed_file]
76
+ runner.invoke(baldwin, ('auto-commit',))
77
+ assert repo.return_value.index.add.called
78
+ assert not repo.return_value.index.remove.called
79
+ assert repo.return_value.index.commit.called
80
+
81
+
82
+ def test_commit_ignore_unreadable_files(runner: CliRunner, mocker: MockerFixture) -> None:
83
+ path = mocker.patch('baldwin.lib.Path')
84
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
85
+ mocker.patch('baldwin.lib.resources')
86
+ which = mocker.patch('baldwin.lib.which')
87
+ which.return_value = None # Disable format
88
+ repo = mocker.patch('baldwin.lib.Repo')
89
+ repo.return_value.untracked_files = ['untracked1']
90
+ changed_file = mocker.MagicMock()
91
+ changed_file.a_path = 'changed1'
92
+ deleted_file = mocker.MagicMock()
93
+ deleted_file.a_path = 'deleted1'
94
+ repo.return_value.index.diff.return_value = [changed_file, deleted_file]
95
+ path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
96
+ path.home.return_value.__truediv__.return_value.is_file.side_effect = [True, False, True]
97
+ path.home.return_value.__truediv__.return_value.open.side_effect = PermissionError
98
+ runner.invoke(baldwin, ('auto-commit',))
99
+ assert len(repo.return_value.index.add.call_args[0][0]) == 1
100
+ assert repo.return_value.index.remove.called
101
+ assert repo.return_value.index.commit.called
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from baldwin.main import baldwin
6
+
7
+ if TYPE_CHECKING:
8
+ from click.testing import CliRunner
9
+ from pytest_mock import MockerFixture
10
+
11
+
12
+ def test_format_no_prettier(runner: CliRunner, mocker: MockerFixture) -> None:
13
+ path = mocker.patch('baldwin.lib.Path')
14
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
15
+ mocker.patch('baldwin.lib.resources')
16
+ run = mocker.patch('baldwin.lib.sp.run')
17
+ which = mocker.patch('baldwin.lib.which')
18
+ which.return_value = None
19
+ repo = mocker.patch('baldwin.lib.Repo')
20
+ repo.untracked_files = ['untracked1']
21
+ changed_file = mocker.MagicMock()
22
+ changed_file.a_path = 'changed1'
23
+ deleted_file = mocker.MagicMock()
24
+ deleted_file.a_path = 'deleted1'
25
+ repo.return_value.index.diff.return_value = [changed_file, deleted_file]
26
+ path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
27
+ runner.invoke(baldwin, ('format',))
28
+ assert not run.called
29
+
30
+
31
+ def test_format_no_files(runner: CliRunner, mocker: MockerFixture) -> None:
32
+ mocker.patch('baldwin.lib.Path')
33
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
34
+ mocker.patch('baldwin.lib.resources')
35
+ run = mocker.patch('baldwin.lib.sp.run')
36
+ which = mocker.patch('baldwin.lib.which')
37
+ repo = mocker.patch('baldwin.lib.Repo')
38
+ repo.untracked_files = []
39
+ repo.return_value.index.diff.return_value = []
40
+ runner.invoke(baldwin, ('format',))
41
+ assert not run.called
42
+ assert not which.called
43
+
44
+
45
+ def test_format(runner: CliRunner, mocker: MockerFixture) -> None:
46
+ path = mocker.patch('baldwin.lib.Path')
47
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
48
+ mocker.patch('baldwin.lib.platformdirs.user_config_path'
49
+ ).return_value.__truediv__.return_value.exists.return_value = False
50
+ mocker.patch('baldwin.lib.resources')
51
+ run = mocker.patch('baldwin.lib.sp.run')
52
+ which = mocker.patch('baldwin.lib.which')
53
+ which.return_value = '/bin/prettier'
54
+ repo = mocker.patch('baldwin.lib.Repo')
55
+ repo.untracked_files = ['untracked1']
56
+ changed_file = mocker.MagicMock()
57
+ changed_file.a_path = 'changed1'
58
+ deleted_file = mocker.MagicMock()
59
+ deleted_file.a_path = 'deleted1'
60
+ repo.return_value.index.diff.return_value = [changed_file, deleted_file]
61
+ path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
62
+ runner.invoke(baldwin, ('format',))
63
+ assert run.called
64
+
65
+
66
+ def test_format_config_file_exists(runner: CliRunner, mocker: MockerFixture) -> None:
67
+ path = mocker.patch('baldwin.lib.Path')
68
+ mocker.patch('baldwin.lib.platformdirs.user_data_path')
69
+ mocker.patch('baldwin.lib.platformdirs.user_config_path'
70
+ ).return_value.__truediv__.return_value.exists.return_value = True
71
+ mocker.patch('baldwin.lib.resources')
72
+ mocker.patch('baldwin.lib.tomlkit.loads').return_value = {}
73
+ run = mocker.patch('baldwin.lib.sp.run')
74
+ which = mocker.patch('baldwin.lib.which')
75
+ which.return_value = '/bin/prettier'
76
+ repo = mocker.patch('baldwin.lib.Repo')
77
+ repo.untracked_files = ['untracked1']
78
+ changed_file = mocker.MagicMock()
79
+ changed_file.a_path = 'changed1'
80
+ deleted_file = mocker.MagicMock()
81
+ deleted_file.a_path = 'deleted1'
82
+ repo.return_value.index.diff.return_value = [changed_file, deleted_file]
83
+ path.home.return_value.__truediv__.return_value.exists.side_effect = [True, False, True, False]
84
+ runner.invoke(baldwin, ('format',))
85
+ assert run.called
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ from baldwin.lib import get_git_path
7
+ from baldwin.main import baldwin, git
8
+
9
+ if TYPE_CHECKING:
10
+ from click.testing import CliRunner
11
+ from pytest_mock import MockerFixture
12
+
13
+
14
+ def test_hgit_wrapper(runner: CliRunner, mocker: MockerFixture) -> None:
15
+ run = mocker.patch('baldwin.lib.sp.run')
16
+ runner.invoke(git, ('status',))
17
+ run.assert_called_once_with(
18
+ ('git', f'--git-dir={get_git_path()}', f'--work-tree={Path.home()}', 'status'), check=False)
19
+
20
+
21
+ def test_bw_git_wrapper(runner: CliRunner, mocker: MockerFixture) -> None:
22
+ run = mocker.patch('baldwin.lib.sp.run')
23
+ runner.invoke(baldwin, ('git', 'status'))
24
+ run.assert_called_once_with(
25
+ ('git', f'--git-dir={get_git_path()}', f'--work-tree={Path.home()}', 'status'), check=False)
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+ import re
5
+
6
+ from baldwin.main import baldwin
7
+
8
+ if TYPE_CHECKING:
9
+ from click.testing import CliRunner
10
+ from pytest_mock import MockerFixture
11
+
12
+
13
+ def test_init_returns_if_exists(runner: CliRunner, mocker: MockerFixture) -> None:
14
+ mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = True
15
+ mocker.patch('baldwin.lib.Path')
16
+ run = runner.invoke(baldwin, ('info',))
17
+ lines = run.stdout.splitlines()
18
+ assert re.match(r'^git-dir path: <MagicMock.*>$', lines[0])
19
+ assert re.match(r'^work-tree path: <MagicMock.*>$', lines[1])
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from baldwin.main import baldwin
6
+
7
+ if TYPE_CHECKING:
8
+ from click.testing import CliRunner
9
+ from pytest_mock import MockerFixture
10
+
11
+
12
+ def test_init_returns_if_exists(runner: CliRunner, mocker: MockerFixture) -> None:
13
+ mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = True
14
+ repo = mocker.patch('baldwin.lib.Repo')
15
+ runner.invoke(baldwin, ('init',))
16
+ assert not repo.called
17
+
18
+
19
+ def test_init(runner: CliRunner, mocker: MockerFixture) -> None:
20
+ path = mocker.patch('baldwin.lib.Path')
21
+ mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = False
22
+ repo = mocker.patch('baldwin.lib.Repo')
23
+ mocker.patch('baldwin.lib.resources')
24
+ which = mocker.patch('baldwin.lib.which')
25
+ which.side_effect = ['jq', 'prettier']
26
+ runner.invoke(baldwin, ('init',))
27
+ assert repo.init.called
28
+ assert repo.init.return_value.index.add.called
29
+ path.home.return_value.__truediv__.assert_any_call('.gitattributes')
30
+ path.home.return_value.__truediv__.assert_any_call('.gitignore')
31
+ assert path.home.return_value.__truediv__.return_value.write_text.call_count == 2
32
+ which.assert_any_call('jq')
33
+ which.assert_any_call('prettier')
34
+ repo.init.return_value.git.execute.assert_any_call(('git', 'config', 'commit.gpgsign', 'false'))
35
+ repo.init.return_value.git.execute.assert_any_call(
36
+ ('git', 'config', 'diff.json.textconv', '"jq" -MS .'))
37
+ repo.init.return_value.git.execute.assert_any_call(
38
+ ('git', 'config', 'diff.xml.textconv',
39
+ '"prettier" --no-editorconfig --parser xml --xml-whitespace-sensitivity ignore'))
40
+ repo.init.return_value.git.execute.assert_any_call(
41
+ ('git', 'config', 'diff.yaml.textconv', '"prettier" --no-editorconfig --parser yaml'))
42
+
43
+
44
+ def test_init_no_tools(runner: CliRunner, mocker: MockerFixture) -> None:
45
+ path = mocker.patch('baldwin.lib.Path')
46
+ mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = False
47
+ repo = mocker.patch('baldwin.lib.Repo')
48
+ mocker.patch('baldwin.lib.resources')
49
+ which = mocker.patch('baldwin.lib.which')
50
+ which.return_value = False
51
+ runner.invoke(baldwin, ('init',))
52
+ assert repo.init.called
53
+ path.home.return_value.__truediv__.assert_any_call('.gitattributes')
54
+ path.home.return_value.__truediv__.assert_any_call('.gitignore')
55
+ assert path.home.return_value.__truediv__.return_value.write_text.call_count == 2
56
+ which.assert_any_call('jq')
57
+ which.assert_any_call('prettier')
58
+ repo.init.return_value.git.execute.assert_any_call(('git', 'config', 'commit.gpgsign', 'false'))
59
+
60
+
61
+ def test_init_no_xml_plugin(runner: CliRunner, mocker: MockerFixture) -> None:
62
+ path = mocker.patch('baldwin.lib.Path')
63
+ mocker.patch('baldwin.lib.platformdirs.user_data_path').return_value.exists.return_value = False
64
+ repo = mocker.patch('baldwin.lib.Repo')
65
+ mocker.patch('baldwin.lib.resources')
66
+ which = mocker.patch('baldwin.lib.which')
67
+ which.side_effect = ['jq', 'prettier']
68
+ path.return_value.resolve.return_value.parent.__truediv__.return_value.__truediv__.return_value.resolve.return_value.__truediv__.return_value.exists.return_value = False # noqa: E501
69
+ runner.invoke(baldwin, ('init',))
70
+ assert repo.init.called
71
+ assert repo.init.return_value.index.add.called
72
+ path.home.return_value.__truediv__.assert_any_call('.gitattributes')
73
+ path.home.return_value.__truediv__.assert_any_call('.gitignore')
74
+ assert path.home.return_value.__truediv__.return_value.write_text.call_count == 2
75
+ which.assert_any_call('jq')
76
+ which.assert_any_call('prettier')
77
+ repo.init.return_value.git.execute.assert_any_call(('git', 'config', 'commit.gpgsign', 'false'))
78
+ repo.init.return_value.git.execute.assert_any_call(
79
+ ('git', 'config', 'diff.json.textconv', '"jq" -MS .'))
80
+ repo.init.return_value.git.execute.assert_any_call(
81
+ ('git', 'config', 'diff.yaml.textconv', '"prettier" --no-editorconfig --parser yaml'))
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from baldwin.main import baldwin
6
+
7
+ if TYPE_CHECKING:
8
+ from click.testing import CliRunner
9
+ from pytest_mock import MockerFixture
10
+
11
+
12
+ def test_install_units_no_bw(runner: CliRunner, mocker: MockerFixture) -> None:
13
+ mocker.patch('baldwin.lib.which').return_value = None
14
+ run = runner.invoke(baldwin, ('install-units',))
15
+ assert run.exit_code != 0
16
+
17
+
18
+ def test_install_units(runner: CliRunner, mocker: MockerFixture) -> None:
19
+ path = mocker.patch('baldwin.lib.Path')
20
+ mocker.patch('baldwin.lib.sp.run')
21
+ runner.invoke(baldwin, ('install-units',))
22
+ service_content = path.return_value.expanduser.return_value.write_text.mock_calls[0].args[0]
23
+ assert 'Type=oneshot' in service_content
24
+ assert 'ExecStart=' in service_content
25
+ timer_content = path.return_value.expanduser.return_value.write_text.mock_calls[1].args[0]
26
+ assert 'OnCalendar=' in timer_content
27
+ assert 'WantedBy=timers.target' in timer_content
@@ -1,2 +0,0 @@
1
- """baldwin module."""
2
- __version__ = '0.0.8'
File without changes
File without changes
File without changes