55# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66from __future__ import print_function
77
8+ import contextlib
89from functools import wraps
910import io
1011import logging
1617import unittest
1718
1819from git .compat import string_types , is_win , PY3
19- from git .util import rmtree
20+ from git .util import rmtree , cwd
2021
2122import os .path as osp
2223
@@ -151,32 +152,67 @@ def repo_creator(self):
151152 return argument_passer
152153
153154
154- def launch_git_daemon (base_path , ip , port ):
155- from git import Git
156- if is_win :
157- ## On MINGW-git, daemon exists in .\Git\mingw64\libexec\git-core\,
158- # but if invoked as 'git daemon', it detaches from parent `git` cmd,
159- # and then CANNOT DIE!
160- # So, invoke it as a single command.
161- ## Cygwin-git has no daemon. But it can use MINGW's.
162- #
163- daemon_cmd = ['git-daemon' ,
164- '--enable=receive-pack' ,
165- '--listen=%s' % ip ,
166- '--port=%s' % port ,
167- '--base-path=%s' % base_path ,
168- base_path ]
169- gd = Git ().execute (daemon_cmd , as_process = True )
170- else :
171- gd = Git ().daemon (base_path ,
172- enable = 'receive-pack' ,
173- listen = ip ,
174- port = port ,
175- base_path = base_path ,
176- as_process = True )
177- # yes, I know ... fortunately, this is always going to work if sleep time is just large enough
178- time .sleep (0.5 )
179- return gd
155+ @contextlib .contextmanager
156+ def git_daemon_launched (base_path , ip , port ):
157+ from git import Git # Avoid circular deps.
158+
159+ gd = None
160+ try :
161+ if is_win :
162+ ## On MINGW-git, daemon exists in .\Git\mingw64\libexec\git-core\,
163+ # but if invoked as 'git daemon', it detaches from parent `git` cmd,
164+ # and then CANNOT DIE!
165+ # So, invoke it as a single command.
166+ ## Cygwin-git has no daemon. But it can use MINGW's.
167+ #
168+ daemon_cmd = ['git-daemon' ,
169+ '--enable=receive-pack' ,
170+ '--listen=%s' % ip ,
171+ '--port=%s' % port ,
172+ '--base-path=%s' % base_path ,
173+ base_path ]
174+ gd = Git ().execute (daemon_cmd , as_process = True )
175+ else :
176+ gd = Git ().daemon (base_path ,
177+ enable = 'receive-pack' ,
178+ listen = ip ,
179+ port = port ,
180+ base_path = base_path ,
181+ as_process = True )
182+ # yes, I know ... fortunately, this is always going to work if sleep time is just large enough
183+ time .sleep (0.5 * (1 + is_win ))
184+
185+ yield
186+
187+ except Exception as ex :
188+ msg = textwrap .dedent ("""
189+ Launching git-daemon failed due to: %s
190+ Probably test will fail subsequently.
191+
192+ BUT you may start *git-daemon* manually with this command:"
193+ git daemon --enable=receive-pack --listen=%s --port=%s --base-path=%s %s
194+ You may also run the daemon on a different port by passing --port=<port>"
195+ and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
196+ """ )
197+ if is_win :
198+ msg += textwrap .dedent ("""
199+
200+ On Windows,
201+ the `git-daemon.exe` must be in PATH.
202+ For MINGW, look into .\Git\mingw64\libexec\git-core\), but problems with paths might appear.
203+ CYGWIN has no daemon, but if one exists, it gets along fine (but has also paths problems).""" )
204+ log .warning (msg , ex , ip , port , base_path , base_path , exc_info = 1 )
205+
206+ yield
207+
208+ finally :
209+ if gd :
210+ try :
211+ log .debug ("Killing git-daemon..." )
212+ gd .proc .kill ()
213+ except Exception as ex :
214+ ## Either it has died (and we're here), or it won't die, again here...
215+ log .debug ("Hidden error while Killing git-daemon: %s" , ex , exc_info = 1 )
180216
181217
182218def with_rw_and_rw_remote_repo (working_tree_ref ):
@@ -193,10 +229,10 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
193229 directories in it.
194230
195231 The following scetch demonstrates this::
196- rorepo ---<bare clone>---> rw_remote_repo ---<clone>---> rw_repo
232+ rorepo ---<bare clone>---> rw_daemon_repo ---<clone>---> rw_repo
197233
198234 The test case needs to support the following signature::
199- def case(self, rw_repo, rw_remote_repo )
235+ def case(self, rw_repo, rw_daemon_repo )
200236
201237 This setup allows you to test push and pull scenarios and hooks nicely.
202238
@@ -211,94 +247,65 @@ def argument_passer(func):
211247
212248 @wraps (func )
213249 def remote_repo_creator (self ):
214- remote_repo_dir = tempfile .mktemp ("remote_repo_%s " % func .__name__ )
215- repo_dir = tempfile .mktemp ("remote_clone_non_bare_repo" )
250+ rw_daemon_repo_dir = tempfile .mktemp (prefix = "daemon_repo-%s- " % func .__name__ )
251+ rw_repo_dir = tempfile .mktemp ("daemon_cloned_repo-%s-" % func . __name__ )
216252
217- rw_remote_repo = self .rorepo .clone (remote_repo_dir , shared = True , bare = True )
253+ rw_daemon_repo = self .rorepo .clone (rw_daemon_repo_dir , shared = True , bare = True )
218254 # recursive alternates info ?
219- rw_repo = rw_remote_repo .clone (repo_dir , shared = True , bare = False , n = True )
220- rw_repo .head .commit = working_tree_ref
221- rw_repo .head .reference .checkout ()
222-
223- # prepare for git-daemon
224- rw_remote_repo .daemon_export = True
225-
226- # this thing is just annoying !
227- with rw_remote_repo .config_writer () as crw :
228- section = "daemon"
229- try :
230- crw .add_section (section )
231- except Exception :
232- pass
233- crw .set (section , "receivepack" , True )
234-
235- # Initialize the remote - first do it as local remote and pull, then
236- # we change the url to point to the daemon.
237- d_remote = Remote .create (rw_repo , "daemon_origin" , remote_repo_dir )
238- d_remote .fetch ()
239-
240- base_path , rel_repo_dir = osp .split (remote_repo_dir )
241-
242- remote_repo_url = Git .polish_url ("git://localhost:%s/%s" % (GIT_DAEMON_PORT , rel_repo_dir ))
243- with d_remote .config_writer as cw :
244- cw .set ('url' , remote_repo_url )
245-
255+ rw_repo = rw_daemon_repo .clone (rw_repo_dir , shared = True , bare = False , n = True )
246256 try :
247- gd = launch_git_daemon (Git .polish_url (base_path ), '127.0.0.1' , GIT_DAEMON_PORT )
248- except Exception as ex :
249- if is_win :
250- msg = textwrap .dedent ("""
251- The `git-daemon.exe` must be in PATH.
252- For MINGW, look into .\Git\mingw64\libexec\git-core\), but problems with paths might appear.
253- CYGWIN has no daemon, but if one exists, it gets along fine (has also paths problems)
254- Anyhow, alternatively try starting `git-daemon` manually:""" )
255- else :
256- msg = "Please try starting `git-daemon` manually:"
257- msg += textwrap .dedent ("""
258- git daemon --enable=receive-pack --base-path=%s %s
259- You can also run the daemon on a different port by passing --port=<port>"
260- and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
261- """ % (base_path , base_path ))
262- raise AssertionError (ex , msg )
263- # END make assertion
264- else :
265- # Try listing remotes, to diagnose whether the daemon is up.
266- rw_repo .git .ls_remote (d_remote )
267-
268- # adjust working dir
269- prev_cwd = os .getcwd ()
270- os .chdir (rw_repo .working_dir )
257+ rw_repo .head .commit = working_tree_ref
258+ rw_repo .head .reference .checkout ()
271259
272- try :
273- return func (self , rw_repo , rw_remote_repo )
274- except :
275- log .info ("Keeping repos after failure: repo_dir = %s, remote_repo_dir = %s" ,
276- repo_dir , remote_repo_dir )
277- repo_dir = remote_repo_dir = None
278- raise
279- finally :
280- os .chdir (prev_cwd )
260+ # prepare for git-daemon
261+ rw_daemon_repo .daemon_export = True
262+
263+ # this thing is just annoying !
264+ with rw_daemon_repo .config_writer () as crw :
265+ section = "daemon"
266+ try :
267+ crw .add_section (section )
268+ except Exception :
269+ pass
270+ crw .set (section , "receivepack" , True )
271+
272+ # Initialize the remote - first do it as local remote and pull, then
273+ # we change the url to point to the daemon.
274+ d_remote = Remote .create (rw_repo , "daemon_origin" , rw_daemon_repo_dir )
275+ d_remote .fetch ()
276+
277+ base_daemon_path , rel_repo_dir = osp .split (rw_daemon_repo_dir )
278+
279+ remote_repo_url = Git .polish_url ("git://localhost:%s/%s" % (GIT_DAEMON_PORT , rel_repo_dir ))
280+ with d_remote .config_writer as cw :
281+ cw .set ('url' , remote_repo_url )
282+
283+ with git_daemon_launched (Git .polish_url (base_daemon_path , is_cygwin = False ), # No daemon in Cygwin.
284+ '127.0.0.1' ,
285+ GIT_DAEMON_PORT ):
286+ # Try listing remotes, to diagnose whether the daemon is up.
287+ rw_repo .git .ls_remote (d_remote )
288+
289+ with cwd (rw_repo .working_dir ):
290+ try :
291+ return func (self , rw_repo , rw_daemon_repo )
292+ except :
293+ log .info ("Keeping repos after failure: \n rw_repo_dir: %s \n rw_daemon_repo_dir: %s" ,
294+ rw_repo_dir , rw_daemon_repo_dir )
295+ rw_repo_dir = rw_daemon_repo_dir = None
296+ raise
281297
282298 finally :
283- try :
284- log .debug ("Killing git-daemon..." )
285- gd .proc .kill ()
286- except :
287- ## Either it has died (and we're here), or it won't die, again here...
288- pass
289-
290299 rw_repo .git .clear_cache ()
291- rw_remote_repo .git .clear_cache ()
292- rw_repo = rw_remote_repo = None
300+ rw_daemon_repo .git .clear_cache ()
301+ del rw_repo
302+ del rw_daemon_repo
293303 import gc
294304 gc .collect ()
295- if repo_dir :
296- rmtree (repo_dir )
297- if remote_repo_dir :
298- rmtree (remote_repo_dir )
299-
300- if gd is not None :
301- gd .proc .wait ()
305+ if rw_repo_dir :
306+ rmtree (rw_repo_dir )
307+ if rw_daemon_repo_dir :
308+ rmtree (rw_daemon_repo_dir )
302309 # END cleanup
303310 # END bare repo creator
304311 return remote_repo_creator
0 commit comments