├── .gitignore ├── LICENSE ├── app.yaml ├── blog ├── __init__.py ├── db.py ├── helpers.py ├── models.py ├── schema │ ├── fixture-sqlite.sql │ └── schema.sql ├── static │ ├── css │ │ ├── boilerplate.css │ │ ├── fonts │ │ │ ├── Chunkfive-webfont.eot │ │ │ ├── Chunkfive-webfont.svg │ │ │ ├── Chunkfive-webfont.ttf │ │ │ ├── Chunkfive-webfont.woff │ │ │ ├── DroidSansMono-webfont.eot │ │ │ ├── DroidSansMono-webfont.svg │ │ │ ├── DroidSansMono-webfont.ttf │ │ │ ├── DroidSansMono-webfont.woff │ │ │ ├── fontin_sans_b_45b-webfont.eot │ │ │ ├── fontin_sans_b_45b-webfont.svg │ │ │ ├── fontin_sans_b_45b-webfont.ttf │ │ │ ├── fontin_sans_b_45b-webfont.woff │ │ │ ├── fontin_sans_i_45b-webfont.eot │ │ │ ├── fontin_sans_i_45b-webfont.svg │ │ │ ├── fontin_sans_i_45b-webfont.ttf │ │ │ ├── fontin_sans_i_45b-webfont.woff │ │ │ ├── fontin_sans_r_45b-webfont.eot │ │ │ ├── fontin_sans_r_45b-webfont.svg │ │ │ ├── fontin_sans_r_45b-webfont.ttf │ │ │ ├── fontin_sans_r_45b-webfont.woff │ │ │ ├── slkscr-webfont.eot │ │ │ ├── slkscr-webfont.svg │ │ │ ├── slkscr-webfont.ttf │ │ │ ├── slkscr-webfont.woff │ │ │ ├── slkscrb-webfont.eot │ │ │ ├── slkscrb-webfont.svg │ │ │ ├── slkscrb-webfont.ttf │ │ │ └── slkscrb-webfont.woff │ │ ├── handheld.css │ │ ├── style.css │ │ ├── syntax-highlight.css │ │ └── syntax.css │ ├── favicon.ico │ ├── img │ │ ├── img_1.png │ │ ├── img_10.png │ │ ├── img_11.png │ │ ├── img_12.png │ │ ├── img_2.png │ │ ├── img_3.png │ │ ├── img_4.png │ │ ├── img_5.png │ │ ├── img_6.png │ │ ├── img_7.png │ │ ├── img_8.png │ │ └── img_9.png │ ├── js │ │ ├── dd_belatedpng.js │ │ ├── jquery-1.4.2.min.js │ │ ├── modernizr-1.5.min.js │ │ ├── plugins.js │ │ └── script.js │ └── pages │ │ └── about.md ├── templates │ ├── about.html │ ├── add_entry.html │ ├── admin.html │ ├── edit_entry.html │ ├── home.html │ ├── layout.html │ ├── list_entries.html │ └── login.html └── views.py ├── config └── __init__.py ├── flask ├── __init__.py ├── app.py ├── config.py ├── ctx.py ├── globals.py ├── helpers.py ├── logging.py ├── module.py ├── session.py ├── signals.py ├── templating.py ├── testing.py └── wrappers.py ├── index.yaml ├── jinja2 ├── __init__.py ├── _speedups.c ├── _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 ├── main.py ├── markdown ├── __init__.py ├── blockparser.py ├── blockprocessors.py ├── commandline.py ├── etree_loader.py ├── extensions │ ├── __init__.py │ ├── abbr.py │ ├── codehilite.py │ ├── def_list.py │ ├── extra.py │ ├── fenced_code.py │ ├── footnotes.py │ ├── headerid.py │ ├── html_tidy.py │ ├── imagelinks.py │ ├── meta.py │ ├── rss.py │ ├── tables.py │ ├── toc.py │ └── wikilinks.py ├── html4.py ├── inlinepatterns.py ├── odict.py ├── postprocessors.py ├── preprocessors.py └── treeprocessors.py ├── pygments ├── __init__.py ├── cmdline.py ├── console.py ├── filter.py ├── filters │ └── __init__.py ├── formatter.py ├── formatters │ ├── __init__.py │ ├── _mapping.py │ ├── bbcode.py │ ├── html.py │ ├── img.py │ ├── latex.py │ ├── other.py │ ├── rtf.py │ ├── svg.py │ ├── terminal.py │ └── terminal256.py ├── lexer.py ├── lexers │ ├── __init__.py │ ├── _asybuiltins.py │ ├── _clbuiltins.py │ ├── _luabuiltins.py │ ├── _mapping.py │ ├── _phpbuiltins.py │ ├── _vimbuiltins.py │ ├── agile.py │ ├── asm.py │ ├── compiled.py │ ├── dotnet.py │ ├── functional.py │ ├── hdl.py │ ├── math.py │ ├── other.py │ ├── parsers.py │ ├── special.py │ ├── templates.py │ ├── text.py │ └── web.py ├── plugin.py ├── scanner.py ├── style.py ├── styles │ ├── __init__.py │ ├── autumn.py │ ├── borland.py │ ├── bw.py │ ├── colorful.py │ ├── default.py │ ├── emacs.py │ ├── friendly.py │ ├── fruity.py │ ├── manni.py │ ├── monokai.py │ ├── murphy.py │ ├── native.py │ ├── pastie.py │ ├── perldoc.py │ ├── tango.py │ ├── trac.py │ ├── vim.py │ └── vs.py ├── token.py ├── unistring.py └── util.py ├── tests └── blog_tests.py └── werkzeug ├── __init__.py ├── _internal.py ├── contrib ├── __init__.py ├── atom.py ├── cache.py ├── fixers.py ├── iterio.py ├── jsrouting.py ├── kickstart.py ├── limiter.py ├── lint.py ├── profiler.py ├── securecookie.py ├── sessions.py ├── testtools.py └── wrappers.py ├── datastructures.py ├── debug ├── __init__.py ├── console.py ├── render.py ├── repr.py ├── shared │ ├── body.tmpl │ ├── codetable.tmpl │ ├── console.png │ ├── debugger.js │ ├── jquery.js │ ├── less.png │ ├── more.png │ ├── source.png │ ├── style.css │ └── vartable.tmpl ├── tbtools.py ├── templates │ ├── console.html │ ├── dump_object.html │ ├── frame.html │ ├── help_command.html │ ├── source.html │ ├── traceback_full.html │ ├── traceback_plaintext.html │ └── traceback_summary.html └── utils.py ├── exceptions.py ├── formparser.py ├── http.py ├── local.py ├── posixemulation.py ├── routing.py ├── script.py ├── security.py ├── serving.py ├── templates.py ├── test.py ├── testapp.py ├── urls.py ├── useragents.py ├── utils.py ├── wrappers.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *.db 4 | *.*~ 5 | config.cfg 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) <2010> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: proudlygeek 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | 7 | handlers: 8 | - url: /.* 9 | script: main.py 10 | 11 | - url: /static 12 | static_dir: static 13 | expiration: "7d" 14 | -------------------------------------------------------------------------------- /blog/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | blog 5 | ~~~~~~~~~~~~~~~~~~ 6 | 7 | A simple blog app written with Flask which 8 | supports sqlite and Google App Engine's Datastore. 9 | 10 | :copyright: (c) 2010 by Gianluca Bargelli. 11 | :license: MIT License, see LICENSE for more details. 12 | 13 | 14 | """ 15 | -------------------------------------------------------------------------------- /blog/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | blog.models 5 | ~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains GAE models. 8 | 9 | :copyright: (c) 2010 by Gianluca Bargelli. 10 | :license: MIT License, see LICENSE for more details. 11 | 12 | 13 | """ 14 | 15 | from google.appengine.ext import db 16 | 17 | 18 | class User(db.Model): 19 | """ 20 | This class models a user into a 21 | Google App Engine's Datastore entity. 22 | """ 23 | username = db.StringProperty(required=True) 24 | password = db.StringProperty(required=True, indexed=False) 25 | rank = db.StringProperty(required=True, 26 | choices = ['user','admin'], 27 | default = 'user') 28 | 29 | 30 | class Entry(db.Model): 31 | """ 32 | This class models a blog post into a 33 | Google App Engine's Datastore entity. 34 | """ 35 | slug = db.StringProperty(required=True) 36 | title = db.StringProperty(required=True) 37 | body = db.TextProperty(required=True) 38 | creation_date = db.DateTimeProperty(auto_now_add=True) 39 | """ 40 | creation_only_date is used for single entry display; 41 | (see http://stackoverflow.com/questions/4109272/why-doesnt-my-gql-query-return-any-results-in-my-gae-app) 42 | """ 43 | creation_only_date = db.DateProperty(auto_now_add=True) 44 | last_date = db.DateTimeProperty(auto_now=True) 45 | user_id_FK = db.ReferenceProperty(User, collection_name='Owner') 46 | tags = db.StringListProperty() 47 | -------------------------------------------------------------------------------- /blog/schema/fixture-sqlite.sql: -------------------------------------------------------------------------------- 1 | PRAGMA foreign_keys=OFF; 2 | BEGIN TRANSACTION; 3 | DROP TABLE IF EXISTS user; 4 | CREATE TABLE user ( 5 | id INTEGER PRIMARY KEY autoincrement, 6 | username VARCHAR(30) NOT NULL, 7 | password VARCHAR(30) NOT NULL, 8 | rank_id_FK INTEGER NOT NULL REFERENCES rank(id) 9 | ); 10 | 11 | DROP TABLE IF EXISTS rank; 12 | CREATE TABLE rank ( 13 | id INTEGER PRIMARY KEY autoincrement, 14 | role_name VARCHAR(20) NOT NULL 15 | ); 16 | 17 | DROP TABLE IF EXISTS entry; 18 | CREATE TABLE entry ( 19 | id INTEGER PRIMARY KEY autoincrement, 20 | slug VARCHAR(80) NOT NULL, 21 | title VARCHAR(80) NOT NULL, 22 | body TEXT NOT NULL, 23 | creation_date DATE NOT NULL, 24 | last_date DATE, 25 | user_id_FK INTEGER NOT NULL REFERENCES user(id) 26 | ); 27 | 28 | DROP TABLE IF EXISTS entry_tags; 29 | CREATE TABLE entry_tags ( 30 | id_entry_FK INTEGER REFERENCES entry(id), 31 | id_tag_FK INTEGER REFERENCES tag(id), 32 | PRIMARY KEY(id_entry_FK, id_tag_FK) 33 | ); 34 | 35 | DROP TABLE IF EXISTS tag; 36 | CREATE TABLE tag ( 37 | id INTEGER PRIMARY KEY autoincrement, 38 | name VARCHAR(10) NOT NULL 39 | ); 40 | 41 | /* Sample data */ 42 | INSERT INTO "user" VALUES(1, 'bargio', 'f1b1a13033eddc3fdeecc0ed03bdc019c25890ba906658addad9fefe',0); 43 | INSERT INTO "user" VALUES(2, 'test', '90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809',0); 44 | INSERT INTO "user" VALUES(3, 'user', '90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809',1); 45 | INSERT INTO "rank" VALUES(0,'administrator'); 46 | INSERT INTO "rank" VALUES(1,'user'); 47 | 48 | DELETE FROM sqlite_sequence; 49 | INSERT INTO "sqlite_sequence" VALUES('rank',1); 50 | INSERT INTO "sqlite_sequence" VALUES('user',1); 51 | COMMIT; 52 | -------------------------------------------------------------------------------- /blog/schema/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS user; 2 | CREATE TABLE user ( 3 | id INTEGER PRIMARY KEY autoincrement, 4 | username VARCHAR(30) NOT NULL, 5 | password VARCHAR(30) NOT NULL, 6 | rank_id_FK INTEGER NOT NULL REFERENCES rank(id) 7 | ); 8 | DROP TABLE IF EXISTS rank; 9 | CREATE TABLE rank ( 10 | id INTEGER PRIMARY KEY autoincrement, 11 | role_name VARCHAR(20) NOT NULL 12 | ); 13 | 14 | DROP TABLE IF EXISTS entry; 15 | CREATE TABLE entry ( 16 | id INTEGER NOT NULL, 17 | slug VARCHAR(80) NOT NULL, 18 | title VARCHAR(80) NOT NULL, 19 | body TEXT NOT NULL, 20 | creation_date DATE NOT NULL, 21 | last_date DATE, 22 | user_id_FK INTEGER NOT NULL REFERENCES user(id), 23 | PRIMARY KEY(id, slug) 24 | ); 25 | 26 | DROP TABLE IF EXISTS entry_tags; 27 | CREATE TABLE entry_tags ( 28 | slug_entry_FK INTEGER REFERENCES entry(id), 29 | id_tag_FK INTEGER REFERENCES tag(id), 30 | PRIMARY KEY(slug_entry_FK, id_tag_FK) 31 | ); 32 | 33 | DROP TABLE IF EXISTS tag; 34 | CREATE TABLE tag ( 35 | id INTEGER PRIMARY KEY autoincrement, 36 | name VARCHAR(10) NOT NULL 37 | ); 38 | -------------------------------------------------------------------------------- /blog/static/css/boilerplate.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,figure,footer,header,hgroup,menu,nav,section,menu,time,mark,audio,video{border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}nav ul{list-style:none;}blockquote,q{quotes:none;}blockquote:before,blockquote:after,q:before,q:after{content:none;}a{font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}ins{background-color:#ff9;color:#000;text-decoration:none;}mark{background-color:#ff9;color:#000;font-style:italic;font-weight:700;}del{text-decoration:line-through;}abbr[title],dfn[title]{border-bottom:1px dotted #000;cursor:help;}table{border-collapse:collapse;border-spacing:0;font-size:inherit;font:100%;}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0;}input,select{vertical-align:middle;}body{font-size:small;font:x-small;line-height:1.22;}select,input,textarea{font:99% sans-serif;}pre,code,kbd,samp{font-family:monospace, sans-serif;}body,select,input,textarea{color:#444;}h1,h2,h3,h4,h5,h6{font-weight:700;text-rendering:optimizeLegibility;}html{-webkit-font-smoothing:antialiased;overflow-y:scroll;}a:hover,a:active{outline:none;}a,a:active,a:visited{color:#607890;}a:hover{color:#036;}ul{margin-left:30px;}ol{margin-left:30px;list-style-type:decimal;}small{font-size:85%;}strong,th{font-weight:700;}td,td img{vertical-align:top;}sub{vertical-align:sub;font-size:smaller;}sup{vertical-align:super;font-size:smaller;}pre{white-space:pre-line;word-wrap:break-word;padding:15px;}input[type=checkbox]{vertical-align:baseline;}label,input[type=button],input[type=submit],button{cursor:pointer;}a:link{-webkit-tap-highlight-color:#FF5E99;}button{width:auto;overflow:visible;}.ie7 img{-ms-interpolation-mode:bicubic;}.ir{display:block;text-indent:-999em;overflow:hidden;background-repeat:no-repeat;}.hidden{display:none;visibility:hidden;}.visuallyhidden{position:absolute!important;clip:rect(1px,1px,1px,1px);}.invisible{visibility:hidden;}.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}* html .clearfix{height:1%;}article,aside,figure,footer,header,hgroup,nav,section,.clearfix{display:block;}input[type=radio],.ie6 input{vertical-align:text-bottom;}::-moz-selection,::selection{background:#FF5E99;color:#fff;text-shadow:none;}@media print{*{background:transparent!important;color:#444!important;text-shadow:none;}a,a:visited{color:#444!important;text-decoration:underline;}a:after{content:" (" attr(href) ")";}abbr:after{content:" (" attr(title) ")";}.ir a:after{content:"";}pre,blockquote{border:1px solid #999;page-break-inside:avoid;}img{page-break-inside:avoid;}@page{margin:.5cm;}p,h2,h3{orphans:3;widows:3;}h2,h3{page-break-after:avoid;}}@media screen and max-device-width 480px{html{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;}} 2 | -------------------------------------------------------------------------------- /blog/static/css/fonts/Chunkfive-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/Chunkfive-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/Chunkfive-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/Chunkfive-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/Chunkfive-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/Chunkfive-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/DroidSansMono-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/DroidSansMono-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/DroidSansMono-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/DroidSansMono-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/DroidSansMono-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/DroidSansMono-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_b_45b-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_b_45b-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_b_45b-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_b_45b-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_b_45b-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_b_45b-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_i_45b-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_i_45b-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_i_45b-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_i_45b-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_i_45b-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_i_45b-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_r_45b-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_r_45b-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_r_45b-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_r_45b-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_r_45b-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/fontin_sans_r_45b-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscr-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/slkscr-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscr-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/slkscr-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscr-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/slkscr-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscrb-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/slkscrb-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscrb-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/slkscrb-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscrb-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/css/fonts/slkscrb-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/handheld.css: -------------------------------------------------------------------------------- 1 | *{float:none;font-size:80%;background:#fff;color:#000;} 2 | -------------------------------------------------------------------------------- /blog/static/css/syntax.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:DroidSansMonoRegular;src:local(☺), url(fonts/DroidSansMono-webfont.woff) format(woff), url(fonts/DroidSansMono-webfont.ttf) format(truetype), url(fonts/DroidSansMono-webfont.svg#webfont9N3UDjKi) format(svg);font-weight:400;font-style:normal;}div.codehilite pre{font:13px DroidSansMonoRegular, Monaco, "Courier New", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", monospace;letter-spacing:0;}div.codehilite .hll{background-color:#ffc;}div.codehilite{background:#3A3A3A;color:#D7D7AF;}div.codehilite .c{color:#87AF7A;font-style:italic;}div.codehilite .err{color:#a61717;background-color:#e3d2d2;}div.codehilite .o{color:#D7D7D7;font-weight:700;}div.codehilite .cp{color:#999;font-weight:700;}div.codehilite .cs{color:#999;font-weight:700;font-style:italic;}div.codehilite .gd{color:#000;background-color:#fdd;}div.codehilite .gd .x{color:#000;background-color:#faa;}div.codehilite .ge{color:#000;font-style:italic;}div.codehilite .gi{color:#000;background-color:#dfd;}div.codehilite .gi .x{color:#000;background-color:#afa;}div.codehilite .go{color:#888;}div.codehilite .gp{color:#555;}div.codehilite .gs{font-weight:700;}div.codehilite .gu{color:#aaa;}div.codehilite .kn{color:#D7D7AF;}div.codehilite .s{color:#D78787;}div.codehilite .nc{color:#458;font-weight:700;}div.codehilite .ni{color:purple;}div.codehilite .ne{color:#900;font-weight:700;}div.codehilite .nf{color:#FFFF87;font-weight:700;}div.codehilite .nt{color:#A7AA86;}div.codehilite .ow{color:#FFFFD1;font-weight:700;}div.codehilite .w{color:#bbb;}div.codehilite .mi{color:#87D7D7;}div.codehilite .sr{color:#009926;}div.codehilite .ss{color:#990073;}div.codehilite .k,div.codehilite .kt{color:#D7D7AF;font-weight:700;}div.codehilite .cm,div.codehilite .c1{color:#998;font-style:italic;}div.codehilite .gr,div.codehilite .gt{color:#a00;}div.codehilite .gh,div.codehilite .bp{color:#999;}div.codehilite .kc,div.codehilite .kd{color:#FFD7AF;font-weight:700;}div.codehilite .kp,div.codehilite .kr{color:#000;font-weight:700;}div.codehilite .m,div.codehilite .mf,div.codehilite .mh,div.codehilite .mo,div.codehilite .il{color:#099;}div.codehilite .na,div.codehilite .no,div.codehilite .nv,div.codehilite .vc,div.codehilite .vg,div.codehilite .vi{color:teal;}div.codehilite .nb,div.codehilite .nn{color:#D7D7D7;}div.codehilite .sb,div.codehilite .sc,div.codehilite .sd,div.codehilite .s2,div.codehilite .se,div.codehilite .sh,div.codehilite .si,div.codehilite .sx,div.codehilite .s1{color:#D77E64;} 2 | -------------------------------------------------------------------------------- /blog/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/favicon.ico -------------------------------------------------------------------------------- /blog/static/img/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_1.png -------------------------------------------------------------------------------- /blog/static/img/img_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_10.png -------------------------------------------------------------------------------- /blog/static/img/img_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_11.png -------------------------------------------------------------------------------- /blog/static/img/img_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_12.png -------------------------------------------------------------------------------- /blog/static/img/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_2.png -------------------------------------------------------------------------------- /blog/static/img/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_3.png -------------------------------------------------------------------------------- /blog/static/img/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_4.png -------------------------------------------------------------------------------- /blog/static/img/img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_5.png -------------------------------------------------------------------------------- /blog/static/img/img_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_6.png -------------------------------------------------------------------------------- /blog/static/img/img_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_7.png -------------------------------------------------------------------------------- /blog/static/img/img_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_8.png -------------------------------------------------------------------------------- /blog/static/img/img_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/blog/static/img/img_9.png -------------------------------------------------------------------------------- /blog/static/js/plugins.js: -------------------------------------------------------------------------------- 1 | 2 | // remap jQuery to $ 3 | (function($){ 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | })(window.jQuery); 16 | 17 | 18 | 19 | // usage: log('inside coolFunc',this,arguments); 20 | // paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ 21 | window.log = function(){ 22 | log.history = log.history || []; // store logs to an array for reference 23 | log.history.push(arguments); 24 | if(this.console){ 25 | console.log( Array.prototype.slice.call(arguments) ); 26 | } 27 | }; 28 | 29 | 30 | 31 | // catch all document.write() calls 32 | (function(doc){ 33 | var write = doc.write; 34 | doc.write = function(q){ 35 | log('document.write(): ',arguments); 36 | if (/docwriteregexwhitelist/.test(q)) write.apply(doc,arguments); 37 | }; 38 | })(document); 39 | 40 | 41 | -------------------------------------------------------------------------------- /blog/static/js/script.js: -------------------------------------------------------------------------------- 1 | /* Author: scroolose 2 | http://got-ravings.blogspot.com/2010/06/line-numbers-in-embedded-gists.html 3 | */ 4 | 5 | function addLineNumbersToAllGists() { 6 | $('.gist').each( function() { 7 | _addLineNumbersToGist('#' + $(this).attr('id')); 8 | }); 9 | } 10 | 11 | function addLineNumbersToGist(id) { 12 | _addLineNumbersToGist('#gist-' + id); 13 | } 14 | 15 | function _addLineNumbersToGist(css_selector) { 16 | $(document).ready( function() { 17 | $(css_selector + ' .line').each(function(i, e) { 18 | $(this).prepend( 19 | $('
').css({ 20 | 'float' : 'left', 21 | 'width': '30px', 22 | 'font-weight' : 'bold', 23 | 'color': '#808080' 24 | }).text(++i) 25 | ); 26 | }); 27 | }); 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /blog/static/pages/about.md: -------------------------------------------------------------------------------- 1 | About Me 2 | ======== 3 | 4 | My name is Gianluca Bargelli and i'm a software developer. 5 | 6 | I simply love to program because *coding* makes me (*almost*) feel happy. 7 | 8 | My interests as a developer ranges from *web development* to *desktop* and *mobile*: i still have to find my field, so i try to approach all that sort of things that tickles my curiosity. 9 | 10 | I live in a very small town near Perugia, Italy but i'm planning to move shortly since i'm looking for a job. 11 | 12 | Other things i **love** to do in my spare time are: 13 | 14 | * Read books; 15 | * Listen music; 16 | * Playing guitar and drums; 17 | * Take very long walks; 18 | * Enlarge my vinyl records collection. 19 | 20 | About this Website 21 | ================== 22 | 23 | This site is mainly used as a web log of my *coding* activies/experiments and related topics such as *programming languages*, *tools*, *algorithms* ecc. 24 | I'll try to write as much as possible even if i'm not a great *writer* so expect a lot of **awful**, **boring** and **useless** posts. I warned *you*. 25 | 26 | What runs behind? 27 | ================= 28 | This weblog was entirely built from scratch using [Flask][1], a very minimal python framework made by [Armin Ronacher][2]. 29 | 30 | It also *uses*: 31 | 32 | * Posts are parsed with [markdown-python][3]; 33 | * Code snippets highlighting using [Pygments][4]; 34 | * Comments are handled by [Disqus][5]; 35 | * HTML and CSS are powered by [HTML5 Boilerplate][12] 36 | 37 | The source code is *available* at [GitHub][6]. 38 | 39 | Contacts 40 | ======== 41 | 42 | You can contact me via [Twitter][7], [GitHub][8] or [Stack Overflow][9]. 43 | 44 | I'm also on [Last.fm][10] and [Anobii][11]. 45 | 46 | [1]: http://flask.pocoo.org 47 | [2]: http://lucumr.pocoo.org/ 48 | [3]: http://www.freewisdom.org/projects/python-markdown/ 49 | [4]: http://pygments.org/ 50 | [5]: http://disqus.com/ 51 | [6]: http://www.github.com/proudlygeek/proudlygeek-blog 52 | [7]: http://www.twitter.com/proudlygeek 53 | [8]: http://www.github.com/proudlygeek 54 | [9]: http://stackoverflow.com/users/243873/gianluca-bargelli 55 | [10]: http://www.lastfm.it/user/Bargio 56 | [11]: http://www.anobii.com/bargio/books 57 | [12]: http://html5boilerplate.com/ 58 | -------------------------------------------------------------------------------- /blog/templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block title %}About Me{% endblock %} 3 | {% block body %} 4 |
5 |
6 | {{entry.content}} 7 |
8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /blog/templates/add_entry.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block title %}Add a new entry{% endblock %} 3 | {% block header %} 4 | {% endblock %} 5 | {% block body %} 6 | {% if errors %} 7 |

