Refactor Python code & .travis.yml
* Refactor Command class for easier reading. * Some other minor clean ups & method renames. * Change travis to use `env` and `matrix` to select builds. * Use case instead of ifs to select behaviour.
This commit is contained in:
		
							parent
							
								
									58d39115f9
								
							
						
					
					
						commit
						7e1dc1bcc8
					
				
							
								
								
									
										70
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								.travis.yml
									
									
									
									
									
								
							|  | @ -1,35 +1,49 @@ | ||||||
| language: ruby | language: ruby | ||||||
| rvm: | rvm: | ||||||
|   - 1.8.7 |   - 1.8.7 | ||||||
|   - 1.9.2 # Test with vim-nox package on ubuntu |  | ||||||
|   - 1.9.3 # Test against python installer |  | ||||||
|   - 2.0.0 |   - 2.0.0 | ||||||
|   - 2.1.0 # Test against python3 installer | env: | ||||||
| 
 |   - ENV=nox | ||||||
| before_script: | |   - ENV=python | ||||||
|   sudo apt-get update -y |   - ENV=python3 | ||||||
|   if [ $(ruby -e 'puts RUBY_VERSION') = 1.9.2 ]; then |   - ENV=ruby | ||||||
|     sudo apt-get install -y vim-nox | matrix: | ||||||
|     sudo ln -s /usr/bin/vim /usr/local/bin/vim |   exclude: | ||||||
|   else |     - rvm: 2.0.0 | ||||||
|     git clone --depth 1 https://github.com/vim/vim |   include: | ||||||
|     cd vim |     - rvm: 2.0.0 | ||||||
|     if [ $(ruby -e 'puts RUBY_VERSION') = 1.9.3 ]; then |       env: ENV=ruby | ||||||
|       sudo apt-get install -y python2.7-dev | install: | | ||||||
|       ./configure --disable-gui --with-features=huge --enable-pythoninterp |  | ||||||
|     elif [ $(ruby -e 'puts RUBY_VERSION') = 2.1.0 ]; then |  | ||||||
|       sudo apt-get install -y python3-dev |  | ||||||
|       ./configure --disable-gui --with-features=huge --enable-python3interp |  | ||||||
|     else |  | ||||||
|       ./configure --disable-gui --with-features=huge --enable-rubyinterp |  | ||||||
|     fi |  | ||||||
|     make |  | ||||||
|     sudo make install |  | ||||||
|     cd - |  | ||||||
|   fi |  | ||||||
| 
 |  | ||||||
|   git config --global user.email "you@example.com" |   git config --global user.email "you@example.com" | ||||||
|   git config --global user.name "Your Name" |   git config --global user.name "Your Name" | ||||||
|  |   sudo apt-get update -y | ||||||
| 
 | 
 | ||||||
| script: | |   if [ "$ENV" == "nox" ]; then | ||||||
|   test/run ! |     sudo apt-get install -y vim-nox | ||||||
|  |     sudo ln -s /usr/bin/vim /usr/local/bin/vim | ||||||
|  |     return | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   C_OPTS="--with-features=huge --disable-gui " | ||||||
|  |   case "$ENV" in | ||||||
|  |     python) | ||||||
|  |       PACKS=python2.7-dev | ||||||
|  |       C_OPtS+=--enable-pythoninterp | ||||||
|  |       ;; | ||||||
|  |     python3) | ||||||
|  |       PACKS=python3-dev | ||||||
|  |       C_OPtS+=--enable-python3interp | ||||||
|  |       ;; | ||||||
|  |     ruby) | ||||||
|  |       C_OPTS+=--enable-rubyinterp | ||||||
|  |       ;; | ||||||
|  |   esac | ||||||
|  | 
 | ||||||
|  |   sudo apt-get install -y $PACKS | ||||||
|  |   git clone --depth 1 https://github.com/vim/vim | ||||||
|  |   cd vim | ||||||
|  |   ./configure $C_OPTS | ||||||
|  |   make | ||||||
|  |   sudo make install | ||||||
|  |   cd - | ||||||
|  | script: test/run ! | ||||||
|  |  | ||||||
							
								
								
									
										135
									
								
								plug.vim
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								plug.vim
									
									
									
									
									
								
							|  | @ -1050,17 +1050,17 @@ G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) | ||||||
| G_STOP = thr.Event() | G_STOP = thr.Event() | ||||||
| G_THREADS = {} | G_THREADS = {} | ||||||
| 
 | 
 | ||||||
