├── template ├── body │ ├── CSS.tmpl │ ├── Diff.tmpl │ ├── Lua.tmpl │ ├── PHP.tmpl │ ├── Perl.tmpl │ ├── R.tmpl │ ├── Ruby.tmpl │ ├── SQL.tmpl │ ├── TCL.tmpl │ ├── Text.tmpl │ ├── XML.tmpl │ ├── YAML.tmpl │ ├── Clojure.tmpl │ ├── Graphviz.tmpl │ ├── Groovy.tmpl │ ├── Makefile.tmpl │ ├── Markdown.tmpl │ ├── Matlab.tmpl │ ├── OCaml.tmpl │ ├── Python.tmpl │ ├── Textile.tmpl │ ├── AppleScript.tmpl │ ├── Batch File.tmpl │ ├── ShellScript.tmpl │ ├── RestructuredText.tmpl │ ├── JavaScript.tmpl │ ├── Lisp.tmpl │ ├── Haskell.tmpl │ ├── Scala.tmpl │ ├── D.tmpl │ ├── Pascal.tmpl │ ├── C#.tmpl │ ├── C.tmpl │ ├── Go.tmpl │ ├── LaTeX.tmpl │ ├── ASP.tmpl │ ├── Erlang.tmpl │ ├── Java.tmpl │ ├── C++.tmpl │ ├── Objective-C.tmpl │ ├── HTML.tmpl │ └── ActionScript.tmpl └── header │ ├── R.tmpl │ ├── ASP.tmpl │ ├── Diff.tmpl │ ├── Groovy.tmpl │ ├── HTML.tmpl │ ├── Text.tmpl │ ├── Textile.tmpl │ ├── XML.tmpl │ ├── YAML.tmpl │ ├── Graphviz.tmpl │ ├── Makefile.tmpl │ ├── Markdown.tmpl │ ├── RestructuredText.tmpl │ ├── Clojure.tmpl │ ├── Erlang.tmpl │ ├── LaTeX.tmpl │ ├── Lua.tmpl │ ├── Matlab.tmpl │ ├── Perl.tmpl │ ├── TCL.tmpl │ ├── Haskell.tmpl │ ├── Batch File.tmpl │ ├── C#.tmpl │ ├── C++.tmpl │ ├── C.tmpl │ ├── CSS.tmpl │ ├── D.tmpl │ ├── Go.tmpl │ ├── Lisp.tmpl │ ├── SQL.tmpl │ ├── Java.tmpl │ ├── OCaml.tmpl │ ├── Pascal.tmpl │ ├── SCSS.tmpl │ ├── Scala.tmpl │ ├── ActionScript.tmpl │ ├── AppleScript.tmpl │ ├── JavaScript.tmpl │ ├── Objective-C.tmpl │ ├── PHP.tmpl │ ├── Ruby.tmpl │ ├── ShellScript.tmpl │ └── Python.tmpl ├── .gitignore ├── doc ├── img │ ├── update.gif │ ├── new-file.gif │ ├── add-header.gif │ └── add-header-dir.gif ├── index.rst ├── make.bat └── conf.py ├── .gitmodules ├── Side Bar.sublime-menu ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── Default.sublime-commands ├── Context.sublime-menu ├── markupsafe ├── _compat.py ├── _native.py ├── tests.py ├── _constants.py ├── _speedups.c └── __init__.py ├── jinja2 ├── defaults.py ├── constants.py ├── __init__.py ├── optimizer.py ├── _compat.py ├── visitor.py ├── tests.py ├── meta.py ├── exceptions.py ├── bccache.py ├── debug.py ├── sandbox.py ├── utils.py ├── loaders.py └── runtime.py ├── Main.sublime-menu ├── README.rst └── FileHeader.sublime-settings /template/body/CSS.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Diff.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Lua.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/PHP.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Perl.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/R.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Ruby.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/SQL.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/TCL.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Text.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/XML.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/YAML.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/R.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Clojure.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Graphviz.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Groovy.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Makefile.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Markdown.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Matlab.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/OCaml.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Python.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Textile.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/ASP.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Diff.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Groovy.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/HTML.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Text.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Textile.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/XML.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/YAML.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/AppleScript.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/Batch File.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/ShellScript.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Graphviz.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Makefile.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/Markdown.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/RestructuredText.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/header/RestructuredText.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/body/JavaScript.tmpl: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | -------------------------------------------------------------------------------- /template/body/Lisp.tmpl: -------------------------------------------------------------------------------- 1 | (DEFUN MAIN () 2 | 3 | ) 4 | -------------------------------------------------------------------------------- /template/body/Haskell.tmpl: -------------------------------------------------------------------------------- 1 | module Main 2 | where 3 | -------------------------------------------------------------------------------- /template/body/Scala.tmpl: -------------------------------------------------------------------------------- 1 | object Main extends Application { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /template/body/D.tmpl: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | void main(){ 4 | 5 | } 6 | -------------------------------------------------------------------------------- /template/body/Pascal.tmpl: -------------------------------------------------------------------------------- 1 | program Main(output); 2 | begin 3 | 4 | end. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *.pyc 4 | github.py 5 | doc/Makefile 6 | doc/_build/ 7 | -------------------------------------------------------------------------------- /doc/img/update.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiyanhui/FileHeader/HEAD/doc/img/update.gif -------------------------------------------------------------------------------- /template/body/C#.tmpl: -------------------------------------------------------------------------------- 1 | class MainClass { 2 | static void Main() { 3 | 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /doc/img/new-file.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiyanhui/FileHeader/HEAD/doc/img/new-file.gif -------------------------------------------------------------------------------- /template/body/C.tmpl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /template/body/Go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /template/body/LaTeX.tmpl: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \begin{document} 3 | 4 | \end{document} 5 | -------------------------------------------------------------------------------- /doc/img/add-header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiyanhui/FileHeader/HEAD/doc/img/add-header.gif -------------------------------------------------------------------------------- /doc/img/add-header-dir.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiyanhui/FileHeader/HEAD/doc/img/add-header-dir.gif -------------------------------------------------------------------------------- /template/body/ASP.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | <% Response.Write "" %> 4 | 5 | 6 | -------------------------------------------------------------------------------- /template/body/Erlang.tmpl: -------------------------------------------------------------------------------- 1 | -module(erlang_hw). 2 | -export([start/0]). 3 | 4 | start() -> 5 | io:format(""). 6 | -------------------------------------------------------------------------------- /template/body/Java.tmpl: -------------------------------------------------------------------------------- 1 | public class java { 2 | public static void main(String[] args) { 3 | 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /template/body/C++.tmpl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int main(){ 6 | 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/_themes/plain"] 2 | path = doc/_themes/plain 3 | url = git://github.com/hit9/sphinx-theme-rux.git 4 | -------------------------------------------------------------------------------- /template/body/Objective-C.tmpl: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, char *argv[]) { 4 | 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /template/body/HTML.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /template/header/Clojure.tmpl: -------------------------------------------------------------------------------- 1 | ; @Author: {{author}} 2 | ; @Date: {{create_time}} 3 | ; @Last Modified by: {{last_modified_by}} 4 | ; @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/Erlang.tmpl: -------------------------------------------------------------------------------- 1 | % @Author: {{author}} 2 | % @Date: {{create_time}} 3 | % @Last Modified by: {{last_modified_by}} 4 | % @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/LaTeX.tmpl: -------------------------------------------------------------------------------- 1 | % @Author: {{author}} 2 | % @Date: {{create_time}} 3 | % @Last Modified by: {{last_modified_by}} 4 | % @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/Lua.tmpl: -------------------------------------------------------------------------------- 1 | -- @Author: {{author}} 2 | -- @Date: {{create_time}} 3 | -- @Last Modified by: {{last_modified_by}} 4 | -- @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/Matlab.tmpl: -------------------------------------------------------------------------------- 1 | % @Author: {{author}} 2 | % @Date: {{create_time}} 3 | % @Last Modified by: {{last_modified_by}} 4 | % @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/Perl.tmpl: -------------------------------------------------------------------------------- 1 | # @Author: {{author}} 2 | # @Date: {{create_time}} 3 | # @Last Modified by: {{last_modified_by}} 4 | # @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/TCL.tmpl: -------------------------------------------------------------------------------- 1 | # @Author: {{author}} 2 | # @Date: {{create_time}} 3 | # @Last Modified by: {{last_modified_by}} 4 | # @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/Haskell.tmpl: -------------------------------------------------------------------------------- 1 | -- @Author: {{author}} 2 | -- @Date: {{create_time}} 3 | -- @Last Modified by: {{last_modified_by}} 4 | -- @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/Batch File.tmpl: -------------------------------------------------------------------------------- 1 | @REM @Author: {{author}} 2 | @REM @Date: {{create_time}} 3 | @REM @Last Modified by: {{last_modified_by}} 4 | @REM Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/C#.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/C++.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/C.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/CSS.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/D.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/Go.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/Lisp.tmpl: -------------------------------------------------------------------------------- 1 | ;;;; @Author: {{author}} 2 | ;;;; @Date: {{create_time}} 3 | ;;;; @Last Modified by: {{last_modified_by}} 4 | ;;;; @Last Modified time: {{last_modified_time}} 5 | 6 | -------------------------------------------------------------------------------- /template/header/SQL.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/body/ActionScript.tmpl: -------------------------------------------------------------------------------- 1 | package { 2 | import flash.display.Sprite; 3 | public class Main extends Sprite { 4 | public function Main():void { 5 | 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /template/header/Java.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/OCaml.tmpl: -------------------------------------------------------------------------------- 1 | (* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | *) 7 | 8 | -------------------------------------------------------------------------------- /template/header/Pascal.tmpl: -------------------------------------------------------------------------------- 1 | (* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | *) 7 | 8 | -------------------------------------------------------------------------------- /template/header/SCSS.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/Scala.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/ActionScript.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/AppleScript.tmpl: -------------------------------------------------------------------------------- 1 | (* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | *) 7 | 8 | -------------------------------------------------------------------------------- /template/header/JavaScript.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/Objective-C.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/PHP.tmpl: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: {{author}} 3 | * @Date: {{create_time}} 4 | * @Last Modified by: {{last_modified_by}} 5 | * @Last Modified time: {{last_modified_time}} 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /template/header/Ruby.tmpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # @Author: {{author}} 3 | # @Date: {{create_time}} 4 | # @Last Modified by: {{last_modified_by}} 5 | # @Last Modified time: {{last_modified_time}} 6 | 7 | -------------------------------------------------------------------------------- /template/header/ShellScript.tmpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # @Author: {{author}} 3 | # @Date: {{create_time}} 4 | # @Last Modified by: {{last_modified_by}} 5 | # @Last Modified time: {{last_modified_time}} 6 | 7 | -------------------------------------------------------------------------------- /template/header/Python.tmpl: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author: {{author}} 3 | # @Date: {{create_time}} 4 | # @Last Modified by: {{last_modified_by}} 5 | # @Last Modified time: {{last_modified_time}} 6 | 7 | -------------------------------------------------------------------------------- /Side Bar.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "File Header", 4 | "children": 5 | [ 6 | { "caption": "New File...", "command": "file_header_new_file", "args": {"paths": []} }, 7 | { "caption": "Add Header...", "command": "file_header_add_header", "args": {"paths": []} } 8 | ] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+alt+n"], 4 | "command": "file_header_new_file", 5 | "args":{ 6 | "paths": [] 7 | } 8 | }, 9 | { 10 | "keys": ["ctrl+alt+a"], 11 | "command": "file_header_add_header", 12 | "args":{ 13 | "paths": [] 14 | } 15 | } 16 | ] -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["super+alt+n"], 4 | "command": "file_header_new_file", 5 | "args":{ 6 | "paths": [] 7 | } 8 | }, 9 | { 10 | "keys": ["super+alt+a"], 11 | "command": "file_header_add_header", 12 | "args":{ 13 | "paths": [] 14 | } 15 | } 16 | ] -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+alt+n"], 4 | "command": "file_header_new_file", 5 | "args":{ 6 | "paths": [] 7 | } 8 | }, 9 | { 10 | "keys": ["ctrl+alt+a"], 11 | "command": "file_header_add_header", 12 | "args":{ 13 | "paths": [] 14 | } 15 | } 16 | ] -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "FileHeader: New File", 4 | "command": "file_header_new_file", 5 | "args": { 6 | "paths": [] 7 | } 8 | }, 9 | { 10 | "caption": "FileHeader: Add Header", 11 | "command": "file_header_add_header", 12 | "args": { 13 | "paths": [] 14 | } 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "File Header", 4 | "children": 5 | [ 6 | { 7 | "caption": "New File", 8 | "command": "file_header_new_file", 9 | "args":{ 10 | "paths": [] 11 | } 12 | }, 13 | { 14 | "caption": "Add Header", 15 | "command": "file_header_add_header", 16 | "args":{ 17 | "paths": [] 18 | } 19 | } 20 | ] 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /markupsafe/_compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe._compat 4 | ~~~~~~~~~~~~~~~~~~ 5 | 6 | Compatibility module for different Python versions. 7 | 8 | :copyright: (c) 2013 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import sys 12 | 13 | PY2 = sys.version_info[0] == 2 14 | 15 | if not PY2: 16 | text_type = str 17 | string_types = (str,) 18 | unichr = chr 19 | int_types = (int,) 20 | else: 21 | text_type = unicode 22 | string_types = (str, unicode) 23 | unichr = unichr 24 | int_types = (int, long) 25 | -------------------------------------------------------------------------------- /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._compat import range_type 12 | from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner 13 | 14 | 15 | # defaults for the parser / lexer 16 | BLOCK_START_STRING = '{%' 17 | BLOCK_END_STRING = '%}' 18 | VARIABLE_START_STRING = '{{' 19 | VARIABLE_END_STRING = '}}' 20 | COMMENT_START_STRING = '{#' 21 | COMMENT_END_STRING = '#}' 22 | LINE_STATEMENT_PREFIX = None 23 | LINE_COMMENT_PREFIX = None 24 | TRIM_BLOCKS = False 25 | LSTRIP_BLOCKS = False 26 | NEWLINE_SEQUENCE = '\n' 27 | KEEP_TRAILING_NEWLINE = False 28 | 29 | 30 | # default filters, tests and namespace 31 | from jinja2.filters import FILTERS as DEFAULT_FILTERS 32 | from jinja2.tests import TESTS as DEFAULT_TESTS 33 | DEFAULT_NAMESPACE = { 34 | 'range': range_type, 35 | 'dict': lambda **kw: kw, 36 | 'lipsum': generate_lorem_ipsum, 37 | 'cycler': Cycler, 38 | 'joiner': Joiner 39 | } 40 | 41 | 42 | # export all constants 43 | __all__ = tuple(x for x in locals().keys() if x.isupper()) 44 | -------------------------------------------------------------------------------- /markupsafe/_native.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe._native 4 | ~~~~~~~~~~~~~~~~~~ 5 | 6 | Native Python implementation the C module is not compiled. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from markupsafe import Markup 12 | from markupsafe._compat import text_type 13 | 14 | 15 | def escape(s): 16 | """Convert the characters &, <, >, ' and " in string s to HTML-safe 17 | sequences. Use this if you need to display text that might contain 18 | such characters in HTML. Marks return value as markup string. 19 | """ 20 | if hasattr(s, '__html__'): 21 | return s.__html__() 22 | return Markup(text_type(s) 23 | .replace('&', '&') 24 | .replace('>', '>') 25 | .replace('<', '<') 26 | .replace("'", ''') 27 | .replace('"', '"') 28 | ) 29 | 30 | 31 | def escape_silent(s): 32 | """Like :func:`escape` but converts `None` into an empty 33 | markup string. 34 | """ 35 | if s is None: 36 | return Markup() 37 | return escape(s) 38 | 39 | 40 | def soft_unicode(s): 41 | """Make a string unicode if it isn't already. That way a markup 42 | string is not converted back to unicode. 43 | """ 44 | if not isinstance(s, text_type): 45 | s = text_type(s) 46 | return s 47 | -------------------------------------------------------------------------------- /jinja2/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja.constants 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Various constants. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | 13 | #: list of lorem ipsum words used by the lipsum() helper function 14 | LOREM_IPSUM_WORDS = u'''\ 15 | a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at 16 | auctor augue bibendum blandit class commodo condimentum congue consectetuer 17 | consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus 18 | diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend 19 | elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames 20 | faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac 21 | hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum 22 | justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem 23 | luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie 24 | mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non 25 | nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque 26 | penatibus per pharetra phasellus placerat platea porta porttitor posuere 27 | potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus 28 | ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit 29 | sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor 30 | tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices 31 | ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus 32 | viverra volutpat vulputate''' 33 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | **FileHeader** 是一个Sublime Text的File Templating插件。目前ST已经有几款File Templating插件了,像SublimeTmpl、Template​Ninja、File​Templates等,但是这些插件的功能太简单了,他们几乎都使用了ST的内置snippets来实现模板的渲染,并且支持的语言很有限(像SublimeTmpl仅支持Python、Ruby、JavaScript、PHP、HTML、CSS、XML),有的插件仅仅支持ST2(File​Templates),还有的使用起来及其不效率。 2 | 3 | 本插件与其他插件有很大的不同。 4 | 5 | - 将一个模板文件分为header和body两部分。允许用户自定义自己的模板文件。 6 | - FileHeader能够自动的监测创建新文件动作,自动的添加模板。 7 | - 不仅支持创建已经使用模板初始化好的文件,而且支持将header添加到已经存在的文件头部,并且支持批量添加。 8 | - 使用了非常强大并且很容易使用的 `Jinja2 `_ 模板系统,在模板文件里你可以完成很多复杂的初始化。 9 | - 几乎支持所有的编程语言,并且支持用户自定义语言。 10 | - 能够自动的更新文件最后修改时间。 11 | - 能够自动的更新文件最后的修改者,这在协同开发中是一个很有用的功能。 12 | - 支持ST2/ST3。 13 | 14 | 安装 15 | ------ 16 | 17 | 可以通过 **Package Control** 搜索 **FileHeader** 安装。 18 | 19 | 或者: 20 | 21 | 进入到你的"Packages"文件夹 **(Preferences / Browse Packages)** ,然后: 22 | 23 | .. code-block:: python 24 | 25 | git clone git@github.com:shiyanhui/FileHeader.git 26 | 27 | 使用 28 | ------ 29 | 30 | FileHeader非常容易使用。 31 | 32 | 创建新文件 33 | ````````` 34 | 35 | FileHeader能够自动的监测创建新文件动作,自动的添加模板。因此你可以用别的插件创建新文件,FileHeader会自动的给你添加模板。 36 | 37 | - Sidebar Menu 38 | 39 | .. image:: https://raw.github.com/shiyanhui/shiyanhui.github.io/master/images/FileHeader/new-file.gif 40 | 41 | - Shortcuts 42 | 43 | The default shortcuts is **super+alt+n** on OS X, **ctrl+alt+n** on Windows and Linux. 44 | 45 | - Context Menu 46 | 47 | Similar to **Sidebar Menu**. 48 | 49 | 50 | 添加文件头 51 | ````````` 52 | 53 | - Sidebar Menu 54 | 55 | .. image:: https://raw.github.com/shiyanhui/shiyanhui.github.io/master/images/FileHeader/add-header.gif 56 | 57 | - Shortcuts 58 | 59 | The default shortcuts is **super+alt+a** on OS X, **ctrl+alt+a** on Windows and Linux. 60 | 61 | - Context Menu 62 | 63 | Similar to **Sidebar Menu**. 64 | 65 | 批量添加文件头 66 | ````````` 67 | 68 | .. image:: https://raw.github.com/shiyanhui/shiyanhui.github.io/master/images/FileHeader/add-header-dir.gif 69 | 70 | 71 | 自动更新文件修改时间和最后修改者 72 | ````````` 73 | 74 | .. image:: https://raw.github.com/shiyanhui/shiyanhui.github.io/master/images/FileHeader/update.gif 75 | 76 | 77 | 详细设置及文档请看 `GitHub `_ 。 -------------------------------------------------------------------------------- /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 | __version__ = '2.8-dev' 31 | 32 | # high level interface 33 | from jinja2.environment import Environment, Template 34 | 35 | # loaders 36 | from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \ 37 | DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \ 38 | ModuleLoader 39 | 40 | # bytecode caches 41 | from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \ 42 | MemcachedBytecodeCache 43 | 44 | # undefined types 45 | from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined 46 | 47 | # exceptions 48 | from jinja2.exceptions import TemplateError, UndefinedError, \ 49 | TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \ 50 | TemplateAssertionError 51 | 52 | # decorators and public utilities 53 | from jinja2.filters import environmentfilter, contextfilter, \ 54 | evalcontextfilter 55 | from jinja2.utils import Markup, escape, clear_caches, \ 56 | environmentfunction, evalcontextfunction, contextfunction, \ 57 | is_undefined 58 | 59 | __all__ = [ 60 | 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader', 61 | 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader', 62 | 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache', 63 | 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined', 64 | 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound', 65 | 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', 66 | 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', 67 | 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', 68 | 'evalcontextfilter', 'evalcontextfunction' 69 | ] 70 | -------------------------------------------------------------------------------- /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/_compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2._compat 4 | ~~~~~~~~~~~~~~ 5 | 6 | Some py2/py3 compatibility support based on a stripped down 7 | version of six so we don't have to depend on a specific version 8 | of it. 9 | 10 | :copyright: Copyright 2013 by the Jinja team, see AUTHORS. 11 | :license: BSD, see LICENSE for details. 12 | """ 13 | import sys 14 | 15 | PY2 = sys.version_info[0] == 2 16 | PYPY = hasattr(sys, 'pypy_translation_info') 17 | _identity = lambda x: x 18 | 19 | 20 | if not PY2: 21 | unichr = chr 22 | range_type = range 23 | text_type = str 24 | string_types = (str,) 25 | 26 | iterkeys = lambda d: iter(d.keys()) 27 | itervalues = lambda d: iter(d.values()) 28 | iteritems = lambda d: iter(d.items()) 29 | 30 | import pickle 31 | from io import BytesIO, StringIO 32 | NativeStringIO = StringIO 33 | 34 | def reraise(tp, value, tb=None): 35 | if value.__traceback__ is not tb: 36 | raise value.with_traceback(tb) 37 | raise value 38 | 39 | ifilter = filter 40 | imap = map 41 | izip = zip 42 | intern = sys.intern 43 | 44 | implements_iterator = _identity 45 | implements_to_string = _identity 46 | encode_filename = _identity 47 | get_next = lambda x: x.__next__ 48 | 49 | else: 50 | unichr = unichr 51 | text_type = unicode 52 | range_type = xrange 53 | string_types = (str, unicode) 54 | 55 | iterkeys = lambda d: d.iterkeys() 56 | itervalues = lambda d: d.itervalues() 57 | iteritems = lambda d: d.iteritems() 58 | 59 | import cPickle as pickle 60 | from cStringIO import StringIO as BytesIO, StringIO 61 | NativeStringIO = BytesIO 62 | 63 | exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') 64 | 65 | from itertools import imap, izip, ifilter 66 | intern = intern 67 | 68 | def implements_iterator(cls): 69 | cls.next = cls.__next__ 70 | del cls.__next__ 71 | return cls 72 | 73 | def implements_to_string(cls): 74 | cls.__unicode__ = cls.__str__ 75 | cls.__str__ = lambda x: x.__unicode__().encode('utf-8') 76 | return cls 77 | 78 | get_next = lambda x: x.next 79 | 80 | def encode_filename(filename): 81 | if isinstance(filename, unicode): 82 | return filename.encode('utf-8') 83 | return filename 84 | 85 | 86 | def with_metaclass(meta, *bases): 87 | # This requires a bit of explanation: the basic idea is to make a 88 | # dummy metaclass for one level of class instanciation that replaces 89 | # itself with the actual metaclass. Because of internal type checks 90 | # we also need to make sure that we downgrade the custom metaclass 91 | # for one level to something closer to type (that's why __call__ and 92 | # __init__ comes back from type etc.). 93 | # 94 | # This has the advantage over six.with_metaclass in that it does not 95 | # introduce dummy classes into the final MRO. 96 | class metaclass(meta): 97 | __call__ = type.__call__ 98 | __init__ = type.__init__ 99 | def __new__(cls, name, this_bases, d): 100 | if this_bases is None: 101 | return type.__new__(cls, name, (), d) 102 | return meta(name, bases, d) 103 | return metaclass('temporary_class', None, {}) 104 | 105 | 106 | try: 107 | from urllib.parse import quote_from_bytes as url_quote 108 | except ImportError: 109 | from urllib import quote as url_quote 110 | -------------------------------------------------------------------------------- /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.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "preferences", 4 | "children": 5 | [ 6 | { 7 | "caption": "Package Settings", 8 | "mnemonic": "P", 9 | "id": "package-settings", 10 | "children": 11 | [ 12 | { 13 | "caption": "File Header", 14 | "children": 15 | [ 16 | { 17 | "command": "open_file", 18 | "args": {"file": "${packages}/FileHeader/FileHeader.sublime-settings"}, 19 | "caption": "Settings – Default" 20 | }, 21 | { 22 | "command": "open_file", 23 | "args": {"file": "${packages}/User/FileHeader.sublime-settings"}, 24 | "caption": "Settings – User" 25 | }, 26 | { "caption": "-" }, 27 | { 28 | "command": "open_file", 29 | "args": { 30 | "file": "${packages}/FileHeader/Default (OSX).sublime-keymap", 31 | "platform": "OSX" 32 | }, 33 | "caption": "Key Bindings – Default" 34 | }, 35 | { 36 | "command": "open_file", 37 | "args": { 38 | "file": "${packages}/FileHeader/Default (Linux).sublime-keymap", 39 | "platform": "Linux" 40 | }, 41 | "caption": "Key Bindings – Default" 42 | }, 43 | { 44 | "command": "open_file", 45 | "args": { 46 | "file": "${packages}/FileHeader/Default (Windows).sublime-keymap", 47 | "platform": "Windows" 48 | }, 49 | "caption": "Key Bindings – Default" 50 | }, 51 | { 52 | "command": "open_file", 53 | "args": { 54 | "file": "${packages}/User/Default (OSX).sublime-keymap", 55 | "platform": "OSX" 56 | }, 57 | "caption": "Key Bindings – User" 58 | }, 59 | { 60 | "command": "open_file", 61 | "args": { 62 | "file": "${packages}/User/Default (Linux).sublime-keymap", 63 | "platform": "Linux" 64 | }, 65 | "caption": "Key Bindings – User" 66 | }, 67 | { 68 | "command": "open_file", 69 | "args": { 70 | "file": "${packages}/User/Default (Windows).sublime-keymap", 71 | "platform": "Windows" 72 | }, 73 | "caption": "Key Bindings – User" 74 | }, 75 | { "caption": "-" } 76 | ] 77 | } 78 | ] 79 | } 80 | ] 81 | } 82 | ] 83 | -------------------------------------------------------------------------------- /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 collections import Mapping 13 | from jinja2.runtime import Undefined 14 | from jinja2._compat import text_type, string_types 15 | 16 | 17 | number_re = re.compile(r'^-?\d+(\.\d+)?$') 18 | regex_type = type(number_re) 19 | 20 | 21 | test_callable = callable 22 | 23 | 24 | def test_odd(value): 25 | """Return true if the variable is odd.""" 26 | return value % 2 == 1 27 | 28 | 29 | def test_even(value): 30 | """Return true if the variable is even.""" 31 | return value % 2 == 0 32 | 33 | 34 | def test_divisibleby(value, num): 35 | """Check if a variable is divisible by a number.""" 36 | return value % num == 0 37 | 38 | 39 | def test_defined(value): 40 | """Return true if the variable is defined: 41 | 42 | .. sourcecode:: jinja 43 | 44 | {% if variable is defined %} 45 | value of variable: {{ variable }} 46 | {% else %} 47 | variable is not defined 48 | {% endif %} 49 | 50 | See the :func:`default` filter for a simple way to set undefined 51 | variables. 52 | """ 53 | return not isinstance(value, Undefined) 54 | 55 | 56 | def test_undefined(value): 57 | """Like :func:`defined` but the other way round.""" 58 | return isinstance(value, Undefined) 59 | 60 | 61 | def test_none(value): 62 | """Return true if the variable is none.""" 63 | return value is None 64 | 65 | 66 | def test_lower(value): 67 | """Return true if the variable is lowercased.""" 68 | return text_type(value).islower() 69 | 70 | 71 | def test_upper(value): 72 | """Return true if the variable is uppercased.""" 73 | return text_type(value).isupper() 74 | 75 | 76 | def test_string(value): 77 | """Return true if the object is a string.""" 78 | return isinstance(value, string_types) 79 | 80 | 81 | def test_mapping(value): 82 | """Return true if the object is a mapping (dict etc.). 83 | 84 | .. versionadded:: 2.6 85 | """ 86 | return isinstance(value, Mapping) 87 | 88 | 89 | def test_number(value): 90 | """Return true if the variable is a number.""" 91 | return isinstance(value, (int, float, complex)) 92 | 93 | 94 | def test_sequence(value): 95 | """Return true if the variable is a sequence. Sequences are variables 96 | that are iterable. 97 | """ 98 | try: 99 | len(value) 100 | value.__getitem__ 101 | except: 102 | return False 103 | return True 104 | 105 | 106 | def test_sameas(value, other): 107 | """Check if an object points to the same memory address than another 108 | object: 109 | 110 | .. sourcecode:: jinja 111 | 112 | {% if foo.attribute is sameas false %} 113 | the foo attribute really is the `False` singleton 114 | {% endif %} 115 | """ 116 | return value is other 117 | 118 | 119 | def test_iterable(value): 120 | """Check if it's possible to iterate over an object.""" 121 | try: 122 | iter(value) 123 | except TypeError: 124 | return False 125 | return True 126 | 127 | 128 | def test_escaped(value): 129 | """Check if the value is escaped.""" 130 | return hasattr(value, '__html__') 131 | 132 | 133 | TESTS = { 134 | 'odd': test_odd, 135 | 'even': test_even, 136 | 'divisibleby': test_divisibleby, 137 | 'defined': test_defined, 138 | 'undefined': test_undefined, 139 | 'none': test_none, 140 | 'lower': test_lower, 141 | 'upper': test_upper, 142 | 'string': test_string, 143 | 'mapping': test_mapping, 144 | 'number': test_number, 145 | 'sequence': test_sequence, 146 | 'iterable': test_iterable, 147 | 'callable': test_callable, 148 | 'sameas': test_sameas, 149 | 'escaped': test_escaped 150 | } 151 | -------------------------------------------------------------------------------- /markupsafe/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import gc 3 | import unittest 4 | from markupsafe import Markup, escape, escape_silent 5 | from markupsafe._compat import text_type 6 | 7 | 8 | class MarkupTestCase(unittest.TestCase): 9 | 10 | def test_adding(self): 11 | # adding two strings should escape the unsafe one 12 | unsafe = '' 13 | safe = Markup('username') 14 | assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) 15 | 16 | def test_string_interpolation(self): 17 | # string interpolations are safe to use too 18 | assert Markup('%s') % '' == \ 19 | '<bad user>' 20 | assert Markup('%(username)s') % { 21 | 'username': '' 22 | } == '<bad user>' 23 | 24 | assert Markup('%i') % 3.14 == '3' 25 | assert Markup('%.2f') % 3.14 == '3.14' 26 | 27 | def test_type_behavior(self): 28 | # an escaped object is markup too 29 | assert type(Markup('foo') + 'bar') is Markup 30 | 31 | # and it implements __html__ by returning itself 32 | x = Markup("foo") 33 | assert x.__html__() is x 34 | 35 | def test_html_interop(self): 36 | # it also knows how to treat __html__ objects 37 | class Foo(object): 38 | def __html__(self): 39 | return 'awesome' 40 | def __unicode__(self): 41 | return 'awesome' 42 | __str__ = __unicode__ 43 | assert Markup(Foo()) == 'awesome' 44 | assert Markup('%s') % Foo() == \ 45 | 'awesome' 46 | 47 | def test_tuple_interpol(self): 48 | self.assertEqual(Markup('%s:%s') % ( 49 | '', 50 | '', 51 | ), Markup(u'<foo>:<bar>')) 52 | 53 | def test_dict_interpol(self): 54 | self.assertEqual(Markup('%(foo)s') % { 55 | 'foo': '', 56 | }, Markup(u'<foo>')) 57 | self.assertEqual(Markup('%(foo)s:%(bar)s') % { 58 | 'foo': '', 59 | 'bar': '', 60 | }, Markup(u'<foo>:<bar>')) 61 | 62 | def test_escaping(self): 63 | # escaping and unescaping 64 | assert escape('"<>&\'') == '"<>&'' 65 | assert Markup("Foo & Bar").striptags() == "Foo & Bar" 66 | assert Markup("<test>").unescape() == "" 67 | 68 | def test_all_set(self): 69 | import markupsafe as markup 70 | for item in markup.__all__: 71 | getattr(markup, item) 72 | 73 | def test_escape_silent(self): 74 | assert escape_silent(None) == Markup() 75 | assert escape(None) == Markup(None) 76 | assert escape_silent('') == Markup(u'<foo>') 77 | 78 | def test_splitting(self): 79 | self.assertEqual(Markup('a b').split(), [ 80 | Markup('a'), 81 | Markup('b') 82 | ]) 83 | self.assertEqual(Markup('a b').rsplit(), [ 84 | Markup('a'), 85 | Markup('b') 86 | ]) 87 | self.assertEqual(Markup('a\nb').splitlines(), [ 88 | Markup('a'), 89 | Markup('b') 90 | ]) 91 | 92 | def test_mul(self): 93 | self.assertEqual(Markup('a') * 3, Markup('aaa')) 94 | 95 | 96 | class MarkupLeakTestCase(unittest.TestCase): 97 | 98 | def test_markup_leaks(self): 99 | counts = set() 100 | for count in range(20): 101 | for item in range(1000): 102 | escape("foo") 103 | escape("") 104 | escape(u"foo") 105 | escape(u"") 106 | counts.add(len(gc.get_objects())) 107 | assert len(counts) == 1, 'ouch, c extension seems to leak objects' 108 | 109 | 110 | def suite(): 111 | suite = unittest.TestSuite() 112 | suite.addTest(unittest.makeSuite(MarkupTestCase)) 113 | 114 | # this test only tests the c extension 115 | if not hasattr(escape, 'func_code'): 116 | suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) 117 | 118 | return suite 119 | 120 | 121 | if __name__ == '__main__': 122 | unittest.main(defaultTest='suite') 123 | 124 | # vim:sts=4:sw=4:et: 125 | -------------------------------------------------------------------------------- /jinja2/meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.meta 4 | ~~~~~~~~~~~ 5 | 6 | This module implements various functions that exposes information about 7 | templates that might be interesting for various kinds of applications. 8 | 9 | :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | from jinja2 import nodes 13 | from jinja2.compiler import CodeGenerator 14 | from jinja2._compat import string_types 15 | 16 | 17 | class TrackingCodeGenerator(CodeGenerator): 18 | """We abuse the code generator for introspection.""" 19 | 20 | def __init__(self, environment): 21 | CodeGenerator.__init__(self, environment, '', 22 | '') 23 | self.undeclared_identifiers = set() 24 | 25 | def write(self, x): 26 | """Don't write.""" 27 | 28 | def pull_locals(self, frame): 29 | """Remember all undeclared identifiers.""" 30 | self.undeclared_identifiers.update(frame.identifiers.undeclared) 31 | 32 | 33 | def find_undeclared_variables(ast): 34 | """Returns a set of all variables in the AST that will be looked up from 35 | the context at runtime. Because at compile time it's not known which 36 | variables will be used depending on the path the execution takes at 37 | runtime, all variables are returned. 38 | 39 | >>> from jinja2 import Environment, meta 40 | >>> env = Environment() 41 | >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') 42 | >>> meta.find_undeclared_variables(ast) 43 | set(['bar']) 44 | 45 | .. admonition:: Implementation 46 | 47 | Internally the code generator is used for finding undeclared variables. 48 | This is good to know because the code generator might raise a 49 | :exc:`TemplateAssertionError` during compilation and as a matter of 50 | fact this function can currently raise that exception as well. 51 | """ 52 | codegen = TrackingCodeGenerator(ast.environment) 53 | codegen.visit(ast) 54 | return codegen.undeclared_identifiers 55 | 56 | 57 | def find_referenced_templates(ast): 58 | """Finds all the referenced templates from the AST. This will return an 59 | iterator over all the hardcoded template extensions, inclusions and 60 | imports. If dynamic inheritance or inclusion is used, `None` will be 61 | yielded. 62 | 63 | >>> from jinja2 import Environment, meta 64 | >>> env = Environment() 65 | >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') 66 | >>> list(meta.find_referenced_templates(ast)) 67 | ['layout.html', None] 68 | 69 | This function is useful for dependency tracking. For example if you want 70 | to rebuild parts of the website after a layout template has changed. 71 | """ 72 | for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, 73 | nodes.Include)): 74 | if not isinstance(node.template, nodes.Const): 75 | # a tuple with some non consts in there 76 | if isinstance(node.template, (nodes.Tuple, nodes.List)): 77 | for template_name in node.template.items: 78 | # something const, only yield the strings and ignore 79 | # non-string consts that really just make no sense 80 | if isinstance(template_name, nodes.Const): 81 | if isinstance(template_name.value, string_types): 82 | yield template_name.value 83 | # something dynamic in there 84 | else: 85 | yield None 86 | # something dynamic we don't know about here 87 | else: 88 | yield None 89 | continue 90 | # constant is a basestring, direct template name 91 | if isinstance(node.template.value, string_types): 92 | yield node.template.value 93 | # a tuple or list (latter *should* not happen) made of consts, 94 | # yield the consts that are strings. We could warn here for 95 | # non string values 96 | elif isinstance(node, nodes.Include) and \ 97 | isinstance(node.template.value, (tuple, list)): 98 | for template_name in node.template.value: 99 | if isinstance(template_name, string_types): 100 | yield template_name 101 | # something else we don't care about, we could warn here 102 | else: 103 | yield None 104 | -------------------------------------------------------------------------------- /jinja2/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.exceptions 4 | ~~~~~~~~~~~~~~~~~ 5 | 6 | Jinja exceptions. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from jinja2._compat import imap, text_type, PY2, implements_to_string 12 | 13 | 14 | class TemplateError(Exception): 15 | """Baseclass for all template errors.""" 16 | 17 | if PY2: 18 | def __init__(self, message=None): 19 | if message is not None: 20 | message = text_type(message).encode('utf-8') 21 | Exception.__init__(self, message) 22 | 23 | @property 24 | def message(self): 25 | if self.args: 26 | message = self.args[0] 27 | if message is not None: 28 | return message.decode('utf-8', 'replace') 29 | 30 | def __unicode__(self): 31 | return self.message or u'' 32 | else: 33 | def __init__(self, message=None): 34 | Exception.__init__(self, message) 35 | 36 | @property 37 | def message(self): 38 | if self.args: 39 | message = self.args[0] 40 | if message is not None: 41 | return message 42 | 43 | 44 | @implements_to_string 45 | class TemplateNotFound(IOError, LookupError, TemplateError): 46 | """Raised if a template does not exist.""" 47 | 48 | # looks weird, but removes the warning descriptor that just 49 | # bogusly warns us about message being deprecated 50 | message = None 51 | 52 | def __init__(self, name, message=None): 53 | IOError.__init__(self) 54 | if message is None: 55 | message = name 56 | self.message = message 57 | self.name = name 58 | self.templates = [name] 59 | 60 | def __str__(self): 61 | return self.message 62 | 63 | 64 | class TemplatesNotFound(TemplateNotFound): 65 | """Like :class:`TemplateNotFound` but raised if multiple templates 66 | are selected. This is a subclass of :class:`TemplateNotFound` 67 | exception, so just catching the base exception will catch both. 68 | 69 | .. versionadded:: 2.2 70 | """ 71 | 72 | def __init__(self, names=(), message=None): 73 | if message is None: 74 | message = u'none of the templates given were found: ' + \ 75 | u', '.join(imap(text_type, names)) 76 | TemplateNotFound.__init__(self, names and names[-1] or None, message) 77 | self.templates = list(names) 78 | 79 | 80 | @implements_to_string 81 | class TemplateSyntaxError(TemplateError): 82 | """Raised to tell the user that there is a problem with the template.""" 83 | 84 | def __init__(self, message, lineno, name=None, filename=None): 85 | TemplateError.__init__(self, message) 86 | self.lineno = lineno 87 | self.name = name 88 | self.filename = filename 89 | self.source = None 90 | 91 | # this is set to True if the debug.translate_syntax_error 92 | # function translated the syntax error into a new traceback 93 | self.translated = False 94 | 95 | def __str__(self): 96 | # for translated errors we only return the message 97 | if self.translated: 98 | return self.message 99 | 100 | # otherwise attach some stuff 101 | location = 'line %d' % self.lineno 102 | name = self.filename or self.name 103 | if name: 104 | location = 'File "%s", %s' % (name, location) 105 | lines = [self.message, ' ' + location] 106 | 107 | # if the source is set, add the line to the output 108 | if self.source is not None: 109 | try: 110 | line = self.source.splitlines()[self.lineno - 1] 111 | except IndexError: 112 | line = None 113 | if line: 114 | lines.append(' ' + line.strip()) 115 | 116 | return u'\n'.join(lines) 117 | 118 | 119 | class TemplateAssertionError(TemplateSyntaxError): 120 | """Like a template syntax error, but covers cases where something in the 121 | template caused an error at compile time that wasn't necessarily caused 122 | by a syntax error. However it's a direct subclass of 123 | :exc:`TemplateSyntaxError` and has the same attributes. 124 | """ 125 | 126 | 127 | class TemplateRuntimeError(TemplateError): 128 | """A generic runtime error in the template engine. Under some situations 129 | Jinja may raise this exception. 130 | """ 131 | 132 | 133 | class UndefinedError(TemplateRuntimeError): 134 | """Raised if a template tries to operate on :class:`Undefined`.""" 135 | 136 | 137 | class SecurityError(TemplateRuntimeError): 138 | """Raised if a template tries to do something insecure if the 139 | sandbox is enabled. 140 | """ 141 | 142 | 143 | class FilterArgumentError(TemplateRuntimeError): 144 | """This error is raised if a filter was called with inappropriate 145 | arguments 146 | """ 147 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | FileHeader 3 | ========== 4 | 5 | FileHeader is a powerful file templating plugin for SublimeText 2 and 6 | SublimeText 3. It makes it easier to create new file with initial contents. It 7 | also can add new header to an existed file or directory. 8 | 9 | Features 10 | ========= 11 | 12 | - Add new file with initial contents. 13 | - Auto detect **New File** action from SulimeText or other plugin. 14 | - Add header to an existed file or directory. 15 | - Batch add header to files in the specified directory. 16 | - Auto update file last modified time and last modified by. 17 | - Auto detect file type. 18 | - Powerful template with Jinja2_. 19 | - Custom templates supported. 20 | - Rich languages supported. 21 | - Support both Sublime Text 2 and Sublime Text 3. 22 | 23 | Installation 24 | ============ 25 | 26 | Package Control 27 | --------------- 28 | 29 | Install `Package Control`_. Then **Package Control: Install Package**, look for 30 | **FileHeader** and install it. 31 | 32 | .. _Package Control: https://sublime.wbond.net/ 33 | 34 | Source Installation 35 | -------------------- 36 | 37 | Go to the "Packages" directory **(Preferences / Browse Packages)**. Then clone 38 | this repository:: 39 | 40 | git clone git@github.com:shiyanhui/FileHeader.git 41 | 42 | Or download zip from Github, and put it in "Packages" directory 43 | **(Preferences / Browse Packages)**. 44 | 45 | Usage 46 | ===== 47 | 48 | Create a new file 49 | ----------------- 50 | 51 | - Sidebar Menu 52 | 53 | .. image:: https://raw.github.com/shiyanhui/FileHeader/master/doc/img/new-file.gif 54 | 55 | - Shortcuts 56 | 57 | The default shortcuts is **super+alt+n** on OS X, **ctrl+alt+n** on Windows and Linux. 58 | 59 | - Context Menu 60 | 61 | Similar to **Sidebar Menu**. 62 | 63 | Add header to an existed file 64 | ----------------------------- 65 | 66 | - Sidebar Menu 67 | 68 | .. image:: https://raw.github.com/shiyanhui/FileHeader/master/doc/img/add-header.gif 69 | 70 | - Shortcuts 71 | 72 | The default shortcuts is **super+alt+a** on OS X, **ctrl+alt+a** on 73 | Windows and Linux. 74 | 75 | - Context Menu 76 | 77 | Similar to **Sidebar Menu**. 78 | 79 | Add header to files in the specified directory 80 | ---------------------------------------------- 81 | 82 | - Sidebar Menu 83 | 84 | .. image:: https://raw.github.com/shiyanhui/FileHeader/master/doc/img/add-header-dir.gif 85 | 86 | A very important feature of FileHeader is that it can automatically update 87 | **last_modified_time** and **last_modified_by** (see options below). Just look 88 | this picture, take care of the **@Last modified time:** before save and after 89 | save: 90 | 91 | .. image:: https://raw.github.com/shiyanhui/FileHeader/master/doc/img/update.gif 92 | 93 | Settings 94 | ======== 95 | 96 | There are two kinds of arguments: **options** and kinds of languages variables 97 | settings. **options** is the functional setting, **Default** is the default 98 | language variables settings. Language variables setting will cover that in 99 | **Default**. 100 | 101 | Open **Preferences => Package Settings => File Header => Settings - Default** 102 | for more details. 103 | 104 | Template 105 | ======== 106 | 107 | FileHeader use Jinja2_ template, find out how to use it 108 | `here `_. 109 | 110 | The template is made up of **header** and **body**. You also can write you 111 | own templates. Take the Python template header **Python.tmpl** for example. 112 | 113 | .. code-block:: ++ 114 | 115 | # -*- coding: utf-8 -*- 116 | # @Author: {{author}} 117 | # @Date: {{create_time}} 118 | # @Last modified by: {{last_modified_by}} 119 | # @Last Modified time: {{last_modified_time}} 120 | 121 | **{{ }}** is variable, you can set it in setting files. **create_time** will be 122 | set when you create a new file using FileHeader, **last_modified_time** and 123 | **last_modified_by** will be update every time you save your file. 124 | 125 | You can define your functions and classes or other contents in your **body** 126 | file. Also take Python template body for example. 127 | 128 | .. code-block:: python 129 | 130 | class MyClass(object): 131 | pass 132 | 133 | if __name__ == '__main__': 134 | pass 135 | 136 | FAQ 137 | === 138 | 139 | - **How to customize my headers?** 140 | 141 | Set **custom_template_header_path** to your path of customized header in 142 | user-settings, for example, **~/header/** 143 | 144 | NOTE: **DO NOT** modify directly that in **Packages/FileHeader** 145 | 146 | - **What if FileHeader conflicts with other file template plugin?** 147 | 148 | For example, **FileHeader** and **Javatar** conflicts in files with 149 | extension **.java**. 150 | 151 | The solution is, open any file with extension **.java** in sublime text, 152 | and open **Preferences ==> Settings - More ==> Syntax Specific - User**, 153 | then add **"enable_add_template_to_empty_file": false**. 154 | 155 | - **What if key-map of FileHeader conflicts with others?** 156 | 157 | Just change that of **FileHeader** or others. 158 | 159 | 160 | Other Editors 161 | ============= 162 | 163 | - `FileHeader for Atom `_ by `guiguan `_ 164 | 165 | If you have any questions, please let me know. 🙂 166 | 167 | .. _Jinja2: http://jinja.pocoo.org/docs/ 168 | -------------------------------------------------------------------------------- /markupsafe/_constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe._constants 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Highlevel implementation of the Markup string. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | 13 | HTML_ENTITIES = { 14 | 'AElig': 198, 15 | 'Aacute': 193, 16 | 'Acirc': 194, 17 | 'Agrave': 192, 18 | 'Alpha': 913, 19 | 'Aring': 197, 20 | 'Atilde': 195, 21 | 'Auml': 196, 22 | 'Beta': 914, 23 | 'Ccedil': 199, 24 | 'Chi': 935, 25 | 'Dagger': 8225, 26 | 'Delta': 916, 27 | 'ETH': 208, 28 | 'Eacute': 201, 29 | 'Ecirc': 202, 30 | 'Egrave': 200, 31 | 'Epsilon': 917, 32 | 'Eta': 919, 33 | 'Euml': 203, 34 | 'Gamma': 915, 35 | 'Iacute': 205, 36 | 'Icirc': 206, 37 | 'Igrave': 204, 38 | 'Iota': 921, 39 | 'Iuml': 207, 40 | 'Kappa': 922, 41 | 'Lambda': 923, 42 | 'Mu': 924, 43 | 'Ntilde': 209, 44 | 'Nu': 925, 45 | 'OElig': 338, 46 | 'Oacute': 211, 47 | 'Ocirc': 212, 48 | 'Ograve': 210, 49 | 'Omega': 937, 50 | 'Omicron': 927, 51 | 'Oslash': 216, 52 | 'Otilde': 213, 53 | 'Ouml': 214, 54 | 'Phi': 934, 55 | 'Pi': 928, 56 | 'Prime': 8243, 57 | 'Psi': 936, 58 | 'Rho': 929, 59 | 'Scaron': 352, 60 | 'Sigma': 931, 61 | 'THORN': 222, 62 | 'Tau': 932, 63 | 'Theta': 920, 64 | 'Uacute': 218, 65 | 'Ucirc': 219, 66 | 'Ugrave': 217, 67 | 'Upsilon': 933, 68 | 'Uuml': 220, 69 | 'Xi': 926, 70 | 'Yacute': 221, 71 | 'Yuml': 376, 72 | 'Zeta': 918, 73 | 'aacute': 225, 74 | 'acirc': 226, 75 | 'acute': 180, 76 | 'aelig': 230, 77 | 'agrave': 224, 78 | 'alefsym': 8501, 79 | 'alpha': 945, 80 | 'amp': 38, 81 | 'and': 8743, 82 | 'ang': 8736, 83 | 'apos': 39, 84 | 'aring': 229, 85 | 'asymp': 8776, 86 | 'atilde': 227, 87 | 'auml': 228, 88 | 'bdquo': 8222, 89 | 'beta': 946, 90 | 'brvbar': 166, 91 | 'bull': 8226, 92 | 'cap': 8745, 93 | 'ccedil': 231, 94 | 'cedil': 184, 95 | 'cent': 162, 96 | 'chi': 967, 97 | 'circ': 710, 98 | 'clubs': 9827, 99 | 'cong': 8773, 100 | 'copy': 169, 101 | 'crarr': 8629, 102 | 'cup': 8746, 103 | 'curren': 164, 104 | 'dArr': 8659, 105 | 'dagger': 8224, 106 | 'darr': 8595, 107 | 'deg': 176, 108 | 'delta': 948, 109 | 'diams': 9830, 110 | 'divide': 247, 111 | 'eacute': 233, 112 | 'ecirc': 234, 113 | 'egrave': 232, 114 | 'empty': 8709, 115 | 'emsp': 8195, 116 | 'ensp': 8194, 117 | 'epsilon': 949, 118 | 'equiv': 8801, 119 | 'eta': 951, 120 | 'eth': 240, 121 | 'euml': 235, 122 | 'euro': 8364, 123 | 'exist': 8707, 124 | 'fnof': 402, 125 | 'forall': 8704, 126 | 'frac12': 189, 127 | 'frac14': 188, 128 | 'frac34': 190, 129 | 'frasl': 8260, 130 | 'gamma': 947, 131 | 'ge': 8805, 132 | 'gt': 62, 133 | 'hArr': 8660, 134 | 'harr': 8596, 135 | 'hearts': 9829, 136 | 'hellip': 8230, 137 | 'iacute': 237, 138 | 'icirc': 238, 139 | 'iexcl': 161, 140 | 'igrave': 236, 141 | 'image': 8465, 142 | 'infin': 8734, 143 | 'int': 8747, 144 | 'iota': 953, 145 | 'iquest': 191, 146 | 'isin': 8712, 147 | 'iuml': 239, 148 | 'kappa': 954, 149 | 'lArr': 8656, 150 | 'lambda': 955, 151 | 'lang': 9001, 152 | 'laquo': 171, 153 | 'larr': 8592, 154 | 'lceil': 8968, 155 | 'ldquo': 8220, 156 | 'le': 8804, 157 | 'lfloor': 8970, 158 | 'lowast': 8727, 159 | 'loz': 9674, 160 | 'lrm': 8206, 161 | 'lsaquo': 8249, 162 | 'lsquo': 8216, 163 | 'lt': 60, 164 | 'macr': 175, 165 | 'mdash': 8212, 166 | 'micro': 181, 167 | 'middot': 183, 168 | 'minus': 8722, 169 | 'mu': 956, 170 | 'nabla': 8711, 171 | 'nbsp': 160, 172 | 'ndash': 8211, 173 | 'ne': 8800, 174 | 'ni': 8715, 175 | 'not': 172, 176 | 'notin': 8713, 177 | 'nsub': 8836, 178 | 'ntilde': 241, 179 | 'nu': 957, 180 | 'oacute': 243, 181 | 'ocirc': 244, 182 | 'oelig': 339, 183 | 'ograve': 242, 184 | 'oline': 8254, 185 | 'omega': 969, 186 | 'omicron': 959, 187 | 'oplus': 8853, 188 | 'or': 8744, 189 | 'ordf': 170, 190 | 'ordm': 186, 191 | 'oslash': 248, 192 | 'otilde': 245, 193 | 'otimes': 8855, 194 | 'ouml': 246, 195 | 'para': 182, 196 | 'part': 8706, 197 | 'permil': 8240, 198 | 'perp': 8869, 199 | 'phi': 966, 200 | 'pi': 960, 201 | 'piv': 982, 202 | 'plusmn': 177, 203 | 'pound': 163, 204 | 'prime': 8242, 205 | 'prod': 8719, 206 | 'prop': 8733, 207 | 'psi': 968, 208 | 'quot': 34, 209 | 'rArr': 8658, 210 | 'radic': 8730, 211 | 'rang': 9002, 212 | 'raquo': 187, 213 | 'rarr': 8594, 214 | 'rceil': 8969, 215 | 'rdquo': 8221, 216 | 'real': 8476, 217 | 'reg': 174, 218 | 'rfloor': 8971, 219 | 'rho': 961, 220 | 'rlm': 8207, 221 | 'rsaquo': 8250, 222 | 'rsquo': 8217, 223 | 'sbquo': 8218, 224 | 'scaron': 353, 225 | 'sdot': 8901, 226 | 'sect': 167, 227 | 'shy': 173, 228 | 'sigma': 963, 229 | 'sigmaf': 962, 230 | 'sim': 8764, 231 | 'spades': 9824, 232 | 'sub': 8834, 233 | 'sube': 8838, 234 | 'sum': 8721, 235 | 'sup': 8835, 236 | 'sup1': 185, 237 | 'sup2': 178, 238 | 'sup3': 179, 239 | 'supe': 8839, 240 | 'szlig': 223, 241 | 'tau': 964, 242 | 'there4': 8756, 243 | 'theta': 952, 244 | 'thetasym': 977, 245 | 'thinsp': 8201, 246 | 'thorn': 254, 247 | 'tilde': 732, 248 | 'times': 215, 249 | 'trade': 8482, 250 | 'uArr': 8657, 251 | 'uacute': 250, 252 | 'uarr': 8593, 253 | 'ucirc': 251, 254 | 'ugrave': 249, 255 | 'uml': 168, 256 | 'upsih': 978, 257 | 'upsilon': 965, 258 | 'uuml': 252, 259 | 'weierp': 8472, 260 | 'xi': 958, 261 | 'yacute': 253, 262 | 'yen': 165, 263 | 'yuml': 255, 264 | 'zeta': 950, 265 | 'zwj': 8205, 266 | 'zwnj': 8204 267 | } 268 | -------------------------------------------------------------------------------- /markupsafe/_speedups.c: -------------------------------------------------------------------------------- 1 | /** 2 | * markupsafe._speedups 3 | * ~~~~~~~~~~~~~~~~~~~~ 4 | * 5 | * This module implements functions for automatic escaping in C for better 6 | * performance. 7 | * 8 | * :copyright: (c) 2010 by Armin Ronacher. 9 | * :license: BSD. 10 | */ 11 | 12 | #include 13 | 14 | #define ESCAPED_CHARS_TABLE_SIZE 63 15 | #define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))); 16 | 17 | #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) 18 | typedef int Py_ssize_t; 19 | #define PY_SSIZE_T_MAX INT_MAX 20 | #define PY_SSIZE_T_MIN INT_MIN 21 | #endif 22 | 23 | 24 | static PyObject* markup; 25 | static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE]; 26 | static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; 27 | 28 | static int 29 | init_constants(void) 30 | { 31 | PyObject *module; 32 | /* happing of characters to replace */ 33 | escaped_chars_repl['"'] = UNICHR("""); 34 | escaped_chars_repl['\''] = UNICHR("'"); 35 | escaped_chars_repl['&'] = UNICHR("&"); 36 | escaped_chars_repl['<'] = UNICHR("<"); 37 | escaped_chars_repl['>'] = UNICHR(">"); 38 | 39 | /* lengths of those characters when replaced - 1 */ 40 | memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); 41 | escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ 42 | escaped_chars_delta_len['&'] = 4; 43 | escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; 44 | 45 | /* import markup type so that we can mark the return value */ 46 | module = PyImport_ImportModule("markupsafe"); 47 | if (!module) 48 | return 0; 49 | markup = PyObject_GetAttrString(module, "Markup"); 50 | Py_DECREF(module); 51 | 52 | return 1; 53 | } 54 | 55 | static PyObject* 56 | escape_unicode(PyUnicodeObject *in) 57 | { 58 | PyUnicodeObject *out; 59 | Py_UNICODE *inp = PyUnicode_AS_UNICODE(in); 60 | const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in); 61 | Py_UNICODE *next_escp; 62 | Py_UNICODE *outp; 63 | Py_ssize_t delta=0, erepl=0, delta_len=0; 64 | 65 | /* First we need to figure out how long the escaped string will be */ 66 | while (*(inp) || inp < inp_end) { 67 | if (*inp < ESCAPED_CHARS_TABLE_SIZE) { 68 | delta += escaped_chars_delta_len[*inp]; 69 | erepl += !!escaped_chars_delta_len[*inp]; 70 | } 71 | ++inp; 72 | } 73 | 74 | /* Do we need to escape anything at all? */ 75 | if (!erepl) { 76 | Py_INCREF(in); 77 | return (PyObject*)in; 78 | } 79 | 80 | out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta); 81 | if (!out) 82 | return NULL; 83 | 84 | outp = PyUnicode_AS_UNICODE(out); 85 | inp = PyUnicode_AS_UNICODE(in); 86 | while (erepl-- > 0) { 87 | /* look for the next substitution */ 88 | next_escp = inp; 89 | while (next_escp < inp_end) { 90 | if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && 91 | (delta_len = escaped_chars_delta_len[*next_escp])) { 92 | ++delta_len; 93 | break; 94 | } 95 | ++next_escp; 96 | } 97 | 98 | if (next_escp > inp) { 99 | /* copy unescaped chars between inp and next_escp */ 100 | Py_UNICODE_COPY(outp, inp, next_escp-inp); 101 | outp += next_escp - inp; 102 | } 103 | 104 | /* escape 'next_escp' */ 105 | Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len); 106 | outp += delta_len; 107 | 108 | inp = next_escp + 1; 109 | } 110 | if (inp < inp_end) 111 | Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in))); 112 | 113 | return (PyObject*)out; 114 | } 115 | 116 | 117 | static PyObject* 118 | escape(PyObject *self, PyObject *text) 119 | { 120 | PyObject *s = NULL, *rv = NULL, *html; 121 | 122 | /* we don't have to escape integers, bools or floats */ 123 | if (PyLong_CheckExact(text) || 124 | #if PY_MAJOR_VERSION < 3 125 | PyInt_CheckExact(text) || 126 | #endif 127 | PyFloat_CheckExact(text) || PyBool_Check(text) || 128 | text == Py_None) 129 | return PyObject_CallFunctionObjArgs(markup, text, NULL); 130 | 131 | /* if the object has an __html__ method that performs the escaping */ 132 | html = PyObject_GetAttrString(text, "__html__"); 133 | if (html) { 134 | rv = PyObject_CallObject(html, NULL); 135 | Py_DECREF(html); 136 | return rv; 137 | } 138 | 139 | /* otherwise make the object unicode if it isn't, then escape */ 140 | PyErr_Clear(); 141 | if (!PyUnicode_Check(text)) { 142 | #if PY_MAJOR_VERSION < 3 143 | PyObject *unicode = PyObject_Unicode(text); 144 | #else 145 | PyObject *unicode = PyObject_Str(text); 146 | #endif 147 | if (!unicode) 148 | return NULL; 149 | s = escape_unicode((PyUnicodeObject*)unicode); 150 | Py_DECREF(unicode); 151 | } 152 | else 153 | s = escape_unicode((PyUnicodeObject*)text); 154 | 155 | /* convert the unicode string into a markup object. */ 156 | rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); 157 | Py_DECREF(s); 158 | return rv; 159 | } 160 | 161 | 162 | static PyObject* 163 | escape_silent(PyObject *self, PyObject *text) 164 | { 165 | if (text != Py_None) 166 | return escape(self, text); 167 | return PyObject_CallFunctionObjArgs(markup, NULL); 168 | } 169 | 170 | 171 | static PyObject* 172 | soft_unicode(PyObject *self, PyObject *s) 173 | { 174 | if (!PyUnicode_Check(s)) 175 | #if PY_MAJOR_VERSION < 3 176 | return PyObject_Unicode(s); 177 | #else 178 | return PyObject_Str(s); 179 | #endif 180 | Py_INCREF(s); 181 | return s; 182 | } 183 | 184 | 185 | static PyMethodDef module_methods[] = { 186 | {"escape", (PyCFunction)escape, METH_O, 187 | "escape(s) -> markup\n\n" 188 | "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n" 189 | "sequences. Use this if you need to display text that might contain\n" 190 | "such characters in HTML. Marks return value as markup string."}, 191 | {"escape_silent", (PyCFunction)escape_silent, METH_O, 192 | "escape_silent(s) -> markup\n\n" 193 | "Like escape but converts None to an empty string."}, 194 | {"soft_unicode", (PyCFunction)soft_unicode, METH_O, 195 | "soft_unicode(object) -> string\n\n" 196 | "Make a string unicode if it isn't already. That way a markup\n" 197 | "string is not converted back to unicode."}, 198 | {NULL, NULL, 0, NULL} /* Sentinel */ 199 | }; 200 | 201 | 202 | #if PY_MAJOR_VERSION < 3 203 | 204 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 205 | #define PyMODINIT_FUNC void 206 | #endif 207 | PyMODINIT_FUNC 208 | init_speedups(void) 209 | { 210 | if (!init_constants()) 211 | return; 212 | 213 | Py_InitModule3("markupsafe._speedups", module_methods, ""); 214 | } 215 | 216 | #else /* Python 3.x module initialization */ 217 | 218 | static struct PyModuleDef module_definition = { 219 | PyModuleDef_HEAD_INIT, 220 | "markupsafe._speedups", 221 | NULL, 222 | -1, 223 | module_methods, 224 | NULL, 225 | NULL, 226 | NULL, 227 | NULL 228 | }; 229 | 230 | PyMODINIT_FUNC 231 | PyInit__speedups(void) 232 | { 233 | if (!init_constants()) 234 | return NULL; 235 | 236 | return PyModule_Create(&module_definition); 237 | } 238 | 239 | #endif 240 | -------------------------------------------------------------------------------- /FileHeader.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | options 4 | ======= 5 | */ 6 | 7 | /* 8 | The datetime format. 9 | 10 | 0: "%Y-%m-%d %H:%M:%S" 11 | 1: "%Y-%m-%d" 12 | 2: "%H:%M:%S" 13 | */ 14 | "time_format": 0, 15 | /* 16 | The custom time format. It will format `create_time` and `last_modified_time` 17 | instead of `time_format` if you set it. The time format refers to` 18 | https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior`. 19 | */ 20 | "custom_time_format": "", 21 | /* 22 | Whether add template on save. Can be either a boolean or a regex which will 23 | be searched against the file name 24 | */ 25 | "enable_add_template_on_save": false, 26 | /* 27 | Whether add template to the empty file. 28 | 29 | It's useful when you create new file through other command, for 30 | example, the default Sublime Text's **New File...** or other plugin. 31 | */ 32 | "enable_add_template_to_empty_file": true, 33 | /* 34 | Set your custom template header path here, it is a directory in which 35 | you write your own header files. The file name should be a language, 36 | "Python.tmpl" for example. 37 | */ 38 | "custom_template_header_path": "", 39 | /* 40 | Set your custom template body path here, it is a directory in which 41 | you write your own body files. The file name should be a language, 42 | "Python.tmpl" for example. 43 | 44 | The template structure is: 45 | 46 | I am a file 47 | ----------- 48 | header 49 | body 50 | 51 | */ 52 | "custom_template_body_path": "", 53 | /* 54 | Whether show input panel when you add header. The default file which 55 | you add header to is the current file you edit. 56 | */ 57 | "show_input_panel_when_add_header": true, 58 | /* 59 | Whether open file when you add header to files in the specified 60 | directory. 61 | */ 62 | "open_file_when_add_header_to_directory": true, 63 | /* 64 | Whether enable add header to hidden directory. If false, FileHeader 65 | won't add header to files under it. 66 | */ 67 | "enable_add_header_to_hidden_dir": false, 68 | /* 69 | Whether enable add header to hidden file. If false, FileHeader 70 | won't add header to it. 71 | */ 72 | "enable_add_header_to_hidden_file": false, 73 | /* 74 | FileHeader judges programming language according file suffix. 75 | 76 | Default programming language if FileHeader judges failed when you 77 | create new file. 78 | */ 79 | "syntax_when_not_match": "Text", 80 | /* 81 | FileHeader will judge programming language according to file suffix. 82 | You can add more file suffix here. Note: language should be one of 83 | that under **Default**. If FileHeader don't find the suffix, 84 | FileHeader will set language as **syntax_when_not_match** above. 85 | */ 86 | "file_suffix_mapping": { 87 | 88 | }, 89 | /* 90 | FileHeader will handle header prefix according to programming language. 91 | You can add more header prefix here. Note: language should be one of 92 | that under **Default**. 93 | */ 94 | "header_prefix_mapping": { 95 | 96 | }, 97 | /* 98 | Set special language syntax mapping, in case of syntax mapping file name 99 | differs from programming language name file name 100 | */ 101 | "language_syntax_mapping": { 102 | 103 | }, 104 | /* 105 | Set special file suffix equivalence. Take `blade.php` for example, 106 | FileHeader will initial file with suffix `blade.php` with that of `html`. 107 | */ 108 | "extension_equivalence": { 109 | 110 | }, 111 | 112 | /* 113 | Variables 114 | ========= 115 | */ 116 | 117 | /* 118 | Below is the variables you render templater. 119 | */ 120 | "Default": { 121 | /* 122 | Builtin Variables 123 | ================= 124 | 125 | - create_time 126 | 127 | The file created time. It will be automatically set when you create 128 | a new file if you use it. 129 | 130 | Can't be set custom. 131 | 132 | - author 133 | 134 | The file creator. 135 | 136 | FileHeader will set it automatically. If it's in 137 | a git repository and the `user.name` has been set, `autor` 138 | will set to `user.name`, otherwise it will be set to current 139 | system user. 140 | 141 | Can be set custom. 142 | 143 | - email 144 | 145 | Author's email 146 | 147 | FileHeader will set it automatically. If it's in 148 | a git repository and the `user.email` has been set, `email` 149 | will set to `user.email`, otherwise it will be set to `None`. 150 | 151 | Can be set custom. 152 | 153 | - last_modified_by 154 | 155 | The file last modified by who? It is specially useful when 156 | cooperation programming. 157 | 158 | FileHeader will set it automatically. If it's in 159 | a git repository and the `user.name` has been set, `autor` 160 | will set to `user.name`, otherwise it will be set to current 161 | system logined user. 162 | 163 | Can be set custom. 164 | 165 | - last_modified_time 166 | 167 | The file last modified time. 168 | 169 | FileHeader will set it automatically when you save the file. 170 | 171 | Can't be set custom. 172 | 173 | - file_path 174 | 175 | The absolute path of the current file. 176 | 177 | FileHeader will update it automatically when you change it. 178 | 179 | Can't be set custom. 180 | 181 | - file_name 182 | 183 | The name of current file with extension. 184 | 185 | FileHeader will update it automatically when you change it. 186 | 187 | Can't be set custom. 188 | 189 | - file_name_without_extension 190 | 191 | The name of current file without extension. 192 | 193 | FileHeader will update it automatically when you change it. 194 | 195 | Can't be set custom. 196 | 197 | - project_name 198 | 199 | The project name. 200 | 201 | Note: `project_name` only works in ST3. 202 | 203 | Can't be set custom. 204 | */ 205 | 206 | // You can add more here...... 207 | }, 208 | /* 209 | You can set different variables in different languages. It will cover 210 | that in "Default". 211 | */ 212 | "ASP": {}, 213 | "ActionScript": {}, 214 | "AppleScript": {}, 215 | "Batch File": {}, 216 | "C#": {}, 217 | "C++": {}, 218 | "CSS": {}, 219 | "Clojure": {}, 220 | "D": {}, 221 | "Diff": {}, 222 | "Erlang": {}, 223 | "Go": {}, 224 | "Graphviz": {}, 225 | "Groovy": {}, 226 | "HTML": {}, 227 | "Haskell": {}, 228 | "Java": {}, 229 | "JavaScript": {}, 230 | "LaTeX": {}, 231 | "Lisp": {}, 232 | "Lua": {}, 233 | "Makefile": {}, 234 | "Markdown": {}, 235 | "Matlab": {}, 236 | "OCaml": {}, 237 | "Objective-C": {}, 238 | "PHP": {}, 239 | "Pascal": {}, 240 | "Perl": {}, 241 | "Python": {}, 242 | "R": {}, 243 | "RestructuredText": {}, 244 | "Ruby": {}, 245 | "SQL": {}, 246 | "Scala": {}, 247 | "ShellScript": {}, 248 | "TCL": {}, 249 | "Text": {}, 250 | "Textile": {}, 251 | "XML": {}, 252 | "YAML": {} 253 | } 254 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\FileHeader.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\FileHeader.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /markupsafe/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | markupsafe 4 | ~~~~~~~~~~ 5 | 6 | Implements a Markup string. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import re 12 | from markupsafe._compat import text_type, string_types, int_types, \ 13 | unichr, PY2 14 | 15 | 16 | __all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent'] 17 | 18 | 19 | _striptags_re = re.compile(r'(|<[^>]*>)') 20 | _entity_re = re.compile(r'&([^;]+);') 21 | 22 | 23 | class Markup(text_type): 24 | r"""Marks a string as being safe for inclusion in HTML/XML output without 25 | needing to be escaped. This implements the `__html__` interface a couple 26 | of frameworks and web applications use. :class:`Markup` is a direct 27 | subclass of `unicode` and provides all the methods of `unicode` just that 28 | it escapes arguments passed and always returns `Markup`. 29 | 30 | The `escape` function returns markup objects so that double escaping can't 31 | happen. 32 | 33 | The constructor of the :class:`Markup` class can be used for three 34 | different things: When passed an unicode object it's assumed to be safe, 35 | when passed an object with an HTML representation (has an `__html__` 36 | method) that representation is used, otherwise the object passed is 37 | converted into a unicode string and then assumed to be safe: 38 | 39 | >>> Markup("Hello World!") 40 | Markup(u'Hello World!') 41 | >>> class Foo(object): 42 | ... def __html__(self): 43 | ... return 'foo' 44 | ... 45 | >>> Markup(Foo()) 46 | Markup(u'foo') 47 | 48 | If you want object passed being always treated as unsafe you can use the 49 | :meth:`escape` classmethod to create a :class:`Markup` object: 50 | 51 | >>> Markup.escape("Hello World!") 52 | Markup(u'Hello <em>World</em>!') 53 | 54 | Operations on a markup string are markup aware which means that all 55 | arguments are passed through the :func:`escape` function: 56 | 57 | >>> em = Markup("%s") 58 | >>> em % "foo & bar" 59 | Markup(u'foo & bar') 60 | >>> strong = Markup("%(text)s") 61 | >>> strong % {'text': 'hacker here'} 62 | Markup(u'<blink>hacker here</blink>') 63 | >>> Markup("Hello ") + "" 64 | Markup(u'Hello <foo>') 65 | """ 66 | __slots__ = () 67 | 68 | def __new__(cls, base=u'', encoding=None, errors='strict'): 69 | if hasattr(base, '__html__'): 70 | base = base.__html__() 71 | if encoding is None: 72 | return text_type.__new__(cls, base) 73 | return text_type.__new__(cls, base, encoding, errors) 74 | 75 | def __html__(self): 76 | return self 77 | 78 | def __add__(self, other): 79 | if isinstance(other, string_types) or hasattr(other, '__html__'): 80 | return self.__class__(super(Markup, self).__add__(self.escape(other))) 81 | return NotImplemented 82 | 83 | def __radd__(self, other): 84 | if hasattr(other, '__html__') or isinstance(other, string_types): 85 | return self.escape(other).__add__(self) 86 | return NotImplemented 87 | 88 | def __mul__(self, num): 89 | if isinstance(num, int_types): 90 | return self.__class__(text_type.__mul__(self, num)) 91 | return NotImplemented 92 | __rmul__ = __mul__ 93 | 94 | def __mod__(self, arg): 95 | if isinstance(arg, tuple): 96 | arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg) 97 | else: 98 | arg = _MarkupEscapeHelper(arg, self.escape) 99 | return self.__class__(text_type.__mod__(self, arg)) 100 | 101 | def __repr__(self): 102 | return '%s(%s)' % ( 103 | self.__class__.__name__, 104 | text_type.__repr__(self) 105 | ) 106 | 107 | def join(self, seq): 108 | return self.__class__(text_type.join(self, map(self.escape, seq))) 109 | join.__doc__ = text_type.join.__doc__ 110 | 111 | def split(self, *args, **kwargs): 112 | return list(map(self.__class__, text_type.split(self, *args, **kwargs))) 113 | split.__doc__ = text_type.split.__doc__ 114 | 115 | def rsplit(self, *args, **kwargs): 116 | return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs))) 117 | rsplit.__doc__ = text_type.rsplit.__doc__ 118 | 119 | def splitlines(self, *args, **kwargs): 120 | return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs))) 121 | splitlines.__doc__ = text_type.splitlines.__doc__ 122 | 123 | def unescape(self): 124 | r"""Unescape markup again into an text_type string. This also resolves 125 | known HTML4 and XHTML entities: 126 | 127 | >>> Markup("Main » About").unescape() 128 | u'Main \xbb About' 129 | """ 130 | from markupsafe._constants import HTML_ENTITIES 131 | def handle_match(m): 132 | name = m.group(1) 133 | if name in HTML_ENTITIES: 134 | return unichr(HTML_ENTITIES[name]) 135 | try: 136 | if name[:2] in ('#x', '#X'): 137 | return unichr(int(name[2:], 16)) 138 | elif name.startswith('#'): 139 | return unichr(int(name[1:])) 140 | except ValueError: 141 | pass 142 | return u'' 143 | return _entity_re.sub(handle_match, text_type(self)) 144 | 145 | def striptags(self): 146 | r"""Unescape markup into an text_type string and strip all tags. This 147 | also resolves known HTML4 and XHTML entities. Whitespace is 148 | normalized to one: 149 | 150 | >>> Markup("Main » About").striptags() 151 | u'Main \xbb About' 152 | """ 153 | stripped = u' '.join(_striptags_re.sub('', self).split()) 154 | return Markup(stripped).unescape() 155 | 156 | @classmethod 157 | def escape(cls, s): 158 | """Escape the string. Works like :func:`escape` with the difference 159 | that for subclasses of :class:`Markup` this function would return the 160 | correct subclass. 161 | """ 162 | rv = escape(s) 163 | if rv.__class__ is not cls: 164 | return cls(rv) 165 | return rv 166 | 167 | def make_wrapper(name): 168 | orig = getattr(text_type, name) 169 | def func(self, *args, **kwargs): 170 | args = _escape_argspec(list(args), enumerate(args), self.escape) 171 | #_escape_argspec(kwargs, kwargs.iteritems(), None) 172 | return self.__class__(orig(self, *args, **kwargs)) 173 | func.__name__ = orig.__name__ 174 | func.__doc__ = orig.__doc__ 175 | return func 176 | 177 | for method in '__getitem__', 'capitalize', \ 178 | 'title', 'lower', 'upper', 'replace', 'ljust', \ 179 | 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ 180 | 'translate', 'expandtabs', 'swapcase', 'zfill': 181 | locals()[method] = make_wrapper(method) 182 | 183 | # new in python 2.5 184 | if hasattr(text_type, 'partition'): 185 | def partition(self, sep): 186 | return tuple(map(self.__class__, 187 | text_type.partition(self, self.escape(sep)))) 188 | def rpartition(self, sep): 189 | return tuple(map(self.__class__, 190 | text_type.rpartition(self, self.escape(sep)))) 191 | 192 | # new in python 2.6 193 | if hasattr(text_type, 'format'): 194 | format = make_wrapper('format') 195 | 196 | # not in python 3 197 | if hasattr(text_type, '__getslice__'): 198 | __getslice__ = make_wrapper('__getslice__') 199 | 200 | del method, make_wrapper 201 | 202 | 203 | def _escape_argspec(obj, iterable, escape): 204 | """Helper for various string-wrapped functions.""" 205 | for key, value in iterable: 206 | if hasattr(value, '__html__') or isinstance(value, string_types): 207 | obj[key] = escape(value) 208 | return obj 209 | 210 | 211 | class _MarkupEscapeHelper(object): 212 | """Helper for Markup.__mod__""" 213 | 214 | def __init__(self, obj, escape): 215 | self.obj = obj 216 | self.escape = escape 217 | 218 | __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape) 219 | __unicode__ = __str__ = lambda s: text_type(s.escape(s.obj)) 220 | __repr__ = lambda s: str(s.escape(repr(s.obj))) 221 | __int__ = lambda s: int(s.obj) 222 | __float__ = lambda s: float(s.obj) 223 | 224 | 225 | # we have to import it down here as the speedups and native 226 | # modules imports the markup type which is define above. 227 | try: 228 | from markupsafe._speedups import escape, escape_silent, soft_unicode 229 | except ImportError: 230 | from markupsafe._native import escape, escape_silent, soft_unicode 231 | 232 | if not PY2: 233 | soft_str = soft_unicode 234 | __all__.append('soft_str') 235 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # FileHeader documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Jun 28 09:25:34 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # The suffix of source filenames. 37 | source_suffix = '.rst' 38 | 39 | # The encoding of source files. 40 | #source_encoding = 'utf-8-sig' 41 | 42 | # The master toctree document. 43 | master_doc = 'index' 44 | 45 | # General information about the project. 46 | project = u'FileHeader' 47 | copyright = u'2014, Lime' 48 | 49 | # The version info for the project you're documenting, acts as replacement for 50 | # |version| and |release|, also used in various other places throughout the 51 | # built documents. 52 | # 53 | # The short X.Y version. 54 | version = '1.5.6' 55 | # The full version, including alpha/beta/rc tags. 56 | release = '1.5.6' 57 | 58 | # The language for content autogenerated by Sphinx. Refer to documentation 59 | # for a list of supported languages. 60 | #language = None 61 | 62 | # There are two options for replacing |today|: either, you set today to some 63 | # non-false value, then it is used: 64 | #today = '' 65 | # Else, today_fmt is used as the format for a strftime call. 66 | #today_fmt = '%B %d, %Y' 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | exclude_patterns = ['_build'] 71 | 72 | # The reST default role (used for this markup: `text`) to use for all 73 | # documents. 74 | #default_role = None 75 | 76 | # If true, '()' will be appended to :func: etc. cross-reference text. 77 | #add_function_parentheses = True 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | #add_module_names = True 82 | 83 | # If true, sectionauthor and moduleauthor directives will be shown in the 84 | # output. They are ignored by default. 85 | #show_authors = False 86 | 87 | # The name of the Pygments (syntax highlighting) style to use. 88 | pygments_style = 'sphinx' 89 | 90 | # A list of ignored prefixes for module index sorting. 91 | #modindex_common_prefix = [] 92 | 93 | # If true, keep warnings as "system message" paragraphs in the built documents. 94 | #keep_warnings = False 95 | 96 | 97 | # -- Options for HTML output ---------------------------------------------- 98 | 99 | # The theme to use for HTML and HTML Help pages. See the documentation for 100 | # a list of builtin themes. 101 | html_theme = 'default' 102 | 103 | # Theme options are theme-specific and customize the look and feel of a theme 104 | # further. For a list of options available for each theme, see the 105 | # documentation. 106 | #html_theme_options = {} 107 | 108 | # Add any paths that contain custom themes here, relative to this directory. 109 | #html_theme_path = [] 110 | 111 | # The name for this set of Sphinx documents. If None, it defaults to 112 | # " v documentation". 113 | #html_title = None 114 | 115 | # A shorter title for the navigation bar. Default is the same as html_title. 116 | #html_short_title = None 117 | 118 | # The name of an image file (relative to this directory) to place at the top 119 | # of the sidebar. 120 | #html_logo = None 121 | 122 | # The name of an image file (within the static path) to use as favicon of the 123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 124 | # pixels large. 125 | #html_favicon = None 126 | 127 | # Add any paths that contain custom static files (such as style sheets) here, 128 | # relative to this directory. They are copied after the builtin static files, 129 | # so a file named "default.css" will overwrite the builtin "default.css". 130 | html_static_path = ['_static'] 131 | 132 | # Add any extra paths that contain custom files (such as robots.txt or 133 | # .htaccess) here, relative to this directory. These files are copied 134 | # directly to the root of the documentation. 135 | #html_extra_path = [] 136 | 137 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 138 | # using the given strftime format. 139 | #html_last_updated_fmt = '%b %d, %Y' 140 | 141 | # If true, SmartyPants will be used to convert quotes and dashes to 142 | # typographically correct entities. 143 | #html_use_smartypants = True 144 | 145 | # Custom sidebar templates, maps document names to template names. 146 | #html_sidebars = {} 147 | 148 | # Additional templates that should be rendered to pages, maps page names to 149 | # template names. 150 | #html_additional_pages = {} 151 | 152 | # If false, no module index is generated. 153 | #html_domain_indices = True 154 | 155 | # If false, no index is generated. 156 | #html_use_index = True 157 | 158 | # If true, the index is split into individual pages for each letter. 159 | #html_split_index = False 160 | 161 | # If true, links to the reST sources are added to the pages. 162 | #html_show_sourcelink = True 163 | 164 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 165 | #html_show_sphinx = True 166 | 167 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 168 | #html_show_copyright = True 169 | 170 | # If true, an OpenSearch description file will be output, and all pages will 171 | # contain a tag referring to it. The value of this option must be the 172 | # base URL from which the finished HTML is served. 173 | #html_use_opensearch = '' 174 | 175 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 176 | #html_file_suffix = None 177 | 178 | # Output file base name for HTML help builder. 179 | htmlhelp_basename = 'FileHeaderdoc' 180 | 181 | 182 | # -- Options for LaTeX output --------------------------------------------- 183 | 184 | latex_elements = { 185 | # The paper size ('letterpaper' or 'a4paper'). 186 | #'papersize': 'letterpaper', 187 | 188 | # The font size ('10pt', '11pt' or '12pt'). 189 | #'pointsize': '10pt', 190 | 191 | # Additional stuff for the LaTeX preamble. 192 | #'preamble': '', 193 | } 194 | 195 | # Grouping the document tree into LaTeX files. List of tuples 196 | # (source start file, target name, title, 197 | # author, documentclass [howto, manual, or own class]). 198 | latex_documents = [ 199 | ('index', 'FileHeader.tex', u'FileHeader Documentation', 200 | u'Lime', 'manual'), 201 | ] 202 | 203 | # The name of an image file (relative to this directory) to place at the top of 204 | # the title page. 205 | #latex_logo = None 206 | 207 | # For "manual" documents, if this is true, then toplevel headings are parts, 208 | # not chapters. 209 | #latex_use_parts = False 210 | 211 | # If true, show page references after internal links. 212 | #latex_show_pagerefs = False 213 | 214 | # If true, show URL addresses after external links. 215 | #latex_show_urls = False 216 | 217 | # Documents to append as an appendix to all manuals. 218 | #latex_appendices = [] 219 | 220 | # If false, no module index is generated. 221 | #latex_domain_indices = True 222 | 223 | 224 | # -- Options for manual page output --------------------------------------- 225 | 226 | # One entry per manual page. List of tuples 227 | # (source start file, name, description, authors, manual section). 228 | man_pages = [ 229 | ('index', 'fileheader', u'FileHeader Documentation', 230 | [u'Lime'], 1) 231 | ] 232 | 233 | # If true, show URL addresses after external links. 234 | #man_show_urls = False 235 | 236 | 237 | # -- Options for Texinfo output ------------------------------------------- 238 | 239 | # Grouping the document tree into Texinfo files. List of tuples 240 | # (source start file, target name, title, author, 241 | # dir menu entry, description, category) 242 | texinfo_documents = [ 243 | ('index', 'FileHeader', u'FileHeader Documentation', 244 | u'Lime', 'FileHeader', 'One line description of project.', 245 | 'Miscellaneous'), 246 | ] 247 | 248 | # Documents to append as an appendix to all manuals. 249 | #texinfo_appendices = [] 250 | 251 | # If false, no module index is generated. 252 | #texinfo_domain_indices = True 253 | 254 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 255 | #texinfo_show_urls = 'footnote' 256 | 257 | # If true, do not generate a @detailmenu in the "Top" node's menu. 258 | #texinfo_no_detailmenu = False 259 | 260 | sys.path.append(os.path.abspath('_themes')) 261 | html_theme_path = ['_themes'] 262 | html_theme = 'plain' 263 | html_theme_options = { 264 | 'github': 'shiyanhui' 265 | } -------------------------------------------------------------------------------- /jinja2/bccache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.bccache 4 | ~~~~~~~~~~~~~~ 5 | 6 | This module implements the bytecode cache system Jinja is optionally 7 | using. This is useful if you have very complex template situations and 8 | the compiliation of all those templates slow down your application too 9 | much. 10 | 11 | Situations where this is useful are often forking web applications that 12 | are initialized on the first request. 13 | 14 | :copyright: (c) 2010 by the Jinja Team. 15 | :license: BSD. 16 | """ 17 | from os import path, listdir 18 | import sys 19 | import marshal 20 | import tempfile 21 | import fnmatch 22 | from hashlib import sha1 23 | from jinja2.utils import open_if_exists 24 | from jinja2._compat import BytesIO, pickle, PY2, text_type 25 | 26 | 27 | # marshal works better on 3.x, one hack less required 28 | if not PY2: 29 | marshal_dump = marshal.dump 30 | marshal_load = marshal.load 31 | else: 32 | 33 | def marshal_dump(code, f): 34 | if isinstance(f, file): 35 | marshal.dump(code, f) 36 | else: 37 | f.write(marshal.dumps(code)) 38 | 39 | def marshal_load(f): 40 | if isinstance(f, file): 41 | return marshal.load(f) 42 | return marshal.loads(f.read()) 43 | 44 | 45 | bc_version = 2 46 | 47 | # magic version used to only change with new jinja versions. With 2.6 48 | # we change this to also take Python version changes into account. The 49 | # reason for this is that Python tends to segfault if fed earlier bytecode 50 | # versions because someone thought it would be a good idea to reuse opcodes 51 | # or make Python incompatible with earlier versions. 52 | bc_magic = 'j2'.encode('ascii') + \ 53 | pickle.dumps(bc_version, 2) + \ 54 | pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1]) 55 | 56 | 57 | class Bucket(object): 58 | """Buckets are used to store the bytecode for one template. It's created 59 | and initialized by the bytecode cache and passed to the loading functions. 60 | 61 | The buckets get an internal checksum from the cache assigned and use this 62 | to automatically reject outdated cache material. Individual bytecode 63 | cache subclasses don't have to care about cache invalidation. 64 | """ 65 | 66 | def __init__(self, environment, key, checksum): 67 | self.environment = environment 68 | self.key = key 69 | self.checksum = checksum 70 | self.reset() 71 | 72 | def reset(self): 73 | """Resets the bucket (unloads the bytecode).""" 74 | self.code = None 75 | 76 | def load_bytecode(self, f): 77 | """Loads bytecode from a file or file like object.""" 78 | # make sure the magic header is correct 79 | magic = f.read(len(bc_magic)) 80 | if magic != bc_magic: 81 | self.reset() 82 | return 83 | # the source code of the file changed, we need to reload 84 | checksum = pickle.load(f) 85 | if self.checksum != checksum: 86 | self.reset() 87 | return 88 | self.code = marshal_load(f) 89 | 90 | def write_bytecode(self, f): 91 | """Dump the bytecode into the file or file like object passed.""" 92 | if self.code is None: 93 | raise TypeError('can\'t write empty bucket') 94 | f.write(bc_magic) 95 | pickle.dump(self.checksum, f, 2) 96 | marshal_dump(self.code, f) 97 | 98 | def bytecode_from_string(self, string): 99 | """Load bytecode from a string.""" 100 | self.load_bytecode(BytesIO(string)) 101 | 102 | def bytecode_to_string(self): 103 | """Return the bytecode as string.""" 104 | out = BytesIO() 105 | self.write_bytecode(out) 106 | return out.getvalue() 107 | 108 | 109 | class BytecodeCache(object): 110 | """To implement your own bytecode cache you have to subclass this class 111 | and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of 112 | these methods are passed a :class:`~jinja2.bccache.Bucket`. 113 | 114 | A very basic bytecode cache that saves the bytecode on the file system:: 115 | 116 | from os import path 117 | 118 | class MyCache(BytecodeCache): 119 | 120 | def __init__(self, directory): 121 | self.directory = directory 122 | 123 | def load_bytecode(self, bucket): 124 | filename = path.join(self.directory, bucket.key) 125 | if path.exists(filename): 126 | with open(filename, 'rb') as f: 127 | bucket.load_bytecode(f) 128 | 129 | def dump_bytecode(self, bucket): 130 | filename = path.join(self.directory, bucket.key) 131 | with open(filename, 'wb') as f: 132 | bucket.write_bytecode(f) 133 | 134 | A more advanced version of a filesystem based bytecode cache is part of 135 | Jinja2. 136 | """ 137 | 138 | def load_bytecode(self, bucket): 139 | """Subclasses have to override this method to load bytecode into a 140 | bucket. If they are not able to find code in the cache for the 141 | bucket, it must not do anything. 142 | """ 143 | raise NotImplementedError() 144 | 145 | def dump_bytecode(self, bucket): 146 | """Subclasses have to override this method to write the bytecode 147 | from a bucket back to the cache. If it unable to do so it must not 148 | fail silently but raise an exception. 149 | """ 150 | raise NotImplementedError() 151 | 152 | def clear(self): 153 | """Clears the cache. This method is not used by Jinja2 but should be 154 | implemented to allow applications to clear the bytecode cache used 155 | by a particular environment. 156 | """ 157 | 158 | def get_cache_key(self, name, filename=None): 159 | """Returns the unique hash key for this template name.""" 160 | hash = sha1(name.encode('utf-8')) 161 | if filename is not None: 162 | filename = '|' + filename 163 | if isinstance(filename, text_type): 164 | filename = filename.encode('utf-8') 165 | hash.update(filename) 166 | return hash.hexdigest() 167 | 168 | def get_source_checksum(self, source): 169 | """Returns a checksum for the source.""" 170 | return sha1(source.encode('utf-8')).hexdigest() 171 | 172 | def get_bucket(self, environment, name, filename, source): 173 | """Return a cache bucket for the given template. All arguments are 174 | mandatory but filename may be `None`. 175 | """ 176 | key = self.get_cache_key(name, filename) 177 | checksum = self.get_source_checksum(source) 178 | bucket = Bucket(environment, key, checksum) 179 | self.load_bytecode(bucket) 180 | return bucket 181 | 182 | def set_bucket(self, bucket): 183 | """Put the bucket into the cache.""" 184 | self.dump_bytecode(bucket) 185 | 186 | 187 | class FileSystemBytecodeCache(BytecodeCache): 188 | """A bytecode cache that stores bytecode on the filesystem. It accepts 189 | two arguments: The directory where the cache items are stored and a 190 | pattern string that is used to build the filename. 191 | 192 | If no directory is specified the system temporary items folder is used. 193 | 194 | The pattern can be used to have multiple separate caches operate on the 195 | same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` 196 | is replaced with the cache key. 197 | 198 | >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') 199 | 200 | This bytecode cache supports clearing of the cache using the clear method. 201 | """ 202 | 203 | def __init__(self, directory=None, pattern='__jinja2_%s.cache'): 204 | if directory is None: 205 | directory = tempfile.gettempdir() 206 | self.directory = directory 207 | self.pattern = pattern 208 | 209 | def _get_cache_filename(self, bucket): 210 | return path.join(self.directory, self.pattern % bucket.key) 211 | 212 | def load_bytecode(self, bucket): 213 | f = open_if_exists(self._get_cache_filename(bucket), 'rb') 214 | if f is not None: 215 | try: 216 | bucket.load_bytecode(f) 217 | finally: 218 | f.close() 219 | 220 | def dump_bytecode(self, bucket): 221 | f = open(self._get_cache_filename(bucket), 'wb') 222 | try: 223 | bucket.write_bytecode(f) 224 | finally: 225 | f.close() 226 | 227 | def clear(self): 228 | # imported lazily here because google app-engine doesn't support 229 | # write access on the file system and the function does not exist 230 | # normally. 231 | from os import remove 232 | files = fnmatch.filter(listdir(self.directory), self.pattern % '*') 233 | for filename in files: 234 | try: 235 | remove(path.join(self.directory, filename)) 236 | except OSError: 237 | pass 238 | 239 | 240 | class MemcachedBytecodeCache(BytecodeCache): 241 | """This class implements a bytecode cache that uses a memcache cache for 242 | storing the information. It does not enforce a specific memcache library 243 | (tummy's memcache or cmemcache) but will accept any class that provides 244 | the minimal interface required. 245 | 246 | Libraries compatible with this class: 247 | 248 | - `werkzeug `_.contrib.cache 249 | - `python-memcached `_ 250 | - `cmemcache `_ 251 | 252 | (Unfortunately the django cache interface is not compatible because it 253 | does not support storing binary data, only unicode. You can however pass 254 | the underlying cache client to the bytecode cache which is available 255 | as `django.core.cache.cache._client`.) 256 | 257 | The minimal interface for the client passed to the constructor is this: 258 | 259 | .. class:: MinimalClientInterface 260 | 261 | .. method:: set(key, value[, timeout]) 262 | 263 | Stores the bytecode in the cache. `value` is a string and 264 | `timeout` the timeout of the key. If timeout is not provided 265 | a default timeout or no timeout should be assumed, if it's 266 | provided it's an integer with the number of seconds the cache 267 | item should exist. 268 | 269 | .. method:: get(key) 270 | 271 | Returns the value for the cache key. If the item does not 272 | exist in the cache the return value must be `None`. 273 | 274 | The other arguments to the constructor are the prefix for all keys that 275 | is added before the actual cache key and the timeout for the bytecode in 276 | the cache system. We recommend a high (or no) timeout. 277 | 278 | This bytecode cache does not support clearing of used items in the cache. 279 | The clear method is a no-operation function. 280 | 281 | .. versionadded:: 2.7 282 | Added support for ignoring memcache errors through the 283 | `ignore_memcache_errors` parameter. 284 | """ 285 | 286 | def __init__(self, client, prefix='jinja2/bytecode/', timeout=None, 287 | ignore_memcache_errors=True): 288 | self.client = client 289 | self.prefix = prefix 290 | self.timeout = timeout 291 | self.ignore_memcache_errors = ignore_memcache_errors 292 | 293 | def load_bytecode(self, bucket): 294 | try: 295 | code = self.client.get(self.prefix + bucket.key) 296 | except Exception: 297 | if not self.ignore_memcache_errors: 298 | raise 299 | code = None 300 | if code is not None: 301 | bucket.bytecode_from_string(code) 302 | 303 | def dump_bytecode(self, bucket): 304 | args = (self.prefix + bucket.key, bucket.bytecode_to_string()) 305 | if self.timeout is not None: 306 | args += (self.timeout,) 307 | try: 308 | self.client.set(*args) 309 | except Exception: 310 | if not self.ignore_memcache_errors: 311 | raise 312 | -------------------------------------------------------------------------------- /jinja2/debug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.debug 4 | ~~~~~~~~~~~~ 5 | 6 | Implements the debug interface for Jinja. This module does some pretty 7 | ugly stuff with the Python traceback system in order to achieve tracebacks 8 | with correct line numbers, locals and contents. 9 | 10 | :copyright: (c) 2010 by the Jinja Team. 11 | :license: BSD, see LICENSE for more details. 12 | """ 13 | import sys 14 | import traceback 15 | from types import TracebackType, CodeType 16 | from jinja2.utils import missing, internal_code 17 | from jinja2.exceptions import TemplateSyntaxError 18 | from jinja2._compat import iteritems, reraise 19 | 20 | # on pypy we can take advantage of transparent proxies 21 | try: 22 | from __pypy__ import tproxy 23 | except ImportError: 24 | tproxy = None 25 | 26 | 27 | # how does the raise helper look like? 28 | try: 29 | exec("raise TypeError, 'foo'") 30 | except SyntaxError: 31 | raise_helper = 'raise __jinja_exception__[1]' 32 | except TypeError: 33 | raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' 34 | 35 | 36 | class TracebackFrameProxy(object): 37 | """Proxies a traceback frame.""" 38 | 39 | def __init__(self, tb): 40 | self.tb = tb 41 | self._tb_next = None 42 | 43 | @property 44 | def tb_next(self): 45 | return self._tb_next 46 | 47 | def set_next(self, next): 48 | if tb_set_next is not None: 49 | try: 50 | tb_set_next(self.tb, next and next.tb or None) 51 | except Exception: 52 | # this function can fail due to all the hackery it does 53 | # on various python implementations. We just catch errors 54 | # down and ignore them if necessary. 55 | pass 56 | self._tb_next = next 57 | 58 | @property 59 | def is_jinja_frame(self): 60 | return '__jinja_template__' in self.tb.tb_frame.f_globals 61 | 62 | def __getattr__(self, name): 63 | return getattr(self.tb, name) 64 | 65 | 66 | def make_frame_proxy(frame): 67 | proxy = TracebackFrameProxy(frame) 68 | if tproxy is None: 69 | return proxy 70 | def operation_handler(operation, *args, **kwargs): 71 | if operation in ('__getattribute__', '__getattr__'): 72 | return getattr(proxy, args[0]) 73 | elif operation == '__setattr__': 74 | proxy.__setattr__(*args, **kwargs) 75 | else: 76 | return getattr(proxy, operation)(*args, **kwargs) 77 | return tproxy(TracebackType, operation_handler) 78 | 79 | 80 | class ProcessedTraceback(object): 81 | """Holds a Jinja preprocessed traceback for printing or reraising.""" 82 | 83 | def __init__(self, exc_type, exc_value, frames): 84 | assert frames, 'no frames for this traceback?' 85 | self.exc_type = exc_type 86 | self.exc_value = exc_value 87 | self.frames = frames 88 | 89 | # newly concatenate the frames (which are proxies) 90 | prev_tb = None 91 | for tb in self.frames: 92 | if prev_tb is not None: 93 | prev_tb.set_next(tb) 94 | prev_tb = tb 95 | prev_tb.set_next(None) 96 | 97 | def render_as_text(self, limit=None): 98 | """Return a string with the traceback.""" 99 | lines = traceback.format_exception(self.exc_type, self.exc_value, 100 | self.frames[0], limit=limit) 101 | return ''.join(lines).rstrip() 102 | 103 | def render_as_html(self, full=False): 104 | """Return a unicode string with the traceback as rendered HTML.""" 105 | from jinja2.debugrenderer import render_traceback 106 | return u'%s\n\n' % ( 107 | render_traceback(self, full=full), 108 | self.render_as_text().decode('utf-8', 'replace') 109 | ) 110 | 111 | @property 112 | def is_template_syntax_error(self): 113 | """`True` if this is a template syntax error.""" 114 | return isinstance(self.exc_value, TemplateSyntaxError) 115 | 116 | @property 117 | def exc_info(self): 118 | """Exception info tuple with a proxy around the frame objects.""" 119 | return self.exc_type, self.exc_value, self.frames[0] 120 | 121 | @property 122 | def standard_exc_info(self): 123 | """Standard python exc_info for re-raising""" 124 | tb = self.frames[0] 125 | # the frame will be an actual traceback (or transparent proxy) if 126 | # we are on pypy or a python implementation with support for tproxy 127 | if type(tb) is not TracebackType: 128 | tb = tb.tb 129 | return self.exc_type, self.exc_value, tb 130 | 131 | 132 | def make_traceback(exc_info, source_hint=None): 133 | """Creates a processed traceback object from the exc_info.""" 134 | exc_type, exc_value, tb = exc_info 135 | if isinstance(exc_value, TemplateSyntaxError): 136 | exc_info = translate_syntax_error(exc_value, source_hint) 137 | initial_skip = 0 138 | else: 139 | initial_skip = 1 140 | return translate_exception(exc_info, initial_skip) 141 | 142 | 143 | def translate_syntax_error(error, source=None): 144 | """Rewrites a syntax error to please traceback systems.""" 145 | error.source = source 146 | error.translated = True 147 | exc_info = (error.__class__, error, None) 148 | filename = error.filename 149 | if filename is None: 150 | filename = '' 151 | return fake_exc_info(exc_info, filename, error.lineno) 152 | 153 | 154 | def translate_exception(exc_info, initial_skip=0): 155 | """If passed an exc_info it will automatically rewrite the exceptions 156 | all the way down to the correct line numbers and frames. 157 | """ 158 | tb = exc_info[2] 159 | frames = [] 160 | 161 | # skip some internal frames if wanted 162 | for x in range(initial_skip): 163 | if tb is not None: 164 | tb = tb.tb_next 165 | initial_tb = tb 166 | 167 | while tb is not None: 168 | # skip frames decorated with @internalcode. These are internal 169 | # calls we can't avoid and that are useless in template debugging 170 | # output. 171 | if tb.tb_frame.f_code in internal_code: 172 | tb = tb.tb_next 173 | continue 174 | 175 | # save a reference to the next frame if we override the current 176 | # one with a faked one. 177 | next = tb.tb_next 178 | 179 | # fake template exceptions 180 | template = tb.tb_frame.f_globals.get('__jinja_template__') 181 | if template is not None: 182 | lineno = template.get_corresponding_lineno(tb.tb_lineno) 183 | tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, 184 | lineno)[2] 185 | 186 | frames.append(make_frame_proxy(tb)) 187 | tb = next 188 | 189 | # if we don't have any exceptions in the frames left, we have to 190 | # reraise it unchanged. 191 | # XXX: can we backup here? when could this happen? 192 | if not frames: 193 | reraise(exc_info[0], exc_info[1], exc_info[2]) 194 | 195 | return ProcessedTraceback(exc_info[0], exc_info[1], frames) 196 | 197 | 198 | def fake_exc_info(exc_info, filename, lineno): 199 | """Helper for `translate_exception`.""" 200 | exc_type, exc_value, tb = exc_info 201 | 202 | # figure the real context out 203 | if tb is not None: 204 | real_locals = tb.tb_frame.f_locals.copy() 205 | ctx = real_locals.get('context') 206 | if ctx: 207 | locals = ctx.get_all() 208 | else: 209 | locals = {} 210 | for name, value in iteritems(real_locals): 211 | if name.startswith('l_') and value is not missing: 212 | locals[name[2:]] = value 213 | 214 | # if there is a local called __jinja_exception__, we get 215 | # rid of it to not break the debug functionality. 216 | locals.pop('__jinja_exception__', None) 217 | else: 218 | locals = {} 219 | 220 | # assamble fake globals we need 221 | globals = { 222 | '__name__': filename, 223 | '__file__': filename, 224 | '__jinja_exception__': exc_info[:2], 225 | 226 | # we don't want to keep the reference to the template around 227 | # to not cause circular dependencies, but we mark it as Jinja 228 | # frame for the ProcessedTraceback 229 | '__jinja_template__': None 230 | } 231 | 232 | # and fake the exception 233 | code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') 234 | 235 | # if it's possible, change the name of the code. This won't work 236 | # on some python environments such as google appengine 237 | try: 238 | if tb is None: 239 | location = 'template' 240 | else: 241 | function = tb.tb_frame.f_code.co_name 242 | if function == 'root': 243 | location = 'top-level template code' 244 | elif function.startswith('block_'): 245 | location = 'block "%s"' % function[6:] 246 | else: 247 | location = 'template' 248 | code = CodeType(0, code.co_nlocals, code.co_stacksize, 249 | code.co_flags, code.co_code, code.co_consts, 250 | code.co_names, code.co_varnames, filename, 251 | location, code.co_firstlineno, 252 | code.co_lnotab, (), ()) 253 | except: 254 | pass 255 | 256 | # execute the code and catch the new traceback 257 | try: 258 | exec(code, globals, locals) 259 | except: 260 | exc_info = sys.exc_info() 261 | new_tb = exc_info[2].tb_next 262 | 263 | # return without this frame 264 | return exc_info[:2] + (new_tb,) 265 | 266 | 267 | def _init_ugly_crap(): 268 | """This function implements a few ugly things so that we can patch the 269 | traceback objects. The function returned allows resetting `tb_next` on 270 | any python traceback object. Do not attempt to use this on non cpython 271 | interpreters 272 | """ 273 | import ctypes 274 | from types import TracebackType 275 | 276 | # figure out side of _Py_ssize_t 277 | if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): 278 | _Py_ssize_t = ctypes.c_int64 279 | else: 280 | _Py_ssize_t = ctypes.c_int 281 | 282 | # regular python 283 | class _PyObject(ctypes.Structure): 284 | pass 285 | _PyObject._fields_ = [ 286 | ('ob_refcnt', _Py_ssize_t), 287 | ('ob_type', ctypes.POINTER(_PyObject)) 288 | ] 289 | 290 | # python with trace 291 | if hasattr(sys, 'getobjects'): 292 | class _PyObject(ctypes.Structure): 293 | pass 294 | _PyObject._fields_ = [ 295 | ('_ob_next', ctypes.POINTER(_PyObject)), 296 | ('_ob_prev', ctypes.POINTER(_PyObject)), 297 | ('ob_refcnt', _Py_ssize_t), 298 | ('ob_type', ctypes.POINTER(_PyObject)) 299 | ] 300 | 301 | class _Traceback(_PyObject): 302 | pass 303 | _Traceback._fields_ = [ 304 | ('tb_next', ctypes.POINTER(_Traceback)), 305 | ('tb_frame', ctypes.POINTER(_PyObject)), 306 | ('tb_lasti', ctypes.c_int), 307 | ('tb_lineno', ctypes.c_int) 308 | ] 309 | 310 | def tb_set_next(tb, next): 311 | """Set the tb_next attribute of a traceback object.""" 312 | if not (isinstance(tb, TracebackType) and 313 | (next is None or isinstance(next, TracebackType))): 314 | raise TypeError('tb_set_next arguments must be traceback objects') 315 | obj = _Traceback.from_address(id(tb)) 316 | if tb.tb_next is not None: 317 | old = _Traceback.from_address(id(tb.tb_next)) 318 | old.ob_refcnt -= 1 319 | if next is None: 320 | obj.tb_next = ctypes.POINTER(_Traceback)() 321 | else: 322 | next = _Traceback.from_address(id(next)) 323 | next.ob_refcnt += 1 324 | obj.tb_next = ctypes.pointer(next) 325 | 326 | return tb_set_next 327 | 328 | 329 | # try to get a tb_set_next implementation if we don't have transparent 330 | # proxies. 331 | tb_set_next = None 332 | if tproxy is None: 333 | try: 334 | tb_set_next = _init_ugly_crap() 335 | except: 336 | pass 337 | del _init_ugly_crap 338 | -------------------------------------------------------------------------------- /jinja2/sandbox.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.sandbox 4 | ~~~~~~~~~~~~~~ 5 | 6 | Adds a sandbox layer to Jinja as it was the default behavior in the old 7 | Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the 8 | default behavior is easier to use. 9 | 10 | The behavior can be changed by subclassing the environment. 11 | 12 | :copyright: (c) 2010 by the Jinja Team. 13 | :license: BSD. 14 | """ 15 | import types 16 | import operator 17 | from jinja2.environment import Environment 18 | from jinja2.exceptions import SecurityError 19 | from jinja2._compat import string_types, PY2 20 | 21 | 22 | #: maximum number of items a range may produce 23 | MAX_RANGE = 100000 24 | 25 | #: attributes of function objects that are considered unsafe. 26 | UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', 27 | 'func_defaults', 'func_globals']) 28 | 29 | #: unsafe method attributes. function attributes are unsafe for methods too 30 | UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) 31 | 32 | #: unsafe generator attirbutes. 33 | UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code']) 34 | 35 | # On versions > python 2 the special attributes on functions are gone, 36 | # but they remain on methods and generators for whatever reason. 37 | if not PY2: 38 | UNSAFE_FUNCTION_ATTRIBUTES = set() 39 | 40 | import warnings 41 | 42 | # make sure we don't warn in python 2.6 about stuff we don't care about 43 | warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, 44 | module='jinja2.sandbox') 45 | 46 | from collections import deque 47 | 48 | _mutable_set_types = (set,) 49 | _mutable_mapping_types = (dict,) 50 | _mutable_sequence_types = (list,) 51 | 52 | 53 | # on python 2.x we can register the user collection types 54 | try: 55 | from UserDict import UserDict, DictMixin 56 | from UserList import UserList 57 | _mutable_mapping_types += (UserDict, DictMixin) 58 | _mutable_set_types += (UserList,) 59 | except ImportError: 60 | pass 61 | 62 | # if sets is still available, register the mutable set from there as well 63 | try: 64 | from sets import Set 65 | _mutable_set_types += (Set,) 66 | except ImportError: 67 | pass 68 | 69 | #: register Python 2.6 abstract base classes 70 | try: 71 | from collections import MutableSet, MutableMapping, MutableSequence 72 | _mutable_set_types += (MutableSet,) 73 | _mutable_mapping_types += (MutableMapping,) 74 | _mutable_sequence_types += (MutableSequence,) 75 | except ImportError: 76 | pass 77 | 78 | _mutable_spec = ( 79 | (_mutable_set_types, frozenset([ 80 | 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', 81 | 'symmetric_difference_update', 'update' 82 | ])), 83 | (_mutable_mapping_types, frozenset([ 84 | 'clear', 'pop', 'popitem', 'setdefault', 'update' 85 | ])), 86 | (_mutable_sequence_types, frozenset([ 87 | 'append', 'reverse', 'insert', 'sort', 'extend', 'remove' 88 | ])), 89 | (deque, frozenset([ 90 | 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', 91 | 'popleft', 'remove', 'rotate' 92 | ])) 93 | ) 94 | 95 | 96 | def safe_range(*args): 97 | """A range that can't generate ranges with a length of more than 98 | MAX_RANGE items. 99 | """ 100 | rng = range(*args) 101 | if len(rng) > MAX_RANGE: 102 | raise OverflowError('range too big, maximum size for range is %d' % 103 | MAX_RANGE) 104 | return rng 105 | 106 | 107 | def unsafe(f): 108 | """Marks a function or method as unsafe. 109 | 110 | :: 111 | 112 | @unsafe 113 | def delete(self): 114 | pass 115 | """ 116 | f.unsafe_callable = True 117 | return f 118 | 119 | 120 | def is_internal_attribute(obj, attr): 121 | """Test if the attribute given is an internal python attribute. For 122 | example this function returns `True` for the `func_code` attribute of 123 | python objects. This is useful if the environment method 124 | :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. 125 | 126 | >>> from jinja2.sandbox import is_internal_attribute 127 | >>> is_internal_attribute(lambda: None, "func_code") 128 | True 129 | >>> is_internal_attribute((lambda x:x).func_code, 'co_code') 130 | True 131 | >>> is_internal_attribute(str, "upper") 132 | False 133 | """ 134 | if isinstance(obj, types.FunctionType): 135 | if attr in UNSAFE_FUNCTION_ATTRIBUTES: 136 | return True 137 | elif isinstance(obj, types.MethodType): 138 | if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ 139 | attr in UNSAFE_METHOD_ATTRIBUTES: 140 | return True 141 | elif isinstance(obj, type): 142 | if attr == 'mro': 143 | return True 144 | elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)): 145 | return True 146 | elif isinstance(obj, types.GeneratorType): 147 | if attr in UNSAFE_GENERATOR_ATTRIBUTES: 148 | return True 149 | return attr.startswith('__') 150 | 151 | 152 | def modifies_known_mutable(obj, attr): 153 | """This function checks if an attribute on a builtin mutable object 154 | (list, dict, set or deque) would modify it if called. It also supports 155 | the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and 156 | with Python 2.6 onwards the abstract base classes `MutableSet`, 157 | `MutableMapping`, and `MutableSequence`. 158 | 159 | >>> modifies_known_mutable({}, "clear") 160 | True 161 | >>> modifies_known_mutable({}, "keys") 162 | False 163 | >>> modifies_known_mutable([], "append") 164 | True 165 | >>> modifies_known_mutable([], "index") 166 | False 167 | 168 | If called with an unsupported object (such as unicode) `False` is 169 | returned. 170 | 171 | >>> modifies_known_mutable("foo", "upper") 172 | False 173 | """ 174 | for typespec, unsafe in _mutable_spec: 175 | if isinstance(obj, typespec): 176 | return attr in unsafe 177 | return False 178 | 179 | 180 | class SandboxedEnvironment(Environment): 181 | """The sandboxed environment. It works like the regular environment but 182 | tells the compiler to generate sandboxed code. Additionally subclasses of 183 | this environment may override the methods that tell the runtime what 184 | attributes or functions are safe to access. 185 | 186 | If the template tries to access insecure code a :exc:`SecurityError` is 187 | raised. However also other exceptions may occour during the rendering so 188 | the caller has to ensure that all exceptions are catched. 189 | """ 190 | sandboxed = True 191 | 192 | #: default callback table for the binary operators. A copy of this is 193 | #: available on each instance of a sandboxed environment as 194 | #: :attr:`binop_table` 195 | default_binop_table = { 196 | '+': operator.add, 197 | '-': operator.sub, 198 | '*': operator.mul, 199 | '/': operator.truediv, 200 | '//': operator.floordiv, 201 | '**': operator.pow, 202 | '%': operator.mod 203 | } 204 | 205 | #: default callback table for the unary operators. A copy of this is 206 | #: available on each instance of a sandboxed environment as 207 | #: :attr:`unop_table` 208 | default_unop_table = { 209 | '+': operator.pos, 210 | '-': operator.neg 211 | } 212 | 213 | #: a set of binary operators that should be intercepted. Each operator 214 | #: that is added to this set (empty by default) is delegated to the 215 | #: :meth:`call_binop` method that will perform the operator. The default 216 | #: operator callback is specified by :attr:`binop_table`. 217 | #: 218 | #: The following binary operators are interceptable: 219 | #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**`` 220 | #: 221 | #: The default operation form the operator table corresponds to the 222 | #: builtin function. Intercepted calls are always slower than the native 223 | #: operator call, so make sure only to intercept the ones you are 224 | #: interested in. 225 | #: 226 | #: .. versionadded:: 2.6 227 | intercepted_binops = frozenset() 228 | 229 | #: a set of unary operators that should be intercepted. Each operator 230 | #: that is added to this set (empty by default) is delegated to the 231 | #: :meth:`call_unop` method that will perform the operator. The default 232 | #: operator callback is specified by :attr:`unop_table`. 233 | #: 234 | #: The following unary operators are interceptable: ``+``, ``-`` 235 | #: 236 | #: The default operation form the operator table corresponds to the 237 | #: builtin function. Intercepted calls are always slower than the native 238 | #: operator call, so make sure only to intercept the ones you are 239 | #: interested in. 240 | #: 241 | #: .. versionadded:: 2.6 242 | intercepted_unops = frozenset() 243 | 244 | def intercept_unop(self, operator): 245 | """Called during template compilation with the name of a unary 246 | operator to check if it should be intercepted at runtime. If this 247 | method returns `True`, :meth:`call_unop` is excuted for this unary 248 | operator. The default implementation of :meth:`call_unop` will use 249 | the :attr:`unop_table` dictionary to perform the operator with the 250 | same logic as the builtin one. 251 | 252 | The following unary operators are interceptable: ``+`` and ``-`` 253 | 254 | Intercepted calls are always slower than the native operator call, 255 | so make sure only to intercept the ones you are interested in. 256 | 257 | .. versionadded:: 2.6 258 | """ 259 | return False 260 | 261 | 262 | def __init__(self, *args, **kwargs): 263 | Environment.__init__(self, *args, **kwargs) 264 | self.globals['range'] = safe_range 265 | self.binop_table = self.default_binop_table.copy() 266 | self.unop_table = self.default_unop_table.copy() 267 | 268 | def is_safe_attribute(self, obj, attr, value): 269 | """The sandboxed environment will call this method to check if the 270 | attribute of an object is safe to access. Per default all attributes 271 | starting with an underscore are considered private as well as the 272 | special attributes of internal python objects as returned by the 273 | :func:`is_internal_attribute` function. 274 | """ 275 | return not (attr.startswith('_') or is_internal_attribute(obj, attr)) 276 | 277 | def is_safe_callable(self, obj): 278 | """Check if an object is safely callable. Per default a function is 279 | considered safe unless the `unsafe_callable` attribute exists and is 280 | True. Override this method to alter the behavior, but this won't 281 | affect the `unsafe` decorator from this module. 282 | """ 283 | return not (getattr(obj, 'unsafe_callable', False) or 284 | getattr(obj, 'alters_data', False)) 285 | 286 | def call_binop(self, context, operator, left, right): 287 | """For intercepted binary operator calls (:meth:`intercepted_binops`) 288 | this function is executed instead of the builtin operator. This can 289 | be used to fine tune the behavior of certain operators. 290 | 291 | .. versionadded:: 2.6 292 | """ 293 | return self.binop_table[operator](left, right) 294 | 295 | def call_unop(self, context, operator, arg): 296 | """For intercepted unary operator calls (:meth:`intercepted_unops`) 297 | this function is executed instead of the builtin operator. This can 298 | be used to fine tune the behavior of certain operators. 299 | 300 | .. versionadded:: 2.6 301 | """ 302 | return self.unop_table[operator](arg) 303 | 304 | def getitem(self, obj, argument): 305 | """Subscribe an object from sandboxed code.""" 306 | try: 307 | return obj[argument] 308 | except (TypeError, LookupError): 309 | if isinstance(argument, string_types): 310 | try: 311 | attr = str(argument) 312 | except Exception: 313 | pass 314 | else: 315 | try: 316 | value = getattr(obj, attr) 317 | except AttributeError: 318 | pass 319 | else: 320 | if self.is_safe_attribute(obj, argument, value): 321 | return value 322 | return self.unsafe_undefined(obj, argument) 323 | return self.undefined(obj=obj, name=argument) 324 | 325 | def getattr(self, obj, attribute): 326 | """Subscribe an object from sandboxed code and prefer the 327 | attribute. The attribute passed *must* be a bytestring. 328 | """ 329 | try: 330 | value = getattr(obj, attribute) 331 | except AttributeError: 332 | try: 333 | return obj[attribute] 334 | except (TypeError, LookupError): 335 | pass 336 | else: 337 | if self.is_safe_attribute(obj, attribute, value): 338 | return value 339 | return self.unsafe_undefined(obj, attribute) 340 | return self.undefined(obj=obj, name=attribute) 341 | 342 | def unsafe_undefined(self, obj, attribute): 343 | """Return an undefined object for unsafe attributes.""" 344 | return self.undefined('access to attribute %r of %r ' 345 | 'object is unsafe.' % ( 346 | attribute, 347 | obj.__class__.__name__ 348 | ), name=attribute, obj=obj, exc=SecurityError) 349 | 350 | def call(__self, __context, __obj, *args, **kwargs): 351 | """Call an object from sandboxed code.""" 352 | # the double prefixes are to avoid double keyword argument 353 | # errors when proxying the call. 354 | if not __self.is_safe_callable(__obj): 355 | raise SecurityError('%r is not safely callable' % (__obj,)) 356 | return __context.call(__obj, *args, **kwargs) 357 | 358 | 359 | class ImmutableSandboxedEnvironment(SandboxedEnvironment): 360 | """Works exactly like the regular `SandboxedEnvironment` but does not 361 | permit modifications on the builtin mutable objects `list`, `set`, and 362 | `dict` by using the :func:`modifies_known_mutable` function. 363 | """ 364 | 365 | def is_safe_attribute(self, obj, attr, value): 366 | if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): 367 | return False 368 | return not modifies_known_mutable(obj, attr) 369 | -------------------------------------------------------------------------------- /jinja2/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.utils 4 | ~~~~~~~~~~~~ 5 | 6 | Utility functions. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import re 12 | import errno 13 | from collections import deque 14 | from threading import Lock 15 | from jinja2._compat import text_type, string_types, implements_iterator, \ 16 | url_quote 17 | 18 | 19 | _word_split_re = re.compile(r'(\s+)') 20 | _punctuation_re = re.compile( 21 | '^(?P(?:%s)*)(?P.*?)(?P(?:%s)*)$' % ( 22 | '|'.join(map(re.escape, ('(', '<', '<'))), 23 | '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) 24 | ) 25 | ) 26 | _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') 27 | _striptags_re = re.compile(r'(|<[^>]*>)') 28 | _entity_re = re.compile(r'&([^;]+);') 29 | _letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 30 | _digits = '0123456789' 31 | 32 | # special singleton representing missing values for the runtime 33 | missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() 34 | 35 | # internal code 36 | internal_code = set() 37 | 38 | concat = u''.join 39 | 40 | 41 | def contextfunction(f): 42 | """This decorator can be used to mark a function or method context callable. 43 | A context callable is passed the active :class:`Context` as first argument when 44 | called from the template. This is useful if a function wants to get access 45 | to the context or functions provided on the context object. For example 46 | a function that returns a sorted list of template variables the current 47 | template exports could look like this:: 48 | 49 | @contextfunction 50 | def get_exported_names(context): 51 | return sorted(context.exported_vars) 52 | """ 53 | f.contextfunction = True 54 | return f 55 | 56 | 57 | def evalcontextfunction(f): 58 | """This decorator can be used to mark a function or method as an eval 59 | context callable. This is similar to the :func:`contextfunction` 60 | but instead of passing the context, an evaluation context object is 61 | passed. For more information about the eval context, see 62 | :ref:`eval-context`. 63 | 64 | .. versionadded:: 2.4 65 | """ 66 | f.evalcontextfunction = True 67 | return f 68 | 69 | 70 | def environmentfunction(f): 71 | """This decorator can be used to mark a function or method as environment 72 | callable. This decorator works exactly like the :func:`contextfunction` 73 | decorator just that the first argument is the active :class:`Environment` 74 | and not context. 75 | """ 76 | f.environmentfunction = True 77 | return f 78 | 79 | 80 | def internalcode(f): 81 | """Marks the function as internally used""" 82 | internal_code.add(f.__code__) 83 | return f 84 | 85 | 86 | def is_undefined(obj): 87 | """Check if the object passed is undefined. This does nothing more than 88 | performing an instance check against :class:`Undefined` but looks nicer. 89 | This can be used for custom filters or tests that want to react to 90 | undefined variables. For example a custom default filter can look like 91 | this:: 92 | 93 | def default(var, default=''): 94 | if is_undefined(var): 95 | return default 96 | return var 97 | """ 98 | from jinja2.runtime import Undefined 99 | return isinstance(obj, Undefined) 100 | 101 | 102 | def consume(iterable): 103 | """Consumes an iterable without doing anything with it.""" 104 | for event in iterable: 105 | pass 106 | 107 | 108 | def clear_caches(): 109 | """Jinja2 keeps internal caches for environments and lexers. These are 110 | used so that Jinja2 doesn't have to recreate environments and lexers all 111 | the time. Normally you don't have to care about that but if you are 112 | messuring memory consumption you may want to clean the caches. 113 | """ 114 | from jinja2.environment import _spontaneous_environments 115 | from jinja2.lexer import _lexer_cache 116 | _spontaneous_environments.clear() 117 | _lexer_cache.clear() 118 | 119 | 120 | def import_string(import_name, silent=False): 121 | """Imports an object based on a string. This is useful if you want to 122 | use import paths as endpoints or something similar. An import path can 123 | be specified either in dotted notation (``xml.sax.saxutils.escape``) 124 | or with a colon as object delimiter (``xml.sax.saxutils:escape``). 125 | 126 | If the `silent` is True the return value will be `None` if the import 127 | fails. 128 | 129 | :return: imported object 130 | """ 131 | try: 132 | if ':' in import_name: 133 | module, obj = import_name.split(':', 1) 134 | elif '.' in import_name: 135 | items = import_name.split('.') 136 | module = '.'.join(items[:-1]) 137 | obj = items[-1] 138 | else: 139 | return __import__(import_name) 140 | return getattr(__import__(module, None, None, [obj]), obj) 141 | except (ImportError, AttributeError): 142 | if not silent: 143 | raise 144 | 145 | 146 | def open_if_exists(filename, mode='rb'): 147 | """Returns a file descriptor for the filename if that file exists, 148 | otherwise `None`. 149 | """ 150 | try: 151 | return open(filename, mode) 152 | except IOError as e: 153 | if e.errno not in (errno.ENOENT, errno.EISDIR): 154 | raise 155 | 156 | 157 | def object_type_repr(obj): 158 | """Returns the name of the object's type. For some recognized 159 | singletons the name of the object is returned instead. (For 160 | example for `None` and `Ellipsis`). 161 | """ 162 | if obj is None: 163 | return 'None' 164 | elif obj is Ellipsis: 165 | return 'Ellipsis' 166 | # __builtin__ in 2.x, builtins in 3.x 167 | if obj.__class__.__module__ in ('__builtin__', 'builtins'): 168 | name = obj.__class__.__name__ 169 | else: 170 | name = obj.__class__.__module__ + '.' + obj.__class__.__name__ 171 | return '%s object' % name 172 | 173 | 174 | def pformat(obj, verbose=False): 175 | """Prettyprint an object. Either use the `pretty` library or the 176 | builtin `pprint`. 177 | """ 178 | try: 179 | from pretty import pretty 180 | return pretty(obj, verbose=verbose) 181 | except ImportError: 182 | from pprint import pformat 183 | return pformat(obj) 184 | 185 | 186 | def urlize(text, trim_url_limit=None, nofollow=False): 187 | """Converts any URLs in text into clickable links. Works on http://, 188 | https:// and www. links. Links can have trailing punctuation (periods, 189 | commas, close-parens) and leading punctuation (opening parens) and 190 | it'll still do the right thing. 191 | 192 | If trim_url_limit is not None, the URLs in link text will be limited 193 | to trim_url_limit characters. 194 | 195 | If nofollow is True, the URLs in link text will get a rel="nofollow" 196 | attribute. 197 | """ 198 | trim_url = lambda x, limit=trim_url_limit: limit is not None \ 199 | and (x[:limit] + (len(x) >=limit and '...' 200 | or '')) or x 201 | words = _word_split_re.split(text_type(escape(text))) 202 | nofollow_attr = nofollow and ' rel="nofollow"' or '' 203 | for i, word in enumerate(words): 204 | match = _punctuation_re.match(word) 205 | if match: 206 | lead, middle, trail = match.groups() 207 | if middle.startswith('www.') or ( 208 | '@' not in middle and 209 | not middle.startswith('http://') and 210 | not middle.startswith('https://') and 211 | len(middle) > 0 and 212 | middle[0] in _letters + _digits and ( 213 | middle.endswith('.org') or 214 | middle.endswith('.net') or 215 | middle.endswith('.com') 216 | )): 217 | middle = '%s' % (middle, 218 | nofollow_attr, trim_url(middle)) 219 | if middle.startswith('http://') or \ 220 | middle.startswith('https://'): 221 | middle = '%s' % (middle, 222 | nofollow_attr, trim_url(middle)) 223 | if '@' in middle and not middle.startswith('www.') and \ 224 | not ':' in middle and _simple_email_re.match(middle): 225 | middle = '%s' % (middle, middle) 226 | if lead + middle + trail != word: 227 | words[i] = lead + middle + trail 228 | return u''.join(words) 229 | 230 | 231 | def generate_lorem_ipsum(n=5, html=True, min=20, max=100): 232 | """Generate some lorem impsum for the template.""" 233 | from jinja2.constants import LOREM_IPSUM_WORDS 234 | from random import choice, randrange 235 | words = LOREM_IPSUM_WORDS.split() 236 | result = [] 237 | 238 | for _ in range(n): 239 | next_capitalized = True 240 | last_comma = last_fullstop = 0 241 | word = None 242 | last = None 243 | p = [] 244 | 245 | # each paragraph contains out of 20 to 100 words. 246 | for idx, _ in enumerate(range(randrange(min, max))): 247 | while True: 248 | word = choice(words) 249 | if word != last: 250 | last = word 251 | break 252 | if next_capitalized: 253 | word = word.capitalize() 254 | next_capitalized = False 255 | # add commas 256 | if idx - randrange(3, 8) > last_comma: 257 | last_comma = idx 258 | last_fullstop += 2 259 | word += ',' 260 | # add end of sentences 261 | if idx - randrange(10, 20) > last_fullstop: 262 | last_comma = last_fullstop = idx 263 | word += '.' 264 | next_capitalized = True 265 | p.append(word) 266 | 267 | # ensure that the paragraph ends with a dot. 268 | p = u' '.join(p) 269 | if p.endswith(','): 270 | p = p[:-1] + '.' 271 | elif not p.endswith('.'): 272 | p += '.' 273 | result.append(p) 274 | 275 | if not html: 276 | return u'\n\n'.join(result) 277 | return Markup(u'\n'.join(u'