8 | Error: 9 |

88 | 99 | {% endblock %} 100 | -------------------------------------------------------------------------------- /blog/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block title %}Login{% endblock %} 3 | {% block header %} 4 | {% endblock %} 5 | {% block body %} 6 |

Please login

7 | {% if error %}

Error: {{ error }}{% endif %} 8 |

9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 | {% endblock %} 19 | {% block footer %}{% endblock %} 20 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- 1 | class Config(object): 2 | PLATFORM = 'sqlite' 3 | DATABASE = '/tmp/blog.db' 4 | DEBUG = False 5 | TESTING = False 6 | SECRET_KEY = 'development key' 7 | MAX_PAGE_ENTRIES = 5 8 | 9 | class ProductionConfig(Config): 10 | DATABASE_URI = 'mysql://user@localhost/foo' 11 | 12 | class DevelopmentConfig(Config): 13 | DEBUG = True 14 | 15 | class TestinConfig(Config): 16 | TESTING = True 17 | -------------------------------------------------------------------------------- /flask/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask 4 | ~~~~~ 5 | 6 | A microframework based on Werkzeug. It's extensively documented 7 | and follows best practice patterns. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | # utilities we import from Werkzeug and Jinja2 that are unused 14 | # in the module but are exported as public interface. 15 | from werkzeug import abort, redirect 16 | from jinja2 import Markup, escape 17 | 18 | from .app import Flask, Request, Response 19 | from .config import Config 20 | from .helpers import url_for, jsonify, json_available, flash, \ 21 | send_file, send_from_directory, get_flashed_messages, \ 22 | get_template_attribute, make_response 23 | from .globals import current_app, g, request, session, _request_ctx_stack 24 | from .module import Module 25 | from .templating import render_template, render_template_string 26 | from .session import Session 27 | 28 | # the signals 29 | from .signals import signals_available, template_rendered, request_started, \ 30 | request_finished, got_request_exception 31 | 32 | # only import json if it's available 33 | if json_available: 34 | from .helpers import json 35 | -------------------------------------------------------------------------------- /flask/ctx.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.ctx 4 | ~~~~~~~~~ 5 | 6 | Implements the objects required to keep the context. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from werkzeug.exceptions import HTTPException 13 | 14 | from .globals import _request_ctx_stack 15 | from .session import _NullSession 16 | 17 | 18 | class _RequestGlobals(object): 19 | pass 20 | 21 | 22 | class _RequestContext(object): 23 | """The request context contains all request relevant information. It is 24 | created at the beginning of the request and pushed to the 25 | `_request_ctx_stack` and removed at the end of it. It will create the 26 | URL adapter and request object for the WSGI environment provided. 27 | """ 28 | 29 | def __init__(self, app, environ): 30 | self.app = app 31 | self.request = app.request_class(environ) 32 | self.url_adapter = app.create_url_adapter(self.request) 33 | self.session = app.open_session(self.request) 34 | if self.session is None: 35 | self.session = _NullSession() 36 | self.g = _RequestGlobals() 37 | self.flashes = None 38 | 39 | try: 40 | url_rule, self.request.view_args = \ 41 | self.url_adapter.match(return_rule=True) 42 | self.request.url_rule = url_rule 43 | except HTTPException, e: 44 | self.request.routing_exception = e 45 | 46 | def push(self): 47 | """Binds the request context.""" 48 | _request_ctx_stack.push(self) 49 | 50 | def pop(self): 51 | """Pops the request context.""" 52 | _request_ctx_stack.pop() 53 | 54 | def __enter__(self): 55 | self.push() 56 | return self 57 | 58 | def __exit__(self, exc_type, exc_value, tb): 59 | # do not pop the request stack if we are in debug mode and an 60 | # exception happened. This will allow the debugger to still 61 | # access the request object in the interactive shell. Furthermore 62 | # the context can be force kept alive for the test client. 63 | # See flask.testing for how this works. 64 | if not self.request.environ.get('flask._preserve_context') and \ 65 | (tb is None or not self.app.debug): 66 | self.pop() 67 | -------------------------------------------------------------------------------- /flask/globals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.globals 4 | ~~~~~~~~~~~~~ 5 | 6 | Defines all the global objects that are proxies to the current 7 | active context. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from werkzeug import LocalStack, LocalProxy 14 | 15 | # context locals 16 | _request_ctx_stack = LocalStack() 17 | current_app = LocalProxy(lambda: _request_ctx_stack.top.app) 18 | request = LocalProxy(lambda: _request_ctx_stack.top.request) 19 | session = LocalProxy(lambda: _request_ctx_stack.top.session) 20 | g = LocalProxy(lambda: _request_ctx_stack.top.g) 21 | -------------------------------------------------------------------------------- /flask/logging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.logging 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements the logging support for Flask. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from __future__ import absolute_import 13 | 14 | from logging import getLogger, StreamHandler, Formatter, Logger, DEBUG 15 | 16 | 17 | def create_logger(app): 18 | """Creates a logger for the given application. This logger works 19 | similar to a regular Python logger but changes the effective logging 20 | level based on the application's debug flag. Furthermore this 21 | function also removes all attached handlers in case there was a 22 | logger with the log name before. 23 | """ 24 | 25 | class DebugLogger(Logger): 26 | def getEffectiveLevel(x): 27 | return DEBUG if app.debug else Logger.getEffectiveLevel(x) 28 | 29 | class DebugHandler(StreamHandler): 30 | def emit(x, record): 31 | StreamHandler.emit(x, record) if app.debug else None 32 | 33 | handler = DebugHandler() 34 | handler.setLevel(DEBUG) 35 | handler.setFormatter(Formatter(app.debug_log_format)) 36 | logger = getLogger(app.logger_name) 37 | # just in case that was not a new logger, get rid of all the handlers 38 | # already attached to it. 39 | del logger.handlers[:] 40 | logger.__class__ = DebugLogger 41 | logger.addHandler(handler) 42 | return logger 43 | -------------------------------------------------------------------------------- /flask/session.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.session 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements cookie based sessions based on Werkzeug's secure cookie 7 | system. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from werkzeug.contrib.securecookie import SecureCookie 14 | 15 | 16 | class Session(SecureCookie): 17 | """Expands the session with support for switching between permanent 18 | and non-permanent sessions. 19 | """ 20 | 21 | def _get_permanent(self): 22 | return self.get('_permanent', False) 23 | 24 | def _set_permanent(self, value): 25 | self['_permanent'] = bool(value) 26 | 27 | permanent = property(_get_permanent, _set_permanent) 28 | del _get_permanent, _set_permanent 29 | 30 | 31 | class _NullSession(Session): 32 | """Class used to generate nicer error messages if sessions are not 33 | available. Will still allow read-only access to the empty session 34 | but fail on setting. 35 | """ 36 | 37 | def _fail(self, *args, **kwargs): 38 | raise RuntimeError('the session is unavailable because no secret ' 39 | 'key was set. Set the secret_key on the ' 40 | 'application to something unique and secret.') 41 | __setitem__ = __delitem__ = clear = pop = popitem = \ 42 | update = setdefault = _fail 43 | del _fail 44 | -------------------------------------------------------------------------------- /flask/signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.signals 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements signals based on blinker if available, otherwise 7 | falls silently back to a noop 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | signals_available = False 13 | try: 14 | from blinker import Namespace 15 | signals_available = True 16 | except ImportError: 17 | class Namespace(object): 18 | def signal(self, name, doc=None): 19 | return _FakeSignal(name, doc) 20 | 21 | class _FakeSignal(object): 22 | """If blinker is unavailable, create a fake class with the same 23 | interface that allows sending of signals but will fail with an 24 | error on anything else. Instead of doing anything on send, it 25 | will just ignore the arguments and do nothing instead. 26 | """ 27 | 28 | def __init__(self, name, doc=None): 29 | self.name = name 30 | self.__doc__ = doc 31 | def _fail(self, *args, **kwargs): 32 | raise RuntimeError('signalling support is unavailable ' 33 | 'because the blinker library is ' 34 | 'not installed.') 35 | send = lambda *a, **kw: None 36 | connect = disconnect = has_receivers_for = receivers_for = \ 37 | temporarily_connected_to = _fail 38 | del _fail 39 | 40 | # the namespace for code signals. If you are not flask code, do 41 | # not put signals in here. Create your own namespace instead. 42 | _signals = Namespace() 43 | 44 | 45 | # core signals. For usage examples grep the sourcecode or consult 46 | # the API documentation in docs/api.rst as well as docs/signals.rst 47 | template_rendered = _signals.signal('template-rendered') 48 | request_started = _signals.signal('request-started') 49 | request_finished = _signals.signal('request-finished') 50 | got_request_exception = _signals.signal('got-request-exception') 51 | -------------------------------------------------------------------------------- /flask/templating.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.templating 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | Implements the bridge to Jinja2. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from jinja2 import BaseLoader, TemplateNotFound 12 | 13 | from .globals import _request_ctx_stack 14 | from .signals import template_rendered 15 | 16 | 17 | def _default_template_ctx_processor(): 18 | """Default template context processor. Injects `request`, 19 | `session` and `g`. 20 | """ 21 | reqctx = _request_ctx_stack.top 22 | return dict( 23 | config=reqctx.app.config, 24 | request=reqctx.request, 25 | session=reqctx.session, 26 | g=reqctx.g 27 | ) 28 | 29 | 30 | class _DispatchingJinjaLoader(BaseLoader): 31 | """A loader that looks for templates in the application and all 32 | the module folders. 33 | """ 34 | 35 | def __init__(self, app): 36 | self.app = app 37 | 38 | def get_source(self, environment, template): 39 | loader = None 40 | try: 41 | module, name = template.split('/', 1) 42 | loader = self.app.modules[module].jinja_loader 43 | except (ValueError, KeyError): 44 | pass 45 | # if there was a module and it has a loader, try this first 46 | if loader is not None: 47 | try: 48 | return loader.get_source(environment, name) 49 | except TemplateNotFound: 50 | pass 51 | # fall back to application loader if module failed 52 | return self.app.jinja_loader.get_source(environment, template) 53 | 54 | def list_templates(self): 55 | result = self.app.jinja_loader.list_templates() 56 | for name, module in self.app.modules.iteritems(): 57 | if module.jinja_loader is not None: 58 | for template in module.jinja_loader.list_templates(): 59 | result.append('%s/%s' % (name, template)) 60 | return result 61 | 62 | 63 | def _render(template, context, app): 64 | """Renders the template and fires the signal""" 65 | rv = template.render(context) 66 | template_rendered.send(app, template=template, context=context) 67 | return rv 68 | 69 | 70 | def render_template(template_name, **context): 71 | """Renders a template from the template folder with the given 72 | context. 73 | 74 | :param template_name: the name of the template to be rendered 75 | :param context: the variables that should be available in the 76 | context of the template. 77 | """ 78 | ctx = _request_ctx_stack.top 79 | ctx.app.update_template_context(context) 80 | return _render(ctx.app.jinja_env.get_template(template_name), 81 | context, ctx.app) 82 | 83 | 84 | def render_template_string(source, **context): 85 | """Renders a template from the given template source string 86 | with the given context. 87 | 88 | :param template_name: the sourcecode of the template to be 89 | rendered 90 | :param context: the variables that should be available in the 91 | context of the template. 92 | """ 93 | ctx = _request_ctx_stack.top 94 | ctx.app.update_template_context(context) 95 | return _render(ctx.app.jinja_env.from_string(source), 96 | context, ctx.app) 97 | -------------------------------------------------------------------------------- /flask/testing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.testing 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements test support helpers. This module is lazily imported 7 | and usually not used in production environments. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from werkzeug import Client 14 | from flask import _request_ctx_stack 15 | 16 | 17 | class FlaskClient(Client): 18 | """Works like a regular Werkzeug test client but has some 19 | knowledge about how Flask works to defer the cleanup of the 20 | request context stack to the end of a with body when used 21 | in a with statement. 22 | """ 23 | 24 | preserve_context = context_preserved = False 25 | 26 | def open(self, *args, **kwargs): 27 | if self.context_preserved: 28 | _request_ctx_stack.pop() 29 | self.context_preserved = False 30 | kwargs.setdefault('environ_overrides', {}) \ 31 | ['flask._preserve_context'] = self.preserve_context 32 | old = _request_ctx_stack.top 33 | try: 34 | return Client.open(self, *args, **kwargs) 35 | finally: 36 | self.context_preserved = _request_ctx_stack.top is not old 37 | 38 | def __enter__(self): 39 | self.preserve_context = True 40 | return self 41 | 42 | def __exit__(self, exc_type, exc_value, tb): 43 | self.preserve_context = False 44 | if self.context_preserved: 45 | _request_ctx_stack.pop() 46 | -------------------------------------------------------------------------------- /flask/wrappers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.wrappers 4 | ~~~~~~~~~~~~~~ 5 | 6 | Implements the WSGI wrappers (request and response). 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from werkzeug import Request as RequestBase, Response as ResponseBase, \ 13 | cached_property 14 | 15 | from .helpers import json, _assert_have_json 16 | from .globals import _request_ctx_stack 17 | 18 | 19 | class Request(RequestBase): 20 | """The request object used by default in flask. Remembers the 21 | matched endpoint and view arguments. 22 | 23 | It is what ends up as :class:`~flask.request`. If you want to replace 24 | the request object used you can subclass this and set 25 | :attr:`~flask.Flask.request_class` to your subclass. 26 | """ 27 | 28 | #: the internal URL rule that matched the request. This can be 29 | #: useful to inspect which methods are allowed for the URL from 30 | #: a before/after handler (``request.url_rule.methods``) etc. 31 | #: 32 | #: .. versionadded:: 0.6 33 | url_rule = None 34 | 35 | #: a dict of view arguments that matched the request. If an exception 36 | #: happened when matching, this will be `None`. 37 | view_args = None 38 | 39 | #: if matching the URL failed, this is the exception that will be 40 | #: raised / was raised as part of the request handling. This is 41 | #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or 42 | #: something similar. 43 | routing_exception = None 44 | 45 | @property 46 | def max_content_length(self): 47 | """Read-only view of the `MAX_CONTENT_LENGTH` config key.""" 48 | ctx = _request_ctx_stack.top 49 | if ctx is not None: 50 | return ctx.app.config['MAX_CONTENT_LENGTH'] 51 | 52 | @property 53 | def endpoint(self): 54 | """The endpoint that matched the request. This in combination with 55 | :attr:`view_args` can be used to reconstruct the same or a 56 | modified URL. If an exception happened when matching, this will 57 | be `None`. 58 | """ 59 | if self.url_rule is not None: 60 | return self.url_rule.endpoint 61 | 62 | @property 63 | def module(self): 64 | """The name of the current module""" 65 | if self.url_rule and '.' in self.url_rule.endpoint: 66 | return self.url_rule.endpoint.rsplit('.', 1)[0] 67 | 68 | @cached_property 69 | def json(self): 70 | """If the mimetype is `application/json` this will contain the 71 | parsed JSON data. 72 | """ 73 | if __debug__: 74 | _assert_have_json() 75 | if self.mimetype == 'application/json': 76 | return json.loads(self.data) 77 | 78 | 79 | class Response(ResponseBase): 80 | """The response object that is used by default in flask. Works like the 81 | response object from Werkzeug but is set to have a HTML mimetype by 82 | default. Quite often you don't have to create this object yourself because 83 | :meth:`~flask.Flask.make_response` will take care of that for you. 84 | 85 | If you want to replace the response object used you can subclass this and 86 | set :attr:`~flask.Flask.response_class` to your subclass. 87 | """ 88 | default_mimetype = 'text/html' 89 | -------------------------------------------------------------------------------- /index.yaml: -------------------------------------------------------------------------------- 1 | indexes: 2 | 3 | - kind: Entry 4 | properties: 5 | - name: tags 6 | - name: creation_date 7 | direction: desc 8 | 9 | # AUTOGENERATED 10 | 11 | # This index.yaml is automatically updated whenever the dev_appserver 12 | # detects that a new type of query is run. If you want to manage the 13 | # index.yaml file manually, remove the above marker line (the line 14 | # saying "# AUTOGENERATED"). If you want to manage some indexes 15 | # manually, move them above the marker line. The index.yaml file is 16 | # automatically uploaded to the admin console when you next deploy 17 | # your application using appcfg.py. 18 | -------------------------------------------------------------------------------- /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: 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/jinja2/testsuite/res/__init__.py -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/broken.html: -------------------------------------------------------------------------------- 1 | Before 2 | {{ fail() }} 3 | After 4 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/foo/test.html: -------------------------------------------------------------------------------- 1 | FOO 2 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/syntaxerror.html: -------------------------------------------------------------------------------- 1 | Foo 2 | {% for item in broken %} 3 | ... 4 | {% endif %} 5 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/test.html: -------------------------------------------------------------------------------- 1 | BAR 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 os 12 | import gc 13 | import unittest 14 | 15 | import pickle 16 | 17 | from jinja2.testsuite import JinjaTestCase 18 | 19 | from jinja2 import Environment, Undefined, DebugUndefined, \ 20 | StrictUndefined, UndefinedError, Template, meta 21 | from jinja2.utils import LRUCache, escape, object_type_repr 22 | 23 | 24 | class LRUCacheTestCase(JinjaTestCase): 25 | 26 | def test_simple(self): 27 | d = LRUCache(3) 28 | d["a"] = 1 29 | d["b"] = 2 30 | d["c"] = 3 31 | d["a"] 32 | d["d"] = 4 33 | assert len(d) == 3 34 | assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d 35 | 36 | def test_pickleable(self): 37 | cache = LRUCache(2) 38 | cache["foo"] = 42 39 | cache["bar"] = 23 40 | cache["foo"] 41 | 42 | for protocol in range(3): 43 | copy = pickle.loads(pickle.dumps(cache, protocol)) 44 | assert copy.capacity == cache.capacity 45 | assert copy._mapping == cache._mapping 46 | assert copy._queue == cache._queue 47 | 48 | 49 | class HelpersTestCase(JinjaTestCase): 50 | 51 | def test_object_type_repr(self): 52 | class X(object): 53 | pass 54 | self.assert_equal(object_type_repr(42), 'int object') 55 | self.assert_equal(object_type_repr([]), 'list object') 56 | self.assert_equal(object_type_repr(X()), 57 | 'jinja2.testsuite.utils.X object') 58 | self.assert_equal(object_type_repr(None), 'None') 59 | self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis') 60 | 61 | 62 | class MarkupLeakTestCase(JinjaTestCase): 63 | 64 | def test_markup_leaks(self): 65 | counts = set() 66 | for count in xrange(20): 67 | for item in xrange(1000): 68 | escape("foo") 69 | escape("") 70 | escape(u"foo") 71 | escape(u"") 72 | counts.add(len(gc.get_objects())) 73 | assert len(counts) == 1, 'ouch, c extension seems to leak objects' 74 | 75 | 76 | def suite(): 77 | suite = unittest.TestSuite() 78 | suite.addTest(unittest.makeSuite(LRUCacheTestCase)) 79 | suite.addTest(unittest.makeSuite(HelpersTestCase)) 80 | 81 | # this test only tests the c extension 82 | if not hasattr(escape, 'func_code'): 83 | suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) 84 | 85 | return suite 86 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from blog.views import app 3 | 4 | def main(): 5 | if app.config['PLATFORM']=='sqlite': 6 | try: 7 | import sqlite3 8 | except: 9 | raise NameError("Sqlite3 module not found.") 10 | 11 | app.run() 12 | 13 | elif app.config['PLATFORM']=='gae': 14 | try: 15 | from google.appengine.ext.webapp.util import run_wsgi_app 16 | except: 17 | raise NameError ("Google App Engine SDK module not found.") 18 | 19 | run_wsgi_app(app) 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /markdown/blockparser.py: -------------------------------------------------------------------------------- 1 | 2 | import markdown 3 | 4 | class State(list): 5 | """ Track the current and nested state of the parser. 6 | 7 | This utility class is used to track the state of the BlockParser and 8 | support multiple levels if nesting. It's just a simple API wrapped around 9 | a list. Each time a state is set, that state is appended to the end of the 10 | list. Each time a state is reset, that state is removed from the end of 11 | the list. 12 | 13 | Therefore, each time a state is set for a nested block, that state must be 14 | reset when we back out of that level of nesting or the state could be 15 | corrupted. 16 | 17 | While all the methods of a list object are available, only the three 18 | defined below need be used. 19 | 20 | """ 21 | 22 | def set(self, state): 23 | """ Set a new state. """ 24 | self.append(state) 25 | 26 | def reset(self): 27 | """ Step back one step in nested state. """ 28 | self.pop() 29 | 30 | def isstate(self, state): 31 | """ Test that top (current) level is of given state. """ 32 | if len(self): 33 | return self[-1] == state 34 | else: 35 | return False 36 | 37 | class BlockParser: 38 | """ Parse Markdown blocks into an ElementTree object. 39 | 40 | A wrapper class that stitches the various BlockProcessors together, 41 | looping through them and creating an ElementTree object. 42 | """ 43 | 44 | def __init__(self): 45 | self.blockprocessors = markdown.odict.OrderedDict() 46 | self.state = State() 47 | 48 | def parseDocument(self, lines): 49 | """ Parse a markdown document into an ElementTree. 50 | 51 | Given a list of lines, an ElementTree object (not just a parent Element) 52 | is created and the root element is passed to the parser as the parent. 53 | The ElementTree object is returned. 54 | 55 | This should only be called on an entire document, not pieces. 56 | 57 | """ 58 | # Create a ElementTree from the lines 59 | self.root = markdown.etree.Element(markdown.DOC_TAG) 60 | self.parseChunk(self.root, '\n'.join(lines)) 61 | return markdown.etree.ElementTree(self.root) 62 | 63 | def parseChunk(self, parent, text): 64 | """ Parse a chunk of markdown text and attach to given etree node. 65 | 66 | While the ``text`` argument is generally assumed to contain multiple 67 | blocks which will be split on blank lines, it could contain only one 68 | block. Generally, this method would be called by extensions when 69 | block parsing is required. 70 | 71 | The ``parent`` etree Element passed in is altered in place. 72 | Nothing is returned. 73 | 74 | """ 75 | self.parseBlocks(parent, text.split('\n\n')) 76 | 77 | def parseBlocks(self, parent, blocks): 78 | """ Process blocks of markdown text and attach to given etree node. 79 | 80 | Given a list of ``blocks``, each blockprocessor is stepped through 81 | until there are no blocks left. While an extension could potentially 82 | call this method directly, it's generally expected to be used internally. 83 | 84 | This is a public method as an extension may need to add/alter additional 85 | BlockProcessors which call this method to recursively parse a nested 86 | block. 87 | 88 | """ 89 | while blocks: 90 | for processor in self.blockprocessors.values(): 91 | if processor.test(parent, blocks[0]): 92 | processor.run(parent, blocks) 93 | break 94 | 95 | 96 | -------------------------------------------------------------------------------- /markdown/commandline.py: -------------------------------------------------------------------------------- 1 | """ 2 | COMMAND-LINE SPECIFIC STUFF 3 | ============================================================================= 4 | 5 | The rest of the code is specifically for handling the case where Python 6 | Markdown is called from the command line. 7 | """ 8 | 9 | import markdown 10 | import sys 11 | import logging 12 | from logging import DEBUG, INFO, WARN, ERROR, CRITICAL 13 | 14 | EXECUTABLE_NAME_FOR_USAGE = "python markdown.py" 15 | """ The name used in the usage statement displayed for python versions < 2.3. 16 | (With python 2.3 and higher the usage statement is generated by optparse 17 | and uses the actual name of the executable called.) """ 18 | 19 | OPTPARSE_WARNING = """ 20 | Python 2.3 or higher required for advanced command line options. 21 | For lower versions of Python use: 22 | 23 | %s INPUT_FILE > OUTPUT_FILE 24 | 25 | """ % EXECUTABLE_NAME_FOR_USAGE 26 | 27 | def parse_options(): 28 | """ 29 | Define and parse `optparse` options for command-line usage. 30 | """ 31 | 32 | try: 33 | optparse = __import__("optparse") 34 | except: 35 | if len(sys.argv) == 2: 36 | return {'input': sys.argv[1], 37 | 'output': None, 38 | 'safe': False, 39 | 'extensions': [], 40 | 'encoding': None }, CRITICAL 41 | else: 42 | print OPTPARSE_WARNING 43 | return None, None 44 | 45 | parser = optparse.OptionParser(usage="%prog INPUTFILE [options]") 46 | parser.add_option("-f", "--file", dest="filename", default=sys.stdout, 47 | help="write output to OUTPUT_FILE", 48 | metavar="OUTPUT_FILE") 49 | parser.add_option("-e", "--encoding", dest="encoding", 50 | help="encoding for input and output files",) 51 | parser.add_option("-q", "--quiet", default = CRITICAL, 52 | action="store_const", const=CRITICAL+10, dest="verbose", 53 | help="suppress all messages") 54 | parser.add_option("-v", "--verbose", 55 | action="store_const", const=INFO, dest="verbose", 56 | help="print info messages") 57 | parser.add_option("-s", "--safe", dest="safe", default=False, 58 | metavar="SAFE_MODE", 59 | help="safe mode ('replace', 'remove' or 'escape' user's HTML tag)") 60 | parser.add_option("-o", "--output_format", dest="output_format", 61 | default='xhtml1', metavar="OUTPUT_FORMAT", 62 | help="Format of output. One of 'xhtml1' (default) or 'html4'.") 63 | parser.add_option("--noisy", 64 | action="store_const", const=DEBUG, dest="verbose", 65 | help="print debug messages") 66 | parser.add_option("-x", "--extension", action="append", dest="extensions", 67 | help = "load extension EXTENSION", metavar="EXTENSION") 68 | 69 | (options, args) = parser.parse_args() 70 | 71 | if not len(args) == 1: 72 | parser.print_help() 73 | return None, None 74 | else: 75 | input_file = args[0] 76 | 77 | if not options.extensions: 78 | options.extensions = [] 79 | 80 | return {'input': input_file, 81 | 'output': options.filename, 82 | 'safe_mode': options.safe, 83 | 'extensions': options.extensions, 84 | 'encoding': options.encoding, 85 | 'output_format': options.output_format}, options.verbose 86 | 87 | def run(): 88 | """Run Markdown from the command line.""" 89 | 90 | # Parse options and adjust logging level if necessary 91 | options, logging_level = parse_options() 92 | if not options: sys.exit(0) 93 | if logging_level: logging.getLogger('MARKDOWN').setLevel(logging_level) 94 | 95 | # Run 96 | markdown.markdownFromFile(**options) 97 | -------------------------------------------------------------------------------- /markdown/etree_loader.py: -------------------------------------------------------------------------------- 1 | 2 | from markdown import message, CRITICAL 3 | import sys 4 | 5 | ## Import 6 | def importETree(): 7 | """Import the best implementation of ElementTree, return a module object.""" 8 | etree_in_c = None 9 | try: # Is it Python 2.5+ with C implemenation of ElementTree installed? 10 | import xml.etree.cElementTree as etree_in_c 11 | except ImportError: 12 | try: # Is it Python 2.5+ with Python implementation of ElementTree? 13 | import xml.etree.ElementTree as etree 14 | except ImportError: 15 | try: # An earlier version of Python with cElementTree installed? 16 | import cElementTree as etree_in_c 17 | except ImportError: 18 | try: # An earlier version of Python with Python ElementTree? 19 | import elementtree.ElementTree as etree 20 | except ImportError: 21 | message(CRITICAL, "Failed to import ElementTree") 22 | sys.exit(1) 23 | if etree_in_c and etree_in_c.VERSION < "1.0": 24 | message(CRITICAL, "For cElementTree version 1.0 or higher is required.") 25 | sys.exit(1) 26 | elif etree_in_c : 27 | return etree_in_c 28 | elif etree.VERSION < "1.1": 29 | message(CRITICAL, "For ElementTree version 1.1 or higher is required") 30 | sys.exit(1) 31 | else : 32 | return etree 33 | 34 | -------------------------------------------------------------------------------- /markdown/extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/markdown/extensions/__init__.py -------------------------------------------------------------------------------- /markdown/extensions/abbr.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Abbreviation Extension for Python-Markdown 3 | ========================================== 4 | 5 | This extension adds abbreviation handling to Python-Markdown. 6 | 7 | Simple Usage: 8 | 9 | >>> import markdown 10 | >>> text = """ 11 | ... Some text with an ABBR and a REF. Ignore REFERENCE and ref. 12 | ... 13 | ... *[ABBR]: Abbreviation 14 | ... *[REF]: Abbreviation Reference 15 | ... """ 16 | >>> markdown.markdown(text, ['abbr']) 17 | u'