| class BaseExc(Exception): | class PlugError(Exception): | ||||||
|   def __init__(self, msg): |   def __init__(self, msg): | ||||||
|     self._msg = msg |     self._msg = msg | ||||||
|   @property |   @property | ||||||
|   def msg(self): |   def msg(self): | ||||||
|     return self._msg |     return self._msg | ||||||
| class CmdTimedOut(BaseExc): | class CmdTimedOut(PlugError): | ||||||
|   pass |   pass | ||||||
| class CmdFailed(BaseExc): | class CmdFailed(PlugError): | ||||||
|   pass |   pass | ||||||
| class InvalidURI(BaseExc): | class InvalidURI(PlugError): | ||||||
|   pass |   pass | ||||||
| class Action(object): | class Action(object): | ||||||
|   INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] |   INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] | ||||||
|  | @ -1074,7 +1074,7 @@ class Buffer(object): | ||||||
|     self.maxy = int(vim.eval('winheight(".")')) |     self.maxy = int(vim.eval('winheight(".")')) | ||||||
|     self.num_plugs = num_plugs |     self.num_plugs = num_plugs | ||||||
| 
 | 
 | ||||||
|   def _where(self, name): |   def __where(self, name): | ||||||
|     """ Find first line with name in current buffer. Return line num. """ |     """ Find first line with name in current buffer. Return line num. """ | ||||||
|     found, lnum = False, 0 |     found, lnum = False, 0 | ||||||
|     matcher = re.compile('^[-+x*] {0}:'.format(name)) |     matcher = re.compile('^[-+x*] {0}:'.format(name)) | ||||||
|  | @ -1103,8 +1103,7 @@ class Buffer(object): | ||||||
|   def write(self, action, name, lines): |   def write(self, action, name, lines): | ||||||
|     first, rest = lines[0], lines[1:] |     first, rest = lines[0], lines[1:] | ||||||
|     msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] |     msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] | ||||||
|     padded_rest = ['    ' + line for line in rest] |     msg.extend(['    ' + line for line in rest]) | ||||||
|     msg.extend(padded_rest) |  | ||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|       if action == Action.ERROR: |       if action == Action.ERROR: | ||||||
|  | @ -1114,7 +1113,7 @@ class Buffer(object): | ||||||
|         self.bar += '=' |         self.bar += '=' | ||||||
| 
 | 
 | ||||||
|       curbuf = vim.current.buffer |       curbuf = vim.current.buffer | ||||||
|       lnum = self._where(name) |       lnum = self.__where(name) | ||||||
|       if lnum != -1: # Found matching line num |       if lnum != -1: # Found matching line num | ||||||
|         del curbuf[lnum] |         del curbuf[lnum] | ||||||
|         if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): |         if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): | ||||||
|  | @ -1128,56 +1127,62 @@ class Buffer(object): | ||||||
|       pass |       pass | ||||||
| 
 | 
 | ||||||
| class Command(object): | class Command(object): | ||||||
|   def __init__(self, cmd, cmd_dir=None, timeout=60, ntries=3, cb=None, clean=None): |   def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): | ||||||
|     self.cmd = cmd |     self.cmd = cmd | ||||||
|     self.cmd_dir = cmd_dir |     self.cmd_dir = cmd_dir | ||||||
|     self.timeout = timeout |     self.timeout = timeout | ||||||
|     self.ntries = ntries |  | ||||||
|     self.callback = cb if cb else (lambda msg: None) |     self.callback = cb if cb else (lambda msg: None) | ||||||
|     self.clean = clean |     self.clean = clean if clean else (lambda: None) | ||||||
|  |     self.proc = None | ||||||
| 
 | 
 | ||||||
|   def attempt_cmd(self): |   @property | ||||||
|     """ Tries to run the command, returns result if no exceptions. """ |   def alive(self): | ||||||
|     attempt = 0 |     """ Returns true only if command still running. """ | ||||||
|     finished = False |     return self.proc and self.proc.poll() is None | ||||||
|     limit = self.timeout | 
 | ||||||
|  |   def execute(self, ntries=3): | ||||||
|  |     """ Execute the command with ntries if CmdTimedOut. | ||||||
|  |         Returns the output of the command if no Exception. | ||||||
|  |     """ | ||||||
|  |     attempt, finished, limit = 0, False, self.timeout | ||||||
| 
 | 
 | ||||||
