mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 09:17:30 +03:00
2a0e8205d4
Summary: I'm going to use runshellfast in infinitepush. To avoid copy-paste let's move it to the separate package. Note: fastmanifest also has runshellfast but it's implementation is a bit different and seems that it doesn't work with remotefilelog. So I'll leave it alone for now. Test Plan: python run-tests.py -j20 Reviewers: #sourcecontrol Subscribers: mjpieters Differential Revision: https://phabricator.intern.facebook.com/D4175836
81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
# extutil.py - useful utility methods for extensions
|
|
#
|
|
# Copyright 2016 Facebook
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
import os
|
|
import platform
|
|
import subprocess
|
|
import sys
|
|
|
|
if platform.system() == 'Windows':
|
|
# no fork on Windows, but we can create a detached process
|
|
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
|
|
# No stdlib constant exists for this value
|
|
DETACHED_PROCESS = 0x00000008
|
|
_creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
|
|
|
|
def runshellcommand(script, env):
|
|
# we can't use close_fds *and* redirect stdin. I'm not sure that we
|
|
# need to because the detached process has no console connection.
|
|
subprocess.Popen(
|
|
script, shell=True, env=env, close_fds=True,
|
|
creationflags=_creationflags)
|
|
else:
|
|
def runshellcommand(script, env):
|
|
# double-fork to completely detach from the parent process
|
|
# based on http://code.activestate.com/recipes/278731
|
|
pid = os.fork()
|
|
if pid:
|
|
# parent
|
|
return
|
|
# subprocess.Popen() forks again, all we need to add is
|
|
# flag the new process as a new session.
|
|
if sys.version_info < (3, 2):
|
|
newsession = {'preexec_fn': os.setsid}
|
|
else:
|
|
newsession = {'start_new_session': True}
|
|
try:
|
|
# connect stdin to devnull to make sure the subprocess can't
|
|
# muck up that stream for mercurial.
|
|
subprocess.Popen(
|
|
script, shell=True, stdout=open(os.devnull, 'w'),
|
|
stderr=open(os.devnull, 'w'), stdin=open(os.devnull, 'r'),
|
|
env=env, close_fds=True, **newsession)
|
|
finally:
|
|
# mission accomplished, this child needs to exit and not
|
|
# continue the hg process here.
|
|
os._exit(0)
|
|
|
|
def replaceclass(container, classname):
|
|
'''Replace a class with another in a module, and interpose it into
|
|
the hierarchies of all loaded subclasses. This function is
|
|
intended for use as a decorator.
|
|
|
|
import mymodule
|
|
@replaceclass(mymodule, 'myclass')
|
|
class mysubclass(mymodule.myclass):
|
|
def foo(self):
|
|
f = super(mysubclass, self).foo()
|
|
return f + ' bar'
|
|
|
|
Existing instances of the class being replaced will not have their
|
|
__class__ modified, so call this function before creating any
|
|
objects of the target type.
|
|
'''
|
|
def wrap(cls):
|
|
oldcls = getattr(container, classname)
|
|
for subcls in oldcls.__subclasses__():
|
|
if subcls is not cls:
|
|
assert oldcls in subcls.__bases__
|
|
newbases = [oldbase
|
|
for oldbase in subcls.__bases__
|
|
if oldbase != oldcls]
|
|
newbases.append(cls)
|
|
subcls.__bases__ = tuple(newbases)
|
|
setattr(container, classname, cls)
|
|
return cls
|
|
return wrap
|