##// END OF EJS Templates
inifinitepush: fix filebundlestore to close file
Yuya Nishihara -
r37814:968ac00c stable
parent child Browse files
Show More
@@ -1,155 +1,154 b''
1 1 # This software may be used and distributed according to the terms of the
2 2 # GNU General Public License version 2 or any later version.
3 3
4 4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
5 5
6 6 from __future__ import absolute_import
7 7
8 8 import abc
9 9 import hashlib
10 10 import os
11 11 import subprocess
12 12 import tempfile
13 13
14 14 NamedTemporaryFile = tempfile.NamedTemporaryFile
15 15
16 16 class BundleWriteException(Exception):
17 17 pass
18 18
19 19 class BundleReadException(Exception):
20 20 pass
21 21
22 22 class abstractbundlestore(object):
23 23 """Defines the interface for bundle stores.
24 24
25 25 A bundle store is an entity that stores raw bundle data. It is a simple
26 26 key-value store. However, the keys are chosen by the store. The keys can
27 27 be any Python object understood by the corresponding bundle index (see
28 28 ``abstractbundleindex`` below).
29 29 """
30 30 __metaclass__ = abc.ABCMeta
31 31
32 32 @abc.abstractmethod
33 33 def write(self, data):
34 34 """Write bundle data to the store.
35 35
36 36 This function receives the raw data to be written as a str.
37 37 Throws BundleWriteException
38 38 The key of the written data MUST be returned.
39 39 """
40 40
41 41 @abc.abstractmethod
42 42 def read(self, key):
43 43 """Obtain bundle data for a key.
44 44
45 45 Returns None if the bundle isn't known.
46 46 Throws BundleReadException
47 47 The returned object should be a file object supporting read()
48 48 and close().
49 49 """
50 50
51 51 class filebundlestore(object):
52 52 """bundle store in filesystem
53 53
54 54 meant for storing bundles somewhere on disk and on network filesystems
55 55 """
56 56 def __init__(self, ui, repo):
57 57 self.ui = ui
58 58 self.repo = repo
59 59 self.storepath = ui.configpath('scratchbranch', 'storepath')
60 60 if not self.storepath:
61 61 self.storepath = self.repo.vfs.join("scratchbranches",
62 62 "filebundlestore")
63 63 if not os.path.exists(self.storepath):
64 64 os.makedirs(self.storepath)
65 65
66 66 def _dirpath(self, hashvalue):
67 67 """First two bytes of the hash are the name of the upper
68 68 level directory, next two bytes are the name of the
69 69 next level directory"""
70 70 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
71 71
72 72 def _filepath(self, filename):
73 73 return os.path.join(self._dirpath(filename), filename)
74 74
75 75 def write(self, data):
76 76 filename = hashlib.sha1(data).hexdigest()
77 77 dirpath = self._dirpath(filename)
78 78
79 79 if not os.path.exists(dirpath):
80 80 os.makedirs(dirpath)
81 81
82 82 with open(self._filepath(filename), 'wb') as f:
83 83 f.write(data)
84 84
85 85 return filename
86 86
87 87 def read(self, key):
88 88 try:
89 f = open(self._filepath(key), 'rb')
89 with open(self._filepath(key), 'rb') as f:
90 return f.read()
90 91 except IOError:
91 92 return None
92 93
93 return f.read()
94
95 94 class externalbundlestore(abstractbundlestore):
96 95 def __init__(self, put_binary, put_args, get_binary, get_args):
97 96 """
98 97 `put_binary` - path to binary file which uploads bundle to external
99 98 storage and prints key to stdout
100 99 `put_args` - format string with additional args to `put_binary`
101 100 {filename} replacement field can be used.
102 101 `get_binary` - path to binary file which accepts filename and key
103 102 (in that order), downloads bundle from store and saves it to file
104 103 `get_args` - format string with additional args to `get_binary`.
105 104 {filename} and {handle} replacement field can be used.
106 105 """
107 106
108 107 self.put_args = put_args
109 108 self.get_args = get_args
110 109 self.put_binary = put_binary
111 110 self.get_binary = get_binary
112 111
113 112 def _call_binary(self, args):
114 113 p = subprocess.Popen(
115 114 args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
116 115 close_fds=True)
117 116 stdout, stderr = p.communicate()
118 117 returncode = p.returncode
119 118 return returncode, stdout, stderr
120 119
121 120 def write(self, data):
122 121 # Won't work on windows because you can't open file second time without
123 122 # closing it
124 123 with NamedTemporaryFile() as temp:
125 124 temp.write(data)
126 125 temp.flush()
127 126 temp.seek(0)
128 127 formatted_args = [arg.format(filename=temp.name)
129 128 for arg in self.put_args]
130 129 returncode, stdout, stderr = self._call_binary(
131 130 [self.put_binary] + formatted_args)
132 131
133 132 if returncode != 0:
134 133 raise BundleWriteException(
135 134 'Failed to upload to external store: %s' % stderr)
136 135 stdout_lines = stdout.splitlines()
137 136 if len(stdout_lines) == 1:
138 137 return stdout_lines[0]
139 138 else:
140 139 raise BundleWriteException(
141 140 'Bad output from %s: %s' % (self.put_binary, stdout))
142 141
143 142 def read(self, handle):
144 143 # Won't work on windows because you can't open file second time without
145 144 # closing it
146 145 with NamedTemporaryFile() as temp:
147 146 formatted_args = [arg.format(filename=temp.name, handle=handle)
148 147 for arg in self.get_args]
149 148 returncode, stdout, stderr = self._call_binary(
150 149 [self.get_binary] + formatted_args)
151 150
152 151 if returncode != 0:
153 152 raise BundleReadException(
154 153 'Failed to download from external store: %s' % stderr)
155 154 return temp.read()
General Comments 0
You need to be logged in to leave comments. Login now