Some text with an ABBR and a REF. Ignore REFERENCE and ref.

' 18 | 19 | Copyright 2007-2008 20 | * [Waylan Limberg](http://achinghead.com/) 21 | * [Seemant Kulleen](http://www.kulleen.org/) 22 | 23 | 24 | ''' 25 | 26 | import markdown, re 27 | from markdown import etree 28 | 29 | # Global Vars 30 | ABBR_REF_RE = re.compile(r'[*]\[(?P[^\]]*)\][ ]?:\s*(?P.*)') 31 | 32 | class AbbrExtension(markdown.Extension): 33 | """ Abbreviation Extension for Python-Markdown. """ 34 | 35 | def extendMarkdown(self, md, md_globals): 36 | """ Insert AbbrPreprocessor before ReferencePreprocessor. """ 37 | md.preprocessors.add('abbr', AbbrPreprocessor(md), '<reference') 38 | 39 | 40 | class AbbrPreprocessor(markdown.preprocessors.Preprocessor): 41 | """ Abbreviation Preprocessor - parse text for abbr references. """ 42 | 43 | def run(self, lines): 44 | ''' 45 | Find and remove all Abbreviation references from the text. 46 | Each reference is set as a new AbbrPattern in the markdown instance. 47 | 48 | ''' 49 | new_text = [] 50 | for line in lines: 51 | m = ABBR_REF_RE.match(line) 52 | if m: 53 | abbr = m.group('abbr').strip() 54 | title = m.group('title').strip() 55 | self.markdown.inlinePatterns['abbr-%s'%abbr] = \ 56 | AbbrPattern(self._generate_pattern(abbr), title) 57 | else: 58 | new_text.append(line) 59 | return new_text 60 | 61 | def _generate_pattern(self, text): 62 | ''' 63 | Given a string, returns an regex pattern to match that string. 64 | 65 | 'HTML' -> r'(?P<abbr>[H][T][M][L])' 66 | 67 | Note: we force each char as a literal match (in brackets) as we don't 68 | know what they will be beforehand. 69 | 70 | ''' 71 | chars = list(text) 72 | for i in range(len(chars)): 73 | chars[i] = r'[%s]' % chars[i] 74 | return r'(?P<abbr>\b%s\b)' % (r''.join(chars)) 75 | 76 | 77 | class AbbrPattern(markdown.inlinepatterns.Pattern): 78 | """ Abbreviation inline pattern. """ 79 | 80 | def __init__(self, pattern, title): 81 | markdown.inlinepatterns.Pattern.__init__(self, pattern) 82 | self.title = title 83 | 84 | def handleMatch(self, m): 85 | abbr = etree.Element('abbr') 86 | abbr.text = m.group('abbr') 87 | abbr.set('title', self.title) 88 | return abbr 89 | 90 | def makeExtension(configs=None): 91 | return AbbrExtension(configs=configs) 92 | 93 | if __name__ == "__main__": 94 | import doctest 95 | doctest.testmod() 96 | -------------------------------------------------------------------------------- /markdown/extensions/def_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Python 2 | """ 3 | Definition List Extension for Python-Markdown 4 | ============================================= 5 | 6 | Added parsing of Definition Lists to Python-Markdown. 7 | 8 | A simple example: 9 | 10 | Apple 11 | : Pomaceous fruit of plants of the genus Malus in 12 | the family Rosaceae. 13 | : An american computer company. 14 | 15 | Orange 16 | : The fruit of an evergreen tree of the genus Citrus. 17 | 18 | Copyright 2008 - [Waylan Limberg](http://achinghead.com) 19 | 20 | """ 21 | 22 | import markdown, re 23 | from markdown import etree 24 | 25 | 26 | class DefListProcessor(markdown.blockprocessors.BlockProcessor): 27 | """ Process Definition Lists. """ 28 | 29 | RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') 30 | 31 | def test(self, parent, block): 32 | return bool(self.RE.search(block)) 33 | 34 | def run(self, parent, blocks): 35 | block = blocks.pop(0) 36 | m = self.RE.search(block) 37 | terms = [l.strip() for l in block[:m.start()].split('\n') if l.strip()] 38 | d, theRest = self.detab(block[m.end():]) 39 | if d: 40 | d = '%s\n%s' % (m.group(2), d) 41 | else: 42 | d = m.group(2) 43 | #import ipdb; ipdb.set_trace() 44 | sibling = self.lastChild(parent) 45 | if not terms and sibling.tag == 'p': 46 | # The previous paragraph contains the terms 47 | state = 'looselist' 48 | terms = sibling.text.split('\n') 49 | parent.remove(sibling) 50 | # Aquire new sibling 51 | sibling = self.lastChild(parent) 52 | else: 53 | state = 'list' 54 | 55 | if sibling and sibling.tag == 'dl': 56 | # This is another item on an existing list 57 | dl = sibling 58 | if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): 59 | state = 'looselist' 60 | else: 61 | # This is a new list 62 | dl = etree.SubElement(parent, 'dl') 63 | # Add terms 64 | for term in terms: 65 | dt = etree.SubElement(dl, 'dt') 66 | dt.text = term 67 | # Add definition 68 | self.parser.state.set(state) 69 | dd = etree.SubElement(dl, 'dd') 70 | self.parser.parseBlocks(dd, [d]) 71 | self.parser.state.reset() 72 | 73 | if theRest: 74 | blocks.insert(0, theRest) 75 | 76 | class DefListIndentProcessor(markdown.blockprocessors.ListIndentProcessor): 77 | """ Process indented children of definition list items. """ 78 | 79 | ITEM_TYPES = ['dd'] 80 | LIST_TYPES = ['dl'] 81 | 82 | def create_item(parent, block): 83 | """ Create a new dd and parse the block with it as the parent. """ 84 | dd = markdown.etree.SubElement(parent, 'dd') 85 | self.parser.parseBlocks(dd, [block]) 86 | 87 | 88 | 89 | class DefListExtension(markdown.Extension): 90 | """ Add definition lists to Markdown. """ 91 | 92 | def extendMarkdown(self, md, md_globals): 93 | """ Add an instance of DefListProcessor to BlockParser. """ 94 | md.parser.blockprocessors.add('defindent', 95 | DefListIndentProcessor(md.parser), 96 | '>indent') 97 | md.parser.blockprocessors.add('deflist', 98 | DefListProcessor(md.parser), 99 | '>ulist') 100 | 101 | 102 | def makeExtension(configs={}): 103 | return DefListExtension(configs=configs) 104 | 105 | -------------------------------------------------------------------------------- /markdown/extensions/extra.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Python-Markdown Extra Extension 4 | =============================== 5 | 6 | A compilation of various Python-Markdown extensions that imitates 7 | [PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/). 8 | 9 | Note that each of the individual extensions still need to be available 10 | on your PYTHONPATH. This extension simply wraps them all up as a 11 | convenience so that only one extension needs to be listed when 12 | initiating Markdown. See the documentation for each individual 13 | extension for specifics about that extension. 14 | 15 | In the event that one or more of the supported extensions are not 16 | available for import, Markdown will issue a warning and simply continue 17 | without that extension. 18 | 19 | There may be additional extensions that are distributed with 20 | Python-Markdown that are not included here in Extra. Those extensions 21 | are not part of PHP Markdown Extra, and therefore, not part of 22 | Python-Markdown Extra. If you really would like Extra to include 23 | additional extensions, we suggest creating your own clone of Extra 24 | under a differant name. You could also edit the `extensions` global 25 | variable defined below, but be aware that such changes may be lost 26 | when you upgrade to any future version of Python-Markdown. 27 | 28 | """ 29 | 30 | import markdown 31 | 32 | extensions = ['fenced_code', 33 | 'footnotes', 34 | 'headerid', 35 | 'def_list', 36 | 'tables', 37 | 'abbr', 38 | ] 39 | 40 | 41 | class ExtraExtension(markdown.Extension): 42 | """ Add various extensions to Markdown class.""" 43 | 44 | def extendMarkdown(self, md, md_globals): 45 | """ Register extension instances. """ 46 | md.registerExtensions(extensions, self.config) 47 | 48 | def makeExtension(configs={}): 49 | return ExtraExtension(configs=dict(configs)) 50 | -------------------------------------------------------------------------------- /markdown/extensions/fenced_code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Fenced Code Extension for Python Markdown 5 | ========================================= 6 | 7 | This extension adds Fenced Code Blocks to Python-Markdown. 8 | 9 | >>> import markdown 10 | >>> text = ''' 11 | ... A paragraph before a fenced code block: 12 | ... 13 | ... ~~~ 14 | ... Fenced code block 15 | ... ~~~ 16 | ... ''' 17 | >>> html = markdown.markdown(text, extensions=['fenced_code']) 18 | >>> html 19 | u'<p>A paragraph before a fenced code block:</p>\\n<pre><code>Fenced code block\\n</code></pre>' 20 | 21 | Works with safe_mode also (we check this because we are using the HtmlStash): 22 | 23 | >>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace') 24 | u'<p>A paragraph before a fenced code block:</p>\\n<pre><code>Fenced code block\\n</code></pre>' 25 | 26 | Include tilde's in a code block and wrap with blank lines: 27 | 28 | >>> text = ''' 29 | ... ~~~~~~~~ 30 | ... 31 | ... ~~~~ 32 | ... 33 | ... ~~~~~~~~''' 34 | >>> markdown.markdown(text, extensions=['fenced_code']) 35 | u'<pre><code>\\n~~~~\\n\\n</code></pre>' 36 | 37 | Multiple blocks and language tags: 38 | 39 | >>> text = ''' 40 | ... ~~~~{.python} 41 | ... block one 42 | ... ~~~~ 43 | ... 44 | ... ~~~~.html 45 | ... <p>block two</p> 46 | ... ~~~~''' 47 | >>> markdown.markdown(text, extensions=['fenced_code']) 48 | u'<pre><code class="python">block one\\n</code></pre>\\n\\n<pre><code class="html"><p>block two</p>\\n</code></pre>' 49 | 50 | Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). 51 | 52 | Project website: <http://www.freewisdom.org/project/python-markdown/Fenced__Code__Blocks> 53 | Contact: markdown@freewisdom.org 54 | 55 | License: BSD (see ../docs/LICENSE for details) 56 | 57 | Dependencies: 58 | * [Python 2.3+](http://python.org) 59 | * [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) 60 | 61 | """ 62 | 63 | import markdown, re 64 | 65 | # Global vars 66 | FENCED_BLOCK_RE = re.compile( \ 67 | r'(?P<fence>^~{3,})[ ]*(\{?\.(?P<lang>[a-zA-Z0-9_-]*)\}?)?[ ]*\n(?P<code>.*?)(?P=fence)[ ]*$', 68 | re.MULTILINE|re.DOTALL 69 | ) 70 | CODE_WRAP = '<pre><code%s>%s</code></pre>' 71 | LANG_TAG = ' class="%s"' 72 | 73 | 74 | class FencedCodeExtension(markdown.Extension): 75 | 76 | def extendMarkdown(self, md, md_globals): 77 | """ Add FencedBlockPreprocessor to the Markdown instance. """ 78 | 79 | md.preprocessors.add('fenced_code_block', 80 | FencedBlockPreprocessor(md), 81 | "_begin") 82 | 83 | 84 | class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor): 85 | 86 | def run(self, lines): 87 | """ Match and store Fenced Code Blocks in the HtmlStash. """ 88 | text = "\n".join(lines) 89 | while 1: 90 | m = FENCED_BLOCK_RE.search(text) 91 | if m: 92 | lang = '' 93 | if m.group('lang'): 94 | lang = LANG_TAG % m.group('lang') 95 | code = CODE_WRAP % (lang, self._escape(m.group('code'))) 96 | placeholder = self.markdown.htmlStash.store(code, safe=True) 97 | text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():]) 98 | else: 99 | break 100 | return text.split("\n") 101 | 102 | def _escape(self, txt): 103 | """ basic html escaping """ 104 | txt = txt.replace('&', '&') 105 | txt = txt.replace('<', '<') 106 | txt = txt.replace('>', '>') 107 | txt = txt.replace('"', '"') 108 | return txt 109 | 110 | 111 | def makeExtension(configs=None): 112 | return FencedCodeExtension() 113 | 114 | 115 | if __name__ == "__main__": 116 | import doctest 117 | doctest.testmod() 118 | -------------------------------------------------------------------------------- /markdown/extensions/html_tidy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | HTML Tidy Extension for Python-Markdown 5 | ======================================= 6 | 7 | Runs [HTML Tidy][] on the output of Python-Markdown using the [uTidylib][] 8 | Python wrapper. Both libtidy and uTidylib must be installed on your system. 9 | 10 | Note than any Tidy [options][] can be passed in as extension configs. So, 11 | for example, to output HTML rather than XHTML, set ``output_xhtml=0``. To 12 | indent the output, set ``indent=auto`` and to have Tidy wrap the output in 13 | ``<html>`` and ``<body>`` tags, set ``show_body_only=0``. 14 | 15 | [HTML Tidy]: http://tidy.sourceforge.net/ 16 | [uTidylib]: http://utidylib.berlios.de/ 17 | [options]: http://tidy.sourceforge.net/docs/quickref.html 18 | 19 | Copyright (c)2008 [Waylan Limberg](http://achinghead.com) 20 | 21 | License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 22 | 23 | Dependencies: 24 | * [Python2.3+](http://python.org) 25 | * [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) 26 | * [HTML Tidy](http://utidylib.berlios.de/) 27 | * [uTidylib](http://utidylib.berlios.de/) 28 | 29 | """ 30 | 31 | import markdown 32 | import tidy 33 | 34 | class TidyExtension(markdown.Extension): 35 | 36 | def __init__(self, configs): 37 | # Set defaults to match typical markdown behavior. 38 | self.config = dict(output_xhtml=1, 39 | show_body_only=1, 40 | ) 41 | # Merge in user defined configs overriding any present if nessecary. 42 | for c in configs: 43 | self.config[c[0]] = c[1] 44 | 45 | def extendMarkdown(self, md, md_globals): 46 | # Save options to markdown instance 47 | md.tidy_options = self.config 48 | # Add TidyProcessor to postprocessors 49 | md.postprocessors['tidy'] = TidyProcessor(md) 50 | 51 | 52 | class TidyProcessor(markdown.postprocessors.Postprocessor): 53 | 54 | def run(self, text): 55 | # Pass text to Tidy. As Tidy does not accept unicode we need to encode 56 | # it and decode its return value. 57 | return unicode(tidy.parseString(text.encode('utf-8'), 58 | **self.markdown.tidy_options)) 59 | 60 | 61 | def makeExtension(configs=None): 62 | return TidyExtension(configs=configs) 63 | -------------------------------------------------------------------------------- /markdown/extensions/imagelinks.py: -------------------------------------------------------------------------------- 1 | """ 2 | ========================= IMAGE LINKS ================================= 3 | 4 | 5 | Turns paragraphs like 6 | 7 | <~~~~~~~~~~~~~~~~~~~~~~~~ 8 | dir/subdir 9 | dir/subdir 10 | dir/subdir 11 | ~~~~~~~~~~~~~~ 12 | dir/subdir 13 | dir/subdir 14 | dir/subdir 15 | ~~~~~~~~~~~~~~~~~~~> 16 | 17 | Into mini-photo galleries. 18 | 19 | """ 20 | 21 | import re, markdown 22 | import url_manager 23 | 24 | 25 | IMAGE_LINK = """<a href="%s"><img src="%s" title="%s"/></a>""" 26 | SLIDESHOW_LINK = """<a href="%s" target="_blank">[slideshow]</a>""" 27 | ALBUM_LINK = """ <a href="%s">[%s]</a>""" 28 | 29 | 30 | class ImageLinksExtension(markdown.Extension): 31 | 32 | def extendMarkdown(self, md, md_globals): 33 | 34 | md.preprocessors.add("imagelink", ImageLinkPreprocessor(md), "_begin") 35 | 36 | 37 | class ImageLinkPreprocessor(markdown.preprocessors.Preprocessor): 38 | 39 | def run(self, lines): 40 | 41 | url = url_manager.BlogEntryUrl(url_manager.BlogUrl("all"), 42 | "2006/08/29/the_rest_of_our") 43 | 44 | 45 | all_images = [] 46 | blocks = [] 47 | in_image_block = False 48 | 49 | new_lines = [] 50 | 51 | for line in lines: 52 | 53 | if line.startswith("<~~~~~~~"): 54 | albums = [] 55 | rows = [] 56 | in_image_block = True 57 | 58 | if not in_image_block: 59 | 60 | new_lines.append(line) 61 | 62 | else: 63 | 64 | line = line.strip() 65 | 66 | if line.endswith("~~~~~~>") or not line: 67 | in_image_block = False 68 | new_block = "<div><br/><center><span class='image-links'>\n" 69 | 70 | album_url_hash = {} 71 | 72 | for row in rows: 73 | for photo_url, title in row: 74 | new_block += " " 75 | new_block += IMAGE_LINK % (photo_url, 76 | photo_url.get_thumbnail(), 77 | title) 78 | 79 | album_url_hash[str(photo_url.get_album())] = 1 80 | 81 | new_block += "<br/>" 82 | 83 | new_block += "</span>" 84 | new_block += SLIDESHOW_LINK % url.get_slideshow() 85 | 86 | album_urls = album_url_hash.keys() 87 | album_urls.sort() 88 | 89 | if len(album_urls) == 1: 90 | new_block += ALBUM_LINK % (album_urls[0], "complete album") 91 | else : 92 | for i in range(len(album_urls)) : 93 | new_block += ALBUM_LINK % (album_urls[i], 94 | "album %d" % (i + 1) ) 95 | 96 | new_lines.append(new_block + "</center><br/></div>") 97 | 98 | elif line[1:6] == "~~~~~" : 99 | rows.append([]) # start a new row 100 | else : 101 | parts = line.split() 102 | line = parts[0] 103 | title = " ".join(parts[1:]) 104 | 105 | album, photo = line.split("/") 106 | photo_url = url.get_photo(album, photo, 107 | len(all_images)+1) 108 | all_images.append(photo_url) 109 | rows[-1].append((photo_url, title)) 110 | 111 | if not album in albums : 112 | albums.append(album) 113 | 114 | return new_lines 115 | 116 | 117 | def makeExtension(configs): 118 | return ImageLinksExtension(configs) 119 | 120 | -------------------------------------------------------------------------------- /markdown/extensions/meta.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python 2 | 3 | """ 4 | Meta Data Extension for Python-Markdown 5 | ======================================= 6 | 7 | This extension adds Meta Data handling to markdown. 8 | 9 | Basic Usage: 10 | 11 | >>> import markdown 12 | >>> text = '''Title: A Test Doc. 13 | ... Author: Waylan Limberg 14 | ... John Doe 15 | ... Blank_Data: 16 | ... 17 | ... The body. This is paragraph one. 18 | ... ''' 19 | >>> md = markdown.Markdown(['meta']) 20 | >>> md.convert(text) 21 | u'<p>The body. This is paragraph one.</p>' 22 | >>> md.Meta 23 | {u'blank_data': [u''], u'author': [u'Waylan Limberg', u'John Doe'], u'title': [u'A Test Doc.']} 24 | 25 | Make sure text without Meta Data still works (markdown < 1.6b returns a <p>). 26 | 27 | >>> text = ' Some Code - not extra lines of meta data.' 28 | >>> md = markdown.Markdown(['meta']) 29 | >>> md.convert(text) 30 | u'<pre><code>Some Code - not extra lines of meta data.\\n</code></pre>' 31 | >>> md.Meta 32 | {} 33 | 34 | Copyright 2007-2008 [Waylan Limberg](http://achinghead.com). 35 | 36 | Project website: <http://www.freewisdom.org/project/python-markdown/Meta-Data> 37 | Contact: markdown@freewisdom.org 38 | 39 | License: BSD (see ../docs/LICENSE for details) 40 | 41 | """ 42 | 43 | import markdown, re 44 | 45 | # Global Vars 46 | META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)') 47 | META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)') 48 | 49 | class MetaExtension (markdown.Extension): 50 | """ Meta-Data extension for Python-Markdown. """ 51 | 52 | def extendMarkdown(self, md, md_globals): 53 | """ Add MetaPreprocessor to Markdown instance. """ 54 | 55 | md.preprocessors.add("meta", MetaPreprocessor(md), "_begin") 56 | 57 | 58 | class MetaPreprocessor(markdown.preprocessors.Preprocessor): 59 | """ Get Meta-Data. """ 60 | 61 | def run(self, lines): 62 | """ Parse Meta-Data and store in Markdown.Meta. """ 63 | meta = {} 64 | key = None 65 | while 1: 66 | line = lines.pop(0) 67 | if line.strip() == '': 68 | break # blank line - done 69 | m1 = META_RE.match(line) 70 | if m1: 71 | key = m1.group('key').lower().strip() 72 | meta[key] = [m1.group('value').strip()] 73 | else: 74 | m2 = META_MORE_RE.match(line) 75 | if m2 and key: 76 | # Add another line to existing key 77 | meta[key].append(m2.group('value').strip()) 78 | else: 79 | lines.insert(0, line) 80 | break # no meta data - done 81 | self.markdown.Meta = meta 82 | return lines 83 | 84 | 85 | def makeExtension(configs={}): 86 | return MetaExtension(configs=configs) 87 | 88 | if __name__ == "__main__": 89 | import doctest 90 | doctest.testmod() 91 | -------------------------------------------------------------------------------- /markdown/extensions/rss.py: -------------------------------------------------------------------------------- 1 | import markdown 2 | from markdown import etree 3 | 4 | DEFAULT_URL = "http://www.freewisdom.org/projects/python-markdown/" 5 | DEFAULT_CREATOR = "Yuri Takhteyev" 6 | DEFAULT_TITLE = "Markdown in Python" 7 | GENERATOR = "http://www.freewisdom.org/projects/python-markdown/markdown2rss" 8 | 9 | month_map = { "Jan" : "01", 10 | "Feb" : "02", 11 | "March" : "03", 12 | "April" : "04", 13 | "May" : "05", 14 | "June" : "06", 15 | "July" : "07", 16 | "August" : "08", 17 | "September" : "09", 18 | "October" : "10", 19 | "November" : "11", 20 | "December" : "12" } 21 | 22 | def get_time(heading): 23 | 24 | heading = heading.split("-")[0] 25 | heading = heading.strip().replace(",", " ").replace(".", " ") 26 | 27 | month, date, year = heading.split() 28 | month = month_map[month] 29 | 30 | return rdftime(" ".join((month, date, year, "12:00:00 AM"))) 31 | 32 | def rdftime(time): 33 | 34 | time = time.replace(":", " ") 35 | time = time.replace("/", " ") 36 | time = time.split() 37 | return "%s-%s-%sT%s:%s:%s-08:00" % (time[0], time[1], time[2], 38 | time[3], time[4], time[5]) 39 | 40 | 41 | def get_date(text): 42 | return "date" 43 | 44 | class RssExtension (markdown.Extension): 45 | 46 | def extendMarkdown(self, md, md_globals): 47 | 48 | self.config = { 'URL' : [DEFAULT_URL, "Main URL"], 49 | 'CREATOR' : [DEFAULT_CREATOR, "Feed creator's name"], 50 | 'TITLE' : [DEFAULT_TITLE, "Feed title"] } 51 | 52 | md.xml_mode = True 53 | 54 | # Insert a tree-processor that would actually add the title tag 55 | treeprocessor = RssTreeProcessor(md) 56 | treeprocessor.ext = self 57 | md.treeprocessors['rss'] = treeprocessor 58 | md.stripTopLevelTags = 0 59 | md.docType = '<?xml version="1.0" encoding="utf-8"?>\n' 60 | 61 | class RssTreeProcessor(markdown.treeprocessors.Treeprocessor): 62 | 63 | def run (self, root): 64 | 65 | rss = etree.Element("rss") 66 | rss.set("version", "2.0") 67 | 68 | channel = etree.SubElement(rss, "channel") 69 | 70 | for tag, text in (("title", self.ext.getConfig("TITLE")), 71 | ("link", self.ext.getConfig("URL")), 72 | ("description", None)): 73 | 74 | element = etree.SubElement(channel, tag) 75 | element.text = text 76 | 77 | for child in root: 78 | 79 | if child.tag in ["h1", "h2", "h3", "h4", "h5"]: 80 | 81 | heading = child.text.strip() 82 | item = etree.SubElement(channel, "item") 83 | link = etree.SubElement(item, "link") 84 | link.text = self.ext.getConfig("URL") 85 | title = etree.SubElement(item, "title") 86 | title.text = heading 87 | 88 | guid = ''.join([x for x in heading if x.isalnum()]) 89 | guidElem = etree.SubElement(item, "guid") 90 | guidElem.text = guid 91 | guidElem.set("isPermaLink", "false") 92 | 93 | elif child.tag in ["p"]: 94 | try: 95 | description = etree.SubElement(item, "description") 96 | except UnboundLocalError: 97 | # Item not defined - moving on 98 | pass 99 | else: 100 | if len(child): 101 | content = "\n".join([etree.tostring(node) 102 | for node in child]) 103 | else: 104 | content = child.text 105 | pholder = self.markdown.htmlStash.store( 106 | "<![CDATA[ %s]]>" % content) 107 | description.text = pholder 108 | 109 | return rss 110 | 111 | 112 | def makeExtension(configs): 113 | 114 | return RssExtension(configs) 115 | -------------------------------------------------------------------------------- /markdown/extensions/tables.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Python 2 | """ 3 | Tables Extension for Python-Markdown 4 | ==================================== 5 | 6 | Added parsing of tables to Python-Markdown. 7 | 8 | A simple example: 9 | 10 | First Header | Second Header 11 | ------------- | ------------- 12 | Content Cell | Content Cell 13 | Content Cell | Content Cell 14 | 15 | Copyright 2009 - [Waylan Limberg](http://achinghead.com) 16 | """ 17 | import markdown 18 | from markdown import etree 19 | 20 | 21 | class TableProcessor(markdown.blockprocessors.BlockProcessor): 22 | """ Process Tables. """ 23 | 24 | def test(self, parent, block): 25 | rows = block.split('\n') 26 | return (len(rows) > 2 and '|' in rows[0] and 27 | '|' in rows[1] and '-' in rows[1] and 28 | rows[1][0] in ['|', ':', '-']) 29 | 30 | def run(self, parent, blocks): 31 | """ Parse a table block and build table. """ 32 | block = blocks.pop(0).split('\n') 33 | header = block[:2] 34 | rows = block[2:] 35 | # Get format type (bordered by pipes or not) 36 | border = False 37 | if header[0].startswith('|'): 38 | border = True 39 | # Get alignment of columns 40 | align = [] 41 | for c in self._split_row(header[1], border): 42 | if c.startswith(':') and c.endswith(':'): 43 | align.append('center') 44 | elif c.startswith(':'): 45 | align.append('left') 46 | elif c.endswith(':'): 47 | align.append('right') 48 | else: 49 | align.append(None) 50 | # Build table 51 | table = etree.SubElement(parent, 'table') 52 | thead = etree.SubElement(table, 'thead') 53 | self._build_row(header[0], thead, align, border) 54 | tbody = etree.SubElement(table, 'tbody') 55 | for row in rows: 56 | self._build_row(row, tbody, align, border) 57 | 58 | def _build_row(self, row, parent, align, border): 59 | """ Given a row of text, build table cells. """ 60 | tr = etree.SubElement(parent, 'tr') 61 | tag = 'td' 62 | if parent.tag == 'thead': 63 | tag = 'th' 64 | cells = self._split_row(row, border) 65 | # We use align here rather than cells to ensure every row 66 | # contains the same number of columns. 67 | for i, a in enumerate(align): 68 | c = etree.SubElement(tr, tag) 69 | try: 70 | c.text = cells[i].strip() 71 | except IndexError: 72 | c.text = "" 73 | if a: 74 | c.set('align', a) 75 | 76 | def _split_row(self, row, border): 77 | """ split a row of text into list of cells. """ 78 | if border: 79 | if row.startswith('|'): 80 | row = row[1:] 81 | if row.endswith('|'): 82 | row = row[:-1] 83 | return row.split('|') 84 | 85 | 86 | class TableExtension(markdown.Extension): 87 | """ Add tables to Markdown. """ 88 | 89 | def extendMarkdown(self, md, md_globals): 90 | """ Add an instance of TableProcessor to BlockParser. """ 91 | md.parser.blockprocessors.add('table', 92 | TableProcessor(md.parser), 93 | '<hashheader') 94 | 95 | 96 | def makeExtension(configs={}): 97 | return TableExtension(configs=configs) 98 | -------------------------------------------------------------------------------- /markdown/postprocessors.py: -------------------------------------------------------------------------------- 1 | """ 2 | POST-PROCESSORS 3 | ============================================================================= 4 | 5 | Markdown also allows post-processors, which are similar to preprocessors in 6 | that they need to implement a "run" method. However, they are run after core 7 | processing. 8 | 9 | """ 10 | 11 | 12 | import markdown 13 | 14 | class Processor: 15 | def __init__(self, markdown_instance=None): 16 | if markdown_instance: 17 | self.markdown = markdown_instance 18 | 19 | class Postprocessor(Processor): 20 | """ 21 | Postprocessors are run after the ElementTree it converted back into text. 22 | 23 | Each Postprocessor implements a "run" method that takes a pointer to a 24 | text string, modifies it as necessary and returns a text string. 25 | 26 | Postprocessors must extend markdown.Postprocessor. 27 | 28 | """ 29 | 30 | def run(self, text): 31 | """ 32 | Subclasses of Postprocessor should implement a `run` method, which 33 | takes the html document as a single text string and returns a 34 | (possibly modified) string. 35 | 36 | """ 37 | pass 38 | 39 | 40 | class RawHtmlPostprocessor(Postprocessor): 41 | """ Restore raw html to the document. """ 42 | 43 | def run(self, text): 44 | """ Iterate over html stash and restore "safe" html. """ 45 | for i in range(self.markdown.htmlStash.html_counter): 46 | html, safe = self.markdown.htmlStash.rawHtmlBlocks[i] 47 | if self.markdown.safeMode and not safe: 48 | if str(self.markdown.safeMode).lower() == 'escape': 49 | html = self.escape(html) 50 | elif str(self.markdown.safeMode).lower() == 'remove': 51 | html = '' 52 | else: 53 | html = markdown.HTML_REMOVED_TEXT 54 | if safe or not self.markdown.safeMode: 55 | text = text.replace("<p>%s</p>" % 56 | (markdown.preprocessors.HTML_PLACEHOLDER % i), 57 | html + "\n") 58 | text = text.replace(markdown.preprocessors.HTML_PLACEHOLDER % i, 59 | html) 60 | return text 61 | 62 | def escape(self, html): 63 | """ Basic html escaping """ 64 | html = html.replace('&', '&') 65 | html = html.replace('<', '<') 66 | html = html.replace('>', '>') 67 | return html.replace('"', '"') 68 | 69 | 70 | class AndSubstitutePostprocessor(Postprocessor): 71 | """ Restore valid entities """ 72 | def __init__(self): 73 | pass 74 | 75 | def run(self, text): 76 | text = text.replace(markdown.AMP_SUBSTITUTE, "&") 77 | return text 78 | -------------------------------------------------------------------------------- /pygments/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Pygments 4 | ~~~~~~~~ 5 | 6 | Pygments is a syntax highlighting package written in Python. 7 | 8 | It is a generic syntax highlighter for general use in all kinds of software 9 | such as forum systems, wikis or other applications that need to prettify 10 | source code. Highlights are: 11 | 12 | * a wide range of common languages and markup formats is supported 13 | * special attention is paid to details, increasing quality by a fair amount 14 | * support for new languages and formats are added easily 15 | * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image 16 | formats that PIL supports, and ANSI sequences 17 | * it is usable as a command-line tool and as a library 18 | * ... and it highlights even Brainfuck! 19 | 20 | The `Pygments tip`_ is installable with ``easy_install Pygments==dev``. 21 | 22 | .. _Pygments tip: 23 | http://bitbucket.org/birkenfeld/pygments-main/get/tip.zip#egg=Pygments-dev 24 | 25 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 26 | :license: BSD, see LICENSE for details. 27 | """ 28 | 29 | __version__ = '1.4' 30 | __docformat__ = 'restructuredtext' 31 | 32 | __all__ = ['lex', 'format', 'highlight'] 33 | 34 | 35 | import sys 36 | 37 | from pygments.util import StringIO, BytesIO 38 | 39 | 40 | def lex(code, lexer): 41 | """ 42 | Lex ``code`` with ``lexer`` and return an iterable of tokens. 43 | """ 44 | try: 45 | return lexer.get_tokens(code) 46 | except TypeError, err: 47 | if isinstance(err.args[0], str) and \ 48 | 'unbound method get_tokens' in err.args[0]: 49 | raise TypeError('lex() argument must be a lexer instance, ' 50 | 'not a class') 51 | raise 52 | 53 | 54 | def format(tokens, formatter, outfile=None): 55 | """ 56 | Format a tokenlist ``tokens`` with the formatter ``formatter``. 57 | 58 | If ``outfile`` is given and a valid file object (an object 59 | with a ``write`` method), the result will be written to it, otherwise 60 | it is returned as a string. 61 | """ 62 | try: 63 | if not outfile: 64 | #print formatter, 'using', formatter.encoding 65 | realoutfile = formatter.encoding and BytesIO() or StringIO() 66 | formatter.format(tokens, realoutfile) 67 | return realoutfile.getvalue() 68 | else: 69 | formatter.format(tokens, outfile) 70 | except TypeError, err: 71 | if isinstance(err.args[0], str) and \ 72 | 'unbound method format' in err.args[0]: 73 | raise TypeError('format() argument must be a formatter instance, ' 74 | 'not a class') 75 | raise 76 | 77 | 78 | def highlight(code, lexer, formatter, outfile=None): 79 | """ 80 | Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``. 81 | 82 | If ``outfile`` is given and a valid file object (an object 83 | with a ``write`` method), the result will be written to it, otherwise 84 | it is returned as a string. 85 | """ 86 | return format(lex(code, lexer), formatter, outfile) 87 | 88 | 89 | if __name__ == '__main__': 90 | from pygments.cmdline import main 91 | sys.exit(main(sys.argv)) 92 | -------------------------------------------------------------------------------- /pygments/console.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.console 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | Format colored console output. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | esc = "\x1b[" 13 | 14 | codes = {} 15 | codes[""] = "" 16 | codes["reset"] = esc + "39;49;00m" 17 | 18 | codes["bold"] = esc + "01m" 19 | codes["faint"] = esc + "02m" 20 | codes["standout"] = esc + "03m" 21 | codes["underline"] = esc + "04m" 22 | codes["blink"] = esc + "05m" 23 | codes["overline"] = esc + "06m" 24 | 25 | dark_colors = ["black", "darkred", "darkgreen", "brown", "darkblue", 26 | "purple", "teal", "lightgray"] 27 | light_colors = ["darkgray", "red", "green", "yellow", "blue", 28 | "fuchsia", "turquoise", "white"] 29 | 30 | x = 30 31 | for d, l in zip(dark_colors, light_colors): 32 | codes[d] = esc + "%im" % x 33 | codes[l] = esc + "%i;01m" % x 34 | x += 1 35 | 36 | del d, l, x 37 | 38 | codes["darkteal"] = codes["turquoise"] 39 | codes["darkyellow"] = codes["brown"] 40 | codes["fuscia"] = codes["fuchsia"] 41 | codes["white"] = codes["bold"] 42 | 43 | 44 | def reset_color(): 45 | return codes["reset"] 46 | 47 | 48 | def colorize(color_key, text): 49 | return codes[color_key] + text + codes["reset"] 50 | 51 | 52 | def ansiformat(attr, text): 53 | """ 54 | Format ``text`` with a color and/or some attributes:: 55 | 56 | color normal color 57 | *color* bold color 58 | _color_ underlined color 59 | +color+ blinking color 60 | """ 61 | result = [] 62 | if attr[:1] == attr[-1:] == '+': 63 | result.append(codes['blink']) 64 | attr = attr[1:-1] 65 | if attr[:1] == attr[-1:] == '*': 66 | result.append(codes['bold']) 67 | attr = attr[1:-1] 68 | if attr[:1] == attr[-1:] == '_': 69 | result.append(codes['underline']) 70 | attr = attr[1:-1] 71 | result.append(codes[attr]) 72 | result.append(text) 73 | result.append(codes['reset']) 74 | return ''.join(result) 75 | -------------------------------------------------------------------------------- /pygments/filter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.filter 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Module that implements the default filter. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | 13 | def apply_filters(stream, filters, lexer=None): 14 | """ 15 | Use this method to apply an iterable of filters to 16 | a stream. If lexer is given it's forwarded to the 17 | filter, otherwise the filter receives `None`. 18 | """ 19 | def _apply(filter_, stream): 20 | for token in filter_.filter(lexer, stream): 21 | yield token 22 | for filter_ in filters: 23 | stream = _apply(filter_, stream) 24 | return stream 25 | 26 | 27 | def simplefilter(f): 28 | """ 29 | Decorator that converts a function into a filter:: 30 | 31 | @simplefilter 32 | def lowercase(lexer, stream, options): 33 | for ttype, value in stream: 34 | yield ttype, value.lower() 35 | """ 36 | return type(f.__name__, (FunctionFilter,), { 37 | 'function': f, 38 | '__module__': getattr(f, '__module__'), 39 | '__doc__': f.__doc__ 40 | }) 41 | 42 | 43 | class Filter(object): 44 | """ 45 | Default filter. Subclass this class or use the `simplefilter` 46 | decorator to create own filters. 47 | """ 48 | 49 | def __init__(self, **options): 50 | self.options = options 51 | 52 | def filter(self, lexer, stream): 53 | raise NotImplementedError() 54 | 55 | 56 | class FunctionFilter(Filter): 57 | """ 58 | Abstract class used by `simplefilter` to create simple 59 | function filters on the fly. The `simplefilter` decorator 60 | automatically creates subclasses of this class for 61 | functions passed to it. 62 | """ 63 | function = None 64 | 65 | def __init__(self, **options): 66 | if not hasattr(self, 'function'): 67 | raise TypeError('%r used without bound function' % 68 | self.__class__.__name__) 69 | Filter.__init__(self, **options) 70 | 71 | def filter(self, lexer, stream): 72 | # pylint: disable-msg=E1102 73 | for ttype, value in self.function(lexer, stream, self.options): 74 | yield ttype, value 75 | -------------------------------------------------------------------------------- /pygments/formatter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.formatter 4 | ~~~~~~~~~~~~~~~~~~ 5 | 6 | Base formatter class. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | import codecs 13 | 14 | from pygments.util import get_bool_opt 15 | from pygments.styles import get_style_by_name 16 | 17 | __all__ = ['Formatter'] 18 | 19 | 20 | def _lookup_style(style): 21 | if isinstance(style, basestring): 22 | return get_style_by_name(style) 23 | return style 24 | 25 | 26 | class Formatter(object): 27 | """ 28 | Converts a token stream to text. 29 | 30 | Options accepted: 31 | 32 | ``style`` 33 | The style to use, can be a string or a Style subclass 34 | (default: "default"). Not used by e.g. the 35 | TerminalFormatter. 36 | ``full`` 37 | Tells the formatter to output a "full" document, i.e. 38 | a complete self-contained document. This doesn't have 39 | any effect for some formatters (default: false). 40 | ``title`` 41 | If ``full`` is true, the title that should be used to 42 | caption the document (default: ''). 43 | ``encoding`` 44 | If given, must be an encoding name. This will be used to 45 | convert the Unicode token strings to byte strings in the 46 | output. If it is "" or None, Unicode strings will be written 47 | to the output file, which most file-like objects do not 48 | support (default: None). 49 | ``outencoding`` 50 | Overrides ``encoding`` if given. 51 | """ 52 | 53 | #: Name of the formatter 54 | name = None 55 | 56 | #: Shortcuts for the formatter 57 | aliases = [] 58 | 59 | #: fn match rules 60 | filenames = [] 61 | 62 | #: If True, this formatter outputs Unicode strings when no encoding 63 | #: option is given. 64 | unicodeoutput = True 65 | 66 | def __init__(self, **options): 67 | self.style = _lookup_style(options.get('style', 'default')) 68 | self.full = get_bool_opt(options, 'full', False) 69 | self.title = options.get('title', '') 70 | self.encoding = options.get('encoding', None) or None 71 | self.encoding = options.get('outencoding', None) or self.encoding 72 | self.options = options 73 | 74 | def get_style_defs(self, arg=''): 75 | """ 76 | Return the style definitions for the current style as a string. 77 | 78 | ``arg`` is an additional argument whose meaning depends on the 79 | formatter used. Note that ``arg`` can also be a list or tuple 80 | for some formatters like the html formatter. 81 | """ 82 | return '' 83 | 84 | def format(self, tokensource, outfile): 85 | """ 86 | Format ``tokensource``, an iterable of ``(tokentype, tokenstring)`` 87 | tuples and write it into ``outfile``. 88 | """ 89 | if self.encoding: 90 | # wrap the outfile in a StreamWriter 91 | outfile = codecs.lookup(self.encoding)[3](outfile) 92 | return self.format_unencoded(tokensource, outfile) 93 | -------------------------------------------------------------------------------- /pygments/formatters/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.formatters 4 | ~~~~~~~~~~~~~~~~~~~ 5 | 6 | Pygments formatters. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | import os.path 12 | import fnmatch 13 | 14 | from pygments.formatters._mapping import FORMATTERS 15 | from pygments.plugin import find_plugin_formatters 16 | from pygments.util import ClassNotFound 17 | 18 | ns = globals() 19 | for fcls in FORMATTERS: 20 | ns[fcls.__name__] = fcls 21 | del fcls 22 | 23 | __all__ = ['get_formatter_by_name', 'get_formatter_for_filename', 24 | 'get_all_formatters'] + [cls.__name__ for cls in FORMATTERS] 25 | 26 | 27 | _formatter_alias_cache = {} 28 | _formatter_filename_cache = [] 29 | 30 | def _init_formatter_cache(): 31 | if _formatter_alias_cache: 32 | return 33 | for cls in get_all_formatters(): 34 | for alias in cls.aliases: 35 | _formatter_alias_cache[alias] = cls 36 | for fn in cls.filenames: 37 | _formatter_filename_cache.append((fn, cls)) 38 | 39 | 40 | def find_formatter_class(name): 41 | _init_formatter_cache() 42 | cls = _formatter_alias_cache.get(name, None) 43 | return cls 44 | 45 | 46 | def get_formatter_by_name(name, **options): 47 | _init_formatter_cache() 48 | cls = _formatter_alias_cache.get(name, None) 49 | if not cls: 50 | raise ClassNotFound("No formatter found for name %r" % name) 51 | return cls(**options) 52 | 53 | 54 | def get_formatter_for_filename(fn, **options): 55 | _init_formatter_cache() 56 | fn = os.path.basename(fn) 57 | for pattern, cls in _formatter_filename_cache: 58 | if fnmatch.fnmatch(fn, pattern): 59 | return cls(**options) 60 | raise ClassNotFound("No formatter found for file name %r" % fn) 61 | 62 | 63 | def get_all_formatters(): 64 | """Return a generator for all formatters.""" 65 | for formatter in FORMATTERS: 66 | yield formatter 67 | for _, formatter in find_plugin_formatters(): 68 | yield formatter 69 | -------------------------------------------------------------------------------- /pygments/formatters/bbcode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.formatters.bbcode 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | BBcode formatter. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | 13 | from pygments.formatter import Formatter 14 | from pygments.util import get_bool_opt 15 | 16 | __all__ = ['BBCodeFormatter'] 17 | 18 | 19 | class BBCodeFormatter(Formatter): 20 | """ 21 | Format tokens with BBcodes. These formatting codes are used by many 22 | bulletin boards, so you can highlight your sourcecode with pygments before 23 | posting it there. 24 | 25 | This formatter has no support for background colors and borders, as there 26 | are no common BBcode tags for that. 27 | 28 | Some board systems (e.g. phpBB) don't support colors in their [code] tag, 29 | so you can't use the highlighting together with that tag. 30 | Text in a [code] tag usually is shown with a monospace font (which this 31 | formatter can do with the ``monofont`` option) and no spaces (which you 32 | need for indentation) are removed. 33 | 34 | Additional options accepted: 35 | 36 | `style` 37 | The style to use, can be a string or a Style subclass (default: 38 | ``'default'``). 39 | 40 | `codetag` 41 | If set to true, put the output into ``[code]`` tags (default: 42 | ``false``) 43 | 44 | `monofont` 45 | If set to true, add a tag to show the code with a monospace font 46 | (default: ``false``). 47 | """ 48 | name = 'BBCode' 49 | aliases = ['bbcode', 'bb'] 50 | filenames = [] 51 | 52 | def __init__(self, **options): 53 | Formatter.__init__(self, **options) 54 | self._code = get_bool_opt(options, 'codetag', False) 55 | self._mono = get_bool_opt(options, 'monofont', False) 56 | 57 | self.styles = {} 58 | self._make_styles() 59 | 60 | def _make_styles(self): 61 | for ttype, ndef in self.style: 62 | start = end = '' 63 | if ndef['color']: 64 | start += '[color=#%s]' % ndef['color'] 65 | end = '[/color]' + end 66 | if ndef['bold']: 67 | start += '[b]' 68 | end = '[/b]' + end 69 | if ndef['italic']: 70 | start += '[i]' 71 | end = '[/i]' + end 72 | if ndef['underline']: 73 | start += '[u]' 74 | end = '[/u]' + end 75 | # there are no common BBcodes for background-color and border 76 | 77 | self.styles[ttype] = start, end 78 | 79 | def format_unencoded(self, tokensource, outfile): 80 | if self._code: 81 | outfile.write('[code]') 82 | if self._mono: 83 | outfile.write('[font=monospace]') 84 | 85 | lastval = '' 86 | lasttype = None 87 | 88 | for ttype, value in tokensource: 89 | while ttype not in self.styles: 90 | ttype = ttype.parent 91 | if ttype == lasttype: 92 | lastval += value 93 | else: 94 | if lastval: 95 | start, end = self.styles[lasttype] 96 | outfile.write(''.join((start, lastval, end))) 97 | lastval = value 98 | lasttype = ttype 99 | 100 | if lastval: 101 | start, end = self.styles[lasttype] 102 | outfile.write(''.join((start, lastval, end))) 103 | 104 | if self._mono: 105 | outfile.write('[/font]') 106 | if self._code: 107 | outfile.write('[/code]') 108 | if self._code or self._mono: 109 | outfile.write('\n') 110 | -------------------------------------------------------------------------------- /pygments/lexers/special.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.lexers.special 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Special lexers. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | import re 13 | import cStringIO 14 | 15 | from pygments.lexer import Lexer 16 | from pygments.token import Token, Error, Text 17 | from pygments.util import get_choice_opt, b 18 | 19 | 20 | __all__ = ['TextLexer', 'RawTokenLexer'] 21 | 22 | 23 | class TextLexer(Lexer): 24 | """ 25 | "Null" lexer, doesn't highlight anything. 26 | """ 27 | name = 'Text only' 28 | aliases = ['text'] 29 | filenames = ['*.txt'] 30 | mimetypes = ['text/plain'] 31 | 32 | def get_tokens_unprocessed(self, text): 33 | yield 0, Text, text 34 | 35 | 36 | _ttype_cache = {} 37 | 38 | line_re = re.compile(b('.*?\n')) 39 | 40 | class RawTokenLexer(Lexer): 41 | """ 42 | Recreate a token stream formatted with the `RawTokenFormatter`. This 43 | lexer raises exceptions during parsing if the token stream in the 44 | file is malformed. 45 | 46 | Additional options accepted: 47 | 48 | `compress` 49 | If set to ``"gz"`` or ``"bz2"``, decompress the token stream with 50 | the given compression algorithm before lexing (default: ``""``). 51 | """ 52 | name = 'Raw token data' 53 | aliases = ['raw'] 54 | filenames = [] 55 | mimetypes = ['application/x-pygments-tokens'] 56 | 57 | def __init__(self, **options): 58 | self.compress = get_choice_opt(options, 'compress', 59 | ['', 'none', 'gz', 'bz2'], '') 60 | Lexer.__init__(self, **options) 61 | 62 | def get_tokens(self, text): 63 | if isinstance(text, unicode): 64 | # raw token stream never has any non-ASCII characters 65 | text = text.encode('ascii') 66 | if self.compress == 'gz': 67 | import gzip 68 | gzipfile = gzip.GzipFile('', 'rb', 9, cStringIO.StringIO(text)) 69 | text = gzipfile.read() 70 | elif self.compress == 'bz2': 71 | import bz2 72 | text = bz2.decompress(text) 73 | 74 | # do not call Lexer.get_tokens() because we do not want Unicode 75 | # decoding to occur, and stripping is not optional. 76 | text = text.strip(b('\n')) + b('\n') 77 | for i, t, v in self.get_tokens_unprocessed(text): 78 | yield t, v 79 | 80 | def get_tokens_unprocessed(self, text): 81 | length = 0 82 | for match in line_re.finditer(text): 83 | try: 84 | ttypestr, val = match.group().split(b('\t'), 1) 85 | except ValueError: 86 | val = match.group().decode(self.encoding) 87 | ttype = Error 88 | else: 89 | ttype = _ttype_cache.get(ttypestr) 90 | if not ttype: 91 | ttype = Token 92 | ttypes = ttypestr.split('.')[1:] 93 | for ttype_ in ttypes: 94 | if not ttype_ or not ttype_[0].isupper(): 95 | raise ValueError('malformed token name') 96 | ttype = getattr(ttype, ttype_) 97 | _ttype_cache[ttypestr] = ttype 98 | val = val[2:-2].decode('unicode-escape') 99 | yield length, ttype, val 100 | length += len(val) 101 | -------------------------------------------------------------------------------- /pygments/plugin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.plugin 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Pygments setuptools plugin interface. The methods defined 7 | here also work if setuptools isn't installed but they just 8 | return nothing. 9 | 10 | lexer plugins:: 11 | 12 | [pygments.lexers] 13 | yourlexer = yourmodule:YourLexer 14 | 15 | formatter plugins:: 16 | 17 | [pygments.formatters] 18 | yourformatter = yourformatter:YourFormatter 19 | /.ext = yourformatter:YourFormatter 20 | 21 | As you can see, you can define extensions for the formatter 22 | with a leading slash. 23 | 24 | syntax plugins:: 25 | 26 | [pygments.styles] 27 | yourstyle = yourstyle:YourStyle 28 | 29 | filter plugin:: 30 | 31 | [pygments.filter] 32 | yourfilter = yourfilter:YourFilter 33 | 34 | 35 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 36 | :license: BSD, see LICENSE for details. 37 | """ 38 | try: 39 | import pkg_resources 40 | except ImportError: 41 | pkg_resources = None 42 | 43 | LEXER_ENTRY_POINT = 'pygments.lexers' 44 | FORMATTER_ENTRY_POINT = 'pygments.formatters' 45 | STYLE_ENTRY_POINT = 'pygments.styles' 46 | FILTER_ENTRY_POINT = 'pygments.filters' 47 | 48 | 49 | def find_plugin_lexers(): 50 | if pkg_resources is None: 51 | return 52 | for entrypoint in pkg_resources.iter_entry_points(LEXER_ENTRY_POINT): 53 | yield entrypoint.load() 54 | 55 | 56 | def find_plugin_formatters(): 57 | if pkg_resources is None: 58 | return 59 | for entrypoint in pkg_resources.iter_entry_points(FORMATTER_ENTRY_POINT): 60 | yield entrypoint.name, entrypoint.load() 61 | 62 | 63 | def find_plugin_styles(): 64 | if pkg_resources is None: 65 | return 66 | for entrypoint in pkg_resources.iter_entry_points(STYLE_ENTRY_POINT): 67 | yield entrypoint.name, entrypoint.load() 68 | 69 | 70 | def find_plugin_filters(): 71 | if pkg_resources is None: 72 | return 73 | for entrypoint in pkg_resources.iter_entry_points(FILTER_ENTRY_POINT): 74 | yield entrypoint.name, entrypoint.load() 75 | -------------------------------------------------------------------------------- /pygments/scanner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.scanner 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | This library implements a regex based scanner. Some languages 7 | like Pascal are easy to parse but have some keywords that 8 | depend on the context. Because of this it's impossible to lex 9 | that just by using a regular expression lexer like the 10 | `RegexLexer`. 11 | 12 | Have a look at the `DelphiLexer` to get an idea of how to use 13 | this scanner. 14 | 15 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 16 | :license: BSD, see LICENSE for details. 17 | """ 18 | import re 19 | 20 | 21 | class EndOfText(RuntimeError): 22 | """ 23 | Raise if end of text is reached and the user 24 | tried to call a match function. 25 | """ 26 | 27 | 28 | class Scanner(object): 29 | """ 30 | Simple scanner 31 | 32 | All method patterns are regular expression strings (not 33 | compiled expressions!) 34 | """ 35 | 36 | def __init__(self, text, flags=0): 37 | """ 38 | :param text: The text which should be scanned 39 | :param flags: default regular expression flags 40 | """ 41 | self.data = text 42 | self.data_length = len(text) 43 | self.start_pos = 0 44 | self.pos = 0 45 | self.flags = flags 46 | self.last = None 47 | self.match = None 48 | self._re_cache = {} 49 | 50 | def eos(self): 51 | """`True` if the scanner reached the end of text.""" 52 | return self.pos >= self.data_length 53 | eos = property(eos, eos.__doc__) 54 | 55 | def check(self, pattern): 56 | """ 57 | Apply `pattern` on the current position and return 58 | the match object. (Doesn't touch pos). Use this for 59 | lookahead. 60 | """ 61 | if self.eos: 62 | raise EndOfText() 63 | if pattern not in self._re_cache: 64 | self._re_cache[pattern] = re.compile(pattern, self.flags) 65 | return self._re_cache[pattern].match(self.data, self.pos) 66 | 67 | def test(self, pattern): 68 | """Apply a pattern on the current position and check 69 | if it patches. Doesn't touch pos.""" 70 | return self.check(pattern) is not None 71 | 72 | def scan(self, pattern): 73 | """ 74 | Scan the text for the given pattern and update pos/match 75 | and related fields. The return value is a boolen that 76 | indicates if the pattern matched. The matched value is 77 | stored on the instance as ``match``, the last value is 78 | stored as ``last``. ``start_pos`` is the position of the 79 | pointer before the pattern was matched, ``pos`` is the 80 | end position. 81 | """ 82 | if self.eos: 83 | raise EndOfText() 84 | if pattern not in self._re_cache: 85 | self._re_cache[pattern] = re.compile(pattern, self.flags) 86 | self.last = self.match 87 | m = self._re_cache[pattern].match(self.data, self.pos) 88 | if m is None: 89 | return False 90 | self.start_pos = m.start() 91 | self.pos = m.end() 92 | self.match = m.group() 93 | return True 94 | 95 | def get_char(self): 96 | """Scan exactly one char.""" 97 | self.scan('.') 98 | 99 | def __repr__(self): 100 | return '<%s %d/%d>' % ( 101 | self.__class__.__name__, 102 | self.pos, 103 | self.data_length 104 | ) 105 | -------------------------------------------------------------------------------- /pygments/styles/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Contains built-in styles. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.plugin import find_plugin_styles 13 | from pygments.util import ClassNotFound 14 | 15 | 16 | #: Maps style names to 'submodule::classname'. 17 | STYLE_MAP = { 18 | 'default': 'default::DefaultStyle', 19 | 'emacs': 'emacs::EmacsStyle', 20 | 'friendly': 'friendly::FriendlyStyle', 21 | 'colorful': 'colorful::ColorfulStyle', 22 | 'autumn': 'autumn::AutumnStyle', 23 | 'murphy': 'murphy::MurphyStyle', 24 | 'manni': 'manni::ManniStyle', 25 | 'monokai': 'monokai::MonokaiStyle', 26 | 'perldoc': 'perldoc::PerldocStyle', 27 | 'pastie': 'pastie::PastieStyle', 28 | 'borland': 'borland::BorlandStyle', 29 | 'trac': 'trac::TracStyle', 30 | 'native': 'native::NativeStyle', 31 | 'fruity': 'fruity::FruityStyle', 32 | 'bw': 'bw::BlackWhiteStyle', 33 | 'vim': 'vim::VimStyle', 34 | 'vs': 'vs::VisualStudioStyle', 35 | 'tango': 'tango::TangoStyle', 36 | } 37 | 38 | 39 | def get_style_by_name(name): 40 | if name in STYLE_MAP: 41 | mod, cls = STYLE_MAP[name].split('::') 42 | builtin = "yes" 43 | else: 44 | for found_name, style in find_plugin_styles(): 45 | if name == found_name: 46 | return style 47 | # perhaps it got dropped into our styles package 48 | builtin = "" 49 | mod = name 50 | cls = name.title() + "Style" 51 | 52 | try: 53 | mod = __import__('pygments.styles.' + mod, None, None, [cls]) 54 | except ImportError: 55 | raise ClassNotFound("Could not find style module %r" % mod + 56 | (builtin and ", though it should be builtin") + ".") 57 | try: 58 | return getattr(mod, cls) 59 | except AttributeError: 60 | raise ClassNotFound("Could not find style class %r in style module." % cls) 61 | 62 | 63 | def get_all_styles(): 64 | """Return an generator for all styles by name, 65 | both builtin and plugin.""" 66 | for name in STYLE_MAP: 67 | yield name 68 | for name, _ in find_plugin_styles(): 69 | yield name 70 | -------------------------------------------------------------------------------- /pygments/styles/autumn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.autumn 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A colorful style, inspired by the terminal highlighting style. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class AutumnStyle(Style): 18 | """ 19 | A colorful style, inspired by the terminal highlighting style. 20 | """ 21 | 22 | default_style = "" 23 | 24 | styles = { 25 | Whitespace: '#bbbbbb', 26 | 27 | Comment: 'italic #aaaaaa', 28 | Comment.Preproc: 'noitalic #4c8317', 29 | Comment.Special: 'italic #0000aa', 30 | 31 | Keyword: '#0000aa', 32 | Keyword.Type: '#00aaaa', 33 | 34 | Operator.Word: '#0000aa', 35 | 36 | Name.Builtin: '#00aaaa', 37 | Name.Function: '#00aa00', 38 | Name.Class: 'underline #00aa00', 39 | Name.Namespace: 'underline #00aaaa', 40 | Name.Variable: '#aa0000', 41 | Name.Constant: '#aa0000', 42 | Name.Entity: 'bold #800', 43 | Name.Attribute: '#1e90ff', 44 | Name.Tag: 'bold #1e90ff', 45 | Name.Decorator: '#888888', 46 | 47 | String: '#aa5500', 48 | String.Symbol: '#0000aa', 49 | String.Regex: '#009999', 50 | 51 | Number: '#009999', 52 | 53 | Generic.Heading: 'bold #000080', 54 | Generic.Subheading: 'bold #800080', 55 | Generic.Deleted: '#aa0000', 56 | Generic.Inserted: '#00aa00', 57 | Generic.Error: '#aa0000', 58 | Generic.Emph: 'italic', 59 | Generic.Strong: 'bold', 60 | Generic.Prompt: '#555555', 61 | Generic.Output: '#888888', 62 | Generic.Traceback: '#aa0000', 63 | 64 | Error: '#F00 bg:#FAA' 65 | } 66 | -------------------------------------------------------------------------------- /pygments/styles/borland.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.borland 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Style similar to the style used in the Borland IDEs. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class BorlandStyle(Style): 18 | """ 19 | Style similar to the style used in the borland IDEs. 20 | """ 21 | 22 | default_style = '' 23 | 24 | styles = { 25 | Whitespace: '#bbbbbb', 26 | 27 | Comment: 'italic #008800', 28 | Comment.Preproc: 'noitalic #008080', 29 | Comment.Special: 'noitalic bold', 30 | 31 | String: '#0000FF', 32 | String.Char: '#800080', 33 | Number: '#0000FF', 34 | Keyword: 'bold #000080', 35 | Operator.Word: 'bold', 36 | Name.Tag: 'bold #000080', 37 | Name.Attribute: '#FF0000', 38 | 39 | Generic.Heading: '#999999', 40 | Generic.Subheading: '#aaaaaa', 41 | Generic.Deleted: 'bg:#ffdddd #000000', 42 | Generic.Inserted: 'bg:#ddffdd #000000', 43 | Generic.Error: '#aa0000', 44 | Generic.Emph: 'italic', 45 | Generic.Strong: 'bold', 46 | Generic.Prompt: '#555555', 47 | Generic.Output: '#888888', 48 | Generic.Traceback: '#aa0000', 49 | 50 | Error: 'bg:#e3d2d2 #a61717' 51 | } 52 | -------------------------------------------------------------------------------- /pygments/styles/bw.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.bw 4 | ~~~~~~~~~~~~~~~~~~ 5 | 6 | Simple black/white only style. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Operator, Generic 15 | 16 | 17 | class BlackWhiteStyle(Style): 18 | 19 | background_color = "#ffffff" 20 | default_style = "" 21 | 22 | styles = { 23 | Comment: "italic", 24 | Comment.Preproc: "noitalic", 25 | 26 | Keyword: "bold", 27 | Keyword.Pseudo: "nobold", 28 | Keyword.Type: "nobold", 29 | 30 | Operator.Word: "bold", 31 | 32 | Name.Class: "bold", 33 | Name.Namespace: "bold", 34 | Name.Exception: "bold", 35 | Name.Entity: "bold", 36 | Name.Tag: "bold", 37 | 38 | String: "italic", 39 | String.Interpol: "bold", 40 | String.Escape: "bold", 41 | 42 | Generic.Heading: "bold", 43 | Generic.Subheading: "bold", 44 | Generic.Emph: "italic", 45 | Generic.Strong: "bold", 46 | Generic.Prompt: "bold", 47 | 48 | Error: "border:#FF0000" 49 | } 50 | -------------------------------------------------------------------------------- /pygments/styles/colorful.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.colorful 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A colorful style, inspired by CodeRay. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class ColorfulStyle(Style): 18 | """ 19 | A colorful style, inspired by CodeRay. 20 | """ 21 | 22 | default_style = "" 23 | 24 | styles = { 25 | Whitespace: "#bbbbbb", 26 | 27 | Comment: "#888", 28 | Comment.Preproc: "#579", 29 | Comment.Special: "bold #cc0000", 30 | 31 | Keyword: "bold #080", 32 | Keyword.Pseudo: "#038", 33 | Keyword.Type: "#339", 34 | 35 | Operator: "#333", 36 | Operator.Word: "bold #000", 37 | 38 | Name.Builtin: "#007020", 39 | Name.Function: "bold #06B", 40 | Name.Class: "bold #B06", 41 | Name.Namespace: "bold #0e84b5", 42 | Name.Exception: "bold #F00", 43 | Name.Variable: "#963", 44 | Name.Variable.Instance: "#33B", 45 | Name.Variable.Class: "#369", 46 | Name.Variable.Global: "bold #d70", 47 | Name.Constant: "bold #036", 48 | Name.Label: "bold #970", 49 | Name.Entity: "bold #800", 50 | Name.Attribute: "#00C", 51 | Name.Tag: "#070", 52 | Name.Decorator: "bold #555", 53 | 54 | String: "bg:#fff0f0", 55 | String.Char: "#04D bg:", 56 | String.Doc: "#D42 bg:", 57 | String.Interpol: "bg:#eee", 58 | String.Escape: "bold #666", 59 | String.Regex: "bg:#fff0ff #000", 60 | String.Symbol: "#A60 bg:", 61 | String.Other: "#D20", 62 | 63 | Number: "bold #60E", 64 | Number.Integer: "bold #00D", 65 | Number.Float: "bold #60E", 66 | Number.Hex: "bold #058", 67 | Number.Oct: "bold #40E", 68 | 69 | Generic.Heading: "bold #000080", 70 | Generic.Subheading: "bold #800080", 71 | Generic.Deleted: "#A00000", 72 | Generic.Inserted: "#00A000", 73 | Generic.Error: "#FF0000", 74 | Generic.Emph: "italic", 75 | Generic.Strong: "bold", 76 | Generic.Prompt: "bold #c65d09", 77 | Generic.Output: "#888", 78 | Generic.Traceback: "#04D", 79 | 80 | Error: "#F00 bg:#FAA" 81 | } 82 | -------------------------------------------------------------------------------- /pygments/styles/default.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.default 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | The default highlighting style. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class DefaultStyle(Style): 18 | """ 19 | The default style (inspired by Emacs 22). 20 | """ 21 | 22 | background_color = "#f8f8f8" 23 | default_style = "" 24 | 25 | styles = { 26 | Whitespace: "#bbbbbb", 27 | Comment: "italic #408080", 28 | Comment.Preproc: "noitalic #BC7A00", 29 | 30 | #Keyword: "bold #AA22FF", 31 | Keyword: "bold #008000", 32 | Keyword.Pseudo: "nobold", 33 | Keyword.Type: "nobold #B00040", 34 | 35 | Operator: "#666666", 36 | Operator.Word: "bold #AA22FF", 37 | 38 | Name.Builtin: "#008000", 39 | Name.Function: "#0000FF", 40 | Name.Class: "bold #0000FF", 41 | Name.Namespace: "bold #0000FF", 42 | Name.Exception: "bold #D2413A", 43 | Name.Variable: "#19177C", 44 | Name.Constant: "#880000", 45 | Name.Label: "#A0A000", 46 | Name.Entity: "bold #999999", 47 | Name.Attribute: "#7D9029", 48 | Name.Tag: "bold #008000", 49 | Name.Decorator: "#AA22FF", 50 | 51 | String: "#BA2121", 52 | String.Doc: "italic", 53 | String.Interpol: "bold #BB6688", 54 | String.Escape: "bold #BB6622", 55 | String.Regex: "#BB6688", 56 | #String.Symbol: "#B8860B", 57 | String.Symbol: "#19177C", 58 | String.Other: "#008000", 59 | Number: "#666666", 60 | 61 | Generic.Heading: "bold #000080", 62 | Generic.Subheading: "bold #800080", 63 | Generic.Deleted: "#A00000", 64 | Generic.Inserted: "#00A000", 65 | Generic.Error: "#FF0000", 66 | Generic.Emph: "italic", 67 | Generic.Strong: "bold", 68 | Generic.Prompt: "bold #000080", 69 | Generic.Output: "#888", 70 | Generic.Traceback: "#04D", 71 | 72 | Error: "border:#FF0000" 73 | } 74 | -------------------------------------------------------------------------------- /pygments/styles/emacs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.emacs 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A highlighting style for Pygments, inspired by Emacs. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class EmacsStyle(Style): 18 | """ 19 | The default style (inspired by Emacs 22). 20 | """ 21 | 22 | background_color = "#f8f8f8" 23 | default_style = "" 24 | 25 | styles = { 26 | Whitespace: "#bbbbbb", 27 | Comment: "italic #008800", 28 | Comment.Preproc: "noitalic", 29 | Comment.Special: "noitalic bold", 30 | 31 | Keyword: "bold #AA22FF", 32 | Keyword.Pseudo: "nobold", 33 | Keyword.Type: "bold #00BB00", 34 | 35 | Operator: "#666666", 36 | Operator.Word: "bold #AA22FF", 37 | 38 | Name.Builtin: "#AA22FF", 39 | Name.Function: "#00A000", 40 | Name.Class: "#0000FF", 41 | Name.Namespace: "bold #0000FF", 42 | Name.Exception: "bold #D2413A", 43 | Name.Variable: "#B8860B", 44 | Name.Constant: "#880000", 45 | Name.Label: "#A0A000", 46 | Name.Entity: "bold #999999", 47 | Name.Attribute: "#BB4444", 48 | Name.Tag: "bold #008000", 49 | Name.Decorator: "#AA22FF", 50 | 51 | String: "#BB4444", 52 | String.Doc: "italic", 53 | String.Interpol: "bold #BB6688", 54 | String.Escape: "bold #BB6622", 55 | String.Regex: "#BB6688", 56 | String.Symbol: "#B8860B", 57 | String.Other: "#008000", 58 | Number: "#666666", 59 | 60 | Generic.Heading: "bold #000080", 61 | Generic.Subheading: "bold #800080", 62 | Generic.Deleted: "#A00000", 63 | Generic.Inserted: "#00A000", 64 | Generic.Error: "#FF0000", 65 | Generic.Emph: "italic", 66 | Generic.Strong: "bold", 67 | Generic.Prompt: "bold #000080", 68 | Generic.Output: "#888", 69 | Generic.Traceback: "#04D", 70 | 71 | Error: "border:#FF0000" 72 | } 73 | -------------------------------------------------------------------------------- /pygments/styles/friendly.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.friendly 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A modern style based on the VIM pyte theme. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class FriendlyStyle(Style): 18 | """ 19 | A modern style based on the VIM pyte theme. 20 | """ 21 | 22 | background_color = "#f0f0f0" 23 | default_style = "" 24 | 25 | styles = { 26 | Whitespace: "#bbbbbb", 27 | Comment: "italic #60a0b0", 28 | Comment.Preproc: "noitalic #007020", 29 | Comment.Special: "noitalic bg:#fff0f0", 30 | 31 | Keyword: "bold #007020", 32 | Keyword.Pseudo: "nobold", 33 | Keyword.Type: "nobold #902000", 34 | 35 | Operator: "#666666", 36 | Operator.Word: "bold #007020", 37 | 38 | Name.Builtin: "#007020", 39 | Name.Function: "#06287e", 40 | Name.Class: "bold #0e84b5", 41 | Name.Namespace: "bold #0e84b5", 42 | Name.Exception: "#007020", 43 | Name.Variable: "#bb60d5", 44 | Name.Constant: "#60add5", 45 | Name.Label: "bold #002070", 46 | Name.Entity: "bold #d55537", 47 | Name.Attribute: "#4070a0", 48 | Name.Tag: "bold #062873", 49 | Name.Decorator: "bold #555555", 50 | 51 | String: "#4070a0", 52 | String.Doc: "italic", 53 | String.Interpol: "italic #70a0d0", 54 | String.Escape: "bold #4070a0", 55 | String.Regex: "#235388", 56 | String.Symbol: "#517918", 57 | String.Other: "#c65d09", 58 | Number: "#40a070", 59 | 60 | Generic.Heading: "bold #000080", 61 | Generic.Subheading: "bold #800080", 62 | Generic.Deleted: "#A00000", 63 | Generic.Inserted: "#00A000", 64 | Generic.Error: "#FF0000", 65 | Generic.Emph: "italic", 66 | Generic.Strong: "bold", 67 | Generic.Prompt: "bold #c65d09", 68 | Generic.Output: "#888", 69 | Generic.Traceback: "#04D", 70 | 71 | Error: "border:#FF0000" 72 | } 73 | -------------------------------------------------------------------------------- /pygments/styles/fruity.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.fruity 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | pygments version of my "fruity" vim theme. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Token, Comment, Name, Keyword, \ 14 | Generic, Number, String, Whitespace 15 | 16 | class FruityStyle(Style): 17 | """ 18 | Pygments version of the "native" vim theme. 19 | """ 20 | 21 | background_color = '#111111' 22 | highlight_color = '#333333' 23 | 24 | styles = { 25 | Whitespace: '#888888', 26 | Token: '#ffffff', 27 | Generic.Output: '#444444 bg:#222222', 28 | Keyword: '#fb660a bold', 29 | Keyword.Pseudo: 'nobold', 30 | Number: '#0086f7 bold', 31 | Name.Tag: '#fb660a bold', 32 | Name.Variable: '#fb660a', 33 | Name.Constant: '#fb660a', 34 | Comment: '#008800 bg:#0f140f italic', 35 | Name.Attribute: '#ff0086 bold', 36 | String: '#0086d2', 37 | Name.Function: '#ff0086 bold', 38 | Generic.Heading: '#ffffff bold', 39 | Keyword.Type: '#cdcaa9 bold', 40 | Generic.Subheading: '#ffffff bold', 41 | Name.Constant: '#0086d2', 42 | Comment.Preproc: '#ff0007 bold' 43 | } 44 | -------------------------------------------------------------------------------- /pygments/styles/manni.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.manni 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A colorful style, inspired by the terminal highlighting style. 7 | 8 | This is a port of the style used in the `php port`_ of pygments 9 | by Manni. The style is called 'default' there. 10 | 11 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 12 | :license: BSD, see LICENSE for details. 13 | """ 14 | 15 | from pygments.style import Style 16 | from pygments.token import Keyword, Name, Comment, String, Error, \ 17 | Number, Operator, Generic, Whitespace 18 | 19 | 20 | class ManniStyle(Style): 21 | """ 22 | A colorful style, inspired by the terminal highlighting style. 23 | """ 24 | 25 | background_color = '#f0f3f3' 26 | 27 | styles = { 28 | Whitespace: '#bbbbbb', 29 | Comment: 'italic #0099FF', 30 | Comment.Preproc: 'noitalic #009999', 31 | Comment.Special: 'bold', 32 | 33 | Keyword: 'bold #006699', 34 | Keyword.Pseudo: 'nobold', 35 | Keyword.Type: '#007788', 36 | 37 | Operator: '#555555', 38 | Operator.Word: 'bold #000000', 39 | 40 | Name.Builtin: '#336666', 41 | Name.Function: '#CC00FF', 42 | Name.Class: 'bold #00AA88', 43 | Name.Namespace: 'bold #00CCFF', 44 | Name.Exception: 'bold #CC0000', 45 | Name.Variable: '#003333', 46 | Name.Constant: '#336600', 47 | Name.Label: '#9999FF', 48 | Name.Entity: 'bold #999999', 49 | Name.Attribute: '#330099', 50 | Name.Tag: 'bold #330099', 51 | Name.Decorator: '#9999FF', 52 | 53 | String: '#CC3300', 54 | String.Doc: 'italic', 55 | String.Interpol: '#AA0000', 56 | String.Escape: 'bold #CC3300', 57 | String.Regex: '#33AAAA', 58 | String.Symbol: '#FFCC33', 59 | String.Other: '#CC3300', 60 | 61 | Number: '#FF6600', 62 | 63 | Generic.Heading: 'bold #003300', 64 | Generic.Subheading: 'bold #003300', 65 | Generic.Deleted: 'border:#CC0000 bg:#FFCCCC', 66 | Generic.Inserted: 'border:#00CC00 bg:#CCFFCC', 67 | Generic.Error: '#FF0000', 68 | Generic.Emph: 'italic', 69 | Generic.Strong: 'bold', 70 | Generic.Prompt: 'bold #000099', 71 | Generic.Output: '#AAAAAA', 72 | Generic.Traceback: '#99CC66', 73 | 74 | Error: 'bg:#FFAAAA #AA0000' 75 | } 76 | -------------------------------------------------------------------------------- /pygments/styles/murphy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.murphy 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Murphy's style from CodeRay. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class MurphyStyle(Style): 18 | """ 19 | Murphy's style from CodeRay. 20 | """ 21 | 22 | default_style = "" 23 | 24 | styles = { 25 | Whitespace: "#bbbbbb", 26 | Comment: "#666 italic", 27 | Comment.Preproc: "#579 noitalic", 28 | Comment.Special: "#c00 bold", 29 | 30 | Keyword: "bold #289", 31 | Keyword.Pseudo: "#08f", 32 | Keyword.Type: "#66f", 33 | 34 | Operator: "#333", 35 | Operator.Word: "bold #000", 36 | 37 | Name.Builtin: "#072", 38 | Name.Function: "bold #5ed", 39 | Name.Class: "bold #e9e", 40 | Name.Namespace: "bold #0e84b5", 41 | Name.Exception: "bold #F00", 42 | Name.Variable: "#036", 43 | Name.Variable.Instance: "#aaf", 44 | Name.Variable.Class: "#ccf", 45 | Name.Variable.Global: "#f84", 46 | Name.Constant: "bold #5ed", 47 | Name.Label: "bold #970", 48 | Name.Entity: "#800", 49 | Name.Attribute: "#007", 50 | Name.Tag: "#070", 51 | Name.Decorator: "bold #555", 52 | 53 | String: "bg:#e0e0ff", 54 | String.Char: "#88F bg:", 55 | String.Doc: "#D42 bg:", 56 | String.Interpol: "bg:#eee", 57 | String.Escape: "bold #666", 58 | String.Regex: "bg:#e0e0ff #000", 59 | String.Symbol: "#fc8 bg:", 60 | String.Other: "#f88", 61 | 62 | Number: "bold #60E", 63 | Number.Integer: "bold #66f", 64 | Number.Float: "bold #60E", 65 | Number.Hex: "bold #058", 66 | Number.Oct: "bold #40E", 67 | 68 | Generic.Heading: "bold #000080", 69 | Generic.Subheading: "bold #800080", 70 | Generic.Deleted: "#A00000", 71 | Generic.Inserted: "#00A000", 72 | Generic.Error: "#FF0000", 73 | Generic.Emph: "italic", 74 | Generic.Strong: "bold", 75 | Generic.Prompt: "bold #c65d09", 76 | Generic.Output: "#888", 77 | Generic.Traceback: "#04D", 78 | 79 | Error: "#F00 bg:#FAA" 80 | } 81 | -------------------------------------------------------------------------------- /pygments/styles/native.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.native 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | pygments version of my "native" vim theme. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Token, Whitespace 15 | 16 | 17 | class NativeStyle(Style): 18 | """ 19 | Pygments version of the "native" vim theme. 20 | """ 21 | 22 | background_color = '#202020' 23 | highlight_color = '#404040' 24 | 25 | styles = { 26 | Token: '#d0d0d0', 27 | Whitespace: '#666666', 28 | 29 | Comment: 'italic #999999', 30 | Comment.Preproc: 'noitalic bold #cd2828', 31 | Comment.Special: 'noitalic bold #e50808 bg:#520000', 32 | 33 | Keyword: 'bold #6ab825', 34 | Keyword.Pseudo: 'nobold', 35 | Operator.Word: 'bold #6ab825', 36 | 37 | String: '#ed9d13', 38 | String.Other: '#ffa500', 39 | 40 | Number: '#3677a9', 41 | 42 | Name.Builtin: '#24909d', 43 | Name.Variable: '#40ffff', 44 | Name.Constant: '#40ffff', 45 | Name.Class: 'underline #447fcf', 46 | Name.Function: '#447fcf', 47 | Name.Namespace: 'underline #447fcf', 48 | Name.Exception: '#bbbbbb', 49 | Name.Tag: 'bold #6ab825', 50 | Name.Attribute: '#bbbbbb', 51 | Name.Decorator: '#ffa500', 52 | 53 | Generic.Heading: 'bold #ffffff', 54 | Generic.Subheading: 'underline #ffffff', 55 | Generic.Deleted: '#d22323', 56 | Generic.Inserted: '#589819', 57 | Generic.Error: '#d22323', 58 | Generic.Emph: 'italic', 59 | Generic.Strong: 'bold', 60 | Generic.Prompt: '#aaaaaa', 61 | Generic.Output: '#cccccc', 62 | Generic.Traceback: '#d22323', 63 | 64 | Error: 'bg:#e3d2d2 #a61717' 65 | } 66 | -------------------------------------------------------------------------------- /pygments/styles/pastie.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.pastie 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Style similar to the `pastie`_ default style. 7 | 8 | .. _pastie: http://pastie.caboo.se/ 9 | 10 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 11 | :license: BSD, see LICENSE for details. 12 | """ 13 | 14 | from pygments.style import Style 15 | from pygments.token import Keyword, Name, Comment, String, Error, \ 16 | Number, Operator, Generic, Whitespace 17 | 18 | 19 | class PastieStyle(Style): 20 | """ 21 | Style similar to the pastie default style. 22 | """ 23 | 24 | default_style = '' 25 | 26 | styles = { 27 | Whitespace: '#bbbbbb', 28 | Comment: '#888888', 29 | Comment.Preproc: 'bold #cc0000', 30 | Comment.Special: 'bg:#fff0f0 bold #cc0000', 31 | 32 | String: 'bg:#fff0f0 #dd2200', 33 | String.Regex: 'bg:#fff0ff #008800', 34 | String.Other: 'bg:#f0fff0 #22bb22', 35 | String.Symbol: '#aa6600', 36 | String.Interpol: '#3333bb', 37 | String.Escape: '#0044dd', 38 | 39 | Operator.Word: '#008800', 40 | 41 | Keyword: 'bold #008800', 42 | Keyword.Pseudo: 'nobold', 43 | Keyword.Type: '#888888', 44 | 45 | Name.Class: 'bold #bb0066', 46 | Name.Exception: 'bold #bb0066', 47 | Name.Function: 'bold #0066bb', 48 | Name.Property: 'bold #336699', 49 | Name.Namespace: 'bold #bb0066', 50 | Name.Builtin: '#003388', 51 | Name.Variable: '#336699', 52 | Name.Variable.Class: '#336699', 53 | Name.Variable.Instance: '#3333bb', 54 | Name.Variable.Global: '#dd7700', 55 | Name.Constant: 'bold #003366', 56 | Name.Tag: 'bold #bb0066', 57 | Name.Attribute: '#336699', 58 | Name.Decorator: '#555555', 59 | Name.Label: 'italic #336699', 60 | 61 | Number: 'bold #0000DD', 62 | 63 | Generic.Heading: '#333', 64 | Generic.Subheading: '#666', 65 | Generic.Deleted: 'bg:#ffdddd #000000', 66 | Generic.Inserted: 'bg:#ddffdd #000000', 67 | Generic.Error: '#aa0000', 68 | Generic.Emph: 'italic', 69 | Generic.Strong: 'bold', 70 | Generic.Prompt: '#555555', 71 | Generic.Output: '#888888', 72 | Generic.Traceback: '#aa0000', 73 | 74 | Error: 'bg:#e3d2d2 #a61717' 75 | } 76 | -------------------------------------------------------------------------------- /pygments/styles/perldoc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.perldoc 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Style similar to the style used in the `perldoc`_ code blocks. 7 | 8 | .. _perldoc: http://perldoc.perl.org/ 9 | 10 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 11 | :license: BSD, see LICENSE for details. 12 | """ 13 | 14 | from pygments.style import Style 15 | from pygments.token import Keyword, Name, Comment, String, Error, \ 16 | Number, Operator, Generic, Whitespace 17 | 18 | 19 | class PerldocStyle(Style): 20 | """ 21 | Style similar to the style used in the perldoc code blocks. 22 | """ 23 | 24 | background_color = '#eeeedd' 25 | default_style = '' 26 | 27 | styles = { 28 | Whitespace: '#bbbbbb', 29 | Comment: '#228B22', 30 | Comment.Preproc: '#1e889b', 31 | Comment.Special: '#8B008B bold', 32 | 33 | String: '#CD5555', 34 | String.Heredoc: '#1c7e71 italic', 35 | String.Regex: '#B452CD', 36 | String.Other: '#cb6c20', 37 | String.Regex: '#1c7e71', 38 | 39 | Number: '#B452CD', 40 | 41 | Operator.Word: '#8B008B', 42 | 43 | Keyword: '#8B008B bold', 44 | Keyword.Type: '#a7a7a7', 45 | 46 | Name.Class: '#008b45 bold', 47 | Name.Exception: '#008b45 bold', 48 | Name.Function: '#008b45', 49 | Name.Namespace: '#008b45 underline', 50 | Name.Variable: '#00688B', 51 | Name.Constant: '#00688B', 52 | Name.Decorator: '#707a7c', 53 | Name.Tag: '#8B008B bold', 54 | Name.Attribute: '#658b00', 55 | Name.Builtin: '#658b00', 56 | 57 | Generic.Heading: 'bold #000080', 58 | Generic.Subheading: 'bold #800080', 59 | Generic.Deleted: '#aa0000', 60 | Generic.Inserted: '#00aa00', 61 | Generic.Error: '#aa0000', 62 | Generic.Emph: 'italic', 63 | Generic.Strong: 'bold', 64 | Generic.Prompt: '#555555', 65 | Generic.Output: '#888888', 66 | Generic.Traceback: '#aa0000', 67 | 68 | Error: 'bg:#e3d2d2 #a61717' 69 | } 70 | -------------------------------------------------------------------------------- /pygments/styles/trac.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.trac 4 | ~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Port of the default trac highlighter design. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class TracStyle(Style): 18 | """ 19 | Port of the default trac highlighter design. 20 | """ 21 | 22 | default_style = '' 23 | 24 | styles = { 25 | Whitespace: '#bbbbbb', 26 | Comment: 'italic #999988', 27 | Comment.Preproc: 'bold noitalic #999999', 28 | Comment.Special: 'bold #999999', 29 | 30 | Operator: 'bold', 31 | 32 | String: '#bb8844', 33 | String.Regex: '#808000', 34 | 35 | Number: '#009999', 36 | 37 | Keyword: 'bold', 38 | Keyword.Type: '#445588', 39 | 40 | Name.Builtin: '#999999', 41 | Name.Function: 'bold #990000', 42 | Name.Class: 'bold #445588', 43 | Name.Exception: 'bold #990000', 44 | Name.Namespace: '#555555', 45 | Name.Variable: '#008080', 46 | Name.Constant: '#008080', 47 | Name.Tag: '#000080', 48 | Name.Attribute: '#008080', 49 | Name.Entity: '#800080', 50 | 51 | Generic.Heading: '#999999', 52 | Generic.Subheading: '#aaaaaa', 53 | Generic.Deleted: 'bg:#ffdddd #000000', 54 | Generic.Inserted: 'bg:#ddffdd #000000', 55 | Generic.Error: '#aa0000', 56 | Generic.Emph: 'italic', 57 | Generic.Strong: 'bold', 58 | Generic.Prompt: '#555555', 59 | Generic.Output: '#888888', 60 | Generic.Traceback: '#aa0000', 61 | 62 | Error: 'bg:#e3d2d2 #a61717' 63 | } 64 | -------------------------------------------------------------------------------- /pygments/styles/vim.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.vim 4 | ~~~~~~~~~~~~~~~~~~~ 5 | 6 | A highlighting style for Pygments, inspired by vim. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace, Token 15 | 16 | 17 | class VimStyle(Style): 18 | """ 19 | Styles somewhat like vim 7.0 20 | """ 21 | 22 | background_color = "#000000" 23 | highlight_color = "#222222" 24 | default_style = "#cccccc" 25 | 26 | styles = { 27 | Token: "#cccccc", 28 | Whitespace: "", 29 | Comment: "#000080", 30 | Comment.Preproc: "", 31 | Comment.Special: "bold #cd0000", 32 | 33 | Keyword: "#cdcd00", 34 | Keyword.Declaration: "#00cd00", 35 | Keyword.Namespace: "#cd00cd", 36 | Keyword.Pseudo: "", 37 | Keyword.Type: "#00cd00", 38 | 39 | Operator: "#3399cc", 40 | Operator.Word: "#cdcd00", 41 | 42 | Name: "", 43 | Name.Class: "#00cdcd", 44 | Name.Builtin: "#cd00cd", 45 | Name.Exception: "bold #666699", 46 | Name.Variable: "#00cdcd", 47 | 48 | String: "#cd0000", 49 | Number: "#cd00cd", 50 | 51 | Generic.Heading: "bold #000080", 52 | Generic.Subheading: "bold #800080", 53 | Generic.Deleted: "#cd0000", 54 | Generic.Inserted: "#00cd00", 55 | Generic.Error: "#FF0000", 56 | Generic.Emph: "italic", 57 | Generic.Strong: "bold", 58 | Generic.Prompt: "bold #000080", 59 | Generic.Output: "#888", 60 | Generic.Traceback: "#04D", 61 | 62 | Error: "border:#FF0000" 63 | } 64 | -------------------------------------------------------------------------------- /pygments/styles/vs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.vs 4 | ~~~~~~~~~~~~~~~~~~ 5 | 6 | Simple style with MS Visual Studio colors. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Operator, Generic 15 | 16 | 17 | class VisualStudioStyle(Style): 18 | 19 | background_color = "#ffffff" 20 | default_style = "" 21 | 22 | styles = { 23 | Comment: "#008000", 24 | Comment.Preproc: "#0000ff", 25 | Keyword: "#0000ff", 26 | Operator.Word: "#0000ff", 27 | Keyword.Type: "#2b91af", 28 | Name.Class: "#2b91af", 29 | String: "#a31515", 30 | 31 | Generic.Heading: "bold", 32 | Generic.Subheading: "bold", 33 | Generic.Emph: "italic", 34 | Generic.Strong: "bold", 35 | Generic.Prompt: "bold", 36 | 37 | Error: "border:#FF0000" 38 | } 39 | -------------------------------------------------------------------------------- /werkzeug/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | Contains user-submitted code that other users may find useful, but which 7 | is not part of the Werkzeug core. Anyone can write code for inclusion in 8 | the `contrib` package. All modules in this package are distributed as an 9 | add-on library and thus are not part of Werkzeug itself. 10 | 11 | This file itself is mostly for informational purposes and to tell the 12 | Python interpreter that `contrib` is a package. 13 | 14 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 15 | :license: BSD, see LICENSE for more details. 16 | """ 17 | -------------------------------------------------------------------------------- /werkzeug/contrib/limiter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib.limiter 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A middleware that limits incoming data. This works around problems with 7 | Trac_ or Django_ because those directly stream into the memory. 8 | 9 | .. _Trac: http://trac.edgewall.org/ 10 | .. _Django: http://www.djangoproject.com/ 11 | 12 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | from warnings import warn 16 | 17 | from werkzeug import LimitedStream 18 | 19 | 20 | class StreamLimitMiddleware(object): 21 | """Limits the input stream to a given number of bytes. This is useful if 22 | you have a WSGI application that reads form data into memory (django for 23 | example) and you don't want users to harm the server by uploading tons of 24 | data. 25 | 26 | Default is 10MB 27 | """ 28 | 29 | def __init__(self, app, maximum_size=1024 * 1024 * 10): 30 | self.app = app 31 | self.maximum_size = maximum_size 32 | 33 | def __call__(self, environ, start_response): 34 | limit = min(limit, int(environ.get('CONTENT_LENGTH') or 0)) 35 | environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit) 36 | return self.app(environ, start_response) 37 | -------------------------------------------------------------------------------- /werkzeug/contrib/testtools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib.testtools 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | This module implements extended wrappers for simplified testing. 7 | 8 | `TestResponse` 9 | A response wrapper which adds various cached attributes for 10 | simplified assertions on various content types. 11 | 12 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | from werkzeug import Response, cached_property, import_string 16 | 17 | 18 | class ContentAccessors(object): 19 | """ 20 | A mixin class for response objects that provides a couple of useful 21 | accessors for unittesting. 22 | """ 23 | 24 | def xml(self): 25 | """Get an etree if possible.""" 26 | if 'xml' not in self.mimetype: 27 | raise AttributeError( 28 | 'Not a XML response (Content-Type: %s)' 29 | % self.mimetype) 30 | for module in ['xml.etree.ElementTree', 'ElementTree', 31 | 'elementtree.ElementTree']: 32 | etree = import_string(module, silent=True) 33 | if etree is not None: 34 | return etree.XML(self.body) 35 | raise RuntimeError('You must have ElementTree installed ' 36 | 'to use TestResponse.xml') 37 | xml = cached_property(xml) 38 | 39 | def lxml(self): 40 | """Get an lxml etree if possible.""" 41 | if ('html' not in self.mimetype and 'xml' not in self.mimetype): 42 | raise AttributeError('Not an HTML/XML response') 43 | from lxml import etree 44 | try: 45 | from lxml.html import fromstring 46 | except ImportError: 47 | fromstring = etree.HTML 48 | if self.mimetype=='text/html': 49 | return fromstring(self.data) 50 | return etree.XML(self.data) 51 | lxml = cached_property(lxml) 52 | 53 | def json(self): 54 | """Get the result of simplejson.loads if possible.""" 55 | if 'json' not in self.mimetype: 56 | raise AttributeError('Not a JSON response') 57 | try: 58 | from simplejson import loads 59 | except: 60 | from json import loads 61 | return loads(self.data) 62 | json = cached_property(json) 63 | 64 | 65 | class TestResponse(Response, ContentAccessors): 66 | """Pass this to `werkzeug.test.Client` for easier unittesting.""" 67 | -------------------------------------------------------------------------------- /werkzeug/debug/render.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.debug.render 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Render the traceback debugging page. 7 | 8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import pprint 12 | from os.path import dirname, join 13 | 14 | from werkzeug.templates import Template 15 | 16 | 17 | def get_template(name): 18 | return Template.from_file(join(dirname(__file__), 'shared', name), 19 | unicode_mode=False, errors='ignore') 20 | 21 | 22 | def load_resource(res): 23 | try: 24 | f = file(join(dirname(__file__), 'shared', res)) 25 | except IOError: 26 | return '' 27 | try: 28 | return f.read() 29 | finally: 30 | f.close() 31 | 32 | 33 | t_body = get_template('body.tmpl') 34 | t_codetable = get_template('codetable.tmpl') 35 | t_vartable = get_template('vartable.tmpl') 36 | 37 | 38 | def code_table(frame): 39 | from werkzeug.debug.util import Namespace 40 | lines = [] 41 | lineno = frame['context_lineno'] 42 | if lineno is not None: 43 | lineno += 1 44 | for l in frame['pre_context']: 45 | lines.append(Namespace(mode='pre', lineno=lineno, code=l)) 46 | lineno += 1 47 | lines.append(Namespace(mode='cur', lineno=lineno, 48 | code=frame['context_line'])) 49 | lineno += 1 50 | for l in frame['post_context']: 51 | lines.append(Namespace(mode='post', lineno=lineno, code=l)) 52 | lineno += 1 53 | else: 54 | lines.append(Namespace(mode='cur', lineno=1, 55 | code='Sourcecode not available')) 56 | 57 | return t_codetable.render(lines=lines) 58 | 59 | 60 | def var_table(var): 61 | def safe_pformat(x): 62 | try: 63 | lines = pprint.pformat(x).splitlines() 64 | except: 65 | return '?' 66 | tmp = [] 67 | for line in lines: 68 | if len(line) > 79: 69 | line = line[:79] + '...' 70 | tmp.append(line) 71 | return '\n'.join(tmp) 72 | 73 | # dicts 74 | if isinstance(var, dict) or hasattr(var, 'items'): 75 | value = var.items() 76 | if not value: 77 | typ = 'empty' 78 | else: 79 | typ = 'dict' 80 | value.sort() 81 | value = [(repr(key), safe_pformat(val)) for key, val in value] 82 | 83 | # lists 84 | elif isinstance(var, list): 85 | if not var: 86 | typ = 'empty' 87 | else: 88 | typ = 'list' 89 | value = [safe_pformat(item) for item in var] 90 | 91 | # others 92 | else: 93 | typ = 'simple' 94 | value = repr(var) 95 | 96 | return t_vartable.render(type=typ, value=value) 97 | 98 | 99 | def debug_page(context): 100 | tc = context.to_dict() 101 | tc['var_table'] = var_table 102 | tc['code_table'] = code_table 103 | return t_body.render(tc) 104 | -------------------------------------------------------------------------------- /werkzeug/debug/shared/body.tmpl: -------------------------------------------------------------------------------- 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 2 | "http://www.w3.org/TR/html4/loose.dtd"> 3 | <html> 4 | <head> 5 | <title>$escape(exception_type) in $escape(last_frame['basename']) (Werkzeug Debugger) 6 | 7 | 8 | 9 | 10 | 11 |
12 |