%s

' % escape(x) for x in result)) 278 | 279 | 280 | def unicode_urlencode(obj, charset='utf-8'): 281 | """URL escapes a single bytestring or unicode string with the 282 | given charset if applicable to URL safe quoting under all rules 283 | that need to be considered under all supported Python versions. 284 | 285 | If non strings are provided they are converted to their unicode 286 | representation first. 287 | """ 288 | if not isinstance(obj, string_types): 289 | obj = text_type(obj) 290 | if isinstance(obj, text_type): 291 | obj = obj.encode(charset) 292 | return text_type(url_quote(obj)) 293 | 294 | 295 | class LRUCache(object): 296 | """A simple LRU Cache implementation.""" 297 | 298 | # this is fast for small capacities (something below 1000) but doesn't 299 | # scale. But as long as it's only used as storage for templates this 300 | # won't do any harm. 301 | 302 | def __init__(self, capacity): 303 | self.capacity = capacity 304 | self._mapping = {} 305 | self._queue = deque() 306 | self._postinit() 307 | 308 | def _postinit(self): 309 | # alias all queue methods for faster lookup 310 | self._popleft = self._queue.popleft 311 | self._pop = self._queue.pop 312 | self._remove = self._queue.remove 313 | self._wlock = Lock() 314 | self._append = self._queue.append 315 | 316 | def __getstate__(self): 317 | return { 318 | 'capacity': self.capacity, 319 | '_mapping': self._mapping, 320 | '_queue': self._queue 321 | } 322 | 323 | def __setstate__(self, d): 324 | self.__dict__.update(d) 325 | self._postinit() 326 | 327 | def __getnewargs__(self): 328 | return (self.capacity,) 329 | 330 | def copy(self): 331 | """Return a shallow copy of the instance.""" 332 | rv = self.__class__(self.capacity) 333 | rv._mapping.update(self._mapping) 334 | rv._queue = deque(self._queue) 335 | return rv 336 | 337 | def get(self, key, default=None): 338 | """Return an item from the cache dict or `default`""" 339 | try: 340 | return self[key] 341 | except KeyError: 342 | return default 343 | 344 | def setdefault(self, key, default=None): 345 | """Set `default` if the key is not in the cache otherwise 346 | leave unchanged. Return the value of this key. 347 | """ 348 | self._wlock.acquire() 349 | try: 350 | try: 351 | return self[key] 352 | except KeyError: 353 | self[key] = default 354 | return default 355 | finally: 356 | self._wlock.release() 357 | 358 | def clear(self): 359 | """Clear the cache.""" 360 | self._wlock.acquire() 361 | try: 362 | self._mapping.clear() 363 | self._queue.clear() 364 | finally: 365 | self._wlock.release() 366 | 367 | def __contains__(self, key): 368 | """Check if a key exists in this cache.""" 369 | return key in self._mapping 370 | 371 | def __len__(self): 372 | """Return the current size of the cache.""" 373 | return len(self._mapping) 374 | 375 | def __repr__(self): 376 | return '<%s %r>' % ( 377 | self.__class__.__name__, 378 | self._mapping 379 | ) 380 | 381 | def __getitem__(self, key): 382 | """Get an item from the cache. Moves the item up so that it has the 383 | highest priority then. 384 | 385 | Raise a `KeyError` if it does not exist. 386 | """ 387 | self._wlock.acquire() 388 | try: 389 | rv = self._mapping[key] 390 | if self._queue[-1] != key: 391 | try: 392 | self._remove(key) 393 | except ValueError: 394 | # if something removed the key from the container 395 | # when we read, ignore the ValueError that we would 396 | # get otherwise. 397 | pass 398 | self._append(key) 399 | return rv 400 | finally: 401 | self._wlock.release() 402 | 403 | def __setitem__(self, key, value): 404 | """Sets the value for an item. Moves the item up so that it 405 | has the highest priority then. 406 | """ 407 | self._wlock.acquire() 408 | try: 409 | if key in self._mapping: 410 | self._remove(key) 411 | elif len(self._mapping) == self.capacity: 412 | del self._mapping[self._popleft()] 413 | self._append(key) 414 | self._mapping[key] = value 415 | finally: 416 | self._wlock.release() 417 | 418 | def __delitem__(self, key): 419 | """Remove an item from the cache dict. 420 | Raise a `KeyError` if it does not exist. 421 | """ 422 | self._wlock.acquire() 423 | try: 424 | del self._mapping[key] 425 | try: 426 | self._remove(key) 427 | except ValueError: 428 | # __getitem__ is not locked, it might happen 429 | pass 430 | finally: 431 | self._wlock.release() 432 | 433 | def items(self): 434 | """Return a list of items.""" 435 | result = [(key, self._mapping[key]) for key in list(self._queue)] 436 | result.reverse() 437 | return result 438 | 439 | def iteritems(self): 440 | """Iterate over all items.""" 441 | return iter(self.items()) 442 | 443 | def values(self): 444 | """Return a list of all values.""" 445 | return [x[1] for x in self.items()] 446 | 447 | def itervalue(self): 448 | """Iterate over all values.""" 449 | return iter(self.values()) 450 | 451 | def keys(self): 452 | """Return a list of all keys ordered by most recent usage.""" 453 | return list(self) 454 | 455 | def iterkeys(self): 456 | """Iterate over all keys in the cache dict, ordered by 457 | the most recent usage. 458 | """ 459 | return reversed(tuple(self._queue)) 460 | 461 | __iter__ = iterkeys 462 | 463 | def __reversed__(self): 464 | """Iterate over the values in the cache dict, oldest items 465 | coming first. 466 | """ 467 | return iter(tuple(self._queue)) 468 | 469 | __copy__ = copy 470 | 471 | 472 | # register the LRU cache as mutable mapping if possible 473 | try: 474 | from collections import MutableMapping 475 | MutableMapping.register(LRUCache) 476 | except ImportError: 477 | pass 478 | 479 | 480 | @implements_iterator 481 | class Cycler(object): 482 | """A cycle helper for templates.""" 483 | 484 | def __init__(self, *items): 485 | if not items: 486 | raise RuntimeError('at least one item has to be provided') 487 | self.items = items 488 | self.reset() 489 | 490 | def reset(self): 491 | """Resets the cycle.""" 492 | self.pos = 0 493 | 494 | @property 495 | def current(self): 496 | """Returns the current item.""" 497 | return self.items[self.pos] 498 | 499 | def __next__(self): 500 | """Goes one item ahead and returns it.""" 501 | rv = self.current 502 | self.pos = (self.pos + 1) % len(self.items) 503 | return rv 504 | 505 | 506 | class Joiner(object): 507 | """A joining helper for templates.""" 508 | 509 | def __init__(self, sep=u', '): 510 | self.sep = sep 511 | self.used = False 512 | 513 | def __call__(self): 514 | if not self.used: 515 | self.used = True 516 | return u'' 517 | return self.sep 518 | 519 | 520 | # Imported here because that's where it was in the past 521 | from markupsafe import Markup, escape, soft_unicode 522 | -------------------------------------------------------------------------------- /jinja2/loaders.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.loaders 4 | ~~~~~~~~~~~~~~ 5 | 6 | Jinja loader classes. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import os 12 | import sys 13 | import weakref 14 | from types import ModuleType 15 | from os import path 16 | from hashlib import sha1 17 | from jinja2.exceptions import TemplateNotFound 18 | from jinja2.utils import open_if_exists, internalcode 19 | from jinja2._compat import string_types, iteritems 20 | 21 | 22 | def split_template_path(template): 23 | """Split a path into segments and perform a sanity check. If it detects 24 | '..' in the path it will raise a `TemplateNotFound` error. 25 | """ 26 | pieces = [] 27 | for piece in template.split('/'): 28 | if path.sep in piece \ 29 | or (path.altsep and path.altsep in piece) or \ 30 | piece == path.pardir: 31 | raise TemplateNotFound(template) 32 | elif piece and piece != '.': 33 | pieces.append(piece) 34 | return pieces 35 | 36 | 37 | class BaseLoader(object): 38 | """Baseclass for all loaders. Subclass this and override `get_source` to 39 | implement a custom loading mechanism. The environment provides a 40 | `get_template` method that calls the loader's `load` method to get the 41 | :class:`Template` object. 42 | 43 | A very basic example for a loader that looks up templates on the file 44 | system could look like this:: 45 | 46 | from jinja2 import BaseLoader, TemplateNotFound 47 | from os.path import join, exists, getmtime 48 | 49 | class MyLoader(BaseLoader): 50 | 51 | def __init__(self, path): 52 | self.path = path 53 | 54 | def get_source(self, environment, template): 55 | path = join(self.path, template) 56 | if not exists(path): 57 | raise TemplateNotFound(template) 58 | mtime = getmtime(path) 59 | with file(path) as f: 60 | source = f.read().decode('utf-8') 61 | return source, path, lambda: mtime == getmtime(path) 62 | """ 63 | 64 | #: if set to `False` it indicates that the loader cannot provide access 65 | #: to the source of templates. 66 | #: 67 | #: .. versionadded:: 2.4 68 | has_source_access = True 69 | 70 | def get_source(self, environment, template): 71 | """Get the template source, filename and reload helper for a template. 72 | It's passed the environment and template name and has to return a 73 | tuple in the form ``(source, filename, uptodate)`` or raise a 74 | `TemplateNotFound` error if it can't locate the template. 75 | 76 | The source part of the returned tuple must be the source of the 77 | template as unicode string or a ASCII bytestring. The filename should 78 | be the name of the file on the filesystem if it was loaded from there, 79 | otherwise `None`. The filename is used by python for the tracebacks 80 | if no loader extension is used. 81 | 82 | The last item in the tuple is the `uptodate` function. If auto 83 | reloading is enabled it's always called to check if the template 84 | changed. No arguments are passed so the function must store the 85 | old state somewhere (for example in a closure). If it returns `False` 86 | the template will be reloaded. 87 | """ 88 | if not self.has_source_access: 89 | raise RuntimeError('%s cannot provide access to the source' % 90 | self.__class__.__name__) 91 | raise TemplateNotFound(template) 92 | 93 | def list_templates(self): 94 | """Iterates over all templates. If the loader does not support that 95 | it should raise a :exc:`TypeError` which is the default behavior. 96 | """ 97 | raise TypeError('this loader cannot iterate over all templates') 98 | 99 | @internalcode 100 | def load(self, environment, name, globals=None): 101 | """Loads a template. This method looks up the template in the cache 102 | or loads one by calling :meth:`get_source`. Subclasses should not 103 | override this method as loaders working on collections of other 104 | loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) 105 | will not call this method but `get_source` directly. 106 | """ 107 | code = None 108 | if globals is None: 109 | globals = {} 110 | 111 | # first we try to get the source for this template together 112 | # with the filename and the uptodate function. 113 | source, filename, uptodate = self.get_source(environment, name) 114 | 115 | # try to load the code from the bytecode cache if there is a 116 | # bytecode cache configured. 117 | bcc = environment.bytecode_cache 118 | if bcc is not None: 119 | bucket = bcc.get_bucket(environment, name, filename, source) 120 | code = bucket.code 121 | 122 | # if we don't have code so far (not cached, no longer up to 123 | # date) etc. we compile the template 124 | if code is None: 125 | code = environment.compile(source, name, filename) 126 | 127 | # if the bytecode cache is available and the bucket doesn't 128 | # have a code so far, we give the bucket the new code and put 129 | # it back to the bytecode cache. 130 | if bcc is not None and bucket.code is None: 131 | bucket.code = code 132 | bcc.set_bucket(bucket) 133 | 134 | return environment.template_class.from_code(environment, code, 135 | globals, uptodate) 136 | 137 | 138 | class FileSystemLoader(BaseLoader): 139 | """Loads templates from the file system. This loader can find templates 140 | in folders on the file system and is the preferred way to load them. 141 | 142 | The loader takes the path to the templates as string, or if multiple 143 | locations are wanted a list of them which is then looked up in the 144 | given order: 145 | 146 | >>> loader = FileSystemLoader('/path/to/templates') 147 | >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) 148 | 149 | Per default the template encoding is ``'utf-8'`` which can be changed 150 | by setting the `encoding` parameter to something else. 151 | """ 152 | 153 | def __init__(self, searchpath, encoding='utf-8'): 154 | if isinstance(searchpath, string_types): 155 | searchpath = [searchpath] 156 | self.searchpath = list(searchpath) 157 | self.encoding = encoding 158 | 159 | def get_source(self, environment, template): 160 | pieces = split_template_path(template) 161 | for searchpath in self.searchpath: 162 | filename = path.join(searchpath, *pieces) 163 | f = open_if_exists(filename) 164 | if f is None: 165 | continue 166 | try: 167 | contents = f.read().decode(self.encoding) 168 | finally: 169 | f.close() 170 | 171 | mtime = path.getmtime(filename) 172 | def uptodate(): 173 | try: 174 | return path.getmtime(filename) == mtime 175 | except OSError: 176 | return False 177 | return contents, filename, uptodate 178 | raise TemplateNotFound(template) 179 | 180 | def list_templates(self): 181 | found = set() 182 | for searchpath in self.searchpath: 183 | for dirpath, dirnames, filenames in os.walk(searchpath): 184 | for filename in filenames: 185 | template = os.path.join(dirpath, filename) \ 186 | [len(searchpath):].strip(os.path.sep) \ 187 | .replace(os.path.sep, '/') 188 | if template[:2] == './': 189 | template = template[2:] 190 | if template not in found: 191 | found.add(template) 192 | return sorted(found) 193 | 194 | 195 | class PackageLoader(BaseLoader): 196 | """Load templates from python eggs or packages. It is constructed with 197 | the name of the python package and the path to the templates in that 198 | package:: 199 | 200 | loader = PackageLoader('mypackage', 'views') 201 | 202 | If the package path is not given, ``'templates'`` is assumed. 203 | 204 | Per default the template encoding is ``'utf-8'`` which can be changed 205 | by setting the `encoding` parameter to something else. Due to the nature 206 | of eggs it's only possible to reload templates if the package was loaded 207 | from the file system and not a zip file. 208 | """ 209 | 210 | def __init__(self, package_name, package_path='templates', 211 | encoding='utf-8'): 212 | from pkg_resources import DefaultProvider, ResourceManager, \ 213 | get_provider 214 | provider = get_provider(package_name) 215 | self.encoding = encoding 216 | self.manager = ResourceManager() 217 | self.filesystem_bound = isinstance(provider, DefaultProvider) 218 | self.provider = provider 219 | self.package_path = package_path 220 | 221 | def get_source(self, environment, template): 222 | pieces = split_template_path(template) 223 | p = '/'.join((self.package_path,) + tuple(pieces)) 224 | if not self.provider.has_resource(p): 225 | raise TemplateNotFound(template) 226 | 227 | filename = uptodate = None 228 | if self.filesystem_bound: 229 | filename = self.provider.get_resource_filename(self.manager, p) 230 | mtime = path.getmtime(filename) 231 | def uptodate(): 232 | try: 233 | return path.getmtime(filename) == mtime 234 | except OSError: 235 | return False 236 | 237 | source = self.provider.get_resource_string(self.manager, p) 238 | return source.decode(self.encoding), filename, uptodate 239 | 240 | def list_templates(self): 241 | path = self.package_path 242 | if path[:2] == './': 243 | path = path[2:] 244 | elif path == '.': 245 | path = '' 246 | offset = len(path) 247 | results = [] 248 | def _walk(path): 249 | for filename in self.provider.resource_listdir(path): 250 | fullname = path + '/' + filename 251 | if self.provider.resource_isdir(fullname): 252 | _walk(fullname) 253 | else: 254 | results.append(fullname[offset:].lstrip('/')) 255 | _walk(path) 256 | results.sort() 257 | return results 258 | 259 | 260 | class DictLoader(BaseLoader): 261 | """Loads a template from a python dict. It's passed a dict of unicode 262 | strings bound to template names. This loader is useful for unittesting: 263 | 264 | >>> loader = DictLoader({'index.html': 'source here'}) 265 | 266 | Because auto reloading is rarely useful this is disabled per default. 267 | """ 268 | 269 | def __init__(self, mapping): 270 | self.mapping = mapping 271 | 272 | def get_source(self, environment, template): 273 | if template in self.mapping: 274 | source = self.mapping[template] 275 | return source, None, lambda: source == self.mapping.get(template) 276 | raise TemplateNotFound(template) 277 | 278 | def list_templates(self): 279 | return sorted(self.mapping) 280 | 281 | 282 | class FunctionLoader(BaseLoader): 283 | """A loader that is passed a function which does the loading. The 284 | function becomes the name of the template passed and has to return either 285 | an unicode string with the template source, a tuple in the form ``(source, 286 | filename, uptodatefunc)`` or `None` if the template does not exist. 287 | 288 | >>> def load_template(name): 289 | ... if name == 'index.html': 290 | ... return '...' 291 | ... 292 | >>> loader = FunctionLoader(load_template) 293 | 294 | The `uptodatefunc` is a function that is called if autoreload is enabled 295 | and has to return `True` if the template is still up to date. For more 296 | details have a look at :meth:`BaseLoader.get_source` which has the same 297 | return value. 298 | """ 299 | 300 | def __init__(self, load_func): 301 | self.load_func = load_func 302 | 303 | def get_source(self, environment, template): 304 | rv = self.load_func(template) 305 | if rv is None: 306 | raise TemplateNotFound(template) 307 | elif isinstance(rv, string_types): 308 | return rv, None, None 309 | return rv 310 | 311 | 312 | class PrefixLoader(BaseLoader): 313 | """A loader that is passed a dict of loaders where each loader is bound 314 | to a prefix. The prefix is delimited from the template by a slash per 315 | default, which can be changed by setting the `delimiter` argument to 316 | something else:: 317 | 318 | loader = PrefixLoader({ 319 | 'app1': PackageLoader('mypackage.app1'), 320 | 'app2': PackageLoader('mypackage.app2') 321 | }) 322 | 323 | By loading ``'app1/index.html'`` the file from the app1 package is loaded, 324 | by loading ``'app2/index.html'`` the file from the second. 325 | """ 326 | 327 | def __init__(self, mapping, delimiter='/'): 328 | self.mapping = mapping 329 | self.delimiter = delimiter 330 | 331 | def get_loader(self, template): 332 | try: 333 | prefix, name = template.split(self.delimiter, 1) 334 | loader = self.mapping[prefix] 335 | except (ValueError, KeyError): 336 | raise TemplateNotFound(template) 337 | return loader, name 338 | 339 | def get_source(self, environment, template): 340 | loader, name = self.get_loader(template) 341 | try: 342 | return loader.get_source(environment, name) 343 | except TemplateNotFound: 344 | # re-raise the exception with the correct fileame here. 345 | # (the one that includes the prefix) 346 | raise TemplateNotFound(template) 347 | 348 | @internalcode 349 | def load(self, environment, name, globals=None): 350 | loader, local_name = self.get_loader(name) 351 | try: 352 | return loader.load(environment, local_name, globals) 353 | except TemplateNotFound: 354 | # re-raise the exception with the correct fileame here. 355 | # (the one that includes the prefix) 356 | raise TemplateNotFound(name) 357 | 358 | def list_templates(self): 359 | result = [] 360 | for prefix, loader in iteritems(self.mapping): 361 | for template in loader.list_templates(): 362 | result.append(prefix + self.delimiter + template) 363 | return result 364 | 365 | 366 | class ChoiceLoader(BaseLoader): 367 | """This loader works like the `PrefixLoader` just that no prefix is 368 | specified. If a template could not be found by one loader the next one 369 | is tried. 370 | 371 | >>> loader = ChoiceLoader([ 372 | ... FileSystemLoader('/path/to/user/templates'), 373 | ... FileSystemLoader('/path/to/system/templates') 374 | ... ]) 375 | 376 | This is useful if you want to allow users to override builtin templates 377 | from a different location. 378 | """ 379 | 380 | def __init__(self, loaders): 381 | self.loaders = loaders 382 | 383 | def get_source(self, environment, template): 384 | for loader in self.loaders: 385 | try: 386 | return loader.get_source(environment, template) 387 | except TemplateNotFound: 388 | pass 389 | raise TemplateNotFound(template) 390 | 391 | @internalcode 392 | def load(self, environment, name, globals=None): 393 | for loader in self.loaders: 394 | try: 395 | return loader.load(environment, name, globals) 396 | except TemplateNotFound: 397 | pass 398 | raise TemplateNotFound(name) 399 | 400 | def list_templates(self): 401 | found = set() 402 | for loader in self.loaders: 403 | found.update(loader.list_templates()) 404 | return sorted(found) 405 | 406 | 407 | class _TemplateModule(ModuleType): 408 | """Like a normal module but with support for weak references""" 409 | 410 | 411 | class ModuleLoader(BaseLoader): 412 | """This loader loads templates from precompiled templates. 413 | 414 | Example usage: 415 | 416 | >>> loader = ChoiceLoader([ 417 | ... ModuleLoader('/path/to/compiled/templates'), 418 | ... FileSystemLoader('/path/to/templates') 419 | ... ]) 420 | 421 | Templates can be precompiled with :meth:`Environment.compile_templates`. 422 | """ 423 | 424 | has_source_access = False 425 | 426 | def __init__(self, path): 427 | package_name = '_jinja2_module_templates_%x' % id(self) 428 | 429 | # create a fake module that looks for the templates in the 430 | # path given. 431 | mod = _TemplateModule(package_name) 432 | if isinstance(path, string_types): 433 | path = [path] 434 | else: 435 | path = list(path) 436 | mod.__path__ = path 437 | 438 | sys.modules[package_name] = weakref.proxy(mod, 439 | lambda x: sys.modules.pop(package_name, None)) 440 | 441 | # the only strong reference, the sys.modules entry is weak 442 | # so that the garbage collector can remove it once the 443 | # loader that created it goes out of business. 444 | self.module = mod 445 | self.package_name = package_name 446 | 447 | @staticmethod 448 | def get_template_key(name): 449 | return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() 450 | 451 | @staticmethod 452 | def get_module_filename(name): 453 | return ModuleLoader.get_template_key(name) + '.py' 454 | 455 | @internalcode 456 | def load(self, environment, name, globals=None): 457 | key = self.get_template_key(name) 458 | module = '%s.%s' % (self.package_name, key) 459 | mod = getattr(self.module, module, None) 460 | if mod is None: 461 | try: 462 | mod = __import__(module, None, None, ['root']) 463 | except ImportError: 464 | raise TemplateNotFound(name) 465 | 466 | # remove the entry from sys.modules, we only want the attribute 467 | # on the module object we have stored on the loader. 468 | sys.modules.pop(module, None) 469 | 470 | return environment.template_class.from_module_dict( 471 | environment, mod.__dict__, globals) 472 | -------------------------------------------------------------------------------- /jinja2/runtime.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.runtime 4 | ~~~~~~~~~~~~~~ 5 | 6 | Runtime helpers. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD. 10 | """ 11 | from itertools import chain 12 | from jinja2.nodes import EvalContext, _context_function_types 13 | from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ 14 | internalcode, object_type_repr 15 | from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ 16 | TemplateNotFound 17 | from jinja2._compat import imap, text_type, iteritems, \ 18 | implements_iterator, implements_to_string, string_types, PY2 19 | 20 | 21 | # these variables are exported to the template runtime 22 | __all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', 23 | 'TemplateRuntimeError', 'missing', 'concat', 'escape', 24 | 'markup_join', 'unicode_join', 'to_string', 'identity', 25 | 'TemplateNotFound'] 26 | 27 | #: the name of the function that is used to convert something into 28 | #: a string. We can just use the text type here. 29 | to_string = text_type 30 | 31 | #: the identity function. Useful for certain things in the environment 32 | identity = lambda x: x 33 | 34 | _last_iteration = object() 35 | 36 | 37 | def markup_join(seq): 38 | """Concatenation that escapes if necessary and converts to unicode.""" 39 | buf = [] 40 | iterator = imap(soft_unicode, seq) 41 | for arg in iterator: 42 | buf.append(arg) 43 | if hasattr(arg, '__html__'): 44 | return Markup(u'').join(chain(buf, iterator)) 45 | return concat(buf) 46 | 47 | 48 | def unicode_join(seq): 49 | """Simple args to unicode conversion and concatenation.""" 50 | return concat(imap(text_type, seq)) 51 | 52 | 53 | def new_context(environment, template_name, blocks, vars=None, 54 | shared=None, globals=None, locals=None): 55 | """Internal helper to for context creation.""" 56 | if vars is None: 57 | vars = {} 58 | if shared: 59 | parent = vars 60 | else: 61 | parent = dict(globals or (), **vars) 62 | if locals: 63 | # if the parent is shared a copy should be created because 64 | # we don't want to modify the dict passed 65 | if shared: 66 | parent = dict(parent) 67 | for key, value in iteritems(locals): 68 | if key[:2] == 'l_' and value is not missing: 69 | parent[key[2:]] = value 70 | return Context(environment, parent, template_name, blocks) 71 | 72 | 73 | class TemplateReference(object): 74 | """The `self` in templates.""" 75 | 76 | def __init__(self, context): 77 | self.__context = context 78 | 79 | def __getitem__(self, name): 80 | blocks = self.__context.blocks[name] 81 | return BlockReference(name, self.__context, blocks, 0) 82 | 83 | def __repr__(self): 84 | return '<%s %r>' % ( 85 | self.__class__.__name__, 86 | self.__context.name 87 | ) 88 | 89 | 90 | class Context(object): 91 | """The template context holds the variables of a template. It stores the 92 | values passed to the template and also the names the template exports. 93 | Creating instances is neither supported nor useful as it's created 94 | automatically at various stages of the template evaluation and should not 95 | be created by hand. 96 | 97 | The context is immutable. Modifications on :attr:`parent` **must not** 98 | happen and modifications on :attr:`vars` are allowed from generated 99 | template code only. Template filters and global functions marked as 100 | :func:`contextfunction`\s get the active context passed as first argument 101 | and are allowed to access the context read-only. 102 | 103 | The template context supports read only dict operations (`get`, 104 | `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, 105 | `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` 106 | method that doesn't fail with a `KeyError` but returns an 107 | :class:`Undefined` object for missing variables. 108 | """ 109 | __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars', 110 | 'name', 'blocks', '__weakref__') 111 | 112 | def __init__(self, environment, parent, name, blocks): 113 | self.parent = parent 114 | self.vars = {} 115 | self.environment = environment 116 | self.eval_ctx = EvalContext(self.environment, name) 117 | self.exported_vars = set() 118 | self.name = name 119 | 120 | # create the initial mapping of blocks. Whenever template inheritance 121 | # takes place the runtime will update this mapping with the new blocks 122 | # from the template. 123 | self.blocks = dict((k, [v]) for k, v in iteritems(blocks)) 124 | 125 | def super(self, name, current): 126 | """Render a parent block.""" 127 | try: 128 | blocks = self.blocks[name] 129 | index = blocks.index(current) + 1 130 | blocks[index] 131 | except LookupError: 132 | return self.environment.undefined('there is no parent block ' 133 | 'called %r.' % name, 134 | name='super') 135 | return BlockReference(name, self, blocks, index) 136 | 137 | def get(self, key, default=None): 138 | """Returns an item from the template context, if it doesn't exist 139 | `default` is returned. 140 | """ 141 | try: 142 | return self[key] 143 | except KeyError: 144 | return default 145 | 146 | def resolve(self, key): 147 | """Looks up a variable like `__getitem__` or `get` but returns an 148 | :class:`Undefined` object with the name of the name looked up. 149 | """ 150 | if key in self.vars: 151 | return self.vars[key] 152 | if key in self.parent: 153 | return self.parent[key] 154 | return self.environment.undefined(name=key) 155 | 156 | def get_exported(self): 157 | """Get a new dict with the exported variables.""" 158 | return dict((k, self.vars[k]) for k in self.exported_vars) 159 | 160 | def get_all(self): 161 | """Return a copy of the complete context as dict including the 162 | exported variables. 163 | """ 164 | return dict(self.parent, **self.vars) 165 | 166 | @internalcode 167 | def call(__self, __obj, *args, **kwargs): 168 | """Call the callable with the arguments and keyword arguments 169 | provided but inject the active context or environment as first 170 | argument if the callable is a :func:`contextfunction` or 171 | :func:`environmentfunction`. 172 | """ 173 | if __debug__: 174 | __traceback_hide__ = True 175 | 176 | # Allow callable classes to take a context 177 | fn = __obj.__call__ 178 | for fn_type in ('contextfunction', 179 | 'evalcontextfunction', 180 | 'environmentfunction'): 181 | if hasattr(fn, fn_type): 182 | __obj = fn 183 | break 184 | 185 | if isinstance(__obj, _context_function_types): 186 | if getattr(__obj, 'contextfunction', 0): 187 | args = (__self,) + args 188 | elif getattr(__obj, 'evalcontextfunction', 0): 189 | args = (__self.eval_ctx,) + args 190 | elif getattr(__obj, 'environmentfunction', 0): 191 | args = (__self.environment,) + args 192 | try: 193 | return __obj(*args, **kwargs) 194 | except StopIteration: 195 | return __self.environment.undefined('value was undefined because ' 196 | 'a callable raised a ' 197 | 'StopIteration exception') 198 | 199 | def derived(self, locals=None): 200 | """Internal helper function to create a derived context.""" 201 | context = new_context(self.environment, self.name, {}, 202 | self.parent, True, None, locals) 203 | context.vars.update(self.vars) 204 | context.eval_ctx = self.eval_ctx 205 | context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks)) 206 | return context 207 | 208 | def _all(meth): 209 | proxy = lambda self: getattr(self.get_all(), meth)() 210 | proxy.__doc__ = getattr(dict, meth).__doc__ 211 | proxy.__name__ = meth 212 | return proxy 213 | 214 | keys = _all('keys') 215 | values = _all('values') 216 | items = _all('items') 217 | 218 | # not available on python 3 219 | if PY2: 220 | iterkeys = _all('iterkeys') 221 | itervalues = _all('itervalues') 222 | iteritems = _all('iteritems') 223 | del _all 224 | 225 | def __contains__(self, name): 226 | return name in self.vars or name in self.parent 227 | 228 | def __getitem__(self, key): 229 | """Lookup a variable or raise `KeyError` if the variable is 230 | undefined. 231 | """ 232 | item = self.resolve(key) 233 | if isinstance(item, Undefined): 234 | raise KeyError(key) 235 | return item 236 | 237 | def __repr__(self): 238 | return '<%s %s of %r>' % ( 239 | self.__class__.__name__, 240 | repr(self.get_all()), 241 | self.name 242 | ) 243 | 244 | 245 | # register the context as mapping if possible 246 | try: 247 | from collections import Mapping 248 | Mapping.register(Context) 249 | except ImportError: 250 | pass 251 | 252 | 253 | class BlockReference(object): 254 | """One block on a template reference.""" 255 | 256 | def __init__(self, name, context, stack, depth): 257 | self.name = name 258 | self._context = context 259 | self._stack = stack 260 | self._depth = depth 261 | 262 | @property 263 | def super(self): 264 | """Super the block.""" 265 | if self._depth + 1 >= len(self._stack): 266 | return self._context.environment. \ 267 | undefined('there is no parent block called %r.' % 268 | self.name, name='super') 269 | return BlockReference(self.name, self._context, self._stack, 270 | self._depth + 1) 271 | 272 | @internalcode 273 | def __call__(self): 274 | rv = concat(self._stack[self._depth](self._context)) 275 | if self._context.eval_ctx.autoescape: 276 | rv = Markup(rv) 277 | return rv 278 | 279 | 280 | class LoopContext(object): 281 | """A loop context for dynamic iteration.""" 282 | 283 | def __init__(self, iterable, recurse=None, depth0=0): 284 | self._iterator = iter(iterable) 285 | self._recurse = recurse 286 | self._after = self._safe_next() 287 | self.index0 = -1 288 | self.depth0 = depth0 289 | 290 | # try to get the length of the iterable early. This must be done 291 | # here because there are some broken iterators around where there 292 | # __len__ is the number of iterations left (i'm looking at your 293 | # listreverseiterator!). 294 | try: 295 | self._length = len(iterable) 296 | except (TypeError, AttributeError): 297 | self._length = None 298 | 299 | def cycle(self, *args): 300 | """Cycles among the arguments with the current loop index.""" 301 | if not args: 302 | raise TypeError('no items for cycling given') 303 | return args[self.index0 % len(args)] 304 | 305 | first = property(lambda x: x.index0 == 0) 306 | last = property(lambda x: x._after is _last_iteration) 307 | index = property(lambda x: x.index0 + 1) 308 | revindex = property(lambda x: x.length - x.index0) 309 | revindex0 = property(lambda x: x.length - x.index) 310 | depth = property(lambda x: x.depth0 + 1) 311 | 312 | def __len__(self): 313 | return self.length 314 | 315 | def __iter__(self): 316 | return LoopContextIterator(self) 317 | 318 | def _safe_next(self): 319 | try: 320 | return next(self._iterator) 321 | except StopIteration: 322 | return _last_iteration 323 | 324 | @internalcode 325 | def loop(self, iterable): 326 | if self._recurse is None: 327 | raise TypeError('Tried to call non recursive loop. Maybe you ' 328 | "forgot the 'recursive' modifier.") 329 | return self._recurse(iterable, self._recurse, self.depth0 + 1) 330 | 331 | # a nifty trick to enhance the error message if someone tried to call 332 | # the the loop without or with too many arguments. 333 | __call__ = loop 334 | del loop 335 | 336 | @property 337 | def length(self): 338 | if self._length is None: 339 | # if was not possible to get the length of the iterator when 340 | # the loop context was created (ie: iterating over a generator) 341 | # we have to convert the iterable into a sequence and use the 342 | # length of that. 343 | iterable = tuple(self._iterator) 344 | self._iterator = iter(iterable) 345 | self._length = len(iterable) + self.index0 + 1 346 | return self._length 347 | 348 | def __repr__(self): 349 | return '<%s %r/%r>' % ( 350 | self.__class__.__name__, 351 | self.index, 352 | self.length 353 | ) 354 | 355 | 356 | @implements_iterator 357 | class LoopContextIterator(object): 358 | """The iterator for a loop context.""" 359 | __slots__ = ('context',) 360 | 361 | def __init__(self, context): 362 | self.context = context 363 | 364 | def __iter__(self): 365 | return self 366 | 367 | def __next__(self): 368 | ctx = self.context 369 | ctx.index0 += 1 370 | if ctx._after is _last_iteration: 371 | raise StopIteration() 372 | next_elem = ctx._after 373 | ctx._after = ctx._safe_next() 374 | return next_elem, ctx 375 | 376 | 377 | class Macro(object): 378 | """Wraps a macro function.""" 379 | 380 | def __init__(self, environment, func, name, arguments, defaults, 381 | catch_kwargs, catch_varargs, caller): 382 | self._environment = environment 383 | self._func = func 384 | self._argument_count = len(arguments) 385 | self.name = name 386 | self.arguments = arguments 387 | self.defaults = defaults 388 | self.catch_kwargs = catch_kwargs 389 | self.catch_varargs = catch_varargs 390 | self.caller = caller 391 | 392 | @internalcode 393 | def __call__(self, *args, **kwargs): 394 | # try to consume the positional arguments 395 | arguments = list(args[:self._argument_count]) 396 | off = len(arguments) 397 | 398 | # if the number of arguments consumed is not the number of 399 | # arguments expected we start filling in keyword arguments 400 | # and defaults. 401 | if off != self._argument_count: 402 | for idx, name in enumerate(self.arguments[len(arguments):]): 403 | try: 404 | value = kwargs.pop(name) 405 | except KeyError: 406 | try: 407 | value = self.defaults[idx - self._argument_count + off] 408 | except IndexError: 409 | value = self._environment.undefined( 410 | 'parameter %r was not provided' % name, name=name) 411 | arguments.append(value) 412 | 413 | # it's important that the order of these arguments does not change 414 | # if not also changed in the compiler's `function_scoping` method. 415 | # the order is caller, keyword arguments, positional arguments! 416 | if self.caller: 417 | caller = kwargs.pop('caller', None) 418 | if caller is None: 419 | caller = self._environment.undefined('No caller defined', 420 | name='caller') 421 | arguments.append(caller) 422 | if self.catch_kwargs: 423 | arguments.append(kwargs) 424 | elif kwargs: 425 | raise TypeError('macro %r takes no keyword argument %r' % 426 | (self.name, next(iter(kwargs)))) 427 | if self.catch_varargs: 428 | arguments.append(args[self._argument_count:]) 429 | elif len(args) > self._argument_count: 430 | raise TypeError('macro %r takes not more than %d argument(s)' % 431 | (self.name, len(self.arguments))) 432 | return self._func(*arguments) 433 | 434 | def __repr__(self): 435 | return '<%s %s>' % ( 436 | self.__class__.__name__, 437 | self.name is None and 'anonymous' or repr(self.name) 438 | ) 439 | 440 | 441 | @implements_to_string 442 | class Undefined(object): 443 | """The default undefined type. This undefined type can be printed and 444 | iterated over, but every other access will raise an :exc:`UndefinedError`: 445 | 446 | >>> foo = Undefined(name='foo') 447 | >>> str(foo) 448 | '' 449 | >>> not foo 450 | True 451 | >>> foo + 42 452 | Traceback (most recent call last): 453 | ... 454 | UndefinedError: 'foo' is undefined 455 | """ 456 | __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', 457 | '_undefined_exception') 458 | 459 | def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): 460 | self._undefined_hint = hint 461 | self._undefined_obj = obj 462 | self._undefined_name = name 463 | self._undefined_exception = exc 464 | 465 | @internalcode 466 | def _fail_with_undefined_error(self, *args, **kwargs): 467 | """Regular callback function for undefined objects that raises an 468 | `UndefinedError` on call. 469 | """ 470 | if self._undefined_hint is None: 471 | if self._undefined_obj is missing: 472 | hint = '%r is undefined' % self._undefined_name 473 | elif not isinstance(self._undefined_name, string_types): 474 | hint = '%s has no element %r' % ( 475 | object_type_repr(self._undefined_obj), 476 | self._undefined_name 477 | ) 478 | else: 479 | hint = '%r has no attribute %r' % ( 480 | object_type_repr(self._undefined_obj), 481 | self._undefined_name 482 | ) 483 | else: 484 | hint = self._undefined_hint 485 | raise self._undefined_exception(hint) 486 | 487 | @internalcode 488 | def __getattr__(self, name): 489 | if name[:2] == '__': 490 | raise AttributeError(name) 491 | return self._fail_with_undefined_error() 492 | 493 | __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ 494 | __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ 495 | __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ 496 | __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ 497 | __float__ = __complex__ = __pow__ = __rpow__ = \ 498 | _fail_with_undefined_error 499 | 500 | def __eq__(self, other): 501 | return type(self) is type(other) 502 | 503 | def __ne__(self, other): 504 | return not self.__eq__(other) 505 | 506 | def __hash__(self): 507 | return id(type(self)) 508 | 509 | def __str__(self): 510 | return u'' 511 | 512 | def __len__(self): 513 | return 0 514 | 515 | def __iter__(self): 516 | if 0: 517 | yield None 518 | 519 | def __nonzero__(self): 520 | return False 521 | 522 | def __repr__(self): 523 | return 'Undefined' 524 | 525 | 526 | @implements_to_string 527 | class DebugUndefined(Undefined): 528 | """An undefined that returns the debug info when printed. 529 | 530 | >>> foo = DebugUndefined(name='foo') 531 | >>> str(foo) 532 | '{{ foo }}' 533 | >>> not foo 534 | True 535 | >>> foo + 42 536 | Traceback (most recent call last): 537 | ... 538 | UndefinedError: 'foo' is undefined 539 | """ 540 | __slots__ = () 541 | 542 | def __str__(self): 543 | if self._undefined_hint is None: 544 | if self._undefined_obj is missing: 545 | return u'{{ %s }}' % self._undefined_name 546 | return '{{ no such element: %s[%r] }}' % ( 547 | object_type_repr(self._undefined_obj), 548 | self._undefined_name 549 | ) 550 | return u'{{ undefined value printed: %s }}' % self._undefined_hint 551 | 552 | 553 | @implements_to_string 554 | class StrictUndefined(Undefined): 555 | """An undefined that barks on print and iteration as well as boolean 556 | tests and all kinds of comparisons. In other words: you can do nothing 557 | with it except checking if it's defined using the `defined` test. 558 | 559 | >>> foo = StrictUndefined(name='foo') 560 | >>> str(foo) 561 | Traceback (most recent call last): 562 | ... 563 | UndefinedError: 'foo' is undefined 564 | >>> not foo 565 | Traceback (most recent call last): 566 | ... 567 | UndefinedError: 'foo' is undefined 568 | >>> foo + 42 569 | Traceback (most recent call last): 570 | ... 571 | UndefinedError: 'foo' is undefined 572 | """ 573 | __slots__ = () 574 | __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ 575 | __ne__ = __bool__ = __hash__ = \ 576 | Undefined._fail_with_undefined_error 577 | 578 | 579 | # remove remaining slots attributes, after the metaclass did the magic they 580 | # are unneeded and irritating as they contain wrong data for the subclasses. 581 | del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ 582 | --------------------------------------------------------------------------------