Mercurial > public > mercurial-scm > hg
comparison mercurial/chgserver.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 0cbe17335857 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
65 from .utils import ( | 65 from .utils import ( |
66 procutil, | 66 procutil, |
67 stringutil, | 67 stringutil, |
68 ) | 68 ) |
69 | 69 |
70 | |
70 def _hashlist(items): | 71 def _hashlist(items): |
71 """return sha1 hexdigest for a list""" | 72 """return sha1 hexdigest for a list""" |
72 return node.hex(hashlib.sha1(stringutil.pprint(items)).digest()) | 73 return node.hex(hashlib.sha1(stringutil.pprint(items)).digest()) |
73 | 74 |
75 | |
74 # sensitive config sections affecting confighash | 76 # sensitive config sections affecting confighash |
75 _configsections = [ | 77 _configsections = [ |
76 'alias', # affects global state commands.table | 78 'alias', # affects global state commands.table |
77 'eol', # uses setconfig('eol', ...) | 79 'eol', # uses setconfig('eol', ...) |
78 'extdiff', # uisetup will register new commands | 80 'extdiff', # uisetup will register new commands |
79 'extensions', | 81 'extensions', |
80 ] | 82 ] |
81 | 83 |
82 _configsectionitems = [ | 84 _configsectionitems = [ |
83 ('commands', 'show.aliasprefix'), # show.py reads it in extsetup | 85 ('commands', 'show.aliasprefix'), # show.py reads it in extsetup |
84 ] | 86 ] |
85 | 87 |
86 # sensitive environment variables affecting confighash | 88 # sensitive environment variables affecting confighash |
87 _envre = re.compile(br'''\A(?: | 89 _envre = re.compile( |
90 br'''\A(?: | |
88 CHGHG | 91 CHGHG |
89 |HG(?:DEMANDIMPORT|EMITWARNINGS|MODULEPOLICY|PROF|RCPATH)? | 92 |HG(?:DEMANDIMPORT|EMITWARNINGS|MODULEPOLICY|PROF|RCPATH)? |
90 |HG(?:ENCODING|PLAIN).* | 93 |HG(?:ENCODING|PLAIN).* |
91 |LANG(?:UAGE)? | 94 |LANG(?:UAGE)? |
92 |LC_.* | 95 |LC_.* |
93 |LD_.* | 96 |LD_.* |
94 |PATH | 97 |PATH |
95 |PYTHON.* | 98 |PYTHON.* |
96 |TERM(?:INFO)? | 99 |TERM(?:INFO)? |
97 |TZ | 100 |TZ |
98 )\Z''', re.X) | 101 )\Z''', |
102 re.X, | |
103 ) | |
104 | |
99 | 105 |
100 def _confighash(ui): | 106 def _confighash(ui): |
101 """return a quick hash for detecting config/env changes | 107 """return a quick hash for detecting config/env changes |
102 | 108 |
103 confighash is the hash of sensitive config items and environment variables. | 109 confighash is the hash of sensitive config items and environment variables. |
117 # If $CHGHG is set, the change to $HG should not trigger a new chg server | 123 # If $CHGHG is set, the change to $HG should not trigger a new chg server |
118 if 'CHGHG' in encoding.environ: | 124 if 'CHGHG' in encoding.environ: |
119 ignored = {'HG'} | 125 ignored = {'HG'} |
120 else: | 126 else: |
121 ignored = set() | 127 ignored = set() |
122 envitems = [(k, v) for k, v in encoding.environ.iteritems() | 128 envitems = [ |
123 if _envre.match(k) and k not in ignored] | 129 (k, v) |
130 for k, v in encoding.environ.iteritems() | |
131 if _envre.match(k) and k not in ignored | |
132 ] | |
124 envhash = _hashlist(sorted(envitems)) | 133 envhash = _hashlist(sorted(envitems)) |
125 return sectionhash[:6] + envhash[:6] | 134 return sectionhash[:6] + envhash[:6] |
135 | |
126 | 136 |
127 def _getmtimepaths(ui): | 137 def _getmtimepaths(ui): |
128 """get a list of paths that should be checked to detect change | 138 """get a list of paths that should be checked to detect change |
129 | 139 |
130 The list will include: | 140 The list will include: |
133 - python binary | 143 - python binary |
134 """ | 144 """ |
135 modules = [m for n, m in extensions.extensions(ui)] | 145 modules = [m for n, m in extensions.extensions(ui)] |
136 try: | 146 try: |
137 from . import __version__ | 147 from . import __version__ |
148 | |
138 modules.append(__version__) | 149 modules.append(__version__) |
139 except ImportError: | 150 except ImportError: |
140 pass | 151 pass |
141 files = [] | 152 files = [] |
142 if pycompat.sysexecutable: | 153 if pycompat.sysexecutable: |
146 files.append(pycompat.fsencode(inspect.getabsfile(m))) | 157 files.append(pycompat.fsencode(inspect.getabsfile(m))) |
147 except TypeError: | 158 except TypeError: |
148 pass | 159 pass |
149 return sorted(set(files)) | 160 return sorted(set(files)) |
150 | 161 |
162 | |
151 def _mtimehash(paths): | 163 def _mtimehash(paths): |
152 """return a quick hash for detecting file changes | 164 """return a quick hash for detecting file changes |
153 | 165 |
154 mtimehash calls stat on given paths and calculate a hash based on size and | 166 mtimehash calls stat on given paths and calculate a hash based on size and |
155 mtime of each file. mtimehash does not read file content because reading is | 167 mtime of each file. mtimehash does not read file content because reading is |
163 | 175 |
164 mtimehash is not included in confighash because we only know the paths of | 176 mtimehash is not included in confighash because we only know the paths of |
165 extensions after importing them (there is imp.find_module but that faces | 177 extensions after importing them (there is imp.find_module but that faces |
166 race conditions). We need to calculate confighash without importing. | 178 race conditions). We need to calculate confighash without importing. |
167 """ | 179 """ |
180 | |
168 def trystat(path): | 181 def trystat(path): |
169 try: | 182 try: |
170 st = os.stat(path) | 183 st = os.stat(path) |
171 return (st[stat.ST_MTIME], st.st_size) | 184 return (st[stat.ST_MTIME], st.st_size) |
172 except OSError: | 185 except OSError: |
173 # could be ENOENT, EPERM etc. not fatal in any case | 186 # could be ENOENT, EPERM etc. not fatal in any case |
174 pass | 187 pass |
188 | |
175 return _hashlist(pycompat.maplist(trystat, paths))[:12] | 189 return _hashlist(pycompat.maplist(trystat, paths))[:12] |
190 | |
176 | 191 |
177 class hashstate(object): | 192 class hashstate(object): |
178 """a structure storing confighash, mtimehash, paths used for mtimehash""" | 193 """a structure storing confighash, mtimehash, paths used for mtimehash""" |
194 | |
179 def __init__(self, confighash, mtimehash, mtimepaths): | 195 def __init__(self, confighash, mtimehash, mtimepaths): |
180 self.confighash = confighash | 196 self.confighash = confighash |
181 self.mtimehash = mtimehash | 197 self.mtimehash = mtimehash |
182 self.mtimepaths = mtimepaths | 198 self.mtimepaths = mtimepaths |
183 | 199 |
185 def fromui(ui, mtimepaths=None): | 201 def fromui(ui, mtimepaths=None): |
186 if mtimepaths is None: | 202 if mtimepaths is None: |
187 mtimepaths = _getmtimepaths(ui) | 203 mtimepaths = _getmtimepaths(ui) |
188 confighash = _confighash(ui) | 204 confighash = _confighash(ui) |
189 mtimehash = _mtimehash(mtimepaths) | 205 mtimehash = _mtimehash(mtimepaths) |
190 ui.log('cmdserver', 'confighash = %s mtimehash = %s\n', | 206 ui.log( |
191 confighash, mtimehash) | 207 'cmdserver', |
208 'confighash = %s mtimehash = %s\n', | |
209 confighash, | |
210 mtimehash, | |
211 ) | |
192 return hashstate(confighash, mtimehash, mtimepaths) | 212 return hashstate(confighash, mtimehash, mtimepaths) |
213 | |
193 | 214 |
194 def _newchgui(srcui, csystem, attachio): | 215 def _newchgui(srcui, csystem, attachio): |
195 class chgui(srcui.__class__): | 216 class chgui(srcui.__class__): |
196 def __init__(self, src=None): | 217 def __init__(self, src=None): |
197 super(chgui, self).__init__(src) | 218 super(chgui, self).__init__(src) |
204 # fallback to the original system method if | 225 # fallback to the original system method if |
205 # a. the output stream is not stdout (e.g. stderr, cStringIO), | 226 # a. the output stream is not stdout (e.g. stderr, cStringIO), |
206 # b. or stdout is redirected by protectfinout(), | 227 # b. or stdout is redirected by protectfinout(), |
207 # because the chg client is not aware of these situations and | 228 # because the chg client is not aware of these situations and |
208 # will behave differently (i.e. write to stdout). | 229 # will behave differently (i.e. write to stdout). |
209 if (out is not self.fout | 230 if ( |
231 out is not self.fout | |
210 or not util.safehasattr(self.fout, 'fileno') | 232 or not util.safehasattr(self.fout, 'fileno') |
211 or self.fout.fileno() != procutil.stdout.fileno() | 233 or self.fout.fileno() != procutil.stdout.fileno() |
212 or self._finoutredirected): | 234 or self._finoutredirected |
235 ): | |
213 return procutil.system(cmd, environ=environ, cwd=cwd, out=out) | 236 return procutil.system(cmd, environ=environ, cwd=cwd, out=out) |
214 self.flush() | 237 self.flush() |
215 return self._csystem(cmd, procutil.shellenviron(environ), cwd) | 238 return self._csystem(cmd, procutil.shellenviron(environ), cwd) |
216 | 239 |
217 def _runpager(self, cmd, env=None): | 240 def _runpager(self, cmd, env=None): |
218 self._csystem(cmd, procutil.shellenviron(env), type='pager', | 241 self._csystem( |
219 cmdtable={'attachio': attachio}) | 242 cmd, |
243 procutil.shellenviron(env), | |
244 type='pager', | |
245 cmdtable={'attachio': attachio}, | |
246 ) | |
220 return True | 247 return True |
221 | 248 |
222 return chgui(srcui) | 249 return chgui(srcui) |
250 | |
223 | 251 |
224 def _loadnewui(srcui, args, cdebug): | 252 def _loadnewui(srcui, args, cdebug): |
225 from . import dispatch # avoid cycle | 253 from . import dispatch # avoid cycle |
226 | 254 |
227 newui = srcui.__class__.load() | 255 newui = srcui.__class__.load() |
254 extensions.populateui(newlui) | 282 extensions.populateui(newlui) |
255 commandserver.setuplogging(newlui, fp=cdebug) | 283 commandserver.setuplogging(newlui, fp=cdebug) |
256 | 284 |
257 return (newui, newlui) | 285 return (newui, newlui) |
258 | 286 |
287 | |
259 class channeledsystem(object): | 288 class channeledsystem(object): |
260 """Propagate ui.system() request in the following format: | 289 """Propagate ui.system() request in the following format: |
261 | 290 |
262 payload length (unsigned int), | 291 payload length (unsigned int), |
263 type, '\0', | 292 type, '\0', |
274 | 303 |
275 if type == 'pager', repetitively waits for a command name ending with '\n' | 304 if type == 'pager', repetitively waits for a command name ending with '\n' |
276 and executes it defined by cmdtable, or exits the loop if the command name | 305 and executes it defined by cmdtable, or exits the loop if the command name |
277 is empty. | 306 is empty. |
278 """ | 307 """ |
308 | |
279 def __init__(self, in_, out, channel): | 309 def __init__(self, in_, out, channel): |
280 self.in_ = in_ | 310 self.in_ = in_ |
281 self.out = out | 311 self.out = out |
282 self.channel = channel | 312 self.channel = channel |
283 | 313 |
289 self.out.write(data) | 319 self.out.write(data) |
290 self.out.flush() | 320 self.out.flush() |
291 | 321 |
292 if type == 'system': | 322 if type == 'system': |
293 length = self.in_.read(4) | 323 length = self.in_.read(4) |
294 length, = struct.unpack('>I', length) | 324 (length,) = struct.unpack('>I', length) |
295 if length != 4: | 325 if length != 4: |
296 raise error.Abort(_('invalid response')) | 326 raise error.Abort(_('invalid response')) |
297 rc, = struct.unpack('>i', self.in_.read(4)) | 327 (rc,) = struct.unpack('>i', self.in_.read(4)) |
298 return rc | 328 return rc |
299 elif type == 'pager': | 329 elif type == 'pager': |
300 while True: | 330 while True: |
301 cmd = self.in_.readline()[:-1] | 331 cmd = self.in_.readline()[:-1] |
302 if not cmd: | 332 if not cmd: |
306 else: | 336 else: |
307 raise error.Abort(_('unexpected command: %s') % cmd) | 337 raise error.Abort(_('unexpected command: %s') % cmd) |
308 else: | 338 else: |
309 raise error.ProgrammingError('invalid S channel type: %s' % type) | 339 raise error.ProgrammingError('invalid S channel type: %s' % type) |
310 | 340 |
341 | |
311 _iochannels = [ | 342 _iochannels = [ |
312 # server.ch, ui.fp, mode | 343 # server.ch, ui.fp, mode |
313 ('cin', 'fin', r'rb'), | 344 ('cin', 'fin', r'rb'), |
314 ('cout', 'fout', r'wb'), | 345 ('cout', 'fout', r'wb'), |
315 ('cerr', 'ferr', r'wb'), | 346 ('cerr', 'ferr', r'wb'), |
316 ] | 347 ] |
317 | 348 |
349 | |
318 class chgcmdserver(commandserver.server): | 350 class chgcmdserver(commandserver.server): |
319 def __init__(self, ui, repo, fin, fout, sock, prereposetups, | 351 def __init__( |
320 hashstate, baseaddress): | 352 self, ui, repo, fin, fout, sock, prereposetups, hashstate, baseaddress |
353 ): | |
321 super(chgcmdserver, self).__init__( | 354 super(chgcmdserver, self).__init__( |
322 _newchgui(ui, channeledsystem(fin, fout, 'S'), self.attachio), | 355 _newchgui(ui, channeledsystem(fin, fout, 'S'), self.attachio), |
323 repo, fin, fout, prereposetups) | 356 repo, |
357 fin, | |
358 fout, | |
359 prereposetups, | |
360 ) | |
324 self.clientsock = sock | 361 self.clientsock = sock |
325 self._ioattached = False | 362 self._ioattached = False |
326 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio" | 363 self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio" |
327 self.hashstate = hashstate | 364 self.hashstate = hashstate |
328 self.baseaddress = baseaddress | 365 self.baseaddress = baseaddress |
510 self.ui.log('chgserver', 'setenv: %r\n', sorted(newenv.keys())) | 547 self.ui.log('chgserver', 'setenv: %r\n', sorted(newenv.keys())) |
511 encoding.environ.clear() | 548 encoding.environ.clear() |
512 encoding.environ.update(newenv) | 549 encoding.environ.update(newenv) |
513 | 550 |
514 capabilities = commandserver.server.capabilities.copy() | 551 capabilities = commandserver.server.capabilities.copy() |
515 capabilities.update({'attachio': attachio, | 552 capabilities.update( |
516 'chdir': chdir, | 553 { |
517 'runcommand': runcommand, | 554 'attachio': attachio, |
518 'setenv': setenv, | 555 'chdir': chdir, |
519 'setumask': setumask, | 556 'runcommand': runcommand, |
520 'setumask2': setumask2}) | 557 'setenv': setenv, |
558 'setumask': setumask, | |
559 'setumask2': setumask2, | |
560 } | |
561 ) | |
521 | 562 |
522 if util.safehasattr(procutil, 'setprocname'): | 563 if util.safehasattr(procutil, 'setprocname'): |
564 | |
523 def setprocname(self): | 565 def setprocname(self): |
524 """Change process title""" | 566 """Change process title""" |
525 name = self._readstr() | 567 name = self._readstr() |
526 self.ui.log('chgserver', 'setprocname: %r\n', name) | 568 self.ui.log('chgserver', 'setprocname: %r\n', name) |
527 procutil.setprocname(name) | 569 procutil.setprocname(name) |
570 | |
528 capabilities['setprocname'] = setprocname | 571 capabilities['setprocname'] = setprocname |
572 | |
529 | 573 |
530 def _tempaddress(address): | 574 def _tempaddress(address): |
531 return '%s.%d.tmp' % (address, os.getpid()) | 575 return '%s.%d.tmp' % (address, os.getpid()) |
576 | |
532 | 577 |
533 def _hashaddress(address, hashstr): | 578 def _hashaddress(address, hashstr): |
534 # if the basename of address contains '.', use only the left part. this | 579 # if the basename of address contains '.', use only the left part. this |
535 # makes it possible for the client to pass 'server.tmp$PID' and follow by | 580 # makes it possible for the client to pass 'server.tmp$PID' and follow by |
536 # an atomic rename to avoid locking when spawning new servers. | 581 # an atomic rename to avoid locking when spawning new servers. |
537 dirname, basename = os.path.split(address) | 582 dirname, basename = os.path.split(address) |
538 basename = basename.split('.', 1)[0] | 583 basename = basename.split('.', 1)[0] |
539 return '%s-%s' % (os.path.join(dirname, basename), hashstr) | 584 return '%s-%s' % (os.path.join(dirname, basename), hashstr) |
585 | |
540 | 586 |
541 class chgunixservicehandler(object): | 587 class chgunixservicehandler(object): |
542 """Set of operations for chg services""" | 588 """Set of operations for chg services""" |
543 | 589 |
544 pollinterval = 1 # [sec] | 590 pollinterval = 1 # [sec] |
592 util.rename(tempaddress, self._baseaddress) | 638 util.rename(tempaddress, self._baseaddress) |
593 | 639 |
594 def _issocketowner(self): | 640 def _issocketowner(self): |
595 try: | 641 try: |
596 st = os.stat(self._realaddress) | 642 st = os.stat(self._realaddress) |
597 return (st.st_ino == self._socketstat.st_ino and | 643 return ( |
598 st[stat.ST_MTIME] == self._socketstat[stat.ST_MTIME]) | 644 st.st_ino == self._socketstat.st_ino |
645 and st[stat.ST_MTIME] == self._socketstat[stat.ST_MTIME] | |
646 ) | |
599 except OSError: | 647 except OSError: |
600 return False | 648 return False |
601 | 649 |
602 def unlinksocket(self, address): | 650 def unlinksocket(self, address): |
603 if not self._issocketowner(): | 651 if not self._issocketowner(): |
608 # the client will start a new server on demand. | 656 # the client will start a new server on demand. |
609 util.tryunlink(self._realaddress) | 657 util.tryunlink(self._realaddress) |
610 | 658 |
611 def shouldexit(self): | 659 def shouldexit(self): |
612 if not self._issocketowner(): | 660 if not self._issocketowner(): |
613 self.ui.log(b'chgserver', b'%s is not owned, exiting.\n', | 661 self.ui.log( |
614 self._realaddress) | 662 b'chgserver', b'%s is not owned, exiting.\n', self._realaddress |
663 ) | |
615 return True | 664 return True |
616 if time.time() - self._lastactive > self._idletimeout: | 665 if time.time() - self._lastactive > self._idletimeout: |
617 self.ui.log(b'chgserver', b'being idle too long. exiting.\n') | 666 self.ui.log(b'chgserver', b'being idle too long. exiting.\n') |
618 return True | 667 return True |
619 return False | 668 return False |
620 | 669 |
621 def newconnection(self): | 670 def newconnection(self): |
622 self._lastactive = time.time() | 671 self._lastactive = time.time() |
623 | 672 |
624 def createcmdserver(self, repo, conn, fin, fout, prereposetups): | 673 def createcmdserver(self, repo, conn, fin, fout, prereposetups): |
625 return chgcmdserver(self.ui, repo, fin, fout, conn, prereposetups, | 674 return chgcmdserver( |
626 self._hashstate, self._baseaddress) | 675 self.ui, |
676 repo, | |
677 fin, | |
678 fout, | |
679 conn, | |
680 prereposetups, | |
681 self._hashstate, | |
682 self._baseaddress, | |
683 ) | |
684 | |
627 | 685 |
628 def chgunixservice(ui, repo, opts): | 686 def chgunixservice(ui, repo, opts): |
629 # CHGINTERNALMARK is set by chg client. It is an indication of things are | 687 # CHGINTERNALMARK is set by chg client. It is an indication of things are |
630 # started by chg so other code can do things accordingly, like disabling | 688 # started by chg so other code can do things accordingly, like disabling |
631 # demandimport or detecting chg client started by chg client. When executed | 689 # demandimport or detecting chg client started by chg client. When executed |