$escape(exception_type)

13 |

$escape(exception_value)

14 | 15 |

16 | $escape(last_frame['filename']) in 17 | $escape(last_frame['function']), 18 | line $last_frame['lineno'] 19 |

20 | 21 |

Traceback (toggle raw view)

22 |
23 |

A problem occurred in your Python WSGI application. 24 | Here is the sequence of function calls leading up to the error, in the order 25 | they occurred. Activate a code line to toggle context lines.

26 | 27 | <% for num, frame in enumerate(frames) %> 28 |
29 |

$escape(frame['function']) in $escape(frame['filename'])

30 | [inspect] 31 | <% if evalex %>[console]<% endif %> 32 | $code_table(frame) 33 | $var_table(frame['vars']) 34 | <% if evalex %> 35 |
36 |
[console ready]
37 | 38 | 39 | 40 |
41 | <% endif %> 42 |
43 | <% endfor %> 44 |
45 | 46 |
47 |

Here is the plain Python traceback for copy and paste:

48 |
$escape(plaintb)
49 |

50 | Create a new Paste with 51 | this traceback in the lodgeit pastebin. 52 |

53 |
54 | 55 | <% if req_vars %> 56 |

Request Data

57 |

The following list contains all important request variables. 58 | Select a header to expand the list.

59 | <% for num, (key, info) in enumerate(req_vars) %> 60 |
61 |
$escape(key)
62 |
$var_table(info)
63 |
64 | <% endfor %> 65 | <% endif %> 66 |
67 | 68 | 72 | 73 | 74 | 75 | 82 | -------------------------------------------------------------------------------- /werkzeug/debug/shared/codetable.tmpl: -------------------------------------------------------------------------------- 1 | 2 | <% for line in lines %> 3 | 4 | 5 | 6 | 7 | <% endfor %> 8 |
$line.lineno$line.code
9 | -------------------------------------------------------------------------------- /werkzeug/debug/shared/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/werkzeug/debug/shared/console.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/werkzeug/debug/shared/less.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/werkzeug/debug/shared/more.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/5b9359973802197fec7d1c31349a8f5346d0ddc9/werkzeug/debug/shared/source.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/vartable.tmpl: -------------------------------------------------------------------------------- 1 | 2 | <% if type == 'empty' %> 3 | 4 | <% elif type == 'simple' %> 5 | 6 | <% elif type == 'dict' %> 7 | 8 | <% for key, item in value %> 9 | 10 | <% endfor %> 11 | <% elif type == 'list' %> 12 | <% for item in value %> 13 | 14 | <% endfor %> 15 | <% endif %> 16 |
no data given
$escape(value)
NameValue
$escape(key)$escape(item)
$escape(item)
17 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/console.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Console // Werkzeug Debugger 6 | 7 | 8 | 9 | 13 | 14 | 15 |
16 |

