sy-python 0.2.0__cp38-abi3-manylinux_2_28_x86_64.whl
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.
- sy_python-0.2.0.dist-info/METADATA +30 -0
- sy_python-0.2.0.dist-info/RECORD +12 -0
- sy_python-0.2.0.dist-info/WHEEL +4 -0
- sy_python-0.2.0.dist-info/entry_points.txt +3 -0
- sypy/__init__.py +136 -0
- sypy/__main__.py +21 -0
- sypy/_sypy.abi3.so +0 -0
- sypy/_sypy.pyi +521 -0
- sypy/ls_types.py +55 -0
- sypy/py.typed +1 -0
- sypy.libs/libcrypto-bfee2032.so.1.1 +0 -0
- sypy.libs/libssl-658e53cd.so.1.1 +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sy-python
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Operating System :: MacOS
|
|
8
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
9
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
|
+
Classifier: Programming Language :: Rust
|
|
18
|
+
Classifier: Topic :: System :: Archiving :: Backup
|
|
19
|
+
Classifier: Topic :: System :: Filesystems
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
22
|
+
Requires-Dist: tqdm>=4.0 ; extra == 'dev'
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Summary: Python bindings for sy - Modern file synchronization tool
|
|
25
|
+
Keywords: rsync,sync,file-transfer,backup
|
|
26
|
+
License: MIT
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Project-URL: Documentation, https://github.com/nijaru/sy#readme
|
|
29
|
+
Project-URL: Homepage, https://github.com/nijaru/sy
|
|
30
|
+
Project-URL: Repository, https://github.com/nijaru/sy
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
sy_python-0.2.0.dist-info/METADATA,sha256=YJoTuHqL-0RMkybFU-Simf37G47X-vvMayEahAr7gCk,1286
|
|
2
|
+
sy_python-0.2.0.dist-info/WHEEL,sha256=RkO9vuZebefxBO82xhdx_Vslv-lk-9t3-S9x5Fqg2yw,107
|
|
3
|
+
sy_python-0.2.0.dist-info/entry_points.txt,sha256=NPHeXCspWz0vGpS57shSO3ezGr5DOiByIM4ytzH_sJc,64
|
|
4
|
+
sypy/__init__.py,sha256=kUQ1kbnGgG15AhivJcHMjdIDjB0e8KD7za5xz4etnGg,2988
|
|
5
|
+
sypy/__main__.py,sha256=K9CZ6AJGxOFYSkuWRmvpm2bynZu1Ws_qDie_IPO8cjg,411
|
|
6
|
+
sypy/_sypy.abi3.so,sha256=1Qim8LWAipqsKs4e3ldWn9Ui_BHWDIreb_PUPiwOufw,20740713
|
|
7
|
+
sypy/_sypy.pyi,sha256=w12Ij8XV6p2f_XytXr-Wfk4YTwVWQSsEAPez3UWdUcs,13815
|
|
8
|
+
sypy/ls_types.py,sha256=PIm8Q9jEo-b5OaGxpJ4mcTgG4Dn4uXQHFvT9ARbiu7M,1299
|
|
9
|
+
sypy/py.typed,sha256=qww23I-O7yv_Kf5Wu2UW3r8K1Vd12UDESBl5tmzRSI0,60
|
|
10
|
+
sypy.libs/libcrypto-bfee2032.so.1.1,sha256=vENxnY50N4f5NMoJR5ElTBXei4_x6ZjNLP7oMzBioo8,3215921
|
|
11
|
+
sypy.libs/libssl-658e53cd.so.1.1,sha256=59sUdvtjU-6lBFU6e8nNeH96zgPwWQ9GIIuaOUUOASs,666857
|
|
12
|
+
sy_python-0.2.0.dist-info/RECORD,,
|
sypy/__init__.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sypy - Python bindings for sy file synchronization tool.
|
|
3
|
+
|
|
4
|
+
Fast, modern file synchronization with support for local, SSH, S3, and GCS.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> import sypy
|
|
8
|
+
>>> stats = sypy.sync("/source", "/dest")
|
|
9
|
+
>>> print(f"Synced {stats.files_created} files")
|
|
10
|
+
|
|
11
|
+
# With options
|
|
12
|
+
>>> stats = sypy.sync(
|
|
13
|
+
... "/source", "/dest",
|
|
14
|
+
... dry_run=True,
|
|
15
|
+
... exclude=["*.log", "node_modules"],
|
|
16
|
+
... )
|
|
17
|
+
|
|
18
|
+
# With S3 credentials
|
|
19
|
+
>>> s3 = sypy.S3Config(
|
|
20
|
+
... access_key_id="...",
|
|
21
|
+
... secret_access_key="...",
|
|
22
|
+
... region="us-east-1",
|
|
23
|
+
... )
|
|
24
|
+
>>> stats = sypy.sync("/local/", "s3://bucket/path/", s3=s3)
|
|
25
|
+
|
|
26
|
+
# With GCS credentials
|
|
27
|
+
>>> gcs = sypy.GcsConfig(credentials_file="/path/to/key.json")
|
|
28
|
+
>>> stats = sypy.sync("/local/", "gs://bucket/path/", gcs=gcs)
|
|
29
|
+
|
|
30
|
+
# With custom client options for high throughput
|
|
31
|
+
>>> options = sypy.CloudClientOptions.high_throughput()
|
|
32
|
+
>>> s3 = sypy.S3Config(..., client_options=options)
|
|
33
|
+
>>> stats = sypy.sync("/local/", "s3://bucket/", s3=s3, parallel=100)
|
|
34
|
+
|
|
35
|
+
# Dry-run with typed summary
|
|
36
|
+
>>> stats = sypy.sync("/source", "/dest", dry_run=True)
|
|
37
|
+
>>> summary = stats.dry_run_summary
|
|
38
|
+
>>> print(f"Would create {summary['would_create']['count']} files")
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from typing import TypedDict
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DryRunChange(TypedDict):
|
|
46
|
+
"""Details about changes that would be made"""
|
|
47
|
+
|
|
48
|
+
count: int
|
|
49
|
+
bytes: int
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class DryRunSummary(TypedDict):
|
|
53
|
+
"""Summary of what would happen in a dry-run"""
|
|
54
|
+
|
|
55
|
+
would_create: DryRunChange
|
|
56
|
+
would_update: DryRunChange
|
|
57
|
+
would_delete: DryRunChange
|
|
58
|
+
total_files: int
|
|
59
|
+
total_bytes: int
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Import TypedDict for ls functionality
|
|
63
|
+
from sypy._sypy import (
|
|
64
|
+
# Dry-run classes
|
|
65
|
+
ChangeAction,
|
|
66
|
+
# Client options
|
|
67
|
+
CloudClientOptions,
|
|
68
|
+
DirectoryChange,
|
|
69
|
+
DryRunDetails,
|
|
70
|
+
FileChange,
|
|
71
|
+
# Config classes
|
|
72
|
+
GcsConfig,
|
|
73
|
+
# List classes
|
|
74
|
+
ListEntry,
|
|
75
|
+
# Progress classes
|
|
76
|
+
ProgressSnapshot,
|
|
77
|
+
S3Config,
|
|
78
|
+
SshConfig,
|
|
79
|
+
SymlinkChange,
|
|
80
|
+
# Classes
|
|
81
|
+
SyncError,
|
|
82
|
+
SyncOptions,
|
|
83
|
+
SyncPath,
|
|
84
|
+
SyncStats,
|
|
85
|
+
# Version
|
|
86
|
+
__version__,
|
|
87
|
+
# Functions
|
|
88
|
+
ls,
|
|
89
|
+
# CLI functions
|
|
90
|
+
main,
|
|
91
|
+
parse_path,
|
|
92
|
+
run_daemon,
|
|
93
|
+
run_server,
|
|
94
|
+
sync,
|
|
95
|
+
sync_with_options,
|
|
96
|
+
)
|
|
97
|
+
from sypy.ls_types import ListEntryDict
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
# Dry-run classes
|
|
101
|
+
"ChangeAction",
|
|
102
|
+
# Client options
|
|
103
|
+
"CloudClientOptions",
|
|
104
|
+
"DirectoryChange",
|
|
105
|
+
# TypedDicts
|
|
106
|
+
"DryRunChange",
|
|
107
|
+
"DryRunDetails",
|
|
108
|
+
"DryRunSummary",
|
|
109
|
+
"FileChange",
|
|
110
|
+
# Config classes
|
|
111
|
+
"GcsConfig",
|
|
112
|
+
# List classes
|
|
113
|
+
"ListEntry",
|
|
114
|
+
"ListEntryDict",
|
|
115
|
+
# Progress classes
|
|
116
|
+
"ProgressSnapshot",
|
|
117
|
+
"S3Config",
|
|
118
|
+
"SshConfig",
|
|
119
|
+
"SymlinkChange",
|
|
120
|
+
# Classes
|
|
121
|
+
"SyncError",
|
|
122
|
+
"SyncOptions",
|
|
123
|
+
"SyncPath",
|
|
124
|
+
"SyncStats",
|
|
125
|
+
# Version
|
|
126
|
+
"__version__",
|
|
127
|
+
# Functions
|
|
128
|
+
"ls",
|
|
129
|
+
# CLI functions
|
|
130
|
+
"main",
|
|
131
|
+
"parse_path",
|
|
132
|
+
"run_daemon",
|
|
133
|
+
"run_server",
|
|
134
|
+
"sync",
|
|
135
|
+
"sync_with_options",
|
|
136
|
+
]
|
sypy/__main__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entry point for `python -m sypy` command.
|
|
3
|
+
|
|
4
|
+
This allows running sy as a Python module:
|
|
5
|
+
python -m sypy /source /dest
|
|
6
|
+
python -m sypy --server /path
|
|
7
|
+
python -m sypy --daemon --socket ~/.sy/daemon.sock
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> int:
|
|
14
|
+
"""Main entry point for the sy CLI."""
|
|
15
|
+
from sypy._sypy import main as _main
|
|
16
|
+
|
|
17
|
+
return _main(sys.argv)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
sys.exit(main())
|
sypy/_sypy.abi3.so
ADDED
|
Binary file
|
sypy/_sypy.pyi
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
__version__: str
|
|
4
|
+
|
|
5
|
+
class CloudClientOptions:
|
|
6
|
+
pool_max_idle_per_host: int
|
|
7
|
+
"""Maximum idle connections per host. Default: 50."""
|
|
8
|
+
|
|
9
|
+
pool_idle_timeout_secs: int
|
|
10
|
+
"""How long to keep idle connections (seconds). Default: 30."""
|
|
11
|
+
|
|
12
|
+
connect_timeout_secs: int
|
|
13
|
+
"""Connection timeout (seconds). Default: 5."""
|
|
14
|
+
|
|
15
|
+
request_timeout_secs: int
|
|
16
|
+
"""Request timeout including transfer (seconds). Default: 60."""
|
|
17
|
+
|
|
18
|
+
max_retries: int
|
|
19
|
+
"""Maximum retry attempts. Default: 3."""
|
|
20
|
+
|
|
21
|
+
retry_timeout_secs: int
|
|
22
|
+
"""Maximum time for retries (seconds). Default: 15."""
|
|
23
|
+
|
|
24
|
+
allow_http: bool
|
|
25
|
+
"""Allow HTTP (non-TLS) connections. Default: False."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
pool_max_idle_per_host: int = 50,
|
|
30
|
+
pool_idle_timeout_secs: int = 30,
|
|
31
|
+
connect_timeout_secs: int = 5,
|
|
32
|
+
request_timeout_secs: int = 60,
|
|
33
|
+
max_retries: int = 3,
|
|
34
|
+
retry_timeout_secs: int = 15,
|
|
35
|
+
allow_http: bool = False,
|
|
36
|
+
) -> None: ...
|
|
37
|
+
@staticmethod
|
|
38
|
+
def high_throughput() -> CloudClientOptions: ...
|
|
39
|
+
@staticmethod
|
|
40
|
+
def low_latency() -> CloudClientOptions: ...
|
|
41
|
+
|
|
42
|
+
class S3Config:
|
|
43
|
+
access_key_id: str | None
|
|
44
|
+
"""AWS access key ID."""
|
|
45
|
+
|
|
46
|
+
secret_access_key: str | None
|
|
47
|
+
"""AWS secret access key."""
|
|
48
|
+
|
|
49
|
+
session_token: str | None
|
|
50
|
+
"""AWS session token (for temporary credentials)."""
|
|
51
|
+
|
|
52
|
+
region: str | None
|
|
53
|
+
"""AWS region (e.g., "us-east-1")."""
|
|
54
|
+
|
|
55
|
+
endpoint: str | None
|
|
56
|
+
"""Custom endpoint URL for S3-compatible services."""
|
|
57
|
+
|
|
58
|
+
profile: str | None
|
|
59
|
+
"""AWS profile name to use from ~/.aws/credentials."""
|
|
60
|
+
|
|
61
|
+
client_options: CloudClientOptions | None
|
|
62
|
+
"""HTTP client options (timeouts, retries, connection pool)."""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
access_key_id: str | None = None,
|
|
67
|
+
secret_access_key: str | None = None,
|
|
68
|
+
session_token: str | None = None,
|
|
69
|
+
region: str | None = None,
|
|
70
|
+
endpoint: str | None = None,
|
|
71
|
+
profile: str | None = None,
|
|
72
|
+
client_options: CloudClientOptions | None = None,
|
|
73
|
+
) -> None: ...
|
|
74
|
+
|
|
75
|
+
class GcsConfig:
|
|
76
|
+
credentials_file: str | None
|
|
77
|
+
"""Path to service account JSON key file."""
|
|
78
|
+
|
|
79
|
+
project_id: str | None
|
|
80
|
+
"""GCP project ID."""
|
|
81
|
+
|
|
82
|
+
credentials_json: str | None
|
|
83
|
+
"""Service account JSON as a string (alternative to credentials_file)."""
|
|
84
|
+
|
|
85
|
+
client_options: CloudClientOptions | None
|
|
86
|
+
"""HTTP client options (timeouts, retries, connection pool)."""
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
credentials_file: str | None = None,
|
|
91
|
+
project_id: str | None = None,
|
|
92
|
+
credentials_json: str | None = None,
|
|
93
|
+
client_options: CloudClientOptions | None = None,
|
|
94
|
+
) -> None: ...
|
|
95
|
+
|
|
96
|
+
class SshConfig:
|
|
97
|
+
key_file: str | None
|
|
98
|
+
"""Path to private key file."""
|
|
99
|
+
|
|
100
|
+
port: int | None
|
|
101
|
+
"""SSH port (default: 22)."""
|
|
102
|
+
|
|
103
|
+
password: str | None
|
|
104
|
+
"""SSH password (usually not needed with key authentication)."""
|
|
105
|
+
|
|
106
|
+
compression: bool
|
|
107
|
+
"""Enable compression for SSH connection."""
|
|
108
|
+
|
|
109
|
+
proxy_jump: str | None
|
|
110
|
+
"""Proxy jump host (e.g., "bastion@proxy.example.com")."""
|
|
111
|
+
|
|
112
|
+
connect_timeout: int | None
|
|
113
|
+
"""Connection timeout in seconds."""
|
|
114
|
+
|
|
115
|
+
pool_size: int | None
|
|
116
|
+
"""Number of parallel SSH connections."""
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
key_file: str | None = None,
|
|
121
|
+
port: int | None = None,
|
|
122
|
+
password: str | None = None,
|
|
123
|
+
compression: bool = False,
|
|
124
|
+
proxy_jump: str | None = None,
|
|
125
|
+
connect_timeout: int | None = None,
|
|
126
|
+
pool_size: int | None = None,
|
|
127
|
+
) -> None: ...
|
|
128
|
+
|
|
129
|
+
class SyncError:
|
|
130
|
+
path: str
|
|
131
|
+
"""Path that caused the error."""
|
|
132
|
+
|
|
133
|
+
error: str
|
|
134
|
+
"""Error message."""
|
|
135
|
+
|
|
136
|
+
action: str
|
|
137
|
+
"""Action that was being performed."""
|
|
138
|
+
|
|
139
|
+
class SyncStats:
|
|
140
|
+
files_scanned: int
|
|
141
|
+
"""Number of files scanned."""
|
|
142
|
+
|
|
143
|
+
files_created: int
|
|
144
|
+
"""Number of files created."""
|
|
145
|
+
|
|
146
|
+
files_updated: int
|
|
147
|
+
"""Number of files updated."""
|
|
148
|
+
|
|
149
|
+
files_skipped: int
|
|
150
|
+
"""Number of files skipped (already up-to-date)."""
|
|
151
|
+
|
|
152
|
+
files_deleted: int
|
|
153
|
+
"""Number of files deleted."""
|
|
154
|
+
|
|
155
|
+
bytes_transferred: int
|
|
156
|
+
"""Total bytes transferred."""
|
|
157
|
+
|
|
158
|
+
files_delta_synced: int
|
|
159
|
+
"""Number of files synced using delta algorithm."""
|
|
160
|
+
|
|
161
|
+
delta_bytes_saved: int
|
|
162
|
+
"""Bytes saved by delta sync."""
|
|
163
|
+
|
|
164
|
+
files_compressed: int
|
|
165
|
+
"""Number of files compressed during transfer."""
|
|
166
|
+
|
|
167
|
+
compression_bytes_saved: int
|
|
168
|
+
"""Bytes saved by compression."""
|
|
169
|
+
|
|
170
|
+
files_verified: int
|
|
171
|
+
"""Number of files verified."""
|
|
172
|
+
|
|
173
|
+
verification_failures: int
|
|
174
|
+
"""Number of verification failures."""
|
|
175
|
+
|
|
176
|
+
duration_secs: float
|
|
177
|
+
"""Duration of the sync operation in seconds."""
|
|
178
|
+
|
|
179
|
+
bytes_would_add: int
|
|
180
|
+
"""Bytes that would be added (dry-run only)."""
|
|
181
|
+
|
|
182
|
+
bytes_would_change: int
|
|
183
|
+
"""Bytes that would change (dry-run only)."""
|
|
184
|
+
|
|
185
|
+
bytes_would_delete: int
|
|
186
|
+
"""Bytes that would be deleted (dry-run only)."""
|
|
187
|
+
|
|
188
|
+
dirs_created: int
|
|
189
|
+
"""Number of directories created."""
|
|
190
|
+
|
|
191
|
+
symlinks_created: int
|
|
192
|
+
"""Number of symlinks created."""
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def errors(self) -> list[SyncError]: ...
|
|
196
|
+
@property
|
|
197
|
+
def success(self) -> bool: ...
|
|
198
|
+
@property
|
|
199
|
+
def transfer_rate(self) -> float: ...
|
|
200
|
+
@property
|
|
201
|
+
def dry_run_summary(self) -> dict[str, dict[str, int] | int]: ...
|
|
202
|
+
|
|
203
|
+
class ProgressSnapshot:
|
|
204
|
+
total_bytes: int
|
|
205
|
+
"""Estimated total bytes to process (may be 0 if unknown)."""
|
|
206
|
+
|
|
207
|
+
bytes: int
|
|
208
|
+
"""Bytes completed so far."""
|
|
209
|
+
|
|
210
|
+
bytes_per_sec: int
|
|
211
|
+
"""Instantaneous speed in bytes per second."""
|
|
212
|
+
|
|
213
|
+
transfers: int
|
|
214
|
+
"""Number of transfers (files) completed."""
|
|
215
|
+
|
|
216
|
+
total_transfers: int
|
|
217
|
+
"""Total number of transfers planned."""
|
|
218
|
+
|
|
219
|
+
active_transfers: int
|
|
220
|
+
"""Number of currently active (in-flight) transfers."""
|
|
221
|
+
|
|
222
|
+
percentage: float | None
|
|
223
|
+
"""Percentage complete (0.0 to 100.0), None if total_bytes is 0/unknown."""
|
|
224
|
+
|
|
225
|
+
elapsed_secs: float
|
|
226
|
+
"""Elapsed time in seconds since sync started."""
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def transferring(self) -> list[str]: ...
|
|
230
|
+
"""List of currently transferring file paths."""
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def current_file(self) -> str | None: ...
|
|
234
|
+
"""Current file being transferred (first in transferring list), or None."""
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def speed_human(self) -> str: ...
|
|
238
|
+
"""Speed as a human-readable string (e.g., "10.5 MB/s")."""
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def bytes_human(self) -> str: ...
|
|
242
|
+
"""Bytes as a human-readable string (e.g., "1.5 GB")."""
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def total_bytes_human(self) -> str: ...
|
|
246
|
+
"""Total bytes as a human-readable string (e.g., "10.2 GB")."""
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def eta_secs(self) -> float | None: ...
|
|
250
|
+
"""Estimated time remaining in seconds, or None if unknown."""
|
|
251
|
+
|
|
252
|
+
class SyncPath:
|
|
253
|
+
def __init__(self, path: str) -> None: ...
|
|
254
|
+
@property
|
|
255
|
+
def path(self) -> str: ...
|
|
256
|
+
@property
|
|
257
|
+
def is_local(self) -> bool: ...
|
|
258
|
+
@property
|
|
259
|
+
def is_remote(self) -> bool: ...
|
|
260
|
+
@property
|
|
261
|
+
def is_s3(self) -> bool: ...
|
|
262
|
+
@property
|
|
263
|
+
def is_gcs(self) -> bool: ...
|
|
264
|
+
@property
|
|
265
|
+
def is_daemon(self) -> bool: ...
|
|
266
|
+
@property
|
|
267
|
+
def has_trailing_slash(self) -> bool: ...
|
|
268
|
+
@property
|
|
269
|
+
def host(self) -> str | None: ...
|
|
270
|
+
@property
|
|
271
|
+
def user(self) -> str | None: ...
|
|
272
|
+
@property
|
|
273
|
+
def bucket(self) -> str | None: ...
|
|
274
|
+
|
|
275
|
+
class SyncOptions:
|
|
276
|
+
dry_run: bool
|
|
277
|
+
"""Dry run mode - show changes without applying."""
|
|
278
|
+
|
|
279
|
+
delete: bool
|
|
280
|
+
"""Delete files in destination not present in source."""
|
|
281
|
+
|
|
282
|
+
delete_threshold: int
|
|
283
|
+
"""Maximum percentage of files that can be deleted (0-100)."""
|
|
284
|
+
|
|
285
|
+
trash: bool
|
|
286
|
+
"""Move deleted files to trash instead of permanent deletion."""
|
|
287
|
+
|
|
288
|
+
force_delete: bool
|
|
289
|
+
"""Skip deletion safety checks."""
|
|
290
|
+
|
|
291
|
+
parallel: int
|
|
292
|
+
"""Number of parallel file transfers."""
|
|
293
|
+
|
|
294
|
+
max_errors: int
|
|
295
|
+
"""Maximum number of errors before aborting (0 = unlimited)."""
|
|
296
|
+
|
|
297
|
+
min_size: str | None
|
|
298
|
+
"""Minimum file size to sync (e.g., "1MB")."""
|
|
299
|
+
|
|
300
|
+
max_size: str | None
|
|
301
|
+
"""Maximum file size to sync (e.g., "1GB")."""
|
|
302
|
+
|
|
303
|
+
exclude: list[str]
|
|
304
|
+
"""Exclude patterns."""
|
|
305
|
+
|
|
306
|
+
include: list[str]
|
|
307
|
+
"""Include patterns."""
|
|
308
|
+
|
|
309
|
+
bwlimit: str | None
|
|
310
|
+
"""Bandwidth limit (e.g., "10MB")."""
|
|
311
|
+
|
|
312
|
+
resume: bool
|
|
313
|
+
"""Enable resume support for interrupted transfers."""
|
|
314
|
+
|
|
315
|
+
verify: bool
|
|
316
|
+
"""Verify file integrity after write."""
|
|
317
|
+
|
|
318
|
+
compress: bool
|
|
319
|
+
"""Enable compression for network transfers."""
|
|
320
|
+
|
|
321
|
+
preserve_xattrs: bool
|
|
322
|
+
"""Preserve extended attributes."""
|
|
323
|
+
|
|
324
|
+
preserve_hardlinks: bool
|
|
325
|
+
"""Preserve hard links."""
|
|
326
|
+
|
|
327
|
+
preserve_acls: bool
|
|
328
|
+
"""Preserve access control lists."""
|
|
329
|
+
|
|
330
|
+
preserve_permissions: bool
|
|
331
|
+
"""Preserve permissions."""
|
|
332
|
+
|
|
333
|
+
preserve_times: bool
|
|
334
|
+
"""Preserve modification times."""
|
|
335
|
+
|
|
336
|
+
ignore_times: bool
|
|
337
|
+
"""Ignore modification times, always compare checksums."""
|
|
338
|
+
|
|
339
|
+
size_only: bool
|
|
340
|
+
"""Only compare file size, skip mtime checks."""
|
|
341
|
+
|
|
342
|
+
checksum: bool
|
|
343
|
+
"""Always compare checksums instead of size+mtime."""
|
|
344
|
+
|
|
345
|
+
update: bool
|
|
346
|
+
"""Skip files where destination is newer."""
|
|
347
|
+
|
|
348
|
+
ignore_existing: bool
|
|
349
|
+
"""Skip files that already exist in destination."""
|
|
350
|
+
|
|
351
|
+
gitignore: bool
|
|
352
|
+
"""Apply .gitignore rules."""
|
|
353
|
+
|
|
354
|
+
exclude_vcs: bool
|
|
355
|
+
"""Exclude .git directories."""
|
|
356
|
+
|
|
357
|
+
bidirectional: bool
|
|
358
|
+
"""Bidirectional sync mode."""
|
|
359
|
+
|
|
360
|
+
conflict_resolve: str
|
|
361
|
+
"""Conflict resolution strategy ("newer", "larger", "smaller", "source", "dest", "rename")."""
|
|
362
|
+
|
|
363
|
+
daemon_auto: bool
|
|
364
|
+
"""Use daemon mode for fast repeated syncs."""
|
|
365
|
+
|
|
366
|
+
retry: int
|
|
367
|
+
"""Maximum retry attempts for network operations."""
|
|
368
|
+
|
|
369
|
+
retry_delay: int
|
|
370
|
+
"""Initial delay between retries in seconds."""
|
|
371
|
+
|
|
372
|
+
s3: S3Config | None
|
|
373
|
+
"""S3 configuration for S3/S3-compatible storage."""
|
|
374
|
+
|
|
375
|
+
gcs: GcsConfig | None
|
|
376
|
+
"""GCS configuration for Google Cloud Storage."""
|
|
377
|
+
|
|
378
|
+
ssh: SshConfig | None
|
|
379
|
+
"""SSH configuration for remote connections."""
|
|
380
|
+
|
|
381
|
+
def __init__(
|
|
382
|
+
self,
|
|
383
|
+
dry_run: bool = False,
|
|
384
|
+
delete: bool = False,
|
|
385
|
+
delete_threshold: int = 50,
|
|
386
|
+
trash: bool = False,
|
|
387
|
+
force_delete: bool = False,
|
|
388
|
+
parallel: int = 10,
|
|
389
|
+
max_errors: int = 100,
|
|
390
|
+
min_size: str | None = None,
|
|
391
|
+
max_size: str | None = None,
|
|
392
|
+
exclude: list[str] | None = None,
|
|
393
|
+
include: list[str] | None = None,
|
|
394
|
+
bwlimit: str | None = None,
|
|
395
|
+
resume: bool = True,
|
|
396
|
+
verify: bool = False,
|
|
397
|
+
compress: bool = False,
|
|
398
|
+
preserve_xattrs: bool = False,
|
|
399
|
+
preserve_hardlinks: bool = False,
|
|
400
|
+
preserve_acls: bool = False,
|
|
401
|
+
preserve_permissions: bool = False,
|
|
402
|
+
preserve_times: bool = False,
|
|
403
|
+
ignore_times: bool = False,
|
|
404
|
+
size_only: bool = False,
|
|
405
|
+
checksum: bool = False,
|
|
406
|
+
update: bool = False,
|
|
407
|
+
ignore_existing: bool = False,
|
|
408
|
+
gitignore: bool = False,
|
|
409
|
+
exclude_vcs: bool = False,
|
|
410
|
+
bidirectional: bool = False,
|
|
411
|
+
conflict_resolve: str = "newer",
|
|
412
|
+
daemon_auto: bool = False,
|
|
413
|
+
retry: int = 3,
|
|
414
|
+
retry_delay: int = 1,
|
|
415
|
+
s3: S3Config | None = None,
|
|
416
|
+
gcs: GcsConfig | None = None,
|
|
417
|
+
ssh: SshConfig | None = None,
|
|
418
|
+
) -> None: ...
|
|
419
|
+
|
|
420
|
+
# Type alias for progress callback
|
|
421
|
+
type ProgressCallback = Callable[[ProgressSnapshot], None]
|
|
422
|
+
"""
|
|
423
|
+
Progress callback function signature.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
snapshot: ProgressSnapshot describing current sync state
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
def sync(
|
|
430
|
+
source: str,
|
|
431
|
+
dest: str,
|
|
432
|
+
*,
|
|
433
|
+
dry_run: bool = False,
|
|
434
|
+
delete: bool = False,
|
|
435
|
+
delete_threshold: int = 50,
|
|
436
|
+
parallel: int = 10,
|
|
437
|
+
verify: bool = False,
|
|
438
|
+
compress: bool = False,
|
|
439
|
+
checksum: bool = False,
|
|
440
|
+
exclude: list[str] | None = None,
|
|
441
|
+
include: list[str] | None = None,
|
|
442
|
+
min_size: str | None = None,
|
|
443
|
+
max_size: str | None = None,
|
|
444
|
+
bwlimit: str | None = None,
|
|
445
|
+
progress_callback: ProgressCallback | None = None,
|
|
446
|
+
progress_frequency_ms: int = 1000,
|
|
447
|
+
daemon_auto: bool = False,
|
|
448
|
+
resume: bool = True,
|
|
449
|
+
ignore_times: bool = False,
|
|
450
|
+
size_only: bool = False,
|
|
451
|
+
update: bool = False,
|
|
452
|
+
ignore_existing: bool = False,
|
|
453
|
+
gitignore: bool = False,
|
|
454
|
+
exclude_vcs: bool = False,
|
|
455
|
+
preserve_xattrs: bool = False,
|
|
456
|
+
preserve_hardlinks: bool = False,
|
|
457
|
+
preserve_permissions: bool = False,
|
|
458
|
+
preserve_times: bool = False,
|
|
459
|
+
retry: int = 3,
|
|
460
|
+
retry_delay: int = 1,
|
|
461
|
+
s3: S3Config | None = None,
|
|
462
|
+
gcs: GcsConfig | None = None,
|
|
463
|
+
ssh: SshConfig | None = None,
|
|
464
|
+
) -> SyncStats: ...
|
|
465
|
+
def sync_with_options(
|
|
466
|
+
source: str,
|
|
467
|
+
dest: str,
|
|
468
|
+
options: SyncOptions,
|
|
469
|
+
progress_callback: ProgressCallback | None = None,
|
|
470
|
+
progress_frequency_ms: int = 1000,
|
|
471
|
+
) -> SyncStats: ...
|
|
472
|
+
def parse_path(path: str) -> SyncPath: ...
|
|
473
|
+
|
|
474
|
+
class ListEntry:
|
|
475
|
+
path: str
|
|
476
|
+
"""Relative path of the entry."""
|
|
477
|
+
|
|
478
|
+
size: int
|
|
479
|
+
"""File size in bytes (0 for directories)."""
|
|
480
|
+
|
|
481
|
+
mod_time: str
|
|
482
|
+
"""Modification time in RFC3339 format."""
|
|
483
|
+
|
|
484
|
+
is_dir: bool
|
|
485
|
+
"""Whether this is a directory."""
|
|
486
|
+
|
|
487
|
+
entry_type: str
|
|
488
|
+
"""Entry type: "file", "directory", or "symlink"."""
|
|
489
|
+
|
|
490
|
+
mime_type: str | None
|
|
491
|
+
"""MIME type (inferred from extension)."""
|
|
492
|
+
|
|
493
|
+
symlink_target: str | None
|
|
494
|
+
"""Symlink target path (if this is a symlink)."""
|
|
495
|
+
|
|
496
|
+
is_sparse: bool | None
|
|
497
|
+
"""Whether this is a sparse file."""
|
|
498
|
+
|
|
499
|
+
allocated_size: int | None
|
|
500
|
+
"""Actual allocated size on disk (may differ from size for sparse files)."""
|
|
501
|
+
|
|
502
|
+
inode: int | None
|
|
503
|
+
"""Inode number (Unix only)."""
|
|
504
|
+
|
|
505
|
+
num_links: int | None
|
|
506
|
+
"""Number of hard links to this file."""
|
|
507
|
+
|
|
508
|
+
def to_dict(self) -> dict[str, str | int | bool | None]: ...
|
|
509
|
+
|
|
510
|
+
def ls(
|
|
511
|
+
path: str,
|
|
512
|
+
recursive: bool = False,
|
|
513
|
+
max_depth: int | None = None,
|
|
514
|
+
files_only: bool = False,
|
|
515
|
+
dirs_only: bool = False,
|
|
516
|
+
) -> list[ListEntry]: ...
|
|
517
|
+
|
|
518
|
+
# CLI functions
|
|
519
|
+
def main(args: list[str] | None = None) -> int: ...
|
|
520
|
+
def run_server(path: str) -> None: ...
|
|
521
|
+
def run_daemon(socket_path: str, root_path: str | None = None) -> None: ...
|
sypy/ls_types.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TypedDict definitions for sy-ls functionality.
|
|
3
|
+
|
|
4
|
+
This module provides TypedDict types for type-safe usage of the ls() function
|
|
5
|
+
and ListEntry objects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TypedDict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ListEntryDict(TypedDict, total=False):
|
|
12
|
+
"""
|
|
13
|
+
TypedDict representing a directory listing entry.
|
|
14
|
+
|
|
15
|
+
All fields are required except those marked Optional.
|
|
16
|
+
Use this for type hints when calling entry.to_dict().
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> entries = sypy.ls("/path")
|
|
20
|
+
>>> dicts: list[ListEntryDict] = [e.to_dict() for e in entries]
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
path: str
|
|
25
|
+
"""Relative path of the entry."""
|
|
26
|
+
|
|
27
|
+
size: int
|
|
28
|
+
"""File size in bytes (0 for directories)."""
|
|
29
|
+
|
|
30
|
+
mod_time: str
|
|
31
|
+
"""Modification time in RFC3339 format."""
|
|
32
|
+
|
|
33
|
+
is_dir: bool
|
|
34
|
+
"""Whether this is a directory."""
|
|
35
|
+
|
|
36
|
+
entry_type: str
|
|
37
|
+
"""Entry type: "file", "directory", or "symlink"."""
|
|
38
|
+
|
|
39
|
+
mime_type: str | None
|
|
40
|
+
"""MIME type (inferred from extension)."""
|
|
41
|
+
|
|
42
|
+
symlink_target: str | None
|
|
43
|
+
"""Symlink target path (if this is a symlink)."""
|
|
44
|
+
|
|
45
|
+
is_sparse: bool | None
|
|
46
|
+
"""Whether this is a sparse file."""
|
|
47
|
+
|
|
48
|
+
allocated_size: int | None
|
|
49
|
+
"""Actual allocated size on disk."""
|
|
50
|
+
|
|
51
|
+
inode: int | None
|
|
52
|
+
"""Inode number (Unix only)."""
|
|
53
|
+
|
|
54
|
+
num_links: int | None
|
|
55
|
+
"""Number of hard links to this file."""
|
sypy/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# PEP 561 marker file - this package supports type checking
|
|
Binary file
|
|
Binary file
|