├── .gitignore ├── Makefile ├── README.markdown ├── app.assets ├── app.py ├── app.yaml ├── assets.manifest ├── assets ├── bootstrap │ ├── css │ │ ├── bootstrap-responsive.css │ │ └── bootstrap.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ └── bootstrap.js ├── embed.css ├── index.js ├── jquery.js └── prettify │ ├── prettify.css │ └── prettify.js ├── gist_it ├── __init__.py └── appengine.py ├── jinja2-assets ├── gist.jinja.html ├── gist.jinja.js ├── index.jinja.html └── test.jinja.html ├── pyl ├── jinja2 │ ├── __init__.py │ ├── _debugsupport.c │ ├── _markupsafe │ │ ├── __init__.py │ │ ├── _bundle.py │ │ ├── _constants.py │ │ ├── _native.py │ │ └── tests.py │ ├── _stringdefs.py │ ├── bccache.py │ ├── compiler.py │ ├── constants.py │ ├── debug.py │ ├── defaults.py │ ├── environment.py │ ├── exceptions.py │ ├── ext.py │ ├── filters.py │ ├── lexer.py │ ├── loaders.py │ ├── meta.py │ ├── nodes.py │ ├── optimizer.py │ ├── parser.py │ ├── runtime.py │ ├── sandbox.py │ ├── tests.py │ ├── testsuite │ │ ├── __init__.py │ │ ├── api.py │ │ ├── core_tags.py │ │ ├── debug.py │ │ ├── doctests.py │ │ ├── ext.py │ │ ├── filters.py │ │ ├── imports.py │ │ ├── inheritance.py │ │ ├── lexnparse.py │ │ ├── loader.py │ │ ├── regression.py │ │ ├── res │ │ │ ├── __init__.py │ │ │ └── templates │ │ │ │ ├── broken.html │ │ │ │ ├── foo │ │ │ │ └── test.html │ │ │ │ ├── syntaxerror.html │ │ │ │ └── test.html │ │ ├── security.py │ │ ├── tests.py │ │ └── utils.py │ ├── utils.py │ └── visitor.py └── versioned_memcache │ ├── __init__.py │ └── memcache.py ├── synopsis.markdown ├── t ├── 01-basic.t ├── Makefile.PL └── cpanm ├── t0 ├── 01-basic.t └── render-gist-it-example └── test ├── test_cgi_escape.py ├── test_footer.py ├── test_gist_it.py ├── test_highlight.py ├── test_slice.py └── test_style.py /.gitignore: -------------------------------------------------------------------------------- 1 | /t/Makefile 2 | /t/p5 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test t crunch 2 | 3 | test: 4 | unit2 discover -v test 5 | @echo 6 | @echo "For remote testing with perl, run the following:" 7 | @echo " dev_appserver.py ." 8 | @echo " $(MAKE) t" 9 | 10 | t: 11 | mkdir -p t/p5 12 | perl t/cpanm -l t/p5 --installdeps ./t 13 | prove t/ 14 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # gist-it 2 | 3 | An AppEngine app to embed files from a github repository like a gist 4 | 5 | http://github.com/robertkrimen/gist-it 6 | 7 | ### Try it 8 | 9 | You can try it at [gist-it.appspot.com](http://gist-it.appspot.com) 10 | 11 | ### Usage 12 | 13 | Launch a working example with: 14 | 15 | dev_appserver.py . 16 | 17 | The gisting handler is located at ```app.py/dispatch_gist_it``` 18 | 19 | Associated the handler with a path is pretty easy, for example: 20 | 21 | wsgi_application = webapp.WSGIApplication( [ 22 | ( r'(.*)', dispatch_gist_it ), 23 | ], debug=True ) 24 | 25 | ### Testing 26 | 27 | To run python unit2 tests: 28 | 29 | make test 30 | 31 | To run perl tests (against a running server): 32 | 33 | make t 34 | 35 | ### Author 36 | 37 | Robert Krimen (robertkrimen@gmail.com) 38 | 39 | * http://github.com/robertkrimen 40 | * http://search.cpan.org/~rokr/?R=D 41 | -------------------------------------------------------------------------------- /app.assets: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Modern::Perl; 4 | 5 | sub { 6 | my ( $collate ) = @_; 7 | 8 | $collate->import( <<_END_ ); 9 | jquery 10 | prettify 11 | _END_ 12 | 13 | #jquery-ui 14 | #zeroclipboard 15 | #css3buttons 16 | #tipsy 17 | 18 | $collate->write( 'assets' ); 19 | 20 | return; 21 | 22 | $collate->write( 'root-assets/_', compressed => 1, 23 | detach => [qw[ 24 | jquery-ui/base 25 | css3buttons 26 | zeroclipboard 27 | ]], 28 | rewrite => [ 29 | '/assets/_', 30 | [ 'appengine/index.html', 'appengine/index_.html' ], 31 | ] 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import logging 3 | import new 4 | import os 5 | import pickle 6 | import cgi 7 | import sys 8 | import traceback 9 | import urlparse 10 | import types 11 | import wsgiref.handlers 12 | import re 13 | import posixpath 14 | import urllib 15 | 16 | from google.appengine.api import urlfetch 17 | from google.appengine.ext import db 18 | from google.appengine.ext import webapp 19 | from google.appengine.ext.webapp.util import run_wsgi_app 20 | from google.appengine.ext.webapp import template 21 | 22 | _LOCAL_ = os.environ[ 'SERVER_SOFTWARE' ].startswith( 'Development' ) 23 | _DEBUG_ = False or _LOCAL_ 24 | _CACHE_ = False 25 | 26 | sys.path.append( 'pyl' ) 27 | 28 | import jinja2 as jinja2_ 29 | jinja2 = jinja2_.Environment( loader=jinja2_.FileSystemLoader( 'jinja2-assets' ) ) 30 | 31 | from gist_it import appengine as gist_it_appengine 32 | gist_it_appengine.jinja2 = jinja2 33 | 34 | class RequestHandler( webapp.RequestHandler ): 35 | 36 | def url_for( self, *arguments ): 37 | parse = urlparse.urlparse( self.request.url ) 38 | path = '' 39 | if len( arguments ): 40 | path = posixpath.join( *arguments ) 41 | return str( urlparse.urlunparse( ( parse.scheme, parse.netloc, path, '', '', '' ) ) ) 42 | 43 | def render_template( self, template_name, **arguments ): 44 | self.response.out.write( jinja2.get_template( template_name ).render( dispatch=self, **arguments ) ) 45 | 46 | class dispatch_index( RequestHandler ): 47 | def get( self ): 48 | self.render_template( 'index.jinja.html' ) 49 | 50 | class dispatch_test( RequestHandler ): 51 | def get( self ): 52 | return gist_it_appengine.dispatch_test( self ) 53 | 54 | class dispatch_test0( RequestHandler ): 55 | def get( self ): 56 | return gist_it_appengine.dispatch_test0( self ) 57 | 58 | class dispatch_gist_it( RequestHandler ): 59 | def get( self, location ): 60 | return gist_it_appengine.dispatch_gist_it( self, location ) 61 | 62 | wsgi_application = webapp.WSGIApplication( [ 63 | ( r'/', dispatch_index ), 64 | ( r'/test', dispatch_test ), 65 | ( r'/test0', dispatch_test0 ), 66 | ( r'/xyzzy/(.*)', dispatch_gist_it ), 67 | ( r'(.*)', dispatch_gist_it ), 68 | ], debug=_DEBUG_ ) 69 | 70 | def main(): 71 | run_wsgi_app( wsgi_application ) 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: 0 2 | version: 1 3 | 4 | runtime: python 5 | api_version: 1 6 | 7 | handlers: 8 | - url: /assets 9 | static_dir: assets 10 | expiration: 1d 11 | 12 | - url: .* 13 | script: app.py 14 | -------------------------------------------------------------------------------- /assets.manifest: -------------------------------------------------------------------------------- 1 | # fetchett 2 | 3 | #google-code-prettify 4 | bootstrap 5 | jquery 6 | -------------------------------------------------------------------------------- /assets/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/assets/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /assets/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/assets/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /assets/embed.css: -------------------------------------------------------------------------------- 1 | .gist-it-gist div { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | .gist-it-gist .gist-file { 7 | border: 1px solid #DEDEDE; 8 | font-family: Consolas,"Liberation Mono",Courier,monospace; 9 | margin-bottom: 1em; 10 | } 11 | 12 | .gist-it-gist .gist-file .gist-meta { 13 | background-color: #EAEAEA; 14 | color: #666666; 15 | font-family: Helvetica,arial,freesans,clean,sans-serif; 16 | font-size: 85%; 17 | overflow: hidden; 18 | padding: 0.5em; 19 | } 20 | 21 | .gist-it-gist .gist-file .gist-meta a { 22 | color: #336699; 23 | } 24 | 25 | .gist-it-gist .gist-file .gist-meta a:visited { 26 | color: #773377; 27 | } 28 | 29 | .gist-it-gist .gist-file .gist-data { 30 | background-color: #F8F8FF; 31 | border-bottom: 1px solid #DDDDDD; 32 | font-size: 100%; 33 | overflow: auto; 34 | word-wrap: normal; 35 | } 36 | 37 | .gist-it-gist .gist-file .gist-data pre { 38 | background: none repeat scroll 0 0 transparent !important; 39 | border: medium none !important; 40 | font-family: Consolas,"Liberation Mono",Courier,monospace; 41 | margin: 0 !important; 42 | padding: 0 !important; 43 | } 44 | -------------------------------------------------------------------------------- /assets/index.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | $( '#try-it' ).click(function(){ 3 | $( '#try-it-modal' ).modal( 'show' ); 4 | return false; 5 | }); 6 | 7 | $modal = $( '#try-it-modal' ); 8 | $input = $modal.find('input'); 9 | $script = $modal.find('.script'); 10 | $output = $modal.find('.output'); 11 | 12 | $modal.find('form').on('submit', function(){ 13 | load(); 14 | }); 15 | $modal.bind('show', function(){ 16 | if (!$output.children().length) { 17 | load(); 18 | } 19 | }); 20 | $modal.bind('shown', function(){ 21 | $input.focus().select(); 22 | }); 23 | 24 | function load(){ 25 | var url = $input.val(); 26 | $output.text(""); 27 | if (url === "") { 28 | $output.addClass('alert alert-warning').text("Enter a GitHub URL"); 29 | return; 30 | } 31 | url = GIST_IT_BASE + '/' + url; 32 | $script.empty().append( 33 | $( '
' ).text('')
34 |         );
35 |         $output.removeClass('alert alert-warning alert-error').text("Loading...");
36 |         prettyPrint();
37 |         $.getJSON(url, { 'test': 'json' }, function(data){
38 |             $output.html(data.html);
39 |             prettyPrint();
40 |         }).error(function(response){
41 |             $output.addClass('alert alert-error').text(response.responseText);
42 |         });
43 |     }
44 | });
45 | 


--------------------------------------------------------------------------------
/assets/prettify/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}


