Merge ~pappacena/turnip:hook-py3-compat into turnip:master

Proposed by Thiago F. Pappacena
Status: Merged
Approved by: Thiago F. Pappacena
Approved revision: a7196fe31e7b927eda6461c241fccd969059f512
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~pappacena/turnip:hook-py3-compat
Merge into: turnip:master
Diff against target: 166 lines (+49/-17)
3 files modified
turnip/compat/__init__.py (+0/-0)
turnip/compat/files.py (+21/-0)
turnip/pack/hooks/hook.py (+28/-17)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+383754@code.launchpad.net

Commit message

Improving compatibility of hook.py with python3.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

Pushed requested change. I'll land this MP.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/turnip/compat/__init__.py b/turnip/compat/__init__.py
2new file mode 100644
3index 0000000..e69de29
4--- /dev/null
5+++ b/turnip/compat/__init__.py
6diff --git a/turnip/compat/files.py b/turnip/compat/files.py
7new file mode 100644
8index 0000000..3f8bb22
9--- /dev/null
10+++ b/turnip/compat/files.py
11@@ -0,0 +1,21 @@
12+import sys
13+
14+
15+def fd_buffer(file_descriptor):
16+ """Returns the raw bytes stream for the given file descriptor.
17+
18+ On Python2, it returns the file descriptor itself (since it reads raw
19+ binary data by default). On Python3, it returns the
20+ file_descriptor.buffer object (since py3, by default, opens files with
21+ an encoding).
22+
23+ It's useful to read and write from sys.std{in,out,err} without reopening
24+ those files, for example.
25+
26+ :param file_descriptor: The file descriptor to get raw buffer from.
27+ :return: A BufferedReader or BufferedWriter object."""
28+ PY3K = sys.version_info >= (3, 0)
29+ if PY3K:
30+ return file_descriptor.buffer
31+ else:
32+ return file_descriptor
33diff --git a/turnip/pack/hooks/hook.py b/turnip/pack/hooks/hook.py
34index e4b6886..61740c4 100755
35--- a/turnip/pack/hooks/hook.py
36+++ b/turnip/pack/hooks/hook.py
37@@ -16,6 +16,10 @@ import socket
38 import subprocess
39 import sys
40
41+import six
42+
43+from turnip.compat.files import fd_buffer
44+
45 # XXX twom 2018-10-23 This should be a pygit2 import, but
46 # that currently causes CFFI warnings to be returned to the client.
47 GIT_OID_HEX_ZERO = '0'*40
48@@ -44,7 +48,8 @@ def determine_permissions_outcome(old, ref, rule_lines):
49 if 'create' in rule:
50 return
51 else:
52- return b'You do not have permission to create ' + ref + b'.'
53+ return (b'You do not have permission to create %s.' %
54+ six.ensure_binary(ref, "UTF-8"))
55 # We have force-push permission, implies push, therefore okay
56 if 'force_push' in rule:
57 return
58@@ -53,7 +58,8 @@ def determine_permissions_outcome(old, ref, rule_lines):
59 if 'push' in rule:
60 return
61 # If we're here, there are no matching rules
62- return b"You do not have permission to push to " + ref + b"."
63+ return (b"You do not have permission to push to %s." %
64+ six.ensure_binary(ref, "UTF-8"))
65
66
67 def match_rules(rule_lines, ref_lines):
68@@ -95,7 +101,8 @@ def match_update_rules(rule_lines, ref_line):
69 rule = rule_lines.get(ref, [])
70 if 'force_push' in rule:
71 return []
72- return [b'You do not have permission to force-push to ' + ref + b'.']
73+ return [b'You do not have permission to force-push to %s.' %
74+ six.ensure_binary(ref, "UTF-8")]
75
76
77 def netstring_send(sock, s):
78@@ -104,7 +111,7 @@ def netstring_send(sock, s):
79
80 def netstring_recv(sock):
81 c = sock.recv(1)
82- lengthstr = ''
83+ lengthstr = b''
84 while c != b':':
85 assert c.isdigit()
86 lengthstr += c
87@@ -121,7 +128,7 @@ def rpc_invoke(sock, method, args):
88 msg = dict(args)
89 assert 'op' not in msg
90 msg['op'] = method
91- netstring_send(sock, json.dumps(msg))
92+ netstring_send(sock, six.ensure_binary(json.dumps(msg), 'UTF-8'))
93 res = json.loads(netstring_recv(sock))
94 if 'error' in res:
95 raise Exception(res)
96@@ -131,7 +138,7 @@ def rpc_invoke(sock, method, args):
97 def check_ref_permissions(sock, rpc_key, ref_paths):
98 ref_paths = [base64.b64encode(path).decode('UTF-8') for path in ref_paths]
99 rule_lines = rpc_invoke(
100- sock, b'check_ref_permissions',
101+ sock, 'check_ref_permissions',
102 {'key': rpc_key, 'paths': ref_paths})
103 return {
104 base64.b64decode(path.encode('UTF-8')): permissions
105@@ -148,38 +155,42 @@ def send_mp_url(received_line):
106 pushed_branch = ref[len(b'refs/heads/'):]
107 if not is_default_branch(pushed_branch):
108 mp_url = rpc_invoke(
109- sock, b'get_mp_url',
110- {'key': rpc_key, 'branch': pushed_branch})
111+ sock, 'get_mp_url',
112+ {'key': rpc_key,
113+ 'branch': six.ensure_text(pushed_branch, "UTF-8")})
114 if mp_url is not None:
115- sys.stdout.write(b' \n')
116- sys.stdout.write(
117+ stdout = fd_buffer(sys.stdout)
118+ stdout.write(b' \n')
119+ stdout.write(
120 b"Create a merge proposal for '%s' on Launchpad by"
121 b" visiting:\n" % pushed_branch)
122- sys.stdout.write(b' %s\n' % mp_url)
123- sys.stdout.write(b' \n')
124+ stdout.write(b' %s\n' % six.ensure_binary(mp_url, "UTF8"))
125+ stdout.write(b' \n')
126
127 if __name__ == '__main__':
128 # Connect to the RPC server, authenticating using the random key
129 # from the environment.
130+ stdin = fd_buffer(sys.stdin)
131+ stdout = fd_buffer(sys.stdout)
132 rpc_key = os.environ['TURNIP_HOOK_RPC_KEY']
133 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
134 sock.connect(os.environ['TURNIP_HOOK_RPC_SOCK'])
135 hook = os.path.basename(sys.argv[0])
136 if hook == 'pre-receive':
137 # Verify the proposed changes against rules from the server.
138- raw_paths = sys.stdin.readlines()
139+ raw_paths = stdin.readlines()
140 ref_paths = [p.rstrip(b'\n').split(b' ', 2)[2] for p in raw_paths]
141 rule_lines = check_ref_permissions(sock, rpc_key, ref_paths)
142 errors = match_rules(rule_lines, raw_paths)
143 for error in errors:
144- sys.stdout.write(error + b'\n')
145+ stdout.write(error + b'\n')
146 sys.exit(1 if errors else 0)
147 elif hook == 'post-receive':
148 # Notify the server about the push if there were any changes.
149 # Details of the changes aren't currently included.
150- lines = sys.stdin.readlines()
151+ lines = stdin.readlines()
152 if lines:
153- rpc_invoke(sock, b'notify_push', {'key': rpc_key})
154+ rpc_invoke(sock, 'notify_push', {'key': rpc_key})
155 if len(lines) == 1:
156 send_mp_url(lines[0])
157 sys.exit(0)
158@@ -188,7 +199,7 @@ if __name__ == '__main__':
159 rule_lines = check_ref_permissions(sock, rpc_key, [ref])
160 errors = match_update_rules(rule_lines, sys.argv[1:4])
161 for error in errors:
162- sys.stdout.write(error + b'\n')
163+ stdout.write(error + b'\n')
164 sys.exit(1 if errors else 0)
165 else:
166 sys.stderr.write('Invalid hook name: %s' % hook)

Subscribers

People subscribed via source and target branches