Interactive Console

17 |
18 | In this console you can execute Python expressions in the context of the 19 | application. The initial namespace was created by the debugger automatically. 20 |
21 |
The Console requires JavaScript.
22 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/dump_object.html: -------------------------------------------------------------------------------- 1 |
2 |

$escape(title)

3 | <% if repr %> 4 |
$repr
5 | <% endif %> 6 | 7 | <% for key, value in items %> 8 | 9 | 10 | 11 | 12 | <% endfor %> 13 |
$escape(key)$value
14 |
15 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/frame.html: -------------------------------------------------------------------------------- 1 |
2 |

File "$escape(frame.filename)", 3 | line $frame.lineno, 4 | in $escape(frame.function_name)

5 |
${escape(frame.current_line.strip())}
6 |
7 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/help_command.html: -------------------------------------------------------------------------------- 1 | <%py missing = object() %> 2 |
3 | <% if title and text %> 4 |

$title

5 |
$text
6 | <% else %> 7 |

Help

8 |

Type help(object) for help about object.

9 | <% endif %> 10 |
11 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/source.html: -------------------------------------------------------------------------------- 1 | 2 | <% for line in lines %> 3 | 4 | 5 | 6 | 7 | <% endfor %> 8 |
${line.lineno}$escape(line.code)
9 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/traceback_full.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | $escape(traceback.exception) // Werkzeug Debugger 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |

