From 177c566ecae8a20750aa80d081e681137f8f9d51 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 4 Jun 2015 13:43:31 -0400 Subject: [PATCH] Replace pecan's homegrown interactive debugging middleware with backlash backlash is a port of Werkzeug's debugger middleware to Webob. It has no additional dependencies beyond Webob and is being used by the TurboGears2 team as an alternative to the antiquated Paste/WebError. Leveraging this as an *optional* dependency to pecan would: * Remove a sizable chunk of code from pecan, some of which is embedded JavaScript that packagers have traditionally balked at. * Improve the interactive debugging experience for developers in a very meaningful way (the Werkzeug-based middleware provides features like an in-browser console debugger, the ability to load source code on a frame-by-frame basis). * Improve the unified debugging experience amongst several popular Python frameworks (some form of the debugging interface will be in use by Flask, Pecan, and TurboGears2). Change-Id: I85f50f677c6052bd2afd32811dedf33835135e12 --- docs/source/debug-middleware-1.png | Bin 55246 -> 0 bytes docs/source/debug-middleware-2.png | Bin 6767 -> 0 bytes docs/source/development.rst | 37 +- pecan/__init__.py | 25 +- pecan/core.py | 4 + pecan/middleware/__init__.py | 1 - pecan/middleware/debug.py | 420 ++--- pecan/middleware/resources/XRegExp.js | 664 -------- pecan/middleware/resources/__init__.py | 28 - pecan/middleware/resources/brush-python.js | 64 - pecan/middleware/resources/pecan.png | Bin 4454 -> 0 bytes pecan/middleware/resources/shCore.js | 1731 -------------------- pecan/middleware/resources/syntax.css | 226 --- pecan/middleware/resources/theme.css | 117 -- pecan/tests/middleware/test_debug.py | 83 - pecan/tests/test_base.py | 35 - 16 files changed, 120 insertions(+), 3315 deletions(-) delete mode 100644 docs/source/debug-middleware-1.png delete mode 100644 docs/source/debug-middleware-2.png delete mode 100644 pecan/middleware/resources/XRegExp.js delete mode 100644 pecan/middleware/resources/__init__.py delete mode 100644 pecan/middleware/resources/brush-python.js delete mode 100644 pecan/middleware/resources/pecan.png delete mode 100644 pecan/middleware/resources/shCore.js delete mode 100644 pecan/middleware/resources/syntax.css delete mode 100644 pecan/middleware/resources/theme.css delete mode 100644 pecan/tests/middleware/test_debug.py diff --git a/docs/source/debug-middleware-1.png b/docs/source/debug-middleware-1.png deleted file mode 100644 index 10c91b0e12c0230b0f38cf72eb29d4d17c9e818d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55246 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 W56^DCBCv!32s~Z=T-G@yGywqMWz+Zo diff --git a/docs/source/debug-middleware-2.png b/docs/source/debug-middleware-2.png deleted file mode 100644 index 1c187b8fb621fb318d17bb555f3b15c54578cafd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6767 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 diff --git a/docs/source/development.rst b/docs/source/development.rst index db9bd84..76b2267 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -21,37 +21,18 @@ in your applications. To enable the debugging middleware, simply set the Once enabled, the middleware will automatically catch exceptions raised by your application and display the Python stack trace and WSGI environment in your -browser for easy debugging: +browser when runtime exceptions are raised. -.. figure:: debug-middleware-1.png - :alt: Pecan debug middleware sample output. - :width: 90% +To improve debugging, including support for an interactive browser-based +console, Pecan makes use of the Python `backlash +` library. You’ll need to install it +for development use before continuing:: -To further aid in debugging, the middleware includes the ability to repeat the -offending request, automatically inserting a breakpoint, and dropping your -console into the Python debugger, ``pdb.post_mortem``: + $ pip install backlash + Downloading/unpacking backlash + ... + Successfully installed backlash -.. figure:: debug-middleware-2.png - :alt: Pecan debug middleware request debugger. - -You can also use any debugger with a suitable ``post_mortem`` entry point. -For example, to use the `PuDB Debugger `_, -set ``debugger`` like so:: - - import pudb - - app = { - ... - 'debug': True, - 'debugger': pudb.post_mortem, - ... - } - -.. seealso:: - - Refer to the `pdb documentation - `_ for more information on - using the Python debugger. Serving Static Files -------------------- diff --git a/pecan/__init__.py b/pecan/__init__.py index 3987892..1adcc71 100644 --- a/pecan/__init__.py +++ b/pecan/__init__.py @@ -20,7 +20,6 @@ except ImportError: from logutils.dictconfig import dictConfig as load_logging_config # noqa -import six import warnings @@ -44,8 +43,6 @@ def make_app(root, **kw): debug mode is set. :param debug: A flag to enable debug mode. This enables the debug middleware and serving static files. - :param debugger: A callable to start debugging, defaulting to the Python - debugger entry point ``pdb.post_mortem``. :param wrap_app: A function or middleware class to wrap the Pecan app. This must either be a wsgi middleware class or a function that returns a wsgi application. This wrapper @@ -101,19 +98,19 @@ def make_app(root, **kw): # Included for internal redirect support app = middleware.recursive.RecursiveMiddleware(app) - # When in debug mode, load our exception dumping middleware + # When in debug mode, load exception debugging middleware static_root = kw.get('static_root', None) if debug: - debugger = kw.get('debugger', None) - debugger_kwargs = {} - if six.callable(debugger): - debugger_kwargs['debugger'] = debugger - elif debugger: - warnings.warn( - "`app.debugger` is not callable, ignoring", - RuntimeWarning - ) - app = middleware.debug.DebugMiddleware(app, **debugger_kwargs) + debug_kwargs = getattr(conf, 'debug', {}) + debug_kwargs.setdefault('context_injectors', []).append( + lambda environ: { + 'request': environ.get('pecan.locals', {}).get('request') + } + ) + app = DebugMiddleware( + app, + **debug_kwargs + ) # Support for serving static files (for development convenience) if static_root: diff --git a/pecan/core.py b/pecan/core.py index 14b9ad3..567f8e5 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -660,6 +660,10 @@ def __call__(self, environ, start_response): req = self.request_cls(environ) resp = self.response_cls() state = RoutingState(req, resp, self) + environ['pecan.locals'] = { + 'request': req, + 'response': resp + } controller = None # handle the request diff --git a/pecan/middleware/__init__.py b/pecan/middleware/__init__.py index d95b453..0c82911 100644 --- a/pecan/middleware/__init__.py +++ b/pecan/middleware/__init__.py @@ -1,4 +1,3 @@ -from . import debug from . import errordocument from . import recursive from . import static diff --git a/pecan/middleware/debug.py b/pecan/middleware/debug.py index ec2faa0..1a6a482 100644 --- a/pecan/middleware/debug.py +++ b/pecan/middleware/debug.py @@ -1,324 +1,96 @@ -from traceback import print_exc -from pprint import pformat -import pdb - -from six.moves import cStringIO as StringIO - -from mako.template import Template -from webob import Response - -from .resources import (pecan_image, xregexp_js, syntax_js, syntax_css, theme, - brush) - - -debug_template_raw = ''' - - Pecan - Application Error - - - - - - - - - - - - - - - -