|     while not finished: |     while not finished: | ||||||
|       try: |       try: | ||||||
|         attempt += 1 |         attempt += 1 | ||||||
|         result = self.timeout_cmd() |         result = self.try_command() | ||||||
|         finished = True |         finished = True | ||||||
|  |         return result | ||||||
|       except CmdTimedOut: |       except CmdTimedOut: | ||||||
|         if attempt != self.ntries: |         if attempt != ntries: | ||||||
|           for count in range(3, 0, -1): |           self.notify_retry() | ||||||
|             if G_STOP.is_set(): |  | ||||||
|               raise KeyboardInterrupt |  | ||||||
|             msg = 'Timeout. Will retry in {0} second{1} ...'.format( |  | ||||||
|                 count, 's' if count != 1 else '') |  | ||||||
|             self.callback([msg]) |  | ||||||
|             time.sleep(1) |  | ||||||
|           self.timeout += limit |           self.timeout += limit | ||||||
|           self.callback(['Retrying ...']) |  | ||||||
|         else: |         else: | ||||||
|           raise |           raise | ||||||
| 
 | 
 | ||||||
|     return result |   def notify_retry(self): | ||||||
|  |     """ Retry required for command, notify user. """ | ||||||
|  |     for count in range(3, 0, -1): | ||||||
|  |       if G_STOP.is_set(): | ||||||
|  |         raise KeyboardInterrupt | ||||||
|  |       msg = 'Timeout. Will retry in {0} second{1} ...'.format( | ||||||
|  |             count, 's' if count != 1 else '') | ||||||
|  |       self.callback([msg]) | ||||||
|  |       time.sleep(1) | ||||||
|  |     self.callback(['Retrying ...']) | ||||||
| 
 | 
 | ||||||
|   def timeout_cmd(self): |   def try_command(self): | ||||||
|     """ Execute a cmd & poll for callback. Returns list of output. |     """ Execute a cmd & poll for callback. Returns list of output. | ||||||
|     Raises CmdFailed   -> return code for Popen isn't 0 |         Raises CmdFailed   -> return code for Popen isn't 0 | ||||||
|     Raises CmdTimedOut -> command exceeded timeout without new output |         Raises CmdTimedOut -> command exceeded timeout without new output | ||||||
|     """ |     """ | ||||||
|     proc = None |  | ||||||
|     first_line = True |     first_line = True | ||||||
|     try: |  | ||||||
|       tfile = tempfile.NamedTemporaryFile(mode='w+b', delete=False) |  | ||||||
|       proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile, |  | ||||||
|           stderr=subprocess.STDOUT, shell=True, preexec_fn=os.setsid) |  | ||||||
|       while proc.poll() is None: |  | ||||||
|         # Yield this thread |  | ||||||
|         time.sleep(0.2) |  | ||||||
| 
 | 
 | ||||||
|  |     try: | ||||||
|  |       tfile = tempfile.NamedTemporaryFile(mode='w+b') | ||||||
|  |       self.proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile, | ||||||
|  |                                    stderr=subprocess.STDOUT, shell=True, | ||||||
|  |                                    preexec_fn=os.setsid) | ||||||
|  |       while self.alive: | ||||||
|         if G_STOP.is_set(): |         if G_STOP.is_set(): | ||||||
|           raise KeyboardInterrupt |           raise KeyboardInterrupt | ||||||
| 
 | 
 | ||||||
|  | @ -1191,23 +1196,24 @@ class Command(object): | ||||||
|         if time_diff > self.timeout: |         if time_diff > self.timeout: | ||||||
|           raise CmdTimedOut(['Timeout!']) |           raise CmdTimedOut(['Timeout!']) | ||||||
| 
 | 
 | ||||||
|  |         time.sleep(0.33) | ||||||
|  | 
 | ||||||
|       tfile.seek(0) |       tfile.seek(0) | ||||||
|       result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] |       result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] | ||||||
| 
 | 
 | ||||||
|       if proc.returncode != 0: |       if self.proc.returncode != 0: | ||||||
|         msg = [''] |         raise CmdFailed([''] + result) | ||||||
|         msg.extend(result) |  | ||||||
|         raise CmdFailed(msg) |  | ||||||
|     except: |  | ||||||
|       if proc and proc.poll() is None: |  | ||||||
|         os.killpg(proc.pid, signal.SIGTERM) |  | ||||||
|       if self.clean: |  | ||||||
|         self.clean() |  | ||||||
|       raise |  | ||||||
|     finally: |  | ||||||
|       os.remove(tfile.name) |  | ||||||
| 
 | 
 | ||||||
|     return result |       return result | ||||||
|  |     except: | ||||||
|  |       self.terminate() | ||||||
|  |       raise | ||||||
|  | 
 | ||||||
|  |   def terminate(self): | ||||||
|  |     """ Terminate process and cleanup. """ | ||||||
|  |     if self.alive: | ||||||
|  |       os.killpg(self.proc.pid, signal.SIGTERM) | ||||||
|  |     self.clean() | ||||||
| 
 | 
 | ||||||
