]>
Tony Duckles's Git Repositories (git.nynim.org) - svn2svn.git/blob - svn2svn/shell.py
   1 """ Shell functions """ 
   4 from errors 
import ExternalCommandFailed
 
  14 from datetime 
import datetime
 
  15 from subprocess 
import Popen
, PIPE
, STDOUT
 
  23 # Windows compatibility code by Bill Baxter 
  25     def find_program(name
): 
  27         Find the name of the program for Popen. 
  28         Windows is finnicky about having the complete file name. Popen 
  29         won't search the %PATH% for you automatically. 
  30         (Adapted from ctypes.find_library) 
  32         # See MSDN for the REAL search order. 
  33         base
, ext 
= os
.path
.splitext(name
) 
  37             exts 
= ['.bat', '.exe'] 
  38         for directory 
in os
.environ
['PATH'].split(os
.pathsep
): 
  40                 fname 
= os
.path
.join(directory
, base 
+ e
) 
  41                 if os
.path
.exists(fname
): 
  45     def find_program(name
): 
  47         Find the name of the program for Popen. 
  48         On Unix, popen isn't picky about having absolute paths. 
  53 def _rmtree_error_handler(func
, path
, exc_info
): 
  55     Error handler for rmtree. Helps removing the read-only protection under 
  56     Windows (and others?). 
  57     Adapted from http://www.proaxis.com/~darkwing/hot-backup.py 
  58     and http://patchwork.ozlabs.org/bazaar-ng/patch?id=4243 
  60     if func 
in (os
.remove
, os
.rmdir
) and os
.path
.exists(path
): 
  61         # Change from read-only to writeable 
  62         os
.chmod(path
, os
.stat(path
).st_mode | stat
.S_IWRITE
) 
  65         # something else must be wrong... 
  70     Wrapper around shutil.rmtree(), to provide more error-resistent behaviour. 
  72     return shutil
.rmtree(path
, False, _rmtree_error_handler
) 
  75 # Make sure we do not get localized output from the Subversion 
  76 # command line client. 
  77 os
.environ
['LC_MESSAGES'] = 'C' 
  79 locale_encoding 
= locale
.getpreferredencoding() 
  82     return locale_encoding
 
  85     # No need to wrap "safe" strings in quotes 
  86     if re
.compile('^[A-Za-z0-9=-]+$').match(s
): 
  92     return q 
+ s
.replace('\\', '\\\\').replace("'", "'\"'\"'") + q
 
  94 def _run_raw_command(cmd
, args
, fail_if_stderr
=False, no_fail
=False): 
  95     cmd_string 
= "%s %s" % (cmd
,  " ".join(map(shell_quote
, args
))) 
  97     if cmd 
== 'svn' and args
[0] in ['status', 'st', 'log', 'info', 'list', 'proplist', 'propget', 'update', 'up', 'cleanup', 'revert']: 
  98         # Show status-only commands (commands which make no changes to WC) in dim-blue 
 100     ui
.status("$ %s", cmd_string
, level
=ui
.EXTRA
, color
=color
) 
 102         pipe 
= Popen([cmd
] + args
, executable
=cmd
, stdout
=PIPE
, stderr
=PIPE
) 
 104         etype
, value 
= sys
.exc_info()[:2] 
 105         raise ExternalCommandFailed( 
 106             "Failed running external program: %s\nError: %s" 
 107             % (cmd_string
, "".join(traceback
.format_exception_only(etype
, value
)))) 
 108     out
, err 
= pipe
.communicate() 
 109     if "nothing changed" == out
.strip(): # skip this error 
 111     if (pipe
.returncode 
!= 0 or (fail_if_stderr 
and err
.strip())) and not no_fail
: 
 112         raise ExternalCommandFailed( 
 113             "External program failed (return code %d): %s\n%s\n%s" 
 114             % (pipe
.returncode
, cmd_string
, err
, out
)) 
 117 def _run_raw_shell_command(cmd
, no_fail
=False): 
 118     ui
.status("* %s", cmd
, level
=ui
.EXTRA
, color
='BLUE') 
 119     st
