##// END OF EJS Templates
infinitepush: remove unused import to tempfile...
marmoute -
r45771:bfc6e75c stable
parent child Browse files
Show More
@@ -1,198 +1,197 b''
1 # This software may be used and distributed according to the terms of the
1 # This software may be used and distributed according to the terms of the
2 # GNU General Public License version 2 or any later version.
2 # GNU General Public License version 2 or any later version.
3
3
4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
4 # based on bundleheads extension by Gregory Szorc <gps@mozilla.com>
5
5
6 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import abc
8 import abc
9 import os
9 import os
10 import subprocess
10 import subprocess
11 import tempfile
12
11
13 from mercurial.pycompat import open
12 from mercurial.pycompat import open
14 from mercurial import (
13 from mercurial import (
15 node,
14 node,
16 pycompat,
15 pycompat,
17 )
16 )
18 from mercurial.utils import (
17 from mercurial.utils import (
19 hashutil,
18 hashutil,
20 procutil,
19 procutil,
21 )
20 )
22
21
23
22
24 class BundleWriteException(Exception):
23 class BundleWriteException(Exception):
25 pass
24 pass
26
25
27
26
28 class BundleReadException(Exception):
27 class BundleReadException(Exception):
29 pass
28 pass
30
29
31
30
32 class abstractbundlestore(object): # pytype: disable=ignored-metaclass
31 class abstractbundlestore(object): # pytype: disable=ignored-metaclass
33 """Defines the interface for bundle stores.
32 """Defines the interface for bundle stores.
34
33
35 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
36 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
37 be any Python object understood by the corresponding bundle index (see
36 be any Python object understood by the corresponding bundle index (see
38 ``abstractbundleindex`` below).
37 ``abstractbundleindex`` below).
39 """
38 """
40
39
41 __metaclass__ = abc.ABCMeta
40 __metaclass__ = abc.ABCMeta
42
41
43 @abc.abstractmethod
42 @abc.abstractmethod
44 def write(self, data):
43 def write(self, data):
45 """Write bundle data to the store.
44 """Write bundle data to the store.
46
45
47 This function receives the raw data to be written as a str.
46 This function receives the raw data to be written as a str.
48 Throws BundleWriteException
47 Throws BundleWriteException
49 The key of the written data MUST be returned.
48 The key of the written data MUST be returned.
50 """
49 """
51
50
52 @abc.abstractmethod
51 @abc.abstractmethod
53 def read(self, key):
52 def read(self, key):
54 """Obtain bundle data for a key.
53 """Obtain bundle data for a key.
55
54
56 Returns None if the bundle isn't known.
55 Returns None if the bundle isn't known.
57 Throws BundleReadException
56 Throws BundleReadException
58 The returned object should be a file object supporting read()
57 The returned object should be a file object supporting read()
59 and close().
58 and close().
60 """
59 """
61
60
62
61
63 class filebundlestore(object):
62 class filebundlestore(object):
64 """bundle store in filesystem
63 """bundle store in filesystem
65
64
66 meant for storing bundles somewhere on disk and on network filesystems
65 meant for storing bundles somewhere on disk and on network filesystems
67 """
66 """
68
67
69 def __init__(self, ui, repo):
68 def __init__(self, ui, repo):
70 self.ui = ui
69 self.ui = ui
71 self.repo = repo
70 self.repo = repo
72 self.storepath = ui.configpath(b'scratchbranch', b'storepath')
71 self.storepath = ui.configpath(b'scratchbranch', b'storepath')
73 if not self.storepath:
72 if not self.storepath:
74 self.storepath = self.repo.vfs.join(
73 self.storepath = self.repo.vfs.join(
75 b"scratchbranches", b"filebundlestore"
74 b"scratchbranches", b"filebundlestore"
76 )
75 )
77 if not os.path.exists(self.storepath):
76 if not os.path.exists(self.storepath):
78 os.makedirs(self.storepath)
77 os.makedirs(self.storepath)
79
78
80 def _dirpath(self, hashvalue):
79 def _dirpath(self, hashvalue):
81 """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
82 level directory, next two bytes are the name of the
81 level directory, next two bytes are the name of the
83 next level directory"""
82 next level directory"""
84 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
83 return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4])
85
84
86 def _filepath(self, filename):
85 def _filepath(self, filename):
87 return os.path.join(self._dirpath(filename), filename)
86 return os.path.join(self._dirpath(filename), filename)
88
87
89 def write(self, data):
88 def write(self, data):
90 filename = node.hex(hashutil.sha1(data).digest())
89 filename = node.hex(hashutil.sha1(data).digest())
91 dirpath = self._dirpath(filename)
90 dirpath = self._dirpath(filename)
92
91
93 if not os.path.exists(dirpath):
92 if not os.path.exists(dirpath):
94 os.makedirs(dirpath)
93 os.makedirs(dirpath)
95
94
96 with open(self._filepath(filename), b'wb') as f:
95 with open(self._filepath(filename), b'wb') as f:
97 f.write(data)
96 f.write(data)
98
97
99 return filename
98 return filename
100
99
101 def read(self, key):
100 def read(self, key):
102 try:
101 try:
103 with open(self._filepath(key), b'rb') as f:
102 with open(self._filepath(key), b'rb') as f:
104 return f.read()
103 return f.read()
105 except IOError:
104 except IOError:
106 return None
105 return None
107
106
108
107
109 def format_placeholders_args(args, filename=None, handle=None):
108 def format_placeholders_args(args, filename=None, handle=None):
110 """Formats `args` with Infinitepush replacements.
109 """Formats `args` with Infinitepush replacements.
111
110
112 Hack to get `str.format()`-ed strings working in a BC way with
111 Hack to get `str.format()`-ed strings working in a BC way with
113 bytes.
112 bytes.
114 """
113 """
115 formatted_args = []
114 formatted_args = []
116 for arg in args:
115 for arg in args:
117 if filename and arg == b'{filename}':
116 if filename and arg == b'{filename}':
118 formatted_args.append(filename)
117 formatted_args.append(filename)
119 elif handle and arg == b'{handle}':
118 elif handle and arg == b'{handle}':
120 formatted_args.append(handle)
119 formatted_args.append(handle)
121 else:
120 else:
122 formatted_args.append(arg)
121 formatted_args.append(arg)
123 return formatted_args
122 return formatted_args
124
123
125
124
126 class externalbundlestore(abstractbundlestore):
125 class externalbundlestore(abstractbundlestore):
127 def __init__(self, put_binary, put_args, get_binary, get_args):
126 def __init__(self, put_binary, put_args, get_binary, get_args):
128 """
127 """
129 `put_binary` - path to binary file which uploads bundle to external
128 `put_binary` - path to binary file which uploads bundle to external
130 storage and prints key to stdout
129 storage and prints key to stdout
131 `put_args` - format string with additional args to `put_binary`
130 `put_args` - format string with additional args to `put_binary`
132 {filename} replacement field can be used.
131 {filename} replacement field can be used.
133 `get_binary` - path to binary file which accepts filename and key
132 `get_binary` - path to binary file which accepts filename and key
134 (in that order), downloads bundle from store and saves it to file
133 (in that order), downloads bundle from store and saves it to file
135 `get_args` - format string with additional args to `get_binary`.
134 `get_args` - format string with additional args to `get_binary`.
136 {filename} and {handle} replacement field can be used.
135 {filename} and {handle} replacement field can be used.
137 """
136 """
138
137
139 self.put_args = put_args
138 self.put_args = put_args
140 self.get_args = get_args
139 self.get_args = get_args
141 self.put_binary = put_binary
140 self.put_binary = put_binary
142 self.get_binary = get_binary
141 self.get_binary = get_binary
143
142
144 def _call_binary(self, args):
143 def _call_binary(self, args):
145 p = subprocess.Popen(
144 p = subprocess.Popen(
146 pycompat.rapply(procutil.tonativestr, args),
145 pycompat.rapply(procutil.tonativestr, args),
147 stdout=subprocess.PIPE,
146 stdout=subprocess.PIPE,
148 stderr=subprocess.PIPE,
147 stderr=subprocess.PIPE,
149 close_fds=True,
148 close_fds=True,
150 )
149 )
151 stdout, stderr = p.communicate()
150 stdout, stderr = p.communicate()
152 returncode = p.returncode
151 returncode = p.returncode
153 return returncode, stdout, stderr
152 return returncode, stdout, stderr
154
153
155 def write(self, data):
154 def write(self, data):
156 # Won't work on windows because you can't open file second time without
155 # Won't work on windows because you can't open file second time without
157 # closing it
156 # closing it
158 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
157 # TODO: rewrite without str.format() and replace NamedTemporaryFile()
159 # with pycompat.namedtempfile()
158 # with pycompat.namedtempfile()
160 with pycompat.namedtempfile() as temp:
159 with pycompat.namedtempfile() as temp:
161 temp.write(data)
160 temp.write(data)
162 temp.flush()
161 temp.flush()
163 temp.seek(0)
162 temp.seek(0)
164 formatted_args = format_placeholders_args(
163 formatted_args = format_placeholders_args(
165 self.put_args, filename=temp.name
164 self.put_args, filename=temp.name
166 )
165 )
167 returncode, stdout, stderr = self._call_binary(
166 returncode, stdout, stderr = self._call_binary(
168 [self.put_binary] + formatted_args
167 [self.put_binary] + formatted_args
169 )
168 )
170
169
171 if returncode != 0:
170 if returncode != 0:
172 raise BundleWriteException(
171 raise BundleWriteException(
173 b'Failed to upload to external store: %s' % stderr
172 b'Failed to upload to external store: %s' % stderr
174 )
173 )
175 stdout_lines = stdout.splitlines()
174 stdout_lines = stdout.splitlines()
176 if len(stdout_lines) == 1:
175 if len(stdout_lines) == 1:
177 return stdout_lines[0]
176 return stdout_lines[0]
178 else:
177 else:
179 raise BundleWriteException(
178 raise BundleWriteException(
180 b'Bad output from %s: %s' % (self.put_binary, stdout)
179 b'Bad output from %s: %s' % (self.put_binary, stdout)
181 )
180 )
182
181
183 def read(self, handle):
182 def read(self, handle):
184 # Won't work on windows because you can't open file second time without
183 # Won't work on windows because you can't open file second time without
185 # closing it
184 # closing it
186 with pycompat.namedtempfile() as temp:
185 with pycompat.namedtempfile() as temp:
187 formatted_args = format_placeholders_args(
186 formatted_args = format_placeholders_args(
188 self.get_args, filename=temp.name, handle=handle
187 self.get_args, filename=temp.name, handle=handle
189 )
188 )
190 returncode, stdout, stderr = self._call_binary(
189 returncode, stdout, stderr = self._call_binary(
191 [self.get_binary] + formatted_args
190 [self.get_binary] + formatted_args
192 )
191 )
193
192
194 if returncode != 0:
193 if returncode != 0:
195 raise BundleReadException(
194 raise BundleReadException(
196 b'Failed to download from external store: %s' % stderr
195 b'Failed to download from external store: %s' % stderr
197 )
196 )
198 return temp.read()
197 return temp.read()
General Comments 0
You need to be logged in to leave comments. Login now