| class Plugin(object): | class Plugin(object): | ||||||
|   def __init__(self, name, args, buf_q, lock): |   def __init__(self, name, args, buf_q, lock): | ||||||
|  | @ -1228,7 +1234,7 @@ class Plugin(object): | ||||||
|         self.install() |         self.install() | ||||||
|         with self.lock: |         with self.lock: | ||||||
|           thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) |           thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) | ||||||
|     except (CmdTimedOut, CmdFailed, InvalidURI) as exc: |     except PlugError as exc: | ||||||
|       self.write(Action.ERROR, self.name, exc.msg) |       self.write(Action.ERROR, self.name, exc.msg) | ||||||
|     except KeyboardInterrupt: |     except KeyboardInterrupt: | ||||||
|       G_STOP.set() |       G_STOP.set() | ||||||
|  | @ -1253,11 +1259,18 @@ class Plugin(object): | ||||||
|     self.write(Action.INSTALL, self.name, ['Installing ...']) |     self.write(Action.INSTALL, self.name, ['Installing ...']) | ||||||
|     callback = functools.partial(self.write, Action.INSTALL, self.name) |     callback = functools.partial(self.write, Action.INSTALL, self.name) | ||||||
|     cmd = 'git clone {0} {1} --recursive {2} -b {3} {4} 2>&1'.format( |     cmd = 'git clone {0} {1} --recursive {2} -b {3} {4} 2>&1'.format( | ||||||
|         '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], self.checkout, esc(target)) |           '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], | ||||||
|     com = Command(cmd, None, G_TIMEOUT, G_RETRIES, callback, clean(target)) |           self.checkout, esc(target)) | ||||||
|     result = com.attempt_cmd() |     com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) | ||||||
|  |     result = com.execute(G_RETRIES) | ||||||
|     self.write(Action.DONE, self.name, result[-1:]) |     self.write(Action.DONE, self.name, result[-1:]) | ||||||
| 
 | 
 | ||||||
|  |   def repo_uri(self): | ||||||
|  |     cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url' | ||||||
|  |     command = Command(cmd, self.args['dir'], G_TIMEOUT,) | ||||||
|  |     result = command.execute(G_RETRIES) | ||||||
|  |     return result[-1] | ||||||
|  | 
 | ||||||
|   def update(self): |   def update(self): | ||||||
|     match = re.compile(r'git::?@') |     match = re.compile(r'git::?@') | ||||||
|     actual_uri = re.sub(match, '', self.repo_uri()) |     actual_uri = re.sub(match, '', self.repo_uri()) | ||||||
|  | @ -1278,18 +1291,12 @@ class Plugin(object): | ||||||
|               'git merge --ff-only {0}'.format(self.merge), |               'git merge --ff-only {0}'.format(self.merge), | ||||||
|               'git submodule update --init --recursive'] |               'git submodule update --init --recursive'] | ||||||
|       cmd = ' 2>&1 && '.join(cmds) |       cmd = ' 2>&1 && '.join(cmds) | ||||||
|       com = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES, callback) |       com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) | ||||||
|       result = com.attempt_cmd() |       result = com.execute(G_RETRIES) | ||||||
|       self.write(Action.DONE, self.name, result[-1:]) |       self.write(Action.DONE, self.name, result[-1:]) | ||||||
|     else: |     else: | ||||||
|       self.write(Action.DONE, self.name, ['Already installed']) |       self.write(Action.DONE, self.name, ['Already installed']) | ||||||
| 
 | 
 | ||||||
|   def repo_uri(self): |  | ||||||
|     cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url' |  | ||||||
|     command = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES) |  | ||||||
|     result = command.attempt_cmd() |  | ||||||
|     return result[-1] |  | ||||||
| 
 |  | ||||||
|   def write(self, action, name, msg): |   def write(self, action, name, msg): | ||||||
|     self.buf_q.put((action, name, msg)) |     self.buf_q.put((action, name, msg)) | ||||||
| 
 | 
 | ||||||
|  | @ -1326,7 +1333,7 @@ class RefreshThread(thr.Thread): | ||||||
|     while self.running: |     while self.running: | ||||||
|       with self.lock: |       with self.lock: | ||||||
|         thread_vim_command('noautocmd normal! a') |         thread_vim_command('noautocmd normal! a') | ||||||
|       time.sleep(0.2) |       time.sleep(0.33) | ||||||
| 
 | 
 | ||||||
|   def stop(self): |   def stop(self): | ||||||
|     self.running = False |     self.running = False | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user