, out 
= commands
.getstatusoutput(cmd
) 
 120     if st 
!= 0 and not no_fail
: 
 121         raise ExternalCommandFailed( 
 122             "External program failed with non-zero return code (%d): %s\n%s" 
 126 def run_command(cmd
, args
=None, bulk_args
=None, encoding
=None, fail_if_stderr
=False, no_fail
=False): 
 128     Run a command without using the shell. 
 131     bulk_args 
= bulk_args 
or [] 
 132     def _transform_arg(a
): 
 133         if isinstance(a
, unicode): 
 134             a 
= a
.encode(encoding 
or locale_encoding 
or 'UTF-8') 
 135         elif not isinstance(a
, str): 
 139     cmd 
= find_program(cmd
) 
 141         return _run_raw_command(cmd
, map(_transform_arg
, args
), fail_if_stderr
, no_fail
) 
 142     # If one of bulk_args starts with a dash (e.g. '-foo.php'), 
 143     # svn will take this as an option. Adding '--' ends the search for 
 146         if a
.strip().startswith('-'): 
 152     while i 
< len(bulk_args
): 
 153         stop 
= i 
+ max_args_num 
- len(args
) 
 155         for a 
in bulk_args
[i
:stop
]: 
 156             sub_args
.append(_transform_arg(a
)) 
 157         out 
+= _run_raw_command(cmd
, args 
+ sub_args
, fail_if_stderr
, no_fail
) 
 161 def run_shell_command(cmd
, args
=None, bulk_args
=None, encoding
=None, no_fail
=False): 
 163     Run a shell command, properly quoting and encoding arguments. 
 164     Probably only works on Un*x-like systems. 
 167         if isinstance(a
, unicode): 
 168             a 
= a
.encode(encoding 
or locale_encoding
) 
 169         elif not isinstance(a
, str): 
 171         return shell_quote(a
) 
 174         cmd 
+= " " + " ".join(_quote_arg(a
) for a 
in args
) 
 179         return _run_raw_shell_command(cmd
, no_fail
) 
 180     while i 
< len(bulk_args
): 
 181         stop 
= i 
+ max_args_num 
- len(args
) 
 183         for a 
in bulk_args
[i
:stop
]: 
 184             sub_args
.append(_quote_arg(a
)) 
 185         sub_cmd 
= cmd 
+ " " + " ".join(sub_args
) 
 186         out 
+= _run_raw_shell_command(sub_cmd
, no_fail
) 
 190 def run_svn(args
=None, bulk_args
=None, fail_if_stderr
=False, 
 191             mask_atsign
=False, no_fail
=False): 
 193     Run an SVN command, returns the (bytes) output. 
 196         # The @ sign in Subversion revers to a pegged revision number. 
 197         # SVN treats files with @ in the filename a bit special. 
 198         # See: http://stackoverflow.com/questions/1985203 
 199         for idx 
in range(len(args
)): 
 200             if "@" in args
[idx
] and args
[idx
][0] not in ("-", '"'): 
 201                 args
[idx
] = "%s@" % args
[idx
] 
 203             for idx 
in range(len(bulk_args
)): 
 204                 if ("@" in bulk_args
[idx
] 
 205                     and bulk_args
[idx
][0] not in ("-", '"')): 
 206                     bulk_args
[idx
] = "%s@" % bulk_args
[idx
] 
 207     return run_command("svn", 
 208         args
=args
, bulk_args
=bulk_args
, fail_if_stderr
=fail_if_stderr
, no_fail
=no_fail
) 
 210 def skip_dirs(paths
, basedir
="."): 
 212     Skip all directories from path list, including symbolic links to real dirs. 
 214     # NOTE: both tests are necessary (Cameron Hutchison's patch for symbolic 
 215     # links to directories) 
 216     return [p 
for p 
in paths
 
 217         if not os
.path
.isdir(os
.path
.join(basedir
, p
)) 
 218         or os
.path
.islink(os
.path
.join(basedir
, p
))] 
 220 def get_script_name(): 
 221     """Helper to return the name of the command line script that was called.""" 
 222     return os
.path
.basename(sys
.argv
[0])