X Tutup
import textwrap, logging from explainshell import util class basefixer(object): '''The base fixer class which other fixers inherit from. Subclasses override the base methods in order to fix manpage content during different parts of the parsing/classifying/saving process.''' runbefore = [] runlast = False def __init__(self, mctx): self.mctx = mctx self.run = True self.logger = logging.getLogger(self.__class__.__name__) def pre_get_raw_manpage(self): pass def pre_parse_manpage(self): pass def post_parse_manpage(self): pass def pre_classify(self): pass def post_classify(self): pass def post_option_extraction(self): pass def pre_add_manpage(self): pass fixerscls = [] fixerspriority = {} class runner(object): '''The runner coordinates the fixers.''' def __init__(self, mctx): self.mctx = mctx self.fixers = [f(mctx) for f in fixerscls] def disable(self, name): before = len(self.fixers) self.fixers = [f for f in self.fixers if f.__class__.__name__ != name] if before == len(self.fixers): raise ValueError('fixer %r not found' % name) def _fixers(self): return (f for f in self.fixers if f.run) def pre_get_raw_manpage(self): for f in self._fixers(): f.pre_get_raw_manpage() def pre_parse_manpage(self): for f in self._fixers(): f.pre_parse_manpage() def post_parse_manpage(self): for f in self._fixers(): f.post_parse_manpage() def pre_classify(self): for f in self._fixers(): f.pre_classify() def post_classify(self): for f in self._fixers(): f.post_classify() def post_option_extraction(self): for f in self._fixers(): f.post_option_extraction() def pre_add_manpage(self): for f in self._fixers(): f.pre_add_manpage() def register(fixercls): fixerscls.append(fixercls) for f in fixercls.runbefore: if not hasattr(f, '_parents'): f._parents = [] f._parents.append(fixercls) return fixercls @register class bulletremover(basefixer): '''remove list bullets from paragraph start, see mysqlslap.1''' def post_parse_manpage(self): toremove = [] for i, p in enumerate(self.mctx.manpage.paragraphs): try: idx = p.text.index('\xc2\xb7') p.text = p.text[:idx] + p.text[idx+2:] if not p.text.strip(): toremove.append(i) except ValueError: pass for i in reversed(toremove): del self.mctx.manpage.paragraphs[i] @register class leadingspaceremover(basefixer): '''go over all known option paragraphs and remove their leading spaces by the amount of spaces in the first line''' def post_option_extraction(self): for i, p in enumerate(self.mctx.manpage.options): text = self._removewhitespace(p.text) p.text = text def _removewhitespace(self, text): ''' >>> f = leadingspaceremover(None) >>> f._removewhitespace(' a\\n b ') 'a\\n b' >>> f._removewhitespace('\\t a\\n\\t \\tb') 'a\\n\\tb' ''' return textwrap.dedent(text).rstrip() @register class tarfixer(basefixer): def __init__(self, *args): super(tarfixer, self).__init__(*args) self.run = self.mctx.name == 'tar' def pre_add_manpage(self): self.mctx.manpage.partialmatch = True @register class paragraphjoiner(basefixer): runbefore = [leadingspaceremover] maxdistance = 5 def post_option_extraction(self): options = [p for p in self.mctx.manpage.paragraphs if p.is_option] self._join(self.mctx.manpage.paragraphs, options) def _join(self, paragraphs, options): def _paragraphsbetween(op1, op2): assert op1.idx < op2.idx r = [] start = None for i, p in enumerate(paragraphs): if op1.idx < p.idx < op2.idx: if not r: start = i r.append(p) return r, start totalmerged = 0 for curr, next in util.pairwise(options): between, start = _paragraphsbetween(curr, next) if curr.section == next.section and 1 <= len(between) < self.maxdistance: self.logger.info('merging paragraphs %d through %d (inclusive)', curr.idx, next.idx-1) newdesc = [curr.text.rstrip()] newdesc.extend([p.text.rstrip() for p in between]) curr.text = '\n\n'.join(newdesc) del paragraphs[start:start+len(between)] totalmerged += len(between) return totalmerged @register class optiontrimmer(basefixer): runbefore = [paragraphjoiner] d = {'git-rebase' : (50, -1)} def __init__(self, mctx): super(optiontrimmer, self).__init__(mctx) self.run = self.mctx.name in self.d def post_classify(self): start, end = self.d[self.mctx.name] classifiedoptions = [p for p in self.mctx.manpage.paragraphs if p.is_option] assert classifiedoptions if end == -1: end = classifiedoptions[-1].idx else: assert start > end for p in classifiedoptions: if not (start <= p.idx <= end): p.is_option = False self.logger.info('removing option %r', p) def _parents(fixercls): p = getattr(fixercls, '_parents', []) last = fixercls.runlast if last and p: raise ValueError("%s can't be last and also run before someone else" % fixercls.__name__) if last: return [f for f in fixerscls if f is not fixercls] return p fixerscls = util.toposorted(fixerscls, _parents)
X Tutup