--------------------------------------------------------------------------------
/assets/prettify/prettify.js:
--------------------------------------------------------------------------------
 1 | !function(){var p=null;window.PR_SHOULD_USE_CONTINUATION=!0;
 2 | (function(){function S(b){function c(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=q[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function d(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a=
 3 | b[0]==="^",d=["["];a&&d.push("^");for(var a=a?1:0,f=b.length;a122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;ah[0]&&(h[1]+1>h[0]&&d.push("-"),d.push(g(h[1])));d.push("]");return d.join("")}function x(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=a.length,c=[],f=0,h=0;f=2&&e==="["?a[f]=d(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var u=0,m=!1,j=!1,k=0,a=b.length;k=5&&"lang-"===w.substring(0,5))&&!(s&&typeof s[1]==="string"))f=!1,w="src";f||(q[z]=w)}h=a;a+=z.length;if(f){f=s[1];var l=z.indexOf(f),B=l+f.length;s[2]&&(B=z.length-s[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}b.g=k}var d={},x;(function(){for(var g=b.concat(c),j=[],k={},a=0,i=g.length;a=0;)d[n.charAt(e)]=q;q=q[1];n=""+q;k.hasOwnProperty(n)||(j.push(q),k[n]=p)}j.push(/[\S\s]/);x=S(j)})();var u=c.length;return g}function v(b){var c=[],g=[];b.tripleQuotedStrings?c.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,p,"'\""]):b.multiLineStrings?c.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
10 | p,"'\"`"]):c.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,p,"\"'"]);b.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,p]);var d=b.hashComments;d&&(b.cStyleComments?(d>1?c.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,p,"#"]):c.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,p,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,p])):c.push(["com",
11 | /^#[^\n\r]*/,p,"#"]));b.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,p]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,p]));b.regexLiterals&&g.push(["lang-regex",/^(?:^^\.?|[+-]|[!=]={0,2}|#|%=?|&&?=?|\(|\*=?|[+-]=|->|\/=?|::?|<{1,3}=?|[,;?@[{~]|\^\^?=?|\|\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(d=b.types)&&g.push(["typ",d]);b=(""+b.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",
12 | RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),p]);c.push(["pln",/^\s+/,p," \r\n\t\u00a0"]);g.push(["lit",/^@[$_a-z][\w$@]*/i,p],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,p],["pln",/^[$_a-z][\w$@]*/i,p],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,p,"0123456789"],["pln",/^\\[\S\s]?/,p],["pun",/^.[^\s\w"$'./@\\`]*/,p]);return C(c,g)}function J(b,c,g){function d(a){var b=a.nodeType;if(b==1&&!u.test(a.className))if("br"===a.nodeName)x(a),a.parentNode&&a.parentNode.removeChild(a);
13 | else for(a=a.firstChild;a;a=a.nextSibling)d(a);else if((b==3||b==4)&&g){var c=a.nodeValue,i=c.match(m);if(i)b=c.substring(0,i.index),a.nodeValue=b,(c=c.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(c),a.nextSibling),x(a),b||a.parentNode.removeChild(a)}}function x(b){function c(a,b){var d=b?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=c(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!b.nextSibling;)if(b=b.parentNode,
14 | !b)return;for(var b=c(b.nextSibling,0),d;(d=b.parentNode)&&d.nodeType===1;)b=d;a.push(b)}for(var u=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=b.ownerDocument,k=j.createElement("li");b.firstChild;)k.appendChild(b.firstChild);for(var a=[k],i=0;i=0;){var d=c[g];F.hasOwnProperty(d)?D.console&&console.warn("cannot override language handler %s",d):F[d]=b}}function I(b,c){if(!b||!F.hasOwnProperty(b))b=/^\s*=l&&(d+=2);g>=B&&(q+=2)}}finally{if(f)f.style.display=h}}catch(t){D.console&&console.log(t&&t.stack||t)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],
18 | M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],
19 | E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],
20 | y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};r(X,["default-code"]);r(C([],
21 | [["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);r(C([["pln",/^\s+/,p," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,
22 | p,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);r(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);r(v({keywords:M,hashComments:!0,
23 | cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);r(v({keywords:"null,true,false"}),["json"]);r(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}),["cs"]);r(v({keywords:N,cStyleComments:!0}),["java"]);r(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);r(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);r(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",
24 | hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);r(v({keywords:Q,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);r(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);r(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),
25 | ["coffee"]);r(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);r(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:r,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(b,c,g){var d=document.createElement("div");
26 | d.innerHTML="
"+b+"
";d=d.firstChild;g&&J(d,g,!0);K({h:c,j:g,c:d,i:1});return d.innerHTML},prettyPrint:D.prettyPrint=function(b,c){function g(){for(var d=D.PR_SHOULD_USE_CONTINUATION?a.now()+250:Infinity;i 0): 92 | end_line += 1 93 | 94 | return '\n'.join(content.splitlines()[start_line:end_line]) 95 | 96 | class Gist: 97 | 98 | @classmethod 99 | def keylist( self ): 100 | return [ 101 | 'user', 'repository', 'branch', 'path', 102 | 'blob_path', 'blob_url', 103 | 'raw_path', 'raw_url', 104 | 'user_repository', 'user_repository_branch_path', 'user_repository_url', 105 | 'start_line', 'end_line', 106 | 'footer', 'style', 'highlight', 107 | 'filename', 108 | ] 109 | 110 | @classmethod 111 | def match( self, location ): 112 | match = re.match( r'^(?:/https?:/)?/?github(?:\.com)?/(.*)$', location ) 113 | if not match: 114 | return None 115 | return match 116 | 117 | @classmethod 118 | def parse( self, location, slice_option = None, footer_option = None, style_option = None, highlight_option = None ): 119 | match = self.match( location ) 120 | if not match: 121 | return None 122 | 123 | path = match.group( 1 ) 124 | splitpath = posixpath.normpath( path ).split( '/' ) 125 | if len( splitpath ) < 5: 126 | return None 127 | 128 | parse = {} 129 | 130 | # user / respository / format / branch / path 131 | user = parse[ 'user' ] = splitpath[ 0 ] 132 | repository = parse[ 'repository' ] = splitpath[ 1 ] 133 | branch = parse[ 'branch' ] = splitpath[ 3 ] 134 | path = parse[ 'path' ] = '/'.join( splitpath[ 4: ] ) 135 | 136 | # format = blob 137 | splitpath[ 2 ] = 'blob' 138 | blob_path = parse[ 'blob_path' ] = '/'.join( splitpath ) 139 | blob_url = parse[ 'blob_url' ] = urlparse.urljoin( 'https://github.com', blob_path ) 140 | 141 | # format = raw 142 | splitpath[ 2 ] = 'raw' 143 | raw_path = parse[ 'raw_path' ] = '/'.join( splitpath ) 144 | raw_url = parse[ 'raw_url' ] = urlparse.urljoin( 'https://github.com', raw_path ) 145 | 146 | user_repository = parse[ 'user_repository' ] = '/'.join([ user, repository ]) 147 | user_repository_branch_path = parse[ 'user_repository_branch_path' ] = '/'.join([ user_repository, branch, path ]); 148 | user_repository_url = parse[ 'user_repository_url' ] = urlparse.urljoin( 'https://github.com', user_repository ) 149 | 150 | slice_option = parse_slice( slice_option ) 151 | parse[ 'start_line' ] = slice_option[0] 152 | parse[ 'end_line' ] = slice_option[1] 153 | 154 | parse[ 'footer' ] = parse_footer( footer_option ) 155 | parse[ 'style' ] = parse_style( style_option ) 156 | parse[ 'highlight' ] = parse_highlight( highlight_option ) 157 | 158 | parse[ 'filename' ] = splitpath[-1] 159 | 160 | return Gist( **parse ) 161 | 162 | def __init__( self, **arguments ): 163 | for key in self.keylist(): 164 | setattr( self, key, arguments[ key ] ) 165 | 166 | def value( self ): 167 | value = {} 168 | for key in self.keylist(): 169 | value[ key ] = getattr( self, key ) 170 | return value 171 | -------------------------------------------------------------------------------- /gist_it/appengine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import logging 3 | import os 4 | import cgi 5 | import sys 6 | import urllib 7 | import simplejson 8 | 9 | jinja2 = None 10 | 11 | _LOCAL_ = os.environ[ 'SERVER_SOFTWARE' ].startswith( 'Development' ) 12 | _DEBUG_ = True 13 | _CACHE_ = False 14 | 15 | from google.appengine.api import urlfetch 16 | from google.appengine.ext import webapp 17 | from versioned_memcache import memcache 18 | 19 | import gist_it 20 | from gist_it import take_slice, cgi_escape 21 | 22 | def render_gist_html( base, gist, document ): 23 | if jinja2 is None: 24 | return 25 | result = jinja2.get_template( 'gist.jinja.html' ).render( cgi_escape = cgi_escape, base = base, gist = gist, document = document ) 26 | return result 27 | 28 | def render_gist_js( base, gist, gist_html ): 29 | if jinja2 is None: 30 | return 31 | result = jinja2.get_template( 'gist.jinja.js' ).render( base = base, gist = gist, gist_html = gist_html ) 32 | return result 33 | 34 | def render_gist_js_callback( callback, gist, gist_html ): 35 | return "%s( '%s', '%s' );" % ( callback, gist_html.encode( 'string_escape' ), gist.raw_path ) 36 | 37 | # dispatch == RequestHandler 38 | def dispatch_test( dispatch ): 39 | dispatch.render_template( 'test.jinja.html', list = 40 | map( lambda _: ( _, 'github/robertkrimen/gist-it-example/raw/master/test.js?' + _ ), [ 41 | # Standard 42 | '' 43 | # Without footer 44 | 'footer=0', 45 | # Footer without "brought to you by" mention 46 | 'footer=minimal', 47 | # Partial file 48 | 'slice=3:10', 49 | # First line of file 50 | 'slice=0', 51 | # Last line of file 52 | 'slice=-1', 53 | # With no style request 54 | 'style=0', 55 | # Documentation 56 | 'slice=24:100', 57 | 'slice=0:-2', 58 | 'slice=0', 59 | ] ) 60 | ) 61 | 62 | # dispatch == RequestHandler 63 | def dispatch_test0( dispatch ): 64 | dispatch.render_template( 'test.jinja.html', list = [ 65 | ( '', 'github/whittle/node-coffee-heroku-tutorial/raw/eb587185509ec8c2e728067d49f4ac2d5a67ec09/app.js' ), 66 | ( '', 'github/horstjens/ThePythonGameBook/blob/master/pygame/015_more_sprites.py' ), 67 | ] 68 | ) 69 | 70 | # dispatch == RequestHandler 71 | def dispatch_gist_it( dispatch, location ): 72 | location = urllib.unquote( location ) 73 | match = gist_it.Gist.match( location ) 74 | dispatch.response.headers['Content-Type'] = 'text/plain'; 75 | if not match: 76 | dispatch.response.set_status( 404 ) 77 | dispatch.response.out.write( dispatch.response.http_status_message( 404 ) ) 78 | dispatch.response.out.write( "\n" ) 79 | return 80 | 81 | else: 82 | slice_option = dispatch.request.get( 'slice' ) 83 | footer_option = dispatch.request.get( 'footer' ) 84 | style_option = dispatch.request.get( 'style' ) 85 | highlight_option = dispatch.request.get( 'highlight' ) 86 | test = dispatch.request.get( 'test' ) 87 | 88 | gist = gist_it.Gist.parse( location, slice_option = slice_option, footer_option = footer_option, style_option = style_option, highlight_option = highlight_option ) 89 | if not gist: 90 | dispatch.response.set_status( 500 ) 91 | dispatch.response.out.write( "Unable to parse \"%s\": Not a valid repository path?" % ( location ) ) 92 | dispatch.response.out.write( "\n" ) 93 | return 94 | 95 | if _CACHE_ and dispatch.request.get( 'flush' ): 96 | dispatch.response.out.write( memcache.delete( memcache_key ) ) 97 | return 98 | 99 | memcache_key = gist.raw_url 100 | data = memcache.get( memcache_key ) 101 | if data is None or not _CACHE_: 102 | base = dispatch.url_for() 103 | # For below, see: http://stackoverflow.com/questions/2826238/does-google-appengine-cache-external-requests 104 | response = urlfetch.fetch( gist.raw_url, headers = { 'Cache-Control': 'max-age=300' } ) 105 | if response.status_code != 200: 106 | if response.status_code == 403: 107 | dispatch.response.set_status( response.status_code ) 108 | elif response.status_code == 404: 109 | dispatch.response.set_status( response.status_code ) 110 | else: 111 | dispatch.response.set_status( 500 ) 112 | dispatch.response.out.write( "Unable to fetch \"%s\": (%i)" % ( gist.raw_url, response.status_code ) ) 113 | return 114 | else: 115 | # I believe GitHub always returns a utf-8 encoding, so this should be safe 116 | response_content = response.content.decode('utf-8') 117 | 118 | gist_content = take_slice( response_content, gist.start_line, gist.end_line ) 119 | gist_html = str( render_gist_html( base, gist, gist_content ) ).strip() 120 | callback = dispatch.request.get( 'callback' ); 121 | if callback != '': 122 | result = render_gist_js_callback( callback, gist, gist_html ) 123 | else: 124 | result = render_gist_js( base, gist, gist_html ) 125 | result = str( result ).strip() 126 | data = result 127 | if test: 128 | if test == 'json': 129 | dispatch.response.headers['Content-Type'] = 'application/json'; 130 | dispatch.response.out.write(simplejson.dumps({ 131 | 'gist': gist.value(), 132 | 'content': gist_content, 133 | 'html': gist_html, 134 | })) 135 | elif False and test == 'example': 136 | pass 137 | else: 138 | dispatch.response.headers['Content-Type'] = 'text/plain' 139 | dispatch.response.out.write( gist_html ) 140 | return 141 | if _CACHE_: 142 | memcache.add( memcache_key, data, 60 * 60 * 24 ) 143 | 144 | dispatch.response.headers['Content-Type'] = 'text/javascript' 145 | dispatch.response.out.write( data ) 146 | -------------------------------------------------------------------------------- /jinja2-assets/gist.jinja.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {% if gist.highlight == 'prettify' or gist.highlight == 'deferred-prettify' %} 5 |
{{ cgi_escape( document ) }}
6 | {% else %} 7 |
{{ cgi_escape( document ) }}
8 | {% endif %} 9 |
10 | {% if gist.footer != '0' %} 11 |
12 | {% if gist.footer != 'minimal' %} 13 | This Gist brought to you by gist-it. 14 | {% endif %} 15 | view raw 16 | 17 | {{ gist.filename }} 18 | 19 |
20 | {% endif %} 21 |
22 |
23 | -------------------------------------------------------------------------------- /jinja2-assets/gist.jinja.js: -------------------------------------------------------------------------------- 1 | {% if gist.highlight == 'prettify' %} 2 | if ( 'prettyPrint' in window ) {} else { 3 | document.write( '' ); 4 | } 5 | {% endif %} 6 | {% if gist.style != '0' %} 7 | document.write( '' ); 8 | {% endif %} 9 | {% if gist.highlight == 'prettify' %} 10 | document.write( '' ); 11 | {% endif %} 12 | document.write( '{{ gist_html.encode( 'string_escape' ) }}' ); 13 | {% if gist.highlight == 'prettify' %} 14 | document.write( '' ); 15 | {% endif %} 16 | -------------------------------------------------------------------------------- /jinja2-assets/index.jinja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | gist-it.appspot.com - Embed files from a github repository like a gist 4 | 5 | 6 | 15 | 16 | 17 | 18 | {% block head %} 19 | {% endblock %} 20 | 21 | 22 | 35 | 36 |
37 |

SYNOPSIS

38 |
39 |
# Embed the file robertkrimen/gist-it-example/example.js
 40 | <script src="http://gist-it.appspot.com/github/robertkrimen/gist-it-example/blob/master/example.js"></script>
 41 | 
 42 | # Embed without a footer
 43 | <script src="http://gist-it.appspot.com/github/robertkrimen/gist-it-example/blob/master/example.js?footer=0"></script>
 44 | 
 45 | # Show only the first and second line
 46 | <script src="http://gist-it.appspot.com/github/robertkrimen/gist-it-example/blob/master/example.js?slice=0:1"></script>
 47 | 
48 | 49 | 50 |
51 |
52 |
53 | 54 |
function Xyzzy() {
 55 |     return "Nothing happens";
 56 | }
57 | 58 |
59 | 60 |
61 | 62 | This Gist brought to you by gist-it. 63 | 64 | view raw 65 | 66 | example.js 67 |
68 | 69 |
70 |
71 | 72 | 73 |
74 | 75 |

DESCRIPTION

76 |

gist-it lets you take a file from your GitHub repository and embed it into any webpage, just like a gist

77 |

Syntax highlighting by google-code-prettify

78 | 79 |

USAGE

80 | 81 |

Specify the slice parameter to show only a particular portion of the file:

82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
slice=1:Show from the second line on (skip the first line)
slice=0:-1Show the first line up to and including the second-to-last line
slice=24:101Show lines 24 through 101
slice=0Show only the first line of the file
100 | 101 |

Use the footer parameter to control the appearance of the footer:

102 | 103 | 104 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
105 | footer=no
106 | footer=0 107 |
Hide the footer
footer=minimalShow the footer without this leading part: This Gist brought to you by gist-it.
115 | 116 |

SOURCE

117 |

http://github.com/robertkrimen/gist-it

118 | 119 |

AUTHOR

120 | Robert Krimen 121 | <robertkrimen@gmail> 122 | 123 |
124 |
125 | 126 |
127 | 128 | 140 | 141 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /jinja2-assets/test.jinja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | gist-it.appspot.com - Test 4 | 5 | 6 | 7 | 8 | 9 | {% block head %} 10 | {% endblock %} 11 | 12 | 13 | 14 | {% for item in list %} 15 |
16 | {{ item[0] }}
17 | 18 |
19 | {% endfor %} 20 | 21 |
22 |

An example github gist

23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pyl/jinja2/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2 4 | ~~~~~~ 5 | 6 | Jinja2 is a template engine written in pure Python. It provides a 7 | Django inspired non-XML syntax but supports inline expressions and 8 | an optional sandboxed environment. 9 | 10 | Nutshell 11 | -------- 12 | 13 | Here a small example of a Jinja2 template:: 14 | 15 | {% extends 'base.html' %} 16 | {% block title %}Memberlist{% endblock %} 17 | {% block content %} 18 | 23 | {% endblock %} 24 | 25 | 26 | :copyright: (c) 2010 by the Jinja Team. 27 | :license: BSD, see LICENSE for more details. 28 | """ 29 | __docformat__ = 'restructuredtext en' 30 | try: 31 | __version__ = __import__('pkg_resources') \ 32 | .get_distribution('Jinja2').version 33 | except Exception: 34 | __version__ = 'unknown' 35 | 36 | # high level interface 37 | from jinja2.environment import Environment, Template 38 | 39 | # loaders 40 | from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \ 41 | DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \ 42 | ModuleLoader 43 | 44 | # bytecode caches 45 | from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \ 46 | MemcachedBytecodeCache 47 | 48 | # undefined types 49 | from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined 50 | 51 | # exceptions 52 | from jinja2.exceptions import TemplateError, UndefinedError, \ 53 | TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \ 54 | TemplateAssertionError 55 | 56 | # decorators and public utilities 57 | from jinja2.filters import environmentfilter, contextfilter, \ 58 | evalcontextfilter 59 | from jinja2.utils import Markup, escape, clear_caches, \ 60 | environmentfunction, evalcontextfunction, contextfunction, \ 61 | is_undefined 62 | 63 | __all__ = [ 64 | 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader', 65 | 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader', 66 | 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache', 67 | 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined', 68 | 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound', 69 | 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', 70 | 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', 71 | 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', 72 | 'evalcontextfilter', 'evalcontextfunction' 73 | ] 74 | -------------------------------------------------------------------------------- /pyl/jinja2/_debugsupport.c: -------------------------------------------------------------------------------- 1 | /** 2 | * jinja2._debugsupport 3 | * ~~~~~~~~~~~~~~~~~~~~ 4 | * 5 | * C implementation of `tb_set_next`. 6 | * 7 | * :copyright: (c) 2010 by the Jinja Team. 8 | * :license: BSD. 9 | */ 10 | 11 | #include 12 | 13 | 14 | static PyObject* 15 | tb_set_next(PyObject *self, PyObject *args) 16 | { 17 | PyTracebackObject *tb, *old; 18 | PyObject *next; 19 | 20 | if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next)) 21 | return NULL; 22 | if (next == Py_None) 23 | next = NULL; 24 | else if (!PyTraceBack_Check(next)) { 25 | PyErr_SetString(PyExc_TypeError, 26 | "tb_set_next arg 2 must be traceback or None"); 27 | return NULL; 28 | } 29 | else 30 | Py_INCREF(next); 31 | 32 | old = tb->tb_next; 33 | tb->tb_next = (PyTracebackObject*)next; 34 | Py_XDECREF(old); 35 | 36 | Py_INCREF(Py_None); 37 | return Py_None; 38 | } 39 | 40 | static PyMethodDef module_methods[] = { 41 | {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS, 42 | "Set the tb_next member of a traceback object."}, 43 | {NULL, NULL, 0, NULL} /* Sentinel */ 44 | }; 45 | 46 | 47 | #if PY_MAJOR_VERSION < 3 48 | 49 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 50 | #define PyMODINIT_FUNC void 51 | #endif 52 | PyMODINIT_FUNC 53 | init_debugsupport(void) 54 | { 55 | Py_InitModule3("jinja2._debugsupport", module_methods, ""); 56 | } 57 | 58 | #else /* Python 3.x module initialization */ 59 | 60 | static struct PyModuleDef module_definition = { 61 | PyModuleDef_HEAD_INIT, 62 | "jinja2._debugsupport", 63 | NULL, 64 | -1, 65 | module_methods, 66 | NULL, 67 | NULL, 68 | NULL, 69 | NULL 70 | }; 71 | 72 | PyMODINIT_FUNC 73 | PyInit__debugsupport(void) 74 | { 75 | return PyModule_Create(&module_definition); 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /pyl/jinja2/_markupsafe/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe 4 | ~~~~~~~~~~ 5 | 6 | Implements a Markup string. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import re 12 | from itertools import imap 13 | 14 | 15 | __all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent'] 16 | 17 | 18 | _striptags_re = re.compile(r'(|<[^>]*>)') 19 | _entity_re = re.compile(r'&([^;]+);') 20 | 21 | 22 | class Markup(unicode): 23 | r"""Marks a string as being safe for inclusion in HTML/XML output without 24 | needing to be escaped. This implements the `__html__` interface a couple 25 | of frameworks and web applications use. :class:`Markup` is a direct 26 | subclass of `unicode` and provides all the methods of `unicode` just that 27 | it escapes arguments passed and always returns `Markup`. 28 | 29 | The `escape` function returns markup objects so that double escaping can't 30 | happen. 31 | 32 | The constructor of the :class:`Markup` class can be used for three 33 | different things: When passed an unicode object it's assumed to be safe, 34 | when passed an object with an HTML representation (has an `__html__` 35 | method) that representation is used, otherwise the object passed is 36 | converted into a unicode string and then assumed to be safe: 37 | 38 | >>> Markup("Hello World!") 39 | Markup(u'Hello World!') 40 | >>> class Foo(object): 41 | ... def __html__(self): 42 | ... return 'foo' 43 | ... 44 | >>> Markup(Foo()) 45 | Markup(u'foo') 46 | 47 | If you want object passed being always treated as unsafe you can use the 48 | :meth:`escape` classmethod to create a :class:`Markup` object: 49 | 50 | >>> Markup.escape("Hello World!") 51 | Markup(u'Hello <em>World</em>!') 52 | 53 | Operations on a markup string are markup aware which means that all 54 | arguments are passed through the :func:`escape` function: 55 | 56 | >>> em = Markup("%s") 57 | >>> em % "foo & bar" 58 | Markup(u'foo & bar') 59 | >>> strong = Markup("%(text)s") 60 | >>> strong % {'text': 'hacker here'} 61 | Markup(u'<blink>hacker here</blink>') 62 | >>> Markup("Hello ") + "" 63 | Markup(u'Hello <foo>') 64 | """ 65 | __slots__ = () 66 | 67 | def __new__(cls, base=u'', encoding=None, errors='strict'): 68 | if hasattr(base, '__html__'): 69 | base = base.__html__() 70 | if encoding is None: 71 | return unicode.__new__(cls, base) 72 | return unicode.__new__(cls, base, encoding, errors) 73 | 74 | def __html__(self): 75 | return self 76 | 77 | def __add__(self, other): 78 | if hasattr(other, '__html__') or isinstance(other, basestring): 79 | return self.__class__(unicode(self) + unicode(escape(other))) 80 | return NotImplemented 81 | 82 | def __radd__(self, other): 83 | if hasattr(other, '__html__') or isinstance(other, basestring): 84 | return self.__class__(unicode(escape(other)) + unicode(self)) 85 | return NotImplemented 86 | 87 | def __mul__(self, num): 88 | if isinstance(num, (int, long)): 89 | return self.__class__(unicode.__mul__(self, num)) 90 | return NotImplemented 91 | __rmul__ = __mul__ 92 | 93 | def __mod__(self, arg): 94 | if isinstance(arg, tuple): 95 | arg = tuple(imap(_MarkupEscapeHelper, arg)) 96 | else: 97 | arg = _MarkupEscapeHelper(arg) 98 | return self.__class__(unicode.__mod__(self, arg)) 99 | 100 | def __repr__(self): 101 | return '%s(%s)' % ( 102 | self.__class__.__name__, 103 | unicode.__repr__(self) 104 | ) 105 | 106 | def join(self, seq): 107 | return self.__class__(unicode.join(self, imap(escape, seq))) 108 | join.__doc__ = unicode.join.__doc__ 109 | 110 | def split(self, *args, **kwargs): 111 | return map(self.__class__, unicode.split(self, *args, **kwargs)) 112 | split.__doc__ = unicode.split.__doc__ 113 | 114 | def rsplit(self, *args, **kwargs): 115 | return map(self.__class__, unicode.rsplit(self, *args, **kwargs)) 116 | rsplit.__doc__ = unicode.rsplit.__doc__ 117 | 118 | def splitlines(self, *args, **kwargs): 119 | return map(self.__class__, unicode.splitlines(self, *args, **kwargs)) 120 | splitlines.__doc__ = unicode.splitlines.__doc__ 121 | 122 | def unescape(self): 123 | r"""Unescape markup again into an unicode string. This also resolves 124 | known HTML4 and XHTML entities: 125 | 126 | >>> Markup("Main » About").unescape() 127 | u'Main \xbb About' 128 | """ 129 | from jinja2._markupsafe._constants import HTML_ENTITIES 130 | def handle_match(m): 131 | name = m.group(1) 132 | if name in HTML_ENTITIES: 133 | return unichr(HTML_ENTITIES[name]) 134 | try: 135 | if name[:2] in ('#x', '#X'): 136 | return unichr(int(name[2:], 16)) 137 | elif name.startswith('#'): 138 | return unichr(int(name[1:])) 139 | except ValueError: 140 | pass 141 | return u'' 142 | return _entity_re.sub(handle_match, unicode(self)) 143 | 144 | def striptags(self): 145 | r"""Unescape markup into an unicode string and strip all tags. This 146 | also resolves known HTML4 and XHTML entities. Whitespace is 147 | normalized to one: 148 | 149 | >>> Markup("Main » About").striptags() 150 | u'Main \xbb About' 151 | """ 152 | stripped = u' '.join(_striptags_re.sub('', self).split()) 153 | return Markup(stripped).unescape() 154 | 155 | @classmethod 156 | def escape(cls, s): 157 | """Escape the string. Works like :func:`escape` with the difference 158 | that for subclasses of :class:`Markup` this function would return the 159 | correct subclass. 160 | """ 161 | rv = escape(s) 162 | if rv.__class__ is not cls: 163 | return cls(rv) 164 | return rv 165 | 166 | def make_wrapper(name): 167 | orig = getattr(unicode, name) 168 | def func(self, *args, **kwargs): 169 | args = _escape_argspec(list(args), enumerate(args)) 170 | _escape_argspec(kwargs, kwargs.iteritems()) 171 | return self.__class__(orig(self, *args, **kwargs)) 172 | func.__name__ = orig.__name__ 173 | func.__doc__ = orig.__doc__ 174 | return func 175 | 176 | for method in '__getitem__', 'capitalize', \ 177 | 'title', 'lower', 'upper', 'replace', 'ljust', \ 178 | 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ 179 | 'translate', 'expandtabs', 'swapcase', 'zfill': 180 | locals()[method] = make_wrapper(method) 181 | 182 | # new in python 2.5 183 | if hasattr(unicode, 'partition'): 184 | partition = make_wrapper('partition'), 185 | rpartition = make_wrapper('rpartition') 186 | 187 | # new in python 2.6 188 | if hasattr(unicode, 'format'): 189 | format = make_wrapper('format') 190 | 191 | # not in python 3 192 | if hasattr(unicode, '__getslice__'): 193 | __getslice__ = make_wrapper('__getslice__') 194 | 195 | del method, make_wrapper 196 | 197 | 198 | def _escape_argspec(obj, iterable): 199 | """Helper for various string-wrapped functions.""" 200 | for key, value in iterable: 201 | if hasattr(value, '__html__') or isinstance(value, basestring): 202 | obj[key] = escape(value) 203 | return obj 204 | 205 | 206 | class _MarkupEscapeHelper(object): 207 | """Helper for Markup.__mod__""" 208 | 209 | def __init__(self, obj): 210 | self.obj = obj 211 | 212 | __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x]) 213 | __str__ = lambda s: str(escape(s.obj)) 214 | __unicode__ = lambda s: unicode(escape(s.obj)) 215 | __repr__ = lambda s: str(escape(repr(s.obj))) 216 | __int__ = lambda s: int(s.obj) 217 | __float__ = lambda s: float(s.obj) 218 | 219 | 220 | # we have to import it down here as the speedups and native 221 | # modules imports the markup type which is define above. 222 | try: 223 | from jinja2._markupsafe._speedups import escape, escape_silent, soft_unicode 224 | except ImportError: 225 | from jinja2._markupsafe._native import escape, escape_silent, soft_unicode 226 | -------------------------------------------------------------------------------- /pyl/jinja2/_markupsafe/_bundle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2._markupsafe._bundle 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | This script pulls in markupsafe from a source folder and 7 | bundles it with Jinja2. It does not pull in the speedups 8 | module though. 9 | 10 | :copyright: Copyright 2010 by the Jinja team, see AUTHORS. 11 | :license: BSD, see LICENSE for details. 12 | """ 13 | import sys 14 | import os 15 | import re 16 | 17 | 18 | def rewrite_imports(lines): 19 | for idx, line in enumerate(lines): 20 | new_line = re.sub(r'(import|from)\s+markupsafe\b', 21 | r'\1 jinja2._markupsafe', line) 22 | if new_line != line: 23 | lines[idx] = new_line 24 | 25 | 26 | def main(): 27 | if len(sys.argv) != 2: 28 | print 'error: only argument is path to markupsafe' 29 | sys.exit(1) 30 | basedir = os.path.dirname(__file__) 31 | markupdir = sys.argv[1] 32 | for filename in os.listdir(markupdir): 33 | if filename.endswith('.py'): 34 | f = open(os.path.join(markupdir, filename)) 35 | try: 36 | lines = list(f) 37 | finally: 38 | f.close() 39 | rewrite_imports(lines) 40 | f = open(os.path.join(basedir, filename), 'w') 41 | try: 42 | for line in lines: 43 | f.write(line) 44 | finally: 45 | f.close() 46 | 47 | 48 | if __name__ == '__main__': 49 | main() 50 | -------------------------------------------------------------------------------- /pyl/jinja2/_markupsafe/_constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe._constants 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Highlevel implementation of the Markup string. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | 13 | HTML_ENTITIES = { 14 | 'AElig': 198, 15 | 'Aacute': 193, 16 | 'Acirc': 194, 17 | 'Agrave': 192, 18 | 'Alpha': 913, 19 | 'Aring': 197, 20 | 'Atilde': 195, 21 | 'Auml': 196, 22 | 'Beta': 914, 23 | 'Ccedil': 199, 24 | 'Chi': 935, 25 | 'Dagger': 8225, 26 | 'Delta': 916, 27 | 'ETH': 208, 28 | 'Eacute': 201, 29 | 'Ecirc': 202, 30 | 'Egrave': 200, 31 | 'Epsilon': 917, 32 | 'Eta': 919, 33 | 'Euml': 203, 34 | 'Gamma': 915, 35 | 'Iacute': 205, 36 | 'Icirc': 206, 37 | 'Igrave': 204, 38 | 'Iota': 921, 39 | 'Iuml': 207, 40 | 'Kappa': 922, 41 | 'Lambda': 923, 42 | 'Mu': 924, 43 | 'Ntilde': 209, 44 | 'Nu': 925, 45 | 'OElig': 338, 46 | 'Oacute': 211, 47 | 'Ocirc': 212, 48 | 'Ograve': 210, 49 | 'Omega': 937, 50 | 'Omicron': 927, 51 | 'Oslash': 216, 52 | 'Otilde': 213, 53 | 'Ouml': 214, 54 | 'Phi': 934, 55 | 'Pi': 928, 56 | 'Prime': 8243, 57 | 'Psi': 936, 58 | 'Rho': 929, 59 | 'Scaron': 352, 60 | 'Sigma': 931, 61 | 'THORN': 222, 62 | 'Tau': 932, 63 | 'Theta': 920, 64 | 'Uacute': 218, 65 | 'Ucirc': 219, 66 | 'Ugrave': 217, 67 | 'Upsilon': 933, 68 | 'Uuml': 220, 69 | 'Xi': 926, 70 | 'Yacute': 221, 71 | 'Yuml': 376, 72 | 'Zeta': 918, 73 | 'aacute': 225, 74 | 'acirc': 226, 75 | 'acute': 180, 76 | 'aelig': 230, 77 | 'agrave': 224, 78 | 'alefsym': 8501, 79 | 'alpha': 945, 80 | 'amp': 38, 81 | 'and': 8743, 82 | 'ang': 8736, 83 | 'apos': 39, 84 | 'aring': 229, 85 | 'asymp': 8776, 86 | 'atilde': 227, 87 | 'auml': 228, 88 | 'bdquo': 8222, 89 | 'beta': 946, 90 | 'brvbar': 166, 91 | 'bull': 8226, 92 | 'cap': 8745, 93 | 'ccedil': 231, 94 | 'cedil': 184, 95 | 'cent': 162, 96 | 'chi': 967, 97 | 'circ': 710, 98 | 'clubs': 9827, 99 | 'cong': 8773, 100 | 'copy': 169, 101 | 'crarr': 8629, 102 | 'cup': 8746, 103 | 'curren': 164, 104 | 'dArr': 8659, 105 | 'dagger': 8224, 106 | 'darr': 8595, 107 | 'deg': 176, 108 | 'delta': 948, 109 | 'diams': 9830, 110 | 'divide': 247, 111 | 'eacute': 233, 112 | 'ecirc': 234, 113 | 'egrave': 232, 114 | 'empty': 8709, 115 | 'emsp': 8195, 116 | 'ensp': 8194, 117 | 'epsilon': 949, 118 | 'equiv': 8801, 119 | 'eta': 951, 120 | 'eth': 240, 121 | 'euml': 235, 122 | 'euro': 8364, 123 | 'exist': 8707, 124 | 'fnof': 402, 125 | 'forall': 8704, 126 | 'frac12': 189, 127 | 'frac14': 188, 128 | 'frac34': 190, 129 | 'frasl': 8260, 130 | 'gamma': 947, 131 | 'ge': 8805, 132 | 'gt': 62, 133 | 'hArr': 8660, 134 | 'harr': 8596, 135 | 'hearts': 9829, 136 | 'hellip': 8230, 137 | 'iacute': 237, 138 | 'icirc': 238, 139 | 'iexcl': 161, 140 | 'igrave': 236, 141 | 'image': 8465, 142 | 'infin': 8734, 143 | 'int': 8747, 144 | 'iota': 953, 145 | 'iquest': 191, 146 | 'isin': 8712, 147 | 'iuml': 239, 148 | 'kappa': 954, 149 | 'lArr': 8656, 150 | 'lambda': 955, 151 | 'lang': 9001, 152 | 'laquo': 171, 153 | 'larr': 8592, 154 | 'lceil': 8968, 155 | 'ldquo': 8220, 156 | 'le': 8804, 157 | 'lfloor': 8970, 158 | 'lowast': 8727, 159 | 'loz': 9674, 160 | 'lrm': 8206, 161 | 'lsaquo': 8249, 162 | 'lsquo': 8216, 163 | 'lt': 60, 164 | 'macr': 175, 165 | 'mdash': 8212, 166 | 'micro': 181, 167 | 'middot': 183, 168 | 'minus': 8722, 169 | 'mu': 956, 170 | 'nabla': 8711, 171 | 'nbsp': 160, 172 | 'ndash': 8211, 173 | 'ne': 8800, 174 | 'ni': 8715, 175 | 'not': 172, 176 | 'notin': 8713, 177 | 'nsub': 8836, 178 | 'ntilde': 241, 179 | 'nu': 957, 180 | 'oacute': 243, 181 | 'ocirc': 244, 182 | 'oelig': 339, 183 | 'ograve': 242, 184 | 'oline': 8254, 185 | 'omega': 969, 186 | 'omicron': 959, 187 | 'oplus': 8853, 188 | 'or': 8744, 189 | 'ordf': 170, 190 | 'ordm': 186, 191 | 'oslash': 248, 192 | 'otilde': 245, 193 | 'otimes': 8855, 194 | 'ouml': 246, 195 | 'para': 182, 196 | 'part': 8706, 197 | 'permil': 8240, 198 | 'perp': 8869, 199 | 'phi': 966, 200 | 'pi': 960, 201 | 'piv': 982, 202 | 'plusmn': 177, 203 | 'pound': 163, 204 | 'prime': 8242, 205 | 'prod': 8719, 206 | 'prop': 8733, 207 | 'psi': 968, 208 | 'quot': 34, 209 | 'rArr': 8658, 210 | 'radic': 8730, 211 | 'rang': 9002, 212 | 'raquo': 187, 213 | 'rarr': 8594, 214 | 'rceil': 8969, 215 | 'rdquo': 8221, 216 | 'real': 8476, 217 | 'reg': 174, 218 | 'rfloor': 8971, 219 | 'rho': 961, 220 | 'rlm': 8207, 221 | 'rsaquo': 8250, 222 | 'rsquo': 8217, 223 | 'sbquo': 8218, 224 | 'scaron': 353, 225 | 'sdot': 8901, 226 | 'sect': 167, 227 | 'shy': 173, 228 | 'sigma': 963, 229 | 'sigmaf': 962, 230 | 'sim': 8764, 231 | 'spades': 9824, 232 | 'sub': 8834, 233 | 'sube': 8838, 234 | 'sum': 8721, 235 | 'sup': 8835, 236 | 'sup1': 185, 237 | 'sup2': 178, 238 | 'sup3': 179, 239 | 'supe': 8839, 240 | 'szlig': 223, 241 | 'tau': 964, 242 | 'there4': 8756, 243 | 'theta': 952, 244 | 'thetasym': 977, 245 | 'thinsp': 8201, 246 | 'thorn': 254, 247 | 'tilde': 732, 248 | 'times': 215, 249 | 'trade': 8482, 250 | 'uArr': 8657, 251 | 'uacute': 250, 252 | 'uarr': 8593, 253 | 'ucirc': 251, 254 | 'ugrave': 249, 255 | 'uml': 168, 256 | 'upsih': 978, 257 | 'upsilon': 965, 258 | 'uuml': 252, 259 | 'weierp': 8472, 260 | 'xi': 958, 261 | 'yacute': 253, 262 | 'yen': 165, 263 | 'yuml': 255, 264 | 'zeta': 950, 265 | 'zwj': 8205, 266 | 'zwnj': 8204 267 | } 268 | -------------------------------------------------------------------------------- /pyl/jinja2/_markupsafe/_native.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe._native 4 | ~~~~~~~~~~~~~~~~~~ 5 | 6 | Native Python implementation the C module is not compiled. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from jinja2._markupsafe import Markup 12 | 13 | 14 | def escape(s): 15 | """Convert the characters &, <, >, ' and " in string s to HTML-safe 16 | sequences. Use this if you need to display text that might contain 17 | such characters in HTML. Marks return value as markup string. 18 | """ 19 | if hasattr(s, '__html__'): 20 | return s.__html__() 21 | return Markup(unicode(s) 22 | .replace('&', '&') 23 | .replace('>', '>') 24 | .replace('<', '<') 25 | .replace("'", ''') 26 | .replace('"', '"') 27 | ) 28 | 29 | 30 | def escape_silent(s): 31 | """Like :func:`escape` but converts `None` into an empty 32 | markup string. 33 | """ 34 | if s is None: 35 | return Markup() 36 | return escape(s) 37 | 38 | 39 | def soft_unicode(s): 40 | """Make a string unicode if it isn't already. That way a markup 41 | string is not converted back to unicode. 42 | """ 43 | if not isinstance(s, unicode): 44 | s = unicode(s) 45 | return s 46 | -------------------------------------------------------------------------------- /pyl/jinja2/_markupsafe/tests.py: -------------------------------------------------------------------------------- 1 | import gc 2 | import unittest 3 | from jinja2._markupsafe import Markup, escape, escape_silent 4 | 5 | 6 | class MarkupTestCase(unittest.TestCase): 7 | 8 | def test_markup_operations(self): 9 | # adding two strings should escape the unsafe one 10 | unsafe = '' 11 | safe = Markup('username') 12 | assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe) 13 | 14 | # string interpolations are safe to use too 15 | assert Markup('%s') % '' == \ 16 | '<bad user>' 17 | assert Markup('%(username)s') % { 18 | 'username': '' 19 | } == '<bad user>' 20 | 21 | # an escaped object is markup too 22 | assert type(Markup('foo') + 'bar') is Markup 23 | 24 | # and it implements __html__ by returning itself 25 | x = Markup("foo") 26 | assert x.__html__() is x 27 | 28 | # it also knows how to treat __html__ objects 29 | class Foo(object): 30 | def __html__(self): 31 | return 'awesome' 32 | def __unicode__(self): 33 | return 'awesome' 34 | assert Markup(Foo()) == 'awesome' 35 | assert Markup('%s') % Foo() == \ 36 | 'awesome' 37 | 38 | # escaping and unescaping 39 | assert escape('"<>&\'') == '"<>&'' 40 | assert Markup("Foo & Bar").striptags() == "Foo & Bar" 41 | assert Markup("<test>").unescape() == "" 42 | 43 | def test_all_set(self): 44 | import jinja2._markupsafe as markup 45 | for item in markup.__all__: 46 | getattr(markup, item) 47 | 48 | def test_escape_silent(self): 49 | assert escape_silent(None) == Markup() 50 | assert escape(None) == Markup(None) 51 | assert escape_silent('') == Markup(u'<foo>') 52 | 53 | 54 | class MarkupLeakTestCase(unittest.TestCase): 55 | 56 | def test_markup_leaks(self): 57 | counts = set() 58 | for count in xrange(20): 59 | for item in xrange(1000): 60 | escape("foo") 61 | escape("") 62 | escape(u"foo") 63 | escape(u"") 64 | counts.add(len(gc.get_objects())) 65 | assert len(counts) == 1, 'ouch, c extension seems to leak objects' 66 | 67 | 68 | def suite(): 69 | suite = unittest.TestSuite() 70 | suite.addTest(unittest.makeSuite(MarkupTestCase)) 71 | 72 | # this test only tests the c extension 73 | if not hasattr(escape, 'func_code'): 74 | suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) 75 | 76 | return suite 77 | 78 | 79 | if __name__ == '__main__': 80 | unittest.main(defaultTest='suite') 81 | -------------------------------------------------------------------------------- /pyl/jinja2/bccache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.bccache 4 | ~~~~~~~~~~~~~~ 5 | 6 | This module implements the bytecode cache system Jinja is optionally 7 | using. This is useful if you have very complex template situations and 8 | the compiliation of all those templates slow down your application too 9 | much. 10 | 11 | Situations where this is useful are often forking web applications that 12 | are initialized on the first request. 13 | 14 | :copyright: (c) 2010 by the Jinja Team. 15 | :license: BSD. 16 | """ 17 | from os import path, listdir 18 | import sys 19 | import marshal 20 | import tempfile 21 | import cPickle as pickle 22 | import fnmatch 23 | from cStringIO import StringIO 24 | try: 25 | from hashlib import sha1 26 | except ImportError: 27 | from sha import new as sha1 28 | from jinja2.utils import open_if_exists 29 | 30 | 31 | bc_version = 2 32 | 33 | # magic version used to only change with new jinja versions. With 2.6 34 | # we change this to also take Python version changes into account. The 35 | # reason for this is that Python tends to segfault if fed earlier bytecode 36 | # versions because someone thought it would be a good idea to reuse opcodes 37 | # or make Python incompatible with earlier versions. 38 | bc_magic = 'j2'.encode('ascii') + \ 39 | pickle.dumps(bc_version, 2) + \ 40 | pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1]) 41 | 42 | 43 | class Bucket(object): 44 | """Buckets are used to store the bytecode for one template. It's created 45 | and initialized by the bytecode cache and passed to the loading functions. 46 | 47 | The buckets get an internal checksum from the cache assigned and use this 48 | to automatically reject outdated cache material. Individual bytecode 49 | cache subclasses don't have to care about cache invalidation. 50 | """ 51 | 52 | def __init__(self, environment, key, checksum): 53 | self.environment = environment 54 | self.key = key 55 | self.checksum = checksum 56 | self.reset() 57 | 58 | def reset(self): 59 | """Resets the bucket (unloads the bytecode).""" 60 | self.code = None 61 | 62 | def load_bytecode(self, f): 63 | """Loads bytecode from a file or file like object.""" 64 | # make sure the magic header is correct 65 | magic = f.read(len(bc_magic)) 66 | if magic != bc_magic: 67 | self.reset() 68 | return 69 | # the source code of the file changed, we need to reload 70 | checksum = pickle.load(f) 71 | if self.checksum != checksum: 72 | self.reset() 73 | return 74 | # now load the code. Because marshal is not able to load 75 | # from arbitrary streams we have to work around that 76 | if isinstance(f, file): 77 | self.code = marshal.load(f) 78 | else: 79 | self.code = marshal.loads(f.read()) 80 | 81 | def write_bytecode(self, f): 82 | """Dump the bytecode into the file or file like object passed.""" 83 | if self.code is None: 84 | raise TypeError('can\'t write empty bucket') 85 | f.write(bc_magic) 86 | pickle.dump(self.checksum, f, 2) 87 | if isinstance(f, file): 88 | marshal.dump(self.code, f) 89 | else: 90 | f.write(marshal.dumps(self.code)) 91 | 92 | def bytecode_from_string(self, string): 93 | """Load bytecode from a string.""" 94 | self.load_bytecode(StringIO(string)) 95 | 96 | def bytecode_to_string(self): 97 | """Return the bytecode as string.""" 98 | out = StringIO() 99 | self.write_bytecode(out) 100 | return out.getvalue() 101 | 102 | 103 | class BytecodeCache(object): 104 | """To implement your own bytecode cache you have to subclass this class 105 | and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of 106 | these methods are passed a :class:`~jinja2.bccache.Bucket`. 107 | 108 | A very basic bytecode cache that saves the bytecode on the file system:: 109 | 110 | from os import path 111 | 112 | class MyCache(BytecodeCache): 113 | 114 | def __init__(self, directory): 115 | self.directory = directory 116 | 117 | def load_bytecode(self, bucket): 118 | filename = path.join(self.directory, bucket.key) 119 | if path.exists(filename): 120 | with open(filename, 'rb') as f: 121 | bucket.load_bytecode(f) 122 | 123 | def dump_bytecode(self, bucket): 124 | filename = path.join(self.directory, bucket.key) 125 | with open(filename, 'wb') as f: 126 | bucket.write_bytecode(f) 127 | 128 | A more advanced version of a filesystem based bytecode cache is part of 129 | Jinja2. 130 | """ 131 | 132 | def load_bytecode(self, bucket): 133 | """Subclasses have to override this method to load bytecode into a 134 | bucket. If they are not able to find code in the cache for the 135 | bucket, it must not do anything. 136 | """ 137 | raise NotImplementedError() 138 | 139 | def dump_bytecode(self, bucket): 140 | """Subclasses have to override this method to write the bytecode 141 | from a bucket back to the cache. If it unable to do so it must not 142 | fail silently but raise an exception. 143 | """ 144 | raise NotImplementedError() 145 | 146 | def clear(self): 147 | """Clears the cache. This method is not used by Jinja2 but should be 148 | implemented to allow applications to clear the bytecode cache used 149 | by a particular environment. 150 | """ 151 | 152 | def get_cache_key(self, name, filename=None): 153 | """Returns the unique hash key for this template name.""" 154 | hash = sha1(name.encode('utf-8')) 155 | if filename is not None: 156 | if isinstance(filename, unicode): 157 | filename = filename.encode('utf-8') 158 | hash.update('|' + filename) 159 | return hash.hexdigest() 160 | 161 | def get_source_checksum(self, source): 162 | """Returns a checksum for the source.""" 163 | return sha1(source.encode('utf-8')).hexdigest() 164 | 165 | def get_bucket(self, environment, name, filename, source): 166 | """Return a cache bucket for the given template. All arguments are 167 | mandatory but filename may be `None`. 168 | """ 169 | key = self.get_cache_key(name, filename) 170 | checksum = self.get_source_checksum(source) 171 | bucket = Bucket(environment, key, checksum) 172 | self.load_bytecode(bucket) 173 | return bucket 174 | 175 | def set_bucket(self, bucket): 176 | """Put the bucket into the cache.""" 177 | self.dump_bytecode(bucket) 178 | 179 | 180 | class FileSystemBytecodeCache(BytecodeCache): 181 | """A bytecode cache that stores bytecode on the filesystem. It accepts 182 | two arguments: The directory where the cache items are stored and a 183 | pattern string that is used to build the filename. 184 | 185 | If no directory is specified the system temporary items folder is used. 186 | 187 | The pattern can be used to have multiple separate caches operate on the 188 | same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` 189 | is replaced with the cache key. 190 | 191 | >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') 192 | 193 | This bytecode cache supports clearing of the cache using the clear method. 194 | """ 195 | 196 | def __init__(self, directory=None, pattern='__jinja2_%s.cache'): 197 | if directory is None: 198 | directory = tempfile.gettempdir() 199 | self.directory = directory 200 | self.pattern = pattern 201 | 202 | def _get_cache_filename(self, bucket): 203 | return path.join(self.directory, self.pattern % bucket.key) 204 | 205 | def load_bytecode(self, bucket): 206 | f = open_if_exists(self._get_cache_filename(bucket), 'rb') 207 | if f is not None: 208 | try: 209 | bucket.load_bytecode(f) 210 | finally: 211 | f.close() 212 | 213 | def dump_bytecode(self, bucket): 214 | f = open(self._get_cache_filename(bucket), 'wb') 215 | try: 216 | bucket.write_bytecode(f) 217 | finally: 218 | f.close() 219 | 220 | def clear(self): 221 | # imported lazily here because google app-engine doesn't support 222 | # write access on the file system and the function does not exist 223 | # normally. 224 | from os import remove 225 | files = fnmatch.filter(listdir(self.directory), self.pattern % '*') 226 | for filename in files: 227 | try: 228 | remove(path.join(self.directory, filename)) 229 | except OSError: 230 | pass 231 | 232 | 233 | class MemcachedBytecodeCache(BytecodeCache): 234 | """This class implements a bytecode cache that uses a memcache cache for 235 | storing the information. It does not enforce a specific memcache library 236 | (tummy's memcache or cmemcache) but will accept any class that provides 237 | the minimal interface required. 238 | 239 | Libraries compatible with this class: 240 | 241 | - `werkzeug `_.contrib.cache 242 | - `python-memcached `_ 243 | - `cmemcache `_ 244 | 245 | (Unfortunately the django cache interface is not compatible because it 246 | does not support storing binary data, only unicode. You can however pass 247 | the underlying cache client to the bytecode cache which is available 248 | as `django.core.cache.cache._client`.) 249 | 250 | The minimal interface for the client passed to the constructor is this: 251 | 252 | .. class:: MinimalClientInterface 253 | 254 | .. method:: set(key, value[, timeout]) 255 | 256 | Stores the bytecode in the cache. `value` is a string and 257 | `timeout` the timeout of the key. If timeout is not provided 258 | a default timeout or no timeout should be assumed, if it's 259 | provided it's an integer with the number of seconds the cache 260 | item should exist. 261 | 262 | .. method:: get(key) 263 | 264 | Returns the value for the cache key. If the item does not 265 | exist in the cache the return value must be `None`. 266 | 267 | The other arguments to the constructor are the prefix for all keys that 268 | is added before the actual cache key and the timeout for the bytecode in 269 | the cache system. We recommend a high (or no) timeout. 270 | 271 | This bytecode cache does not support clearing of used items in the cache. 272 | The clear method is a no-operation function. 273 | """ 274 | 275 | def __init__(self, client, prefix='jinja2/bytecode/', timeout=None): 276 | self.client = client 277 | self.prefix = prefix 278 | self.timeout = timeout 279 | 280 | def load_bytecode(self, bucket): 281 | code = self.client.get(self.prefix + bucket.key) 282 | if code is not None: 283 | bucket.bytecode_from_string(code) 284 | 285 | def dump_bytecode(self, bucket): 286 | args = (self.prefix + bucket.key, bucket.bytecode_to_string()) 287 | if self.timeout is not None: 288 | args += (self.timeout,) 289 | self.client.set(*args) 290 | -------------------------------------------------------------------------------- /pyl/jinja2/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja.constants 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Various constants. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | 13 | #: list of lorem ipsum words used by the lipsum() helper function 14 | LOREM_IPSUM_WORDS = u'''\ 15 | a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at 16 | auctor augue bibendum blandit class commodo condimentum congue consectetuer 17 | consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus 18 | diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend 19 | elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames 20 | faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac 21 | hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum 22 | justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem 23 | luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie 24 | mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non 25 | nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque 26 | penatibus per pharetra phasellus placerat platea porta porttitor posuere 27 | potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus 28 | ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit 29 | sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor 30 | tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices 31 | ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus 32 | viverra volutpat vulputate''' 33 | -------------------------------------------------------------------------------- /pyl/jinja2/debug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.debug 4 | ~~~~~~~~~~~~ 5 | 6 | Implements the debug interface for Jinja. This module does some pretty 7 | ugly stuff with the Python traceback system in order to achieve tracebacks 8 | with correct line numbers, locals and contents. 9 | 10 | :copyright: (c) 2010 by the Jinja Team. 11 | :license: BSD, see LICENSE for more details. 12 | """ 13 | import sys 14 | import traceback 15 | from types import TracebackType 16 | from jinja2.utils import CodeType, missing, internal_code 17 | from jinja2.exceptions import TemplateSyntaxError 18 | 19 | # on pypy we can take advantage of transparent proxies 20 | try: 21 | from __pypy__ import tproxy 22 | except ImportError: 23 | tproxy = None 24 | 25 | 26 | # how does the raise helper look like? 27 | try: 28 | exec "raise TypeError, 'foo'" 29 | except SyntaxError: 30 | raise_helper = 'raise __jinja_exception__[1]' 31 | except TypeError: 32 | raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' 33 | 34 | 35 | class TracebackFrameProxy(object): 36 | """Proxies a traceback frame.""" 37 | 38 | def __init__(self, tb): 39 | self.tb = tb 40 | self._tb_next = None 41 | 42 | @property 43 | def tb_next(self): 44 | return self._tb_next 45 | 46 | def set_next(self, next): 47 | if tb_set_next is not None: 48 | try: 49 | tb_set_next(self.tb, next and next.tb or None) 50 | except Exception: 51 | # this function can fail due to all the hackery it does 52 | # on various python implementations. We just catch errors 53 | # down and ignore them if necessary. 54 | pass 55 | self._tb_next = next 56 | 57 | @property 58 | def is_jinja_frame(self): 59 | return '__jinja_template__' in self.tb.tb_frame.f_globals 60 | 61 | def __getattr__(self, name): 62 | return getattr(self.tb, name) 63 | 64 | 65 | def make_frame_proxy(frame): 66 | proxy = TracebackFrameProxy(frame) 67 | if tproxy is None: 68 | return proxy 69 | def operation_handler(operation, *args, **kwargs): 70 | if operation in ('__getattribute__', '__getattr__'): 71 | return getattr(proxy, args[0]) 72 | elif operation == '__setattr__': 73 | proxy.__setattr__(*args, **kwargs) 74 | else: 75 | return getattr(proxy, operation)(*args, **kwargs) 76 | return tproxy(TracebackType, operation_handler) 77 | 78 | 79 | class ProcessedTraceback(object): 80 | """Holds a Jinja preprocessed traceback for priting or reraising.""" 81 | 82 | def __init__(self, exc_type, exc_value, frames): 83 | assert frames, 'no frames for this traceback?' 84 | self.exc_type = exc_type 85 | self.exc_value = exc_value 86 | self.frames = frames 87 | 88 | # newly concatenate the frames (which are proxies) 89 | prev_tb = None 90 | for tb in self.frames: 91 | if prev_tb is not None: 92 | prev_tb.set_next(tb) 93 | prev_tb = tb 94 | prev_tb.set_next(None) 95 | 96 | def render_as_text(self, limit=None): 97 | """Return a string with the traceback.""" 98 | lines = traceback.format_exception(self.exc_type, self.exc_value, 99 | self.frames[0], limit=limit) 100 | return ''.join(lines).rstrip() 101 | 102 | def render_as_html(self, full=False): 103 | """Return a unicode string with the traceback as rendered HTML.""" 104 | from jinja2.debugrenderer import render_traceback 105 | return u'%s\n\n' % ( 106 | render_traceback(self, full=full), 107 | self.render_as_text().decode('utf-8', 'replace') 108 | ) 109 | 110 | @property 111 | def is_template_syntax_error(self): 112 | """`True` if this is a template syntax error.""" 113 | return isinstance(self.exc_value, TemplateSyntaxError) 114 | 115 | @property 116 | def exc_info(self): 117 | """Exception info tuple with a proxy around the frame objects.""" 118 | return self.exc_type, self.exc_value, self.frames[0] 119 | 120 | @property 121 | def standard_exc_info(self): 122 | """Standard python exc_info for re-raising""" 123 | tb = self.frames[0] 124 | # the frame will be an actual traceback (or transparent proxy) if 125 | # we are on pypy or a python implementation with support for tproxy 126 | if type(tb) is not TracebackType: 127 | tb = tb.tb 128 | return self.exc_type, self.exc_value, tb 129 | 130 | 131 | def make_traceback(exc_info, source_hint=None): 132 | """Creates a processed traceback object from the exc_info.""" 133 | exc_type, exc_value, tb = exc_info 134 | if isinstance(exc_value, TemplateSyntaxError): 135 | exc_info = translate_syntax_error(exc_value, source_hint) 136 | initial_skip = 0 137 | else: 138 | initial_skip = 1 139 | return translate_exception(exc_info, initial_skip) 140 | 141 | 142 | def translate_syntax_error(error, source=None): 143 | """Rewrites a syntax error to please traceback systems.""" 144 | error.source = source 145 | error.translated = True 146 | exc_info = (error.__class__, error, None) 147 | filename = error.filename 148 | if filename is None: 149 | filename = '' 150 | return fake_exc_info(exc_info, filename, error.lineno) 151 | 152 | 153 | def translate_exception(exc_info, initial_skip=0): 154 | """If passed an exc_info it will automatically rewrite the exceptions 155 | all the way down to the correct line numbers and frames. 156 | """ 157 | tb = exc_info[2] 158 | frames = [] 159 | 160 | # skip some internal frames if wanted 161 | for x in xrange(initial_skip): 162 | if tb is not None: 163 | tb = tb.tb_next 164 | initial_tb = tb 165 | 166 | while tb is not None: 167 | # skip frames decorated with @internalcode. These are internal 168 | # calls we can't avoid and that are useless in template debugging 169 | # output. 170 | if tb.tb_frame.f_code in internal_code: 171 | tb = tb.tb_next 172 | continue 173 | 174 | # save a reference to the next frame if we override the current 175 | # one with a faked one. 176 | next = tb.tb_next 177 | 178 | # fake template exceptions 179 | template = tb.tb_frame.f_globals.get('__jinja_template__') 180 | if template is not None: 181 | lineno = template.get_corresponding_lineno(tb.tb_lineno) 182 | tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, 183 | lineno)[2] 184 | 185 | frames.append(make_frame_proxy(tb)) 186 | tb = next 187 | 188 | # if we don't have any exceptions in the frames left, we have to 189 | # reraise it unchanged. 190 | # XXX: can we backup here? when could this happen? 191 | if not frames: 192 | raise exc_info[0], exc_info[1], exc_info[2] 193 | 194 | return ProcessedTraceback(exc_info[0], exc_info[1], frames) 195 | 196 | 197 | def fake_exc_info(exc_info, filename, lineno): 198 | """Helper for `translate_exception`.""" 199 | exc_type, exc_value, tb = exc_info 200 | 201 | # figure the real context out 202 | if tb is not None: 203 | real_locals = tb.tb_frame.f_locals.copy() 204 | ctx = real_locals.get('context') 205 | if ctx: 206 | locals = ctx.get_all() 207 | else: 208 | locals = {} 209 | for name, value in real_locals.iteritems(): 210 | if name.startswith('l_') and value is not missing: 211 | locals[name[2:]] = value 212 | 213 | # if there is a local called __jinja_exception__, we get 214 | # rid of it to not break the debug functionality. 215 | locals.pop('__jinja_exception__', None) 216 | else: 217 | locals = {} 218 | 219 | # assamble fake globals we need 220 | globals = { 221 | '__name__': filename, 222 | '__file__': filename, 223 | '__jinja_exception__': exc_info[:2], 224 | 225 | # we don't want to keep the reference to the template around 226 | # to not cause circular dependencies, but we mark it as Jinja 227 | # frame for the ProcessedTraceback 228 | '__jinja_template__': None 229 | } 230 | 231 | # and fake the exception 232 | code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') 233 | 234 | # if it's possible, change the name of the code. This won't work 235 | # on some python environments such as google appengine 236 | try: 237 | if tb is None: 238 | location = 'template' 239 | else: 240 | function = tb.tb_frame.f_code.co_name 241 | if function == 'root': 242 | location = 'top-level template code' 243 | elif function.startswith('block_'): 244 | location = 'block "%s"' % function[6:] 245 | else: 246 | location = 'template' 247 | code = CodeType(0, code.co_nlocals, code.co_stacksize, 248 | code.co_flags, code.co_code, code.co_consts, 249 | code.co_names, code.co_varnames, filename, 250 | location, code.co_firstlineno, 251 | code.co_lnotab, (), ()) 252 | except: 253 | pass 254 | 255 | # execute the code and catch the new traceback 256 | try: 257 | exec code in globals, locals 258 | except: 259 | exc_info = sys.exc_info() 260 | new_tb = exc_info[2].tb_next 261 | 262 | # return without this frame 263 | return exc_info[:2] + (new_tb,) 264 | 265 | 266 | def _init_ugly_crap(): 267 | """This function implements a few ugly things so that we can patch the 268 | traceback objects. The function returned allows resetting `tb_next` on 269 | any python traceback object. Do not attempt to use this on non cpython 270 | interpreters 271 | """ 272 | import ctypes 273 | from types import TracebackType 274 | 275 | # figure out side of _Py_ssize_t 276 | if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): 277 | _Py_ssize_t = ctypes.c_int64 278 | else: 279 | _Py_ssize_t = ctypes.c_int 280 | 281 | # regular python 282 | class _PyObject(ctypes.Structure): 283 | pass 284 | _PyObject._fields_ = [ 285 | ('ob_refcnt', _Py_ssize_t), 286 | ('ob_type', ctypes.POINTER(_PyObject)) 287 | ] 288 | 289 | # python with trace 290 | if hasattr(sys, 'getobjects'): 291 | class _PyObject(ctypes.Structure): 292 | pass 293 | _PyObject._fields_ = [ 294 | ('_ob_next', ctypes.POINTER(_PyObject)), 295 | ('_ob_prev', ctypes.POINTER(_PyObject)), 296 | ('ob_refcnt', _Py_ssize_t), 297 | ('ob_type', ctypes.POINTER(_PyObject)) 298 | ] 299 | 300 | class _Traceback(_PyObject): 301 | pass 302 | _Traceback._fields_ = [ 303 | ('tb_next', ctypes.POINTER(_Traceback)), 304 | ('tb_frame', ctypes.POINTER(_PyObject)), 305 | ('tb_lasti', ctypes.c_int), 306 | ('tb_lineno', ctypes.c_int) 307 | ] 308 | 309 | def tb_set_next(tb, next): 310 | """Set the tb_next attribute of a traceback object.""" 311 | if not (isinstance(tb, TracebackType) and 312 | (next is None or isinstance(next, TracebackType))): 313 | raise TypeError('tb_set_next arguments must be traceback objects') 314 | obj = _Traceback.from_address(id(tb)) 315 | if tb.tb_next is not None: 316 | old = _Traceback.from_address(id(tb.tb_next)) 317 | old.ob_refcnt -= 1 318 | if next is None: 319 | obj.tb_next = ctypes.POINTER(_Traceback)() 320 | else: 321 | next = _Traceback.from_address(id(next)) 322 | next.ob_refcnt += 1 323 | obj.tb_next = ctypes.pointer(next) 324 | 325 | return tb_set_next 326 | 327 | 328 | # try to get a tb_set_next implementation if we don't have transparent 329 | # proxies. 330 | tb_set_next = None 331 | if tproxy is None: 332 | try: 333 | from jinja2._debugsupport import tb_set_next 334 | except ImportError: 335 | try: 336 | tb_set_next = _init_ugly_crap() 337 | except: 338 | pass 339 | del _init_ugly_crap 340 | -------------------------------------------------------------------------------- /pyl/jinja2/defaults.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.defaults 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Jinja default filters and tags. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner 12 | 13 | 14 | # defaults for the parser / lexer 15 | BLOCK_START_STRING = '{%' 16 | BLOCK_END_STRING = '%}' 17 | VARIABLE_START_STRING = '{{' 18 | VARIABLE_END_STRING = '}}' 19 | COMMENT_START_STRING = '{#' 20 | COMMENT_END_STRING = '#}' 21 | LINE_STATEMENT_PREFIX = None 22 | LINE_COMMENT_PREFIX = None 23 | TRIM_BLOCKS = False 24 | NEWLINE_SEQUENCE = '\n' 25 | 26 | 27 | # default filters, tests and namespace 28 | from jinja2.filters import FILTERS as DEFAULT_FILTERS 29 | from jinja2.tests import TESTS as DEFAULT_TESTS 30 | DEFAULT_NAMESPACE = { 31 | 'range': xrange, 32 | 'dict': lambda **kw: kw, 33 | 'lipsum': generate_lorem_ipsum, 34 | 'cycler': Cycler, 35 | 'joiner': Joiner 36 | } 37 | 38 | 39 | # export all constants 40 | __all__ = tuple(x for x in locals().keys() if x.isupper()) 41 | -------------------------------------------------------------------------------- /pyl/jinja2/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.exceptions 4 | ~~~~~~~~~~~~~~~~~ 5 | 6 | Jinja exceptions. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | 13 | class TemplateError(Exception): 14 | """Baseclass for all template errors.""" 15 | 16 | def __init__(self, message=None): 17 | if message is not None: 18 | message = unicode(message).encode('utf-8') 19 | Exception.__init__(self, message) 20 | 21 | @property 22 | def message(self): 23 | if self.args: 24 | message = self.args[0] 25 | if message is not None: 26 | return message.decode('utf-8', 'replace') 27 | 28 | 29 | class TemplateNotFound(IOError, LookupError, TemplateError): 30 | """Raised if a template does not exist.""" 31 | 32 | # looks weird, but removes the warning descriptor that just 33 | # bogusly warns us about message being deprecated 34 | message = None 35 | 36 | def __init__(self, name, message=None): 37 | IOError.__init__(self) 38 | if message is None: 39 | message = name 40 | self.message = message 41 | self.name = name 42 | self.templates = [name] 43 | 44 | def __str__(self): 45 | return self.message.encode('utf-8') 46 | 47 | # unicode goes after __str__ because we configured 2to3 to rename 48 | # __unicode__ to __str__. because the 2to3 tree is not designed to 49 | # remove nodes from it, we leave the above __str__ around and let 50 | # it override at runtime. 51 | def __unicode__(self): 52 | return self.message 53 | 54 | 55 | class TemplatesNotFound(TemplateNotFound): 56 | """Like :class:`TemplateNotFound` but raised if multiple templates 57 | are selected. This is a subclass of :class:`TemplateNotFound` 58 | exception, so just catching the base exception will catch both. 59 | 60 | .. versionadded:: 2.2 61 | """ 62 | 63 | def __init__(self, names=(), message=None): 64 | if message is None: 65 | message = u'non of the templates given were found: ' + \ 66 | u', '.join(map(unicode, names)) 67 | TemplateNotFound.__init__(self, names and names[-1] or None, message) 68 | self.templates = list(names) 69 | 70 | 71 | class TemplateSyntaxError(TemplateError): 72 | """Raised to tell the user that there is a problem with the template.""" 73 | 74 | def __init__(self, message, lineno, name=None, filename=None): 75 | TemplateError.__init__(self, message) 76 | self.lineno = lineno 77 | self.name = name 78 | self.filename = filename 79 | self.source = None 80 | 81 | # this is set to True if the debug.translate_syntax_error 82 | # function translated the syntax error into a new traceback 83 | self.translated = False 84 | 85 | def __str__(self): 86 | return unicode(self).encode('utf-8') 87 | 88 | # unicode goes after __str__ because we configured 2to3 to rename 89 | # __unicode__ to __str__. because the 2to3 tree is not designed to 90 | # remove nodes from it, we leave the above __str__ around and let 91 | # it override at runtime. 92 | def __unicode__(self): 93 | # for translated errors we only return the message 94 | if self.translated: 95 | return self.message 96 | 97 | # otherwise attach some stuff 98 | location = 'line %d' % self.lineno 99 | name = self.filename or self.name 100 | if name: 101 | location = 'File "%s", %s' % (name, location) 102 | lines = [self.message, ' ' + location] 103 | 104 | # if the source is set, add the line to the output 105 | if self.source is not None: 106 | try: 107 | line = self.source.splitlines()[self.lineno - 1] 108 | except IndexError: 109 | line = None 110 | if line: 111 | lines.append(' ' + line.strip()) 112 | 113 | return u'\n'.join(lines) 114 | 115 | 116 | class TemplateAssertionError(TemplateSyntaxError): 117 | """Like a template syntax error, but covers cases where something in the 118 | template caused an error at compile time that wasn't necessarily caused 119 | by a syntax error. However it's a direct subclass of 120 | :exc:`TemplateSyntaxError` and has the same attributes. 121 | """ 122 | 123 | 124 | class TemplateRuntimeError(TemplateError): 125 | """A generic runtime error in the template engine. Under some situations 126 | Jinja may raise this exception. 127 | """ 128 | 129 | 130 | class UndefinedError(TemplateRuntimeError): 131 | """Raised if a template tries to operate on :class:`Undefined`.""" 132 | 133 | 134 | class SecurityError(TemplateRuntimeError): 135 | """Raised if a template tries to do something insecure if the 136 | sandbox is enabled. 137 | """ 138 | 139 | 140 | class FilterArgumentError(TemplateRuntimeError): 141 | """This error is raised if a filter was called with inappropriate 142 | arguments 143 | """ 144 | -------------------------------------------------------------------------------- /pyl/jinja2/meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.meta 4 | ~~~~~~~~~~~ 5 | 6 | This module implements various functions that exposes information about 7 | templates that might be interesting for various kinds of applications. 8 | 9 | :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | from jinja2 import nodes 13 | from jinja2.compiler import CodeGenerator 14 | 15 | 16 | class TrackingCodeGenerator(CodeGenerator): 17 | """We abuse the code generator for introspection.""" 18 | 19 | def __init__(self, environment): 20 | CodeGenerator.__init__(self, environment, '', 21 | '') 22 | self.undeclared_identifiers = set() 23 | 24 | def write(self, x): 25 | """Don't write.""" 26 | 27 | def pull_locals(self, frame): 28 | """Remember all undeclared identifiers.""" 29 | self.undeclared_identifiers.update(frame.identifiers.undeclared) 30 | 31 | 32 | def find_undeclared_variables(ast): 33 | """Returns a set of all variables in the AST that will be looked up from 34 | the context at runtime. Because at compile time it's not known which 35 | variables will be used depending on the path the execution takes at 36 | runtime, all variables are returned. 37 | 38 | >>> from jinja2 import Environment, meta 39 | >>> env = Environment() 40 | >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') 41 | >>> meta.find_undeclared_variables(ast) 42 | set(['bar']) 43 | 44 | .. admonition:: Implementation 45 | 46 | Internally the code generator is used for finding undeclared variables. 47 | This is good to know because the code generator might raise a 48 | :exc:`TemplateAssertionError` during compilation and as a matter of 49 | fact this function can currently raise that exception as well. 50 | """ 51 | codegen = TrackingCodeGenerator(ast.environment) 52 | codegen.visit(ast) 53 | return codegen.undeclared_identifiers 54 | 55 | 56 | def find_referenced_templates(ast): 57 | """Finds all the referenced templates from the AST. This will return an 58 | iterator over all the hardcoded template extensions, inclusions and 59 | imports. If dynamic inheritance or inclusion is used, `None` will be 60 | yielded. 61 | 62 | >>> from jinja2 import Environment, meta 63 | >>> env = Environment() 64 | >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') 65 | >>> list(meta.find_referenced_templates(ast)) 66 | ['layout.html', None] 67 | 68 | This function is useful for dependency tracking. For example if you want 69 | to rebuild parts of the website after a layout template has changed. 70 | """ 71 | for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, 72 | nodes.Include)): 73 | if not isinstance(node.template, nodes.Const): 74 | # a tuple with some non consts in there 75 | if isinstance(node.template, (nodes.Tuple, nodes.List)): 76 | for template_name in node.template.items: 77 | # something const, only yield the strings and ignore 78 | # non-string consts that really just make no sense 79 | if isinstance(template_name, nodes.Const): 80 | if isinstance(template_name.value, basestring): 81 | yield template_name.value 82 | # something dynamic in there 83 | else: 84 | yield None 85 | # something dynamic we don't know about here 86 | else: 87 | yield None 88 | continue 89 | # constant is a basestring, direct template name 90 | if isinstance(node.template.value, basestring): 91 | yield node.template.value 92 | # a tuple or list (latter *should* not happen) made of consts, 93 | # yield the consts that are strings. We could warn here for 94 | # non string values 95 | elif isinstance(node, nodes.Include) and \ 96 | isinstance(node.template.value, (tuple, list)): 97 | for template_name in node.template.value: 98 | if isinstance(template_name, basestring): 99 | yield template_name 100 | # something else we don't care about, we could warn here 101 | else: 102 | yield None 103 | -------------------------------------------------------------------------------- /pyl/jinja2/optimizer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.optimizer 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | The jinja optimizer is currently trying to constant fold a few expressions 7 | and modify the AST in place so that it should be easier to evaluate it. 8 | 9 | Because the AST does not contain all the scoping information and the 10 | compiler has to find that out, we cannot do all the optimizations we 11 | want. For example loop unrolling doesn't work because unrolled loops would 12 | have a different scoping. 13 | 14 | The solution would be a second syntax tree that has the scoping rules stored. 15 | 16 | :copyright: (c) 2010 by the Jinja Team. 17 | :license: BSD. 18 | """ 19 | from jinja2 import nodes 20 | from jinja2.visitor import NodeTransformer 21 | 22 | 23 | def optimize(node, environment): 24 | """The context hint can be used to perform an static optimization 25 | based on the context given.""" 26 | optimizer = Optimizer(environment) 27 | return optimizer.visit(node) 28 | 29 | 30 | class Optimizer(NodeTransformer): 31 | 32 | def __init__(self, environment): 33 | self.environment = environment 34 | 35 | def visit_If(self, node): 36 | """Eliminate dead code.""" 37 | # do not optimize ifs that have a block inside so that it doesn't 38 | # break super(). 39 | if node.find(nodes.Block) is not None: 40 | return self.generic_visit(node) 41 | try: 42 | val = self.visit(node.test).as_const() 43 | except nodes.Impossible: 44 | return self.generic_visit(node) 45 | if val: 46 | body = node.body 47 | else: 48 | body = node.else_ 49 | result = [] 50 | for node in body: 51 | result.extend(self.visit_list(node)) 52 | return result 53 | 54 | def fold(self, node): 55 | """Do constant folding.""" 56 | node = self.generic_visit(node) 57 | try: 58 | return nodes.Const.from_untrusted(node.as_const(), 59 | lineno=node.lineno, 60 | environment=self.environment) 61 | except nodes.Impossible: 62 | return node 63 | 64 | visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \ 65 | visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \ 66 | visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \ 67 | visit_Filter = visit_Test = visit_CondExpr = fold 68 | del fold 69 | -------------------------------------------------------------------------------- /pyl/jinja2/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.tests 4 | ~~~~~~~~~~~~ 5 | 6 | Jinja test functions. Used with the "is" operator. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import re 12 | from jinja2.runtime import Undefined 13 | 14 | # nose, nothing here to test 15 | __test__ = False 16 | 17 | 18 | number_re = re.compile(r'^-?\d+(\.\d+)?$') 19 | regex_type = type(number_re) 20 | 21 | 22 | try: 23 | test_callable = callable 24 | except NameError: 25 | def test_callable(x): 26 | return hasattr(x, '__call__') 27 | 28 | 29 | def test_odd(value): 30 | """Return true if the variable is odd.""" 31 | return value % 2 == 1 32 | 33 | 34 | def test_even(value): 35 | """Return true if the variable is even.""" 36 | return value % 2 == 0 37 | 38 | 39 | def test_divisibleby(value, num): 40 | """Check if a variable is divisible by a number.""" 41 | return value % num == 0 42 | 43 | 44 | def test_defined(value): 45 | """Return true if the variable is defined: 46 | 47 | .. sourcecode:: jinja 48 | 49 | {% if variable is defined %} 50 | value of variable: {{ variable }} 51 | {% else %} 52 | variable is not defined 53 | {% endif %} 54 | 55 | See the :func:`default` filter for a simple way to set undefined 56 | variables. 57 | """ 58 | return not isinstance(value, Undefined) 59 | 60 | 61 | def test_undefined(value): 62 | """Like :func:`defined` but the other way round.""" 63 | return isinstance(value, Undefined) 64 | 65 | 66 | def test_none(value): 67 | """Return true if the variable is none.""" 68 | return value is None 69 | 70 | 71 | def test_lower(value): 72 | """Return true if the variable is lowercased.""" 73 | return unicode(value).islower() 74 | 75 | 76 | def test_upper(value): 77 | """Return true if the variable is uppercased.""" 78 | return unicode(value).isupper() 79 | 80 | 81 | def test_string(value): 82 | """Return true if the object is a string.""" 83 | return isinstance(value, basestring) 84 | 85 | 86 | def test_number(value): 87 | """Return true if the variable is a number.""" 88 | return isinstance(value, (int, long, float, complex)) 89 | 90 | 91 | def test_sequence(value): 92 | """Return true if the variable is a sequence. Sequences are variables 93 | that are iterable. 94 | """ 95 | try: 96 | len(value) 97 | value.__getitem__ 98 | except: 99 | return False 100 | return True 101 | 102 | 103 | def test_sameas(value, other): 104 | """Check if an object points to the same memory address than another 105 | object: 106 | 107 | .. sourcecode:: jinja 108 | 109 | {% if foo.attribute is sameas false %} 110 | the foo attribute really is the `False` singleton 111 | {% endif %} 112 | """ 113 | return value is other 114 | 115 | 116 | def test_iterable(value): 117 | """Check if it's possible to iterate over an object.""" 118 | try: 119 | iter(value) 120 | except TypeError: 121 | return False 122 | return True 123 | 124 | 125 | def test_escaped(value): 126 | """Check if the value is escaped.""" 127 | return hasattr(value, '__html__') 128 | 129 | 130 | TESTS = { 131 | 'odd': test_odd, 132 | 'even': test_even, 133 | 'divisibleby': test_divisibleby, 134 | 'defined': test_defined, 135 | 'undefined': test_undefined, 136 | 'none': test_none, 137 | 'lower': test_lower, 138 | 'upper': test_upper, 139 | 'string': test_string, 140 | 'number': test_number, 141 | 'sequence': test_sequence, 142 | 'iterable': test_iterable, 143 | 'callable': test_callable, 144 | 'sameas': test_sameas, 145 | 'escaped': test_escaped 146 | } 147 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | All the unittests of Jinja2. These tests can be executed by 7 | either running run-tests.py using multiple Python versions at 8 | the same time. 9 | 10 | :copyright: (c) 2010 by the Jinja Team. 11 | :license: BSD, see LICENSE for more details. 12 | """ 13 | import os 14 | import re 15 | import sys 16 | import unittest 17 | from traceback import format_exception 18 | from jinja2 import loaders 19 | 20 | 21 | here = os.path.dirname(os.path.abspath(__file__)) 22 | 23 | dict_loader = loaders.DictLoader({ 24 | 'justdict.html': 'FOO' 25 | }) 26 | package_loader = loaders.PackageLoader('jinja2.testsuite.res', 'templates') 27 | filesystem_loader = loaders.FileSystemLoader(here + '/res/templates') 28 | function_loader = loaders.FunctionLoader({'justfunction.html': 'FOO'}.get) 29 | choice_loader = loaders.ChoiceLoader([dict_loader, package_loader]) 30 | prefix_loader = loaders.PrefixLoader({ 31 | 'a': filesystem_loader, 32 | 'b': dict_loader 33 | }) 34 | 35 | 36 | class JinjaTestCase(unittest.TestCase): 37 | 38 | ### use only these methods for testing. If you need standard 39 | ### unittest method, wrap them! 40 | 41 | def setup(self): 42 | pass 43 | 44 | def teardown(self): 45 | pass 46 | 47 | def setUp(self): 48 | self.setup() 49 | 50 | def tearDown(self): 51 | self.teardown() 52 | 53 | def assert_equal(self, a, b): 54 | return self.assertEqual(a, b) 55 | 56 | def assert_raises(self, *args, **kwargs): 57 | return self.assertRaises(*args, **kwargs) 58 | 59 | def assert_traceback_matches(self, callback, expected_tb): 60 | try: 61 | callback() 62 | except Exception, e: 63 | tb = format_exception(*sys.exc_info()) 64 | if re.search(expected_tb.strip(), ''.join(tb)) is None: 65 | raise self.fail('Traceback did not match:\n\n%s\nexpected:\n%s' 66 | % (''.join(tb), expected_tb)) 67 | else: 68 | self.fail('Expected exception') 69 | 70 | 71 | def suite(): 72 | from jinja2.testsuite import ext, filters, tests, core_tags, \ 73 | loader, inheritance, imports, lexnparse, security, api, \ 74 | regression, debug, utils, doctests 75 | suite = unittest.TestSuite() 76 | suite.addTest(ext.suite()) 77 | suite.addTest(filters.suite()) 78 | suite.addTest(tests.suite()) 79 | suite.addTest(core_tags.suite()) 80 | suite.addTest(loader.suite()) 81 | suite.addTest(inheritance.suite()) 82 | suite.addTest(imports.suite()) 83 | suite.addTest(lexnparse.suite()) 84 | suite.addTest(security.suite()) 85 | suite.addTest(api.suite()) 86 | suite.addTest(regression.suite()) 87 | suite.addTest(debug.suite()) 88 | suite.addTest(utils.suite()) 89 | 90 | # doctests will not run on python 3 currently. Too many issues 91 | # with that, do not test that on that platform. 92 | if sys.version_info < (3, 0): 93 | suite.addTest(doctests.suite()) 94 | 95 | return suite 96 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.api 4 | ~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests the public API and related stuff. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | 13 | from jinja2.testsuite import JinjaTestCase 14 | 15 | from jinja2 import Environment, Undefined, DebugUndefined, \ 16 | StrictUndefined, UndefinedError, meta, \ 17 | is_undefined, Template, DictLoader 18 | from jinja2.utils import Cycler 19 | 20 | env = Environment() 21 | 22 | 23 | class ExtendedAPITestCase(JinjaTestCase): 24 | 25 | def test_item_and_attribute(self): 26 | from jinja2.sandbox import SandboxedEnvironment 27 | 28 | for env in Environment(), SandboxedEnvironment(): 29 | # the |list is necessary for python3 30 | tmpl = env.from_string('{{ foo.items()|list }}') 31 | assert tmpl.render(foo={'items': 42}) == "[('items', 42)]" 32 | tmpl = env.from_string('{{ foo|attr("items")()|list }}') 33 | assert tmpl.render(foo={'items': 42}) == "[('items', 42)]" 34 | tmpl = env.from_string('{{ foo["items"] }}') 35 | assert tmpl.render(foo={'items': 42}) == '42' 36 | 37 | def test_finalizer(self): 38 | def finalize_none_empty(value): 39 | if value is None: 40 | value = u'' 41 | return value 42 | env = Environment(finalize=finalize_none_empty) 43 | tmpl = env.from_string('{% for item in seq %}|{{ item }}{% endfor %}') 44 | assert tmpl.render(seq=(None, 1, "foo")) == '||1|foo' 45 | tmpl = env.from_string('<{{ none }}>') 46 | assert tmpl.render() == '<>' 47 | 48 | def test_cycler(self): 49 | items = 1, 2, 3 50 | c = Cycler(*items) 51 | for item in items + items: 52 | assert c.current == item 53 | assert c.next() == item 54 | c.next() 55 | assert c.current == 2 56 | c.reset() 57 | assert c.current == 1 58 | 59 | def test_expressions(self): 60 | expr = env.compile_expression("foo") 61 | assert expr() is None 62 | assert expr(foo=42) == 42 63 | expr2 = env.compile_expression("foo", undefined_to_none=False) 64 | assert is_undefined(expr2()) 65 | 66 | expr = env.compile_expression("42 + foo") 67 | assert expr(foo=42) == 84 68 | 69 | def test_template_passthrough(self): 70 | t = Template('Content') 71 | assert env.get_template(t) is t 72 | assert env.select_template([t]) is t 73 | assert env.get_or_select_template([t]) is t 74 | assert env.get_or_select_template(t) is t 75 | 76 | def test_autoescape_autoselect(self): 77 | def select_autoescape(name): 78 | if name is None or '.' not in name: 79 | return False 80 | return name.endswith('.html') 81 | env = Environment(autoescape=select_autoescape, 82 | loader=DictLoader({ 83 | 'test.txt': '{{ foo }}', 84 | 'test.html': '{{ foo }}' 85 | })) 86 | t = env.get_template('test.txt') 87 | assert t.render(foo='') == '' 88 | t = env.get_template('test.html') 89 | assert t.render(foo='') == '<foo>' 90 | t = env.from_string('{{ foo }}') 91 | assert t.render(foo='') == '' 92 | 93 | 94 | class MetaTestCase(JinjaTestCase): 95 | 96 | def test_find_undeclared_variables(self): 97 | ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') 98 | x = meta.find_undeclared_variables(ast) 99 | assert x == set(['bar']) 100 | 101 | ast = env.parse('{% set foo = 42 %}{{ bar + foo }}' 102 | '{% macro meh(x) %}{{ x }}{% endmacro %}' 103 | '{% for item in seq %}{{ muh(item) + meh(seq) }}{% endfor %}') 104 | x = meta.find_undeclared_variables(ast) 105 | assert x == set(['bar', 'seq', 'muh']) 106 | 107 | def test_find_refererenced_templates(self): 108 | ast = env.parse('{% extends "layout.html" %}{% include helper %}') 109 | i = meta.find_referenced_templates(ast) 110 | assert i.next() == 'layout.html' 111 | assert i.next() is None 112 | assert list(i) == [] 113 | 114 | ast = env.parse('{% extends "layout.html" %}' 115 | '{% from "test.html" import a, b as c %}' 116 | '{% import "meh.html" as meh %}' 117 | '{% include "muh.html" %}') 118 | i = meta.find_referenced_templates(ast) 119 | assert list(i) == ['layout.html', 'test.html', 'meh.html', 'muh.html'] 120 | 121 | def test_find_included_templates(self): 122 | ast = env.parse('{% include ["foo.html", "bar.html"] %}') 123 | i = meta.find_referenced_templates(ast) 124 | assert list(i) == ['foo.html', 'bar.html'] 125 | 126 | ast = env.parse('{% include ("foo.html", "bar.html") %}') 127 | i = meta.find_referenced_templates(ast) 128 | assert list(i) == ['foo.html', 'bar.html'] 129 | 130 | ast = env.parse('{% include ["foo.html", "bar.html", foo] %}') 131 | i = meta.find_referenced_templates(ast) 132 | assert list(i) == ['foo.html', 'bar.html', None] 133 | 134 | ast = env.parse('{% include ("foo.html", "bar.html", foo) %}') 135 | i = meta.find_referenced_templates(ast) 136 | assert list(i) == ['foo.html', 'bar.html', None] 137 | 138 | 139 | class StreamingTestCase(JinjaTestCase): 140 | 141 | def test_basic_streaming(self): 142 | tmpl = env.from_string("
    {% for item in seq %}
  • {{ loop.index " 143 | "}} - {{ item }}
  • {%- endfor %}
") 144 | stream = tmpl.stream(seq=range(4)) 145 | self.assert_equal(stream.next(), '
    ') 146 | self.assert_equal(stream.next(), '
  • 1 - 0
  • ') 147 | self.assert_equal(stream.next(), '
  • 2 - 1
  • ') 148 | self.assert_equal(stream.next(), '
  • 3 - 2
  • ') 149 | self.assert_equal(stream.next(), '
  • 4 - 3
  • ') 150 | self.assert_equal(stream.next(), '
') 151 | 152 | def test_buffered_streaming(self): 153 | tmpl = env.from_string("
    {% for item in seq %}
  • {{ loop.index " 154 | "}} - {{ item }}
  • {%- endfor %}
") 155 | stream = tmpl.stream(seq=range(4)) 156 | stream.enable_buffering(size=3) 157 | self.assert_equal(stream.next(), u'
  • 1 - 0
  • 2 - 1
  • ') 158 | self.assert_equal(stream.next(), u'
  • 3 - 2
  • 4 - 3
') 159 | 160 | def test_streaming_behavior(self): 161 | tmpl = env.from_string("") 162 | stream = tmpl.stream() 163 | assert not stream.buffered 164 | stream.enable_buffering(20) 165 | assert stream.buffered 166 | stream.disable_buffering() 167 | assert not stream.buffered 168 | 169 | 170 | class UndefinedTestCase(JinjaTestCase): 171 | 172 | def test_stopiteration_is_undefined(self): 173 | def test(): 174 | raise StopIteration() 175 | t = Template('A{{ test() }}B') 176 | assert t.render(test=test) == 'AB' 177 | t = Template('A{{ test().missingattribute }}B') 178 | self.assert_raises(UndefinedError, t.render, test=test) 179 | 180 | def test_undefined_and_special_attributes(self): 181 | try: 182 | Undefined('Foo').__dict__ 183 | except AttributeError: 184 | pass 185 | else: 186 | assert False, "Expected actual attribute error" 187 | 188 | def test_default_undefined(self): 189 | env = Environment(undefined=Undefined) 190 | self.assert_equal(env.from_string('{{ missing }}').render(), u'') 191 | self.assert_raises(UndefinedError, 192 | env.from_string('{{ missing.attribute }}').render) 193 | self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]') 194 | self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True') 195 | self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '') 196 | self.assert_equal(env.from_string('{{ not missing }}').render(), 'True') 197 | 198 | def test_debug_undefined(self): 199 | env = Environment(undefined=DebugUndefined) 200 | self.assert_equal(env.from_string('{{ missing }}').render(), '{{ missing }}') 201 | self.assert_raises(UndefinedError, 202 | env.from_string('{{ missing.attribute }}').render) 203 | self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]') 204 | self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True') 205 | self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), 206 | u"{{ no such element: int object['missing'] }}") 207 | self.assert_equal(env.from_string('{{ not missing }}').render(), 'True') 208 | 209 | def test_strict_undefined(self): 210 | env = Environment(undefined=StrictUndefined) 211 | self.assert_raises(UndefinedError, env.from_string('{{ missing }}').render) 212 | self.assert_raises(UndefinedError, env.from_string('{{ missing.attribute }}').render) 213 | self.assert_raises(UndefinedError, env.from_string('{{ missing|list }}').render) 214 | self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True') 215 | self.assert_raises(UndefinedError, env.from_string('{{ foo.missing }}').render, foo=42) 216 | self.assert_raises(UndefinedError, env.from_string('{{ not missing }}').render) 217 | 218 | def test_indexing_gives_undefined(self): 219 | t = Template("{{ var[42].foo }}") 220 | self.assert_raises(UndefinedError, t.render, var=0) 221 | 222 | def test_none_gives_proper_error(self): 223 | try: 224 | Environment().getattr(None, 'split')() 225 | except UndefinedError, e: 226 | assert e.message == "'None' has no attribute 'split'" 227 | else: 228 | assert False, 'expected exception' 229 | 230 | def test_object_repr(self): 231 | try: 232 | Undefined(obj=42, name='upper')() 233 | except UndefinedError, e: 234 | assert e.message == "'int object' has no attribute 'upper'" 235 | else: 236 | assert False, 'expected exception' 237 | 238 | 239 | def suite(): 240 | suite = unittest.TestSuite() 241 | suite.addTest(unittest.makeSuite(ExtendedAPITestCase)) 242 | suite.addTest(unittest.makeSuite(MetaTestCase)) 243 | suite.addTest(unittest.makeSuite(StreamingTestCase)) 244 | suite.addTest(unittest.makeSuite(UndefinedTestCase)) 245 | return suite 246 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/core_tags.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.core_tags 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Test the core tags like for and if. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | 13 | from jinja2.testsuite import JinjaTestCase 14 | 15 | from jinja2 import Environment, TemplateSyntaxError, UndefinedError, \ 16 | DictLoader 17 | 18 | env = Environment() 19 | 20 | 21 | class ForLoopTestCase(JinjaTestCase): 22 | 23 | def test_simple(self): 24 | tmpl = env.from_string('{% for item in seq %}{{ item }}{% endfor %}') 25 | assert tmpl.render(seq=range(10)) == '0123456789' 26 | 27 | def test_else(self): 28 | tmpl = env.from_string('{% for item in seq %}XXX{% else %}...{% endfor %}') 29 | assert tmpl.render() == '...' 30 | 31 | def test_empty_blocks(self): 32 | tmpl = env.from_string('<{% for item in seq %}{% else %}{% endfor %}>') 33 | assert tmpl.render() == '<>' 34 | 35 | def test_context_vars(self): 36 | tmpl = env.from_string('''{% for item in seq -%} 37 | {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{ 38 | loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{ 39 | loop.length }}###{% endfor %}''') 40 | one, two, _ = tmpl.render(seq=[0, 1]).split('###') 41 | (one_index, one_index0, one_revindex, one_revindex0, one_first, 42 | one_last, one_length) = one.split('|') 43 | (two_index, two_index0, two_revindex, two_revindex0, two_first, 44 | two_last, two_length) = two.split('|') 45 | 46 | assert int(one_index) == 1 and int(two_index) == 2 47 | assert int(one_index0) == 0 and int(two_index0) == 1 48 | assert int(one_revindex) == 2 and int(two_revindex) == 1 49 | assert int(one_revindex0) == 1 and int(two_revindex0) == 0 50 | assert one_first == 'True' and two_first == 'False' 51 | assert one_last == 'False' and two_last == 'True' 52 | assert one_length == two_length == '2' 53 | 54 | def test_cycling(self): 55 | tmpl = env.from_string('''{% for item in seq %}{{ 56 | loop.cycle('<1>', '<2>') }}{% endfor %}{% 57 | for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''') 58 | output = tmpl.render(seq=range(4), through=('<1>', '<2>')) 59 | assert output == '<1><2>' * 4 60 | 61 | def test_scope(self): 62 | tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}') 63 | output = tmpl.render(seq=range(10)) 64 | assert not output 65 | 66 | def test_varlen(self): 67 | def inner(): 68 | for item in range(5): 69 | yield item 70 | tmpl = env.from_string('{% for item in iter %}{{ item }}{% endfor %}') 71 | output = tmpl.render(iter=inner()) 72 | assert output == '01234' 73 | 74 | def test_noniter(self): 75 | tmpl = env.from_string('{% for item in none %}...{% endfor %}') 76 | self.assert_raises(TypeError, tmpl.render) 77 | 78 | def test_recursive(self): 79 | tmpl = env.from_string('''{% for item in seq recursive -%} 80 | [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] 81 | {%- endfor %}''') 82 | assert tmpl.render(seq=[ 83 | dict(a=1, b=[dict(a=1), dict(a=2)]), 84 | dict(a=2, b=[dict(a=1), dict(a=2)]), 85 | dict(a=3, b=[dict(a='a')]) 86 | ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]' 87 | 88 | def test_looploop(self): 89 | tmpl = env.from_string('''{% for row in table %} 90 | {%- set rowloop = loop -%} 91 | {% for cell in row -%} 92 | [{{ rowloop.index }}|{{ loop.index }}] 93 | {%- endfor %} 94 | {%- endfor %}''') 95 | assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]' 96 | 97 | def test_reversed_bug(self): 98 | tmpl = env.from_string('{% for i in items %}{{ i }}' 99 | '{% if not loop.last %}' 100 | ',{% endif %}{% endfor %}') 101 | assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3' 102 | 103 | def test_loop_errors(self): 104 | tmpl = env.from_string('''{% for item in [1] if loop.index 105 | == 0 %}...{% endfor %}''') 106 | self.assert_raises(UndefinedError, tmpl.render) 107 | tmpl = env.from_string('''{% for item in [] %}...{% else 108 | %}{{ loop }}{% endfor %}''') 109 | assert tmpl.render() == '' 110 | 111 | def test_loop_filter(self): 112 | tmpl = env.from_string('{% for item in range(10) if item ' 113 | 'is even %}[{{ item }}]{% endfor %}') 114 | assert tmpl.render() == '[0][2][4][6][8]' 115 | tmpl = env.from_string(''' 116 | {%- for item in range(10) if item is even %}[{{ 117 | loop.index }}:{{ item }}]{% endfor %}''') 118 | assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]' 119 | 120 | def test_loop_unassignable(self): 121 | self.assert_raises(TemplateSyntaxError, env.from_string, 122 | '{% for loop in seq %}...{% endfor %}') 123 | 124 | def test_scoped_special_var(self): 125 | t = env.from_string('{% for s in seq %}[{{ loop.first }}{% for c in s %}' 126 | '|{{ loop.first }}{% endfor %}]{% endfor %}') 127 | assert t.render(seq=('ab', 'cd')) == '[True|True|False][False|True|False]' 128 | 129 | def test_scoped_loop_var(self): 130 | t = env.from_string('{% for x in seq %}{{ loop.first }}' 131 | '{% for y in seq %}{% endfor %}{% endfor %}') 132 | assert t.render(seq='ab') == 'TrueFalse' 133 | t = env.from_string('{% for x in seq %}{% for y in seq %}' 134 | '{{ loop.first }}{% endfor %}{% endfor %}') 135 | assert t.render(seq='ab') == 'TrueFalseTrueFalse' 136 | 137 | def test_recursive_empty_loop_iter(self): 138 | t = env.from_string(''' 139 | {%- for item in foo recursive -%}{%- endfor -%} 140 | ''') 141 | assert t.render(dict(foo=[])) == '' 142 | 143 | def test_call_in_loop(self): 144 | t = env.from_string(''' 145 | {%- macro do_something() -%} 146 | [{{ caller() }}] 147 | {%- endmacro %} 148 | 149 | {%- for i in [1, 2, 3] %} 150 | {%- call do_something() -%} 151 | {{ i }} 152 | {%- endcall %} 153 | {%- endfor -%} 154 | ''') 155 | assert t.render() == '[1][2][3]' 156 | 157 | def test_scoping_bug(self): 158 | t = env.from_string(''' 159 | {%- for item in foo %}...{{ item }}...{% endfor %} 160 | {%- macro item(a) %}...{{ a }}...{% endmacro %} 161 | {{- item(2) -}} 162 | ''') 163 | assert t.render(foo=(1,)) == '...1......2...' 164 | 165 | def test_unpacking(self): 166 | tmpl = env.from_string('{% for a, b, c in [[1, 2, 3]] %}' 167 | '{{ a }}|{{ b }}|{{ c }}{% endfor %}') 168 | assert tmpl.render() == '1|2|3' 169 | 170 | 171 | class IfConditionTestCase(JinjaTestCase): 172 | 173 | def test_simple(self): 174 | tmpl = env.from_string('''{% if true %}...{% endif %}''') 175 | assert tmpl.render() == '...' 176 | 177 | def test_elif(self): 178 | tmpl = env.from_string('''{% if false %}XXX{% elif true 179 | %}...{% else %}XXX{% endif %}''') 180 | assert tmpl.render() == '...' 181 | 182 | def test_else(self): 183 | tmpl = env.from_string('{% if false %}XXX{% else %}...{% endif %}') 184 | assert tmpl.render() == '...' 185 | 186 | def test_empty(self): 187 | tmpl = env.from_string('[{% if true %}{% else %}{% endif %}]') 188 | assert tmpl.render() == '[]' 189 | 190 | def test_complete(self): 191 | tmpl = env.from_string('{% if a %}A{% elif b %}B{% elif c == d %}' 192 | 'C{% else %}D{% endif %}') 193 | assert tmpl.render(a=0, b=False, c=42, d=42.0) == 'C' 194 | 195 | def test_no_scope(self): 196 | tmpl = env.from_string('{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}') 197 | assert tmpl.render(a=True) == '1' 198 | tmpl = env.from_string('{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}') 199 | assert tmpl.render() == '1' 200 | 201 | 202 | class MacrosTestCase(JinjaTestCase): 203 | env = Environment(trim_blocks=True) 204 | 205 | def test_simple(self): 206 | tmpl = self.env.from_string('''\ 207 | {% macro say_hello(name) %}Hello {{ name }}!{% endmacro %} 208 | {{ say_hello('Peter') }}''') 209 | assert tmpl.render() == 'Hello Peter!' 210 | 211 | def test_scoping(self): 212 | tmpl = self.env.from_string('''\ 213 | {% macro level1(data1) %} 214 | {% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %} 215 | {{ level2('bar') }}{% endmacro %} 216 | {{ level1('foo') }}''') 217 | assert tmpl.render() == 'foo|bar' 218 | 219 | def test_arguments(self): 220 | tmpl = self.env.from_string('''\ 221 | {% macro m(a, b, c='c', d='d') %}{{ a }}|{{ b }}|{{ c }}|{{ d }}{% endmacro %} 222 | {{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}''') 223 | assert tmpl.render() == '||c|d|a||c|d|a|b|c|d|1|2|3|d' 224 | 225 | def test_varargs(self): 226 | tmpl = self.env.from_string('''\ 227 | {% macro test() %}{{ varargs|join('|') }}{% endmacro %}\ 228 | {{ test(1, 2, 3) }}''') 229 | assert tmpl.render() == '1|2|3' 230 | 231 | def test_simple_call(self): 232 | tmpl = self.env.from_string('''\ 233 | {% macro test() %}[[{{ caller() }}]]{% endmacro %}\ 234 | {% call test() %}data{% endcall %}''') 235 | assert tmpl.render() == '[[data]]' 236 | 237 | def test_complex_call(self): 238 | tmpl = self.env.from_string('''\ 239 | {% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\ 240 | {% call(data) test() %}{{ data }}{% endcall %}''') 241 | assert tmpl.render() == '[[data]]' 242 | 243 | def test_caller_undefined(self): 244 | tmpl = self.env.from_string('''\ 245 | {% set caller = 42 %}\ 246 | {% macro test() %}{{ caller is not defined }}{% endmacro %}\ 247 | {{ test() }}''') 248 | assert tmpl.render() == 'True' 249 | 250 | def test_include(self): 251 | self.env = Environment(loader=DictLoader({'include': 252 | '{% macro test(foo) %}[{{ foo }}]{% endmacro %}'})) 253 | tmpl = self.env.from_string('{% from "include" import test %}{{ test("foo") }}') 254 | assert tmpl.render() == '[foo]' 255 | 256 | def test_macro_api(self): 257 | tmpl = self.env.from_string('{% macro foo(a, b) %}{% endmacro %}' 258 | '{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}' 259 | '{% macro baz() %}{{ caller() }}{% endmacro %}') 260 | assert tmpl.module.foo.arguments == ('a', 'b') 261 | assert tmpl.module.foo.defaults == () 262 | assert tmpl.module.foo.name == 'foo' 263 | assert not tmpl.module.foo.caller 264 | assert not tmpl.module.foo.catch_kwargs 265 | assert not tmpl.module.foo.catch_varargs 266 | assert tmpl.module.bar.arguments == () 267 | assert tmpl.module.bar.defaults == () 268 | assert not tmpl.module.bar.caller 269 | assert tmpl.module.bar.catch_kwargs 270 | assert tmpl.module.bar.catch_varargs 271 | assert tmpl.module.baz.caller 272 | 273 | def test_callself(self): 274 | tmpl = self.env.from_string('{% macro foo(x) %}{{ x }}{% if x > 1 %}|' 275 | '{{ foo(x - 1) }}{% endif %}{% endmacro %}' 276 | '{{ foo(5) }}') 277 | assert tmpl.render() == '5|4|3|2|1' 278 | 279 | 280 | def suite(): 281 | suite = unittest.TestSuite() 282 | suite.addTest(unittest.makeSuite(ForLoopTestCase)) 283 | suite.addTest(unittest.makeSuite(IfConditionTestCase)) 284 | suite.addTest(unittest.makeSuite(MacrosTestCase)) 285 | return suite 286 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/debug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.debug 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests the debug system. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import sys 12 | import unittest 13 | 14 | from jinja2.testsuite import JinjaTestCase, filesystem_loader 15 | 16 | from jinja2 import Environment, TemplateSyntaxError 17 | 18 | env = Environment(loader=filesystem_loader) 19 | 20 | 21 | class DebugTestCase(JinjaTestCase): 22 | 23 | if sys.version_info[:2] != (2, 4): 24 | def test_runtime_error(self): 25 | def test(): 26 | tmpl.render(fail=lambda: 1 / 0) 27 | tmpl = env.get_template('broken.html') 28 | self.assert_traceback_matches(test, r''' 29 | File ".*?broken.html", line 2, in (top-level template code|) 30 | \{\{ fail\(\) \}\} 31 | File ".*?debug.pyc?", line \d+, in 32 | tmpl\.render\(fail=lambda: 1 / 0\) 33 | ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero 34 | ''') 35 | 36 | def test_syntax_error(self): 37 | # XXX: the .*? is necessary for python3 which does not hide 38 | # some of the stack frames we don't want to show. Not sure 39 | # what's up with that, but that is not that critical. Should 40 | # be fixed though. 41 | self.assert_traceback_matches(lambda: env.get_template('syntaxerror.html'), r'''(?sm) 42 | File ".*?syntaxerror.html", line 4, in (template|) 43 | \{% endif %\}.*? 44 | (jinja2\.exceptions\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'. 45 | ''') 46 | 47 | def test_regular_syntax_error(self): 48 | def test(): 49 | raise TemplateSyntaxError('wtf', 42) 50 | self.assert_traceback_matches(test, r''' 51 | File ".*debug.pyc?", line \d+, in test 52 | raise TemplateSyntaxError\('wtf', 42\) 53 | (jinja2\.exceptions\.)?TemplateSyntaxError: wtf 54 | line 42''') 55 | 56 | 57 | def suite(): 58 | suite = unittest.TestSuite() 59 | suite.addTest(unittest.makeSuite(DebugTestCase)) 60 | return suite 61 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/doctests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.doctests 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | The doctests. Collects all tests we want to test from 7 | the Jinja modules. 8 | 9 | :copyright: (c) 2010 by the Jinja Team. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | import unittest 13 | import doctest 14 | 15 | 16 | def suite(): 17 | from jinja2 import utils, sandbox, runtime, meta, loaders, \ 18 | ext, environment, bccache, nodes 19 | suite = unittest.TestSuite() 20 | suite.addTest(doctest.DocTestSuite(utils)) 21 | suite.addTest(doctest.DocTestSuite(sandbox)) 22 | suite.addTest(doctest.DocTestSuite(runtime)) 23 | suite.addTest(doctest.DocTestSuite(meta)) 24 | suite.addTest(doctest.DocTestSuite(loaders)) 25 | suite.addTest(doctest.DocTestSuite(ext)) 26 | suite.addTest(doctest.DocTestSuite(environment)) 27 | suite.addTest(doctest.DocTestSuite(bccache)) 28 | suite.addTest(doctest.DocTestSuite(nodes)) 29 | return suite 30 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/imports.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.imports 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests the import features (with includes). 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | 13 | from jinja2.testsuite import JinjaTestCase 14 | 15 | from jinja2 import Environment, DictLoader 16 | from jinja2.exceptions import TemplateNotFound, TemplatesNotFound 17 | 18 | 19 | test_env = Environment(loader=DictLoader(dict( 20 | module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}', 21 | header='[{{ foo }}|{{ 23 }}]', 22 | o_printer='({{ o }})' 23 | ))) 24 | test_env.globals['bar'] = 23 25 | 26 | 27 | class ImportsTestCase(JinjaTestCase): 28 | 29 | def test_context_imports(self): 30 | t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') 31 | assert t.render(foo=42) == '[|23]' 32 | t = test_env.from_string('{% import "module" as m without context %}{{ m.test() }}') 33 | assert t.render(foo=42) == '[|23]' 34 | t = test_env.from_string('{% import "module" as m with context %}{{ m.test() }}') 35 | assert t.render(foo=42) == '[42|23]' 36 | t = test_env.from_string('{% from "module" import test %}{{ test() }}') 37 | assert t.render(foo=42) == '[|23]' 38 | t = test_env.from_string('{% from "module" import test without context %}{{ test() }}') 39 | assert t.render(foo=42) == '[|23]' 40 | t = test_env.from_string('{% from "module" import test with context %}{{ test() }}') 41 | assert t.render(foo=42) == '[42|23]' 42 | 43 | def test_trailing_comma(self): 44 | test_env.from_string('{% from "foo" import bar, baz with context %}') 45 | test_env.from_string('{% from "foo" import bar, baz, with context %}') 46 | test_env.from_string('{% from "foo" import bar, with context %}') 47 | test_env.from_string('{% from "foo" import bar, with, context %}') 48 | test_env.from_string('{% from "foo" import bar, with with context %}') 49 | 50 | def test_exports(self): 51 | m = test_env.from_string(''' 52 | {% macro toplevel() %}...{% endmacro %} 53 | {% macro __private() %}...{% endmacro %} 54 | {% set variable = 42 %} 55 | {% for item in [1] %} 56 | {% macro notthere() %}{% endmacro %} 57 | {% endfor %} 58 | ''').module 59 | assert m.toplevel() == '...' 60 | assert not hasattr(m, '__missing') 61 | assert m.variable == 42 62 | assert not hasattr(m, 'notthere') 63 | 64 | 65 | class IncludesTestCase(JinjaTestCase): 66 | 67 | def test_context_include(self): 68 | t = test_env.from_string('{% include "header" %}') 69 | assert t.render(foo=42) == '[42|23]' 70 | t = test_env.from_string('{% include "header" with context %}') 71 | assert t.render(foo=42) == '[42|23]' 72 | t = test_env.from_string('{% include "header" without context %}') 73 | assert t.render(foo=42) == '[|23]' 74 | 75 | def test_choice_includes(self): 76 | t = test_env.from_string('{% include ["missing", "header"] %}') 77 | assert t.render(foo=42) == '[42|23]' 78 | 79 | t = test_env.from_string('{% include ["missing", "missing2"] ignore missing %}') 80 | assert t.render(foo=42) == '' 81 | 82 | t = test_env.from_string('{% include ["missing", "missing2"] %}') 83 | self.assert_raises(TemplateNotFound, t.render) 84 | try: 85 | t.render() 86 | except TemplatesNotFound, e: 87 | assert e.templates == ['missing', 'missing2'] 88 | assert e.name == 'missing2' 89 | else: 90 | assert False, 'thou shalt raise' 91 | 92 | def test_includes(t, **ctx): 93 | ctx['foo'] = 42 94 | assert t.render(ctx) == '[42|23]' 95 | 96 | t = test_env.from_string('{% include ["missing", "header"] %}') 97 | test_includes(t) 98 | t = test_env.from_string('{% include x %}') 99 | test_includes(t, x=['missing', 'header']) 100 | t = test_env.from_string('{% include [x, "header"] %}') 101 | test_includes(t, x='missing') 102 | t = test_env.from_string('{% include x %}') 103 | test_includes(t, x='header') 104 | t = test_env.from_string('{% include x %}') 105 | test_includes(t, x='header') 106 | t = test_env.from_string('{% include [x] %}') 107 | test_includes(t, x='header') 108 | 109 | def test_include_ignoring_missing(self): 110 | t = test_env.from_string('{% include "missing" %}') 111 | self.assert_raises(TemplateNotFound, t.render) 112 | for extra in '', 'with context', 'without context': 113 | t = test_env.from_string('{% include "missing" ignore missing ' + 114 | extra + ' %}') 115 | assert t.render() == '' 116 | 117 | def test_context_include_with_overrides(self): 118 | env = Environment(loader=DictLoader(dict( 119 | main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", 120 | item="{{ item }}" 121 | ))) 122 | assert env.get_template("main").render() == "123" 123 | 124 | def test_unoptimized_scopes(self): 125 | t = test_env.from_string(""" 126 | {% macro outer(o) %} 127 | {% macro inner() %} 128 | {% include "o_printer" %} 129 | {% endmacro %} 130 | {{ inner() }} 131 | {% endmacro %} 132 | {{ outer("FOO") }} 133 | """) 134 | assert t.render().strip() == '(FOO)' 135 | 136 | 137 | def suite(): 138 | suite = unittest.TestSuite() 139 | suite.addTest(unittest.makeSuite(ImportsTestCase)) 140 | suite.addTest(unittest.makeSuite(IncludesTestCase)) 141 | return suite 142 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/inheritance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.inheritance 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests the template inheritance feature. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | 13 | from jinja2.testsuite import JinjaTestCase 14 | 15 | from jinja2 import Environment, DictLoader 16 | 17 | 18 | LAYOUTTEMPLATE = '''\ 19 | |{% block block1 %}block 1 from layout{% endblock %} 20 | |{% block block2 %}block 2 from layout{% endblock %} 21 | |{% block block3 %} 22 | {% block block4 %}nested block 4 from layout{% endblock %} 23 | {% endblock %}|''' 24 | 25 | LEVEL1TEMPLATE = '''\ 26 | {% extends "layout" %} 27 | {% block block1 %}block 1 from level1{% endblock %}''' 28 | 29 | LEVEL2TEMPLATE = '''\ 30 | {% extends "level1" %} 31 | {% block block2 %}{% block block5 %}nested block 5 from level2{% 32 | endblock %}{% endblock %}''' 33 | 34 | LEVEL3TEMPLATE = '''\ 35 | {% extends "level2" %} 36 | {% block block5 %}block 5 from level3{% endblock %} 37 | {% block block4 %}block 4 from level3{% endblock %} 38 | ''' 39 | 40 | LEVEL4TEMPLATE = '''\ 41 | {% extends "level3" %} 42 | {% block block3 %}block 3 from level4{% endblock %} 43 | ''' 44 | 45 | WORKINGTEMPLATE = '''\ 46 | {% extends "layout" %} 47 | {% block block1 %} 48 | {% if false %} 49 | {% block block2 %} 50 | this should workd 51 | {% endblock %} 52 | {% endif %} 53 | {% endblock %} 54 | ''' 55 | 56 | env = Environment(loader=DictLoader({ 57 | 'layout': LAYOUTTEMPLATE, 58 | 'level1': LEVEL1TEMPLATE, 59 | 'level2': LEVEL2TEMPLATE, 60 | 'level3': LEVEL3TEMPLATE, 61 | 'level4': LEVEL4TEMPLATE, 62 | 'working': WORKINGTEMPLATE 63 | }), trim_blocks=True) 64 | 65 | 66 | class InheritanceTestCase(JinjaTestCase): 67 | 68 | def test_layout(self): 69 | tmpl = env.get_template('layout') 70 | assert tmpl.render() == ('|block 1 from layout|block 2 from ' 71 | 'layout|nested block 4 from layout|') 72 | 73 | def test_level1(self): 74 | tmpl = env.get_template('level1') 75 | assert tmpl.render() == ('|block 1 from level1|block 2 from ' 76 | 'layout|nested block 4 from layout|') 77 | 78 | def test_level2(self): 79 | tmpl = env.get_template('level2') 80 | assert tmpl.render() == ('|block 1 from level1|nested block 5 from ' 81 | 'level2|nested block 4 from layout|') 82 | 83 | def test_level3(self): 84 | tmpl = env.get_template('level3') 85 | assert tmpl.render() == ('|block 1 from level1|block 5 from level3|' 86 | 'block 4 from level3|') 87 | 88 | def test_level4(sel): 89 | tmpl = env.get_template('level4') 90 | assert tmpl.render() == ('|block 1 from level1|block 5 from ' 91 | 'level3|block 3 from level4|') 92 | 93 | def test_super(self): 94 | env = Environment(loader=DictLoader({ 95 | 'a': '{% block intro %}INTRO{% endblock %}|' 96 | 'BEFORE|{% block data %}INNER{% endblock %}|AFTER', 97 | 'b': '{% extends "a" %}{% block data %}({{ ' 98 | 'super() }}){% endblock %}', 99 | 'c': '{% extends "b" %}{% block intro %}--{{ ' 100 | 'super() }}--{% endblock %}\n{% block data ' 101 | '%}[{{ super() }}]{% endblock %}' 102 | })) 103 | tmpl = env.get_template('c') 104 | assert tmpl.render() == '--INTRO--|BEFORE|[(INNER)]|AFTER' 105 | 106 | def test_working(self): 107 | tmpl = env.get_template('working') 108 | 109 | def test_reuse_blocks(self): 110 | tmpl = env.from_string('{{ self.foo() }}|{% block foo %}42' 111 | '{% endblock %}|{{ self.foo() }}') 112 | assert tmpl.render() == '42|42|42' 113 | 114 | def test_preserve_blocks(self): 115 | env = Environment(loader=DictLoader({ 116 | 'a': '{% if false %}{% block x %}A{% endblock %}{% endif %}{{ self.x() }}', 117 | 'b': '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}' 118 | })) 119 | tmpl = env.get_template('b') 120 | assert tmpl.render() == 'BA' 121 | 122 | def test_dynamic_inheritance(self): 123 | env = Environment(loader=DictLoader({ 124 | 'master1': 'MASTER1{% block x %}{% endblock %}', 125 | 'master2': 'MASTER2{% block x %}{% endblock %}', 126 | 'child': '{% extends master %}{% block x %}CHILD{% endblock %}' 127 | })) 128 | tmpl = env.get_template('child') 129 | for m in range(1, 3): 130 | assert tmpl.render(master='master%d' % m) == 'MASTER%dCHILD' % m 131 | 132 | def test_multi_inheritance(self): 133 | env = Environment(loader=DictLoader({ 134 | 'master1': 'MASTER1{% block x %}{% endblock %}', 135 | 'master2': 'MASTER2{% block x %}{% endblock %}', 136 | 'child': '''{% if master %}{% extends master %}{% else %}{% extends 137 | 'master1' %}{% endif %}{% block x %}CHILD{% endblock %}''' 138 | })) 139 | tmpl = env.get_template('child') 140 | assert tmpl.render(master='master2') == 'MASTER2CHILD' 141 | assert tmpl.render(master='master1') == 'MASTER1CHILD' 142 | assert tmpl.render() == 'MASTER1CHILD' 143 | 144 | def test_scoped_block(self): 145 | env = Environment(loader=DictLoader({ 146 | 'master.html': '{% for item in seq %}[{% block item scoped %}' 147 | '{% endblock %}]{% endfor %}' 148 | })) 149 | t = env.from_string('{% extends "master.html" %}{% block item %}' 150 | '{{ item }}{% endblock %}') 151 | assert t.render(seq=range(5)) == '[0][1][2][3][4]' 152 | 153 | def test_super_in_scoped_block(self): 154 | env = Environment(loader=DictLoader({ 155 | 'master.html': '{% for item in seq %}[{% block item scoped %}' 156 | '{{ item }}{% endblock %}]{% endfor %}' 157 | })) 158 | t = env.from_string('{% extends "master.html" %}{% block item %}' 159 | '{{ super() }}|{{ item * 2 }}{% endblock %}') 160 | assert t.render(seq=range(5)) == '[0|0][1|2][2|4][3|6][4|8]' 161 | 162 | def test_scoped_block_after_inheritance(self): 163 | env = Environment(loader=DictLoader({ 164 | 'layout.html': ''' 165 | {% block useless %}{% endblock %} 166 | ''', 167 | 'index.html': ''' 168 | {%- extends 'layout.html' %} 169 | {% from 'helpers.html' import foo with context %} 170 | {% block useless %} 171 | {% for x in [1, 2, 3] %} 172 | {% block testing scoped %} 173 | {{ foo(x) }} 174 | {% endblock %} 175 | {% endfor %} 176 | {% endblock %} 177 | ''', 178 | 'helpers.html': ''' 179 | {% macro foo(x) %}{{ the_foo + x }}{% endmacro %} 180 | ''' 181 | })) 182 | rv = env.get_template('index.html').render(the_foo=42).split() 183 | assert rv == ['43', '44', '45'] 184 | 185 | 186 | class BugFixTestCase(JinjaTestCase): 187 | 188 | def test_fixed_macro_scoping_bug(self): 189 | assert Environment(loader=DictLoader({ 190 | 'test.html': '''\ 191 | {% extends 'details.html' %} 192 | 193 | {% macro my_macro() %} 194 | my_macro 195 | {% endmacro %} 196 | 197 | {% block inner_box %} 198 | {{ my_macro() }} 199 | {% endblock %} 200 | ''', 201 | 'details.html': '''\ 202 | {% extends 'standard.html' %} 203 | 204 | {% macro my_macro() %} 205 | my_macro 206 | {% endmacro %} 207 | 208 | {% block content %} 209 | {% block outer_box %} 210 | outer_box 211 | {% block inner_box %} 212 | inner_box 213 | {% endblock %} 214 | {% endblock %} 215 | {% endblock %} 216 | ''', 217 | 'standard.html': ''' 218 | {% block content %} {% endblock %} 219 | ''' 220 | })).get_template("test.html").render().split() == [u'outer_box', u'my_macro'] 221 | 222 | 223 | def suite(): 224 | suite = unittest.TestSuite() 225 | suite.addTest(unittest.makeSuite(InheritanceTestCase)) 226 | suite.addTest(unittest.makeSuite(BugFixTestCase)) 227 | return suite 228 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.loader 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Test the loaders. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import os 12 | import sys 13 | import tempfile 14 | import shutil 15 | import unittest 16 | 17 | from jinja2.testsuite import JinjaTestCase, dict_loader, \ 18 | package_loader, filesystem_loader, function_loader, \ 19 | choice_loader, prefix_loader 20 | 21 | from jinja2 import Environment, loaders 22 | from jinja2.loaders import split_template_path 23 | from jinja2.exceptions import TemplateNotFound 24 | 25 | 26 | class LoaderTestCase(JinjaTestCase): 27 | 28 | def test_dict_loader(self): 29 | env = Environment(loader=dict_loader) 30 | tmpl = env.get_template('justdict.html') 31 | assert tmpl.render().strip() == 'FOO' 32 | self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') 33 | 34 | def test_package_loader(self): 35 | env = Environment(loader=package_loader) 36 | tmpl = env.get_template('test.html') 37 | assert tmpl.render().strip() == 'BAR' 38 | self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') 39 | 40 | def test_filesystem_loader(self): 41 | env = Environment(loader=filesystem_loader) 42 | tmpl = env.get_template('test.html') 43 | assert tmpl.render().strip() == 'BAR' 44 | tmpl = env.get_template('foo/test.html') 45 | assert tmpl.render().strip() == 'FOO' 46 | self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') 47 | 48 | def test_choice_loader(self): 49 | env = Environment(loader=choice_loader) 50 | tmpl = env.get_template('justdict.html') 51 | assert tmpl.render().strip() == 'FOO' 52 | tmpl = env.get_template('test.html') 53 | assert tmpl.render().strip() == 'BAR' 54 | self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') 55 | 56 | def test_function_loader(self): 57 | env = Environment(loader=function_loader) 58 | tmpl = env.get_template('justfunction.html') 59 | assert tmpl.render().strip() == 'FOO' 60 | self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') 61 | 62 | def test_prefix_loader(self): 63 | env = Environment(loader=prefix_loader) 64 | tmpl = env.get_template('a/test.html') 65 | assert tmpl.render().strip() == 'BAR' 66 | tmpl = env.get_template('b/justdict.html') 67 | assert tmpl.render().strip() == 'FOO' 68 | self.assert_raises(TemplateNotFound, env.get_template, 'missing') 69 | 70 | def test_caching(self): 71 | changed = False 72 | class TestLoader(loaders.BaseLoader): 73 | def get_source(self, environment, template): 74 | return u'foo', None, lambda: not changed 75 | env = Environment(loader=TestLoader(), cache_size=-1) 76 | tmpl = env.get_template('template') 77 | assert tmpl is env.get_template('template') 78 | changed = True 79 | assert tmpl is not env.get_template('template') 80 | changed = False 81 | 82 | env = Environment(loader=TestLoader(), cache_size=0) 83 | assert env.get_template('template') \ 84 | is not env.get_template('template') 85 | 86 | env = Environment(loader=TestLoader(), cache_size=2) 87 | t1 = env.get_template('one') 88 | t2 = env.get_template('two') 89 | assert t2 is env.get_template('two') 90 | assert t1 is env.get_template('one') 91 | t3 = env.get_template('three') 92 | assert 'one' in env.cache 93 | assert 'two' not in env.cache 94 | assert 'three' in env.cache 95 | 96 | def test_split_template_path(self): 97 | assert split_template_path('foo/bar') == ['foo', 'bar'] 98 | assert split_template_path('./foo/bar') == ['foo', 'bar'] 99 | self.assert_raises(TemplateNotFound, split_template_path, '../foo') 100 | 101 | 102 | class ModuleLoaderTestCase(JinjaTestCase): 103 | archive = None 104 | 105 | def compile_down(self, zip='deflated', py_compile=False): 106 | super(ModuleLoaderTestCase, self).setup() 107 | log = [] 108 | self.reg_env = Environment(loader=prefix_loader) 109 | if zip is not None: 110 | self.archive = tempfile.mkstemp(suffix='.zip')[1] 111 | else: 112 | self.archive = tempfile.mkdtemp() 113 | self.reg_env.compile_templates(self.archive, zip=zip, 114 | log_function=log.append, 115 | py_compile=py_compile) 116 | self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive)) 117 | return ''.join(log) 118 | 119 | def teardown(self): 120 | super(ModuleLoaderTestCase, self).teardown() 121 | if hasattr(self, 'mod_env'): 122 | if os.path.isfile(self.archive): 123 | os.remove(self.archive) 124 | else: 125 | shutil.rmtree(self.archive) 126 | self.archive = None 127 | 128 | def test_log(self): 129 | log = self.compile_down() 130 | assert 'Compiled "a/foo/test.html" as ' \ 131 | 'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log 132 | assert 'Finished compiling templates' in log 133 | assert 'Could not compile "a/syntaxerror.html": ' \ 134 | 'Encountered unknown tag \'endif\'' in log 135 | 136 | def _test_common(self): 137 | tmpl1 = self.reg_env.get_template('a/test.html') 138 | tmpl2 = self.mod_env.get_template('a/test.html') 139 | assert tmpl1.render() == tmpl2.render() 140 | 141 | tmpl1 = self.reg_env.get_template('b/justdict.html') 142 | tmpl2 = self.mod_env.get_template('b/justdict.html') 143 | assert tmpl1.render() == tmpl2.render() 144 | 145 | def test_deflated_zip_compile(self): 146 | self.compile_down(zip='deflated') 147 | self._test_common() 148 | 149 | def test_stored_zip_compile(self): 150 | self.compile_down(zip='stored') 151 | self._test_common() 152 | 153 | def test_filesystem_compile(self): 154 | self.compile_down(zip=None) 155 | self._test_common() 156 | 157 | def test_weak_references(self): 158 | self.compile_down() 159 | tmpl = self.mod_env.get_template('a/test.html') 160 | key = loaders.ModuleLoader.get_template_key('a/test.html') 161 | name = self.mod_env.loader.module.__name__ 162 | 163 | assert hasattr(self.mod_env.loader.module, key) 164 | assert name in sys.modules 165 | 166 | # unset all, ensure the module is gone from sys.modules 167 | self.mod_env = tmpl = None 168 | 169 | try: 170 | import gc 171 | gc.collect() 172 | except: 173 | pass 174 | 175 | assert name not in sys.modules 176 | 177 | def test_byte_compilation(self): 178 | log = self.compile_down(py_compile=True) 179 | assert 'Byte-compiled "a/test.html"' in log 180 | tmpl1 = self.mod_env.get_template('a/test.html') 181 | mod = self.mod_env.loader.module. \ 182 | tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490 183 | assert mod.__file__.endswith('.pyc') 184 | 185 | 186 | def suite(): 187 | suite = unittest.TestSuite() 188 | suite.addTest(unittest.makeSuite(LoaderTestCase)) 189 | suite.addTest(unittest.makeSuite(ModuleLoaderTestCase)) 190 | return suite 191 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/regression.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.regression 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests corner cases and bugs. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | 13 | from jinja2.testsuite import JinjaTestCase 14 | 15 | from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \ 16 | TemplateNotFound, PrefixLoader 17 | 18 | env = Environment() 19 | 20 | 21 | class CornerTestCase(JinjaTestCase): 22 | 23 | def test_assigned_scoping(self): 24 | t = env.from_string(''' 25 | {%- for item in (1, 2, 3, 4) -%} 26 | [{{ item }}] 27 | {%- endfor %} 28 | {{- item -}} 29 | ''') 30 | assert t.render(item=42) == '[1][2][3][4]42' 31 | 32 | t = env.from_string(''' 33 | {%- for item in (1, 2, 3, 4) -%} 34 | [{{ item }}] 35 | {%- endfor %} 36 | {%- set item = 42 %} 37 | {{- item -}} 38 | ''') 39 | assert t.render() == '[1][2][3][4]42' 40 | 41 | t = env.from_string(''' 42 | {%- set item = 42 %} 43 | {%- for item in (1, 2, 3, 4) -%} 44 | [{{ item }}] 45 | {%- endfor %} 46 | {{- item -}} 47 | ''') 48 | assert t.render() == '[1][2][3][4]42' 49 | 50 | def test_closure_scoping(self): 51 | t = env.from_string(''' 52 | {%- set wrapper = "" %} 53 | {%- for item in (1, 2, 3, 4) %} 54 | {%- macro wrapper() %}[{{ item }}]{% endmacro %} 55 | {{- wrapper() }} 56 | {%- endfor %} 57 | {{- wrapper -}} 58 | ''') 59 | assert t.render() == '[1][2][3][4]' 60 | 61 | t = env.from_string(''' 62 | {%- for item in (1, 2, 3, 4) %} 63 | {%- macro wrapper() %}[{{ item }}]{% endmacro %} 64 | {{- wrapper() }} 65 | {%- endfor %} 66 | {%- set wrapper = "" %} 67 | {{- wrapper -}} 68 | ''') 69 | assert t.render() == '[1][2][3][4]' 70 | 71 | t = env.from_string(''' 72 | {%- for item in (1, 2, 3, 4) %} 73 | {%- macro wrapper() %}[{{ item }}]{% endmacro %} 74 | {{- wrapper() }} 75 | {%- endfor %} 76 | {{- wrapper -}} 77 | ''') 78 | assert t.render(wrapper=23) == '[1][2][3][4]23' 79 | 80 | 81 | class BugTestCase(JinjaTestCase): 82 | 83 | def test_keyword_folding(self): 84 | env = Environment() 85 | env.filters['testing'] = lambda value, some: value + some 86 | assert env.from_string("{{ 'test'|testing(some='stuff') }}") \ 87 | .render() == 'teststuff' 88 | 89 | def test_extends_output_bugs(self): 90 | env = Environment(loader=DictLoader({ 91 | 'parent.html': '(({% block title %}{% endblock %}))' 92 | })) 93 | 94 | t = env.from_string('{% if expr %}{% extends "parent.html" %}{% endif %}' 95 | '[[{% block title %}title{% endblock %}]]' 96 | '{% for item in [1, 2, 3] %}({{ item }}){% endfor %}') 97 | assert t.render(expr=False) == '[[title]](1)(2)(3)' 98 | assert t.render(expr=True) == '((title))' 99 | 100 | def test_urlize_filter_escaping(self): 101 | tmpl = env.from_string('{{ "http://www.example.org/http://www.example.org/<foo' 103 | 104 | def test_loop_call_loop(self): 105 | tmpl = env.from_string(''' 106 | 107 | {% macro test() %} 108 | {{ caller() }} 109 | {% endmacro %} 110 | 111 | {% for num1 in range(5) %} 112 | {% call test() %} 113 | {% for num2 in range(10) %} 114 | {{ loop.index }} 115 | {% endfor %} 116 | {% endcall %} 117 | {% endfor %} 118 | 119 | ''') 120 | 121 | assert tmpl.render().split() == map(unicode, range(1, 11)) * 5 122 | 123 | def test_weird_inline_comment(self): 124 | env = Environment(line_statement_prefix='%') 125 | self.assert_raises(TemplateSyntaxError, env.from_string, 126 | '% for item in seq {# missing #}\n...% endfor') 127 | 128 | def test_old_macro_loop_scoping_bug(self): 129 | tmpl = env.from_string('{% for i in (1, 2) %}{{ i }}{% endfor %}' 130 | '{% macro i() %}3{% endmacro %}{{ i() }}') 131 | assert tmpl.render() == '123' 132 | 133 | def test_partial_conditional_assignments(self): 134 | tmpl = env.from_string('{% if b %}{% set a = 42 %}{% endif %}{{ a }}') 135 | assert tmpl.render(a=23) == '23' 136 | assert tmpl.render(b=True) == '42' 137 | 138 | def test_stacked_locals_scoping_bug(self): 139 | env = Environment(line_statement_prefix='#') 140 | t = env.from_string('''\ 141 | # for j in [1, 2]: 142 | # set x = 1 143 | # for i in [1, 2]: 144 | # print x 145 | # if i % 2 == 0: 146 | # set x = x + 1 147 | # endif 148 | # endfor 149 | # endfor 150 | # if a 151 | # print 'A' 152 | # elif b 153 | # print 'B' 154 | # elif c == d 155 | # print 'C' 156 | # else 157 | # print 'D' 158 | # endif 159 | ''') 160 | assert t.render(a=0, b=False, c=42, d=42.0) == '1111C' 161 | 162 | def test_stacked_locals_scoping_bug_twoframe(self): 163 | t = Template(''' 164 | {% set x = 1 %} 165 | {% for item in foo %} 166 | {% if item == 1 %} 167 | {% set x = 2 %} 168 | {% endif %} 169 | {% endfor %} 170 | {{ x }} 171 | ''') 172 | rv = t.render(foo=[1]).strip() 173 | assert rv == u'1' 174 | 175 | def test_call_with_args(self): 176 | t = Template("""{% macro dump_users(users) -%} 177 |
    178 | {%- for user in users -%} 179 |
  • {{ user.username|e }}

    {{ caller(user) }}
  • 180 | {%- endfor -%} 181 |
182 | {%- endmacro -%} 183 | 184 | {% call(user) dump_users(list_of_user) -%} 185 |
186 |
Realname
187 |
{{ user.realname|e }}
188 |
Description
189 |
{{ user.description }}
190 |
191 | {% endcall %}""") 192 | 193 | assert [x.strip() for x in t.render(list_of_user=[{ 194 | 'username':'apo', 195 | 'realname':'something else', 196 | 'description':'test' 197 | }]).splitlines()] == [ 198 | u'
  • apo

    ', 199 | u'
    Realname
    ', 200 | u'
    something else
    ', 201 | u'
    Description
    ', 202 | u'
    test
    ', 203 | u'
    ', 204 | u'
' 205 | ] 206 | 207 | def test_empty_if_condition_fails(self): 208 | self.assert_raises(TemplateSyntaxError, Template, '{% if %}....{% endif %}') 209 | self.assert_raises(TemplateSyntaxError, Template, '{% if foo %}...{% elif %}...{% endif %}') 210 | self.assert_raises(TemplateSyntaxError, Template, '{% for x in %}..{% endfor %}') 211 | 212 | def test_recursive_loop_bug(self): 213 | tpl1 = Template(""" 214 | {% for p in foo recursive%} 215 | {{p.bar}} 216 | {% for f in p.fields recursive%} 217 | {{f.baz}} 218 | {{p.bar}} 219 | {% if f.rec %} 220 | {{ loop(f.sub) }} 221 | {% endif %} 222 | {% endfor %} 223 | {% endfor %} 224 | """) 225 | 226 | tpl2 = Template(""" 227 | {% for p in foo%} 228 | {{p.bar}} 229 | {% for f in p.fields recursive%} 230 | {{f.baz}} 231 | {{p.bar}} 232 | {% if f.rec %} 233 | {{ loop(f.sub) }} 234 | {% endif %} 235 | {% endfor %} 236 | {% endfor %} 237 | """) 238 | 239 | def test_correct_prefix_loader_name(self): 240 | env = Environment(loader=PrefixLoader({ 241 | 'foo': DictLoader({}) 242 | })) 243 | try: 244 | env.get_template('foo/bar.html') 245 | except TemplateNotFound, e: 246 | assert e.name == 'foo/bar.html' 247 | else: 248 | assert False, 'expected error here' 249 | 250 | 251 | def suite(): 252 | suite = unittest.TestSuite() 253 | suite.addTest(unittest.makeSuite(CornerTestCase)) 254 | suite.addTest(unittest.makeSuite(BugTestCase)) 255 | return suite 256 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/res/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/pyl/jinja2/testsuite/res/__init__.py -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/res/templates/broken.html: -------------------------------------------------------------------------------- 1 | Before 2 | {{ fail() }} 3 | After 4 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/res/templates/foo/test.html: -------------------------------------------------------------------------------- 1 | FOO 2 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/res/templates/syntaxerror.html: -------------------------------------------------------------------------------- 1 | Foo 2 | {% for item in broken %} 3 | ... 4 | {% endif %} 5 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/res/templates/test.html: -------------------------------------------------------------------------------- 1 | BAR 2 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/security.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.security 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Checks the sandbox and other security features. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | 13 | from jinja2.testsuite import JinjaTestCase 14 | 15 | from jinja2 import Environment 16 | from jinja2.sandbox import SandboxedEnvironment, \ 17 | ImmutableSandboxedEnvironment, unsafe 18 | from jinja2 import Markup, escape 19 | from jinja2.exceptions import SecurityError, TemplateSyntaxError, \ 20 | TemplateRuntimeError 21 | 22 | 23 | class PrivateStuff(object): 24 | 25 | def bar(self): 26 | return 23 27 | 28 | @unsafe 29 | def foo(self): 30 | return 42 31 | 32 | def __repr__(self): 33 | return 'PrivateStuff' 34 | 35 | 36 | class PublicStuff(object): 37 | bar = lambda self: 23 38 | _foo = lambda self: 42 39 | 40 | def __repr__(self): 41 | return 'PublicStuff' 42 | 43 | 44 | class SandboxTestCase(JinjaTestCase): 45 | 46 | def test_unsafe(self): 47 | env = SandboxedEnvironment() 48 | self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render, 49 | foo=PrivateStuff()) 50 | self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23') 51 | 52 | self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render, 53 | foo=PublicStuff()) 54 | self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23') 55 | self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '') 56 | self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '') 57 | # security error comes from __class__ already. 58 | self.assert_raises(SecurityError, env.from_string( 59 | "{{ foo.__class__.__subclasses__() }}").render, foo=42) 60 | 61 | def test_immutable_environment(self): 62 | env = ImmutableSandboxedEnvironment() 63 | self.assert_raises(SecurityError, env.from_string( 64 | '{{ [].append(23) }}').render) 65 | self.assert_raises(SecurityError, env.from_string( 66 | '{{ {1:2}.clear() }}').render) 67 | 68 | def test_restricted(self): 69 | env = SandboxedEnvironment() 70 | self.assert_raises(TemplateSyntaxError, env.from_string, 71 | "{% for item.attribute in seq %}...{% endfor %}") 72 | self.assert_raises(TemplateSyntaxError, env.from_string, 73 | "{% for foo, bar.baz in seq %}...{% endfor %}") 74 | 75 | def test_markup_operations(self): 76 | # adding two strings should escape the unsafe one 77 | unsafe = '' 78 | safe = Markup('username') 79 | assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe) 80 | 81 | # string interpolations are safe to use too 82 | assert Markup('%s') % '' == \ 83 | '<bad user>' 84 | assert Markup('%(username)s') % { 85 | 'username': '' 86 | } == '<bad user>' 87 | 88 | # an escaped object is markup too 89 | assert type(Markup('foo') + 'bar') is Markup 90 | 91 | # and it implements __html__ by returning itself 92 | x = Markup("foo") 93 | assert x.__html__() is x 94 | 95 | # it also knows how to treat __html__ objects 96 | class Foo(object): 97 | def __html__(self): 98 | return 'awesome' 99 | def __unicode__(self): 100 | return 'awesome' 101 | assert Markup(Foo()) == 'awesome' 102 | assert Markup('%s') % Foo() == \ 103 | 'awesome' 104 | 105 | # escaping and unescaping 106 | assert escape('"<>&\'') == '"<>&'' 107 | assert Markup("Foo & Bar").striptags() == "Foo & Bar" 108 | assert Markup("<test>").unescape() == "" 109 | 110 | def test_template_data(self): 111 | env = Environment(autoescape=True) 112 | t = env.from_string('{% macro say_hello(name) %}' 113 | '

Hello {{ name }}!

{% endmacro %}' 114 | '{{ say_hello("foo") }}') 115 | escaped_out = '

Hello <blink>foo</blink>!

' 116 | assert t.render() == escaped_out 117 | assert unicode(t.module) == escaped_out 118 | assert escape(t.module) == escaped_out 119 | assert t.module.say_hello('foo') == escaped_out 120 | assert escape(t.module.say_hello('foo')) == escaped_out 121 | 122 | def test_attr_filter(self): 123 | env = SandboxedEnvironment() 124 | tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}') 125 | self.assert_raises(SecurityError, tmpl.render, cls=int) 126 | 127 | def test_binary_operator_intercepting(self): 128 | def disable_op(left, right): 129 | raise TemplateRuntimeError('that operator so does not work') 130 | for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'): 131 | env = SandboxedEnvironment() 132 | env.binop_table['+'] = disable_op 133 | t = env.from_string('{{ %s }}' % expr) 134 | assert t.render(ctx) == rv 135 | env.intercepted_binops = frozenset(['+']) 136 | t = env.from_string('{{ %s }}' % expr) 137 | try: 138 | t.render(ctx) 139 | except TemplateRuntimeError, e: 140 | pass 141 | else: 142 | self.fail('expected runtime error') 143 | 144 | def test_unary_operator_intercepting(self): 145 | def disable_op(arg): 146 | raise TemplateRuntimeError('that operator so does not work') 147 | for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'): 148 | env = SandboxedEnvironment() 149 | env.unop_table['-'] = disable_op 150 | t = env.from_string('{{ %s }}' % expr) 151 | assert t.render(ctx) == rv 152 | env.intercepted_unops = frozenset(['-']) 153 | t = env.from_string('{{ %s }}' % expr) 154 | try: 155 | t.render(ctx) 156 | except TemplateRuntimeError, e: 157 | pass 158 | else: 159 | self.fail('expected runtime error') 160 | 161 | 162 | def suite(): 163 | suite = unittest.TestSuite() 164 | suite.addTest(unittest.makeSuite(SandboxTestCase)) 165 | return suite 166 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.tests 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Who tests the tests? 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | from jinja2.testsuite import JinjaTestCase 13 | 14 | from jinja2 import Markup, Environment 15 | 16 | env = Environment() 17 | 18 | 19 | class TestsTestCase(JinjaTestCase): 20 | 21 | def test_defined(self): 22 | tmpl = env.from_string('{{ missing is defined }}|{{ true is defined }}') 23 | assert tmpl.render() == 'False|True' 24 | 25 | def test_even(self): 26 | tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''') 27 | assert tmpl.render() == 'False|True' 28 | 29 | def test_odd(self): 30 | tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''') 31 | assert tmpl.render() == 'True|False' 32 | 33 | def test_lower(self): 34 | tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''') 35 | assert tmpl.render() == 'True|False' 36 | 37 | def test_typechecks(self): 38 | tmpl = env.from_string(''' 39 | {{ 42 is undefined }} 40 | {{ 42 is defined }} 41 | {{ 42 is none }} 42 | {{ none is none }} 43 | {{ 42 is number }} 44 | {{ 42 is string }} 45 | {{ "foo" is string }} 46 | {{ "foo" is sequence }} 47 | {{ [1] is sequence }} 48 | {{ range is callable }} 49 | {{ 42 is callable }} 50 | {{ range(5) is iterable }} 51 | ''') 52 | assert tmpl.render().split() == [ 53 | 'False', 'True', 'False', 'True', 'True', 'False', 54 | 'True', 'True', 'True', 'True', 'False', 'True' 55 | ] 56 | 57 | def test_sequence(self): 58 | tmpl = env.from_string( 59 | '{{ [1, 2, 3] is sequence }}|' 60 | '{{ "foo" is sequence }}|' 61 | '{{ 42 is sequence }}' 62 | ) 63 | assert tmpl.render() == 'True|True|False' 64 | 65 | def test_upper(self): 66 | tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') 67 | assert tmpl.render() == 'True|False' 68 | 69 | def test_sameas(self): 70 | tmpl = env.from_string('{{ foo is sameas false }}|' 71 | '{{ 0 is sameas false }}') 72 | assert tmpl.render(foo=False) == 'True|False' 73 | 74 | def test_no_paren_for_arg1(self): 75 | tmpl = env.from_string('{{ foo is sameas none }}') 76 | assert tmpl.render(foo=None) == 'True' 77 | 78 | def test_escaped(self): 79 | env = Environment(autoescape=True) 80 | tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}') 81 | assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True' 82 | 83 | 84 | def suite(): 85 | suite = unittest.TestSuite() 86 | suite.addTest(unittest.makeSuite(TestsTestCase)) 87 | return suite 88 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.utils 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests utilities jinja uses. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import gc 12 | import unittest 13 | 14 | import pickle 15 | 16 | from jinja2.testsuite import JinjaTestCase 17 | 18 | from jinja2.utils import LRUCache, escape, object_type_repr 19 | 20 | 21 | class LRUCacheTestCase(JinjaTestCase): 22 | 23 | def test_simple(self): 24 | d = LRUCache(3) 25 | d["a"] = 1 26 | d["b"] = 2 27 | d["c"] = 3 28 | d["a"] 29 | d["d"] = 4 30 | assert len(d) == 3 31 | assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d 32 | 33 | def test_pickleable(self): 34 | cache = LRUCache(2) 35 | cache["foo"] = 42 36 | cache["bar"] = 23 37 | cache["foo"] 38 | 39 | for protocol in range(3): 40 | copy = pickle.loads(pickle.dumps(cache, protocol)) 41 | assert copy.capacity == cache.capacity 42 | assert copy._mapping == cache._mapping 43 | assert copy._queue == cache._queue 44 | 45 | 46 | class HelpersTestCase(JinjaTestCase): 47 | 48 | def test_object_type_repr(self): 49 | class X(object): 50 | pass 51 | self.assert_equal(object_type_repr(42), 'int object') 52 | self.assert_equal(object_type_repr([]), 'list object') 53 | self.assert_equal(object_type_repr(X()), 54 | 'jinja2.testsuite.utils.X object') 55 | self.assert_equal(object_type_repr(None), 'None') 56 | self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis') 57 | 58 | 59 | class MarkupLeakTestCase(JinjaTestCase): 60 | 61 | def test_markup_leaks(self): 62 | counts = set() 63 | for count in xrange(20): 64 | for item in xrange(1000): 65 | escape("foo") 66 | escape("") 67 | escape(u"foo") 68 | escape(u"") 69 | counts.add(len(gc.get_objects())) 70 | assert len(counts) == 1, 'ouch, c extension seems to leak objects' 71 | 72 | 73 | def suite(): 74 | suite = unittest.TestSuite() 75 | suite.addTest(unittest.makeSuite(LRUCacheTestCase)) 76 | suite.addTest(unittest.makeSuite(HelpersTestCase)) 77 | 78 | # this test only tests the c extension 79 | if not hasattr(escape, 'func_code'): 80 | suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) 81 | 82 | return suite 83 | -------------------------------------------------------------------------------- /pyl/jinja2/visitor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.visitor 4 | ~~~~~~~~~~~~~~ 5 | 6 | This module implements a visitor for the nodes. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD. 10 | """ 11 | from jinja2.nodes import Node 12 | 13 | 14 | class NodeVisitor(object): 15 | """Walks the abstract syntax tree and call visitor functions for every 16 | node found. The visitor functions may return values which will be 17 | forwarded by the `visit` method. 18 | 19 | Per default the visitor functions for the nodes are ``'visit_'`` + 20 | class name of the node. So a `TryFinally` node visit function would 21 | be `visit_TryFinally`. This behavior can be changed by overriding 22 | the `get_visitor` function. If no visitor function exists for a node 23 | (return value `None`) the `generic_visit` visitor is used instead. 24 | """ 25 | 26 | def get_visitor(self, node): 27 | """Return the visitor function for this node or `None` if no visitor 28 | exists for this node. In that case the generic visit function is 29 | used instead. 30 | """ 31 | method = 'visit_' + node.__class__.__name__ 32 | return getattr(self, method, None) 33 | 34 | def visit(self, node, *args, **kwargs): 35 | """Visit a node.""" 36 | f = self.get_visitor(node) 37 | if f is not None: 38 | return f(node, *args, **kwargs) 39 | return self.generic_visit(node, *args, **kwargs) 40 | 41 | def generic_visit(self, node, *args, **kwargs): 42 | """Called if no explicit visitor function exists for a node.""" 43 | for node in node.iter_child_nodes(): 44 | self.visit(node, *args, **kwargs) 45 | 46 | 47 | class NodeTransformer(NodeVisitor): 48 | """Walks the abstract syntax tree and allows modifications of nodes. 49 | 50 | The `NodeTransformer` will walk the AST and use the return value of the 51 | visitor functions to replace or remove the old node. If the return 52 | value of the visitor function is `None` the node will be removed 53 | from the previous location otherwise it's replaced with the return 54 | value. The return value may be the original node in which case no 55 | replacement takes place. 56 | """ 57 | 58 | def generic_visit(self, node, *args, **kwargs): 59 | for field, old_value in node.iter_fields(): 60 | if isinstance(old_value, list): 61 | new_values = [] 62 | for value in old_value: 63 | if isinstance(value, Node): 64 | value = self.visit(value, *args, **kwargs) 65 | if value is None: 66 | continue 67 | elif not isinstance(value, Node): 68 | new_values.extend(value) 69 | continue 70 | new_values.append(value) 71 | old_value[:] = new_values 72 | elif isinstance(old_value, Node): 73 | new_node = self.visit(old_value, *args, **kwargs) 74 | if new_node is None: 75 | delattr(node, field) 76 | else: 77 | setattr(node, field, new_node) 78 | return node 79 | 80 | def visit_list(self, node, *args, **kwargs): 81 | """As transformers may return lists in some places this method 82 | can be used to enforce a list as return value. 83 | """ 84 | rv = self.visit(node, *args, **kwargs) 85 | if not isinstance(rv, list): 86 | rv = [rv] 87 | return rv 88 | -------------------------------------------------------------------------------- /pyl/versioned_memcache/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/pyl/versioned_memcache/__init__.py -------------------------------------------------------------------------------- /pyl/versioned_memcache/memcache.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2009 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | """Memcache wrapper that transparently adds app version 19 | 20 | This wraps google.appengine.api.memcache, adding a namespace 21 | parameter to all calls that is based on the app version 22 | environment variable (os.environ["CURRENT_VERSION_ID"]). This 23 | means new versions of the app will not access cached entries 24 | from an old version, so no cache flushing is required when 25 | changing app versions. 26 | """ 27 | 28 | # Pylint doesn't like the names memcache uses -- disable those warnings 29 | # Also disabling no docstring warning 30 | # pylint: disable-msg=C0111 31 | # pylint: disable-msg=W0622 32 | 33 | import os 34 | 35 | from google.appengine.api import memcache 36 | 37 | # 38 | # Wrap memcache API calls with calls that set the namespace to 39 | # the App Engine version string, obtained from the environment 40 | # variables. 41 | # 42 | # These calls do not support a namespace parameter, so as not 43 | # to confuse the user. 44 | # 45 | # The Client() object is not supported by this wrapper either. 46 | # 47 | 48 | def get(key): 49 | return memcache.get(key, 50 | namespace=os.environ["CURRENT_VERSION_ID"]) 51 | 52 | def get_multi(keys, key_prefix=''): 53 | return memcache.get_multi(keys, key_prefix, 54 | namespace=os.environ["CURRENT_VERSION_ID"]) 55 | 56 | def delete(key, seconds=0): 57 | return memcache.delete(key, seconds, 58 | namespace=os.environ["CURRENT_VERSION_ID"]) 59 | 60 | def delete_multi(keys, seconds=0, key_prefix=''): 61 | return memcache.delete_multi(keys, seconds, key_prefix, 62 | namespace=os.environ["CURRENT_VERSION_ID"]) 63 | 64 | def set(key, value, time=0, min_compress_len=0): 65 | return memcache.set(key, value, time, min_compress_len, 66 | namespace=os.environ["CURRENT_VERSION_ID"]) 67 | 68 | def add(key, value, time=0, min_compress_len=0): 69 | return memcache.add(key, value, time, min_compress_len, 70 | namespace=os.environ["CURRENT_VERSION_ID"]) 71 | 72 | def replace(key, value, time=0, min_compress_len=0): 73 | return memcache.replace(key, value, time, min_compress_len, 74 | namespace=os.environ["CURRENT_VERSION_ID"]) 75 | 76 | def set_multi(mapping, time=0, key_prefix='', min_compress_len=0): 77 | return memcache.set_multi(mapping, time, key_prefix, min_compress_len, 78 | namespace=os.environ["CURRENT_VERSION_ID"]) 79 | 80 | def add_multi(mapping, time=0, key_prefix='', min_compress_len=0): 81 | return memcache.add_multi(mapping, time, key_prefix, min_compress_len, 82 | namespace=os.environ["CURRENT_VERSION_ID"]) 83 | 84 | def replace_multi(mapping, time=0, key_prefix='', min_compress_len=0): 85 | return memcache.replace_multi(mapping, time, key_prefix, min_compress_len, 86 | namespace=os.environ["CURRENT_VERSION_ID"]) 87 | 88 | def incr(key, delta=1, initial_value=None): 89 | return memcache.incr(key, delta, 90 | namespace=os.environ["CURRENT_VERSION_ID"], 91 | initial_value=initial_value) 92 | 93 | def decr(key, delta=1, initial_value=None): 94 | return memcache.decr(key, delta, 95 | namespace=os.environ["CURRENT_VERSION_ID"], 96 | initial_value=initial_value) 97 | 98 | def flush_all(): 99 | return memcache.flush_all() 100 | 101 | def get_stats(): 102 | return memcache.get_stats() 103 | 104 | -------------------------------------------------------------------------------- /synopsis.markdown: -------------------------------------------------------------------------------- 1 | 2 | # Embed the file robertkrimen/gist-it-example/example.js 3 | 4 | 5 | # Embed without a footer 6 | 7 | 8 | # Show only the first and second line 9 | 10 | -------------------------------------------------------------------------------- /t/01-basic.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib 't/p5/lib/perl5'; 7 | use local::lib 't/p5'; 8 | 9 | use Test::Most; 10 | use Test::HTTP; 11 | 12 | my $TEST = $ENV{ TEST_GIST_IT_APPSPOT } // 'local'; 13 | 14 | my $base; 15 | if ( $TEST eq 'local' ) { $base = "http://localhost:8080" } 16 | elsif ( $TEST eq 0 ) { $base = "http://gist-it.appspot.com" } 17 | else { $base = "http://$TEST.gist-it.appspot.com" } 18 | 19 | my $is_local = $base =~ m/localhost/; 20 | diag "\$base = $base"; 21 | 22 | my $test = Test::HTTP->new( '' ); 23 | 24 | my ( $body, $body_re ); 25 | 26 | $test->get( "$base" ); 27 | $test->status_code_is( 200 ); 28 | $test->body_like( qr/DESCRIPTION/ ); 29 | $test->body_like( qr/USAGE/ ); 30 | 31 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/README" ); 32 | $test->status_code_is( 200 ); 33 | 34 | $test->get( "$base/github.com/robertkrimen/gist-it-example/blob/master/README" ); 35 | $test->status_code_is( 200 ); 36 | 37 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/example.js" ); 38 | $test->status_code_is( 200 ); 39 | 40 | $test->body_like( qr/\Qif ( 'prettyPrint' in window ) {} else {\E/ ); 41 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 42 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 43 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 44 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 45 | $test->body_like( qr{\Q
\E} ); 46 | $test->body_like( qr{\Q
\E} ); 47 | $test->body_like( qr{\Q
\E} ); 48 | $test->body_like( qr!\Q
function Xyzzy() {\n    return "Nothing happens";\n}\n
\E! ); 49 | $test->body_like( qr{\Q
\E} ); 50 | $test->body_like( qr{\QThis Gist brought to you by gist-it.\E} ); 51 | $test->body_like( qr{\Qview raw\E} ); 52 | $test->body_like( qr{\Q\E\s*\\n\s*\Qexample.js_decoded_content =~ m{(?:script type="text/javascript" src|link rel="stylesheet" href)="([^"]+)"}g; 55 | is( scalar @resource, 3 ); 56 | SKIP: { 57 | skip 'local', 3 if $is_local; 58 | for my $resource ( @resource ) { 59 | unlike( $resource, qr/localhost/, "$resource is not a localhost resource" ); 60 | $test->get( "$resource" ); 61 | $test->status_code_is( 200, "GET $resource" ); 62 | } 63 | } 64 | 65 | $test->get( "$base/github/robertkrimen/as3-projection/blob/master/src/yzzy/projection/Aperture.as" ); 66 | $test->body_like( qr/yzzy\.projection/ ); 67 | $test->status_code_is( 200 ); 68 | 69 | $test->get( "$base/github/miyagawa/CPAN-Any/blob/master/README" ); 70 | $test->status_code_is( 200 ); 71 | $test->body_like( qr/CPAN::Any/ ); 72 | 73 | $test->get( "$base/github/robertkrimen" ); 74 | $test->status_code_is( 500 ); 75 | $test->body_like( qr{\QUnable to parse "/github/robertkrimen": Not a valid repository path?\E} ); 76 | 77 | $test->get( "$base/github/robertkrimen/gist-it-example" ); 78 | $test->status_code_is( 500 ); 79 | $test->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example": Not a valid repository path?\E} ); 80 | 81 | $test->get( "$base/github/robertkrimen/gist-it-example/blob" ); 82 | $test->status_code_is( 500 ); 83 | $test->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example/blob": Not a valid repository path?\E} ); 84 | 85 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master" ); 86 | $test->status_code_is( 500 ); 87 | $test->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example/blob/master": Not a valid repository path?\E} ); 88 | 89 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/" ); 90 | $test->status_code_is( 500 ); 91 | $test->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example/blob/master/": Not a valid repository path?\E} ); 92 | 93 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/Test" ); 94 | $test->status_code_is( 404 ); 95 | $test->body_like( qr{\QUnable to fetch "https://github.com/robertkrimen/gist-it-example/raw/master/Test": (404)\E} ); 96 | 97 | $test->get( "$base/github/robertkrimen/gist-it-example/master/blob/README" ); 98 | diag( $body = $test->_decoded_content ); 99 | $test->status_code_is( 404 ); 100 | $test->body_like( qr{\QUnable to fetch "https://github.com/robertkrimen/gist-it-example/raw/blob/README": (404)\E} ); 101 | 102 | $test->get( "$base/github" ); 103 | $test->status_code_is( 404 ); 104 | $test->body_like( qr{\QNot Found\E} ); 105 | 106 | $test->get( "$base/xyzzy" ); 107 | $test->status_code_is( 404 ); 108 | $test->body_like( qr{\QNot Found\E} ); 109 | 110 | $test->get( "$base/github/" ); 111 | $test->status_code_is( 500 ); 112 | $test->body_like( qr{\QUnable to parse "/github/": Not a valid repository path?\E} ); 113 | diag $test->response->decoded_content; 114 | 115 | for ( 116 | [ 'embed.css' => { size => 983 } ], 117 | [ 'prettify/prettify.css' => { size => 675 } ], 118 | [ 'prettify/prettify.js' => { size => 14387 } ], 119 | ){ 120 | my $asset = $_->[0]; 121 | my $expect = $_->[1]; 122 | $test->get( "$base/assets/$asset" ); 123 | $test->status_code_is( 200, $asset ); 124 | my $size = length( $test->response->decoded_content ); 125 | cmp_ok( $size, '>', 0 ); 126 | cmp_ok( abs( $size - $expect->{size} ), '<', 128 ); 127 | } 128 | 129 | SKIP: { 130 | skip "Skip /xyzzy testing", 0 if $ENV{ TEST_GIST_IT_APPSPOT }; 131 | 132 | $test->get( "$base/xyzzy/github/robertkrimen/gist-it-example/blob/master" ); 133 | $test->status_code_is( 500 ); 134 | $test->body_like( qr{\QUnable to parse "github/robertkrimen/gist-it-example/blob/master": Not a valid repository path?\E} ); 135 | 136 | $test->get( "$base/xyzzy/github/miyagawa/CPAN-Any/blob/master/README" ); 137 | $test->status_code_is( 200 ); 138 | $test->body_like( qr/CPAN::Any/ ); 139 | } 140 | 141 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/example.js?style=0" ); 142 | $test->status_code_is( 200 ); 143 | $test->body_like( qr/\Qif ( 'prettyPrint' in window ) {} else {\E/ ); 144 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 145 | unlike( $test->response->decoded_content, qr{\Qdocument.write( '' )\E} ); 146 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 147 | $test->body_like( qr{\Qdocument.write( '' )\E} ); 148 | 149 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/example.js?style=1" ); 150 | $test->status_code_is( 200 ); 151 | like( $test->response->decoded_content, qr{\Qdocument.write( '' )\E} ); 152 | 153 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/example.js?highlight=0" ); 154 | $body = $test->response->decoded_content; 155 | $test->status_code_is( 200 ); 156 | unlike( $body, qr/\Qif ( 'prettyPrint' in window ) {} else {\E/ ); 157 | unlike( $body, qr{\Qdocument.write( '' )\E} ); 158 | like( $body, qr{\Qdocument.write( '' )\E} ); 159 | unlike( $body, qr{\Qdocument.write( '' )\E} ); 160 | unlike( $body, qr{\Qdocument.write( '' )\E} ); 161 | 162 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/example.js?highlight=1" ); 163 | $body = $test->response->decoded_content; 164 | $test->status_code_is( 200 ); 165 | like( $body, qr{\Qdocument.write( '' )\E} ); 166 | like( $body, qr{\Qdocument.write( '' )\E} ); 167 | 168 | $test->get( "$base/github/robertkrimen/gist-it-example/blob/master/example.js?highlight=deferred-prettify" ); 169 | $body = $test->response->decoded_content; 170 | $test->status_code_is( 200 ); 171 | unlike( $body, qr{\Qdocument.write( '' )\E} ); 172 | unlike( $body, qr{\Qdocument.write( '' )\E} ); 173 | unlike( $body, qr{\Qdocument.write( '' )\E} ); 174 | like( $body, qr!\Q
function Xyzzy() {\n    return "Nothing happens";\n}\n
\E! ); 175 | done_testing; 176 | -------------------------------------------------------------------------------- /t/Makefile.PL: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use ExtUtils::MakeMaker 6.31; 5 | 6 | my %PREREQ_PM = map { my ( $package, $version ) = split m/\s/; ( $package => $version || 0 ) } grep { m/\S/ } grep { ! /^\s*#/ } split m/\n/, <<_END_; 7 | local::lib 8 | Test::Most 9 | Test::HTTP 10 | _END_ 11 | 12 | my %WriteMakefileArgs = ( 13 | 'ABSTRACT' => '', 14 | 'AUTHOR' => '', 15 | 'BUILD_REQUIRES' => { 16 | }, 17 | 'CONFIGURE_REQUIRES' => { 18 | 'ExtUtils::MakeMaker' => '6.31' 19 | }, 20 | 'DISTNAME' => 'Task-t-gist-it', 21 | 'EXE_FILES' => [], 22 | 'LICENSE' => 'perl', 23 | 'NAME' => 'Task::BuildRule90', 24 | 'PREREQ_PM' => { %PREREQ_PM }, 25 | 'VERSION' => '0.001', 26 | 'test' => { 27 | 'TESTS' => 't/*.t' 28 | } 29 | ); 30 | 31 | 32 | unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { 33 | my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; 34 | my $pp = $WriteMakefileArgs{PREREQ_PM}; 35 | for my $mod ( keys %$br ) { 36 | if ( exists $pp->{$mod} ) { 37 | $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; 38 | } 39 | else { 40 | $pp->{$mod} = $br->{$mod}; 41 | } 42 | } 43 | } 44 | 45 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 46 | unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; 47 | 48 | WriteMakefile(%WriteMakefileArgs); 49 | 50 | -------------------------------------------------------------------------------- /t/cpanm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/t/cpanm -------------------------------------------------------------------------------- /t0/01-basic.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::Most; 7 | use Test::HTTP::TWIM; 8 | 9 | my $base = "http://localhost:8080"; 10 | my $is_local = $base =~ m/localhost/; 11 | diag "\$base = $base"; 12 | 13 | my $twim = Test::HTTP::TWIM->twim( base => $base ); 14 | 15 | my ( $body, $body_re ); 16 | 17 | $twim->get( '/' ); 18 | $twim->status_code_is( 200 ); 19 | $twim->body_like( qr/Description/ ); 20 | $twim->body_like( qr/Usage/ ); 21 | 22 | $twim->get( "/github/robertkrimen/gist-it-example/blob/master/README" ); 23 | $twim->status_code_is( 200 ); 24 | 25 | $twim->get( "/github.com/robertkrimen/gist-it-example/blob/master/README" ); 26 | $twim->status_code_is( 200 ); 27 | 28 | $twim->get( "/github/robertkrimen/gist-it-example/blob/master/example.js" ); 29 | $twim->status_code_is( 200 ); 30 | 31 | $twim->body_like( qr/\Qif ( 'prettyPrint' in window ) {} else {\E/ ); 32 | $twim->body_like( qr{\Qdocument.write( '' )\E} ); 33 | $twim->body_like( qr{\Qdocument.write( '' )\E} ); 34 | $twim->body_like( qr{\Qdocument.write( '' )\E} ); 35 | $twim->body_like( qr{\Qdocument.write( '' )\E} ); 36 | $twim->body_like( qr{\Q
\E} ); 37 | $twim->body_like( qr{\Q
\E} ); 38 | $twim->body_like( qr{\Q
\E} ); 39 | $twim->body_like( qr!\Q
function Xyzzy() {\n    return "Nothing happens";\n}\n
\E! ); 40 | $twim->body_like( qr{\Q
\E} ); 41 | $twim->body_like( qr{\QThis Gist by gist-it\E} ); 42 | $twim->body_like( qr{\Qview raw\E} ); 43 | $twim->body_like( qr{\Q\E\s*\\n\s*\Qexample.jsresponse->body =~ m{(?:script type="text/javascript" src|link rel="stylesheet" href)="([^"]+)"}g; 46 | is( scalar @resource, 3 ); 47 | SKIP: { 48 | skip 'local', 3 if $is_local; 49 | for my $resource ( @resource ) { 50 | unlike( $resource, qr/localhost/, "$resource is not a localhost resource" ); 51 | $twim->get( "$resource" ); 52 | $twim->status_code_is( 200, "GET $resource" ); 53 | } 54 | } 55 | 56 | $twim->get( "github/robertkrimen/as3-projection/blob/master/src/yzzy/projection/Aperture.as" ); 57 | $twim->body_like( qr/yzzy\.projection/ ); 58 | $twim->status_code_is( 200 ); 59 | 60 | $twim->get( "github/miyagawa/CPAN-Any/blob/master/README" ); 61 | $twim->status_code_is( 200 ); 62 | $twim->body_like( qr/CPAN::Any/ ); 63 | 64 | $twim->get_fail( "github/robertkrimen" ); 65 | $twim->status_code_is( 500 ); 66 | $twim->body_like( qr{\QUnable to parse "/github/robertkrimen": Not a valid repository path?\E} ); 67 | 68 | $twim->get_fail( "github/robertkrimen/gist-it-example" ); 69 | $twim->status_code_is( 500 ); 70 | $twim->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example": Not a valid repository path?\E} ); 71 | 72 | $twim->get_fail( "github/robertkrimen/gist-it-example/blob" ); 73 | $twim->status_code_is( 500 ); 74 | $twim->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example/blob": Not a valid repository path?\E} ); 75 | 76 | $twim->get_fail( "github/robertkrimen/gist-it-example/blob/master" ); 77 | $twim->status_code_is( 500 ); 78 | $twim->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example/blob/master": Not a valid repository path?\E} ); 79 | 80 | $twim->get_fail( "github/robertkrimen/gist-it-example/blob/master/" ); 81 | $twim->status_code_is( 500 ); 82 | $twim->body_like( qr{\QUnable to parse "/github/robertkrimen/gist-it-example/blob/master/": Not a valid repository path?\E} ); 83 | 84 | $twim->get_fail( "github/robertkrimen/gist-it-example/blob/master/Test" ); 85 | $twim->status_code_is( 404 ); 86 | $twim->body_like( qr{\QUnable to fetch "https://github.com/robertkrimen/gist-it-example/raw/master/Test": (404)\E} ); 87 | 88 | $twim->get_fail( "github/robertkrimen/gist-it-example/master/blob/README" ); 89 | diag( $body = $twim->response->content ); 90 | $twim->status_code_is( 404 ); 91 | $twim->body_like( qr{\QUnable to fetch "https://github.com/robertkrimen/gist-it-example/raw/blob/README": (404)\E} ); 92 | 93 | $twim->get_fail( "github" ); 94 | $twim->status_code_is( 404 ); 95 | $twim->body_like( qr{\QNot Found\E} ); 96 | 97 | $twim->get_fail( "xyzzy" ); 98 | $twim->status_code_is( 404 ); 99 | $twim->body_like( qr{\QNot Found\E} ); 100 | 101 | $twim->get_fail( "github/" ); 102 | $twim->status_code_is( 500 ); 103 | $twim->body_like( qr{\QUnable to parse "/github/": Not a valid repository path?\E} ); 104 | diag $twim->response->content; 105 | 106 | for (qw[ 107 | embed.css 108 | prettify/prettify.css 109 | prettify/prettify.js 110 | ]){ 111 | $twim->get( "assets/$_" ); 112 | $twim->status_code_is( 200, $_ ); 113 | } 114 | 115 | $twim->get_fail( "xyzzy/github/robertkrimen/gist-it-example/blob/master" ); 116 | $twim->status_code_is( 500 ); 117 | $twim->body_like( qr{\QUnable to parse "github/robertkrimen/gist-it-example/blob/master": Not a valid repository path?\E} ); 118 | 119 | $twim->get( "xyzzy/github/miyagawa/CPAN-Any/blob/master/README" ); 120 | $twim->status_code_is( 200 ); 121 | $twim->body_like( qr/CPAN::Any/ ); 122 | 123 | done_testing; 124 | -------------------------------------------------------------------------------- /t0/render-gist-it-example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Modern::Perl; 7 | use HTTP::DWIM; 8 | 9 | my $base = "http://localhost:8080"; 10 | my $is_local = $base =~ m/localhost/; 11 | 12 | my $dwim = HTTP::DWIM->dwim( base => $base ); 13 | my $response = $dwim->get( "/github/robertkrimen/gist-it-example/blob/master/example.js?test=json" => sub { 14 | my $data = shift; 15 | say $data->{html}; 16 | } ); 17 | #say $response->http_response->as_string; 18 | -------------------------------------------------------------------------------- /test/test_cgi_escape.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ft=python: set fileencoding=utf-8 3 | import random 4 | import unittest2 5 | import sys; 6 | import os; 7 | 8 | sys.path.append( os.path.join( sys.path[0], ".." ) ) 9 | from gist_it import cgi_escape 10 | 11 | # https://github.com/whittle/node-coffee-heroku-tutorial/blob/eb587185509ec8c2e728067d49f4ac2d5a67ec09/app.js 12 | class t( unittest2.TestCase ): 13 | def runTest( self ): 14 | self.assertEqual( len(cgi_escape( """ 15 | (function() { 16 | var http = require('http'); 17 | 18 | var say_nothing = function(request, response) { 19 | var message = 'مرحبا العالم'; 20 | 21 | response.setHeader('Content-Type', 'text/plain; charset=utf-8'); 22 | response.setHeader('X-Bad-Content-Length', message.length); 23 | response.setHeader('Content-Length', Buffer.byteLength(message, 'utf8')); 24 | response.write(message, 'utf8'); 25 | response.end(); 26 | }; 27 | 28 | var app = http.createServer(say_nothing); 29 | app.listen(3080); 30 | })(); 31 | 32 | """.decode('utf-8') ) ), 542 ) 33 | 34 | if __name__ == '__main__': 35 | unittest2.main() 36 | -------------------------------------------------------------------------------- /test/test_footer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ft=python: 3 | import random 4 | import unittest2 5 | import sys; 6 | import os; 7 | 8 | sys.path.append( os.path.join( sys.path[0], ".." ) ) 9 | import gist_it 10 | 11 | class t( unittest2.TestCase ): 12 | def runTest( self ): 13 | self.assertEqual( gist_it.parse_footer( 0 ), '0' ) 14 | self.assertEqual( gist_it.parse_footer( '0' ), '0' ) 15 | self.assertEqual( gist_it.parse_footer( False ), '0' ) 16 | self.assertEqual( gist_it.parse_footer( True ), '1' ) 17 | self.assertEqual( gist_it.parse_footer( None ), '1' ) 18 | self.assertEqual( gist_it.parse_footer( ' 1' ), '1' ) 19 | self.assertEqual( gist_it.parse_footer( 'yes ' ), '1' ) 20 | self.assertEqual( gist_it.parse_footer( ' no' ), '0' ) 21 | self.assertEqual( gist_it.parse_footer( 'none ' ), '0' ) 22 | self.assertEqual( gist_it.parse_footer( ' minimal ' ), 'minimal' ) 23 | 24 | if __name__ == '__main__': 25 | unittest2.main() 26 | -------------------------------------------------------------------------------- /test/test_gist_it.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ft=python: 3 | import random 4 | import unittest2 5 | import sys; 6 | import os; 7 | 8 | sys.path.append( os.path.join( sys.path[0], ".." ) ) 9 | import gist_it 10 | 11 | class t( unittest2.TestCase ): 12 | def runTest( self ): 13 | self.assertTrue( True, 'Xyzzy' ) 14 | 15 | self.assertTrue( gist_it.match( 'github/robertkrimen/yzzy-projection/raw/master/src/yzzy/projection/View.as' ) ) 16 | 17 | gist = gist_it.parse( 'github/robertkrimen/yzzy-projection/raw/master/src/yzzy/projection/View.as' ) 18 | self.assertEqual( gist.user, 'robertkrimen' ) 19 | self.assertEqual( gist.repository, 'yzzy-projection' ) 20 | self.assertEqual( gist.branch, 'master' ) 21 | self.assertEqual( gist.path, 'src/yzzy/projection/View.as' ) 22 | 23 | self.assertEqual( gist.blob_path, 'robertkrimen/yzzy-projection/blob/master/src/yzzy/projection/View.as' ) 24 | self.assertEqual( gist.blob_url, 'https://github.com/robertkrimen/yzzy-projection/blob/master/src/yzzy/projection/View.as' ) 25 | 26 | self.assertEqual( gist.raw_path, 'robertkrimen/yzzy-projection/raw/master/src/yzzy/projection/View.as' ) 27 | self.assertEqual( gist.raw_url, 'https://github.com/robertkrimen/yzzy-projection/raw/master/src/yzzy/projection/View.as' ) 28 | 29 | self.assertEqual( gist.user_repository, 'robertkrimen/yzzy-projection' ) 30 | self.assertEqual( gist.user_repository_branch_path, 'robertkrimen/yzzy-projection/master/src/yzzy/projection/View.as' ) 31 | self.assertEqual( gist.user_repository_url, 'https://github.com/robertkrimen/yzzy-projection' ) 32 | 33 | self.assertEqual( gist.start_line, 0 ) 34 | self.assertEqual( gist.end_line, 0 ) 35 | 36 | gist = gist_it.parse( 'github/robertkrimen/yzzy-projection/raw/master/src/yzzy/projection/View.as', slice_option = '1:' ) 37 | 38 | self.assertEqual( gist.start_line, 1 ) 39 | self.assertEqual( gist.end_line, 0 ) 40 | 41 | if __name__ == '__main__': 42 | unittest2.main() 43 | -------------------------------------------------------------------------------- /test/test_highlight.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ft=python: 3 | import random 4 | import unittest2 5 | import sys; 6 | import os; 7 | 8 | sys.path.append( os.path.join( sys.path[0], ".." ) ) 9 | import gist_it 10 | 11 | class t( unittest2.TestCase ): 12 | def runTest( self ): 13 | self.assertEqual( gist_it.parse_highlight( 0 ), '0' ) 14 | self.assertEqual( gist_it.parse_highlight( '0' ), '0' ) 15 | self.assertEqual( gist_it.parse_highlight( False ), '0' ) 16 | self.assertEqual( gist_it.parse_highlight( True ), 'prettify' ) 17 | self.assertEqual( gist_it.parse_highlight( None ), 'prettify' ) 18 | self.assertEqual( gist_it.parse_highlight( ' 1' ), 'prettify' ) 19 | self.assertEqual( gist_it.parse_highlight( 'yes ' ), 'prettify' ) 20 | self.assertEqual( gist_it.parse_highlight( ' no' ), '0' ) 21 | self.assertEqual( gist_it.parse_highlight( 'none ' ), '0' ) 22 | self.assertEqual( gist_it.parse_highlight( 'deferred-prettify' ), 'deferred-prettify' ) 23 | 24 | if __name__ == '__main__': 25 | unittest2.main() 26 | -------------------------------------------------------------------------------- /test/test_slice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ft=python: 3 | import random 4 | import unittest2 5 | import sys; 6 | import os; 7 | 8 | sys.path.append( os.path.join( sys.path[0], ".." ) ) 9 | import gist_it 10 | 11 | class t( unittest2.TestCase ): 12 | def runTest( self ): 13 | slice_ = gist_it.parse_slice( '' ) 14 | self.assertEqual( len( slice_ ), 2 ) 15 | self.assertEqual( slice_[0], 0 ) 16 | self.assertEqual( slice_[1], 0 ) 17 | 18 | slice_ = gist_it.parse_slice( '1' ) 19 | self.assertEqual( len( slice_ ), 2 ) 20 | self.assertEqual( slice_[0], 1 ) 21 | self.assertEqual( slice_[1], None ) 22 | 23 | slice_ = gist_it.parse_slice( '1:' ) 24 | self.assertEqual( len( slice_ ), 2 ) 25 | self.assertEqual( slice_[0], 1 ) 26 | self.assertEqual( slice_[1], 0 ) 27 | 28 | slice_ = gist_it.parse_slice( '1:0' ) 29 | self.assertEqual( len( slice_ ), 2 ) 30 | self.assertEqual( slice_[0], 1 ) 31 | self.assertEqual( slice_[1], 0 ) 32 | 33 | slice_ = gist_it.parse_slice( ':1' ) 34 | self.assertEqual( len( slice_ ), 2 ) 35 | self.assertEqual( slice_[0], 0 ) 36 | self.assertEqual( slice_[1], 1 ) 37 | 38 | slice_ = gist_it.parse_slice( '0:1' ) 39 | self.assertEqual( len( slice_ ), 2 ) 40 | self.assertEqual( slice_[0], 0 ) 41 | self.assertEqual( slice_[1], 1 ) 42 | 43 | slice_ = gist_it.parse_slice( '1:1' ) 44 | self.assertEqual( len( slice_ ), 2 ) 45 | self.assertEqual( slice_[0], 1 ) 46 | self.assertEqual( slice_[1], 1 ) 47 | 48 | slice_ = gist_it.parse_slice( ':' ) 49 | self.assertEqual( len( slice_ ), 2 ) 50 | self.assertEqual( slice_[0], 0 ) 51 | self.assertEqual( slice_[1], 0 ) 52 | 53 | slice_ = gist_it.parse_slice( '-0:-0' ) 54 | self.assertEqual( len( slice_ ), 2 ) 55 | self.assertEqual( slice_[0], 0 ) 56 | self.assertEqual( slice_[1], 0 ) 57 | 58 | slice_ = gist_it.parse_slice( '-1:' ) 59 | self.assertEqual( len( slice_ ), 2 ) 60 | self.assertEqual( slice_[0], -1 ) 61 | self.assertEqual( slice_[1], 0 ) 62 | 63 | content = """ 64 | Line 2 65 | Line 3 66 | Line 4 67 | Line 5 68 | Line 6 69 | 70 | Line 8 71 | """ 72 | self.assertEqual( gist_it.take_slice( content, 0, 0 ), content ) 73 | self.assertEqual( gist_it.take_slice( content, 1, 2 ), "Line 2\nLine 3" ) 74 | self.assertEqual( gist_it.take_slice( content, 1, None ), "Line 2" ) 75 | self.assertEqual( gist_it.take_slice( content, 0, 2 ), "\nLine 2\nLine 3" ) 76 | self.assertEqual( gist_it.take_slice( content, 0, -1 ), """ 77 | Line 2 78 | Line 3 79 | Line 4 80 | Line 5 81 | Line 6 82 | """ ) 83 | self.assertEqual( gist_it.take_slice( content, -1, 0 ), "Line 8" ) 84 | 85 | if __name__ == '__main__': 86 | unittest2.main() 87 | -------------------------------------------------------------------------------- /test/test_style.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ft=python: 3 | import random 4 | import unittest2 5 | import sys; 6 | import os; 7 | 8 | sys.path.append( os.path.join( sys.path[0], ".." ) ) 9 | import gist_it 10 | 11 | class t( unittest2.TestCase ): 12 | def runTest( self ): 13 | self.assertEqual( gist_it.parse_style( 0 ), '0' ) 14 | self.assertEqual( gist_it.parse_style( '0' ), '0' ) 15 | self.assertEqual( gist_it.parse_style( False ), '0' ) 16 | self.assertEqual( gist_it.parse_style( True ), '1' ) 17 | self.assertEqual( gist_it.parse_style( None ), '1' ) 18 | self.assertEqual( gist_it.parse_style( ' 1' ), '1' ) 19 | self.assertEqual( gist_it.parse_style( 'yes ' ), '1' ) 20 | self.assertEqual( gist_it.parse_style( ' no' ), '0' ) 21 | self.assertEqual( gist_it.parse_style( 'none ' ), '0' ) 22 | 23 | if __name__ == '__main__': 24 | unittest2.main() 25 | --------------------------------------------------------------------------------