def prepare(self): super(Patch, self).prepare() self.package = self.kwds['package'] self.subpackage_of = self.kwds.get('subpackage_of') self.patch = self.kwds['patch'] self.branch = self.kwds['branch'] if self.subpackage_of: main_package = self.subpackage_of else: main_package = self.package repo = FedoraGitRepo(main_package, branch=self.branch) diff = repo.get_patch(self.patch) if self.diffstat: self.diffstat = repo.get_diffstat(self.patch) self.text = highlight(diff, DiffLexer(), HtmlFormatter(full=True, nobackground=True)) self.changelog = repo.get_patch_changelog(self.patch)
def get_context_data(self, *args, **kwargs): deploy_id = kwargs['deploy'] context = super(DeployInfo, self).get_context_data(*args, **kwargs) url = '{}/deploys/{}'.format(settings.TSURU_HOST, deploy_id) response = requests.get(url, headers=self.authorization) context['deploy'] = response.json() diff = context['deploy'].get('Diff') if diff and diff != u'The deployment must have at least two commits for the diff.': format = HtmlFormatter() diff = highlight(diff, DiffLexer(), format) else: diff = None context['deploy']['Diff'] = diff return context
def print_diff(path: Path, old: TextDocument, new: TextDocument) -> None: """Print ``black --diff`` style output for the changes""" relative_path = path.resolve().relative_to(Path.cwd()).as_posix() diff = "\n".join( line.rstrip("\n") for line in unified_diff( old.lines, new.lines, relative_path, relative_path)) if sys.stdout.isatty(): try: from pygments import highlight from pygments.formatters import TerminalFormatter from pygments.lexers import DiffLexer except ImportError: print(diff) else: print(highlight(diff, DiffLexer(), TerminalFormatter())) else: print(diff)
def _style_diff(self, diff): """Return a syntax-highlighted version of the diff. Args: diff (bytes): The raw diff content. Returns: django.utils.safestring.SafeText: The syntax-highlighted HTML. """ # NOTE: Django wraps the contents in a <p>, but browsers will # be sad about that, because it contains a <pre>. Chrome, # for instance, will move it out into its own node. Be # consistent and just make that happen for them. return format_html( '</p>{0}<p>', mark_safe(highlight(diff, DiffLexer(), HtmlFormatter())))
def diffmenu(outputfile, diffout): print "[Continue, Abort, View, Replace?] ", response = sys.stdin.readline() rchar = response[0].upper() if rchar == 'C': return elif rchar == 'A': sys.exit(-1) elif rchar == 'V': print highlight(diffout, DiffLexer(), TerminalFormatter(bg="dark")) diffmenu(outputfile, diffout) elif rchar == 'R': dest = 'expected/' + outputfile copy(outputfile, dest) else: print "Please enter a valid option: ", diffmenu(sourcefile, diffout)
def view_diff(self, request, object_id, mode="u", r1=None, r2=None): """ ## ## Render diff form ## :param request: :param object_id: :param mode: :param r1: :param r2: :return: """ o = get_object_or_404(Object.get_object_class(self.repo), id=int(object_id)) if not o.has_access(request.user): return self.response_forbidden("Access denied") if request.POST: r1 = request.POST.get("r1", r1) r2 = request.POST.get("r2", r2) if r1 and r2: rev1 = o.find_revision(r1) rev2 = o.find_revision(r2) if mode == "2": d1 = o.get_revision(rev1) d2 = o.get_revision(rev2) d = difflib.HtmlDiff() diff = d.make_table(d1.splitlines(), d2.splitlines()) diff = diff.replace("rules=\"groups\"", "rules=\"none\"", 1) # Use no colgroup rules else: diff = o.diff(rev1, rev2) diff = unicode(diff, "utf8") diff = highlight(diff, DiffLexer(), NOCHtmlFormatter()) # Highlight diff return self.render(request, "diff.html", { "o": o, "diff": diff, "r1": r1, "r2": r2, "mode": mode }) else: return self.response_redirect_to_object(o)
def get_diff_html(snapshots): html = '' for new, old in zip(snapshots, snapshots[1:]): old_date = '{} ({})'.format(old[0].replace(microsecond=0), humanize.naturaltime(old[0])) new_date = '{} ({})'.format(new[0].replace(microsecond=0), humanize.naturaltime(new[0])) diff_text = '\n'.join( difflib.unified_diff( old[1], new[1], fromfiledate=old_date, tofiledate=new_date, n=5, lineterm='', )) html += highlight(diff_text, DiffLexer(), HtmlFormatter()) return html
def get_context_data(self, **kwargs): deploy_id = kwargs["deploy"] url = "{}/deploys/{}".format(settings.TSURU_HOST, deploy_id) response = requests.get(url, headers=self.authorization) if response.status_code > 399: raise Http404("Deploy does not exist") context = {"deploy": response.json()} diff = context["deploy"].get("Diff") if diff and diff != u'The deployment must have at least two commits for the diff.': format = HtmlFormatter() diff = highlight(diff, DiffLexer(), format) else: diff = None context["deploy"]["Diff"] = diff return context
def get(self, first_rev, second_rev): before = self.db.get(self.table_name, first_rev) after = self.db.get(self.table_name, second_rev) if not (before and after): raise NotFound() if before["date"] > after[ "date"]: # Check whether the before was created after the after raise BadRequest() if before["id"] == after["id"]: # The same revision has been requested raise BadRequest() before_text = before["post"]["rst"] after_text = after["post"]["rst"] if not before_text.endswith("\n"): before_text += "\n" if not after_text.endswith("\n"): after_text += "\n" before_text = before_text.splitlines(keepends=True) after_text = after_text.splitlines(keepends=True) if not before["slug"] == after["slug"]: raise BadRequest() # The revisions are not from the same post diff = difflib.unified_diff(before_text, after_text, fromfile=f"{first_rev}.rst", tofile=f"{second_rev}.rst") diff = "".join(diff) diff = highlight(diff, DiffLexer(), HtmlFormatter()) return self.render("wiki/compare_revision.html", title=after["post"]["title"], page=before["slug"], diff=diff, slug=before["slug"], can_edit=self.is_staff())
def _handle_metadata_post(request, form, return_view): if form.is_valid(): if request.is_ajax(): # validation of metadata and diff calculation diff = form.get_diff() html = highlight(diff, DiffLexer(), HtmlFormatter(linenos=True)) return HttpResponse(html.encode(settings.DEFAULT_CHARSET)) else: # after validation and when user has added the commit message if 'button_clicked' in request.POST and request.POST[ 'button_clicked'] != '': action = request.POST['button_clicked'] else: action = None form.save(action) messages.success(request, _get_success_message(action)) return_url = reverse(return_view, args=(form.entity.id, )) return HttpResponseRedirect(return_url) else: messages.error(request, _('Please correct the errors indicated below')) if request.is_ajax(): sorted_errors = {} for field, errors in form.errors.items(): sorted_error_list = [] for error in errors: if ':ERROR:' in error or ':FATAL:' in error: sorted_error_list.insert(0, error) else: sorted_error_list.append(error) sorted_errors[field] = sorted_error_list content = render_to_string( 'entity/validation_errors.html', { 'errors': sorted_errors, }, context_instance=RequestContext(request)) return HttpResponseBadRequest(content)
def diff_to_html(diff_text): html = HtmlFormatter() return highlight(diff_text, DiffLexer(), html) + "<style>%s</style" % html.get_style_defs()
def _style_diff(self, diff): # NOTE: Django wraps the contents in a <p>, but browsers will # be sad about that, because it contains a <pre>. Chrome, # for instance, will move it out into its own node. Be # consistent and just make that happen for them. return '</p>%s<p>' % highlight(diff, DiffLexer(), HtmlFormatter())
def main(appliances=[], credentials=[], timeout=120, no_check_hostname=False, base_dir=default_base_dir, comment=default_comment, page=False, no_highlight_diff=False): """ track_autoconfig.py Description: Store persisted domain configuration in a local git repository for auditing purposes. Usage: :::bash $ mast contrib/track_autoconfig.py --appliances <HOSTNAMES> --credentials <USER:PASS> --base-dir tmp/config Parameters: * `-a, --appliances` - The hostname(s), ip addresse(s), environment name(s) or alias(es) of the appliances you would like to affect. For details on configuring environments please see the comments in `environments.conf` located in `$MAST_HOME/etc/default`. For details on configuring aliases please see the comments in `hosts.conf` located in `$MAST_HOME/etc/default`. * `-c, --credentials`: The credentials to use for authenticating to the appliances. Should be either one set to use for all appliances or one set for each appliance. Credentials should be in the form `username:password` and should be provided in a space-seperated list if multiple are provided. If you would prefer to not use plain-text passwords, you can use the output of `$ mast-system xor <username:password>`. * `-t, --timeout`: The timeout in seconds to wait for a response from an appliance for any single request. __NOTE__ Program execution may halt if a timeout is reached. * `-n, --no-check-hostname`: If specified SSL verification will be turned off when sending commands to the appliances. * `-b, --base-dir`: The base directory where to store the downloaded files. Files will actually be stored in a subdirectory of `base_dir` named after the hostname in the form of `base_dir/<hostname>` * `-p, --page`: If specified, page the output when too long to display at once * `-N, --no-highlight-diff`: If specified, the output of the diff will be syntax-highlighted """ base_dir = os.path.abspath(base_dir) if not os.path.exists(base_dir): os.makedirs(base_dir) try: repo = git.Repo(base_dir) first_sha1 = repo.head() print first_sha1 except NotGitRepository: print "Initializing git repository" git.init(base_dir) git.add(base_dir) first_sha1 = git.commit(base_dir, message="Initial Commit") print first_sha1 except KeyError: git.add(base_dir) git.commit(base_dir, message="Initial Commit") print pull_autoconfig(appliances=appliances, credentials=credentials, timeout=timeout, no_check_hostname=no_check_hostname, base_dir=base_dir) git.add(base_dir) print git.status(base_dir) second_sha1 = git.commit(base_dir, message=comment) print second_sha1 print "\n\nDIFF\n\n" tmp = StringIO() git.show(base_dir, outstream=tmp) tmp.seek(0) if no_highlight_diff: out = tmp.read() else: out = highlight(tmp.read(), DiffLexer(), TerminalFormatter()) if page: pprint.page(out) else: print out
def main(): import argparse parser = argparse.ArgumentParser( description="reformat the docstrigns of some file") parser.add_argument("files", metavar="files", type=str, nargs="+", help="TODO") parser.add_argument("--context", metavar="context", type=int, default=3) parser.add_argument("--write", dest="write", action="store_true", help="print the diff") args = parser.parse_args() for file in args.files: #print(file) with open(file, "r") as f: data = f.read() tree = ast.parse(data) new = data funcs = [t for t in tree.body if isinstance(t, ast.FunctionDef)] for i, func in enumerate(funcs[:]): # print(i, "==", func.name, "==") try: docstring = func.body[0].value.s except AttributeError: continue if not isinstance(docstring, str): continue start, nindent, stop = ( func.body[0].lineno, func.body[0].col_offset, func.body[0].end_lineno, ) if not docstring in data: print(f"skip {file}: {func.name}, can't do replacement yet") new_doc = compute_new_doc(docstring, file) # test(docstring, file) if new_doc: if ('"""' in new_doc) or ("'''" in new_doc): print("SKIPPING", file, func.name, "triple quote not handled", new_doc) else: new = new.replace(docstring, new_doc) # test(docstring, file) if new != data: dold = data.splitlines() dnew = new.splitlines() diffs = list( difflib.unified_diff(dold, dnew, n=args.context, fromfile=file, tofile=file), ) from pygments import highlight from pygments.lexers import DiffLexer from pygments.formatters import TerminalFormatter if not args.write: code = "\n".join(diffs) hldiff = highlight(code, DiffLexer(), TerminalFormatter()) print(hldiff) else: with open(file, "w") as f: f.write(new)
fd = os.path.realpath(curdir) if os.path.isfile(fd): prog = fd curdir = os.path.dirname(prog) elif not (prog := guess_program(fd)): return [], [(f"Cannot guess program from {curdir}", [])] if not (tests := guess_tests(fd)): return [], [(f"Cannot guess tests from {curdir}", [])] passed, failed = suite(prog, tests) if failed: for name, diff in failed: print(f"\t### {bname(name)} ###") if all((os.isatty(1), highlight, DiffLexer, TerminalFormatter)): print( highlight("".join(diff), DiffLexer(), TerminalFormatter())) else: sys.stdout.writelines(diff) print() return passed, failed if __name__ == "__main__": cwd = os.getcwd() ex = 0 wait = "-" in sys.argv if wait: sys.argv.remove("-") if len(sys.argv) < 2:
def pygmentize_diff(value): return mark_safe(highlight(value, DiffLexer(), HtmlFormatter()))
def _highlight(diff): return pygments.highlight(diff, DiffLexer(), html_formatter)
def diff(self): if self.change_type == 'diff': content = self.content.replace('\\n', '\n') return highlight( content, DiffLexer(), HtmlFormatter(cssclass='code highlight', noclasses=True))
def finished(self): from pygments.lexers import (PythonTracebackLexer, PythonLexer, DiffLexer) if ANSI_COLORS_SUPPORT: from pygments.console import colorize from pygments import highlight if self.style in ('light', 'dark'): from pygments.formatters import TerminalFormatter formatter = TerminalFormatter(bg=self.style) if self.colorscheme is not None: from pygments.token import string_to_tokentype for token, value in self.colorscheme.iteritems(): token = string_to_tokentype(token.capitalize()) formatter.colorscheme[token] = (value, value) else: from pygments.formatters import Terminal256Formatter formatter = Terminal256Formatter(style=self.style) else: # ANSI color codes seem not to be supported, make colorize() # and highlight() no-ops. formatter = None def colorize(_format, text): return text def highlight(text, _lexer, _formatter): return text if self.counter: self.progress.finish() print width, _ = utils.get_terminal_size() def show(result): print colorize('bold', result.test_name) if result.test.__doc__: print inspect.getdoc(result.test) print colorize('faint', '─' * width) for line in result.stdout: print colorize('bold', '→'), print line for line in result.stderr: print colorize('red', '→'), print line if self.verbose: for result in self.passes: if result.stdout or result.stderr: show(result) print for result in self.failures: show(result) # result.traceback seems to be in UTF-8 on my system (eg. for # literal unicode strings) but I guess this depends on the source # file encoding. Tell Pygments to guess: try UTF-8 and then latin1. # Without an `encoding` argument, Pygments just uses latin1. print highlight(result.traceback, PythonTracebackLexer(encoding='guess'), formatter) assertion = result.assertion if assertion is not None: print highlight(assertion, PythonLexer(encoding='guess'), formatter) equality_diff = result.equality_diff if equality_diff is not None: print highlight(equality_diff, DiffLexer(encoding='guess'), formatter) result.debug() if self.failures: failed = colorize('red', str(len(self.failures))) else: failed = len(self.failures) print 'Failures: %s/%s (%s assertions, %.3f seconds)' % ( failed, self.counter, statistics.assertions, self.total_time) if self.failures: raise SystemExit(1)
def set_diff(self, diff): data = highlight(diff, DiffLexer(), HtmlFormatter(noclasses=True)) self._html.display_html(data)
def main(appliances=[], credentials=[], timeout=120, no_check_hostname=False, base_dir=default_base_dir, comment=default_comment, persisted=False, recursive=False, no_strip_timestamp=False, page=False): """ track_getconfig.py Description: Store running or persisted domain configuration in a local git repository for auditing purposes. Usage: :::bash $ mast contrib/track_getconfig.py --appliances <HOSTNAMES> --credentials <USER:PASS> --base-dir tmp/config Parameters: * `-a, --appliances` - The hostname(s), ip addresse(s), environment name(s) or alias(es) of the appliances you would like to affect. For details on configuring environments please see the comments in `environments.conf` located in `$MAST_HOME/etc/default`. For details on configuring aliases please see the comments in `hosts.conf` located in `$MAST_HOME/etc/default`. * `-c, --credentials`: The credentials to use for authenticating to the appliances. Should be either one set to use for all appliances or one set for each appliance. Credentials should be in the form `username:password` and should be provided in a space-seperated list if multiple are provided. If you would prefer to not use plain-text passwords, you can use the output of `$ mast-system xor <username:password>`. * `-t, --timeout`: The timeout in seconds to wait for a response from an appliance for any single request. __NOTE__ Program execution may halt if a timeout is reached. * `-n, --no-check-hostname`: If specified SSL verification will be turned off when sending commands to the appliances. * `-b, --base-dir`: The base directory where to store the downloaded files. Files will actually be stored in a subdirectory of `base_dir` named after the hostname in the form of `base_dir/<hostname>` * `-p, --persisted`: If specified, the persisted configuration will be retrieved as opposed to the running configuration (which is the default) * `-r, --recursive`: If specified, the configuration will be retrieved recursively to the extent of the facilities provided by DataPower * `-N, --no-strip-timestamp`: If specified, the timestamp will not be stripped from the XML document, This is done because we are tracking this information with git and stripping this out allows us to alert on any changes in the repository as opposed to special-casing the difference in timestamp. * `-P, --page`: If specified, page the output when too long to display at once """ base_dir = os.path.abspath(base_dir) if not os.path.exists(base_dir): os.makedirs(base_dir) try: repo = git.Repo(base_dir) repo.head() except NotGitRepository: print "Initializing git repository" git.init(base_dir) git.add(base_dir) git.commit(base_dir, message="Initial Commit") except KeyError: git.add(base_dir) git.commit(base_dir, message="Initial Commit") check_hostname = not no_check_hostname env = datapower.Environment(appliances, credentials, timeout=timeout, check_hostname=check_hostname) for appliance in env.appliances: appliance_directory = os.path.join(base_dir, appliance.hostname) if not os.path.exists(appliance_directory): os.mkdir(appliance_directory) for domain in appliance.domains: config = appliance.get_config(domain=domain, recursive=recursive, persisted=persisted) config = config.pretty if no_strip_timestamp: pass else: config = re.sub(r"^.*?<dp:timestamp>.*?</dp:timestamp>.*?$", r"", config, flags=re.MULTILINE) filename = os.path.join(appliance_directory, "{}.xml".format(domain)) with open(filename, "wb") as fout: fout.write(config) git.add(base_dir) print git.status(base_dir) git.commit(base_dir, message=comment) tmp = StringIO() git.show(base_dir, outstream=tmp) tmp.seek(0) out = highlight(tmp.read(), DiffLexer(), TerminalFormatter()) if page: pprint.page(out) else: print out
def send_extra_emails(self): total = len(self.added_commits) for i, commit in enumerate(self.added_commits): if not commit.id in self.detailed_commits: continue if self.short_refname == 'master': branch = "" else: branch = "/" + self.short_refname total = len(self.added_commits) if total > 1 and self.needs_cover_email: count_string = ": %(index)s/%(total)s" % { 'index': i + 1, 'total': total } else: count_string = "" subject = "[%(projectshort)s%(branch)s%(count_string)s] [%(revision)s] %(subject)s" % { 'projectshort': projectshort, 'branch': branch, 'count_string': count_string, 'revision': git.rev_list('--all').count('\n') + 1 - (total - (i + 1)), 'subject': commit.subject[0:SUBJECT_MAX_SUBJECT_CHARS] } # If there is a cover email, it has the X-Git-OldRev/X-Git-NewRev in it # for the total branch update. Without a cover email, we are conceptually # breaking up the update into individual updates for each commit #if self.needs_cover_email: # self.generate_header(subject, include_revs=False, cc=[]) #else: # parent = git.rev_parse(commit.id + "^") # self.generate_header(subject, # include_revs=True, # oldrev=parent, newrev=commit.id) body_summary = git.show(commit.id, M=True, stat=True) body = body_summary + "\n" + \ git.show(commit.id, p=True, M=True, diff_filter="ACMRTUXB", pretty="format:---") if len(body) > MAX_DETAIL_BODY_SIZE: body = body_summary + "\n (The body has been shortened. Not all diffs are included) \n\n" html_body = None if len(body) < MAX_HTML_BODY_SIZE: try: html_body = highlight( body, DiffLexer(encoding='latin1'), HtmlFormatter(encoding='latin1', full=True, noclasses=True, nobackground=True)) except UnicodeDecodeError: html_body = None self.mailer.send(subject, body, html_body)
def main(): _config = ConfigParser() patterns = [] if Path("setup.cfg").exists(): _config.read("setup.cfg") patterns = [ SkipPattern(x.strip()) for x in _config.get("velin", "ignore_patterns", fallback="").split("\n") if x ] parser = argparse.ArgumentParser(description="reformat the docstrigns of some file") parser.add_argument( "paths", metavar="path", type=str, nargs="+", help="Files or folder to reformat", ) parser.add_argument( "--context", metavar="context", type=int, default=3, help="Number of context lines in the diff", ) parser.add_argument( "--unsafe", action="store_true", help="Lift some safety feature (don't fail if updating the docstring is not indempotent", ) parser.add_argument( "--check", action="store_true", help="Print the list of files/lines number and exit with a non-0 exit status, Use it for CI.", ) parser.add_argument( "--no-diff", action="store_false", dest="print_diff", help="Do not print the diff", ) parser.add_argument( "--black", action="store_true", dest="run_black", help="Do not run black on examples", ) parser.add_argument( "--with-placeholder", action="store_true", dest="with_placeholder", help="insert missing sections/parameters placehoders", ) parser.add_argument("--no-color", action="store_false", dest="do_highlight") parser.add_argument("--compact", action="store_true", help="Please ignore") parser.add_argument("--no-fail", action="store_false", dest="fail") parser.add_argument( "--space-in-see-also-title", action="store_true", dest="space_in_see_also_title" ) parser.add_argument( "--space-in-notes-title", action="store_true", dest="space_in_notes_title" ) parser.add_argument( "--no-fixers", action="store_false", dest="run_fixers", help="try to only reformat and does not run fixers heuristics", ) parser.add_argument( "--write", dest="write", action="store_true", help="Try to write the updated docstring to the files", ) parser.add_argument( "--verbose", action="store_true", help="increase the verbosity of the output", ) args = parser.parse_args() from types import SimpleNamespace config = Config( { "with_placeholder": args.with_placeholder, "compact_param": args.compact, "space_in_see_also_title": args.space_in_see_also_title, "space_in_notes_title": args.space_in_notes_title, "run_fixers": args.run_fixers, } ) global BLACK_REFORMAT if args.run_black: BLACK_REFORMAT = True else: BLACK_REFORMAT = False global print if args.verbose: try: from there import print except ImportError: pass to_format = [] for f in args.paths: p = Path(f) if p.is_dir(): for sf in p.glob("**/*.py"): to_format.append(sf) else: to_format.append(p) def to_skip(file, patterns): for p in patterns: if re.match(p.file, file): if p.obj_pattern is None: return True else: return False return False need_changes = [] for file in to_format: if to_skip(str(file), patterns): print("ignoring", file) continue try: with open(file) as f: data = f.read() except Exception as e: # continue continue raise RuntimeError(f"Fail reading {file}") from e obj_p = [p.obj_pattern for p in patterns if re.match(p.file, str(file))] new = reformat_file( data, file, args.compact, args.unsafe, fail=args.fail, config=config, obj_p=obj_p, ) # test(docstring, file) if new != data: need_changes.append(str(file)) dold = data.splitlines() dnew = new.splitlines() diffs = list( difflib.unified_diff( dold, dnew, n=args.context, fromfile=str(file), tofile=str(file) ), ) if args.print_diff and not args.write: code = "\n".join(diffs) if args.do_highlight: from pygments import highlight from pygments.formatters import TerminalFormatter from pygments.lexers import DiffLexer code = highlight(code, DiffLexer(), TerminalFormatter()) print(code) if args.write: with open(file, "w") as f: f.write(new) if args.check: if len(need_changes) != 0: sys.exit( "Some files/functions need updates:\n - " + "\n - ".join(need_changes) ) else: sys.exit(0)
def diff(self, path): diff = self.system("git diff --ignore-space-at-eol " + path).stdout.strip() html = highlight(diff, DiffLexer(), HtmlFormatter(full=True, style="trac", nowrap=True)) return web.storage(name=path, diff=diff, htmldiff=html)
elif prop.tag == "code": #and prop.text != "InvalidBinaryFile": ticketOut["code"] = prop.text if len(prop.text) < 1000: ticketOut["$$PATCH_SIZE$$"] = "%d bytes" % len(prop.text) else: ticketOut["$$PATCH_SIZE$$"] = "%.1f KB" % (len(prop.text) / 1024.0) if prop.text == "InvalidBinaryFile": ticketOut["$$PATCH$$"] = "<em>Patch is corrupt.</em>" ticketOut["$$PYGMENTS$$"] = "" else: hlCode = prop.text.replace("\t", " ") if len(hlCode) > 5200: hlCode = hlCode[:5000].rstrip( ) + "\ndownload for full patch..." ticketOut["$$PATCH$$"] = highlight(hlCode, DiffLexer( ), HtmlFormatter()).replace( '<pre', '<pre class="pre-scrollable"', 1).replace( 'download for full patch...', '<em><strong>download for full patch...</strong></em>') ticketOut[ "$$PYGMENTS$$"] = '\n <link rel="stylesheet" href="../pygments.css">' if "HISTORY" not in ticketOut: ticketOut["$$HISTORY$$"] = "" ticketOut["$$NUMBER_STAT$$"] = ticketOut["$$NUMBER$$"] else: ticketOut[ "$$NUMBER_STAT$$"] = '<table style="width:100%%"><tr><td>%s</td><td class="text-right"><span class="badge">%d</span></td></tr></table>' % ( ticketOut["$$NUMBER$$"], len(ticketOut["HISTORY"])) ticketsOut.append(ticketOut)
def makemail(entries, txtdiff): """ Create an email definition suiting Amazon SES boto3 API. Args: entries: txtdiff: A piece of unified diff. Returns: Dictionary corresponding to message part of SES boto3 API. """ from pygments import highlight from pygments.lexers import DiffLexer from pygments.formatters import HtmlFormatter loglines = [] naughtyblock = "" pieces = [] filediffs = "" hfmt = HtmlFormatter(style="rainbow_dash", noclasses=True) subjectline = "M-4 Yellow Alert!" for entry in entries: rev, ctime, author, committype, msg = entry[:5] if committype != "merge": ctime = ctime.strftime('%Y-%m-%d %H:%M:%S') arev = """<A href="https://github.com/theme-ontology/theming/commit/%s">%s..</A>""" % (rev, rev[:6]) loglines.append("""<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>""" % (arev, ctime, author, msg)) for line in txtdiff.split("\n"): match = re.match("^diff --git a/(.+?) b/(.+?)$", line) if match: pieces.append([]) if pieces: pieces[-1].append(line.strip()) for block in pieces: txt = '\n'.join(block) naughtylines = profanities(txt) filediffs += """\n%s""" % highlight(txt, DiffLexer(), hfmt) if naughtylines: if len(naughtylines) > 1: subjectline = "M-4 Double Red Alert!" else: subjectline = "M-4 Red Alert!" badlines = "\n".join(x[0] for x in naughtylines) badtypes, badexplain = [], [] for _line, matches in naughtylines: badtypes.extend(x[2].capitalize() for x in matches) badexplain.extend((x[0], x[1]) for x in matches) badtypes = " and ".join(sorted(set(badtypes), reverse=True)) badexplain = ', '.join('"%s" => "%s"' % x for x in sorted(set(badexplain))) naughtyblock = """ <DIV style="background:#ff8888; padding: .1em .5em; margin: 1em 0em;"> <H4>Potentially %s Language Detected in the Vicinity of M-4:</H4> <PRE>%s</PRE> <i style="font-size: xx-small;">%s</i> </DIV> """ % (badtypes, badlines, badexplain) style = lib.email.ST_BASE + lib.email.ST_REAL_RAINBOW_DASH htmldiff = """ <style> table.rainbow_dashtable { } %s </style> <DIV style="margin-bottom: 1em;"> <H4><b>Your friendly M-4 Themeontolonic Assistant</b> detected changes in GIT.</H4> <P>Go to <A href="https://themeontology.org/">https://themeontology.org/</A> for more information.</P> </DIV> %s <TABLE class="motable"> <tr> <th>rev</th> <th>utc</th> <th>author</th> <th>comment</th> </tr> %s </TABLE> %s """ % (style, naughtyblock, "\n".join(loglines), filediffs) return { "Body": { "Html": { "Charset": "UTF-8", "Data": htmldiff, }, }, "Subject": { "Charset": "UTF-8", "Data": subjectline, }, }