1515
1616
1717_SNAPSHOT_RE = re .compile (r"Snapshot created: (\S+)" )
18+ SHORT_TIMEOUT_SECONDS = 120
19+ LONG_TIMEOUT_SECONDS = 3600
20+
21+
22+ def timeout_for_args (args : list [str ]) -> int :
23+ """Return the default timeout for a vykar subcommand."""
24+ if not args :
25+ return SHORT_TIMEOUT_SECONDS
26+
27+ command = args [0 ]
28+ if command in {"backup" , "restore" , "check" , "compact" , "prune" , "delete" }:
29+ return LONG_TIMEOUT_SECONDS
30+ return SHORT_TIMEOUT_SECONDS
1831
1932
2033def _env_with_passphrase () -> dict [str , str ]:
@@ -32,7 +45,7 @@ def run_vykar(
3245 config_path : str ,
3346 args : list [str ],
3447 * ,
35- timeout : int = 120 ,
48+ timeout : int | None = None ,
3649 label : str = "" ,
3750 allow_missing_repo : bool = False ,
3851 passphrase : str | None = None ,
@@ -43,14 +56,33 @@ def run_vykar(
4356 env = _env_with_passphrase ()
4457 if passphrase is not None :
4558 env ["VYKAR_PASSPHRASE" ] = passphrase
59+ resolved_timeout = timeout if timeout is not None else timeout_for_args (args )
60+
61+ try :
62+ result = subprocess .run (
63+ cmd ,
64+ capture_output = capture ,
65+ text = True ,
66+ env = env ,
67+ timeout = resolved_timeout ,
68+ )
69+ except subprocess .TimeoutExpired as exc :
70+ stdout = exc .stdout if isinstance (exc .stdout , str ) else ""
71+ stderr = exc .stderr if isinstance (exc .stderr , str ) else ""
72+ timeout_msg = (
73+ f"Command timed out after { resolved_timeout } seconds: { ' ' .join (cmd )} "
74+ )
75+ if stderr :
76+ stderr = f"{ stderr } \n { timeout_msg } "
77+ else :
78+ stderr = timeout_msg
79+ result = subprocess .CompletedProcess (
80+ args = cmd ,
81+ returncode = 124 ,
82+ stdout = stdout ,
83+ stderr = stderr ,
84+ )
4685
47- result = subprocess .run (
48- cmd ,
49- capture_output = capture ,
50- text = True ,
51- env = env ,
52- timeout = timeout ,
53- )
5486 missing_repo = allow_missing_repo and _is_missing_repo_error (result .stderr )
5587 if result .returncode != 0 and not missing_repo :
5688 print (
@@ -81,7 +113,7 @@ def vykar_backup(
81113) -> tuple [subprocess .CompletedProcess , str | None ]:
82114 """Run backup, return (result, snapshot_id or None)."""
83115 args = ["backup" , "-R" , repo_label ]
84- result = run_vykar (vykar_bin , config_path , args , timeout = 3600 , label = "backup" )
116+ result = run_vykar (vykar_bin , config_path , args , label = "backup" )
85117 snapshot_id = None
86118 if result .returncode == 0 :
87119 snapshot_id = extract_snapshot_id (result .stdout )
@@ -103,7 +135,6 @@ def vykar_restore(
103135 vykar_bin ,
104136 config_path ,
105137 ["restore" , "-R" , repo_label , snapshot_id , target_dir ],
106- timeout = 3600 ,
107138 label = "restore" ,
108139 )
109140
@@ -118,7 +149,7 @@ def vykar_check(
118149 args = ["check" , "-R" , repo_label ]
119150 if verify_data :
120151 args .append ("--verify-data" )
121- return run_vykar (vykar_bin , config_path , args , timeout = 3600 , label = "check" )
152+ return run_vykar (vykar_bin , config_path , args , label = "check" )
122153
123154
124155def vykar_prune (vykar_bin : str , config_path : str , repo_label : str ) -> subprocess .CompletedProcess :
0 commit comments