Permalink
Browse files

Checking some edge cases on diff action dealing with directories vs f…

…iles.
  • Loading branch information...
1 parent f1aad2d commit ae3fda64a1aad6c7d40b343a98a1239305a6fda9 @fabioz committed May 24, 2012
View
@@ -10,6 +10,23 @@
import subprocess
from mu_repo.execute_git_command_in_thread import ExecuteGitCommandThread
from Queue import Queue
+from mu_repo.rmtree import RmTree
+
+#Erorr listeners may add themselves here (activated when errors happen in a worker thread).
+on_errors_listeners = set()
+
+#===================================================================================================
+# NotifyErrorListeners
+#===================================================================================================
+def NotifyErrorListeners():
+ import StringIO
+ import traceback
+ cstr = StringIO.StringIO()
+ traceback.print_exc(file=cstr)
+ error = cstr.getvalue()
+ for listener in on_errors_listeners:
+ listener(error)
+ Print(error)
#===================================================================================================
@@ -19,7 +36,7 @@ def ExecuteGettingStdOutput(cmd, cwd):
p = subprocess.Popen(
cmd,
cwd=cwd,
- stderr=subprocess.STDOUT,
+ #stderr=subprocess.STDOUT, # -- let stderr go to sys.stderr!
stdout=subprocess.PIPE,
stdin=subprocess.PIPE
)
@@ -28,7 +45,6 @@ def ExecuteGettingStdOutput(cmd, cwd):
return stdout
-
#===================================================================================================
# CreateFromGit
#===================================================================================================
@@ -40,11 +56,19 @@ def __init__(self, *args):
self._args = args
def __call__(self):
- git, repo, original_repo, target_repo = self._args
- stdout = ExecuteGettingStdOutput(
- '%s show HEAD:%s' % (git, original_repo,), repo)
- with open(target_repo, 'wb') as f:
- f.write(stdout)
+ try:
+ git, repo, original_repo, target_repo = self._args
+ stdout = ExecuteGettingStdOutput(
+ '%s show HEAD:%s' % (git, original_repo,), repo)
+
+ try:
+ if not os.path.isdir(target_repo):
+ with open(target_repo, 'wb') as f:
+ f.write(stdout)
+ except:
+ print 'Error writing to file: %s\n%s' % (target_repo, stdout,)
+ except:
+ NotifyErrorListeners()
#===================================================================================================
# Symlink
@@ -57,11 +81,14 @@ def __init__(self, *args):
self._args = args
def __call__(self):
- symlink, original, link = self._args
- symlink(
- original,
- link
- )
+ try:
+ symlink, original, link = self._args
+ symlink(
+ original,
+ link
+ )
+ except:
+ NotifyErrorListeners()
#===================================================================================================
@@ -152,7 +179,10 @@ def __init__(self, config, repo, symlink, temp_working, temp_repo):
def run(self):
- ExecuteGitCommandThread.run(self, serial=False)
+ try:
+ ExecuteGitCommandThread.run(self, serial=False)
+ except:
+ NotifyErrorListeners()
def _HandleOutput(self, msg, stdout):
@@ -195,7 +225,7 @@ def Run(params):
(temp_dir_name,)
).strip().lower()
if n == 'y':
- shutil.rmtree(temp_dir_name)
+ RmTree(temp_dir_name)
break
if n == 'n':
Print('Canceling diff action.')
@@ -228,8 +258,20 @@ def symlink(src, target):
except:
from mu_repo import keep_files_synched
def symlink(src, target):
- shutil.copyfile(src, target)
- keep_files_synched.KeepInSync(src, target)
+ if os.path.isdir(src):
+ if os.path.exists(target):
+ os.rmdir(target)
+ shutil.copytree(src, target)
+ #This print is a TODO!
+ Print('WARNING: TODO: Structure from: %s is copied (and not symlinked nor kept in sync).' % (target,))
+ else:
+ if os.path.exists(target):
+ if os.path.isdir(target):
+ RmTree(target)
+ else:
+ os.remove(target)
+ shutil.copyfile(src, target)
+ keep_files_synched.KeepInSync(src, target)
try:
#Note: we could use diff status --porcelain instead if we wanted to check untracked files.
@@ -259,7 +301,7 @@ def symlink(src, target):
finally:
def onerror(*args):
Print('Error removing temporary directory structure: %s' % (args,))
- shutil.rmtree(temp_dir_name, onerror=onerror)
+ RmTree(temp_dir_name, onerror=onerror)
@@ -40,7 +40,7 @@ def run(self, serial=False):
p = subprocess.Popen(
cmd,
cwd=repo,
- stderr=subprocess.STDOUT,
+ #stderr=subprocess.STDOUT, # -- let stderr go to sys.stderr!
stdout=subprocess.PIPE,
stdin=subprocess.PIPE
)
View
@@ -0,0 +1,37 @@
+'''
+Created on May 23, 2012
+
+@author: Fabio Zadrozny
+'''
+import shutil
+import os
+
+#===================================================================================================
+# _OnError
+#===================================================================================================
+def _OnError(func, path, exc_info):
+ """
+ See: http://stackoverflow.com/questions/2656322/python-shutil-rmtree-fails-on-windows-with-access-is-denied
+
+ Error handler for ``shutil.rmtree``.
+
+ If the error is due to an access error (read only file)
+ it attempts to add write permission and then retries.
+
+ If the error is for another reason it re-raises the error.
+
+ Usage : ``shutil.rmtree(path, _OnError=_OnError)``
+ """
+ import stat
+ if not os.access(path, os.W_OK):
+ # Is the error an access error ?
+ os.chmod(path, stat.S_IWUSR)
+ func(path)
+ else:
+ raise
+
+#===================================================================================================
+# RmTree
+#===================================================================================================
+def RmTree(path, ignore_errors=False, onerror=None):
+ return shutil.rmtree(path, ignore_errors, onerror or _OnError)
@@ -0,0 +1,100 @@
+'''
+Created on May 23, 2012
+
+@author: Fabio Zadrozny
+'''
+import unittest
+import os.path
+from mu_repo.config import Config
+from mu_repo import action_diff, Params
+import shutil
+import subprocess
+import sys
+import time
+from mu_repo.rmtree import RmTree
+
+
+#===================================================================================================
+# Test
+#===================================================================================================
+class Test(unittest.TestCase):
+
+
+ def setUp(self):
+ unittest.TestCase.setUp(self)
+ self.clear()
+
+
+ def tearDown(self):
+ unittest.TestCase.tearDown(self)
+ self.clear()
+
+
+ def clear(self):
+ if os.path.exists('test_diff_command_git_repo_dir'):
+ try:
+ RmTree('test_diff_command_git_repo_dir')
+ except:
+ time.sleep(1)
+ RmTree('test_diff_command_git_repo_dir')
+
+
+ def CallDiff(self):
+ config = Config(repos=['test_diff_command_git_repo_dir'], git=self.git)
+ params = Params(config, ['dd'], config_file=None, stream=sys.stdout)
+
+ called = []
+ def Call(cmd, *args, **kwargs):
+ called.append(cmd[0].lower())
+
+ errors = []
+ def OnError(error):
+ errors.append(error)
+
+ #Mock things
+ original_call = subprocess.call
+ subprocess.call = Call
+ action_diff.on_errors_listeners.add(OnError)
+ try:
+ action_diff.Run(params)
+ finally:
+ action_diff.on_errors_listeners.remove(OnError)
+ subprocess.call = original_call
+ if errors:
+ self.fail('\n\n'.join(errors))
+ return called
+
+
+
+ def testActionDiff(self):
+ temp_dir = 'test_diff_command_git_repo_dir'
+ git = r'C:\D\bin\git\bin\git.exe'
+ if not os.path.exists(git):
+ git = 'git'
+ self.git = git
+
+ #1. Test diffing with new folder structure
+ subprocess.call([git] + 'init test_diff_command_git_repo_dir'.split(), cwd='.')
+ os.mkdir(os.path.join(temp_dir, 'folder1'))
+ with open(os.path.join(temp_dir, 'folder1', 'out.txt'), 'w') as f:
+ f.write('out')
+ called = self.CallDiff()
+
+ self.assertEqual(['winmergeu.exe'], called)
+
+ #2. Test diffing dir structure in git changed for file in working dir
+ subprocess.call([git] + 'add -A'.split(), cwd=temp_dir)
+ subprocess.call([git] + 'commit -m "First'.split(), cwd=temp_dir)
+ RmTree(os.path.join(temp_dir, 'folder1'))
+ with open(os.path.join(temp_dir, 'folder1'), 'w') as f:
+ f.write('folder1 is now file.')
+
+ called = self.CallDiff()
+ self.assertEqual(['winmergeu.exe'], called)
+
+#===================================================================================================
+# main
+#===================================================================================================
+if __name__ == "__main__":
+ #import sys;sys.argv = ['', 'Test.testMuRepo']
+ unittest.main()
@@ -7,6 +7,7 @@
import os.path
import shutil
from mu_repo import keep_files_synched
+from mu_repo.rmtree import RmTree
#===================================================================================================
@@ -26,7 +27,7 @@ def tearDown(self):
def clear(self):
if os.path.exists('.test_temp_dir'):
- shutil.rmtree('.test_temp_dir')
+ RmTree('.test_temp_dir')
def read(self, file2):

0 comments on commit ae3fda6

Please sign in to comment.