$escape(traceback.exception_type)

18 |
19 |

$escape(traceback.exception)

20 |
21 |

Traceback (most recent call last)

22 | $traceback.render_summary(include_title=False) 23 |
24 |
25 |

26 | 27 | This is the Copy/Paste friendly version of the traceback. You can also paste this traceback into the public 29 | lodgeit pastebin: 30 |

31 | 32 |
33 |
34 |
35 | The debugger caught an exception in your WSGI application. You can now 36 | look at the traceback which led to the error. 37 | If you enable JavaScript you can also use additional features such as code 38 | execution (if the evalex feature is enabled), automatic pasting of the 39 | exceptions and much more. 40 |
41 | 45 |
46 | 47 | 48 | 56 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/traceback_plaintext.html: -------------------------------------------------------------------------------- 1 | Traceback (most recent call last): 2 | <% for frame in traceback.frames %> 3 | File "$frame.filename", line $frame.lineno, in $frame.function_name 4 | $frame.current_line.strip() 5 | <% endfor %> 6 | $traceback.exception 7 | -------------------------------------------------------------------------------- /werkzeug/debug/templates/traceback_summary.html: -------------------------------------------------------------------------------- 1 |
2 | <% if traceback.is_syntax_error %> 3 | <% if include_title %> 4 |