-

- pecan logo - application error -

-
- -
- -

- To disable this interface, set -

conf.app.debug = False
-

- -

Traceback

-
-
${traceback}
-
- - % if not debugging: - Want to debug this request? -
- You can with a Python debugger breakpoint. -
- % endif - -

WSGI Environment

-
-
${environment}
-
-
- - -''' - -debug_template = Template(debug_template_raw) -__debug_environ__ = None - - -class PdbMiddleware(object): - def __init__(self, app, debugger): - self.app = app - self.debugger = debugger - - def __call__(self, environ, start_response): - try: - return self.app(environ, start_response) - except: - self.debugger() - - -class DebugMiddleware(object): - """A WSGI middleware that provides debugging assistance for development - environments. - - To enable the debugging middleware, simply set the ``debug`` flag to - ``True`` in your configuration file:: - - app = { - ... - 'debug': True, - ... - } - - Once enabled, the middleware will automatically catch exceptions raised by - your application, and display the Python stack trace and WSGI environment - in your browser for easy debugging. - - To further aid in debugging, the middleware includes the ability to repeat - the offending request, automatically inserting a breakpoint, and dropping - your console into the Python debugger, ``pdb.post_mortem``. - - You can also use any debugger with a suitable ``post_mortem`` entry point - such as the `PuDB Debugger `_, - - For more information, refer to the `documentation for pdb - `_ available on the Python - website. - - :param app: the application to wrap. - :param debugger: a callable to start debugging, defaulting to the Python - debugger entry point ``pdb.post_mortem``. - """ - - def __init__(self, app, debugger=pdb.post_mortem): - self.app = app - self.debugger = debugger - - def __call__(self, environ, start_response): - if environ['wsgi.multiprocess']: - raise RuntimeError( - "The DebugMiddleware middleware is not usable in a " - "multi-process environment" - ) - - if environ.get('paste.testing'): - return self.app(environ, start_response) - - # initiate a PDB session if requested - global __debug_environ__ - debugging = environ['PATH_INFO'] == '/__pecan_initiate_pdb__' - if debugging: - PdbMiddleware(self.app, self.debugger)( - __debug_environ__, start_response - ) - environ = __debug_environ__ - - try: - return self.app(environ, start_response) - except: - # save the environ for debugging - if not debugging: - __debug_environ__ = environ - - # get a formatted exception - out = StringIO() - print_exc(file=out) - - # get formatted WSGI environment - formatted_environ = pformat(environ) - - # render our template - result = debug_template.render( - traceback=out.getvalue(), - environment=formatted_environ, - pecan_image=pecan_image, - xregexp_js=xregexp_js, - syntax_js=syntax_js, - brush=brush, - syntax_css=syntax_css, - theme=theme, - debugging=debugging - ) - - # construct and return our response - response = Response() - response.status_int = 400 - response.unicode_body = result - return response(environ, start_response) +__CONFIG_HELP__ = ''' +
+ To disable this interface, set + +
conf.app.debug = False
+
+
+''' # noqa + +try: + import re + from backlash.debug import DebuggedApplication + + class DebugMiddleware(DebuggedApplication): + + body_re = re.compile('(]*>)', re.I) + + def debug_application(self, environ, start_response): + for part in super(DebugMiddleware, self).debug_application( + environ, start_response + ): + yield self.body_re.sub('\g<1>%s' % __CONFIG_HELP__, part) + + +except ImportError: + from traceback import print_exc + from pprint import pformat + + from mako.template import Template + from six.moves import cStringIO as StringIO + from webob import Response + from webob.exc import HTTPException + + debug_template_raw = ''' + + Pecan - Application Error + +
+

