##// END OF EJS Templates
state: add a pytype annotation...
Augie Fackler -
r44101:127d4646 default
parent child Browse files
Show More
@@ -1,270 +1,280 b''
1 1 # state.py - writing and reading state files in Mercurial
2 2 #
3 3 # Copyright 2018 Pulkit Goyal <pulkitmgoyal@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """
9 9 This file contains class to wrap the state for commands and other
10 10 related logic.
11 11
12 12 All the data related to the command state is stored as dictionary in the object.
13 13 The class has methods using which the data can be stored to disk in a file under
14 14 .hg/ directory.
15 15
16 16 We store the data on disk in cbor, for which we use the CBOR format to encode
17 17 the data.
18 18 """
19 19
20 20 from __future__ import absolute_import
21 21
22 22 from .i18n import _
23 23
24 24 from . import (
25 25 error,
26 26 util,
27 27 )
28 28 from .utils import cborutil
29 29
30 if not globals():
31 from typing import (
32 Any,
33 Dict,
34 )
35
36 for t in (Any, Dict):
37 assert t
38
30 39
31 40 class cmdstate(object):
32 41 """a wrapper class to store the state of commands like `rebase`, `graft`,
33 42 `histedit`, `shelve` etc. Extensions can also use this to write state files.
34 43
35 44 All the data for the state is stored in the form of key-value pairs in a
36 45 dictionary.
37 46
38 47 The class object can write all the data to a file in .hg/ directory and
39 48 can populate the object data reading that file.
40 49
41 50 Uses cbor to serialize and deserialize data while writing and reading from
42 51 disk.
43 52 """
44 53
45 54 def __init__(self, repo, fname):
46 55 """ repo is the repo object
47 56 fname is the file name in which data should be stored in .hg directory
48 57 """
49 58 self._repo = repo
50 59 self.fname = fname
51 60
52 61 def read(self):
62 # type: () -> Dict[bytes, Any]
53 63 """read the existing state file and return a dict of data stored"""
54 64 return self._read()
55 65
56 66 def save(self, version, data):
57 67 """write all the state data stored to .hg/<filename> file
58 68
59 69 we use third-party library cbor to serialize data to write in the file.
60 70 """
61 71 if not isinstance(version, int):
62 72 raise error.ProgrammingError(
63 73 b"version of state file should be an integer"
64 74 )
65 75
66 76 with self._repo.vfs(self.fname, b'wb', atomictemp=True) as fp:
67 77 fp.write(b'%d\n' % version)
68 78 for chunk in cborutil.streamencode(data):
69 79 fp.write(chunk)
70 80
71 81 def _read(self):
72 82 """reads the state file and returns a dictionary which contain
73 83 data in the same format as it was before storing"""
74 84 with self._repo.vfs(self.fname, b'rb') as fp:
75 85 try:
76 86 int(fp.readline())
77 87 except ValueError:
78 88 raise error.CorruptedState(
79 89 b"unknown version of state file found"
80 90 )
81 91
82 92 return cborutil.decodeall(fp.read())[0]
83 93
84 94 def delete(self):
85 95 """drop the state file if exists"""
86 96 util.unlinkpath(self._repo.vfs.join(self.fname), ignoremissing=True)
87 97
88 98 def exists(self):
89 99 """check whether the state file exists or not"""
90 100 return self._repo.vfs.exists(self.fname)
91 101
92 102
93 103 class _statecheck(object):
94 104 """a utility class that deals with multistep operations like graft,
95 105 histedit, bisect, update etc and check whether such commands
96 106 are in an unfinished conditition or not and return appropriate message
97 107 and hint.
98 108 It also has the ability to register and determine the states of any new
99 109 multistep operation or multistep command extension.
100 110 """
101 111
102 112 def __init__(
103 113 self,
104 114 opname,
105 115 fname,
106 116 clearable,
107 117 allowcommit,
108 118 reportonly,
109 119 continueflag,
110 120 stopflag,
111 121 cmdmsg,
112 122 cmdhint,
113 123 statushint,
114 124 abortfunc,
115 125 continuefunc,
116 126 ):
117 127 self._opname = opname
118 128 self._fname = fname
119 129 self._clearable = clearable
120 130 self._allowcommit = allowcommit
121 131 self._reportonly = reportonly
122 132 self._continueflag = continueflag
123 133 self._stopflag = stopflag
124 134 self._cmdmsg = cmdmsg
125 135 self._cmdhint = cmdhint
126 136 self._statushint = statushint
127 137 self.abortfunc = abortfunc
128 138 self.continuefunc = continuefunc
129 139
130 140 def statusmsg(self):
131 141 """returns the hint message corresponding to the command for
132 142 hg status --verbose
133 143 """
134 144 if not self._statushint:
135 145 hint = _(
136 146 b'To continue: hg %s --continue\n'
137 147 b'To abort: hg %s --abort'
138 148 ) % (self._opname, self._opname)
139 149 if self._stopflag:
140 150 hint = hint + (
141 151 _(b'\nTo stop: hg %s --stop') % (self._opname)
142 152 )
143 153 return hint
144 154 return self._statushint
145 155
146 156 def hint(self):
147 157 """returns the hint message corresponding to an interrupted
148 158 operation
149 159 """
150 160 if not self._cmdhint:
151 161 return _(b"use 'hg %s --continue' or 'hg %s --abort'") % (
152 162 self._opname,
153 163 self._opname,
154 164 )
155 165 return self._cmdhint
156 166
157 167 def msg(self):
158 168 """returns the status message corresponding to the command"""
159 169 if not self._cmdmsg:
160 170 return _(b'%s in progress') % (self._opname)
161 171 return self._cmdmsg
162 172
163 173 def continuemsg(self):
164 174 """ returns appropriate continue message corresponding to command"""
165 175 return _(b'hg %s --continue') % (self._opname)
166 176
167 177 def isunfinished(self, repo):
168 178 """determines whether a multi-step operation is in progress
169 179 or not
170 180 """
171 181 if self._opname == b'merge':
172 182 return len(repo[None].parents()) > 1
173 183 else:
174 184 return repo.vfs.exists(self._fname)
175 185
176 186
177 187 # A list of statecheck objects for multistep operations like graft.
178 188 _unfinishedstates = []
179 189
180 190
181 191 def addunfinished(
182 192 opname,
183 193 fname,
184 194 clearable=False,
185 195 allowcommit=False,
186 196 reportonly=False,
187 197 continueflag=False,
188 198 stopflag=False,
189 199 cmdmsg=b"",
190 200 cmdhint=b"",
191 201 statushint=b"",
192 202 abortfunc=None,
193 203 continuefunc=None,
194 204 ):
195 205 """this registers a new command or operation to unfinishedstates
196 206 opname is the name the command or operation
197 207 fname is the file name in which data should be stored in .hg directory.
198 208 It is None for merge command.
199 209 clearable boolean determines whether or not interrupted states can be
200 210 cleared by running `hg update -C .` which in turn deletes the
201 211 state file.
202 212 allowcommit boolean decides whether commit is allowed during interrupted
203 213 state or not.
204 214 reportonly flag is used for operations like bisect where we just
205 215 need to detect the operation using 'hg status --verbose'
206 216 continueflag is a boolean determines whether or not a command supports
207 217 `--continue` option or not.
208 218 stopflag is a boolean that determines whether or not a command supports
209 219 --stop flag
210 220 cmdmsg is used to pass a different status message in case standard
211 221 message of the format "abort: cmdname in progress" is not desired.
212 222 cmdhint is used to pass a different hint message in case standard
213 223 message of the format "To continue: hg cmdname --continue
214 224 To abort: hg cmdname --abort" is not desired.
215 225 statushint is used to pass a different status message in case standard
216 226 message of the format ('To continue: hg cmdname --continue'
217 227 'To abort: hg cmdname --abort') is not desired
218 228 abortfunc stores the function required to abort an unfinished state.
219 229 continuefunc stores the function required to finish an interrupted
220 230 operation.
221 231 """
222 232 statecheckobj = _statecheck(
223 233 opname,
224 234 fname,
225 235 clearable,
226 236 allowcommit,
227 237 reportonly,
228 238 continueflag,
229 239 stopflag,
230 240 cmdmsg,
231 241 cmdhint,
232 242 statushint,
233 243 abortfunc,
234 244 continuefunc,
235 245 )
236 246 if opname == b'merge':
237 247 _unfinishedstates.append(statecheckobj)
238 248 else:
239 249 _unfinishedstates.insert(0, statecheckobj)
240 250
241 251
242 252 addunfinished(
243 253 b'update',
244 254 fname=b'updatestate',
245 255 clearable=True,
246 256 cmdmsg=_(b'last update was interrupted'),
247 257 cmdhint=_(b"use 'hg update' to get a consistent checkout"),
248 258 statushint=_(b"To continue: hg update ."),
249 259 )
250 260 addunfinished(
251 261 b'bisect',
252 262 fname=b'bisect.state',
253 263 allowcommit=True,
254 264 reportonly=True,
255 265 statushint=_(
256 266 b'To mark the changeset good: hg bisect --good\n'
257 267 b'To mark the changeset bad: hg bisect --bad\n'
258 268 b'To abort: hg bisect --reset\n'
259 269 ),
260 270 )
261 271
262 272
263 273 def getrepostate(repo):
264 274 # experimental config: commands.status.skipstates
265 275 skip = set(repo.ui.configlist(b'commands', b'status.skipstates'))
266 276 for state in _unfinishedstates:
267 277 if state._opname in skip:
268 278 continue
269 279 if state.isunfinished(repo):
270 280 return (state._opname, state.statusmsg())
General Comments 0
You need to be logged in to leave comments. Login now