From 0846b0c905a885720138e8194fb0289f121b5da1 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 20 Jan 2013 13:15:36 +0000 Subject: git-remote-testpy: hash bytes explicitly Under Python 3 'hasher.update(...)' must take a byte string and not a unicode string. Explicitly encode the argument to this method to hex bytes so that we don't need to worry about failures to encode that might occur if we chose a textual encoding. This changes the directory used by git-remote-testpy for its git mirror of the remote repository, but this tool should not have any serious users as it is used primarily to test the Python remote helper framework. The use of encode() moves the required Python version forward to 2.0. Signed-off-by: John Keeping Signed-off-by: Junio C Hamano --- git-remote-testpy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'git-remote-testpy.py') diff --git a/git-remote-testpy.py b/git-remote-testpy.py index d94a66a870..197b7be508 100644 --- a/git-remote-testpy.py +++ b/git-remote-testpy.py @@ -31,9 +31,9 @@ from git_remote_helpers.git.exporter import GitExporter from git_remote_helpers.git.importer import GitImporter from git_remote_helpers.git.non_local import NonLocalGit -if sys.hexversion < 0x01050200: - # os.makedirs() is the limiter - sys.stderr.write("git-remote-testgit: requires Python 1.5.2 or later.\n") +if sys.hexversion < 0x02000000: + # string.encode() is the limiter + sys.stderr.write("git-remote-testgit: requires Python 2.0 or later.\n") sys.exit(1) def get_repo(alias, url): @@ -45,7 +45,7 @@ def get_repo(alias, url): repo.get_head() hasher = _digest() - hasher.update(repo.path) + hasher.update(repo.path.encode('hex')) repo.hash = hasher.hexdigest() repo.get_base_path = lambda base: os.path.join( -- cgit v1.2.3 From d04c94a2ea21cb218b43be192d8569c0e669d080 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 20 Jan 2013 13:15:37 +0000 Subject: git-remote-testpy: don't do unbuffered text I/O Python 3 forbids unbuffered I/O in text mode. Change the reading of stdin in git-remote-testpy so that we read the lines as bytes and then decode them a line at a time. This allows us to keep the I/O unbuffered in order to avoid reintroducing the bug fixed by commit 7fb8e16 (git-remote-testgit: fix race when spawning fast-import). Signed-off-by: John Keeping Signed-off-by: Junio C Hamano --- git-remote-testpy.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'git-remote-testpy.py') diff --git a/git-remote-testpy.py b/git-remote-testpy.py index 197b7be508..5dbf1cc066 100644 --- a/git-remote-testpy.py +++ b/git-remote-testpy.py @@ -154,7 +154,7 @@ def do_import(repo, args): refs = [ref] while True: - line = sys.stdin.readline() + line = sys.stdin.readline().decode() if line == '\n': break if not line.startswith('import '): @@ -225,7 +225,7 @@ def read_one_line(repo): line = sys.stdin.readline() - cmdline = line + cmdline = line.decode() if not cmdline: warn("Unexpected EOF") @@ -277,7 +277,11 @@ def main(args): more = True - sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0) + # Use binary mode since Python 3 does not permit unbuffered I/O in text + # mode. Unbuffered I/O is required to avoid data that should be going + # to git-fast-import after an "export" command getting caught in our + # stdin buffer instead. + sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0) while (more): more = read_one_line(repo) -- cgit v1.2.3 From f9640ac26ce0478695487f657718157bb7b4f804 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 20 Jan 2013 13:15:38 +0000 Subject: git-remote-testpy: call print as a function This is harmless in Python 2, which sees the parentheses as redundant grouping, but is required for Python 3. Since this is the only change required to make this script just run under Python 3 without needing 2to3 it seems worthwhile. The case of an empty print must be handled specially because in that case Python 2 will interpret '()' as an empty tuple and print it as '()'; inserting an empty string fixes this. Signed-off-by: John Keeping Acked-by: Sverre Rabbelier Signed-off-by: Junio C Hamano --- git-remote-testpy.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'git-remote-testpy.py') diff --git a/git-remote-testpy.py b/git-remote-testpy.py index 5dbf1cc066..c7a04ecae2 100644 --- a/git-remote-testpy.py +++ b/git-remote-testpy.py @@ -87,9 +87,9 @@ def do_capabilities(repo, args): """Prints the supported capabilities. """ - print "import" - print "export" - print "refspec refs/heads/*:%s*" % repo.prefix + print("import") + print("export") + print("refspec refs/heads/*:%s*" % repo.prefix) dirname = repo.get_base_path(repo.gitdir) @@ -98,11 +98,11 @@ def do_capabilities(repo, args): path = os.path.join(dirname, 'git.marks') - print "*export-marks %s" % path + print("*export-marks %s" % path) if os.path.exists(path): - print "*import-marks %s" % path + print("*import-marks %s" % path) - print # end capabilities + print('') # end capabilities def do_list(repo, args): @@ -115,16 +115,16 @@ def do_list(repo, args): for ref in repo.revs: debug("? refs/heads/%s", ref) - print "? refs/heads/%s" % ref + print("? refs/heads/%s" % ref) if repo.head: debug("@refs/heads/%s HEAD" % repo.head) - print "@refs/heads/%s HEAD" % repo.head + print("@refs/heads/%s HEAD" % repo.head) else: debug("@refs/heads/master HEAD") - print "@refs/heads/master HEAD" + print("@refs/heads/master HEAD") - print # end list + print('') # end list def update_local_repo(repo): @@ -164,7 +164,7 @@ def do_import(repo, args): ref = line[7:].strip() refs.append(ref) - print "feature done" + print("feature done") if os.environ.get("GIT_REMOTE_TESTGIT_FAILURE"): die('Told to fail') @@ -172,7 +172,7 @@ def do_import(repo, args): repo = update_local_repo(repo) repo.exporter.export_repo(repo.gitdir, refs) - print "done" + print("done") def do_export(repo, args): @@ -192,8 +192,8 @@ def do_export(repo, args): repo.non_local.push(repo.gitdir) for ref in changed: - print "ok %s" % ref - print + print("ok %s" % ref) + print('') COMMANDS = { -- cgit v1.2.3 From 3ac221a78eba2f447a7990fc5c4709853dc32fee Mon Sep 17 00:00:00 2001 From: John Keeping Date: Sun, 27 Jan 2013 14:50:56 +0000 Subject: git-remote-testpy: fix path hashing on Python 3 When this change was originally made (0846b0c - git-remote-testpy: hash bytes explicitly , I didn't realise that the "hex" encoding we chose is a "bytes to bytes" encoding so it just fails with an error on Python 3 in the same way as the original code. It is not possible to provide a single code path that works on Python 2 and Python 3 since Python 2.x will attempt to decode the string before encoding it, which fails for strings that are not valid in the default encoding. Python 3.1 introduced the "surrogateescape" error handler which handles this correctly and permits a bytes -> unicode -> bytes round-trip to be lossless. As the original came from reading the filesystem path, we convert them back into the original bytes encoded in sys.getfilesystemencoding(). At this point Python 3.0 is unsupported so we don't go out of our way to try to support it. Helped-by: Michael Haggerty Signed-off-by: John Keeping Signed-off-by: Junio C Hamano --- git-remote-testpy.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'git-remote-testpy.py') diff --git a/git-remote-testpy.py b/git-remote-testpy.py index c7a04ecae2..ca6789996a 100644 --- a/git-remote-testpy.py +++ b/git-remote-testpy.py @@ -36,6 +36,22 @@ if sys.hexversion < 0x02000000: sys.stderr.write("git-remote-testgit: requires Python 2.0 or later.\n") sys.exit(1) + +def encode_filepath(path): + """Encodes a Unicode file path to a byte string. + + On Python 2 this is a no-op; on Python 3 we encode the string as + suggested by [1] which allows an exact round-trip from the command line + to the filesystem. + + [1] http://docs.python.org/3/c-api/unicode.html#file-system-encoding + + """ + if sys.hexversion < 0x03000000: + return path + return path.encode(sys.getfilesystemencoding(), 'surrogateescape') + + def get_repo(alias, url): """Returns a git repository object initialized for usage. """ @@ -45,7 +61,7 @@ def get_repo(alias, url): repo.get_head() hasher = _digest() - hasher.update(repo.path.encode('hex')) + hasher.update(encode_filepath(repo.path)) repo.hash = hasher.hexdigest() repo.get_base_path = lambda base: os.path.join( -- cgit v1.2.3