yd-cli 0.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.
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright 2026 Christian Heinze
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
yd_cli-0.3/PKG-INFO ADDED
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: yd-cli
3
+ Version: 0.3
4
+ Summary: CLI tool to synchronize directories using rsync.
5
+ Author: Christian Heinze
6
+ License-Expression: MIT
7
+ License-File: LICENSES/MIT.txt
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: System :: Archiving
15
+ Classifier: Topic :: Utilities
16
+ Classifier: Typing :: Typed
17
+ Requires-Dist: msgspec>=0.20
18
+ Requires-Dist: rich>=14.3
19
+ Requires-Dist: typer>=0.24
20
+ Requires-Python: >=3.14
21
+ Project-URL: Repository, https://codeberg.org/christianheinze/syf
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Directory synchronization tool `yd`
25
+
26
+ Build and execute `rsync` commands from *TOML* configuration files.
27
+
28
+ ## Create a config
29
+
30
+ Create a new configuration called `photos` with:
31
+
32
+ ```bash
33
+ yd edit --new photos
34
+ ```
35
+
36
+ This command
37
+
38
+ - creates `~/.config/yd/photos.toml` (or under `$XDG_CONFIG_HOME/yd` if set; relative paths are resolved from your home directory), and
39
+ - opens it in the editor selected via `EDITOR`.
40
+
41
+ Reopen an existing configuration with:
42
+
43
+ ```bash
44
+ yd edit photos
45
+ ```
46
+
47
+ ## Configuration format
48
+
49
+ ### Example
50
+
51
+ ```toml
52
+ # Phone backup
53
+ src_home = "/home/alice"
54
+ target_home = "/mnt/backup"
55
+ backup = "deleted/%Y-%m-%d"
56
+ exclude = [".venv/", "__pycache__/"]
57
+
58
+ [[commands]]
59
+ src = "Documents"
60
+ exclude = ["*.log"]
61
+
62
+ [[commands]]
63
+ src = "Pictures"
64
+ target = "pics-%Y-%m-%d"
65
+ delete_extra = false
66
+ ```
67
+
68
+ Leading comment lines directly at the top of the file are treated as the configuration description and are shown by `yd ls`.
69
+
70
+ ### Top-level options
71
+
72
+ | Key | Meaning |
73
+ | --- | --- |
74
+ | `src_home` | Base directory for all `src` paths. Relative paths are resolved from your home directory. |
75
+ | `target_home` | Base directory for all target paths. Relative paths are resolved from your home directory. `strftime` placeholders such as `%Y-%m-%d` are supported. |
76
+ | `mtp_target` | Use in-place syncing for MTP targets. |
77
+ | `backup` | Backup directory for replaced or deleted files. Relative paths are resolved from `target_home`. `strftime` placeholders are supported. |
78
+ | `exclude` | Exclude patterns applied to every command. |
79
+
80
+ `mtp_target` matters because `rsync` normally copies to a temporary file and renames it afterward, but that is not possible when syncing via *MTP*.
81
+
82
+ ### Command options
83
+
84
+ | Key | Meaning |
85
+ | --- | --- |
86
+ | `src` | Relative source directory below `src_home`. |
87
+ | `target` | Relative target directory below `target_home`; defaults to `src`. `strftime` placeholders are supported. |
88
+ | `delete_extra` | Delete files in the target that do not exist in the source. Defaults to `true`. |
89
+ | `exclude` | Extra exclude patterns for this command only. |
90
+
91
+ ### Notes
92
+
93
+ - `src` and `target` must stay within `src_home` and `target_home` after path resolution.
94
+ - `src_home` or `target_home` may be omitted from the configuration, but every missing value must then be supplied when running (via CLI parameters).
95
+ - Exactly one of `--src-home -` and `--target-home -` may read from standard input in a single run.
96
+ - If `backup` is omitted, `yd` creates a timestamped backup directory automatically unless `--no-backup` is specified.
97
+ - If a configured source directory exists but is empty, `yd` reports that no synchronization was performed for that command. E.g., forgetting to mount an external drive does not delete all corresponding copies on harddrive).
98
+
99
+ ## Run a config
100
+
101
+ Run a saved configuration by name:
102
+
103
+ ```bash
104
+ yd run photos
105
+ ```
106
+
107
+ ### Options
108
+
109
+ | Option | Meaning |
110
+ | --- | --- |
111
+ | `--dry-run` | Show what would happen without changing files. |
112
+ | `--no-backup` | Disable backup handling for this run. |
113
+ | `--keep-newer` | Skip updates when the target file is newer. |
114
+ | `--rename-speedup` | Enable `rsync` options tuned for rename-heavy targets. This may require more disk space on the target. |
115
+ | `--src-home PATH` | Override `src_home` from the config. Use `-` to read the value from standard input. |
116
+ | `--target-home PATH` | Override `target_home` from the config. Use `-` to read the value from standard input. |
117
+
118
+ ## List configs
119
+
120
+ List available configurations with:
121
+
122
+ ```bash
123
+ yd ls
124
+ ```
125
+
126
+ This shows the configuration name together with the optional leading-comment description.
127
+
128
+ ## Why `yd`?
129
+
130
+ - Has one character from `synchronize` and one from `directory`.
131
+ - Easy to type with both *QWERTZ* and *QUERTY* keyboards.
132
+ - Name was still available on *PyPI*.
yd_cli-0.3/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # Directory synchronization tool `yd`
2
+
3
+ Build and execute `rsync` commands from *TOML* configuration files.
4
+
5
+ ## Create a config
6
+
7
+ Create a new configuration called `photos` with:
8
+
9
+ ```bash
10
+ yd edit --new photos
11
+ ```
12
+
13
+ This command
14
+
15
+ - creates `~/.config/yd/photos.toml` (or under `$XDG_CONFIG_HOME/yd` if set; relative paths are resolved from your home directory), and
16
+ - opens it in the editor selected via `EDITOR`.
17
+
18
+ Reopen an existing configuration with:
19
+
20
+ ```bash
21
+ yd edit photos
22
+ ```
23
+
24
+ ## Configuration format
25
+
26
+ ### Example
27
+
28
+ ```toml
29
+ # Phone backup
30
+ src_home = "/home/alice"
31
+ target_home = "/mnt/backup"
32
+ backup = "deleted/%Y-%m-%d"
33
+ exclude = [".venv/", "__pycache__/"]
34
+
35
+ [[commands]]
36
+ src = "Documents"
37
+ exclude = ["*.log"]
38
+
39
+ [[commands]]
40
+ src = "Pictures"
41
+ target = "pics-%Y-%m-%d"
42
+ delete_extra = false
43
+ ```
44
+
45
+ Leading comment lines directly at the top of the file are treated as the configuration description and are shown by `yd ls`.
46
+
47
+ ### Top-level options
48
+
49
+ | Key | Meaning |
50
+ | --- | --- |
51
+ | `src_home` | Base directory for all `src` paths. Relative paths are resolved from your home directory. |
52
+ | `target_home` | Base directory for all target paths. Relative paths are resolved from your home directory. `strftime` placeholders such as `%Y-%m-%d` are supported. |
53
+ | `mtp_target` | Use in-place syncing for MTP targets. |
54
+ | `backup` | Backup directory for replaced or deleted files. Relative paths are resolved from `target_home`. `strftime` placeholders are supported. |
55
+ | `exclude` | Exclude patterns applied to every command. |
56
+
57
+ `mtp_target` matters because `rsync` normally copies to a temporary file and renames it afterward, but that is not possible when syncing via *MTP*.
58
+
59
+ ### Command options
60
+
61
+ | Key | Meaning |
62
+ | --- | --- |
63
+ | `src` | Relative source directory below `src_home`. |
64
+ | `target` | Relative target directory below `target_home`; defaults to `src`. `strftime` placeholders are supported. |
65
+ | `delete_extra` | Delete files in the target that do not exist in the source. Defaults to `true`. |
66
+ | `exclude` | Extra exclude patterns for this command only. |
67
+
68
+ ### Notes
69
+
70
+ - `src` and `target` must stay within `src_home` and `target_home` after path resolution.
71
+ - `src_home` or `target_home` may be omitted from the configuration, but every missing value must then be supplied when running (via CLI parameters).
72
+ - Exactly one of `--src-home -` and `--target-home -` may read from standard input in a single run.
73
+ - If `backup` is omitted, `yd` creates a timestamped backup directory automatically unless `--no-backup` is specified.
74
+ - If a configured source directory exists but is empty, `yd` reports that no synchronization was performed for that command. E.g., forgetting to mount an external drive does not delete all corresponding copies on harddrive).
75
+
76
+ ## Run a config
77
+
78
+ Run a saved configuration by name:
79
+
80
+ ```bash
81
+ yd run photos
82
+ ```
83
+
84
+ ### Options
85
+
86
+ | Option | Meaning |
87
+ | --- | --- |
88
+ | `--dry-run` | Show what would happen without changing files. |
89
+ | `--no-backup` | Disable backup handling for this run. |
90
+ | `--keep-newer` | Skip updates when the target file is newer. |
91
+ | `--rename-speedup` | Enable `rsync` options tuned for rename-heavy targets. This may require more disk space on the target. |
92
+ | `--src-home PATH` | Override `src_home` from the config. Use `-` to read the value from standard input. |
93
+ | `--target-home PATH` | Override `target_home` from the config. Use `-` to read the value from standard input. |
94
+
95
+ ## List configs
96
+
97
+ List available configurations with:
98
+
99
+ ```bash
100
+ yd ls
101
+ ```
102
+
103
+ This shows the configuration name together with the optional leading-comment description.
104
+
105
+ ## Why `yd`?
106
+
107
+ - Has one character from `synchronize` and one from `directory`.
108
+ - Easy to type with both *QWERTZ* and *QUERTY* keyboards.
109
+ - Name was still available on *PyPI*.
@@ -0,0 +1,198 @@
1
+ [build-system]
2
+ build-backend = "uv_build"
3
+ requires = [ "uv-build>=0.10,<0.12" ]
4
+
5
+ [project]
6
+ name = "yd-cli"
7
+ version = "0.3"
8
+ description = "CLI tool to synchronize directories using rsync."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = [ "LICENSES/MIT.txt" ]
12
+ authors = [ { name = "Christian Heinze" } ]
13
+ requires-python = ">=3.14"
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: End Users/Desktop",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Topic :: System :: Archiving",
22
+ "Topic :: Utilities",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ "msgspec>=0.20",
27
+ "rich>=14.3",
28
+ "typer>=0.24",
29
+ ]
30
+ urls.Repository = "https://codeberg.org/christianheinze/syf"
31
+ scripts.yd = "yd.cli:app"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "ipython>9",
36
+ { include-group = "format" },
37
+ { include-group = "lint" },
38
+ { include-group = "test" },
39
+ ]
40
+ test = [
41
+ # pytest-asyncio does not support pytest>=9 yet.
42
+ "pytest>=9",
43
+ "pytest-asyncio>=1.3",
44
+ "pytest-cov>=7",
45
+ "pytest-randomly>=4",
46
+ ]
47
+ format = [
48
+ "mdformat>=1",
49
+ "mdformat-ruff>=0.1.3",
50
+ "pyproject-fmt>=2.11",
51
+ "ruff>=0.15.8",
52
+ ]
53
+ lint = [
54
+ "ruff>=0.15.8",
55
+ "ty>=0.0.27",
56
+ ]
57
+
58
+ [tool.uv]
59
+ # Suggestion from https://mkennedy.codes/posts/python-supply-chain-security-made-easy/
60
+ build-backend.exclude-newer = "72 hours"
61
+ build-backend.module-name = "yd"
62
+
63
+ [[tool.uv.index]]
64
+ name = "testpypi"
65
+ url = "https://test.pypi.org/simple/"
66
+ publish-url = "https://test.pypi.org/legacy/"
67
+ explicit = true
68
+
69
+ [tool.ruff]
70
+ respect-gitignore = false
71
+ fix = false
72
+ unsafe-fixes = false
73
+ format.line-ending = "lf"
74
+ lint.select = [
75
+ "A", # Builtin shadowing
76
+ "ANN", # Type annotations
77
+ "ARG", # Unused arguments
78
+ "ASYNC", # Async
79
+ "B", # Common sources of bugs
80
+ "C4", # Comprehensions
81
+ "COM", # Trailing comma issues
82
+ "D", # Docstrings
83
+ "E", # pycodestyle
84
+ "ERA", # Commented-out code
85
+ "F", # pyflakes
86
+ "FURB", # General stylistic improvements
87
+ "G", # Logging string format
88
+ "I", # Imports
89
+ "ICN", # Import conventions
90
+ "INP", # No namespace packages
91
+ "LOG", # Logging
92
+ "N", # PEP8 naming
93
+ "PERF", # Performance issues.
94
+ "PGH", # Too unspecific checking excludes
95
+ "PIE",
96
+ "PL", # pylint
97
+ "PT", # pytest
98
+ "PTH", # Enfore pathlib use
99
+ "PYI",
100
+ "Q", # Quotes
101
+ "RET", # Return values
102
+ "RSE", # Exception style
103
+ "RUF", # Ruff-specific rules
104
+ "S", # Security
105
+ "SIM", # Simplifications
106
+ "SLF", # Private member access
107
+ "SLOT", # Slots requirements
108
+ "T10", # Debugger used
109
+ "TC", # Typing
110
+ "TCH", # TYPE_CHECKING sections
111
+ "TID", # Good import practices
112
+ "UP", # pyupgrade
113
+ "W",
114
+ "YTT",
115
+ ]
116
+ lint.ignore = [
117
+ # Any allowed.
118
+ "ANN401",
119
+ "COM812",
120
+ "COM819",
121
+ # Missing documentation allowed.
122
+ "D100",
123
+ "D101",
124
+ "D102",
125
+ "D103",
126
+ "D104",
127
+ "D105",
128
+ "D106",
129
+ "D107",
130
+ # No empty lines before docstrings.
131
+ "D203",
132
+ "D206",
133
+ "D213",
134
+ "D300",
135
+ # No empty line after final section of docstring.
136
+ "D413",
137
+ # Undocumented parameters allowed.
138
+ "D417",
139
+ "E111",
140
+ "E114",
141
+ "E117",
142
+ "ISC001",
143
+ "ISC002",
144
+ # Complex code allowed.
145
+ "PLR0911",
146
+ "PLR0912",
147
+ "PLR0913",
148
+ # Comparison with hardcoded literals allowed.
149
+ "PLR2004",
150
+ "Q000",
151
+ "Q001",
152
+ "Q002",
153
+ "Q003",
154
+ # `__all__` needs not be sorted.
155
+ "RUF022",
156
+ # assert statements allowed.
157
+ "S101",
158
+ # pseudo random number warning.
159
+ "S311",
160
+ # shell parameter not required in subprocess calls.
161
+ "S603",
162
+ "S604",
163
+ # Should not be using when using ruff formatter
164
+ "W191",
165
+ ]
166
+ lint.flake8-tidy-imports.ban-relative-imports = "all"
167
+ lint.flake8-type-checking.runtime-evaluated-base-classes = [
168
+ "pydantic.BaseModel",
169
+ ]
170
+ lint.flake8-type-checking.runtime-evaluated-decorators = [
171
+ "pydantic.validate_call",
172
+ ]
173
+ lint.flake8-unused-arguments.ignore-variadic-names = true
174
+ lint.isort.required-imports = [ "from __future__ import annotations" ]
175
+
176
+ [tool.pyproject-fmt]
177
+ column_width = 88
178
+ indent = 4
179
+ max_supported_python = "3.14"
180
+
181
+ [tool.ty]
182
+ environment.python-platform = "linux"
183
+ environment.root = [ "src/" ]
184
+ src.include = [ "src/", "tests/" ]
185
+ src.respect-ignore-files = false
186
+
187
+ [tool.pytest]
188
+ minversion = "9.0"
189
+ addopts = [
190
+ "--import-mode=importlib",
191
+ "--strict-markers",
192
+ "--cov=yd",
193
+ "--cov=tests",
194
+ ]
195
+ testpaths = [ "tests" ]
196
+ python_files = [ "test_*.py" ]
197
+ filterwarnings = [ "error" ]
198
+ asyncio_default_fixture_loop_scope = "function"
@@ -0,0 +1,30 @@
1
+ # SPDX-FileCopyrightText: Christian Heinze
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """The yd synchronization tool.
5
+
6
+ Do not directly import items from this module (e.g. `from yd import io`) which
7
+ triggers `__getattr__` and thereby eager importing of the module. Instead, import
8
+ `yd` and use attribute access.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import importlib
14
+ import importlib.metadata
15
+
16
+ TYPE_CHECKING = False
17
+ if TYPE_CHECKING:
18
+ from types import ModuleType
19
+
20
+ from yd import commands, execution, io, types
21
+ else:
22
+
23
+ def __getattr__(key: str, /) -> ModuleType:
24
+ if key in __all__:
25
+ return importlib.import_module(f"{__package__}.{key}", package=__package__)
26
+ raise AttributeError(f"module `{__name__}` has not attribute `{key}`")
27
+
28
+
29
+ __version__ = importlib.metadata.version("yd-cli")
30
+ __all__ = ["__version__", "commands", "io", "execution", "types"]
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: Christian Heinze
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """CLI entry point."""
5
+
6
+ from __future__ import annotations
7
+
8
+ if __name__ == "__main__":
9
+ from yd import cli
10
+
11
+ cli.app()