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