Mercurial > hg > hg-fastimport
comparison hgext3rd/fastimport/vendor/python_fastimport/helpers.py @ 86:28704a2a7461 vendor/python-fastimport
Import python-fastimport-0.9.8
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Tue, 19 Jan 2021 22:56:34 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 85:1f5544a8870b | 86:28704a2a7461 |
|---|---|
| 1 # Copyright (C) 2008 Canonical Ltd | |
| 2 # | |
| 3 # This program is free software; you can redistribute it and/or modify | |
| 4 # it under the terms of the GNU General Public License as published by | |
| 5 # the Free Software Foundation; either version 2 of the License, or | |
| 6 # (at your option) any later version. | |
| 7 # | |
| 8 # This program is distributed in the hope that it will be useful, | |
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 # GNU General Public License for more details. | |
| 12 # | |
| 13 # You should have received a copy of the GNU General Public License | |
| 14 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 15 | |
| 16 """Miscellaneous useful stuff.""" | |
| 17 import sys | |
| 18 | |
| 19 | |
| 20 def _common_path_and_rest(l1, l2, common=[]): | |
| 21 # From http://code.activestate.com/recipes/208993/ | |
| 22 if len(l1) < 1: return (common, l1, l2) | |
| 23 if len(l2) < 1: return (common, l1, l2) | |
| 24 if l1[0] != l2[0]: return (common, l1, l2) | |
| 25 return _common_path_and_rest( | |
| 26 l1[1:], | |
| 27 l2[1:], | |
| 28 common + [ | |
| 29 l1[0:1] # return a byte string in python 3 unlike l1[0] that | |
| 30 # would return an integer. | |
| 31 ] | |
| 32 ) | |
| 33 | |
| 34 | |
| 35 def common_path(path1, path2): | |
| 36 """Find the common bit of 2 paths.""" | |
| 37 return b''.join(_common_path_and_rest(path1, path2)[0]) | |
| 38 | |
| 39 | |
| 40 def common_directory(paths): | |
| 41 """Find the deepest common directory of a list of paths. | |
| 42 | |
| 43 :return: if no paths are provided, None is returned; | |
| 44 if there is no common directory, '' is returned; | |
| 45 otherwise the common directory with a trailing / is returned. | |
| 46 """ | |
| 47 import posixpath | |
| 48 def get_dir_with_slash(path): | |
| 49 if path == b'' or path.endswith(b'/'): | |
| 50 return path | |
| 51 else: | |
| 52 dirname, basename = posixpath.split(path) | |
| 53 if dirname == b'': | |
| 54 return dirname | |
| 55 else: | |
| 56 return dirname + b'/' | |
| 57 | |
| 58 if not paths: | |
| 59 return None | |
| 60 elif len(paths) == 1: | |
| 61 return get_dir_with_slash(paths[0]) | |
| 62 else: | |
| 63 common = common_path(paths[0], paths[1]) | |
| 64 for path in paths[2:]: | |
| 65 common = common_path(common, path) | |
| 66 return get_dir_with_slash(common) | |
| 67 | |
| 68 | |
| 69 def is_inside(directory, fname): | |
| 70 """True if fname is inside directory. | |
| 71 | |
| 72 The parameters should typically be passed to osutils.normpath first, so | |
| 73 that . and .. and repeated slashes are eliminated, and the separators | |
| 74 are canonical for the platform. | |
| 75 | |
| 76 The empty string as a dir name is taken as top-of-tree and matches | |
| 77 everything. | |
| 78 """ | |
| 79 # XXX: Most callers of this can actually do something smarter by | |
| 80 # looking at the inventory | |
| 81 if directory == fname: | |
| 82 return True | |
| 83 | |
| 84 if directory == b'': | |
| 85 return True | |
| 86 | |
| 87 if not directory.endswith(b'/'): | |
| 88 directory += b'/' | |
| 89 | |
| 90 return fname.startswith(directory) | |
| 91 | |
| 92 | |
| 93 def is_inside_any(dir_list, fname): | |
| 94 """True if fname is inside any of given dirs.""" | |
| 95 for dirname in dir_list: | |
| 96 if is_inside(dirname, fname): | |
| 97 return True | |
| 98 return False | |
| 99 | |
| 100 | |
| 101 def utf8_bytes_string(s): | |
| 102 """Convert a string to a bytes string (if necessary, encode in utf8)""" | |
| 103 if sys.version_info[0] == 2: | |
| 104 if isinstance(s, str): | |
| 105 return s | |
| 106 else: | |
| 107 return s.encode('utf8') | |
| 108 else: | |
| 109 if isinstance(s, str): | |
| 110 return bytes(s, encoding='utf8') | |
| 111 else: | |
| 112 return s | |
| 113 | |
| 114 | |
| 115 def repr_bytes(obj): | |
| 116 """Return a bytes representation of the object""" | |
| 117 if sys.version_info[0] == 2: | |
| 118 return repr(obj) | |
| 119 else: | |
| 120 return bytes(obj) | |
| 121 | |
| 122 | |
| 123 class newobject(object): | |
| 124 """ | |
| 125 A magical object class that provides Python 2 compatibility methods:: | |
| 126 next | |
| 127 __unicode__ | |
| 128 __nonzero__ | |
| 129 | |
| 130 Subclasses of this class can merely define the Python 3 methods (__next__, | |
| 131 __str__, and __bool__). | |
| 132 | |
| 133 This is a copy/paste of the future.types.newobject class of the future | |
| 134 package. | |
| 135 """ | |
| 136 def next(self): | |
| 137 if hasattr(self, '__next__'): | |
| 138 return type(self).__next__(self) | |
| 139 raise TypeError('newobject is not an iterator') | |
| 140 | |
| 141 def __unicode__(self): | |
| 142 # All subclasses of the builtin object should have __str__ defined. | |
| 143 # Note that old-style classes do not have __str__ defined. | |
| 144 if hasattr(self, '__str__'): | |
| 145 s = type(self).__str__(self) | |
| 146 else: | |
| 147 s = str(self) | |
| 148 if isinstance(s, unicode): | |
| 149 return s | |
| 150 else: | |
| 151 return s.decode('utf-8') | |
| 152 | |
| 153 def __nonzero__(self): | |
| 154 if hasattr(self, '__bool__'): | |
| 155 return type(self).__bool__(self) | |
| 156 # object has no __nonzero__ method | |
| 157 return True | |
| 158 | |
| 159 # Are these ever needed? | |
| 160 # def __div__(self): | |
| 161 # return self.__truediv__() | |
| 162 | |
| 163 # def __idiv__(self, other): | |
| 164 # return self.__itruediv__(other) | |
| 165 | |
| 166 def __long__(self): | |
| 167 if not hasattr(self, '__int__'): | |
| 168 return NotImplemented | |
| 169 return self.__int__() # not type(self).__int__(self) | |
| 170 | |
| 171 # def __new__(cls, *args, **kwargs): | |
| 172 # """ | |
| 173 # dict() -> new empty dictionary | |
| 174 # dict(mapping) -> new dictionary initialized from a mapping object's | |
| 175 # (key, value) pairs | |
| 176 # dict(iterable) -> new dictionary initialized as if via: | |
| 177 # d = {} | |
| 178 # for k, v in iterable: | |
| 179 # d[k] = v | |
| 180 # dict(**kwargs) -> new dictionary initialized with the name=value pairs | |
| 181 # in the keyword argument list. For example: dict(one=1, two=2) | |
| 182 # """ | |
| 183 | |
| 184 # if len(args) == 0: | |
| 185 # return super(newdict, cls).__new__(cls) | |
| 186 # elif type(args[0]) == newdict: | |
| 187 # return args[0] | |
| 188 # else: | |
| 189 # value = args[0] | |
| 190 # return super(newdict, cls).__new__(cls, value) | |
| 191 | |
| 192 def __native__(self): | |
| 193 """ | |
| 194 Hook for the future.utils.native() function | |
| 195 """ | |
| 196 return object(self) | |
| 197 | |
| 198 | |
| 199 def binary_stream(stream): | |
| 200 """Ensure a stream is binary on Windows. | |
| 201 | |
| 202 :return: the stream | |
| 203 """ | |
| 204 try: | |
| 205 import os | |
| 206 if os.name == 'nt': | |
| 207 fileno = getattr(stream, 'fileno', None) | |
| 208 if fileno: | |
| 209 no = fileno() | |
| 210 if no >= 0: # -1 means we're working as subprocess | |
| 211 import msvcrt | |
| 212 msvcrt.setmode(no, os.O_BINARY) | |
| 213 except ImportError: | |
| 214 pass | |
| 215 return stream | |
| 216 | |
| 217 | |
| 218 def invert_dictset(d): | |
| 219 """Invert a dictionary with keys matching a set of values, turned into lists.""" | |
| 220 # Based on recipe from ASPN | |
| 221 result = {} | |
| 222 for k, c in d.items(): | |
| 223 for v in c: | |
| 224 keys = result.setdefault(v, []) | |
| 225 keys.append(k) | |
| 226 return result | |
| 227 | |
| 228 | |
| 229 def invert_dict(d): | |
| 230 """Invert a dictionary with keys matching each value turned into a list.""" | |
| 231 # Based on recipe from ASPN | |
| 232 result = {} | |
| 233 for k, v in d.items(): | |
| 234 keys = result.setdefault(v, []) | |
| 235 keys.append(k) | |
| 236 return result | |
| 237 | |
| 238 | |
| 239 def defines_to_dict(defines): | |
| 240 """Convert a list of definition strings to a dictionary.""" | |
| 241 if defines is None: | |
| 242 return None | |
| 243 result = {} | |
| 244 for define in defines: | |
| 245 kv = define.split('=', 1) | |
| 246 if len(kv) == 1: | |
| 247 result[define.strip()] = 1 | |
| 248 else: | |
| 249 result[kv[0].strip()] = kv[1].strip() | |
| 250 return result | |
| 251 | |
| 252 | |
| 253 def get_source_stream(source): | |
| 254 if source == '-' or source is None: | |
| 255 import sys | |
| 256 stream = binary_stream(sys.stdin) | |
| 257 elif source.endswith('.gz'): | |
| 258 import gzip | |
| 259 stream = gzip.open(source, "rb") | |
| 260 else: | |
| 261 stream = open(source, "rb") | |
| 262 return stream | |
| 263 | |
| 264 | |
| 265 |
