hgext/infinitepush/store.py
changeset 43076 2372284d9457
parent 40252 090e5f3900b7
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    13 
    13 
    14 from mercurial import (
    14 from mercurial import (
    15     node,
    15     node,
    16     pycompat,
    16     pycompat,
    17 )
    17 )
    18 from mercurial.utils import (
    18 from mercurial.utils import procutil
    19     procutil,
       
    20 )
       
    21 
    19 
    22 NamedTemporaryFile = tempfile.NamedTemporaryFile
    20 NamedTemporaryFile = tempfile.NamedTemporaryFile
       
    21 
    23 
    22 
    24 class BundleWriteException(Exception):
    23 class BundleWriteException(Exception):
    25     pass
    24     pass
    26 
    25 
       
    26 
    27 class BundleReadException(Exception):
    27 class BundleReadException(Exception):
    28     pass
    28     pass
       
    29 
    29 
    30 
    30 class abstractbundlestore(object):
    31 class abstractbundlestore(object):
    31     """Defines the interface for bundle stores.
    32     """Defines the interface for bundle stores.
    32 
    33 
    33     A bundle store is an entity that stores raw bundle data. It is a simple
    34     A bundle store is an entity that stores raw bundle data. It is a simple
    34     key-value store. However, the keys are chosen by the store. The keys can
    35     key-value store. However, the keys are chosen by the store. The keys can
    35     be any Python object understood by the corresponding bundle index (see
    36     be any Python object understood by the corresponding bundle index (see
    36     ``abstractbundleindex`` below).
    37     ``abstractbundleindex`` below).
    37     """
    38     """
       
    39 
    38     __metaclass__ = abc.ABCMeta
    40     __metaclass__ = abc.ABCMeta
    39 
    41 
    40     @abc.abstractmethod
    42     @abc.abstractmethod
    41     def write(self, data):
    43     def write(self, data):
    42         """Write bundle data to the store.
    44         """Write bundle data to the store.
    54         Throws BundleReadException
    56         Throws BundleReadException
    55         The returned object should be a file object supporting read()
    57         The returned object should be a file object supporting read()
    56         and close().
    58         and close().
    57         """
    59         """
    58 
    60 
       
    61 
    59 class filebundlestore(object):
    62 class filebundlestore(object):
    60     """bundle store in filesystem
    63     """bundle store in filesystem
    61 
    64 
    62     meant for storing bundles somewhere on disk and on network filesystems
    65     meant for storing bundles somewhere on disk and on network filesystems
    63     """
    66     """
       
    67 
    64     def __init__(self, ui, repo):
    68     def __init__(self, ui, repo):
    65         self.ui = ui
    69         self.ui = ui
    66         self.repo = repo
    70         self.repo = repo
    67         self.storepath = ui.configpath('scratchbranch', 'storepath')
    71         self.storepath = ui.configpath('scratchbranch', 'storepath')
    68         if not self.storepath:
    72         if not self.storepath:
    69             self.storepath = self.repo.vfs.join("scratchbranches",
    73             self.storepath = self.repo.vfs.join(
    70                                                 "filebundlestore")
    74                 "scratchbranches", "filebundlestore"
       
    75             )
    71         if not os.path.exists(self.storepath):
    76         if not os.path.exists(self.storepath):
    72             os.makedirs(self.storepath)
    77             os.makedirs(self.storepath)
    73 
    78 
    74     def _dirpath(self, hashvalue):
    79     def _dirpath(self, hashvalue):
    75         """First two bytes of the hash are the name of the upper
    80         """First two bytes of the hash are the name of the upper
    97             with open(self._filepath(key), 'rb') as f:
   102             with open(self._filepath(key), 'rb') as f:
    98                 return f.read()
   103                 return f.read()
    99         except IOError:
   104         except IOError:
   100             return None
   105             return None
   101 
   106 
       
   107 
   102 class externalbundlestore(abstractbundlestore):
   108 class externalbundlestore(abstractbundlestore):
   103     def __init__(self, put_binary, put_args, get_binary, get_args):
   109     def __init__(self, put_binary, put_args, get_binary, get_args):
   104         """
   110         """
   105         `put_binary` - path to binary file which uploads bundle to external
   111         `put_binary` - path to binary file which uploads bundle to external
   106             storage and prints key to stdout
   112             storage and prints key to stdout
   118         self.get_binary = get_binary
   124         self.get_binary = get_binary
   119 
   125 
   120     def _call_binary(self, args):
   126     def _call_binary(self, args):
   121         p = subprocess.Popen(
   127         p = subprocess.Popen(
   122             pycompat.rapply(procutil.tonativestr, args),
   128             pycompat.rapply(procutil.tonativestr, args),
   123             stdout=subprocess.PIPE, stderr=subprocess.PIPE,
   129             stdout=subprocess.PIPE,
   124             close_fds=True)
   130             stderr=subprocess.PIPE,
       
   131             close_fds=True,
       
   132         )
   125         stdout, stderr = p.communicate()
   133         stdout, stderr = p.communicate()
   126         returncode = p.returncode
   134         returncode = p.returncode
   127         return returncode, stdout, stderr
   135         return returncode, stdout, stderr
   128 
   136 
   129     def write(self, data):
   137     def write(self, data):
   133         # with pycompat.namedtempfile()
   141         # with pycompat.namedtempfile()
   134         with NamedTemporaryFile() as temp:
   142         with NamedTemporaryFile() as temp:
   135             temp.write(data)
   143             temp.write(data)
   136             temp.flush()
   144             temp.flush()
   137             temp.seek(0)
   145             temp.seek(0)
   138             formatted_args = [arg.format(filename=temp.name)
   146             formatted_args = [
   139                               for arg in self.put_args]
   147                 arg.format(filename=temp.name) for arg in self.put_args
       
   148             ]
   140             returncode, stdout, stderr = self._call_binary(
   149             returncode, stdout, stderr = self._call_binary(
   141                 [self.put_binary] + formatted_args)
   150                 [self.put_binary] + formatted_args
       
   151             )
   142 
   152 
   143             if returncode != 0:
   153             if returncode != 0:
   144                 raise BundleWriteException(
   154                 raise BundleWriteException(
   145                     'Failed to upload to external store: %s' % stderr)
   155                     'Failed to upload to external store: %s' % stderr
       
   156                 )
   146             stdout_lines = stdout.splitlines()
   157             stdout_lines = stdout.splitlines()
   147             if len(stdout_lines) == 1:
   158             if len(stdout_lines) == 1:
   148                 return stdout_lines[0]
   159                 return stdout_lines[0]
   149             else:
   160             else:
   150                 raise BundleWriteException(
   161                 raise BundleWriteException(
   151                     'Bad output from %s: %s' % (self.put_binary, stdout))
   162                     'Bad output from %s: %s' % (self.put_binary, stdout)
       
   163                 )
   152 
   164 
   153     def read(self, handle):
   165     def read(self, handle):
   154         # Won't work on windows because you can't open file second time without
   166         # Won't work on windows because you can't open file second time without
   155         # closing it
   167         # closing it
   156         # TODO: rewrite without str.format() and replace NamedTemporaryFile()
   168         # TODO: rewrite without str.format() and replace NamedTemporaryFile()
   157         # with pycompat.namedtempfile()
   169         # with pycompat.namedtempfile()
   158         with NamedTemporaryFile() as temp:
   170         with NamedTemporaryFile() as temp:
   159             formatted_args = [arg.format(filename=temp.name, handle=handle)
   171             formatted_args = [
   160                               for arg in self.get_args]
   172                 arg.format(filename=temp.name, handle=handle)
       
   173                 for arg in self.get_args
       
   174             ]
   161             returncode, stdout, stderr = self._call_binary(
   175             returncode, stdout, stderr = self._call_binary(
   162                 [self.get_binary] + formatted_args)
   176                 [self.get_binary] + formatted_args
       
   177             )
   163 
   178 
   164             if returncode != 0:
   179             if returncode != 0:
   165                 raise BundleReadException(
   180                 raise BundleReadException(
   166                     'Failed to download from external store: %s' % stderr)
   181                     'Failed to download from external store: %s' % stderr
       
   182                 )
   167             return temp.read()
   183             return temp.read()