| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,10 @@ | |||
| 2 | 2 | Changelog | |
| 3 | 3 | ========= | |
| 4 | 4 | ||
| 5 | + 0.3.4 - python 3 support | ||
| 6 | + ======================== | ||
| 7 | + * Internally, hexadecimal SHA1 are treated as ascii encoded strings. Binary SHA1 are treated as bytes. | ||
| 8 | + | ||
| 5 | 9 | 0.3.3 | |
| 6 | 10 | ===== | |
| 7 | 11 | * When fetching, pulling or pushing, and an error occours, it will not be reported on stdout anymore. However, if there is a fatal error, it will still result in a GitCommandError to be thrown. This goes hand in hand with improved fetch result parsing. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,7 +19,11 @@ | |||
| 19 | 19 | stream_copy | |
| 20 | 20 | ) | |
| 21 | 21 | from .exc import GitCommandError | |
| 22 | - from git.compat import text_type | ||
| 22 | + from git.compat import ( | ||
| 23 | + text_type, | ||
| 24 | + string_types, | ||
| 25 | + defenc | ||
| 26 | + ) | ||
| 23 | 27 | ||
| 24 | 28 | execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', | |
| 25 | 29 | 'with_exceptions', 'as_process', | |
@@ -373,9 +377,9 @@ def execute(self, command, | |||
| 373 | 377 | if output_stream is None: | |
| 374 | 378 | stdout_value, stderr_value = proc.communicate() | |
| 375 | 379 | # strip trailing "\n" | |
| 376 | - if stdout_value.endswith("\n"): | ||
| 380 | + if stdout_value.endswith(b"\n"): | ||
| 377 | 381 | stdout_value = stdout_value[:-1] | |
| 378 | - if stderr_value.endswith("\n"): | ||
| 382 | + if stderr_value.endswith(b"\n"): | ||
| 379 | 383 | stderr_value = stderr_value[:-1] | |
| 380 | 384 | status = proc.returncode | |
| 381 | 385 | else: | |
@@ -394,9 +398,9 @@ def execute(self, command, | |||
| 394 | 398 | if self.GIT_PYTHON_TRACE == 'full': | |
| 395 | 399 | cmdstr = " ".join(command) | |
| 396 | 400 | if stderr_value: | |
| 397 | - log.info("%s -> %d; stdout: '%s'; stderr: '%s'", cmdstr, status, stdout_value, stderr_value) | ||
| 401 | + log.info("%s -> %d; stdout: '%s'; stderr: '%s'", cmdstr, status, stdout_value.decode(defenc), stderr_value.decode(defenc)) | ||
| 398 | 402 | elif stdout_value: | |
| 399 | - log.info("%s -> %d; stdout: '%s'", cmdstr, status, stdout_value) | ||
| 403 | + log.info("%s -> %d; stdout: '%s'", cmdstr, status, stdout_value.decode(defenc)) | ||
| 400 | 404 | else: | |
| 401 | 405 | log.info("%s -> %d", cmdstr, status) | |
| 402 | 406 | # END handle debug printing | |
@@ -436,15 +440,15 @@ def transform_kwargs(self, split_single_char_options=False, **kwargs): | |||
| 436 | 440 | def __unpack_args(cls, arg_list): | |
| 437 | 441 | if not isinstance(arg_list, (list, tuple)): | |
| 438 | 442 | if isinstance(arg_list, text_type): | |
| 439 | - return [arg_list.encode('utf-8')] | ||
| 443 | + return [arg_list.encode(defenc)] | ||
| 440 | 444 | return [str(arg_list)] | |
| 441 | 445 | ||
| 442 | 446 | outlist = list() | |
| 443 | 447 | for arg in arg_list: | |
| 444 | 448 | if isinstance(arg_list, (list, tuple)): | |
| 445 | 449 | outlist.extend(cls.__unpack_args(arg)) | |
| 446 | 450 | elif isinstance(arg_list, text_type): | |
| 447 | - outlist.append(arg_list.encode('utf-8')) | ||
| 451 | + outlist.append(arg_list.encode(defenc)) | ||
| 448 | 452 | # END recursion | |
| 449 | 453 | else: | |
| 450 | 454 | outlist.append(str(arg)) | |
@@ -569,14 +573,20 @@ def _parse_object_header(self, header_line): | |||
| 569 | 573 | raise ValueError("Failed to parse header: %r" % header_line) | |
| 570 | 574 | return (tokens[0], tokens[1], int(tokens[2])) | |
| 571 | 575 | ||
| 572 | - def __prepare_ref(self, ref): | ||
| 573 | - # required for command to separate refs on stdin | ||
| 574 | - refstr = str(ref) # could be ref-object | ||
| 575 | - if refstr.endswith("\n"): | ||
| 576 | - return refstr | ||
| 577 | - return refstr + "\n" | ||
| 576 | + def _prepare_ref(self, ref): | ||
| 577 | + # required for command to separate refs on stdin, as bytes | ||
| 578 | + refstr = ref | ||
| 579 | + if isinstance(ref, bytes): | ||
| 580 | + # Assume 40 bytes hexsha - bin-to-ascii for some reason returns bytes, not text | ||
| 581 | + refstr = ref.decode('ascii') | ||
| 582 | + elif not isinstance(ref, string_types): | ||
| 583 | + refstr = str(ref) # could be ref-object | ||
| 584 | + | ||
| 585 | + if not refstr.endswith("\n"): | ||
| 586 | + refstr += "\n" | ||
| 587 | + return refstr.encode(defenc) | ||
| 578 | 588 | ||
| 579 | - def __get_persistent_cmd(self, attr_name, cmd_name, *args, **kwargs): | ||
| 589 | + def _get_persistent_cmd(self, attr_name, cmd_name, *args, **kwargs): | ||
| 580 | 590 | cur_val = getattr(self, attr_name) | |
| 581 | 591 | if cur_val is not None: | |
| 582 | 592 | return cur_val | |
@@ -589,7 +599,7 @@ def __get_persistent_cmd(self, attr_name, cmd_name, *args, **kwargs): | |||
| 589 | 599 | return cmd | |
| 590 | 600 | ||
| 591 | 601 | def __get_object_header(self, cmd, ref): | |
| 592 | - cmd.stdin.write(self.__prepare_ref(ref)) | ||
| 602 | + cmd.stdin.write(self._prepare_ref(ref)) | ||
| 593 | 603 | cmd.stdin.flush() | |
| 594 | 604 | return self._parse_object_header(cmd.stdout.readline()) | |
| 595 | 605 | ||
@@ -601,7 +611,7 @@ def get_object_header(self, ref): | |||
| 601 | 611 | once and reuses the command in subsequent calls. | |
| 602 | 612 | ||
| 603 | 613 | :return: (hexsha, type_string, size_as_int)""" | |
| 604 | - cmd = self.__get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) | ||
| 614 | + cmd = self._get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) | ||
| 605 | 615 | return self.__get_object_header(cmd, ref) | |
| 606 | 616 | ||
| 607 | 617 | def get_object_data(self, ref): | |
@@ -618,7 +628,7 @@ def stream_object_data(self, ref): | |||
| 618 | 628 | :return: (hexsha, type_string, size_as_int, stream) | |
| 619 | 629 | :note: This method is not threadsafe, you need one independent Command instance | |
| 620 | 630 | per thread to be safe !""" | |
| 621 | - cmd = self.__get_persistent_cmd("cat_file_all", "cat_file", batch=True) | ||
| 631 | + cmd = self._get_persistent_cmd("cat_file_all", "cat_file", batch=True) | ||
| 622 | 632 | hexsha, typename, size = self.__get_object_header(cmd, ref) | |
| 623 | 633 | return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout)) | |
| 624 | 634 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,8 @@ | |||
| 7 | 7 | """utilities to help provide compatibility with python 3""" | |
| 8 | 8 | # flake8: noqa | |
| 9 | 9 | ||
| 10 | + import sys | ||
| 11 | + | ||
| 10 | 12 | from gitdb.utils.compat import ( | |
| 11 | 13 | PY3, | |
| 12 | 14 | xrange, | |
@@ -17,11 +19,39 @@ | |||
| 17 | 19 | from gitdb.utils.encoding import ( | |
| 18 | 20 | string_types, | |
| 19 | 21 | text_type, | |
| 20 | - force_bytes | ||
| 22 | + force_bytes, | ||
| 23 | + force_text | ||
| 21 | 24 | ) | |
| 22 | 25 | ||
| 26 | + defenc = sys.getdefaultencoding() | ||
| 23 | 27 | if PY3: | |
| 24 | 28 | import io | |
| 25 | 29 | FileType = io.IOBase | |
| 26 | 30 | else: | |
| 27 | 31 | FileType = file | |
| 32 | + # usually, this is just ascii, which might not enough for our encoding needs | ||
| 33 | + # Unless it's set specifically, we override it to be utf-8 | ||
| 34 | + if defenc == 'ascii': | ||
| 35 | + defenc = 'utf-8' | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + def with_metaclass(meta, *bases): | ||
| 39 | + """copied from https://github.com/Byron/bcore/blob/master/src/python/butility/future.py#L15""" | ||
| 40 | + class metaclass(meta): | ||
| 41 | + __call__ = type.__call__ | ||
| 42 | + __init__ = type.__init__ | ||
| 43 | + | ||
| 44 | + def __new__(cls, name, nbases, d): | ||
| 45 | + if nbases is None: | ||
| 46 | + return type.__new__(cls, name, (), d) | ||
| 47 | + # There may be clients who rely on this attribute to be set to a reasonable value, which is why | ||
| 48 | + # we set the __metaclass__ attribute explicitly | ||
| 49 | + if not PY3 and '___metaclass__' not in d: | ||
| 50 | + d['__metaclass__'] = meta | ||
| 51 | + # end | ||
| 52 | + return meta(name, bases, d) | ||
| 53 | + # end | ||
| 54 | + # end metaclass | ||
| 55 | + return metaclass(meta.__name__ + 'Helper', None, {}) | ||
| 56 | + # end handle py2 | ||
| 57 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,12 +14,15 @@ | |||
| 14 | 14 | import configparser as cp | |
| 15 | 15 | import inspect | |
| 16 | 16 | import logging | |
| 17 | + import abc | ||
| 17 | 18 | ||
| 18 | 19 | from git.odict import OrderedDict | |
| 19 | 20 | from git.util import LockFile | |
| 20 | 21 | from git.compat import ( | |
| 21 | 22 | string_types, | |
| 22 | - FileType | ||
| 23 | + FileType, | ||
| 24 | + defenc, | ||
| 25 | + with_metaclass | ||
| 23 | 26 | ) | |
| 24 | 27 | ||
| 25 | 28 | __all__ = ('GitConfigParser', 'SectionConstraint') | |
@@ -28,7 +31,7 @@ | |||
| 28 | 31 | log = logging.getLogger('git.config') | |
| 29 | 32 | ||
| 30 | 33 | ||
| 31 | - class MetaParserBuilder(type): | ||
| 34 | + class MetaParserBuilder(abc.ABCMeta): | ||
| 32 | 35 | ||
| 33 | 36 | """Utlity class wrapping base-class methods into decorators that assure read-only properties""" | |
| 34 | 37 | def __new__(metacls, name, bases, clsdict): | |
@@ -39,7 +42,7 @@ def __new__(metacls, name, bases, clsdict): | |||
| 39 | 42 | if kmm in clsdict: | |
| 40 | 43 | mutating_methods = clsdict[kmm] | |
| 41 | 44 | for base in bases: | |
| 42 | - methods = (t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_")) | ||
| 45 | + methods = (t for t in inspect.getmembers(base, inspect.isroutine) if not t[0].startswith("_")) | ||
| 43 | 46 | for name, method in methods: | |
| 44 | 47 | if name in clsdict: | |
| 45 | 48 | continue | |
@@ -112,7 +115,7 @@ def config(self): | |||
| 112 | 115 | return self._config | |
| 113 | 116 | ||
| 114 | 117 | ||
| 115 | - class GitConfigParser(cp.RawConfigParser, object): | ||
| 118 | + class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): | ||
| 116 | 119 | ||
| 117 | 120 | """Implements specifics required to read git style configuration files. | |
| 118 | 121 | ||
@@ -128,7 +131,6 @@ class GitConfigParser(cp.RawConfigParser, object): | |||
| 128 | 131 | :note: | |
| 129 | 132 | The config is case-sensitive even when queried, hence section and option names | |
| 130 | 133 | must match perfectly.""" | |
| 131 | - __metaclass__ = MetaParserBuilder | ||
| 132 | 134 | ||
| 133 | 135 | #{ Configuration | |
| 134 | 136 | # The lock type determines the type of lock to use in new configuration readers. | |
@@ -150,7 +152,6 @@ class GitConfigParser(cp.RawConfigParser, object): | |||
| 150 | 152 | ||
| 151 | 153 | # list of RawConfigParser methods able to change the instance | |
| 152 | 154 | _mutating_methods_ = ("add_section", "remove_section", "remove_option", "set") | |
| 153 | - __slots__ = ("_sections", "_defaults", "_file_or_files", "_read_only", "_is_initialized", '_lock') | ||
| 154 | 155 | ||
| 155 | 156 | def __init__(self, file_or_files, read_only=True): | |
| 156 | 157 | """Initialize a configuration reader to read the given file_or_files and to | |
@@ -162,12 +163,12 @@ def __init__(self, file_or_files, read_only=True): | |||
| 162 | 163 | :param read_only: | |
| 163 | 164 | If True, the ConfigParser may only read the data , but not change it. | |
| 164 | 165 | If False, only a single file path or file object may be given.""" | |
| 165 | - super(GitConfigParser, self).__init__() | ||
| 166 | - # initialize base with ordered dictionaries to be sure we write the same | ||
| 167 | - # file back | ||
| 168 | - self._sections = OrderedDict() | ||
| 169 | - self._defaults = OrderedDict() | ||
| 166 | + cp.RawConfigParser.__init__(self, dict_type=OrderedDict) | ||
| 170 | 167 | ||
| 168 | + # Used in python 3, needs to stay in sync with sections for underlying implementation to work | ||
| 169 | + if not hasattr(self, '_proxies'): | ||
| 170 | + self._proxies = self._dict() | ||
| 171 | + | ||
| 171 | 172 | self._file_or_files = file_or_files | |
| 172 | 173 | self._read_only = read_only | |
| 173 | 174 | self._is_initialized = False | |
@@ -222,7 +223,8 @@ def _read(self, fp, fpname): | |||
| 222 | 223 | lineno = 0 | |
| 223 | 224 | e = None # None, or an exception | |
| 224 | 225 | while True: | |
| 225 | - line = fp.readline() | ||
| 226 | + # we assume to read binary ! | ||
| 227 | + line = fp.readline().decode(defenc) | ||
| 226 | 228 | if not line: | |
| 227 | 229 | break | |
| 228 | 230 | lineno = lineno + 1 | |
@@ -242,9 +244,9 @@ def _read(self, fp, fpname): | |||
| 242 | 244 | elif sectname == cp.DEFAULTSECT: | |
| 243 | 245 | cursect = self._defaults | |
| 244 | 246 | else: | |
| 245 | - # THE ONLY LINE WE CHANGED ! | ||
| 246 | - cursect = OrderedDict((('__name__', sectname),)) | ||
| 247 | + cursect = self._dict((('__name__', sectname),)) | ||
| 247 | 248 | self._sections[sectname] = cursect | |
| 249 | + self._proxies[sectname] = None | ||
| 248 | 250 | # So sections can't start with a continuation line | |
| 249 | 251 | optname = None | |
| 250 | 252 | # no section header in the file? | |
@@ -295,7 +297,7 @@ def read(self): | |||
| 295 | 297 | # assume a path if it is not a file-object | |
| 296 | 298 | if not hasattr(file_object, "seek"): | |
| 297 | 299 | try: | |
| 298 | - fp = open(file_object) | ||
| 300 | + fp = open(file_object, 'rb') | ||
| 299 | 301 | close_fp = True | |
| 300 | 302 | except IOError: | |
| 301 | 303 | continue | |
@@ -314,16 +316,17 @@ def _write(self, fp): | |||
| 314 | 316 | """Write an .ini-format representation of the configuration state in | |
| 315 | 317 | git compatible format""" | |
| 316 | 318 | def write_section(name, section_dict): | |
| 317 | - fp.write("[%s]\n" % name) | ||
| 319 | + fp.write(("[%s]\n" % name).encode(defenc)) | ||
| 318 | 320 | for (key, value) in section_dict.items(): | |
| 319 | 321 | if key != "__name__": | |
| 320 | - fp.write("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) | ||
| 322 | + fp.write(("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))).encode(defenc)) | ||
| 321 | 323 | # END if key is not __name__ | |
| 322 | 324 | # END section writing | |
| 323 | 325 | ||
| 324 | 326 | if self._defaults: | |
| 325 | 327 | write_section(cp.DEFAULTSECT, self._defaults) | |
| 326 | - map(lambda t: write_section(t[0], t[1]), self._sections.items()) | ||
| 328 | + for name, value in self._sections.items(): | ||
| 329 | + write_section(name, value) | ||
| 327 | 330 | ||
| 328 | 331 | @needs_values | |
| 329 | 332 | def write(self): | |
@@ -371,8 +374,7 @@ def _assure_writable(self, method_name): | |||
| 371 | 374 | @set_dirty_and_flush_changes | |
| 372 | 375 | def add_section(self, section): | |
| 373 | 376 | """Assures added options will stay in order""" | |
| 374 | - super(GitConfigParser, self).add_section(section) | ||
| 375 | - self._sections[section] = OrderedDict() | ||
| 377 | + return super(GitConfigParser, self).add_section(section) | ||
| 376 | 378 | ||
| 377 | 379 | @property | |
| 378 | 380 | def read_only(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -46,17 +46,17 @@ def get_object_type_by_name(object_type_name): | |||
| 46 | 46 | :param object_type_name: Member of TYPES | |
| 47 | 47 | ||
| 48 | 48 | :raise ValueError: In case object_type_name is unknown""" | |
| 49 | - if object_type_name == "commit": | ||
| 50 | - import commit | ||
| 49 | + if object_type_name == b"commit": | ||
| 50 | + from . import commit | ||
| 51 | 51 | return commit.Commit | |
| 52 | - elif object_type_name == "tag": | ||
| 53 | - import tag | ||
| 52 | + elif object_type_name == b"tag": | ||
| 53 | + from . import tag | ||
| 54 | 54 | return tag.TagObject | |
| 55 | - elif object_type_name == "blob": | ||
| 56 | - import blob | ||
| 55 | + elif object_type_name == b"blob": | ||
| 56 | + from . import blob | ||
| 57 | 57 | return blob.Blob | |
| 58 | - elif object_type_name == "tree": | ||
| 59 | - import tree | ||
| 58 | + elif object_type_name == b"tree": | ||
| 59 | + from . import tree | ||
| 60 | 60 | return tree.Tree | |
| 61 | 61 | else: | |
| 62 | 62 | raise ValueError("Cannot handle unknown object type: %s" % object_type_name) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,7 +19,9 @@ | |||
| 19 | 19 | hex_to_bin, | |
| 20 | 20 | LockedFD | |
| 21 | 21 | ) | |
| 22 | - from git.compat import string_types | ||
| 22 | + from git.compat import ( | ||
| 23 | + string_types, | ||
| 24 | + ) | ||
| 23 | 25 | ||
| 24 | 26 | from .log import RefLog | |
| 25 | 27 | ||
@@ -79,10 +81,10 @@ def _get_packed_refs_path(cls, repo): | |||
| 79 | 81 | ||
| 80 | 82 | @classmethod | |
| 81 | 83 | def _iter_packed_refs(cls, repo): | |
| 82 | - """Returns an iterator yielding pairs of sha1/path pairs for the corresponding refs. | ||
| 84 | + """Returns an iterator yielding pairs of sha1/path pairs (as bytes) for the corresponding refs. | ||
| 83 | 85 | :note: The packed refs file will be kept open as long as we iterate""" | |
| 84 | 86 | try: | |
| 85 | - fp = open(cls._get_packed_refs_path(repo), 'rb') | ||
| 87 | + fp = open(cls._get_packed_refs_path(repo), 'rt') | ||
| 86 | 88 | for line in fp: | |
| 87 | 89 | line = line.strip() | |
| 88 | 90 | if not line: | |
@@ -123,12 +125,12 @@ def dereference_recursive(cls, repo, ref_path): | |||
| 123 | 125 | ||
| 124 | 126 | @classmethod | |
| 125 | 127 | def _get_ref_info(cls, repo, ref_path): | |
| 126 | - """Return: (sha, target_ref_path) if available, the sha the file at | ||
| 128 | + """Return: (str(sha), str(target_ref_path)) if available, the sha the file at | ||
| 127 | 129 | rela_path points to, or None. target_ref_path is the reference we | |
| 128 | 130 | point to, or None""" | |
| 129 | 131 | tokens = None | |
| 130 | 132 | try: | |
| 131 | - fp = open(join(repo.git_dir, ref_path), 'r') | ||
| 133 | + fp = open(join(repo.git_dir, ref_path), 'rt') | ||
| 132 | 134 | value = fp.read().rstrip() | |
| 133 | 135 | fp.close() | |
| 134 | 136 | # Don't only split on spaces, but on whitespace, which allows to parse lines like | |
@@ -141,7 +143,8 @@ def _get_ref_info(cls, repo, ref_path): | |||
| 141 | 143 | for sha, path in cls._iter_packed_refs(repo): | |
| 142 | 144 | if path != ref_path: | |
| 143 | 145 | continue | |
| 144 | - tokens = (sha, path) | ||
| 146 | + # sha will be used as | ||
| 147 | + tokens = sha, path | ||
| 145 | 148 | break | |
| 146 | 149 | # END for each packed ref | |
| 147 | 150 | # END handle packed refs | |
| Back | FazBrowse Home | New Git URL |
0 commit comments