From f6aa8d116eb33293c0a9d6d600eb7c32832758b9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 Jan 2015 19:14:33 +0100 Subject: initial set of adjustments to make (most) imports work. More to come, especially when it's about strings --- git/cmd.py | 14 ++++++++------ git/compat.py | 19 +++++++++++++++++++ git/config.py | 6 +++++- git/db.py | 11 +++++------ git/diff.py | 6 +++--- git/index/base.py | 11 +++++------ git/index/fun.py | 8 ++++---- git/index/typ.py | 9 ++++----- git/objects/__init__.py | 7 ++++--- git/objects/base.py | 2 +- git/objects/blob.py | 3 +-- git/objects/commit.py | 15 +++++++-------- git/objects/submodule/base.py | 6 +++--- git/objects/submodule/root.py | 7 +++++-- git/objects/submodule/util.py | 2 +- git/objects/tag.py | 6 +++--- git/objects/tree.py | 16 +++++++--------- git/refs/head.py | 8 +++----- git/refs/reference.py | 3 ++- git/refs/remote.py | 3 ++- git/refs/symbolic.py | 3 ++- git/refs/tag.py | 2 +- git/remote.py | 29 +++++++++++++++-------------- git/repo/base.py | 2 +- git/repo/fun.py | 4 +++- git/test/lib/asserts.py | 18 ++++++++---------- git/test/lib/helper.py | 6 +++--- git/test/performance/test_commit.py | 2 +- git/test/test_commit.py | 2 +- git/test/test_fun.py | 2 +- git/test/test_index.py | 2 +- git/test/test_repo.py | 2 +- git/test/test_tree.py | 2 +- git/util.py | 5 +++-- 34 files changed, 134 insertions(+), 109 deletions(-) create mode 100644 git/compat.py (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index c355eacf..b9e4bc09 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -7,18 +7,20 @@ import os import sys import logging -from util import ( - LazyMixin, - stream_copy -) -from exc import GitCommandError - from subprocess import ( call, Popen, PIPE ) + +from .util import ( + LazyMixin, + stream_copy +) +from .exc import GitCommandError + + execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', 'output_stream') diff --git a/git/compat.py b/git/compat.py new file mode 100644 index 00000000..52fc599c --- /dev/null +++ b/git/compat.py @@ -0,0 +1,19 @@ +#-*-coding:utf-8-*- +# config.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +"""utilities to help provide compatibility with python 3""" + +from gitdb.utils.compat import ( # noqa + PY3, + xrange, + MAXSIZE, + izip, +) + +from gitdb.utils.encoding import ( # noqa + string_types, + text_type +) diff --git a/git/config.py b/git/config.py index 6a85760c..685dbed8 100644 --- a/git/config.py +++ b/git/config.py @@ -7,7 +7,11 @@ configuration files""" import re -import ConfigParser as cp +try: + import ConfigParser as cp +except ImportError: + # PY3 + import configparser as cp import inspect import logging diff --git a/git/db.py b/git/db.py index ab39f6c5..c4e19858 100644 --- a/git/db.py +++ b/git/db.py @@ -1,14 +1,8 @@ """Module with our own gitdb implementation - it uses the git command""" -from exc import ( - GitCommandError, - BadObject -) - from gitdb.base import ( OInfo, OStream ) - from gitdb.util import ( bin_to_hex, hex_to_bin @@ -16,6 +10,11 @@ from gitdb.util import ( from gitdb.db import GitDB from gitdb.db import LooseObjectDB +from .exc import ( + GitCommandError, + BadObject +) + __all__ = ('GitCmdObjectDB', 'GitDB') diff --git a/git/diff.py b/git/diff.py index 5325ad6b..b3e7245b 100644 --- a/git/diff.py +++ b/git/diff.py @@ -3,13 +3,13 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - import re -from objects.blob import Blob -from objects.util import mode_str_to_int from gitdb.util import hex_to_bin +from .objects.blob import Blob +from .objects.util import mode_str_to_int + __all__ = ('Diffable', 'DiffIndex', 'Diff') diff --git a/git/index/base.py b/git/index/base.py index fdcfcd12..91dcea42 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -8,16 +8,16 @@ import os import sys import subprocess import glob -from cStringIO import StringIO +from io import StringIO from stat import S_ISLNK -from typ import ( +from .typ import ( BaseIndexEntry, IndexEntry, ) -from util import ( +from .util import ( TemporaryFileSwap, post_clear_cache, default_index, @@ -25,7 +25,6 @@ from util import ( ) import git.diff as diff - from git.exc import ( GitCommandError, CheckoutError @@ -40,6 +39,7 @@ from git.objects import ( ) from git.objects.util import Serializable +from git.compat import izip from git.util import ( LazyMixin, @@ -49,7 +49,7 @@ from git.util import ( to_native_path_linux, ) -from fun import ( +from .fun import ( entry_key, write_cache, read_cache, @@ -62,7 +62,6 @@ from fun import ( from gitdb.base import IStream from gitdb.db import MemoryDB from gitdb.util import to_bin_sha -from itertools import izip __all__ = ('IndexFile', 'CheckoutError') diff --git a/git/index/fun.py b/git/index/fun.py index eec90519..004f992e 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -12,7 +12,7 @@ from stat import ( S_IFGITLINK = S_IFLNK | S_IFDIR # a submodule -from cStringIO import StringIO +from io import StringIO from git.util import IndexFileSHA1Writer from git.exc import UnmergedEntriesError @@ -22,7 +22,7 @@ from git.objects.fun import ( traverse_trees_recursive ) -from typ import ( +from .typ import ( BaseIndexEntry, IndexEntry, CE_NAMEMASK, @@ -30,7 +30,7 @@ from typ import ( ) CE_NAMEMASK_INV = ~CE_NAMEMASK -from util import ( +from .util import ( pack, unpack ) @@ -49,7 +49,7 @@ def stat_mode_to_index_mode(mode): return S_IFLNK if S_ISDIR(mode) or S_IFMT(mode) == S_IFGITLINK: # submodules return S_IFGITLINK - return S_IFREG | 0644 | (mode & 0100) # blobs with or without executable bit + return S_IFREG | 0o644 | (mode & 0o100) # blobs with or without executable bit def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1Writer): diff --git a/git/index/typ.py b/git/index/typ.py index 222252c5..692e1e18 100644 --- a/git/index/typ.py +++ b/git/index/typ.py @@ -1,15 +1,14 @@ """Module with additional types used by the index""" -from util import ( +from binascii import b2a_hex + +from .util import ( pack, unpack ) +from git.objects import Blob -from binascii import ( - b2a_hex, -) -from git.objects import Blob __all__ = ('BlobFilter', 'BaseIndexEntry', 'IndexEntry') #{ Invariants diff --git a/git/objects/__init__.py b/git/objects/__init__.py index 70fc52cb..ee642876 100644 --- a/git/objects/__init__.py +++ b/git/objects/__init__.py @@ -7,9 +7,10 @@ import inspect from .base import * # Fix import dependency - add IndexObject to the util module, so that it can be # imported by the submodule.base -from .submodule import util -util.IndexObject = IndexObject -util.Object = Object +from .submodule import util as smutil +smutil.IndexObject = IndexObject +smutil.Object = Object +del(smutil) from .submodule.base import * from .submodule.root import * diff --git a/git/objects/base.py b/git/objects/base.py index 20147e57..1f0d5752 100644 --- a/git/objects/base.py +++ b/git/objects/base.py @@ -3,8 +3,8 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php +from .util import get_object_type_by_name from git.util import LazyMixin, join_path_native, stream_copy -from util import get_object_type_by_name from gitdb.util import ( bin_to_hex, basename diff --git a/git/objects/blob.py b/git/objects/blob.py index b05e5b84..322f6992 100644 --- a/git/objects/blob.py +++ b/git/objects/blob.py @@ -3,9 +3,8 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - from mimetypes import guess_type -import base +from . import base __all__ = ('Blob', ) diff --git a/git/objects/commit.py b/git/objects/commit.py index 9c733695..5b6b9a33 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -4,6 +4,8 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php +from gitdb import IStream +from gitdb.util import hex_to_bin from git.util import ( Actor, Iterable, @@ -11,26 +13,23 @@ from git.util import ( finalize_process ) from git.diff import Diffable -from tree import Tree -from gitdb import IStream -from cStringIO import StringIO -import base -from gitdb.util import ( - hex_to_bin -) -from util import ( +from .tree import Tree +from . import base +from .util import ( Traversable, Serializable, parse_date, altz_to_utctz_str, parse_actor_and_date ) + from time import ( time, altzone ) import os +from io import StringIO import logging log = logging.getLogger('git.objects.commit') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index d6f8982b..5ccebd4c 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -1,5 +1,5 @@ -import util -from util import ( +from . import util +from .util import ( mkhead, sm_name, sm_section, @@ -8,7 +8,7 @@ from util import ( find_first_remote_branch ) from git.objects.util import Traversable -from StringIO import StringIO # need a dict to set bloody .name field +from io import StringIO # need a dict to set bloody .name field from git.util import ( Iterable, join_path_native, diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index 708749c7..8c9afff1 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -1,5 +1,8 @@ -from base import Submodule, UpdateProgress -from util import ( +from .base import ( + Submodule, + UpdateProgress +) +from .util import ( find_first_remote_branch ) from git.exc import InvalidGitRepositoryError diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 01bd03b3..cb84ccb1 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -1,7 +1,7 @@ import git from git.exc import InvalidGitRepositoryError from git.config import GitConfigParser -from StringIO import StringIO +from io import StringIO import weakref __all__ = ('sm_section', 'sm_name', 'mkhead', 'unbare_repo', 'find_first_remote_branch', diff --git a/git/objects/tag.py b/git/objects/tag.py index 3c379579..5e76e230 100644 --- a/git/objects/tag.py +++ b/git/objects/tag.py @@ -4,12 +4,12 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php """ Module containing all object based types. """ -import base -from gitdb.util import hex_to_bin -from util import ( +from . import base +from .util import ( get_object_type_by_name, parse_actor_and_date ) +from gitdb.util import hex_to_bin __all__ = ("TagObject", ) diff --git a/git/objects/tree.py b/git/objects/tree.py index c77e6056..a216322b 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -3,22 +3,20 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import util -from base import IndexObject from git.util import join_path -from blob import Blob -from submodule.base import Submodule import git.diff as diff +from gitdb.util import to_bin_sha -from fun import ( +from . import util +from .base import IndexObject +from .blob import Blob +from .submodule.base import Submodule + +from .fun import ( tree_entries_from_data, tree_to_stream ) -from gitdb.util import ( - to_bin_sha, -) - __all__ = ("TreeModifier", "Tree") diff --git a/git/refs/head.py b/git/refs/head.py index 25c994a3..0a14158c 100644 --- a/git/refs/head.py +++ b/git/refs/head.py @@ -1,12 +1,10 @@ -from symbolic import SymbolicReference -from reference import Reference - from git.config import SectionConstraint - from git.util import join_path - from git.exc import GitCommandError +from .symbolic import SymbolicReference +from .reference import Reference + __all__ = ["HEAD", "Head"] diff --git a/git/refs/reference.py b/git/refs/reference.py index b07ac0cd..8741ebb9 100644 --- a/git/refs/reference.py +++ b/git/refs/reference.py @@ -1,8 +1,9 @@ -from symbolic import SymbolicReference from git.util import ( LazyMixin, Iterable, ) +from .symbolic import SymbolicReference + __all__ = ["Reference"] diff --git a/git/refs/remote.py b/git/refs/remote.py index e3827ad9..b692e6df 100644 --- a/git/refs/remote.py +++ b/git/refs/remote.py @@ -1,7 +1,8 @@ -from head import Head from git.util import join_path from gitdb.util import join +from .head import Head + import os diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index e0f5531a..0cd04e07 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -1,4 +1,5 @@ import os + from git.objects import Object, Commit from git.util import ( join_path, @@ -19,7 +20,7 @@ from gitdb.util import ( LockedFD ) -from log import RefLog +from .log import RefLog __all__ = ["SymbolicReference"] diff --git a/git/refs/tag.py b/git/refs/tag.py index 6509c891..3334e53c 100644 --- a/git/refs/tag.py +++ b/git/refs/tag.py @@ -1,4 +1,4 @@ -from reference import Reference +from .reference import Reference __all__ = ["TagReference", "Tag"] diff --git a/git/remote.py b/git/remote.py index 44b7ffaa..9ebc52fe 100644 --- a/git/remote.py +++ b/git/remote.py @@ -5,33 +5,34 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php # Module implementing a remote object allowing easy access to git remotes +import re +import os -from exc import GitCommandError -from ConfigParser import NoOptionError -from config import SectionConstraint - -from git.util import ( - LazyMixin, - Iterable, - IterableList, - RemoteProgress +from .exc import GitCommandError +from .config import ( + SectionConstraint, + cp, ) - -from refs import ( +from .refs import ( Reference, RemoteReference, SymbolicReference, TagReference ) + +from git.util import ( + LazyMixin, + Iterable, + IterableList, + RemoteProgress +) from git.util import ( join_path, finalize_process ) from gitdb.util import join -import re -import os __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') @@ -390,7 +391,7 @@ class Remote(LazyMixin, Iterable): # even though a slot of the same name exists try: return self._config_reader.get(attr) - except NoOptionError: + except cp.NoOptionError: return super(Remote, self).__getattr__(attr) # END handle exception diff --git a/git/repo/base.py b/git/repo/base.py index dcf98152..e5ae7623 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -40,7 +40,7 @@ from gitdb.util import ( hex_to_bin ) -from fun import ( +from .fun import ( rev_parse, is_git_dir, find_git_dir, diff --git a/git/repo/fun.py b/git/repo/fun.py index b8905517..d08e5fed 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -1,5 +1,7 @@ """Package with general repository related functions""" import os +from string import digits + from gitdb.exc import BadObject from git.refs import SymbolicReference from git.objects import Object @@ -11,7 +13,7 @@ from gitdb.util import ( hex_to_bin, bin_to_hex ) -from string import digits + __all__ = ('rev_parse', 'is_git_dir', 'touch', 'read_gitfile', 'find_git_dir', 'name_to_object', 'short_to_long', 'deref_tag', 'to_commit') diff --git a/git/test/lib/asserts.py b/git/test/lib/asserts.py index 0f2fd99a..60a888b3 100644 --- a/git/test/lib/asserts.py +++ b/git/test/lib/asserts.py @@ -7,13 +7,6 @@ import re import stat -__all__ = ['assert_instance_of', 'assert_not_instance_of', - 'assert_none', 'assert_not_none', - 'assert_match', 'assert_not_match', 'assert_mode_644', - 'assert_mode_755', - 'assert_equal', 'assert_not_equal', 'assert_raises', 'patch', 'raises', - 'assert_true', 'assert_false'] - from nose.tools import ( assert_equal, assert_not_equal, @@ -23,9 +16,14 @@ from nose.tools import ( assert_false ) -from mock import ( - patch -) +from mock import patch + +__all__ = ['assert_instance_of', 'assert_not_instance_of', + 'assert_none', 'assert_not_none', + 'assert_match', 'assert_not_match', 'assert_mode_644', + 'assert_mode_755', + 'assert_equal', 'assert_not_equal', 'assert_raises', 'patch', 'raises', + 'assert_true', 'assert_false'] def assert_instance_of(expected, actual, msg=None): diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index 9c935ce0..0ea4fc7e 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -11,7 +11,7 @@ from unittest import TestCase import time import tempfile import shutil -import cStringIO +import io GIT_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) @@ -46,8 +46,8 @@ class StringProcessAdapter(object): Its tailored to work with the test system only""" def __init__(self, input_string): - self.stdout = cStringIO.StringIO(input_string) - self.stderr = cStringIO.StringIO() + self.stdout = io.StringIO(input_string) + self.stderr = io.StringIO() def wait(self): return 0 diff --git a/git/test/performance/test_commit.py b/git/test/performance/test_commit.py index a890c833..fed6ef18 100644 --- a/git/test/performance/test_commit.py +++ b/git/test/performance/test_commit.py @@ -8,7 +8,7 @@ from .lib import TestBigRepoRW from git import Commit from gitdb import IStream from git.test.test_commit import assert_commit_serialization -from cStringIO import StringIO +from io import StringIO from time import time import sys diff --git a/git/test/test_commit.py b/git/test/test_commit.py index bfad6fd6..84f81f21 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -21,7 +21,7 @@ from git import ( from gitdb import IStream from gitdb.util import hex_to_bin -from cStringIO import StringIO +from io import StringIO import time import sys import re diff --git a/git/test/test_fun.py b/git/test/test_fun.py index bf178aaa..4093065d 100644 --- a/git/test/test_fun.py +++ b/git/test/test_fun.py @@ -24,7 +24,7 @@ from stat import ( ) from git.index import IndexFile -from cStringIO import StringIO +from io import StringIO class TestFun(TestBase): diff --git a/git/test/test_index.py b/git/test/test_index.py index 15fff8d4..74bdac53 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -30,7 +30,7 @@ from stat import ( ST_MODE ) -from StringIO import StringIO +from io import StringIO from gitdb.base import IStream from git.objects import Blob from git.index.typ import ( diff --git a/git/test/test_repo.py b/git/test/test_repo.py index f6b46a6e..41c1c8f1 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -35,7 +35,7 @@ import os import sys import tempfile import shutil -from cStringIO import StringIO +from io import StringIO class TestRepo(TestBase): diff --git a/git/test/test_tree.py b/git/test/test_tree.py index d2e3606b..3b89abee 100644 --- a/git/test/test_tree.py +++ b/git/test/test_tree.py @@ -11,7 +11,7 @@ from git import ( Blob ) -from cStringIO import StringIO +from io import StringIO class TestTree(TestBase): diff --git a/git/util.py b/git/util.py index fecd9fa2..b3a22883 100644 --- a/git/util.py +++ b/git/util.py @@ -15,7 +15,8 @@ import getpass # NOTE: Some of the unused imports might be used/imported by others. # Handle once test-cases are back up and running. -from exc import GitCommandError +from .exc import GitCommandError +from .compat import MAXSIZE # Most of these are unused here, but are for use by git-python modules so these # don't see gitdb all the time. Flake of course doesn't like it. @@ -548,7 +549,7 @@ class BlockingLockFile(LockFile): can never be obtained.""" __slots__ = ("_check_interval", "_max_block_time") - def __init__(self, file_path, check_interval_s=0.3, max_block_time_s=sys.maxint): + def __init__(self, file_path, check_interval_s=0.3, max_block_time_s=MAXSIZE): """Configure the instance :parm check_interval_s: -- cgit v1.2.1 From ae2ff0f9d704dc776a1934f72a339da206a9fff4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 Jan 2015 19:50:28 +0100 Subject: Dum brute force conversion of all types. However, StringIO really is ByteIO in most cases, and py2.7 should run but doesn't. This should be made work first. --- git/cmd.py | 6 +++--- git/compat.py | 11 +++++++++-- git/config.py | 12 ++++++++---- git/index/base.py | 14 +++++++++----- git/objects/commit.py | 7 ++++--- git/objects/fun.py | 6 +++++- git/objects/submodule/base.py | 3 ++- git/objects/tree.py | 3 ++- git/refs/log.py | 6 +++++- git/refs/symbolic.py | 3 ++- git/repo/base.py | 9 +++++---- git/repo/fun.py | 1 + git/test/lib/helper.py | 8 +++++--- git/test/performance/test_commit.py | 9 ++++++--- git/test/test_commit.py | 12 ++++++++---- git/test/test_config.py | 3 ++- git/test/test_index.py | 3 ++- git/test/test_remote.py | 5 +++-- git/test/test_repo.py | 3 ++- git/test/test_submodule.py | 3 ++- git/test/test_util.py | 3 ++- 21 files changed, 87 insertions(+), 43 deletions(-) (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index b9e4bc09..aa5e3a25 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -19,7 +19,7 @@ from .util import ( stream_copy ) from .exc import GitCommandError - +from git.compat import text_type execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', @@ -435,7 +435,7 @@ class Git(LazyMixin): @classmethod def __unpack_args(cls, arg_list): if not isinstance(arg_list, (list, tuple)): - if isinstance(arg_list, unicode): + if isinstance(arg_list, text_type): return [arg_list.encode('utf-8')] return [str(arg_list)] @@ -443,7 +443,7 @@ class Git(LazyMixin): for arg in arg_list: if isinstance(arg_list, (list, tuple)): outlist.extend(cls.__unpack_args(arg)) - elif isinstance(arg_list, unicode): + elif isinstance(arg_list, text_type): outlist.append(arg_list.encode('utf-8')) # END recursion else: diff --git a/git/compat.py b/git/compat.py index 52fc599c..611005a1 100644 --- a/git/compat.py +++ b/git/compat.py @@ -5,15 +5,22 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php """utilities to help provide compatibility with python 3""" +# flake8: noqa -from gitdb.utils.compat import ( # noqa +from gitdb.utils.compat import ( PY3, xrange, MAXSIZE, izip, ) -from gitdb.utils.encoding import ( # noqa +from gitdb.utils.encoding import ( string_types, text_type ) + +if PY3: + import io + FileType = io.IOBase +else: + FileType = file diff --git a/git/config.py b/git/config.py index 685dbed8..34fe290b 100644 --- a/git/config.py +++ b/git/config.py @@ -17,6 +17,10 @@ import logging from git.odict import OrderedDict from git.util import LockFile +from git.compat import ( + string_types, + FileType +) __all__ = ('GitConfigParser', 'SectionConstraint') @@ -175,7 +179,7 @@ class GitConfigParser(cp.RawConfigParser, object): "Write-ConfigParsers can operate on a single file only, multiple files have been passed") # END single file check - if not isinstance(file_or_files, basestring): + if not isinstance(file_or_files, string_types): file_or_files = file_or_files.name # END get filename from handle/stream # initialize lock base - we want to write @@ -333,7 +337,7 @@ class GitConfigParser(cp.RawConfigParser, object): close_fp = False # we have a physical file on disk, so get a lock - if isinstance(fp, (basestring, file)): + if isinstance(fp, string_types + (FileType, )): self._lock._obtain_lock() # END get lock for physical files @@ -391,7 +395,7 @@ class GitConfigParser(cp.RawConfigParser, object): return default raise - types = (long, float) + types = (int, float) for numtype in types: try: val = numtype(valuestr) @@ -412,7 +416,7 @@ class GitConfigParser(cp.RawConfigParser, object): if vl == 'true': return True - if not isinstance(valuestr, basestring): + if not isinstance(valuestr, string_types): raise TypeError("Invalid value type: only int, long, float and str are allowed", valuestr) return valuestr diff --git a/git/index/base.py b/git/index/base.py index 91dcea42..25ac121c 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -39,7 +39,11 @@ from git.objects import ( ) from git.objects.util import Serializable -from git.compat import izip +from git.compat import ( + izip, + xrange, + string_types, +) from git.util import ( LazyMixin, @@ -541,7 +545,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): entries = list() for item in items: - if isinstance(item, basestring): + if isinstance(item, string_types): paths.append(self._to_relative_path(item)) elif isinstance(item, (Blob, Submodule)): entries.append(BaseIndexEntry.from_blob(item)) @@ -752,7 +756,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): for item in items: if isinstance(item, (BaseIndexEntry, (Blob, Submodule))): paths.append(self._to_relative_path(item.path)) - elif isinstance(item, basestring): + elif isinstance(item, string_types): paths.append(self._to_relative_path(item)) else: raise TypeError("Invalid item type: %r" % item) @@ -1004,7 +1008,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): handle_stderr(proc, rval_iter) return rval_iter else: - if isinstance(paths, basestring): + if isinstance(paths, string_types): paths = [paths] # make sure we have our entries loaded before we start checkout_index @@ -1140,7 +1144,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): # index against anything but None is a reverse diff with the respective # item. Handle existing -R flags properly. Transform strings to the object # so that we can call diff on it - if isinstance(other, basestring): + if isinstance(other, string_types): other = self.repo.rev_parse(other) # END object conversion diff --git a/git/objects/commit.py b/git/objects/commit.py index 5b6b9a33..c9d7ddc8 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -23,6 +23,7 @@ from .util import ( altz_to_utctz_str, parse_actor_and_date ) +from git.compat import text_type from time import ( time, @@ -378,7 +379,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): a = self.author aname = a.name - if isinstance(aname, unicode): + if isinstance(aname, text_type): aname = aname.encode(self.encoding) # END handle unicode in name @@ -390,7 +391,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): # encode committer aname = c.name - if isinstance(aname, unicode): + if isinstance(aname, text_type): aname = aname.encode(self.encoding) # END handle unicode in name write(fmt % ("committer", aname, c.email, @@ -408,7 +409,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): write("\n") # write plain bytes, be sure its encoded according to our encoding - if isinstance(self.message, unicode): + if isinstance(self.message, text_type): write(self.message.encode(self.encoding)) else: write(self.message) diff --git a/git/objects/fun.py b/git/objects/fun.py index 416a52e6..db2ec7c2 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -1,5 +1,9 @@ """Module with functions which are supposed to be as fast as possible""" from stat import S_ISDIR +from git.compat import ( + xrange, + text_type +) __all__ = ('tree_to_stream', 'tree_entries_from_data', 'traverse_trees_recursive', 'traverse_tree_recursive') @@ -28,7 +32,7 @@ def tree_to_stream(entries, write): # hence we must convert to an utf8 string for it to work properly. # According to my tests, this is exactly what git does, that is it just # takes the input literally, which appears to be utf8 on linux. - if isinstance(name, unicode): + if isinstance(name, text_type): name = name.encode("utf8") write("%s %s\0%s" % (mode_str, name, binsha)) # END for each item diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 5ccebd4c..69bf748a 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -22,6 +22,7 @@ from git.exc import ( InvalidGitRepositoryError, NoSuchPathError ) +from git.compat import string_types import stat import git @@ -93,7 +94,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): if url is not None: self._url = url if branch_path is not None: - assert isinstance(branch_path, basestring) + assert isinstance(branch_path, string_types) self._branch_path = branch_path if name is not None: self._name = name diff --git a/git/objects/tree.py b/git/objects/tree.py index a216322b..6776a15e 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -11,6 +11,7 @@ from . import util from .base import IndexObject from .blob import Blob from .submodule.base import Submodule +from git.compat import string_types from .fun import ( tree_entries_from_data, @@ -232,7 +233,7 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): info = self._cache[item] return self._map_id_to_type[info[1] >> 12](self.repo, info[0], info[1], join_path(self.path, info[2])) - if isinstance(item, basestring): + if isinstance(item, string_types): # compatability return self.__div__(item) # END index is basestring diff --git a/git/refs/log.py b/git/refs/log.py index e3f3363c..94e07104 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -17,6 +17,10 @@ from git.objects.util import ( Serializable, altz_to_utctz_str, ) +from git.compat import ( + xrange, + string_types +) import time import re @@ -170,7 +174,7 @@ class RefLog(list, Serializable): :param stream: file-like object containing the revlog in its native format or basestring instance pointing to a file to read""" new_entry = RefLogEntry.from_line - if isinstance(stream, basestring): + if isinstance(stream, string_types): stream = file_contents_ro_filepath(stream) # END handle stream type while True: diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 0cd04e07..624b1a09 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -19,6 +19,7 @@ from gitdb.util import ( hex_to_bin, LockedFD ) +from git.compat import string_types from .log import RefLog @@ -274,7 +275,7 @@ class SymbolicReference(object): elif isinstance(ref, Object): obj = ref write_value = ref.hexsha - elif isinstance(ref, basestring): + elif isinstance(ref, string_types): try: obj = self.repo.rev_parse(ref + "^{}") # optionally deref tags write_value = obj.hexsha diff --git a/git/repo/base.py b/git/repo/base.py index e5ae7623..f92a85ce 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -47,6 +47,7 @@ from .fun import ( read_gitfile, touch, ) +from git.compat import text_type import os import sys @@ -176,11 +177,11 @@ class Repo(object): # Description property def _get_description(self): filename = join(self.git_dir, 'description') - return file(filename).read().rstrip() + return open(filename).read().rstrip() def _set_description(self, descr): filename = join(self.git_dir, 'description') - file(filename, 'w').write(descr + '\n') + open(filename, 'w').write(descr + '\n') description = property(_get_description, _set_description, doc="the project's description") @@ -389,7 +390,7 @@ class Repo(object): if rev is None: return self.head.commit else: - return self.rev_parse(unicode(rev) + "^0") + return self.rev_parse(text_type(rev) + "^0") def iter_trees(self, *args, **kwargs): """:return: Iterator yielding Tree objects @@ -412,7 +413,7 @@ class Repo(object): if rev is None: return self.head.commit.tree else: - return self.rev_parse(unicode(rev) + "^{tree}") + return self.rev_parse(text_type(rev) + "^{tree}") def iter_commits(self, rev=None, paths='', **kwargs): """A list of Commit objects representing the history of a given ref/commit diff --git a/git/repo/fun.py b/git/repo/fun.py index d08e5fed..64b9b4a9 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -13,6 +13,7 @@ from gitdb.util import ( hex_to_bin, bin_to_hex ) +from git.compat import xrange __all__ = ('rev_parse', 'is_git_dir', 'touch', 'read_gitfile', 'find_git_dir', 'name_to_object', diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index 0ea4fc7e..43079dbe 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -6,13 +6,15 @@ from __future__ import print_function import os import sys -from git import Repo, Remote, GitCommandError, Git from unittest import TestCase import time import tempfile import shutil import io +from git import Repo, Remote, GitCommandError, Git +from git.compat import string_types + GIT_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) __all__ = ( @@ -89,7 +91,7 @@ def with_rw_repo(working_tree_ref, bare=False): To make working with relative paths easier, the cwd will be set to the working dir of the repository. """ - assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout" + assert isinstance(working_tree_ref, string_types), "Decorator requires ref name for working tree checkout" def argument_passer(func): def repo_creator(self): @@ -152,7 +154,7 @@ def with_rw_and_rw_remote_repo(working_tree_ref): See working dir info in with_rw_repo :note: We attempt to launch our own invocation of git-daemon, which will be shutdown at the end of the test. """ - assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout" + assert isinstance(working_tree_ref, string_types), "Decorator requires ref name for working tree checkout" def argument_passer(func): def remote_repo_creator(self): diff --git a/git/test/performance/test_commit.py b/git/test/performance/test_commit.py index fed6ef18..a55b6d43 100644 --- a/git/test/performance/test_commit.py +++ b/git/test/performance/test_commit.py @@ -4,13 +4,16 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php from __future__ import print_function +from io import StringIO +from time import time +import sys + from .lib import TestBigRepoRW from git import Commit from gitdb import IStream +from git.compat import xrange from git.test.test_commit import assert_commit_serialization -from io import StringIO -from time import time -import sys + class TestPerformance(TestBigRepoRW): diff --git a/git/test/test_commit.py b/git/test/test_commit.py index 84f81f21..adf1cb10 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -20,6 +20,10 @@ from git import ( ) from gitdb import IStream from gitdb.util import hex_to_bin +from git.compat import ( + string_types, + text_type +) from io import StringIO import time @@ -129,7 +133,7 @@ class TestCommit(TestBase): assert len(name) == 9 special = Actor._from_string(u"%s " % name) assert special.name == name - assert isinstance(special.name, unicode) + assert isinstance(special.name, text_type) def test_traversal(self): start = self.rorepo.commit("a4d06724202afccd2b5c54f81bcf2bf26dea7fff") @@ -250,7 +254,7 @@ class TestCommit(TestBase): def test_base(self): name_rev = self.rorepo.head.commit.name_rev - assert isinstance(name_rev, basestring) + assert isinstance(name_rev, string_types) @with_rw_repo('HEAD', bare=True) def test_serialization(self, rwrepo): @@ -263,8 +267,8 @@ class TestCommit(TestBase): # create a commit with unicode in the message, and the author's name # Verify its serialization and deserialization cmt = self.rorepo.commit('0.1.6') - assert isinstance(cmt.message, unicode) # it automatically decodes it as such - assert isinstance(cmt.author.name, unicode) # same here + assert isinstance(cmt.message, text_type) # it automatically decodes it as such + assert isinstance(cmt.author.name, text_type) # same here cmt.message = "üäêèß".decode("utf-8") assert len(cmt.message) == 5 diff --git a/git/test/test_config.py b/git/test/test_config.py index d1c8e72f..ef0707e9 100644 --- a/git/test/test_config.py +++ b/git/test/test_config.py @@ -11,6 +11,7 @@ from git.test.lib import ( from git import ( GitConfigParser ) +from git.compat import string_types import StringIO from copy import copy from ConfigParser import NoSectionError @@ -85,7 +86,7 @@ class TestBase(TestCase): num_options += 1 val = r_config.get(section, option) val_typed = r_config.get_value(section, option) - assert isinstance(val_typed, (bool, long, float, basestring)) + assert isinstance(val_typed, (bool, int, float, ) + string_types) assert val assert "\n" not in option assert "\n" not in val diff --git a/git/test/test_index.py b/git/test/test_index.py index 74bdac53..70d70a05 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -20,6 +20,7 @@ from git import ( GitCommandError, CheckoutError, ) +from git.compat import string_types from gitdb.util import hex_to_bin import os import sys @@ -343,7 +344,7 @@ class TestIndex(TestBase): index.checkout(test_file) except CheckoutError as e: assert len(e.failed_files) == 1 and e.failed_files[0] == os.path.basename(test_file) - assert (len(e.failed_files) == len(e.failed_reasons)) and isinstance(e.failed_reasons[0], basestring) + assert (len(e.failed_files) == len(e.failed_reasons)) and isinstance(e.failed_reasons[0], string_types) assert len(e.valid_files) == 0 assert open(test_file).read().endswith(append_data) else: diff --git a/git/test/test_remote.py b/git/test/test_remote.py index a8d5179a..75dc19c5 100644 --- a/git/test/test_remote.py +++ b/git/test/test_remote.py @@ -23,6 +23,7 @@ from git import ( GitCommandError ) from git.util import IterableList +from git.compat import string_types import tempfile import shutil import os @@ -97,7 +98,7 @@ class TestRemote(TestBase): # self._print_fetchhead(remote.repo) assert len(results) > 0 and isinstance(results[0], FetchInfo) for info in results: - assert isinstance(info.note, basestring) + assert isinstance(info.note, string_types) if isinstance(info.ref, Reference): assert info.flags != 0 # END reference type flags handling @@ -113,7 +114,7 @@ class TestRemote(TestBase): assert len(results) > 0 and isinstance(results[0], PushInfo) for info in results: assert info.flags - assert isinstance(info.summary, basestring) + assert isinstance(info.summary, string_types) if info.old_commit is not None: assert isinstance(info.old_commit, Commit) if info.flags & info.ERROR: diff --git a/git/test/test_repo.py b/git/test/test_repo.py index 41c1c8f1..f33fe467 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -30,6 +30,7 @@ from git import ( from git.util import join_path_native from git.exc import BadObject from gitdb.util import bin_to_hex +from git.compat import string_types import os import sys @@ -286,7 +287,7 @@ class TestRepo(TestBase): # test the 'lines per commit' entries tlist = b[0][1] assert_true(tlist) - assert_true(isinstance(tlist[0], basestring)) + assert_true(isinstance(tlist[0], string_types)) assert_true(len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug def test_blame_real(self): diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index ec3459e4..8c1580fe 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -9,6 +9,7 @@ from git.exc import InvalidGitRepositoryError from git.objects.submodule.base import Submodule from git.objects.submodule.root import RootModule, RootUpdateProgress from git.util import to_native_path_linux, join_path_native +from git.compat import string_types import shutil import git import sys @@ -76,7 +77,7 @@ class TestSubmodule(TestBase): self.failUnlessRaises(InvalidGitRepositoryError, getattr, sm, 'branch') # branch_path works, as its just a string - assert isinstance(sm.branch_path, basestring) + assert isinstance(sm.branch_path, string_types) # some commits earlier we still have a submodule, but its at a different commit smold = Submodule.iter_items(rwrepo, self.k_subm_changed).next() diff --git a/git/test/test_util.py b/git/test/test_util.py index 888eb4ee..c6ca6920 100644 --- a/git/test/test_util.py +++ b/git/test/test_util.py @@ -24,6 +24,7 @@ from git.objects.util import ( parse_date, ) from git.cmd import dashify +from git.compat import string_types import time @@ -104,7 +105,7 @@ class TestUtils(TestBase): # now that we are here, test our conversion functions as well utctz = altz_to_utctz_str(offset) - assert isinstance(utctz, basestring) + assert isinstance(utctz, string_types) assert utctz_to_altz(verify_utctz(utctz)) == offset # END assert rval utility -- cgit v1.2.1 From bc8c91200a7fb2140aadd283c66b5ab82f9ad61e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 10:09:51 +0100 Subject: Fixed io types to make tests work on PY2 once again. Now it's about going through PY3 issues --- git/compat.py | 3 ++- git/index/base.py | 6 ++++-- git/index/fun.py | 4 ++-- git/objects/commit.py | 6 +++--- git/objects/submodule/base.py | 6 +++--- git/objects/submodule/util.py | 4 ++-- git/refs/log.py | 2 +- git/test/fixtures/git_config_global | 1 + git/test/lib/helper.py | 4 ++-- git/test/performance/test_commit.py | 4 ++-- git/test/test_commit.py | 12 ++++++------ git/test/test_config.py | 4 ++-- git/test/test_fun.py | 4 ++-- git/test/test_index.py | 6 +++--- git/test/test_repo.py | 14 +++++++------- git/test/test_tree.py | 6 +++--- 16 files changed, 45 insertions(+), 41 deletions(-) (limited to 'git') diff --git a/git/compat.py b/git/compat.py index 611005a1..a95c5667 100644 --- a/git/compat.py +++ b/git/compat.py @@ -16,7 +16,8 @@ from gitdb.utils.compat import ( from gitdb.utils.encoding import ( string_types, - text_type + text_type, + force_bytes ) if PY3: diff --git a/git/index/base.py b/git/index/base.py index 25ac121c..a994e7b6 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -8,7 +8,7 @@ import os import sys import subprocess import glob -from io import StringIO +from io import BytesIO from stat import S_ISLNK @@ -43,6 +43,7 @@ from git.compat import ( izip, xrange, string_types, + force_bytes ) from git.util import ( @@ -562,7 +563,8 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): st = os.lstat(filepath) # handles non-symlinks as well stream = None if S_ISLNK(st.st_mode): - stream = StringIO(os.readlink(filepath)) + # in PY3, readlink is string, but we need bytes. In PY2, it's just OS encoded bytes, we assume UTF-8 + stream = BytesIO(force_bytes(os.readlink(filepath), encoding='utf-8')) else: stream = open(filepath, 'rb') # END handle stream diff --git a/git/index/fun.py b/git/index/fun.py index 004f992e..0e49ae8d 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -12,7 +12,7 @@ from stat import ( S_IFGITLINK = S_IFLNK | S_IFDIR # a submodule -from io import StringIO +from io import BytesIO from git.util import IndexFileSHA1Writer from git.exc import UnmergedEntriesError @@ -218,7 +218,7 @@ def write_tree_from_cache(entries, odb, sl, si=0): # END for each entry # finally create the tree - sio = StringIO() + sio = BytesIO() tree_to_stream(tree_items, sio.write) sio.seek(0) diff --git a/git/objects/commit.py b/git/objects/commit.py index c9d7ddc8..79d460ad 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -30,7 +30,7 @@ from time import ( altzone ) import os -from io import StringIO +from io import BytesIO import logging log = logging.getLogger('git.objects.commit') @@ -133,7 +133,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): if attr in Commit.__slots__: # read the data in a chunk, its faster - then provide a file wrapper binsha, typename, self.size, stream = self.repo.odb.stream(self.binsha) - self._deserialize(StringIO(stream.read())) + self._deserialize(BytesIO(stream.read())) else: super(Commit, self)._set_cache_(attr) # END handle attrs @@ -345,7 +345,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): committer, committer_time, committer_offset, message, parent_commits, conf_encoding) - stream = StringIO() + stream = BytesIO() new_commit._serialize(stream) streamlen = stream.tell() stream.seek(0) diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 69bf748a..0fb3f35f 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -8,7 +8,7 @@ from .util import ( find_first_remote_branch ) from git.objects.util import Traversable -from io import StringIO # need a dict to set bloody .name field +from io import BytesIO # need a dict to set bloody .name field from git.util import ( Iterable, join_path_native, @@ -187,8 +187,8 @@ class Submodule(util.IndexObject, Iterable, Traversable): @classmethod def _sio_modules(cls, parent_commit): - """:return: Configuration file as StringIO - we only access it through the respective blob's data""" - sio = StringIO(parent_commit.tree[cls.k_modules_file].data_stream.read()) + """:return: Configuration file as BytesIO - we only access it through the respective blob's data""" + sio = BytesIO(parent_commit.tree[cls.k_modules_file].data_stream.read()) sio.name = cls.k_modules_file return sio diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index cb84ccb1..5604dec7 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -1,7 +1,7 @@ import git from git.exc import InvalidGitRepositoryError from git.config import GitConfigParser -from io import StringIO +from io import BytesIO import weakref __all__ = ('sm_section', 'sm_name', 'mkhead', 'unbare_repo', 'find_first_remote_branch', @@ -83,7 +83,7 @@ class SubmoduleConfigParser(GitConfigParser): """Flush changes in our configuration file to the index""" assert self._smref is not None # should always have a file here - assert not isinstance(self._file_or_files, StringIO) + assert not isinstance(self._file_or_files, BytesIO) sm = self._smref() if sm is not None: diff --git a/git/refs/log.py b/git/refs/log.py index 94e07104..f397548e 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -85,7 +85,7 @@ class RefLogEntry(tuple): :param line: line without trailing newline :raise ValueError: If line could not be parsed""" try: - info, msg = line.split('\t', 2) + info, msg = line.split('\t', 1) except ValueError: raise ValueError("line is missing tab separator") # END handle first plit diff --git a/git/test/fixtures/git_config_global b/git/test/fixtures/git_config_global index 1a55397f..56fbd3b3 100644 --- a/git/test/fixtures/git_config_global +++ b/git/test/fixtures/git_config_global @@ -1,3 +1,4 @@ +# just a comment [alias] st = status ci = commit diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index 43079dbe..bc9c351a 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -48,8 +48,8 @@ class StringProcessAdapter(object): Its tailored to work with the test system only""" def __init__(self, input_string): - self.stdout = io.StringIO(input_string) - self.stderr = io.StringIO() + self.stdout = io.BytesIO(input_string) + self.stderr = io.BytesIO() def wait(self): return 0 diff --git a/git/test/performance/test_commit.py b/git/test/performance/test_commit.py index a55b6d43..9e8c1325 100644 --- a/git/test/performance/test_commit.py +++ b/git/test/performance/test_commit.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php from __future__ import print_function -from io import StringIO +from io import BytesIO from time import time import sys @@ -93,7 +93,7 @@ class TestPerformance(TestBigRepoRW): hc.committer, hc.committed_date, hc.committer_tz_offset, str(i), parents=hc.parents, encoding=hc.encoding) - stream = StringIO() + stream = BytesIO() cm._serialize(stream) slen = stream.tell() stream.seek(0) diff --git a/git/test/test_commit.py b/git/test/test_commit.py index adf1cb10..5f45e59d 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -25,7 +25,7 @@ from git.compat import ( text_type ) -from io import StringIO +from io import BytesIO import time import sys import re @@ -44,7 +44,7 @@ def assert_commit_serialization(rwrepo, commit_id, print_performance_info=False) # assert that we deserialize commits correctly, hence we get the same # sha on serialization - stream = StringIO() + stream = BytesIO() cm._serialize(stream) ns += 1 streamlen = stream.tell() @@ -59,7 +59,7 @@ def assert_commit_serialization(rwrepo, commit_id, print_performance_info=False) cm.message, cm.parents, cm.encoding) assert nc.parents == cm.parents - stream = StringIO() + stream = BytesIO() nc._serialize(stream) ns += 1 streamlen = stream.tell() @@ -276,7 +276,7 @@ class TestCommit(TestBase): cmt.author.name = "äüß".decode("utf-8") assert len(cmt.author.name) == 3 - cstream = StringIO() + cstream = BytesIO() cmt._serialize(cstream) cstream.seek(0) assert len(cstream.getvalue()) @@ -316,7 +316,7 @@ JzJMZDRLQLFvnzqZuCjE cmt.gpgsig = "" assert cmt.gpgsig != fixture_sig - cstream = StringIO() + cstream = BytesIO() cmt._serialize(cstream) assert re.search(r"^gpgsig $", cstream.getvalue(), re.MULTILINE) @@ -326,6 +326,6 @@ JzJMZDRLQLFvnzqZuCjE assert cmt.gpgsig == "" cmt.gpgsig = None - cstream = StringIO() + cstream = BytesIO() cmt._serialize(cstream) assert not re.search(r"^gpgsig ", cstream.getvalue(), re.MULTILINE) diff --git a/git/test/test_config.py b/git/test/test_config.py index ef0707e9..0301c54f 100644 --- a/git/test/test_config.py +++ b/git/test/test_config.py @@ -12,7 +12,7 @@ from git import ( GitConfigParser ) from git.compat import string_types -import StringIO +import io from copy import copy from ConfigParser import NoSectionError @@ -21,7 +21,7 @@ class TestBase(TestCase): def _to_memcache(self, file_path): fp = open(file_path, "r") - sio = StringIO.StringIO(fp.read()) + sio = io.BytesIO(fp.read()) sio.name = file_path return sio diff --git a/git/test/test_fun.py b/git/test/test_fun.py index 4093065d..9d3e749e 100644 --- a/git/test/test_fun.py +++ b/git/test/test_fun.py @@ -24,7 +24,7 @@ from stat import ( ) from git.index import IndexFile -from io import StringIO +from io import BytesIO class TestFun(TestBase): @@ -72,7 +72,7 @@ class TestFun(TestBase): def mktree(self, odb, entries): """create a tree from the given tree entries and safe it to the database""" - sio = StringIO() + sio = BytesIO() tree_to_stream(entries, sio.write) sio.seek(0) istream = odb.store(IStream(str_tree_type, len(sio.getvalue()), sio)) diff --git a/git/test/test_index.py b/git/test/test_index.py index 70d70a05..38cc3563 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -31,7 +31,7 @@ from stat import ( ST_MODE ) -from io import StringIO +from io import BytesIO from gitdb.base import IStream from git.objects import Blob from git.index.typ import ( @@ -698,9 +698,9 @@ class TestIndex(TestBase): # instead of throwing the Exception we are expecting. This is # a quick hack to make this test fail when expected. rw_bare_repo._working_tree_dir = None - contents = 'This is a StringIO file' + contents = b'This is a BytesIO file' filesize = len(contents) - fileobj = StringIO(contents) + fileobj = BytesIO(contents) filename = 'my-imaginary-file' istream = rw_bare_repo.odb.store( IStream(Blob.type, filesize, fileobj)) diff --git a/git/test/test_repo.py b/git/test/test_repo.py index f33fe467..ae824086 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -36,7 +36,7 @@ import os import sys import tempfile import shutil -from io import StringIO +from io import BytesIO class TestRepo(TestBase): @@ -362,22 +362,22 @@ class TestRepo(TestBase): def test_git_cmd(self): # test CatFileContentStream, just to be very sure we have no fencepost errors # last \n is the terminating newline that it expects - l1 = "0123456789\n" - l2 = "abcdefghijklmnopqrstxy\n" - l3 = "z\n" - d = "%s%s%s\n" % (l1, l2, l3) + l1 = b"0123456789\n" + l2 = b"abcdefghijklmnopqrstxy\n" + l3 = b"z\n" + d = b"%s%s%s\n" % (l1, l2, l3) l1p = l1[:5] # full size # size is without terminating newline def mkfull(): - return Git.CatFileContentStream(len(d) - 1, StringIO(d)) + return Git.CatFileContentStream(len(d) - 1, BytesIO(d)) ts = 5 def mktiny(): - return Git.CatFileContentStream(ts, StringIO(d)) + return Git.CatFileContentStream(ts, BytesIO(d)) # readlines no limit s = mkfull() diff --git a/git/test/test_tree.py b/git/test/test_tree.py index 3b89abee..72bda485 100644 --- a/git/test/test_tree.py +++ b/git/test/test_tree.py @@ -11,7 +11,7 @@ from git import ( Blob ) -from io import StringIO +from io import BytesIO class TestTree(TestBase): @@ -30,7 +30,7 @@ class TestTree(TestBase): orig_data = tree.data_stream.read() orig_cache = tree._cache - stream = StringIO() + stream = BytesIO() tree._serialize(stream) assert stream.getvalue() == orig_data @@ -82,7 +82,7 @@ class TestTree(TestBase): mod.set_done() # multiple times are okay # serialize, its different now - stream = StringIO() + stream = BytesIO() testtree._serialize(stream) stream.seek(0) assert stream.getvalue() != orig_data -- cgit v1.2.1 From 04357d0d46fee938a618b64daed1716606e05ca5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 15:53:46 +0100 Subject: Intermediate commit: test_config and test_actor works Kind of tackling the tasks step by step, picking low-hanging fruit first, or the ones that everyone depends on --- git/cmd.py | 44 +++++++++++++++++++++++++++----------------- git/compat.py | 32 +++++++++++++++++++++++++++++++- git/config.py | 42 ++++++++++++++++++++++-------------------- git/objects/util.py | 16 ++++++++-------- git/refs/symbolic.py | 15 +++++++++------ git/remote.py | 11 ++++++----- git/test/test_config.py | 13 +++++++------ 7 files changed, 110 insertions(+), 63 deletions(-) (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index aa5e3a25..c536b43c 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -19,7 +19,11 @@ from .util import ( stream_copy ) from .exc import GitCommandError -from git.compat import text_type +from git.compat import ( + text_type, + string_types, + defenc +) execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', @@ -373,9 +377,9 @@ class Git(LazyMixin): if output_stream is None: stdout_value, stderr_value = proc.communicate() # strip trailing "\n" - if stdout_value.endswith("\n"): + if stdout_value.endswith(b"\n"): stdout_value = stdout_value[:-1] - if stderr_value.endswith("\n"): + if stderr_value.endswith(b"\n"): stderr_value = stderr_value[:-1] status = proc.returncode else: @@ -394,9 +398,9 @@ class Git(LazyMixin): if self.GIT_PYTHON_TRACE == 'full': cmdstr = " ".join(command) if stderr_value: - log.info("%s -> %d; stdout: '%s'; stderr: '%s'", cmdstr, status, stdout_value, stderr_value) + log.info("%s -> %d; stdout: '%s'; stderr: '%s'", cmdstr, status, stdout_value.decode(defenc), stderr_value.decode(defenc)) elif stdout_value: - log.info("%s -> %d; stdout: '%s'", cmdstr, status, stdout_value) + log.info("%s -> %d; stdout: '%s'", cmdstr, status, stdout_value.decode(defenc)) else: log.info("%s -> %d", cmdstr, status) # END handle debug printing @@ -436,7 +440,7 @@ class Git(LazyMixin): def __unpack_args(cls, arg_list): if not isinstance(arg_list, (list, tuple)): if isinstance(arg_list, text_type): - return [arg_list.encode('utf-8')] + return [arg_list.encode(defenc)] return [str(arg_list)] outlist = list() @@ -444,7 +448,7 @@ class Git(LazyMixin): if isinstance(arg_list, (list, tuple)): outlist.extend(cls.__unpack_args(arg)) elif isinstance(arg_list, text_type): - outlist.append(arg_list.encode('utf-8')) + outlist.append(arg_list.encode(defenc)) # END recursion else: outlist.append(str(arg)) @@ -569,14 +573,20 @@ class Git(LazyMixin): raise ValueError("Failed to parse header: %r" % header_line) return (tokens[0], tokens[1], int(tokens[2])) - def __prepare_ref(self, ref): - # required for command to separate refs on stdin - refstr = str(ref) # could be ref-object - if refstr.endswith("\n"): - return refstr - return refstr + "\n" + def _prepare_ref(self, ref): + # required for command to separate refs on stdin, as bytes + refstr = ref + if isinstance(ref, bytes): + # Assume 40 bytes hexsha - bin-to-ascii for some reason returns bytes, not text + refstr = ref.decode('ascii') + elif not isinstance(ref, string_types): + refstr = str(ref) # could be ref-object + + if not refstr.endswith("\n"): + refstr += "\n" + return refstr.encode(defenc) - def __get_persistent_cmd(self, attr_name, cmd_name, *args, **kwargs): + def _get_persistent_cmd(self, attr_name, cmd_name, *args, **kwargs): cur_val = getattr(self, attr_name) if cur_val is not None: return cur_val @@ -589,7 +599,7 @@ class Git(LazyMixin): return cmd def __get_object_header(self, cmd, ref): - cmd.stdin.write(self.__prepare_ref(ref)) + cmd.stdin.write(self._prepare_ref(ref)) cmd.stdin.flush() return self._parse_object_header(cmd.stdout.readline()) @@ -601,7 +611,7 @@ class Git(LazyMixin): once and reuses the command in subsequent calls. :return: (hexsha, type_string, size_as_int)""" - cmd = self.__get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) + cmd = self._get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) return self.__get_object_header(cmd, ref) def get_object_data(self, ref): @@ -618,7 +628,7 @@ class Git(LazyMixin): :return: (hexsha, type_string, size_as_int, stream) :note: This method is not threadsafe, you need one independent Command instance per thread to be safe !""" - cmd = self.__get_persistent_cmd("cat_file_all", "cat_file", batch=True) + cmd = self._get_persistent_cmd("cat_file_all", "cat_file", batch=True) hexsha, typename, size = self.__get_object_header(cmd, ref) return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout)) diff --git a/git/compat.py b/git/compat.py index a95c5667..4a892ad2 100644 --- a/git/compat.py +++ b/git/compat.py @@ -7,6 +7,8 @@ """utilities to help provide compatibility with python 3""" # flake8: noqa +import sys + from gitdb.utils.compat import ( PY3, xrange, @@ -17,11 +19,39 @@ from gitdb.utils.compat import ( from gitdb.utils.encoding import ( string_types, text_type, - force_bytes + force_bytes, + force_text ) +defenc = sys.getdefaultencoding() if PY3: import io FileType = io.IOBase else: FileType = file + # usually, this is just ascii, which might not enough for our encoding needs + # Unless it's set specifically, we override it to be utf-8 + if defenc == 'ascii': + defenc = 'utf-8' + + +def with_metaclass(meta, *bases): + """copied from https://github.com/Byron/bcore/blob/master/src/python/butility/future.py#L15""" + class metaclass(meta): + __call__ = type.__call__ + __init__ = type.__init__ + + def __new__(cls, name, nbases, d): + if nbases is None: + return type.__new__(cls, name, (), d) + # There may be clients who rely on this attribute to be set to a reasonable value, which is why + # we set the __metaclass__ attribute explicitly + if not PY3 and '___metaclass__' not in d: + d['__metaclass__'] = meta + # end + return meta(name, bases, d) + # end + # end metaclass + return metaclass(meta.__name__ + 'Helper', None, {}) + # end handle py2 + diff --git a/git/config.py b/git/config.py index 34fe290b..988547a0 100644 --- a/git/config.py +++ b/git/config.py @@ -14,12 +14,15 @@ except ImportError: import configparser as cp import inspect import logging +import abc from git.odict import OrderedDict from git.util import LockFile from git.compat import ( string_types, - FileType + FileType, + defenc, + with_metaclass ) __all__ = ('GitConfigParser', 'SectionConstraint') @@ -28,7 +31,7 @@ __all__ = ('GitConfigParser', 'SectionConstraint') log = logging.getLogger('git.config') -class MetaParserBuilder(type): +class MetaParserBuilder(abc.ABCMeta): """Utlity class wrapping base-class methods into decorators that assure read-only properties""" def __new__(metacls, name, bases, clsdict): @@ -39,7 +42,7 @@ class MetaParserBuilder(type): if kmm in clsdict: mutating_methods = clsdict[kmm] for base in bases: - methods = (t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_")) + methods = (t for t in inspect.getmembers(base, inspect.isroutine) if not t[0].startswith("_")) for name, method in methods: if name in clsdict: continue @@ -112,7 +115,7 @@ class SectionConstraint(object): return self._config -class GitConfigParser(cp.RawConfigParser, object): +class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): """Implements specifics required to read git style configuration files. @@ -128,7 +131,6 @@ class GitConfigParser(cp.RawConfigParser, object): :note: The config is case-sensitive even when queried, hence section and option names must match perfectly.""" - __metaclass__ = MetaParserBuilder #{ Configuration # The lock type determines the type of lock to use in new configuration readers. @@ -150,7 +152,6 @@ class GitConfigParser(cp.RawConfigParser, object): # list of RawConfigParser methods able to change the instance _mutating_methods_ = ("add_section", "remove_section", "remove_option", "set") - __slots__ = ("_sections", "_defaults", "_file_or_files", "_read_only", "_is_initialized", '_lock') def __init__(self, file_or_files, read_only=True): """Initialize a configuration reader to read the given file_or_files and to @@ -162,12 +163,12 @@ class GitConfigParser(cp.RawConfigParser, object): :param read_only: If True, the ConfigParser may only read the data , but not change it. If False, only a single file path or file object may be given.""" - super(GitConfigParser, self).__init__() - # initialize base with ordered dictionaries to be sure we write the same - # file back - self._sections = OrderedDict() - self._defaults = OrderedDict() + cp.RawConfigParser.__init__(self, dict_type=OrderedDict) + # Used in python 3, needs to stay in sync with sections for underlying implementation to work + if not hasattr(self, '_proxies'): + self._proxies = self._dict() + self._file_or_files = file_or_files self._read_only = read_only self._is_initialized = False @@ -222,7 +223,8 @@ class GitConfigParser(cp.RawConfigParser, object): lineno = 0 e = None # None, or an exception while True: - line = fp.readline() + # we assume to read binary ! + line = fp.readline().decode(defenc) if not line: break lineno = lineno + 1 @@ -242,9 +244,9 @@ class GitConfigParser(cp.RawConfigParser, object): elif sectname == cp.DEFAULTSECT: cursect = self._defaults else: - # THE ONLY LINE WE CHANGED ! - cursect = OrderedDict((('__name__', sectname),)) + cursect = self._dict((('__name__', sectname),)) self._sections[sectname] = cursect + self._proxies[sectname] = None # So sections can't start with a continuation line optname = None # no section header in the file? @@ -295,7 +297,7 @@ class GitConfigParser(cp.RawConfigParser, object): # assume a path if it is not a file-object if not hasattr(file_object, "seek"): try: - fp = open(file_object) + fp = open(file_object, 'rb') close_fp = True except IOError: continue @@ -314,16 +316,17 @@ class GitConfigParser(cp.RawConfigParser, object): """Write an .ini-format representation of the configuration state in git compatible format""" def write_section(name, section_dict): - fp.write("[%s]\n" % name) + fp.write(("[%s]\n" % name).encode(defenc)) for (key, value) in section_dict.items(): if key != "__name__": - fp.write("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) + fp.write(("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))).encode(defenc)) # END if key is not __name__ # END section writing if self._defaults: write_section(cp.DEFAULTSECT, self._defaults) - map(lambda t: write_section(t[0], t[1]), self._sections.items()) + for name, value in self._sections.items(): + write_section(name, value) @needs_values def write(self): @@ -371,8 +374,7 @@ class GitConfigParser(cp.RawConfigParser, object): @set_dirty_and_flush_changes def add_section(self, section): """Assures added options will stay in order""" - super(GitConfigParser, self).add_section(section) - self._sections[section] = OrderedDict() + return super(GitConfigParser, self).add_section(section) @property def read_only(self): diff --git a/git/objects/util.py b/git/objects/util.py index fdf9622b..cefef862 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -46,17 +46,17 @@ def get_object_type_by_name(object_type_name): :param object_type_name: Member of TYPES :raise ValueError: In case object_type_name is unknown""" - if object_type_name == "commit": - import commit + if object_type_name == b"commit": + from . import commit return commit.Commit - elif object_type_name == "tag": - import tag + elif object_type_name == b"tag": + from . import tag return tag.TagObject - elif object_type_name == "blob": - import blob + elif object_type_name == b"blob": + from . import blob return blob.Blob - elif object_type_name == "tree": - import tree + elif object_type_name == b"tree": + from . import tree return tree.Tree else: raise ValueError("Cannot handle unknown object type: %s" % object_type_name) diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 624b1a09..1ac9ac65 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -19,7 +19,9 @@ from gitdb.util import ( hex_to_bin, LockedFD ) -from git.compat import string_types +from git.compat import ( + string_types, +) from .log import RefLog @@ -79,10 +81,10 @@ class SymbolicReference(object): @classmethod def _iter_packed_refs(cls, repo): - """Returns an iterator yielding pairs of sha1/path pairs for the corresponding refs. + """Returns an iterator yielding pairs of sha1/path pairs (as bytes) for the corresponding refs. :note: The packed refs file will be kept open as long as we iterate""" try: - fp = open(cls._get_packed_refs_path(repo), 'rb') + fp = open(cls._get_packed_refs_path(repo), 'rt') for line in fp: line = line.strip() if not line: @@ -123,12 +125,12 @@ class SymbolicReference(object): @classmethod def _get_ref_info(cls, repo, ref_path): - """Return: (sha, target_ref_path) if available, the sha the file at + """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" tokens = None try: - fp = open(join(repo.git_dir, ref_path), 'r') + fp = open(join(repo.git_dir, ref_path), 'rt') value = fp.read().rstrip() fp.close() # Don't only split on spaces, but on whitespace, which allows to parse lines like @@ -141,7 +143,8 @@ class SymbolicReference(object): for sha, path in cls._iter_packed_refs(repo): if path != ref_path: continue - tokens = (sha, path) + # sha will be used as + tokens = sha, path break # END for each packed ref # END handle packed refs diff --git a/git/remote.py b/git/remote.py index 9ebc52fe..63f21c4e 100644 --- a/git/remote.py +++ b/git/remote.py @@ -32,6 +32,7 @@ from git.util import ( finalize_process ) from gitdb.util import join +from git.compat import defenc __all__ = ('RemoteProgress', 'PushInfo', 'FetchInfo', 'Remote') @@ -46,16 +47,16 @@ def digest_process_messages(fh, progress): :param fh: File handle to read from :return: list(line, ...) list of lines without linebreaks that did not contain progress information""" - line_so_far = '' + line_so_far = b'' dropped_lines = list() while True: - char = fh.read(1) + char = fh.read(1) # reads individual single byte strings if not char: break - if char in ('\r', '\n') and line_so_far: - dropped_lines.extend(progress._parse_progress_line(line_so_far)) - line_so_far = '' + if char in (b'\r', b'\n') and line_so_far: + dropped_lines.extend(progress._parse_progress_line(line_so_far.decode(defenc))) + line_so_far = b'' else: line_so_far += char # END process parsed line diff --git a/git/test/test_config.py b/git/test/test_config.py index 0301c54f..f02754d5 100644 --- a/git/test/test_config.py +++ b/git/test/test_config.py @@ -11,16 +11,17 @@ from git.test.lib import ( from git import ( GitConfigParser ) -from git.compat import string_types +from git.compat import ( + string_types, +) import io from copy import copy -from ConfigParser import NoSectionError - +from git.config import cp class TestBase(TestCase): def _to_memcache(self, file_path): - fp = open(file_path, "r") + fp = open(file_path, "rb") sio = io.BytesIO(fp.read()) sio.name = file_path return sio @@ -39,7 +40,7 @@ class TestBase(TestCase): w_config.write() # enforce writing # we stripped lines when reading, so the results differ - assert file_obj.getvalue() != file_obj_orig.getvalue() + assert file_obj.getvalue() and file_obj.getvalue() != file_obj_orig.getvalue() # creating an additional config writer must fail due to exclusive access self.failUnlessRaises(IOError, GitConfigParser, file_obj, read_only=False) @@ -105,4 +106,4 @@ class TestBase(TestCase): assert r_config.get_value("doesnt", "exist", default) == default # it raises if there is no default though - self.failUnlessRaises(NoSectionError, r_config.get_value, "doesnt", "exist") + self.failUnlessRaises(cp.NoSectionError, r_config.get_value, "doesnt", "exist") -- cgit v1.2.1 From 3d0556a31916a709e9da3eafb92fc6b8bf69896c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 16:10:50 +0100 Subject: Added test of #147 to verify it works. Applied a few more fixes to commit implementation, possibly not the last --- git/objects/commit.py | 25 +++++++++---------------- git/test/test_base.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) (limited to 'git') diff --git a/git/objects/commit.py b/git/objects/commit.py index 79d460ad..5ad7902b 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -373,40 +373,33 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): def _serialize(self, stream): write = stream.write - write("tree %s\n" % self.tree) + write(("tree %s\n" % self.tree).encode('ascii')) for p in self.parents: - write("parent %s\n" % p) + write(("parent %s\n" % p).encode('ascii')) a = self.author aname = a.name - if isinstance(aname, text_type): - aname = aname.encode(self.encoding) - # END handle unicode in name - c = self.committer fmt = "%s %s <%s> %s %s\n" - write(fmt % ("author", aname, a.email, + write((fmt % ("author", aname, a.email, self.authored_date, - altz_to_utctz_str(self.author_tz_offset))) + altz_to_utctz_str(self.author_tz_offset))).encode(self.encoding)) # encode committer aname = c.name - if isinstance(aname, text_type): - aname = aname.encode(self.encoding) - # END handle unicode in name - write(fmt % ("committer", aname, c.email, + write((fmt % ("committer", aname, c.email, self.committed_date, - altz_to_utctz_str(self.committer_tz_offset))) + altz_to_utctz_str(self.committer_tz_offset))).encode(self.encoding)) if self.encoding != self.default_encoding: - write("encoding %s\n" % self.encoding) + write(("encoding %s\n" % self.encoding).encode('ascii')) if self.gpgsig: write("gpgsig") for sigline in self.gpgsig.rstrip("\n").split("\n"): - write(" " + sigline + "\n") + write((" " + sigline + "\n").encode('ascii')) - write("\n") + write(b"\n") # write plain bytes, be sure its encoded according to our encoding if isinstance(self.message, text_type): diff --git a/git/test/test_base.py b/git/test/test_base.py index a14d4680..edacbd80 100644 --- a/git/test/test_base.py +++ b/git/test/test_base.py @@ -1,3 +1,4 @@ +#-*-coding:utf-8-*- # test_base.py # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors # @@ -106,3 +107,13 @@ class TestBase(TestBase): assert not rw_repo.config_reader("repository").getboolean("core", "bare") assert rw_remote_repo.config_reader("repository").getboolean("core", "bare") assert os.path.isdir(os.path.join(rw_repo.working_tree_dir, 'lib')) + + @with_rw_repo('0.1.6') + def test_add_unicode(self, rw_repo): + filename = u"שלום.txt" + + file_path = os.path.join(rw_repo.working_dir, filename) + open(file_path, "wb").write('something') + + rw_repo.git.add(rw_repo.working_dir) + rw_repo.index.commit('message') -- cgit v1.2.1 From 8a308613467a1510f8dac514624abae4e10c0779 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 16:44:54 +0100 Subject: Fixes test_blob and improved commit writing/reading --- git/compat.py | 4 +++- git/objects/commit.py | 44 ++++++++++++++++++++++++-------------------- git/objects/fun.py | 26 +++++++++++++------------- 3 files changed, 40 insertions(+), 34 deletions(-) (limited to 'git') diff --git a/git/compat.py b/git/compat.py index 4a892ad2..f11d1423 100644 --- a/git/compat.py +++ b/git/compat.py @@ -27,12 +27,15 @@ defenc = sys.getdefaultencoding() if PY3: import io FileType = io.IOBase + def byte_ord(b): + return b else: FileType = file # usually, this is just ascii, which might not enough for our encoding needs # Unless it's set specifically, we override it to be utf-8 if defenc == 'ascii': defenc = 'utf-8' + byte_ord = ord def with_metaclass(meta, *bases): @@ -54,4 +57,3 @@ def with_metaclass(meta, *bases): # end metaclass return metaclass(meta.__name__ + 'Helper', None, {}) # end handle py2 - diff --git a/git/objects/commit.py b/git/objects/commit.py index 5ad7902b..f8b5c969 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -419,23 +419,25 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): next_line = None while True: parent_line = readline() - if not parent_line.startswith('parent'): + if not parent_line.startswith(b'parent'): next_line = parent_line break # END abort reading parents - self.parents.append(type(self)(self.repo, hex_to_bin(parent_line.split()[-1]))) + self.parents.append(type(self)(self.repo, hex_to_bin(parent_line.split()[-1].decode('ascii')))) # END for each parent line self.parents = tuple(self.parents) - self.author, self.authored_date, self.author_tz_offset = parse_actor_and_date(next_line) - self.committer, self.committed_date, self.committer_tz_offset = parse_actor_and_date(readline()) + # we don't know actual author encoding before we have parsed it, so keep the lines around + author_line = next_line + committer_line = readline() # we might run into one or more mergetag blocks, skip those for now next_line = readline() - while next_line.startswith('mergetag '): + while next_line.startswith(b'mergetag '): next_line = readline() while next_line.startswith(' '): next_line = readline() + # end skip mergetags # now we can have the encoding line, or an empty line followed by the optional # message. @@ -444,39 +446,40 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): # read headers enc = next_line buf = enc.strip() - while buf != "": - if buf[0:10] == "encoding ": - self.encoding = buf[buf.find(' ') + 1:] - elif buf[0:7] == "gpgsig ": - sig = buf[buf.find(' ') + 1:] + "\n" + while buf: + if buf[0:10] == b"encoding ": + self.encoding = buf[buf.find(' ') + 1:].decode('ascii') + elif buf[0:7] == b"gpgsig ": + sig = buf[buf.find(b' ') + 1:] + b"\n" is_next_header = False while True: sigbuf = readline() - if sigbuf == "": + if not sigbuf: break - if sigbuf[0:1] != " ": + if sigbuf[0:1] != b" ": buf = sigbuf.strip() is_next_header = True break sig += sigbuf[1:] - self.gpgsig = sig.rstrip("\n") + # end read all signature + self.gpgsig = sig.rstrip(b"\n").decode('ascii') if is_next_header: continue buf = readline().strip() - # decode the authors name + try: - self.author.name = self.author.name.decode(self.encoding) + self.author, self.authored_date, self.author_tz_offset = \ + parse_actor_and_date(author_line.decode(self.encoding)) except UnicodeDecodeError: - log.error("Failed to decode author name '%s' using encoding %s", self.author.name, self.encoding, + log.error("Failed to decode author line '%s' using encoding %s", author_line, self.encoding, exc_info=True) - # END handle author's encoding - # decode committer name try: - self.committer.name = self.committer.name.decode(self.encoding) + self.committer, self.committed_date, self.committer_tz_offset = \ + parse_actor_and_date(committer_line.decode(self.encoding)) except UnicodeDecodeError: - log.error("Failed to decode committer name '%s' using encoding %s", self.committer.name, self.encoding, + log.error("Failed to decode committer line '%s' using encoding %s", committer_line, self.encoding, exc_info=True) # END handle author's encoding @@ -488,6 +491,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): except UnicodeDecodeError: log.error("Failed to decode message '%s' using encoding %s", self.message, self.encoding, exc_info=True) # END exception handling + return self #} END serializable implementation diff --git a/git/objects/fun.py b/git/objects/fun.py index db2ec7c2..f92a4c06 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -1,6 +1,9 @@ """Module with functions which are supposed to be as fast as possible""" from stat import S_ISDIR from git.compat import ( + byte_ord, + force_bytes, + defenc, xrange, text_type ) @@ -17,13 +20,13 @@ def tree_to_stream(entries, write): bit_mask = 7 # 3 bits set for binsha, mode, name in entries: - mode_str = '' + mode_str = b'' for i in xrange(6): mode_str = chr(((mode >> (i * 3)) & bit_mask) + ord_zero) + mode_str # END for each 8 octal value # git slices away the first octal if its zero - if mode_str[0] == '0': + if byte_ord(mode_str[0]) == ord_zero: mode_str = mode_str[1:] # END save a byte @@ -33,16 +36,16 @@ def tree_to_stream(entries, write): # According to my tests, this is exactly what git does, that is it just # takes the input literally, which appears to be utf8 on linux. if isinstance(name, text_type): - name = name.encode("utf8") - write("%s %s\0%s" % (mode_str, name, binsha)) + name = name.encode(defenc) + write(b''.join(mode_str, b' ', name, b'\0', binsha)) # END for each item - def tree_entries_from_data(data): """Reads the binary representation of a tree and returns tuples of Tree items - :param data: data block with tree data + :param data: data block with tree data (as bytes) :return: list(tuple(binsha, mode, tree_relative_path), ...)""" ord_zero = ord('0') + space_ord = ord(' ') len_data = len(data) i = 0 out = list() @@ -52,10 +55,10 @@ def tree_entries_from_data(data): # read mode # Some git versions truncate the leading 0, some don't # The type will be extracted from the mode later - while data[i] != ' ': + while byte_ord(data[i]) != space_ord: # move existing mode integer up one level being 3 bits # and add the actual ordinal value of the character - mode = (mode << 3) + (ord(data[i]) - ord_zero) + mode = (mode << 3) + (byte_ord(data[i]) - ord_zero) i += 1 # END while reading mode @@ -65,7 +68,7 @@ def tree_entries_from_data(data): # parse name, it is NULL separated ns = i - while data[i] != '\0': + while byte_ord(data[i]) != 0: i += 1 # END while not reached NULL @@ -73,12 +76,9 @@ def tree_entries_from_data(data): # Only use the respective unicode object if the byte stream was encoded name = data[ns:i] try: - name_enc = name.decode("utf-8") + name = name.decode(defenc) except UnicodeDecodeError: pass - else: - if len(name) > len(name_enc): - name = name_enc # END handle encoding # byte is NULL, get next 20 -- cgit v1.2.1 From e1060a2a8c90c0730c3541811df8f906dac510a7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 17:59:22 +0100 Subject: test_commit works once again --- git/cmd.py | 7 ++++++- git/config.py | 7 ++++++- git/diff.py | 2 +- git/objects/base.py | 7 ++++--- git/objects/commit.py | 4 ++-- git/objects/fun.py | 3 +-- git/refs/symbolic.py | 2 +- git/repo/base.py | 17 ++++++++++------- git/test/test_commit.py | 44 +++++++++++++++++++++++--------------------- 9 files changed, 54 insertions(+), 39 deletions(-) (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index c536b43c..2bff3310 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -320,6 +320,7 @@ class Git(LazyMixin): always be created with a pipe due to issues with subprocess. This merely is a workaround as data will be copied from the output pipe to the given output stream directly. + Judging from the implementation, you shouldn't use this flag ! :param subprocess_kwargs: Keyword arguments to be passed to subprocess.Popen. Please note that @@ -411,9 +412,13 @@ class Git(LazyMixin): else: raise GitCommandError(command, status, stderr_value) + + if isinstance(stdout_value, bytes): # could also be output_stream + stdout_value = stdout_value.decode(defenc) + # Allow access to the command's status code if with_extended_output: - return (status, stdout_value, stderr_value) + return (status, stdout_value, stderr_value.decode(defenc)) else: return stdout_value diff --git a/git/config.py b/git/config.py index 988547a0..7917bc5a 100644 --- a/git/config.py +++ b/git/config.py @@ -201,6 +201,11 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje self.write() except IOError: log.error("Exception during destruction of GitConfigParser", exc_info=True) + except ReferenceError: + # This happens in PY3 ... and usually means that some state cannot be written + # as the sections dict cannot be iterated + # Usually when shutting down the interpreter, don'y know how to fix this + pass finally: self._lock._release_lock() @@ -345,7 +350,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje # END get lock for physical files if not hasattr(fp, "seek"): - fp = open(self._file_or_files, "w") + fp = open(self._file_or_files, "wb") close_fp = True else: fp.seek(0) diff --git a/git/diff.py b/git/diff.py index b3e7245b..1692d83e 100644 --- a/git/diff.py +++ b/git/diff.py @@ -195,7 +195,7 @@ class Diff(object): """, re.VERBOSE | re.MULTILINE) # can be used for comparisons NULL_HEX_SHA = "0" * 40 - NULL_BIN_SHA = "\0" * 20 + NULL_BIN_SHA = b"\0" * 20 __slots__ = ("a_blob", "b_blob", "a_mode", "b_mode", "new_file", "deleted_file", "rename_from", "rename_to", "diff") diff --git a/git/objects/base.py b/git/objects/base.py index 1f0d5752..004e3981 100644 --- a/git/objects/base.py +++ b/git/objects/base.py @@ -21,7 +21,7 @@ class Object(LazyMixin): """Implements an Object which may be Blobs, Trees, Commits and Tags""" NULL_HEX_SHA = '0' * 40 - NULL_BIN_SHA = '\0' * 20 + NULL_BIN_SHA = b'\0' * 20 TYPES = (dbtyp.str_blob_type, dbtyp.str_tree_type, dbtyp.str_commit_type, dbtyp.str_tag_type) __slots__ = ("repo", "binsha", "size") @@ -94,7 +94,7 @@ class Object(LazyMixin): def __str__(self): """:return: string of our SHA1 as understood by all git commands""" - return bin_to_hex(self.binsha) + return self.hexsha def __repr__(self): """:return: string with pythonic representation of our object""" @@ -103,7 +103,8 @@ class Object(LazyMixin): @property def hexsha(self): """:return: 40 byte hex version of our 20 byte binary sha""" - return bin_to_hex(self.binsha) + # b2a_hex produces bytes + return bin_to_hex(self.binsha).decode('ascii') @property def data_stream(self): diff --git a/git/objects/commit.py b/git/objects/commit.py index f8b5c969..53af22cd 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -62,7 +62,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): "author", "authored_date", "author_tz_offset", "committer", "committed_date", "committer_tz_offset", "message", "parents", "encoding", "gpgsig") - _id_attribute_ = "binsha" + _id_attribute_ = "hexsha" def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, author_tz_offset=None, committer=None, committed_date=None, committer_tz_offset=None, @@ -395,7 +395,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): write(("encoding %s\n" % self.encoding).encode('ascii')) if self.gpgsig: - write("gpgsig") + write(b"gpgsig") for sigline in self.gpgsig.rstrip("\n").split("\n"): write((" " + sigline + "\n").encode('ascii')) diff --git a/git/objects/fun.py b/git/objects/fun.py index f92a4c06..610bdb5c 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -2,7 +2,6 @@ from stat import S_ISDIR from git.compat import ( byte_ord, - force_bytes, defenc, xrange, text_type @@ -37,7 +36,7 @@ def tree_to_stream(entries, write): # takes the input literally, which appears to be utf8 on linux. if isinstance(name, text_type): name = name.encode(defenc) - write(b''.join(mode_str, b' ', name, b'\0', binsha)) + write(b''.join((mode_str, b' ', name, b'\0', binsha))) # END for each item def tree_entries_from_data(data): diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 1ac9ac65..252462a9 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -308,7 +308,7 @@ class SymbolicReference(object): lfd = LockedFD(fpath) fd = lfd.open(write=True, stream=True) - fd.write(write_value) + fd.write(write_value.encode('ascii')) lfd.commit() # Adjust the reflog diff --git a/git/repo/base.py b/git/repo/base.py index f92a85ce..27c640ff 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -47,7 +47,10 @@ from .fun import ( read_gitfile, touch, ) -from git.compat import text_type +from git.compat import ( + text_type, + defenc +) import os import sys @@ -177,11 +180,11 @@ class Repo(object): # Description property def _get_description(self): filename = join(self.git_dir, 'description') - return open(filename).read().rstrip() + return open(filename, 'rb').read().rstrip().decode(defenc) def _set_description(self, descr): filename = join(self.git_dir, 'description') - open(filename, 'w').write(descr + '\n') + open(filename, 'wb').write((descr + '\n').encode(defenc)) description = property(_get_description, _set_description, doc="the project's description") @@ -464,8 +467,8 @@ class Repo(object): if os.path.exists(alternates_path): try: - f = open(alternates_path) - alts = f.read() + f = open(alternates_path, 'rb') + alts = f.read().decode(defenc) finally: f.close() return alts.strip().splitlines() @@ -489,8 +492,8 @@ class Repo(object): os.remove(alternates_path) else: try: - f = open(alternates_path, 'w') - f.write("\n".join(alts)) + f = open(alternates_path, 'wb') + f.write("\n".join(alts).encode(defenc)) finally: f.close() # END file handling diff --git a/git/test/test_commit.py b/git/test/test_commit.py index 5f45e59d..37a54092 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -51,7 +51,7 @@ def assert_commit_serialization(rwrepo, commit_id, print_performance_info=False) stream.seek(0) istream = rwrepo.odb.store(IStream(Commit.type, streamlen, stream)) - assert istream.hexsha == cm.hexsha + assert istream.hexsha == cm.hexsha.encode('ascii') nc = Commit(rwrepo, Commit.NULL_BIN_SHA, cm.tree, cm.author, cm.authored_date, cm.author_tz_offset, @@ -129,7 +129,7 @@ class TestCommit(TestBase): def test_unicode_actor(self): # assure we can parse unicode actors correctly - name = "Üäöß ÄußÉ".decode("utf-8") + name = u"Üäöß ÄußÉ" assert len(name) == 9 special = Actor._from_string(u"%s " % name) assert special.name == name @@ -146,13 +146,13 @@ class TestCommit(TestBase): # basic branch first, depth first dfirst = start.traverse(branch_first=False) bfirst = start.traverse(branch_first=True) - assert dfirst.next() == p0 - assert dfirst.next() == p00 + assert next(dfirst) == p0 + assert next(dfirst) == p00 - assert bfirst.next() == p0 - assert bfirst.next() == p1 - assert bfirst.next() == p00 - assert bfirst.next() == p10 + assert next(bfirst) == p0 + assert next(bfirst) == p1 + assert next(bfirst) == p00 + assert next(bfirst) == p10 # at some point, both iterations should stop assert list(bfirst)[-1] == first @@ -161,19 +161,19 @@ class TestCommit(TestBase): assert len(l[0]) == 2 # ignore self - assert start.traverse(ignore_self=False).next() == start + assert next(start.traverse(ignore_self=False)) == start # depth assert len(list(start.traverse(ignore_self=False, depth=0))) == 1 # prune - assert start.traverse(branch_first=1, prune=lambda i, d: i == p0).next() == p1 + assert next(start.traverse(branch_first=1, prune=lambda i, d: i == p0)) == p1 # predicate - assert start.traverse(branch_first=1, predicate=lambda i, d: i == p1).next() == p1 + assert next(start.traverse(branch_first=1, predicate=lambda i, d: i == p1)) == p1 # traversal should stop when the beginning is reached - self.failUnlessRaises(StopIteration, first.traverse().next) + self.failUnlessRaises(StopIteration, next, first.traverse()) # parents of the first commit should be empty ( as the only parent has a null # sha ) @@ -210,7 +210,7 @@ class TestCommit(TestBase): first_parent=True, bisect_all=True) - commits = Commit._iter_from_process_or_stream(self.rorepo, StringProcessAdapter(revs)) + commits = Commit._iter_from_process_or_stream(self.rorepo, StringProcessAdapter(revs.encode('ascii'))) expected_ids = ( '7156cece3c49544abb6bf7a0c218eb36646fad6d', '1f66cfbbce58b4b552b041707a12d437cc5f400a', @@ -224,8 +224,10 @@ class TestCommit(TestBase): assert self.rorepo.tag('refs/tags/0.1.5').commit.count() == 143 def test_list(self): + # This doesn't work anymore, as we will either attempt getattr with bytes, or compare 20 byte string + # with actual 20 byte bytes. This usage makes no sense anyway assert isinstance(Commit.list_items(self.rorepo, '0.1.5', max_count=5)[ - hex_to_bin('5117c9c8a4d3af19a9958677e45cda9269de1541')], Commit) + '5117c9c8a4d3af19a9958677e45cda9269de1541'], Commit) def test_str(self): commit = Commit(self.rorepo, Commit.NULL_BIN_SHA) @@ -247,12 +249,12 @@ class TestCommit(TestBase): c = self.rorepo.commit('0.1.5') for skip in (0, 1): piter = c.iter_parents(skip=skip) - first_parent = piter.next() + first_parent = next(piter) assert first_parent != c assert first_parent == c.parents[0] # END for each - def test_base(self): + def test_name_rev(self): name_rev = self.rorepo.head.commit.name_rev assert isinstance(name_rev, string_types) @@ -270,10 +272,10 @@ class TestCommit(TestBase): assert isinstance(cmt.message, text_type) # it automatically decodes it as such assert isinstance(cmt.author.name, text_type) # same here - cmt.message = "üäêèß".decode("utf-8") + cmt.message = u"üäêèß" assert len(cmt.message) == 5 - cmt.author.name = "äüß".decode("utf-8") + cmt.author.name = u"äüß" assert len(cmt.author.name) == 3 cstream = BytesIO() @@ -292,7 +294,7 @@ class TestCommit(TestBase): def test_gpgsig(self): cmt = self.rorepo.commit() - cmt._deserialize(open(fixture_path('commit_with_gpgsig'))) + cmt._deserialize(open(fixture_path('commit_with_gpgsig'), 'rb')) fixture_sig = """-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) @@ -318,7 +320,7 @@ JzJMZDRLQLFvnzqZuCjE cstream = BytesIO() cmt._serialize(cstream) - assert re.search(r"^gpgsig $", cstream.getvalue(), re.MULTILINE) + assert re.search(r"^gpgsig $", cstream.getvalue().decode('ascii'), re.MULTILINE) cstream.seek(0) cmt.gpgsig = None @@ -328,4 +330,4 @@ JzJMZDRLQLFvnzqZuCjE cmt.gpgsig = None cstream = BytesIO() cmt._serialize(cstream) - assert not re.search(r"^gpgsig ", cstream.getvalue(), re.MULTILINE) + assert not re.search(r"^gpgsig ", cstream.getvalue().decode('ascii'), re.MULTILINE) -- cgit v1.2.1 From 31b3673bdb9d8fb7feea8ae887be455c4a880f76 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 18:03:08 +0100 Subject: test_diff works --- git/diff.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'git') diff --git a/git/diff.py b/git/diff.py index 1692d83e..3c4e8529 100644 --- a/git/diff.py +++ b/git/diff.py @@ -10,6 +10,8 @@ from gitdb.util import hex_to_bin from .objects.blob import Blob from .objects.util import mode_str_to_int +from git.compat import defenc + __all__ = ('Diffable', 'DiffIndex', 'Diff') @@ -294,7 +296,7 @@ class Diff(object): :param stream: result of 'git diff' as a stream (supporting file protocol) :return: git.DiffIndex """ # for now, we have to bake the stream - text = stream.read() + text = stream.read().decode(defenc) index = DiffIndex() diff_header = cls.re_header.match @@ -323,6 +325,7 @@ class Diff(object): # :100644 100644 687099101... 37c5e30c8... M .gitignore index = DiffIndex() for line in stream: + line = line.decode(defenc) if not line.startswith(":"): continue # END its not a valid diff line -- cgit v1.2.1 From 4a67e4e49c4e7b82e416067df69c72656213e886 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 18:21:49 +0100 Subject: test_fun works --- git/compat.py | 3 +++ git/index/fun.py | 9 +++++---- git/objects/fun.py | 5 +++-- git/test/test_fun.py | 12 ++++++------ 4 files changed, 17 insertions(+), 12 deletions(-) (limited to 'git') diff --git a/git/compat.py b/git/compat.py index f11d1423..b9205418 100644 --- a/git/compat.py +++ b/git/compat.py @@ -29,6 +29,8 @@ if PY3: FileType = io.IOBase def byte_ord(b): return b + def bchr(n): + return bytes([n]) else: FileType = file # usually, this is just ascii, which might not enough for our encoding needs @@ -36,6 +38,7 @@ else: if defenc == 'ascii': defenc = 'utf-8' byte_ord = ord + bchr = chr def with_metaclass(meta, *bases): diff --git a/git/index/fun.py b/git/index/fun.py index 0e49ae8d..3e66a7ba 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -37,6 +37,7 @@ from .util import ( from gitdb.base import IStream from gitdb.typ import str_tree_type +from git.compat import defenc __all__ = ('write_cache', 'read_cache', 'write_tree_from_cache', 'entry_key', 'stat_mode_to_index_mode', 'S_IFGITLINK') @@ -86,9 +87,9 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1 flags = plen | (entry[2] & CE_NAMEMASK_INV) # clear possible previous values write(pack(">LLLLLL20sH", entry[6], entry[7], entry[0], entry[8], entry[9], entry[10], entry[1], flags)) - write(path) + write(path.encode(defenc)) real_size = ((tell() - beginoffset + 8) & ~7) - write("\0" * ((beginoffset + real_size) - tell())) + write(b"\0" * ((beginoffset + real_size) - tell())) # END for each entry # write previously cached extensions data @@ -102,7 +103,7 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1 def read_header(stream): """Return tuple(version_long, num_entries) from the given stream""" type_id = stream.read(4) - if type_id != "DIRC": + if type_id != b"DIRC": raise AssertionError("Invalid index file header: %r" % type_id) version, num_entries = unpack(">LL", stream.read(4 * 2)) @@ -142,7 +143,7 @@ def read_cache(stream): (dev, ino, mode, uid, gid, size, sha, flags) = \ unpack(">LLLLLL20sH", read(20 + 4 * 6 + 2)) path_size = flags & CE_NAMEMASK - path = read(path_size) + path = read(path_size).decode(defenc) real_size = ((tell() - beginoffset + 8) & ~7) read((beginoffset + real_size) - tell()) diff --git a/git/objects/fun.py b/git/objects/fun.py index 610bdb5c..ba8dbcf4 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -4,7 +4,8 @@ from git.compat import ( byte_ord, defenc, xrange, - text_type + text_type, + bchr ) __all__ = ('tree_to_stream', 'tree_entries_from_data', 'traverse_trees_recursive', @@ -21,7 +22,7 @@ def tree_to_stream(entries, write): for binsha, mode, name in entries: mode_str = b'' for i in xrange(6): - mode_str = chr(((mode >> (i * 3)) & bit_mask) + ord_zero) + mode_str + mode_str = bchr(((mode >> (i * 3)) & bit_mask) + ord_zero) + mode_str # END for each 8 octal value # git slices away the first octal if its zero diff --git a/git/test/test_fun.py b/git/test/test_fun.py index 9d3e749e..40d040b9 100644 --- a/git/test/test_fun.py +++ b/git/test/test_fun.py @@ -30,7 +30,7 @@ from io import BytesIO class TestFun(TestBase): def _assert_index_entries(self, entries, trees): - index = IndexFile.from_tree(self.rorepo, *[self.rorepo.tree(bin_to_hex(t)) for t in trees]) + index = IndexFile.from_tree(self.rorepo, *[self.rorepo.tree(bin_to_hex(t).decode('ascii')) for t in trees]) assert entries assert len(index.entries) == len(entries) for entry in entries: @@ -91,9 +91,9 @@ class TestFun(TestBase): assert has_conflict == (len([e for e in entries if e.stage != 0]) > 0) mktree = self.mktree - shaa = "\1" * 20 - shab = "\2" * 20 - shac = "\3" * 20 + shaa = b"\1" * 20 + shab = b"\2" * 20 + shac = b"\3" * 20 odb = rwrepo.odb @@ -256,6 +256,6 @@ class TestFun(TestBase): assert entries # END for each commit - def test_tree_entries_from_data(self): + def test_tree_entries_from_data_with_failing_name_decode(self): r = tree_entries_from_data(b'100644 \x9f\0aaa') - assert r == [('aaa', 33188, '\x9f')], r + assert r == [(b'aaa', 33188, b'\x9f')], r -- cgit v1.2.1 From 7297ff651c3cc6abf648b3fe730c2b5b1f3edbd3 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 18:30:30 +0100 Subject: test_git works --- git/test/test_git.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'git') diff --git a/git/test/test_git.py b/git/test/test_git.py index 553f8d1b..8087d6f8 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -1,3 +1,4 @@ +#-*-coding:utf-8-*- # test_git.py # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors # @@ -32,12 +33,12 @@ class TestGit(TestBase): assert_equal(git.call_args, ((['git', 'version'],), {})) def test_call_unpack_args_unicode(self): - args = Git._Git__unpack_args(u'Unicode' + unichr(40960)) - assert_equal(args, ['Unicode\xea\x80\x80']) + args = Git._Git__unpack_args(u'Unicode€™') + assert_equal(args, [b'Unicode\xe2\x82\xac\xe2\x84\xa2']) def test_call_unpack_args(self): - args = Git._Git__unpack_args(['git', 'log', '--', u'Unicode' + unichr(40960)]) - assert_equal(args, ['git', 'log', '--', 'Unicode\xea\x80\x80']) + args = Git._Git__unpack_args(['git', 'log', '--', u'Unicode€™']) + assert_equal(args, [b'git', b'log', b'--', b'Unicode\xe2\x82\xac\xe2\x84\xa2']) @raises(GitCommandError) def test_it_raises_errors(self): @@ -75,13 +76,13 @@ class TestGit(TestBase): import subprocess as sp hexsha = "b2339455342180c7cc1e9bba3e9f181f7baa5167" g = self.git.cat_file(batch_check=True, istream=sp.PIPE, as_process=True) - g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") + g.stdin.write(b"b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() obj_info = g.stdout.readline() # read header + data g = self.git.cat_file(batch=True, istream=sp.PIPE, as_process=True) - g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") + g.stdin.write(b"b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() obj_info_two = g.stdout.readline() assert obj_info == obj_info_two @@ -92,7 +93,7 @@ class TestGit(TestBase): g.stdout.read(1) # now we should be able to read a new object - g.stdin.write("b2339455342180c7cc1e9bba3e9f181f7baa5167\n") + g.stdin.write(b"b2339455342180c7cc1e9bba3e9f181f7baa5167\n") g.stdin.flush() assert g.stdout.readline() == obj_info -- cgit v1.2.1 From 45c1c99a22e95d730d3096c339d97181d314d6ca Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 10:43:25 +0100 Subject: test_index works --- git/compat.py | 4 ++++ git/config.py | 9 ++++++++- git/index/base.py | 23 ++++++++++++----------- git/index/fun.py | 2 +- git/index/typ.py | 2 +- git/repo/base.py | 5 ++++- git/test/test_index.py | 37 +++++++++++++++++++------------------ git/util.py | 9 +++------ 8 files changed, 52 insertions(+), 39 deletions(-) (limited to 'git') diff --git a/git/compat.py b/git/compat.py index b9205418..5c330e5b 100644 --- a/git/compat.py +++ b/git/compat.py @@ -31,6 +31,8 @@ if PY3: return b def bchr(n): return bytes([n]) + def mviter(d): + return d.values() else: FileType = file # usually, this is just ascii, which might not enough for our encoding needs @@ -39,6 +41,8 @@ else: defenc = 'utf-8' byte_ord = ord bchr = chr + def mviter(d): + return d.itervalues() def with_metaclass(meta, *bases): diff --git a/git/config.py b/git/config.py index 7917bc5a..96991b84 100644 --- a/git/config.py +++ b/git/config.py @@ -99,6 +99,13 @@ class SectionConstraint(object): self._config = config self._section_name = section + def __del__(self): + # Yes, for some reason, we have to call it explicitly for it to work in PY3 ! + # Apparently __del__ doesn't get call anymore if refcount becomes 0 + # Ridiculous ... . + self._config.__del__() + # del self._config + def __getattr__(self, attr): if attr in self._valid_attrs_: return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) @@ -193,7 +200,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje """Write pending changes if required and release locks""" # checking for the lock here makes sure we do not raise during write() # in case an invalid parser was created who could not get a lock - if self.read_only or not self._lock._has_lock(): + if self.read_only or (self._lock and not self._lock._has_lock()): return try: diff --git a/git/index/base.py b/git/index/base.py index a994e7b6..cc883469 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -43,7 +43,9 @@ from git.compat import ( izip, xrange, string_types, - force_bytes + force_bytes, + defenc, + mviter ) from git.util import ( @@ -105,7 +107,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): repository's index on demand.""" self.repo = repo self.version = self._VERSION - self._extension_data = '' + self._extension_data = b'' self._file_path = file_path or self._index_path() def _set_cache_(self, attr): @@ -165,9 +167,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): def _entries_sorted(self): """:return: list of entries, in a sorted fashion, first by path, then by stage""" - entries_sorted = self.entries.values() - entries_sorted.sort(key=lambda e: (e.path, e.stage)) # use path/stage as sort key - return entries_sorted + return sorted(self.entries.values(), key=lambda e: (e.path, e.stage)) def _serialize(self, stream, ignore_tree_extension_data=False): entries = self._entries_sorted() @@ -399,7 +399,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): fprogress(filepath, False, item) rval = None try: - proc.stdin.write("%s\n" % filepath) + proc.stdin.write(("%s\n" % filepath).encode(defenc)) except IOError: # pipe broke, usually because some error happend raise fmakeexc() @@ -418,7 +418,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): Function(t) returning True if tuple(stage, Blob) should be yielded by the iterator. A default filter, the BlobFilter, allows you to yield blobs only if they match a given list of paths. """ - for entry in self.entries.itervalues(): + for entry in mviter(self.entries): blob = entry.to_blob(self.repo) blob.size = entry.size output = (entry.stage, blob) @@ -443,7 +443,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): for stage, blob in self.iter_blobs(is_unmerged_blob): path_map.setdefault(blob.path, list()).append((stage, blob)) # END for each unmerged blob - for l in path_map.itervalues(): + for l in mviter(path_map): l.sort() return path_map @@ -860,7 +860,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): # parse result - first 0:n/2 lines are 'checking ', the remaining ones # are the 'renaming' ones which we parse - for ln in xrange(len(mvlines) / 2, len(mvlines)): + for ln in xrange(int(len(mvlines) / 2), len(mvlines)): tokens = mvlines[ln].split(' to ') assert len(tokens) == 2, "Too many tokens in %s" % mvlines[ln] @@ -958,6 +958,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): if not stderr: return # line contents: + stderr = stderr.decode(defenc) # git-checkout-index: this already exists failed_files = list() failed_reasons = list() @@ -1006,7 +1007,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): proc = self.repo.git.checkout_index(*args, **kwargs) proc.wait() fprogress(None, True, None) - rval_iter = (e.path for e in self.entries.itervalues()) + rval_iter = (e.path for e in mviter(self.entries)) handle_stderr(proc, rval_iter) return rval_iter else: @@ -1036,7 +1037,7 @@ class IndexFile(LazyMixin, diff.Diffable, Serializable): dir = co_path if not dir.endswith('/'): dir += '/' - for entry in self.entries.itervalues(): + for entry in mviter(self.entries): if entry.path.startswith(dir): p = entry.path self._write_path_to_stdin(proc, p, p, make_exc, diff --git a/git/index/fun.py b/git/index/fun.py index 3e66a7ba..f0dee961 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -73,7 +73,7 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1 # header version = 2 - write("DIRC") + write(b"DIRC") write(pack(">LL", version, len(entries))) # body diff --git a/git/index/typ.py b/git/index/typ.py index 692e1e18..0998ecb0 100644 --- a/git/index/typ.py +++ b/git/index/typ.py @@ -75,7 +75,7 @@ class BaseIndexEntry(tuple): @property def hexsha(self): """hex version of our sha""" - return b2a_hex(self[1]) + return b2a_hex(self[1]).decode('ascii') @property def stage(self): diff --git a/git/repo/base.py b/git/repo/base.py index 27c640ff..3e0e51cc 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -732,7 +732,10 @@ class Repo(object): # sure repo = cls(os.path.abspath(path), odbt=odbt) if repo.remotes: - repo.remotes[0].config_writer.set_value('url', repo.remotes[0].url.replace("\\\\", "\\").replace("\\", "/")) + writer = repo.remotes[0].config_writer + writer.set_value('url', repo.remotes[0].url.replace("\\\\", "\\").replace("\\", "/")) + # PY3: be sure cleanup is performed and lock is released + del writer # END handle remote repo return repo diff --git a/git/test/test_index.py b/git/test/test_index.py index 38cc3563..d81d08ef 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -48,7 +48,7 @@ class TestIndex(TestBase): def _assert_fprogress(self, entries): assert len(entries) == len(self._fprogress_map) - for path, call_count in self._fprogress_map.iteritems(): + for path, call_count in self._fprogress_map.items(): assert call_count == 2 # END for each item in progress map self._reset_progress() @@ -86,7 +86,7 @@ class TestIndex(TestBase): assert index.version > 0 # test entry - entry = index.entries.itervalues().next() + entry = next(iter(index.entries.values())) for attr in ("path", "ctime", "mtime", "dev", "inode", "mode", "uid", "gid", "size", "binsha", "hexsha", "stage"): getattr(entry, attr) @@ -100,7 +100,7 @@ class TestIndex(TestBase): # test stage index_merge = IndexFile(self.rorepo, fixture_path("index_merge")) assert len(index_merge.entries) == 106 - assert len(list(e for e in index_merge.entries.itervalues() if e.stage != 0)) + assert len(list(e for e in index_merge.entries.values() if e.stage != 0)) # write the data - it must match the original tmpfile = tempfile.mktemp() @@ -167,7 +167,7 @@ class TestIndex(TestBase): assert unmerged_blob_map # pick the first blob at the first stage we find and use it as resolved version - three_way_index.resolve_blobs(l[0][1] for l in unmerged_blob_map.itervalues()) + three_way_index.resolve_blobs(l[0][1] for l in unmerged_blob_map.values()) tree = three_way_index.write_tree() assert isinstance(tree, Tree) num_blobs = 0 @@ -201,7 +201,7 @@ class TestIndex(TestBase): # Add a change with a NULL sha that should conflict with next_commit. We # pretend there was a change, but we do not even bother adding a proper # sha for it ( which makes things faster of course ) - manifest_fake_entry = BaseIndexEntry((manifest_entry[0], "\0" * 20, 0, manifest_entry[3])) + manifest_fake_entry = BaseIndexEntry((manifest_entry[0], b"\0" * 20, 0, manifest_entry[3])) # try write flag self._assert_entries(rw_repo.index.add([manifest_fake_entry], write=False)) # add actually resolves the null-hex-sha for us as a feature, but we can @@ -236,7 +236,7 @@ class TestIndex(TestBase): # now make a proper three way merge with unmerged entries unmerged_tree = IndexFile.from_tree(rw_repo, parent_commit, tree, next_commit) unmerged_blobs = unmerged_tree.unmerged_blobs() - assert len(unmerged_blobs) == 1 and unmerged_blobs.keys()[0] == manifest_key[0] + assert len(unmerged_blobs) == 1 and list(unmerged_blobs.keys())[0] == manifest_key[0] @with_rw_repo('0.1.6') def test_index_file_diffing(self, rw_repo): @@ -295,7 +295,7 @@ class TestIndex(TestBase): assert index.diff(None) # reset the working copy as well to current head,to pull 'back' as well - new_data = "will be reverted" + new_data = b"will be reverted" file_path = os.path.join(rw_repo.working_tree_dir, "CHANGES") fp = open(file_path, "wb") fp.write(new_data) @@ -312,7 +312,7 @@ class TestIndex(TestBase): # test full checkout test_file = os.path.join(rw_repo.working_tree_dir, "CHANGES") - open(test_file, 'ab').write("some data") + open(test_file, 'ab').write(b"some data") rval = index.checkout(None, force=True, fprogress=self._fprogress) assert 'CHANGES' in list(rval) self._assert_fprogress([None]) @@ -336,7 +336,7 @@ class TestIndex(TestBase): self.failUnlessRaises(CheckoutError, index.checkout, paths=["doesnt/exist"]) # checkout file with modifications - append_data = "hello" + append_data = b"hello" fp = open(test_file, "ab") fp.write(append_data) fp.close() @@ -346,13 +346,13 @@ class TestIndex(TestBase): assert len(e.failed_files) == 1 and e.failed_files[0] == os.path.basename(test_file) assert (len(e.failed_files) == len(e.failed_reasons)) and isinstance(e.failed_reasons[0], string_types) assert len(e.valid_files) == 0 - assert open(test_file).read().endswith(append_data) + assert open(test_file, 'rb').read().endswith(append_data) else: raise AssertionError("Exception CheckoutError not thrown") # if we force it it should work index.checkout(test_file, force=True) - assert not open(test_file).read().endswith(append_data) + assert not open(test_file, 'rb').read().endswith(append_data) # checkout directory shutil.rmtree(os.path.join(rw_repo.working_tree_dir, "lib")) @@ -379,14 +379,15 @@ class TestIndex(TestBase): uname = "Some Developer" umail = "sd@company.com" - rw_repo.config_writer().set_value("user", "name", uname) - rw_repo.config_writer().set_value("user", "email", umail) + writer = rw_repo.config_writer() + writer.set_value("user", "name", uname) + writer.set_value("user", "email", umail) # remove all of the files, provide a wild mix of paths, BaseIndexEntries, # IndexEntries def mixed_iterator(): count = 0 - for entry in index.entries.itervalues(): + for entry in index.entries.values(): type_id = count % 4 if type_id == 0: # path yield entry.path @@ -500,7 +501,7 @@ class TestIndex(TestBase): # mode 0 not allowed null_hex_sha = Diff.NULL_HEX_SHA - null_bin_sha = "\0" * 20 + null_bin_sha = b"\0" * 20 self.failUnlessRaises(ValueError, index.reset( new_commit).add, [BaseIndexEntry((0, null_bin_sha, 0, "doesntmatter"))]) @@ -526,7 +527,7 @@ class TestIndex(TestBase): assert S_ISLNK(index.entries[index.entry_key("my_real_symlink", 0)].mode) # we expect only the target to be written - assert index.repo.odb.stream(entries[0].binsha).read() == target + assert index.repo.odb.stream(entries[0].binsha).read().decode('ascii') == target # END real symlink test # add fake symlink and assure it checks-our as symlink @@ -618,7 +619,7 @@ class TestIndex(TestBase): for fid in range(3): fname = 'newfile%i' % fid - open(fname, 'wb').write("abcd") + open(fname, 'wb').write(b"abcd") yield Blob(rw_repo, Blob.NULL_BIN_SHA, 0o100644, fname) # END for each new file # END path producer @@ -716,5 +717,5 @@ class TestIndex(TestBase): try: rw_bare_repo.index.add([path]) except Exception as e: - asserted = "does not have a working tree" in e.message + asserted = "does not have a working tree" in str(e) assert asserted, "Adding using a filename is not correctly asserted." diff --git a/git/util.py b/git/util.py index b3a22883..4de736d3 100644 --- a/git/util.py +++ b/git/util.py @@ -446,7 +446,7 @@ class IndexFileSHA1Writer(object): def __init__(self, f): self.f = f - self.sha1 = make_sha("") + self.sha1 = make_sha(b"") def write(self, data): self.sha1.update(data) @@ -490,10 +490,7 @@ class LockFile(object): def _has_lock(self): """:return: True if we have a lock and if the lockfile still exists :raise AssertionError: if our lock-file does not exist""" - if not self._owns_lock: - return False - - return True + return self._owns_lock def _obtain_lock_or_raise(self): """Create a lock file as flag for other instances, mark our instance as lock-holder @@ -531,7 +528,7 @@ class LockFile(object): # on bloody windows, the file needs write permissions to be removable. # Why ... if os.name == 'nt': - os.chmod(lfp, int("0777", 8)) + os.chmod(lfp, 0o777) # END handle win32 os.remove(lfp) except OSError: -- cgit v1.2.1 From b5a37564b6eec05b98c2efa5edcd1460a2df02aa Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 11:02:31 +0100 Subject: test_reflog works --- git/refs/log.py | 22 +++++++++++++--------- git/test/test_reflog.py | 5 +++-- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'git') diff --git a/git/refs/log.py b/git/refs/log.py index f397548e..c3019d39 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -19,7 +19,8 @@ from git.objects.util import ( ) from git.compat import ( xrange, - string_types + string_types, + defenc ) import time @@ -38,9 +39,8 @@ class RefLogEntry(tuple): def __repr__(self): """Representation of ourselves in git reflog format""" act = self.actor - name = act.name.encode('utf-8') time = self.time - return self._fmt % (self.oldhexsha, self.newhexsha, name, act.email, + return self._fmt % (self.oldhexsha, self.newhexsha, act.name, act.email, time[0], altz_to_utctz_str(time[1]), self.message) @property @@ -82,8 +82,9 @@ class RefLogEntry(tuple): @classmethod def from_line(cls, line): """:return: New RefLogEntry instance from the given revlog line. - :param line: line without trailing newline + :param line: line bytes without trailing newline :raise ValueError: If line could not be parsed""" + line = line.decode(defenc) try: info, msg = line.split('\t', 1) except ValueError: @@ -253,15 +254,18 @@ class RefLog(list, Serializable): # END handle sha type assure_directory_exists(filepath, is_file=True) committer = isinstance(config_reader, Actor) and config_reader or Actor.committer(config_reader) - entry = RefLogEntry( - (bin_to_hex(oldbinsha), bin_to_hex(newbinsha), committer, (int(time.time()), time.altzone), message)) + entry = RefLogEntry(( + bin_to_hex(oldbinsha).decode('ascii'), + bin_to_hex(newbinsha).decode('ascii'), + committer, (int(time.time()), time.altzone), message + )) lf = LockFile(filepath) lf._obtain_lock_or_raise() - fd = open(filepath, 'a') + fd = open(filepath, 'ab') try: - fd.write(repr(entry)) + fd.write(repr(entry).encode(defenc)) finally: fd.close() lf._release_lock() @@ -286,7 +290,7 @@ class RefLog(list, Serializable): # write all entries for e in self: - write(repr(e)) + write(repr(e).encode(defenc)) # END for each entry def _deserialize(self, stream): diff --git a/git/test/test_reflog.py b/git/test/test_reflog.py index 4efb8025..3571e083 100644 --- a/git/test/test_reflog.py +++ b/git/test/test_reflog.py @@ -8,6 +8,7 @@ from git.refs import ( RefLog ) from git.util import Actor +from gitdb.util import hex_to_bin import tempfile import shutil @@ -51,7 +52,7 @@ class TestRefLog(TestBase): assert len(reflog) # iter_entries works with path and with stream - assert len(list(RefLog.iter_entries(open(rlp_master)))) + assert len(list(RefLog.iter_entries(open(rlp_master, 'rb')))) assert len(list(RefLog.iter_entries(rlp_master))) # raise on invalid revlog @@ -65,7 +66,7 @@ class TestRefLog(TestBase): self.failUnlessRaises(ValueError, RefLog().write) # test serialize and deserialize - results must match exactly - binsha = chr(255) * 20 + binsha = hex_to_bin(('f' * 40).encode('ascii')) msg = "my reflog message" cr = self.rorepo.config_reader() for rlp in (rlp_head, rlp_master): -- cgit v1.2.1 From a83eee5bcee64eeb000dd413ab55aa98dcc8c7f2 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 11:14:19 +0100 Subject: test_refs works --- git/objects/tag.py | 6 ++++-- git/refs/symbolic.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'git') diff --git a/git/objects/tag.py b/git/objects/tag.py index 5e76e230..c8684447 100644 --- a/git/objects/tag.py +++ b/git/objects/tag.py @@ -10,6 +10,7 @@ from .util import ( parse_actor_and_date ) from gitdb.util import hex_to_bin +from git.compat import defenc __all__ = ("TagObject", ) @@ -52,11 +53,12 @@ class TagObject(base.Object): """Cache all our attributes at once""" if attr in TagObject.__slots__: ostream = self.repo.odb.stream(self.binsha) - lines = ostream.read().splitlines() + lines = ostream.read().decode(defenc).splitlines() obj, hexsha = lines[0].split(" ") # object type_token, type_name = lines[1].split(" ") # type - self.object = get_object_type_by_name(type_name)(self.repo, hex_to_bin(hexsha)) + self.object = \ + get_object_type_by_name(type_name.encode('ascii'))(self.repo, hex_to_bin(hexsha)) self.tag = lines[2][4:] # tag diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 252462a9..45a12385 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -21,6 +21,7 @@ from gitdb.util import ( ) from git.compat import ( string_types, + defenc ) from .log import RefLog @@ -429,6 +430,7 @@ class SymbolicReference(object): # in the line # If we deleted the last line and this one is a tag-reference object, # we drop it as well + line = line.decode(defenc) if (line.startswith('#') or full_ref_path not in line) and \ (not dropped_last_line or dropped_last_line and not line.startswith('^')): new_lines.append(line) @@ -446,7 +448,7 @@ class SymbolicReference(object): if made_change: # write-binary is required, otherwise windows will # open the file in text mode and change LF to CRLF ! - open(pack_file_path, 'wb').writelines(new_lines) + open(pack_file_path, 'wb').writelines(l.encode(defenc) for l in new_lines) # END write out file # END open exception handling # END handle deletion @@ -478,7 +480,7 @@ class SymbolicReference(object): target_data = target.path if not resolve: target_data = "ref: " + target_data - existing_data = open(abs_ref_path, 'rb').read().strip() + existing_data = open(abs_ref_path, 'rb').read().decode(defenc).strip() if existing_data != target_data: raise OSError("Reference at %r does already exist, pointing to %r, requested was %r" % (full_ref_path, existing_data, target_data)) -- cgit v1.2.1 From 60e54133aa1105a1270f0a42e74813f75cd2dc46 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 12:22:16 +0100 Subject: test_remote works And I have to wonder why git-daemon serves under py2.7, but really wants receive-pack to be allowed under 3.4. Maybe it's a repository override which for some reason doesn't work in py3.4 ? Maybe because the change is not flushed ? --- git/cmd.py | 2 +- git/exc.py | 10 ++++++---- git/remote.py | 10 ++++++---- git/repo/fun.py | 2 +- git/test/lib/helper.py | 11 +++++++---- 5 files changed, 21 insertions(+), 14 deletions(-) (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index 2bff3310..aac7ffb7 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -120,7 +120,7 @@ class Git(LazyMixin): :raise GitCommandError: if the return status is not 0""" status = self.proc.wait() if status != 0: - raise GitCommandError(self.args, status, self.proc.stderr.read()) + raise GitCommandError(self.args, status, self.proc.stderr.read().decode(defenc)) # END status handling return status # END auto interrupt diff --git a/git/exc.py b/git/exc.py index ba57c624..67fb9894 100644 --- a/git/exc.py +++ b/git/exc.py @@ -29,10 +29,12 @@ class GitCommandError(Exception): self.command = command def __str__(self): - ret = "'%s' returned exit status %i: %s" % \ - (' '.join(str(i) for i in self.command), self.status, self.stderr) - if self.stdout is not None: - ret += "\nstdout: %s" % self.stdout + ret = "'%s' returned with exit code %i" % \ + (' '.join(str(i) for i in self.command), self.status) + if self.stderr: + ret += "\nstderr: '%s'" % self.stderr + if self.stdout: + ret += "\nstdout: '%s'" % self.stdout return ret diff --git a/git/remote.py b/git/remote.py index 63f21c4e..484bc031 100644 --- a/git/remote.py +++ b/git/remote.py @@ -138,7 +138,7 @@ class PushInfo(object): @classmethod def _from_line(cls, remote, line): """Create a new PushInfo instance as parsed from line which is expected to be like - refs/heads/master:refs/heads/master 05d2687..1d0568e""" + refs/heads/master:refs/heads/master 05d2687..1d0568e as bytes""" control_character, from_to, summary = line.split('\t', 3) flags = 0 @@ -522,6 +522,7 @@ class Remote(LazyMixin, Iterable): def _get_fetch_info_from_stderr(self, proc, progress): # skip first line as it is some remote info we are not interested in + # TODO: Use poll() to process stdout and stderr at same time output = IterableList('name') # lines which are no progress are fetch info lines @@ -544,8 +545,8 @@ class Remote(LazyMixin, Iterable): # END for each line # read head information - fp = open(join(self.repo.git_dir, 'FETCH_HEAD'), 'r') - fetch_head_info = fp.readlines() + fp = open(join(self.repo.git_dir, 'FETCH_HEAD'), 'rb') + fetch_head_info = [l.decode(defenc) for l in fp.readlines()] fp.close() # NOTE: We assume to fetch at least enough progress lines to allow matching each fetch head line with it. @@ -562,10 +563,12 @@ class Remote(LazyMixin, Iterable): # we hope stdout can hold all the data, it should ... # read the lines manually as it will use carriage returns between the messages # to override the previous one. This is why we read the bytes manually + # TODO: poll() on file descriptors to know what to read next, process streams concurrently digest_process_messages(proc.stderr, progress) output = IterableList('name') for line in proc.stdout.readlines(): + line = line.decode(defenc) try: output.append(PushInfo._from_line(self, line)) except ValueError: @@ -573,7 +576,6 @@ class Remote(LazyMixin, Iterable): pass # END exception handling # END for each line - finalize_process(proc) return output diff --git a/git/repo/fun.py b/git/repo/fun.py index 64b9b4a9..ac0fa6f1 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -21,7 +21,7 @@ __all__ = ('rev_parse', 'is_git_dir', 'touch', 'read_gitfile', 'find_git_dir', ' def touch(filename): - fp = open(filename, "a") + fp = open(filename, "ab") fp.close() diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index bc9c351a..eea594e7 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -193,7 +193,7 @@ def with_rw_and_rw_remote_repo(working_tree_ref): temp_dir = os.path.dirname(_mktemp()) # On windows, this will fail ... we deal with failures anyway and default to telling the user to do it try: - gd = Git().daemon(temp_dir, as_process=True) + gd = Git().daemon(temp_dir, enable='receive-pack', as_process=True) # yes, I know ... fortunately, this is always going to work if sleep time is just large enough time.sleep(0.5) except Exception: @@ -215,7 +215,8 @@ def with_rw_and_rw_remote_repo(working_tree_ref): msg += 'Otherwise, run: git-daemon "%s"' % temp_dir raise AssertionError(msg) else: - msg = 'Please start a git-daemon to run this test, execute: git-daemon "%s"' % temp_dir + msg = 'Please start a git-daemon to run this test, execute: git daemon --enable=receive-pack "%s"' + msg %= temp_dir raise AssertionError(msg) # END make assertion # END catch ls remote error @@ -227,7 +228,8 @@ def with_rw_and_rw_remote_repo(working_tree_ref): return func(self, rw_repo, rw_remote_repo) finally: # gd.proc.kill() ... no idea why that doesn't work - os.kill(gd.proc.pid, 15) + if gd is not None: + os.kill(gd.proc.pid, 15) os.chdir(prev_cwd) rw_repo.git.clear_cache() @@ -235,7 +237,8 @@ def with_rw_and_rw_remote_repo(working_tree_ref): shutil.rmtree(repo_dir, onerror=_rmtree_onerror) shutil.rmtree(remote_repo_dir, onerror=_rmtree_onerror) - gd.proc.wait() + if gd is not None: + gd.proc.wait() # END cleanup # END bare repo creator remote_repo_creator.__name__ = func.__name__ -- cgit v1.2.1 From a05e49d2419d65c59c65adf5cd8c05f276550e1d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:05:30 +0100 Subject: test_repo works --- git/cmd.py | 17 ++++++++++------- git/config.py | 14 ++++++++++++-- git/exc.py | 6 ++++-- git/objects/submodule/base.py | 31 +++++++++++++++++++++++-------- git/refs/head.py | 1 + git/repo/base.py | 5 +++-- git/repo/fun.py | 2 +- git/test/lib/helper.py | 1 + git/test/test_index.py | 1 + git/test/test_refs.py | 6 ++++-- git/test/test_repo.py | 28 ++++++++++++++++++---------- git/test/test_submodule.py | 38 +++++++++++++++++++++++++++----------- 12 files changed, 105 insertions(+), 45 deletions(-) (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index aac7ffb7..9b85b14a 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -22,7 +22,8 @@ from .exc import GitCommandError from git.compat import ( text_type, string_types, - defenc + defenc, + PY3 ) execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', @@ -372,8 +373,8 @@ class Git(LazyMixin): # Wait for the process to return status = 0 - stdout_value = '' - stderr_value = '' + stdout_value = b'' + stderr_value = b'' try: if output_stream is None: stdout_value, stderr_value = proc.communicate() @@ -388,7 +389,7 @@ class Git(LazyMixin): stdout_value = output_stream stderr_value = proc.stderr.read() # strip trailing "\n" - if stderr_value.endswith("\n"): + if stderr_value.endswith(b"\n"): stderr_value = stderr_value[:-1] status = proc.wait() # END stdout handling @@ -444,7 +445,9 @@ class Git(LazyMixin): @classmethod def __unpack_args(cls, arg_list): if not isinstance(arg_list, (list, tuple)): - if isinstance(arg_list, text_type): + # This is just required for unicode conversion, as subprocess can't handle it + # However, in any other case, passing strings (usually utf-8 encoded) is totally fine + if not PY3 and isinstance(arg_list, unicode): return [arg_list.encode(defenc)] return [str(arg_list)] @@ -452,7 +455,7 @@ class Git(LazyMixin): for arg in arg_list: if isinstance(arg_list, (list, tuple)): outlist.extend(cls.__unpack_args(arg)) - elif isinstance(arg_list, text_type): + elif not PY3 and isinstance(arg_list, unicode): outlist.append(arg_list.encode(defenc)) # END recursion else: @@ -509,8 +512,8 @@ class Git(LazyMixin): # Prepare the argument list opt_args = self.transform_kwargs(**kwargs) - ext_args = self.__unpack_args([a for a in args if a is not None]) + args = opt_args + ext_args def make_call(): diff --git a/git/config.py b/git/config.py index 96991b84..c7e8b7ac 100644 --- a/git/config.py +++ b/git/config.py @@ -103,8 +103,7 @@ class SectionConstraint(object): # Yes, for some reason, we have to call it explicitly for it to work in PY3 ! # Apparently __del__ doesn't get call anymore if refcount becomes 0 # Ridiculous ... . - self._config.__del__() - # del self._config + self._config.release() def __getattr__(self, attr): if attr in self._valid_attrs_: @@ -121,6 +120,10 @@ class SectionConstraint(object): """return: Configparser instance we constrain""" return self._config + def release(self): + """Equivalent to GitConfigParser.release(), which is called on our underlying parser instance""" + return self._config.release() + class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): @@ -198,6 +201,13 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje def __del__(self): """Write pending changes if required and release locks""" + # NOTE: only consistent in PY2 + self.release() + + def release(self): + """Flush changes and release the configuration write lock. This instance must not be used anymore afterwards. + In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called + deterministically anymore.""" # checking for the lock here makes sure we do not raise during write() # in case an invalid parser was created who could not get a lock if self.read_only or (self._lock and not self._lock._has_lock()): diff --git a/git/exc.py b/git/exc.py index 67fb9894..42191c62 100644 --- a/git/exc.py +++ b/git/exc.py @@ -7,6 +7,8 @@ from gitdb.exc import * # NOQA +from git.compat import defenc + class InvalidGitRepositoryError(Exception): @@ -32,9 +34,9 @@ class GitCommandError(Exception): ret = "'%s' returned with exit code %i" % \ (' '.join(str(i) for i in self.command), self.status) if self.stderr: - ret += "\nstderr: '%s'" % self.stderr + ret += "\nstderr: '%s'" % self.stderr.decode(defenc) if self.stdout: - ret += "\nstdout: '%s'" % self.stdout + ret += "\nstdout: '%s'" % self.stdout.decode(defenc) return ret diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 0fb3f35f..0ec6f656 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -17,7 +17,10 @@ from git.util import ( rmtree ) -from git.config import SectionConstraint +from git.config import ( + SectionConstraint, + cp +) from git.exc import ( InvalidGitRepositoryError, NoSuchPathError @@ -302,6 +305,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): writer.set_value(cls.k_head_option, br.path) sm._branch_path = br.path # END handle path + writer.release() del(writer) # we deliberatly assume that our head matches our index ! @@ -419,7 +423,9 @@ class Submodule(util.IndexObject, Iterable, Traversable): # the default implementation will be offended and not update the repository # Maybe this is a good way to assure it doesn't get into our way, but # we want to stay backwards compatible too ... . Its so redundant ! - self.repo.config_writer().set_value(sm_section(self.name), 'url', self.url) + writer = self.repo.config_writer() + writer.set_value(sm_section(self.name), 'url', self.url) + writer.release() # END handle dry_run # END handle initalization @@ -576,6 +582,7 @@ class Submodule(util.IndexObject, Iterable, Traversable): writer = self.config_writer(index=index) # auto-write writer.set_value('path', module_path) self.path = module_path + writer.release() del(writer) # END handle configuration flag except Exception: @@ -700,8 +707,12 @@ class Submodule(util.IndexObject, Iterable, Traversable): # now git config - need the config intact, otherwise we can't query # inforamtion anymore - self.repo.config_writer().remove_section(sm_section(self.name)) - self.config_writer().remove_section() + writer = self.repo.config_writer() + writer.remove_section(sm_section(self.name)) + writer.release() + writer = self.config_writer() + writer.remove_section() + writer.release() # END delete configuration # void our data not to delay invalid access @@ -800,14 +811,18 @@ class Submodule(util.IndexObject, Iterable, Traversable): """ :return: True if the submodule exists, False otherwise. Please note that a submodule may exist (in the .gitmodules file) even though its module - doesn't exist""" + doesn't exist on disk""" # keep attributes for later, and restore them if we have no valid data # this way we do not actually alter the state of the object loc = locals() for attr in self._cache_attrs: - if hasattr(self, attr): - loc[attr] = getattr(self, attr) - # END if we have the attribute cache + try: + if hasattr(self, attr): + loc[attr] = getattr(self, attr) + # END if we have the attribute cache + except cp.NoSectionError: + # on PY3, this can happen apparently ... don't know why this doesn't happen on PY2 + pass # END for each attr self._clear_cache() diff --git a/git/refs/head.py b/git/refs/head.py index 0a14158c..750d15b6 100644 --- a/git/refs/head.py +++ b/git/refs/head.py @@ -148,6 +148,7 @@ class Head(Reference): writer.set_value(self.k_config_remote, remote_reference.remote_name) writer.set_value(self.k_config_remote_ref, Head.to_full_path(remote_reference.remote_head)) # END handle ref value + writer.release() return self diff --git a/git/repo/base.py b/git/repo/base.py index 3e0e51cc..2a63492b 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -551,6 +551,7 @@ class Repo(object): prefix = "?? " untracked_files = list() for line in proc.stdout: + line = line.decode(defenc) if not line.startswith(prefix): continue filename = line[len(prefix):].rstrip('\n') @@ -735,7 +736,7 @@ class Repo(object): writer = repo.remotes[0].config_writer writer.set_value('url', repo.remotes[0].url.replace("\\\\", "\\").replace("\\", "/")) # PY3: be sure cleanup is performed and lock is released - del writer + writer.release() # END handle remote repo return repo @@ -767,7 +768,7 @@ class Repo(object): def archive(self, ostream, treeish=None, prefix=None, **kwargs): """Archive the tree at the given revision. - :parm ostream: file compatible stream object to which the archive will be written + :parm ostream: file compatible stream object to which the archive will be written as bytes :parm treeish: is the treeish name/id, defaults to active branch :parm prefix: is the optional prefix to prepend to each filename in the archive :parm kwargs: diff --git a/git/repo/fun.py b/git/repo/fun.py index ac0fa6f1..233666c9 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -150,7 +150,7 @@ def to_commit(obj): def rev_parse(repo, rev): """ :return: Object at the given revision, either Commit, Tag, Tree or Blob - :param rev: git-rev-parse compatible revision specification, please see + :param rev: git-rev-parse compatible revision specification as string, please see http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html for details :note: Currently there is no access to the rev-log, rev-specs may only contain diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py index eea594e7..bd679512 100644 --- a/git/test/lib/helper.py +++ b/git/test/lib/helper.py @@ -179,6 +179,7 @@ def with_rw_and_rw_remote_repo(working_tree_ref): pass crw.set(section, "receivepack", True) # release lock + crw.release() del(crw) # initialize the remote - first do it as local remote and pull, then diff --git a/git/test/test_index.py b/git/test/test_index.py index d81d08ef..f7504b32 100644 --- a/git/test/test_index.py +++ b/git/test/test_index.py @@ -382,6 +382,7 @@ class TestIndex(TestBase): writer = rw_repo.config_writer() writer.set_value("user", "name", uname) writer.set_value("user", "email", umail) + writer.release() # remove all of the files, provide a wild mix of paths, BaseIndexEntries, # IndexEntries diff --git a/git/test/test_refs.py b/git/test/test_refs.py index af33765a..14b91cfe 100644 --- a/git/test/test_refs.py +++ b/git/test/test_refs.py @@ -105,9 +105,11 @@ class TestRefs(TestBase): tv = "testopt" writer.set_value(tv, 1) assert writer.get_value(tv) == 1 - del(writer) + writer.release() assert head.config_reader().get_value(tv) == 1 - head.config_writer().remove_option(tv) + writer = head.config_writer() + writer.remove_option(tv) + writer.release() # after the clone, we might still have a tracking branch setup head.set_tracking_branch(None) diff --git a/git/test/test_repo.py b/git/test/test_repo.py index ae824086..f216039e 100644 --- a/git/test/test_repo.py +++ b/git/test/test_repo.py @@ -30,7 +30,10 @@ from git import ( from git.util import join_path_native from git.exc import BadObject from gitdb.util import bin_to_hex -from git.compat import string_types +from git.compat import ( + string_types, + defenc +) import os import sys @@ -259,13 +262,16 @@ class TestRepo(TestBase): assert self.rorepo.tag('refs/tags/0.1.5').commit def test_archive(self): - tmpfile = os.tmpfile() - self.rorepo.archive(tmpfile, '0.1.5') - assert tmpfile.tell() + tmpfile = tempfile.mktemp(suffix='archive-test') + stream = open(tmpfile, 'wb') + self.rorepo.archive(stream, '0.1.5') + assert stream.tell() + stream.close() + os.remove(tmpfile) @patch.object(Git, '_call_process') def test_should_display_blame_information(self, git): - git.return_value = fixture('blame') + git.return_value = fixture('blame').decode(defenc) b = self.rorepo.blame('master', 'lib/git.py') assert_equal(13, len(b)) assert_equal(2, len(b[0])) @@ -336,6 +342,7 @@ class TestRepo(TestBase): try: writer = self.rorepo.config_writer(config_level) assert not writer.read_only + writer.release() except IOError: # its okay not to get a writer for some configuration files if we # have no permissions @@ -350,7 +357,8 @@ class TestRepo(TestBase): tag = self.rorepo.create_tag("new_tag", "HEAD~2") self.rorepo.delete_tag(tag) - self.rorepo.config_writer() + writer = self.rorepo.config_writer() + writer.release() remote = self.rorepo.create_remote("new_remote", "git@server:repo.git") self.rorepo.delete_remote(remote) @@ -365,7 +373,7 @@ class TestRepo(TestBase): l1 = b"0123456789\n" l2 = b"abcdefghijklmnopqrstxy\n" l3 = b"z\n" - d = b"%s%s%s\n" % (l1, l2, l3) + d = l1 + l2 + l3 + b"\n" l1p = l1[:5] @@ -382,7 +390,7 @@ class TestRepo(TestBase): # readlines no limit s = mkfull() lines = s.readlines() - assert len(lines) == 3 and lines[-1].endswith('\n') + assert len(lines) == 3 and lines[-1].endswith(b'\n') assert s._stream.tell() == len(d) # must have scrubbed to the end # realines line limit @@ -566,7 +574,7 @@ class TestRepo(TestBase): # try partial parsing max_items = 40 for i, binsha in enumerate(self.rorepo.odb.sha_iter()): - assert rev_parse(bin_to_hex(binsha)[:8 - (i % 2)]).binsha == binsha + assert rev_parse(bin_to_hex(binsha)[:8 - (i % 2)].decode('ascii')).binsha == binsha if i > max_items: # this is rather slow currently, as rev_parse returns an object # which requires accessing packs, it has some additional overhead @@ -645,6 +653,6 @@ class TestRepo(TestBase): assert os.path.abspath(git_file_repo.git_dir) == real_path_abs # Test using an absolute gitdir path in the .git file. - open(git_file_path, 'wb').write('gitdir: %s\n' % real_path_abs) + open(git_file_path, 'wb').write(('gitdir: %s\n' % real_path_abs).encode('ascii')) git_file_repo = Repo(rwrepo.working_tree_dir) assert os.path.abspath(git_file_repo.git_dir) == real_path_abs diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index 8c1580fe..499d6bac 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -99,7 +99,7 @@ class TestSubmodule(TestBase): # for faster checkout, set the url to the local path new_smclone_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path)) writer.set_value('url', new_smclone_path) - del(writer) + writer.release() assert sm.config_reader().get_value('url') == new_smclone_path assert sm.url == new_smclone_path # END handle bare repo @@ -196,7 +196,9 @@ class TestSubmodule(TestBase): # adjust the path of the submodules module to point to the local destination new_csmclone_path = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path, csm.path)) - csm.config_writer().set_value('url', new_csmclone_path) + writer = csm.config_writer() + writer.set_value('url', new_csmclone_path) + writer.release() assert csm.url == new_csmclone_path # dry-run does nothing @@ -257,8 +259,12 @@ class TestSubmodule(TestBase): # NOTE: As we did a few updates in the meanwhile, the indices were reset # Hence we create some changes csm.set_parent_commit(csm.repo.head.commit) - sm.config_writer().set_value("somekey", "somevalue") - csm.config_writer().set_value("okey", "ovalue") + writer = sm.config_writer() + writer.set_value("somekey", "somevalue") + writer.release() + writer = csm.config_writer() + writer.set_value("okey", "ovalue") + writer.release() self.failUnlessRaises(InvalidGitRepositoryError, sm.remove) # if we remove the dirty index, it would work sm.module().index.reset() @@ -406,7 +412,8 @@ class TestSubmodule(TestBase): assert len(rm.list_items(rm.module())) == 1 rm.config_reader() - rm.config_writer() + w = rm.config_writer() + w.release() # deep traversal gitdb / async rsmsp = [sm.path for sm in rm.traverse()] @@ -431,8 +438,9 @@ class TestSubmodule(TestBase): assert not sm.module_exists() # was never updated after rwrepo's clone # assure we clone from a local source - sm.config_writer().set_value( - 'url', to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path))) + writer = sm.config_writer() + writer.set_value('url', to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, sm.path))) + writer.release() # dry-run does nothing sm.update(recursive=False, dry_run=True, progress=prog) @@ -440,7 +448,9 @@ class TestSubmodule(TestBase): sm.update(recursive=False) assert sm.module_exists() - sm.config_writer().set_value('path', fp) # change path to something with prefix AFTER url change + writer = sm.config_writer() + writer.set_value('path', fp) # change path to something with prefix AFTER url change + writer.release() # update fails as list_items in such a situations cannot work, as it cannot # find the entry at the changed path @@ -504,7 +514,9 @@ class TestSubmodule(TestBase): # repository at the different url nsm.set_parent_commit(csmremoved) nsmurl = to_native_path_linux(join_path_native(self.rorepo.working_tree_dir, rsmsp[0])) - nsm.config_writer().set_value('url', nsmurl) + writer = nsm.config_writer() + writer.set_value('url', nsmurl) + writer.release() csmpathchange = rwrepo.index.commit("changed url") nsm.set_parent_commit(csmpathchange) @@ -532,7 +544,9 @@ class TestSubmodule(TestBase): nsmm = nsm.module() prev_commit = nsmm.head.commit for branch in ("some_virtual_branch", cur_branch.name): - nsm.config_writer().set_value(Submodule.k_head_option, git.Head.to_full_path(branch)) + writer = nsm.config_writer() + writer.set_value(Submodule.k_head_option, git.Head.to_full_path(branch)) + writer.release() csmbranchchange = rwrepo.index.commit("changed branch to %s" % branch) nsm.set_parent_commit(csmbranchchange) # END for each branch to change @@ -560,7 +574,9 @@ class TestSubmodule(TestBase): assert nsm.exists() and nsm.module_exists() and len(nsm.children()) >= 1 # assure we pull locally only nsmc = nsm.children()[0] - nsmc.config_writer().set_value('url', async_url) + writer = nsmc.config_writer() + writer.set_value('url', async_url) + writer.release() rm.update(recursive=True, progress=prog, dry_run=True) # just to run the code rm.update(recursive=True, progress=prog) -- cgit v1.2.1 From 8f219b53f339fc0133800fac96deaf75eb4f9bf6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:09:17 +0100 Subject: test_stat works --- git/test/test_stats.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git') diff --git a/git/test/test_stats.py b/git/test/test_stats.py index c4535b75..eb8b9dda 100644 --- a/git/test/test_stats.py +++ b/git/test/test_stats.py @@ -10,12 +10,12 @@ from git.test.lib import ( assert_equal ) from git import Stats - +from git.compat import defenc class TestStats(TestBase): def test_list_from_string(self): - output = fixture('diff_numstat') + output = fixture('diff_numstat').decode(defenc) stats = Stats._list_from_string(self.rorepo, output) assert_equal(2, stats.total['files']) -- cgit v1.2.1 From 4cfc682ff87d3629b31e0196004d1954810b206c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:11:48 +0100 Subject: test_submodule works --- git/objects/base.py | 2 +- git/test/test_base.py | 2 +- git/test/test_submodule.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'git') diff --git a/git/objects/base.py b/git/objects/base.py index 004e3981..3f595d9d 100644 --- a/git/objects/base.py +++ b/git/objects/base.py @@ -60,7 +60,7 @@ class Object(LazyMixin): :param sha1: 20 byte binary sha1""" if sha1 == cls.NULL_BIN_SHA: # the NULL binsha is always the root commit - return get_object_type_by_name('commit')(repo, sha1) + return get_object_type_by_name(b'commit')(repo, sha1) # END handle special case oinfo = repo.odb.info(sha1) inst = get_object_type_by_name(oinfo.type)(repo, oinfo.binsha) diff --git a/git/test/test_base.py b/git/test/test_base.py index edacbd80..9ae2dd2b 100644 --- a/git/test/test_base.py +++ b/git/test/test_base.py @@ -86,7 +86,7 @@ class TestBase(TestBase): assert base.Object in get_object_type_by_name(tname).mro() # END for each known type - assert_raises(ValueError, get_object_type_by_name, "doesntexist") + assert_raises(ValueError, get_object_type_by_name, b"doesntexist") def test_object_resolution(self): # objects must be resolved to shas so they compare equal diff --git a/git/test/test_submodule.py b/git/test/test_submodule.py index 499d6bac..99996ce3 100644 --- a/git/test/test_submodule.py +++ b/git/test/test_submodule.py @@ -80,7 +80,7 @@ class TestSubmodule(TestBase): assert isinstance(sm.branch_path, string_types) # some commits earlier we still have a submodule, but its at a different commit - smold = Submodule.iter_items(rwrepo, self.k_subm_changed).next() + smold = next(Submodule.iter_items(rwrepo, self.k_subm_changed)) assert smold.binsha != sm.binsha assert smold != sm # the name changed -- cgit v1.2.1 From 95bb489a50f6c254f49ba4562d423dbf2201554f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:20:58 +0100 Subject: test_tree works --- git/objects/tree.py | 12 ++++++++++-- git/test/test_tree.py | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'git') diff --git a/git/objects/tree.py b/git/objects/tree.py index 6776a15e..f9bee01e 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -159,7 +159,7 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): raise TypeError("Unknown mode %o found in tree data for path '%s'" % (mode, path)) # END for each item - def __div__(self, file): + def join(self, file): """Find the named object in this tree's contents :return: ``git.Blob`` or ``git.Tree`` or ``git.Submodule`` @@ -192,6 +192,14 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): raise KeyError(msg % file) # END handle long paths + def __div__(self, file): + """For PY2 only""" + return self.join(file) + + def __truediv__(self, file): + """For PY3 only""" + return self.join(file) + @property def trees(self): """:return: list(Tree, ...) list of trees directly below this tree""" @@ -235,7 +243,7 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): if isinstance(item, string_types): # compatability - return self.__div__(item) + return self.join(item) # END index is basestring raise TypeError("Invalid index type: %r" % item) diff --git a/git/test/test_tree.py b/git/test/test_tree.py index 72bda485..246584ee 100644 --- a/git/test/test_tree.py +++ b/git/test/test_tree.py @@ -138,6 +138,7 @@ class TestTree(TestBase): # END check for slash # slashes in paths are supported as well + # NOTE: on py3, / doesn't work with strings anymore ... assert root[item.path] == item == root / item.path # END for each item assert found_slash -- cgit v1.2.1 From 725bde98d5cf680a087b6cb47a11dc469cfe956c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:28:32 +0100 Subject: test_base works --- git/test/test_base.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'git') diff --git a/git/test/test_base.py b/git/test/test_base.py index 9ae2dd2b..301384ef 100644 --- a/git/test/test_base.py +++ b/git/test/test_base.py @@ -4,10 +4,10 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - -import git.objects.base as base import os +import tempfile +import git.objects.base as base from git.test.lib import ( TestBase, assert_raises, @@ -69,10 +69,13 @@ class TestBase(TestBase): data = data_stream.read() assert data - tmpfile = os.tmpfile() + tmpfilename = tempfile.mktemp(suffix='test-stream') + tmpfile = open(tmpfilename, 'wb+') assert item == item.stream_data(tmpfile) tmpfile.seek(0) assert tmpfile.read() == data + tmpfile.close() + os.remove(tmpfilename) # END stream to file directly # END for each object type to create @@ -113,7 +116,7 @@ class TestBase(TestBase): filename = u"שלום.txt" file_path = os.path.join(rw_repo.working_dir, filename) - open(file_path, "wb").write('something') + open(file_path, "wb").write(b'something') rw_repo.git.add(rw_repo.working_dir) rw_repo.index.commit('message') -- cgit v1.2.1 From 02e942b7c603163c87509195d76b2117c4997119 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:29:46 +0100 Subject: test_streams works However, there is a performance regression in test-odb --- git/test/performance/test_streams.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git') diff --git a/git/test/performance/test_streams.py b/git/test/performance/test_streams.py index ff664c10..aecb7728 100644 --- a/git/test/performance/test_streams.py +++ b/git/test/performance/test_streams.py @@ -80,7 +80,7 @@ class TestObjDBPerformance(TestBigRepoR): elapsed_readchunks = time() - st stream.seek(0) - assert ''.join(chunks) == stream.getvalue() + assert b''.join(chunks) == stream.getvalue() cs_kib = cs / 1000 print("Read %i KiB of %s data in %i KiB chunks from loose odb in %f s ( %f Read KiB / s)" -- cgit v1.2.1 From e0c65d6638698f4e3a9e726efca8c0bcf466cd62 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 15:38:20 +0100 Subject: Make flake8 happy --- git/cmd.py | 7 +++---- git/config.py | 4 ++-- git/objects/commit.py | 8 ++++---- git/objects/fun.py | 1 + git/refs/log.py | 2 +- git/refs/symbolic.py | 2 +- git/test/performance/test_commit.py | 1 - git/test/test_commit.py | 1 - git/test/test_config.py | 1 + git/test/test_stats.py | 1 + git/test/test_tree.py | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) (limited to 'git') diff --git a/git/cmd.py b/git/cmd.py index 9b85b14a..7a670c46 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -20,7 +20,6 @@ from .util import ( ) from .exc import GitCommandError from git.compat import ( - text_type, string_types, defenc, PY3 @@ -400,7 +399,8 @@ class Git(LazyMixin): if self.GIT_PYTHON_TRACE == 'full': cmdstr = " ".join(command) if stderr_value: - log.info("%s -> %d; stdout: '%s'; stderr: '%s'", cmdstr, status, stdout_value.decode(defenc), stderr_value.decode(defenc)) + log.info("%s -> %d; stdout: '%s'; stderr: '%s'", + cmdstr, status, stdout_value.decode(defenc), stderr_value.decode(defenc)) elif stdout_value: log.info("%s -> %d; stdout: '%s'", cmdstr, status, stdout_value.decode(defenc)) else: @@ -413,8 +413,7 @@ class Git(LazyMixin): else: raise GitCommandError(command, status, stderr_value) - - if isinstance(stdout_value, bytes): # could also be output_stream + if isinstance(stdout_value, bytes): # could also be output_stream stdout_value = stdout_value.decode(defenc) # Allow access to the command's status code diff --git a/git/config.py b/git/config.py index c7e8b7ac..eefab299 100644 --- a/git/config.py +++ b/git/config.py @@ -178,7 +178,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje # Used in python 3, needs to stay in sync with sections for underlying implementation to work if not hasattr(self, '_proxies'): self._proxies = self._dict() - + self._file_or_files = file_or_files self._read_only = read_only self._is_initialized = False @@ -206,7 +206,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje def release(self): """Flush changes and release the configuration write lock. This instance must not be used anymore afterwards. - In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called + In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called deterministically anymore.""" # checking for the lock here makes sure we do not raise during write() # in case an invalid parser was created who could not get a lock diff --git a/git/objects/commit.py b/git/objects/commit.py index 53af22cd..8f93d1b9 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -382,14 +382,14 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): c = self.committer fmt = "%s %s <%s> %s %s\n" write((fmt % ("author", aname, a.email, - self.authored_date, - altz_to_utctz_str(self.author_tz_offset))).encode(self.encoding)) + self.authored_date, + altz_to_utctz_str(self.author_tz_offset))).encode(self.encoding)) # encode committer aname = c.name write((fmt % ("committer", aname, c.email, - self.committed_date, - altz_to_utctz_str(self.committer_tz_offset))).encode(self.encoding)) + self.committed_date, + altz_to_utctz_str(self.committer_tz_offset))).encode(self.encoding)) if self.encoding != self.default_encoding: write(("encoding %s\n" % self.encoding).encode('ascii')) diff --git a/git/objects/fun.py b/git/objects/fun.py index ba8dbcf4..c04f80b5 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -40,6 +40,7 @@ def tree_to_stream(entries, write): write(b''.join((mode_str, b' ', name, b'\0', binsha))) # END for each item + def tree_entries_from_data(data): """Reads the binary representation of a tree and returns tuples of Tree items :param data: data block with tree data (as bytes) diff --git a/git/refs/log.py b/git/refs/log.py index c3019d39..69189b51 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -256,7 +256,7 @@ class RefLog(list, Serializable): committer = isinstance(config_reader, Actor) and config_reader or Actor.committer(config_reader) entry = RefLogEntry(( bin_to_hex(oldbinsha).decode('ascii'), - bin_to_hex(newbinsha).decode('ascii'), + bin_to_hex(newbinsha).decode('ascii'), committer, (int(time.time()), time.altzone), message )) diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 45a12385..cbb129d4 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -144,7 +144,7 @@ class SymbolicReference(object): for sha, path in cls._iter_packed_refs(repo): if path != ref_path: continue - # sha will be used as + # sha will be used tokens = sha, path break # END for each packed ref diff --git a/git/test/performance/test_commit.py b/git/test/performance/test_commit.py index 9e8c1325..7d3e87c4 100644 --- a/git/test/performance/test_commit.py +++ b/git/test/performance/test_commit.py @@ -15,7 +15,6 @@ from git.compat import xrange from git.test.test_commit import assert_commit_serialization - class TestPerformance(TestBigRepoRW): # ref with about 100 commits in its history diff --git a/git/test/test_commit.py b/git/test/test_commit.py index 37a54092..1f0f8c56 100644 --- a/git/test/test_commit.py +++ b/git/test/test_commit.py @@ -19,7 +19,6 @@ from git import ( Actor, ) from gitdb import IStream -from gitdb.util import hex_to_bin from git.compat import ( string_types, text_type diff --git a/git/test/test_config.py b/git/test/test_config.py index f02754d5..546a2fe1 100644 --- a/git/test/test_config.py +++ b/git/test/test_config.py @@ -18,6 +18,7 @@ import io from copy import copy from git.config import cp + class TestBase(TestCase): def _to_memcache(self, file_path): diff --git a/git/test/test_stats.py b/git/test/test_stats.py index eb8b9dda..884ab1ab 100644 --- a/git/test/test_stats.py +++ b/git/test/test_stats.py @@ -12,6 +12,7 @@ from git.test.lib import ( from git import Stats from git.compat import defenc + class TestStats(TestBase): def test_list_from_string(self): diff --git a/git/test/test_tree.py b/git/test/test_tree.py index 246584ee..7a16b777 100644 --- a/git/test/test_tree.py +++ b/git/test/test_tree.py @@ -138,7 +138,7 @@ class TestTree(TestBase): # END check for slash # slashes in paths are supported as well - # NOTE: on py3, / doesn't work with strings anymore ... + # NOTE: on py3, / doesn't work with strings anymore ... assert root[item.path] == item == root / item.path # END for each item assert found_slash -- cgit v1.2.1 From 1527b5734c0f2821fd6f38c1a1d70723a4bb88ab Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 15:40:35 +0100 Subject: fixed test_git --- git/test/test_git.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git') diff --git a/git/test/test_git.py b/git/test/test_git.py index 8087d6f8..532e6659 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -34,11 +34,11 @@ class TestGit(TestBase): def test_call_unpack_args_unicode(self): args = Git._Git__unpack_args(u'Unicode€™') - assert_equal(args, [b'Unicode\xe2\x82\xac\xe2\x84\xa2']) + assert_equal(args, ['Unicode\u20ac\u2122']) def test_call_unpack_args(self): args = Git._Git__unpack_args(['git', 'log', '--', u'Unicode€™']) - assert_equal(args, [b'git', b'log', b'--', b'Unicode\xe2\x82\xac\xe2\x84\xa2']) + assert_equal(args, ['git', 'log', '--', 'Unicode\u20ac\u2122']) @raises(GitCommandError) def test_it_raises_errors(self): -- cgit v1.2.1 From c8cf69b6f8bd73525b5375bd73f1fc79ae322a82 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 16:06:23 +0100 Subject: Fixed test_git once again --- git/test/test_git.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'git') diff --git a/git/test/test_git.py b/git/test/test_git.py index 532e6659..502e6091 100644 --- a/git/test/test_git.py +++ b/git/test/test_git.py @@ -17,6 +17,8 @@ from git.test.lib import (TestBase, from git import (Git, GitCommandError) +from git.compat import PY3 + class TestGit(TestBase): @@ -34,11 +36,19 @@ class TestGit(TestBase): def test_call_unpack_args_unicode(self): args = Git._Git__unpack_args(u'Unicode€™') - assert_equal(args, ['Unicode\u20ac\u2122']) + if PY3: + mangled_value = 'Unicode\u20ac\u2122' + else: + mangled_value = 'Unicode\xe2\x82\xac\xe2\x84\xa2' + assert_equal(args, [mangled_value]) def test_call_unpack_args(self): args = Git._Git__unpack_args(['git', 'log', '--', u'Unicode€™']) - assert_equal(args, ['git', 'log', '--', 'Unicode\u20ac\u2122']) + if PY3: + mangled_value = 'Unicode\u20ac\u2122' + else: + mangled_value = 'Unicode\xe2\x82\xac\xe2\x84\xa2' + assert_equal(args, ['git', 'log', '--', mangled_value]) @raises(GitCommandError) def test_it_raises_errors(self): -- cgit v1.2.1