+ An error occurred! +

+
+
+

+ %(config_help)s + Pecan offers support for interactive debugging by installing the backlash package: +
+

pip install backlash
+ ...and reloading this page. +

+

Traceback

+
+
${traceback}
+
+

WSGI Environment

+
+
${environment}
+
+
+ + + ''' % {'config_help': __CONFIG_HELP__} # noqa + + debug_template = Template(debug_template_raw) + + class DebugMiddleware(object): + + def __init__(self, app, *args, **kwargs): + self.app = app + + def __call__(self, environ, start_response): + try: + return self.app(environ, start_response) + except Exception as exc: + # get a formatted exception + out = StringIO() + print_exc(file=out) + + # get formatted WSGI environment + formatted_environ = pformat(environ) + + # render our template + result = debug_template.render( + traceback=out.getvalue(), + environment=formatted_environ + ) + + # construct and return our response + response = Response() + if isinstance(exc, HTTPException): + response.status_int = exc.status + else: + response.status_int = 500 + response.unicode_body = result + return response(environ, start_response) diff --git a/pecan/middleware/resources/XRegExp.js b/pecan/middleware/resources/XRegExp.js deleted file mode 100644 index ebdb9c9..0000000 --- a/pecan/middleware/resources/XRegExp.js +++ /dev/null @@ -1,664 +0,0 @@ -// XRegExp 1.5.1 -// (c) 2007-2012 Steven Levithan -// MIT License -// -// Provides an augmented, extensible, cross-browser implementation of regular expressions, -// including support for additional syntax, flags, and methods - -var XRegExp; - -if (XRegExp) { - // Avoid running twice, since that would break references to native globals - throw Error("can't load XRegExp twice in the same frame"); -} - -// Run within an anonymous function to protect variables and avoid new globals -(function (undefined) { - - //--------------------------------- - // Constructor - //--------------------------------- - - // Accepts a pattern and flags; returns a new, extended `RegExp` object. Differs from a native - // regular expression in that additional syntax and flags are supported and cross-browser - // syntax inconsistencies are ameliorated. `XRegExp(/regex/)` clones an existing regex and - // converts to type XRegExp - XRegExp = function (pattern, flags) { - var output = [], - currScope = XRegExp.OUTSIDE_CLASS, - pos = 0, - context, tokenResult, match, chr, regex; - - if (XRegExp.isRegExp(pattern)) { - if (flags !== undefined) - throw TypeError("can't supply flags when constructing one RegExp from another"); - return clone(pattern); - } - // Tokens become part of the regex construction process, so protect against infinite - // recursion when an XRegExp is constructed within a token handler or trigger - if (isInsideConstructor) - throw Error("can't call the XRegExp constructor within token definition functions"); - - flags = flags || ""; - context = { // `this` object for custom tokens - hasNamedCapture: false, - captureNames: [], - hasFlag: function (flag) {return flags.indexOf(flag) > -1;}, - setFlag: function (flag) {flags += flag;} - }; - - while (pos < pattern.length) { - // Check for custom tokens at the current position - tokenResult = runTokens(pattern, pos, currScope, context); - - if (tokenResult) { - output.push(tokenResult.output); - pos += (tokenResult.match[0].length || 1); - } else { - // Check for native multicharacter metasequences (excluding character classes) at - // the current position - if (match = nativ.exec.call(nativeTokens[currScope], pattern.slice(pos))) { - output.push(match[0]); - pos += match[0].length; - } else { - chr = pattern.charAt(pos); - if (chr === "[") - currScope = XRegExp.INSIDE_CLASS; - else if (chr === "]") - currScope = XRegExp.OUTSIDE_CLASS; - // Advance position one character - output.push(chr); - pos++; - } - } - } - - regex = RegExp(output.join(""), nativ.replace.call(flags, flagClip, "")); - regex._xregexp = { - source: pattern, - captureNames: context.hasNamedCapture ? context.captureNames : null - }; - return regex; - }; - - - //--------------------------------- - // Public properties - //--------------------------------- - - XRegExp.version = "1.5.1"; - - // Token scope bitflags - XRegExp.INSIDE_CLASS = 1; - XRegExp.OUTSIDE_CLASS = 2; - - - //--------------------------------- - // Private variables - //--------------------------------- - - var replacementToken = /\$(?:(\d\d?|[$&`'])|{([$\w]+)})/g, - flagClip = /[^gimy]+|([\s\S])(?=[\s\S]*\1)/g, // Nonnative and duplicate flags - quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/, - isInsideConstructor = false, - tokens = [], - // Copy native globals for reference ("native" is an ES3 reserved keyword) - nativ = { - exec: RegExp.prototype.exec, - test: RegExp.prototype.test, - match: String.prototype.match, - replace: String.prototype.replace, - split: String.prototype.split - }, - compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups - compliantLastIndexIncrement = function () { - var x = /^/g; - nativ.test.call(x, ""); - return !x.lastIndex; - }(), - hasNativeY = RegExp.prototype.sticky !== undefined, - nativeTokens = {}; - - // `nativeTokens` match native multicharacter metasequences only (including deprecated octals, - // excluding character classes) - nativeTokens[XRegExp.INSIDE_CLASS] = /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/; - nativeTokens[XRegExp.OUTSIDE_CLASS] = /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/; - - - //--------------------------------- - // Public methods - //--------------------------------- - - // Lets you extend or change XRegExp syntax and create custom flags. This is used internally by - // the XRegExp library and can be used to create XRegExp plugins. This function is intended for - // users with advanced knowledge of JavaScript's regular expression syntax and behavior. It can - // be disabled by `XRegExp.freezeTokens` - XRegExp.addToken = function (regex, handler, scope, trigger) { - tokens.push({ - pattern: clone(regex, "g" + (hasNativeY ? "y" : "")), - handler: handler, - scope: scope || XRegExp.OUTSIDE_CLASS, - trigger: trigger || null - }); - }; - - // Accepts a pattern and flags; returns an extended `RegExp` object. If the pattern and flag - // combination has previously been cached, the cached copy is returned; otherwise the newly - // created regex is cached - XRegExp.cache = function (pattern, flags) { - var key = pattern + "/" + (flags || ""); - return XRegExp.cache[key] || (XRegExp.cache[key] = XRegExp(pattern, flags)); - }; - - // Accepts a `RegExp` instance; returns a copy with the `/g` flag set. The copy has a fresh - // `lastIndex` (set to zero). If you want to copy a regex without forcing the `global` - // property, use `XRegExp(regex)`. Do not use `RegExp(regex)` because it will not preserve - // special properties required for named capture - XRegExp.copyAsGlobal = function (regex) { - return clone(regex, "g"); - }; - - // Accepts a string; returns the string with regex metacharacters escaped. The returned string - // can safely be used at any point within a regex to match the provided literal string. Escaped - // characters are [ ] { } ( ) * + ? - . , \ ^ $ | # and whitespace - XRegExp.escape = function (str) { - return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - }; - - // Accepts a string to search, regex to search with, position to start the search within the - // string (default: 0), and an optional Boolean indicating whether matches must start at-or- - // after the position or at the specified position only. This function ignores the `lastIndex` - // of the provided regex in its own handling, but updates the property for compatibility - XRegExp.execAt = function (str, regex, pos, anchored) { - var r2 = clone(regex, "g" + ((anchored && hasNativeY) ? "y" : "")), - match; - r2.lastIndex = pos = pos || 0; - match = r2.exec(str); // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (anchored && match && match.index !== pos) - match = null; - if (regex.global) - regex.lastIndex = match ? r2.lastIndex : 0; - return match; - }; - - // Breaks the unrestorable link to XRegExp's private list of tokens, thereby preventing - // syntax and flag changes. Should be run after XRegExp and any plugins are loaded - XRegExp.freezeTokens = function () { - XRegExp.addToken = function () { - throw Error("can't run addToken after freezeTokens"); - }; - }; - - // Accepts any value; returns a Boolean indicating whether the argument is a `RegExp` object. - // Note that this is also `true` for regex literals and regexes created by the `XRegExp` - // constructor. This works correctly for variables created in another frame, when `instanceof` - // and `constructor` checks would fail to work as intended - XRegExp.isRegExp = function (o) { - return Object.prototype.toString.call(o) === "[object RegExp]"; - }; - - // Executes `callback` once per match within `str`. Provides a simpler and cleaner way to - // iterate over regex matches compared to the traditional approaches of subverting - // `String.prototype.replace` or repeatedly calling `exec` within a `while` loop - XRegExp.iterate = function (str, regex, callback, context) { - var r2 = clone(regex, "g"), - i = -1, match; - while (match = r2.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (regex.global) - regex.lastIndex = r2.lastIndex; // Doing this to follow expectations if `lastIndex` is checked within `callback` - callback.call(context, match, ++i, str, regex); - if (r2.lastIndex === match.index) - r2.lastIndex++; - } - if (regex.global) - regex.lastIndex = 0; - }; - - // Accepts a string and an array of regexes; returns the result of using each successive regex - // to search within the matches of the previous regex. The array of regexes can also contain - // objects with `regex` and `backref` properties, in which case the named or numbered back- - // references specified are passed forward to the next regex or returned. E.g.: - // var xregexpImgFileNames = XRegExp.matchChain(html, [ - // {regex: /]+)>/i, backref: 1}, // tag attributes - // {regex: XRegExp('(?ix) \\s src=" (? [^"]+ )'), backref: "src"}, // src attribute values - // {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths - // /[^\/]+$/ // filenames (strip directory paths) - // ]); - XRegExp.matchChain = function (str, chain) { - return function recurseChain (values, level) { - var item = chain[level].regex ? chain[level] : {regex: chain[level]}, - regex = clone(item.regex, "g"), - matches = [], i; - for (i = 0; i < values.length; i++) { - XRegExp.iterate(values[i], regex, function (match) { - matches.push(item.backref ? (match[item.backref] || "") : match[0]); - }); - } - return ((level === chain.length - 1) || !matches.length) ? - matches : recurseChain(matches, level + 1); - }([str], 0); - }; - - - //--------------------------------- - // New RegExp prototype methods - //--------------------------------- - - // Accepts a context object and arguments array; returns the result of calling `exec` with the - // first value in the arguments array. the context is ignored but is accepted for congruity - // with `Function.prototype.apply` - RegExp.prototype.apply = function (context, args) { - return this.exec(args[0]); - }; - - // Accepts a context object and string; returns the result of calling `exec` with the provided - // string. the context is ignored but is accepted for congruity with `Function.prototype.call` - RegExp.prototype.call = function (context, str) { - return this.exec(str); - }; - - - //--------------------------------- - // Overriden native methods - //--------------------------------- - - // Adds named capture support (with backreferences returned as `result.name`), and fixes two - // cross-browser issues per ES3: - // - Captured values for nonparticipating capturing groups should be returned as `undefined`, - // rather than the empty string. - // - `lastIndex` should not be incremented after zero-length matches. - RegExp.prototype.exec = function (str) { - var match, name, r2, origLastIndex; - if (!this.global) - origLastIndex = this.lastIndex; - match = nativ.exec.apply(this, arguments); - if (match) { - // Fix browsers whose `exec` methods don't consistently return `undefined` for - // nonparticipating capturing groups - if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { - r2 = RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", "")); - // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed - // matching due to characters outside the match - nativ.replace.call((str + "").slice(match.index), r2, function () { - for (var i = 1; i < arguments.length - 2; i++) { - if (arguments[i] === undefined) - match[i] = undefined; - } - }); - } - // Attach named capture properties - if (this._xregexp && this._xregexp.captureNames) { - for (var i = 1; i < match.length; i++) { - name = this._xregexp.captureNames[i - 1]; - if (name) - match[name] = match[i]; - } - } - // Fix browsers that increment `lastIndex` after zero-length matches - if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) - this.lastIndex--; - } - if (!this.global) - this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - return match; - }; - - // Fix browser bugs in native method - RegExp.prototype.test = function (str) { - // Use the native `exec` to skip some processing overhead, even though the altered - // `exec` would take care of the `lastIndex` fixes - var match, origLastIndex; - if (!this.global) - origLastIndex = this.lastIndex; - match = nativ.exec.call(this, str); - // Fix browsers that increment `lastIndex` after zero-length matches - if (match && !compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) - this.lastIndex--; - if (!this.global) - this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - return !!match; - }; - - // Adds named capture support and fixes browser bugs in native method - String.prototype.match = function (regex) { - if (!XRegExp.isRegExp(regex)) - regex = RegExp(regex); // Native `RegExp` - if (regex.global) { - var result = nativ.match.apply(this, arguments); - regex.lastIndex = 0; // Fix IE bug - return result; - } - return regex.exec(this); // Run the altered `exec` - }; - - // Adds support for `${n}` tokens for named and numbered backreferences in replacement text, - // and provides named backreferences to replacement functions as `arguments[0].name`. Also - // fixes cross-browser differences in replacement text syntax when performing a replacement - // using a nonregex search value, and the value of replacement regexes' `lastIndex` property - // during replacement iterations. Note that this doesn't support SpiderMonkey's proprietary - // third (`flags`) parameter - String.prototype.replace = function (search, replacement) { - var isRegex = XRegExp.isRegExp(search), - captureNames, result, str, origLastIndex; - - // There are too many combinations of search/replacement types/values and browser bugs that - // preclude passing to native `replace`, so don't try - //if (...) - // return nativ.replace.apply(this, arguments); - - if (isRegex) { - if (search._xregexp) - captureNames = search._xregexp.captureNames; // Array or `null` - if (!search.global) - origLastIndex = search.lastIndex; - } else { - search = search + ""; // Type conversion - } - - if (Object.prototype.toString.call(replacement) === "[object Function]") { - result = nativ.replace.call(this + "", search, function () { - if (captureNames) { - // Change the `arguments[0]` string primitive to a String object which can store properties - arguments[0] = new String(arguments[0]); - // Store named backreferences on `arguments[0]` - for (var i = 0; i < captureNames.length; i++) { - if (captureNames[i]) - arguments[0][captureNames[i]] = arguments[i + 1]; - } - } - // Update `lastIndex` before calling `replacement` (fix browsers) - if (isRegex && search.global) - search.lastIndex = arguments[arguments.length - 2] + arguments[0].length; - return replacement.apply(null, arguments); - }); - } else { - str = this + ""; // Type conversion, so `args[args.length - 1]` will be a string (given nonstring `this`) - result = nativ.replace.call(str, search, function () { - var args = arguments; // Keep this function's `arguments` available through closure - return nativ.replace.call(replacement + "", replacementToken, function ($0, $1, $2) { - // Numbered backreference (without delimiters) or special variable - if ($1) { - switch ($1) { - case "$": return "$"; - case "&": return args[0]; - case "`": return args[args.length - 1].slice(0, args[args.length - 2]); - case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length); - // Numbered backreference - default: - // What does "$10" mean? - // - Backreference 10, if 10 or more capturing groups exist - // - Backreference 1 followed by "0", if 1-9 capturing groups exist - // - Otherwise, it's the string "$10" - // Also note: - // - Backreferences cannot be more than two digits (enforced by `replacementToken`) - // - "$01" is equivalent to "$1" if a capturing group exists, otherwise it's the string "$01" - // - There is no "$0" token ("$&" is the entire match) - var literalNumbers = ""; - $1 = +$1; // Type conversion; drop leading zero - if (!$1) // `$1` was "0" or "00" - return $0; - while ($1 > args.length - 3) { - literalNumbers = String.prototype.slice.call($1, -1) + literalNumbers; - $1 = Math.floor($1 / 10); // Drop the last digit - } - return ($1 ? args[$1] || "" : "$") + literalNumbers; - } - // Named backreference or delimited numbered backreference - } else { - // What does "${n}" mean? - // - Backreference to numbered capture n. Two differences from "$n": - // - n can be more than two digits - // - Backreference 0 is allowed, and is the entire match - // - Backreference to named capture n, if it exists and is not a number overridden by numbered capture - // - Otherwise, it's the string "${n}" - var n = +$2; // Type conversion; drop leading zeros - if (n <= args.length - 3) - return args[n]; - n = captureNames ? indexOf(captureNames, $2) : -1; - return n > -1 ? args[n + 1] : $0; - } - }); - }); - } - - if (isRegex) { - if (search.global) - search.lastIndex = 0; // Fix IE, Safari bug (last tested IE 9.0.5, Safari 5.1.2 on Windows) - else - search.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - } - - return result; - }; - - // A consistent cross-browser, ES3 compliant `split` - String.prototype.split = function (s /* separator */, limit) { - // If separator `s` is not a regex, use the native `split` - if (!XRegExp.isRegExp(s)) - return nativ.split.apply(this, arguments); - - var str = this + "", // Type conversion - output = [], - lastLastIndex = 0, - match, lastLength; - - // Behavior for `limit`: if it's... - // - `undefined`: No limit - // - `NaN` or zero: Return an empty array - // - A positive number: Use `Math.floor(limit)` - // - A negative number: No limit - // - Other: Type-convert, then use the above rules - if (limit === undefined || +limit < 0) { - limit = Infinity; - } else { - limit = Math.floor(+limit); - if (!limit) - return []; - } - - // This is required if not `s.global`, and it avoids needing to set `s.lastIndex` to zero - // and restore it to its original value when we're done using the regex - s = XRegExp.copyAsGlobal(s); - - while (match = s.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (s.lastIndex > lastLastIndex) { - output.push(str.slice(lastLastIndex, match.index)); - - if (match.length > 1 && match.index < str.length) - Array.prototype.push.apply(output, match.slice(1)); - - lastLength = match[0].length; - lastLastIndex = s.lastIndex; - - if (output.length >= limit) - break; - } - - if (s.lastIndex === match.index) - s.lastIndex++; - } - - if (lastLastIndex === str.length) { - if (!nativ.test.call(s, "") || lastLength) - output.push(""); - } else { - output.push(str.slice(lastLastIndex)); - } - - return output.length > limit ? output.slice(0, limit) : output; - }; - - - //--------------------------------- - // Private helper functions - //--------------------------------- - - // Supporting function for `XRegExp`, `XRegExp.copyAsGlobal`, etc. Returns a copy of a `RegExp` - // instance with a fresh `lastIndex` (set to zero), preserving properties required for named - // capture. Also allows adding new flags in the process of copying the regex - function clone (regex, additionalFlags) { - if (!XRegExp.isRegExp(regex)) - throw TypeError("type RegExp expected"); - var x = regex._xregexp; - regex = XRegExp(regex.source, getNativeFlags(regex) + (additionalFlags || "")); - if (x) { - regex._xregexp = { - source: x.source, - captureNames: x.captureNames ? x.captureNames.slice(0) : null - }; - } - return regex; - } - - function getNativeFlags (regex) { - return (regex.global ? "g" : "") + - (regex.ignoreCase ? "i" : "") + - (regex.multiline ? "m" : "") + - (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3 - (regex.sticky ? "y" : ""); - } - - function runTokens (pattern, index, scope, context) { - var i = tokens.length, - result, match, t; - // Protect against constructing XRegExps within token handler and trigger functions - isInsideConstructor = true; - // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws - try { - while (i--) { // Run in reverse order - t = tokens[i]; - if ((scope & t.scope) && (!t.trigger || t.trigger.call(context))) { - t.pattern.lastIndex = index; - match = t.pattern.exec(pattern); // Running the altered `exec` here allows use of named backreferences, etc. - if (match && match.index === index) { - result = { - output: t.handler.call(context, match, scope), - match: match - }; - break; - } - } - } - } catch (err) { - throw err; - } finally { - isInsideConstructor = false; - } - return result; - } - - function indexOf (array, item, from) { - if (Array.prototype.indexOf) // Use the native array method if available - return array.indexOf(item, from); - for (var i = from || 0; i < array.length; i++) { - if (array[i] === item) - return i; - } - return -1; - } - - - //--------------------------------- - // Built-in tokens - //--------------------------------- - - // Augment XRegExp's regular expression syntax and flags. Note that when adding tokens, the - // third (`scope`) argument defaults to `XRegExp.OUTSIDE_CLASS` - - // Comment pattern: (?# ) - XRegExp.addToken( - /\(\?#[^)]*\)/, - function (match) { - // Keep tokens separated unless the following token is a quantifier - return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; - } - ); - - // Capturing group (match the opening parenthesis only). - // Required for support of named capturing groups - XRegExp.addToken( - /\((?!\?)/, - function () { - this.captureNames.push(null); - return "("; - } - ); - - // Named capturing group (match the opening delimiter only): (? - XRegExp.addToken( - /\(\?<([$\w]+)>/, - function (match) { - this.captureNames.push(match[1]); - this.hasNamedCapture = true; - return "("; - } - ); - - // Named backreference: \k - XRegExp.addToken( - /\\k<([\w$]+)>/, - function (match) { - var index = indexOf(this.captureNames, match[1]); - // Keep backreferences separate from subsequent literal numbers. Preserve back- - // references to named groups that are undefined at this point as literal strings - return index > -1 ? - "\\" + (index + 1) + (isNaN(match.input.charAt(match.index + match[0].length)) ? "" : "(?:)") : - match[0]; - } - ); - - // Empty character class: [] or [^] - XRegExp.addToken( - /\[\^?]/, - function (match) { - // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S]. - // (?!) should work like \b\B, but is unreliable in Firefox - return match[0] === "[]" ? "\\b\\B" : "[\\s\\S]"; - } - ); - - // Mode modifier at the start of the pattern only, with any combination of flags imsx: (?imsx) - // Does not support x(?i), (?-i), (?i-m), (?i: ), (?i)(?m), etc. - XRegExp.addToken( - /^\(\?([imsx]+)\)/, - function (match) { - this.setFlag(match[1]); - return ""; - } - ); - - // Whitespace and comments, in free-spacing (aka extended) mode only - XRegExp.addToken( - /(?:\s+|#.*)+/, - function (match) { - // Keep tokens separated unless the following token is a quantifier - return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; - }, - XRegExp.OUTSIDE_CLASS, - function () {return this.hasFlag("x");} - ); - - // Dot, in dotall (aka singleline) mode only - XRegExp.addToken( - /\./, - function () {return "[\\s\\S]";}, - XRegExp.OUTSIDE_CLASS, - function () {return this.hasFlag("s");} - ); - - - //--------------------------------- - // Backward compatibility - //--------------------------------- - - // Uncomment the following block for compatibility with XRegExp 1.0-1.2: - /* - XRegExp.matchWithinChain = XRegExp.matchChain; - RegExp.prototype.addFlags = function (s) {return clone(this, s);}; - RegExp.prototype.execAll = function (s) {var r = []; XRegExp.iterate(s, this, function (m) {r.push(m);}); return r;}; - RegExp.prototype.forEachExec = function (s, f, c) {return XRegExp.iterate(s, this, f, c);}; - RegExp.prototype.validate = function (s) {var r = RegExp("^(?:" + this.source + ")$(?!\\s)", getNativeFlags(this)); if (this.global) this.lastIndex = 0; return s.search(r) === 0;}; - */ - -})(); - diff --git a/pecan/middleware/resources/__init__.py b/pecan/middleware/resources/__init__.py deleted file mode 100644 index 2d8c627..0000000 --- a/pecan/middleware/resources/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -from mimetypes import guess_type -from contextlib import closing -from base64 import b64encode - -from pecan.compat import quote - - -def load_resource(filename): - with closing(open( - os.path.join( - os.path.dirname(__file__), - filename - ), - 'rb' - )) as f: - data = f.read() - return 'data:%s;base64,%s' % ( - guess_type(filename)[0], - quote(b64encode(data)) - ) - -pecan_image = load_resource('pecan.png') -xregexp_js = load_resource('XRegExp.js') -syntax_js = load_resource('shCore.js') -syntax_css = load_resource('syntax.css') -theme = load_resource('theme.css') -brush = load_resource('brush-python.js') diff --git a/pecan/middleware/resources/brush-python.js b/pecan/middleware/resources/brush-python.js deleted file mode 100644 index 30f0a8f..0000000 --- a/pecan/middleware/resources/brush-python.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ -(function() -{ - // CommonJS - typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; - - function Brush() - { - // Contributed by Gheorghe Milas and Ahmad Sherif - - var keywords = 'and assert break class continue def del elif else ' + - 'except exec finally for from global if import in is ' + - 'lambda not or pass print raise return try yield while'; - - var funcs = '__import__ abs all any apply basestring bin bool buffer callable ' + - 'chr classmethod cmp coerce compile complex delattr dict dir ' + - 'divmod enumerate eval execfile file filter float format frozenset ' + - 'getattr globals hasattr hash help hex id input int intern ' + - 'isinstance issubclass iter len list locals long map max min next ' + - 'object oct open ord pow print property range raw_input reduce ' + - 'reload repr reversed round set setattr slice sorted staticmethod ' + - 'str sum super tuple type type unichr unicode vars xrange zip'; - - var special = 'None True False self cls class_'; - - this.regexList = [ - { regex: SyntaxHighlighter.regexLib.singleLinePerlComments, css: 'comments' }, - { regex: /^\s*@\w+/gm, css: 'decorator' }, - { regex: /(['\"]{3})([^\1])*?\1/gm, css: 'comments' }, - { regex: /"(?!")(?:\.|\\\"|[^\""\n])*"/gm, css: 'string' }, - { regex: /'(?!')(?:\.|(\\\')|[^\''\n])*'/gm, css: 'string' }, - { regex: /\+|\-|\*|\/|\%|=|==/gm, css: 'keyword' }, - { regex: /\b\d+\.?\w*/g, css: 'value' }, - { regex: new RegExp(this.getKeywords(funcs), 'gmi'), css: 'functions' }, - { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, - { regex: new RegExp(this.getKeywords(special), 'gm'), css: 'color1' } - ]; - - this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags); - }; - - Brush.prototype = new SyntaxHighlighter.Highlighter(); - Brush.aliases = ['py', 'python']; - - SyntaxHighlighter.brushes.Python = Brush; - - // CommonJS - typeof(exports) != 'undefined' ? exports.Brush = Brush : null; -})(); diff --git a/pecan/middleware/resources/pecan.png b/pecan/middleware/resources/pecan.png deleted file mode 100644 index 16f2490be78aac47852e5795d4967aeb09cde6be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4454 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 diff --git a/pecan/middleware/resources/shCore.js b/pecan/middleware/resources/shCore.js deleted file mode 100644 index 320e0b5..0000000 --- a/pecan/middleware/resources/shCore.js +++ /dev/null @@ -1,1731 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * @VERSION@ (@DATE@) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ - -// -// Begin anonymous function. This is used to contain local scope variables without polutting global scope. -// -if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() { - -// CommonJS -if (typeof(require) != 'undefined' && typeof(XRegExp) == 'undefined') -{ - XRegExp = require('XRegExp').XRegExp; -} - -// Shortcut object which will be assigned to the SyntaxHighlighter variable. -// This is a shorthand for local reference in order to avoid long namespace -// references to SyntaxHighlighter.whatever... -var sh = { - defaults : { - /** Additional CSS class names to be added to highlighter elements. */ - 'class-name' : '', - - /** First line number. */ - 'first-line' : 1, - - /** - * Pads line numbers. Possible values are: - * - * false - don't pad line numbers. - * true - automaticaly pad numbers with minimum required number of leading zeroes. - * [int] - length up to which pad line numbers. - */ - 'pad-line-numbers' : false, - - /** Lines to highlight. */ - 'highlight' : null, - - /** Title to be displayed above the code block. */ - 'title' : null, - - /** Enables or disables smart tabs. */ - 'smart-tabs' : true, - - /** Gets or sets tab size. */ - 'tab-size' : 4, - - /** Enables or disables gutter. */ - 'gutter' : true, - - /** Enables or disables toolbar. */ - 'toolbar' : true, - - /** Enables quick code copy and paste from double click. */ - 'quick-code' : true, - - /** Forces code view to be collapsed. */ - 'collapse' : false, - - /** Enables or disables automatic links. */ - 'auto-links' : true, - - /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */ - 'light' : false, - - 'unindent' : true, - - 'html-script' : false - }, - - config : { - space : ' ', - - /** Enables use of