Syntax Error

5 | <% endif %> 6 |
    7 | <% for frame in traceback.frames %> 8 |
  • $frame.render()
  • 9 | <% endfor %> 10 |
11 |
$escape(traceback.exception)
12 | <% else %> 13 | <% if include_title %> 14 |

Traceback (most recent call last):

15 | <% endif %> 16 |
    17 | <% for frame in traceback.frames %> 18 | title="$escape(frame.info, True)"<% endif %>>$frame.render() 19 | <% endfor %> 20 |
21 |
$escape(traceback.exception)
22 | <% endif %> 23 |
24 | -------------------------------------------------------------------------------- /werkzeug/debug/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.debug.utils 4 | ~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Various other utilities. 7 | 8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 9 | :license: BSD. 10 | """ 11 | from os.path import join, dirname 12 | from werkzeug.templates import Template 13 | 14 | 15 | def get_template(filename): 16 | return Template.from_file(join(dirname(__file__), 'templates', filename)) 17 | 18 | 19 | def render_template(template_filename, **context): 20 | return get_template(template_filename).render(**context) 21 | -------------------------------------------------------------------------------- /werkzeug/posixemulation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r""" 3 | werkzeug.posixemulation 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Provides a POSIX emulation for some features that are relevant to 7 | web applications. The main purpose is to simplify support for 8 | systems such as Windows NT that are not 100% POSIX compatible. 9 | 10 | Currently this only implements a :func:`rename` function that 11 | follows POSIX semantics. Eg: if the target file already exists it 12 | will be replaced without asking. 13 | 14 | This module was introduced in 0.6.1 and is not a public interface. 15 | It might become one in later versions of Werkzeug. 16 | 17 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 18 | :license: BSD, see LICENSE for more details. 19 | """ 20 | import os 21 | import errno 22 | import time 23 | import random 24 | 25 | 26 | can_rename_open_file = False 27 | if os.name == 'nt': # pragma: no cover 28 | _rename = lambda src, dst: False 29 | _rename_atomic = lambda src, dst: False 30 | 31 | try: 32 | import ctypes 33 | 34 | _MOVEFILE_REPLACE_EXISTING = 0x1 35 | _MOVEFILE_WRITE_THROUGH = 0x8 36 | _MoveFileEx = ctypes.windll.kernel32.MoveFileExW 37 | 38 | def _rename(src, dst): 39 | if not isinstance(src, unicode): 40 | src = unicode(src, sys.getfilesystemencoding()) 41 | if not isinstance(dst, unicode): 42 | dst = unicode(dst, sys.getfilesystemencoding()) 43 | if _rename_atomic(src, dst): 44 | return True 45 | retry = 0 46 | rv = False 47 | while not rv and retry < 100: 48 | rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING | 49 | _MOVEFILE_WRITE_THROUGH) 50 | if not rv: 51 | time.sleep(0.001) 52 | retry += 1 53 | return rv 54 | 55 | # new in Vista and Windows Server 2008 56 | _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction 57 | _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction 58 | _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW 59 | _CloseHandle = ctypes.windll.kernel32.CloseHandle 60 | can_rename_open_file = True 61 | 62 | def _rename_atomic(src, dst): 63 | ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename') 64 | if ta == -1: 65 | return False 66 | try: 67 | retry = 0 68 | rv = False 69 | while not rv and retry < 100: 70 | rv = _MoveFileTransacted(src, dst, None, None, 71 | _MOVEFILE_REPLACE_EXISTING | 72 | _MOVEFILE_WRITE_THROUGH, ta) 73 | if rv: 74 | rv = _CommitTransaction(ta) 75 | break 76 | else: 77 | time.sleep(0.001) 78 | retry += 1 79 | return rv 80 | finally: 81 | _CloseHandle(ta) 82 | except Exception: 83 | pass 84 | 85 | def rename(src, dst): 86 | # Try atomic or pseudo-atomic rename 87 | if _rename(src, dst): 88 | return 89 | # Fall back to "move away and replace" 90 | try: 91 | os.rename(src, dst) 92 | except OSError, e: 93 | if e.errno != errno.EEXIST: 94 | raise 95 | old = "%s-%08x" % (dst, random.randint(0, sys.maxint)) 96 | os.rename(dst, old) 97 | os.rename(src, dst) 98 | try: 99 | os.unlink(old) 100 | except Exception: 101 | pass 102 | else: 103 | rename = os.rename 104 | can_rename_open_file = True 105 | -------------------------------------------------------------------------------- /werkzeug/security.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.security 4 | ~~~~~~~~~~~~~~~~~ 5 | 6 | Security related helpers such as secure password hashing tools. 7 | 8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import hmac 12 | import string 13 | from random import SystemRandom 14 | 15 | # because the API of hmac changed with the introduction of the 16 | # new hashlib module, we have to support both. This sets up a 17 | # mapping to the digest factory functions and the digest modules 18 | # (or factory functions with changed API) 19 | try: 20 | from hashlib import sha1, md5 21 | _hash_funcs = _hash_mods = {'sha1': sha1, 'md5': md5} 22 | _sha1_mod = sha1 23 | _md5_mod = md5 24 | except ImportError: 25 | import sha as _sha1_mod, md5 as _md5_mod 26 | _hash_mods = {'sha1': _sha1_mod, 'md5': _md5_mod} 27 | _hash_funcs = {'sha1': _sha1_mod.new, 'md5': _md5_mod.new} 28 | 29 | 30 | SALT_CHARS = string.letters + string.digits 31 | 32 | 33 | _sys_rng = SystemRandom() 34 | 35 | 36 | def gen_salt(length): 37 | """Generate a random string of SALT_CHARS with specified ``length``.""" 38 | if length <= 0: 39 | raise ValueError('requested salt of length <= 0') 40 | return ''.join(_sys_rng.choice(SALT_CHARS) for _ in xrange(length)) 41 | 42 | 43 | def _hash_internal(method, salt, password): 44 | """Internal password hash helper. Supports plaintext without salt, 45 | unsalted and salted passwords. In case salted passwords are used 46 | hmac is used. 47 | """ 48 | if method == 'plain': 49 | return password 50 | if salt: 51 | if method not in _hash_mods: 52 | return None 53 | if isinstance(salt, unicode): 54 | salt = salt.encode('utf-8') 55 | h = hmac.new(salt, None, _hash_mods[method]) 56 | else: 57 | if method not in _hash_funcs: 58 | return None 59 | h = _hash_funcs[method]() 60 | if isinstance(password, unicode): 61 | password = password.encode('utf-8') 62 | h.update(password) 63 | return h.hexdigest() 64 | 65 | 66 | def generate_password_hash(password, method='sha1', salt_length=8): 67 | """Hash a password with the given method and salt with with a string of 68 | the given length. The format of the string returned includes the method 69 | that was used so that :func:`check_password_hash` can check the hash. 70 | 71 | The format for the hashed string looks like this:: 72 | 73 | method$salt$hash 74 | 75 | This method can **not** generate unsalted passwords but it is possible 76 | to set the method to plain to enforce plaintext passwords. If a salt 77 | is used, hmac is used internally to salt the password. 78 | 79 | :param password: the password to hash 80 | :param method: the hash method to use (``'md5'`` or ``'sha1'``) 81 | :param salt_length: the lengt of the salt in letters 82 | """ 83 | salt = method != 'plain' and gen_salt(salt_length) or '' 84 | h = _hash_internal(method, salt, password) 85 | if h is None: 86 | raise TypeError('invalid method %r' % method) 87 | return '%s$%s$%s' % (method, salt, h) 88 | 89 | 90 | def check_password_hash(pwhash, password): 91 | """check a password against a given salted and hashed password value. 92 | In order to support unsalted legacy passwords this method supports 93 | plain text passwords, md5 and sha1 hashes (both salted and unsalted). 94 | 95 | Returns `True` if the password matched, `False` otherwise. 96 | 97 | :param pwhash: a hashed string like returned by 98 | :func:`generate_password_hash` 99 | :param password: the plaintext password to compare against the hash 100 | """ 101 | if pwhash.count('$') < 2: 102 | return False 103 | method, salt, hashval = pwhash.split('$', 2) 104 | return _hash_internal(method, salt, password) == hashval 105 | --------------------------------------------------------------------------------